From 129be82cb39c909828afc1c30b9d215edf7a2118 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 3 Mar 2022 23:36:22 -0500 Subject: [PATCH 001/844] Roll Flutter from f63e0c8d4682 to ae9a796cfae1 (19 revisions) (#4993) --- .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 a76b27a3dd0f..afacd760ffbc 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f63e0c8d46826e79f671c9b1e9580ecddcd8f3d7 +ae9a796cfae1ee42e0242d2924dbc026ce1a7f18 From ab66a4bf3c4e43fb4a445222580314050ffc19cf Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 4 Mar 2022 08:01:24 -0500 Subject: [PATCH 002/844] Roll Flutter from ae9a796cfae1 to 646c22085e52 (2 revisions) (#4997) --- .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 afacd760ffbc..70939b55e775 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ae9a796cfae1ee42e0242d2924dbc026ce1a7f18 +646c22085e52be308e1700650130f5d0940465fd From bed9aa79ca1050bbf6e152b49ab62c0194d434ac Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 4 Mar 2022 10:56:19 -0500 Subject: [PATCH 003/844] Roll Flutter from 646c22085e52 to fbf6a58bf19c (1 revision) (#4998) --- .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 70939b55e775..57e681ad1a34 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -646c22085e52be308e1700650130f5d0940465fd +fbf6a58bf19c52b6b2dba2dd218f046eedeabc6d From 77e841d5c88178f35428be0c3d274d0f05301c6d Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 4 Mar 2022 11:01:52 -0500 Subject: [PATCH 004/844] [file_selector] Add macOS support (#4381) Brings file_selector_macos into flutter/plugins from FDE, with the following changes: - Refactored slightly to allow for unit tests of almost all of the native code - Added native unit test coverage - Translated to Swift, to follow repo conventions for macOS plugins - Added an in-package example (almost an exact duplicate of the app-facing version, but written against the platform interface, as is our current practice) - Moved to an in-package method channel. As part of that, moved the flattening of type groups from Swift to Dart. Does not currently include native UI tests to allow for end-to-end testing (since Flutter integration tests can't be used). They should be added later (they are currently blocked on https://github.com/flutter/flutter/issues/90673), but the unit tests give substantial coverage, making it substantially better to move the plugin now to get those tests running. macOS portion of https://github.com/flutter/flutter/issues/70221 --- .cirrus.yml | 2 +- .../file_selector_macos/.gitignore | 5 + .../file_selector_macos/.metadata | 10 + .../file_selector/file_selector_macos/AUTHORS | 6 + .../file_selector_macos/CHANGELOG.md | 26 + .../file_selector/file_selector_macos/LICENSE | 25 + .../file_selector_macos/README.md | 34 + .../file_selector_macos/example/.gitignore | 48 ++ .../file_selector_macos/example/.metadata | 10 + .../file_selector_macos/example/README.md | 4 + .../example/lib/get_directory_page.dart | 77 ++ .../example/lib/home_page.dart | 57 ++ .../file_selector_macos/example/lib/main.dart | 37 + .../example/lib/open_image_page.dart | 87 ++ .../lib/open_multiple_images_page.dart | 99 +++ .../example/lib/open_text_page.dart | 84 ++ .../example/lib/save_text_page.dart | 78 ++ .../example/macos/.gitignore | 6 + .../macos/Flutter/Flutter-Debug.xcconfig | 2 + .../macos/Flutter/Flutter-Release.xcconfig | 2 + .../file_selector_macos/example/macos/Podfile | 40 + .../macos/Runner.xcodeproj/project.pbxproj | 767 ++++++++++++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 107 +++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 68 ++ .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++++++++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 14 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 19 + .../example/macos/Runner/Release.entitlements | 10 + .../example/macos/RunnerTests/Info.plist | 22 + .../macos/RunnerTests/RunnerTests.swift | 283 +++++++ .../file_selector_macos/example/pubspec.yaml | 27 + .../lib/file_selector_macos.dart | 121 +++ .../macos/Classes/FileSelectorPlugin.swift | 218 +++++ .../macos/file_selector_macos.podspec | 21 + .../file_selector_macos/pubspec.yaml | 25 + .../test/file_selector_macos_test.dart | 288 +++++++ script/configs/exclude_integration_macos.yaml | 2 + 53 files changed, 3171 insertions(+), 1 deletion(-) create mode 100644 packages/file_selector/file_selector_macos/.gitignore create mode 100644 packages/file_selector/file_selector_macos/.metadata create mode 100644 packages/file_selector/file_selector_macos/AUTHORS create mode 100644 packages/file_selector/file_selector_macos/CHANGELOG.md create mode 100644 packages/file_selector/file_selector_macos/LICENSE create mode 100644 packages/file_selector/file_selector_macos/README.md create mode 100644 packages/file_selector/file_selector_macos/example/.gitignore create mode 100644 packages/file_selector/file_selector_macos/example/.metadata create mode 100644 packages/file_selector/file_selector_macos/example/README.md create mode 100644 packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart create mode 100644 packages/file_selector/file_selector_macos/example/lib/home_page.dart create mode 100644 packages/file_selector/file_selector_macos/example/lib/main.dart create mode 100644 packages/file_selector/file_selector_macos/example/lib/open_image_page.dart create mode 100644 packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart create mode 100644 packages/file_selector/file_selector_macos/example/lib/open_text_page.dart create mode 100644 packages/file_selector/file_selector_macos/example/lib/save_text_page.dart create mode 100644 packages/file_selector/file_selector_macos/example/macos/.gitignore create mode 100644 packages/file_selector/file_selector_macos/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/file_selector/file_selector_macos/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/file_selector/file_selector_macos/example/macos/Podfile create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/AppDelegate.swift create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Info.plist create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/file_selector/file_selector_macos/example/macos/Runner/Release.entitlements create mode 100644 packages/file_selector/file_selector_macos/example/macos/RunnerTests/Info.plist create mode 100644 packages/file_selector/file_selector_macos/example/macos/RunnerTests/RunnerTests.swift create mode 100644 packages/file_selector/file_selector_macos/example/pubspec.yaml create mode 100644 packages/file_selector/file_selector_macos/lib/file_selector_macos.dart create mode 100644 packages/file_selector/file_selector_macos/macos/Classes/FileSelectorPlugin.swift create mode 100644 packages/file_selector/file_selector_macos/macos/file_selector_macos.podspec create mode 100644 packages/file_selector/file_selector_macos/pubspec.yaml create mode 100644 packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart create mode 100644 script/configs/exclude_integration_macos.yaml diff --git a/.cirrus.yml b/.cirrus.yml index b283e84f4b91..1184ac7e00a1 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -338,4 +338,4 @@ task: native_test_script: - ./script/tool_runner.sh native-test --macos drive_script: - - ./script/tool_runner.sh drive-examples --macos + - ./script/tool_runner.sh drive-examples --macos --exclude=script/configs/exclude_integration_macos.yaml diff --git a/packages/file_selector/file_selector_macos/.gitignore b/packages/file_selector/file_selector_macos/.gitignore new file mode 100644 index 000000000000..0393a47ff732 --- /dev/null +++ b/packages/file_selector/file_selector_macos/.gitignore @@ -0,0 +1,5 @@ +.dart_tool +.packages +.flutter-plugins +.flutter-plugins-dependencies +pubspec.lock diff --git a/packages/file_selector/file_selector_macos/.metadata b/packages/file_selector/file_selector_macos/.metadata new file mode 100644 index 000000000000..720a4596c087 --- /dev/null +++ b/packages/file_selector/file_selector_macos/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 6d1c244b79f3a2747281f718297ce248bd5ad099 + channel: master + +project_type: plugin diff --git a/packages/file_selector/file_selector_macos/AUTHORS b/packages/file_selector/file_selector_macos/AUTHORS new file mode 100644 index 000000000000..557dff97933b --- /dev/null +++ b/packages/file_selector/file_selector_macos/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/file_selector/file_selector_macos/CHANGELOG.md b/packages/file_selector/file_selector_macos/CHANGELOG.md new file mode 100644 index 000000000000..794d056811f4 --- /dev/null +++ b/packages/file_selector/file_selector_macos/CHANGELOG.md @@ -0,0 +1,26 @@ +## 0.8.2 + +* Moves source to flutter/plugins. +* Adds native unit tests. +* Converts native implementation to Swift. +* Switches to an internal method channel implementation. + +## 0.0.4+1 + +* Update README + +## 0.0.4 + +* Treat empty filter lists the same as null. + +## 0.0.3 + +* Fix README + +## 0.0.2 + +* Update SDK constraint to signal compatibility with null safety. + +## 0.0.1 + +* Initial macOS implementation of `file_selector`. diff --git a/packages/file_selector/file_selector_macos/LICENSE b/packages/file_selector/file_selector_macos/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/file_selector/file_selector_macos/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/file_selector/file_selector_macos/README.md b/packages/file_selector/file_selector_macos/README.md new file mode 100644 index 000000000000..efa5272149be --- /dev/null +++ b/packages/file_selector/file_selector_macos/README.md @@ -0,0 +1,34 @@ +# file\_selector\_macos + +The macOS implementation of [`file_selector`][1]. + +## Usage + +### Importing the package + +This implementation has not yet been endorsed, meaning that you need to +[depend on `file_selector_macos`][2] in addition to +[depending on `file_selector`][3]. + +Once your pubspec includes the macOS implementation, you can use the +`file_selector` APIs normally. You should not use the `file_selector_macos` +APIs directly. + +### Entitlements + +You will need to [add an entitlement][4] for either read-only access: +``` + com.apple.security.files.user-selected.read-only + +``` +or read/write access: +``` + com.apple.security.files.user-selected.read-write + +``` +depending on your use case. + +[1]: https://pub.dev/packages/file_selector +[2]: https://pub.dev/packages/file_selector_macos/install +[3]: https://pub.dev/packages/file_selector/install +[4]: https://flutter.dev/desktop#entitlements-and-the-app-sandbox diff --git a/packages/file_selector/file_selector_macos/example/.gitignore b/packages/file_selector/file_selector_macos/example/.gitignore new file mode 100644 index 000000000000..7abd0753cfc3 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/.gitignore @@ -0,0 +1,48 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Currently only web supported +android/ +ios/ + +# Exceptions to above rules. +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/packages/file_selector/file_selector_macos/example/.metadata b/packages/file_selector/file_selector_macos/example/.metadata new file mode 100644 index 000000000000..897381f2373f --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 7736f3bc90270dcb0480db2ccffbf1d13c28db85 + channel: dev + +project_type: app diff --git a/packages/file_selector/file_selector_macos/example/README.md b/packages/file_selector/file_selector_macos/example/README.md new file mode 100644 index 000000000000..782fe679fcb0 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/README.md @@ -0,0 +1,4 @@ +# `file_selector_macos` example + +Demonstrates macOS implementation of the +[`file_selector` plugin](https://pub.dev/packages/file_selector). diff --git a/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart b/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart new file mode 100644 index 000000000000..0e55df8ce622 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart @@ -0,0 +1,77 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select a directory using `getDirectoryPath`, +/// then displays the selected directory in a dialog. +class GetDirectoryPage extends StatelessWidget { + Future _getDirectoryPath(BuildContext context) async { + const String confirmButtonText = 'Choose'; + final String? directoryPath = + await FileSelectorPlatform.instance.getDirectoryPath( + confirmButtonText: confirmButtonText, + ); + if (directoryPath == null) { + // Operation was canceled by the user. + return; + } + await showDialog( + context: context, + builder: (BuildContext context) => TextDisplay(directoryPath), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open a text file'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.blue, + onPrimary: Colors.white, + ), + child: const Text('Press to ask user to choose a directory'), + onPressed: () => _getDirectoryPath(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class TextDisplay extends StatelessWidget { + /// Creates a `TextDisplay`. + const TextDisplay(this.directoryPath); + + /// The path selected in the dialog. + final String directoryPath; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Selected Directory'), + content: Scrollbar( + child: SingleChildScrollView( + child: Text(directoryPath), + ), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () => Navigator.pop(context), + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_macos/example/lib/home_page.dart b/packages/file_selector/file_selector_macos/example/lib/home_page.dart new file mode 100644 index 000000000000..958680be0e3b --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/lib/home_page.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/material.dart'; + +/// Home Page of the application. +class HomePage extends StatelessWidget { + @override + Widget build(BuildContext context) { + final ButtonStyle style = ElevatedButton.styleFrom( + primary: Colors.blue, + onPrimary: Colors.white, + ); + return Scaffold( + appBar: AppBar( + title: const Text('File Selector Demo Home Page'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: style, + child: const Text('Open a text file'), + onPressed: () => Navigator.pushNamed(context, '/open/text'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open an image'), + onPressed: () => Navigator.pushNamed(context, '/open/image'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open multiple images'), + onPressed: () => Navigator.pushNamed(context, '/open/images'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Save a file'), + onPressed: () => Navigator.pushNamed(context, '/save/text'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open a get directory dialog'), + onPressed: () => Navigator.pushNamed(context, '/directory'), + ), + ], + ), + ), + ); + } +} diff --git a/packages/file_selector/file_selector_macos/example/lib/main.dart b/packages/file_selector/file_selector_macos/example/lib/main.dart new file mode 100644 index 000000000000..a49ebac1aea5 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/lib/main.dart @@ -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. + +import 'package:example/get_directory_page.dart'; +import 'package:example/home_page.dart'; +import 'package:example/open_image_page.dart'; +import 'package:example/open_multiple_images_page.dart'; +import 'package:example/open_text_page.dart'; +import 'package:example/save_text_page.dart'; +import 'package:flutter/material.dart'; + +void main() { + runApp(MyApp()); +} + +/// MyApp is the Main Application. +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'File Selector Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: HomePage(), + routes: { + '/open/image': (BuildContext context) => OpenImagePage(), + '/open/images': (BuildContext context) => OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => OpenTextPage(), + '/save/text': (BuildContext context) => SaveTextPage(), + '/directory': (BuildContext context) => GetDirectoryPage(), + }, + ); + } +} diff --git a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart new file mode 100644 index 000000000000..aaf083603e72 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart @@ -0,0 +1,87 @@ +// 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:io'; + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select an image file using +/// `openFiles`, then displays the selected images in a gallery dialog. +class OpenImagePage extends StatelessWidget { + Future _openImageFile(BuildContext context) async { + final XTypeGroup typeGroup = XTypeGroup( + label: 'images', + extensions: ['jpg', 'png'], + ); + final XFile? file = await FileSelectorPlatform.instance + .openFile(acceptedTypeGroups: [typeGroup]); + if (file == null) { + // Operation was canceled by the user. + return; + } + final String fileName = file.name; + final String filePath = file.path; + + await showDialog( + context: context, + builder: (BuildContext context) => ImageDisplay(fileName, filePath), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open an image'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.blue, + onPrimary: Colors.white, + ), + child: const Text('Press to open an image file(png, jpg)'), + onPressed: () => _openImageFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays an image in a dialog. +class ImageDisplay extends StatelessWidget { + /// Default Constructor. + const ImageDisplay(this.fileName, this.filePath); + + /// The name of the selected file. + final String fileName; + + /// The path to the selected file. + final String filePath; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(fileName), + // On web the filePath is a blob url + // while on other platforms it is a system path. + content: kIsWeb ? Image.network(filePath) : Image.file(File(filePath)), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart new file mode 100644 index 000000000000..a030b8b4b10b --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart @@ -0,0 +1,99 @@ +// 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:io'; + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select multiple image files using +/// `openFiles`, then displays the selected images in a gallery dialog. +class OpenMultipleImagesPage extends StatelessWidget { + Future _openImageFile(BuildContext context) async { + final XTypeGroup jpgsTypeGroup = XTypeGroup( + label: 'JPEGs', + extensions: ['jpg', 'jpeg'], + ); + final XTypeGroup pngTypeGroup = XTypeGroup( + label: 'PNGs', + extensions: ['png'], + ); + final List files = await FileSelectorPlatform.instance + .openFiles(acceptedTypeGroups: [ + jpgsTypeGroup, + pngTypeGroup, + ]); + if (files.isEmpty) { + // Operation was canceled by the user. + return; + } + await showDialog( + context: context, + builder: (BuildContext context) => MultipleImagesDisplay(files), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open multiple images'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.blue, + onPrimary: Colors.white, + ), + child: const Text('Press to open multiple images (png, jpg)'), + onPressed: () => _openImageFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class MultipleImagesDisplay extends StatelessWidget { + /// Default Constructor. + const MultipleImagesDisplay(this.files); + + /// The files containing the images. + final List files; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Gallery'), + // On web the filePath is a blob url + // while on other platforms it is a system path. + content: Center( + child: Row( + children: [ + ...files.map( + (XFile file) => Flexible( + child: kIsWeb + ? Image.network(file.path) + : Image.file(File(file.path))), + ) + ], + ), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart new file mode 100644 index 000000000000..fa281a0020d5 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart @@ -0,0 +1,84 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select a text file using `openFile`, then +/// displays its contents in a dialog. +class OpenTextPage extends StatelessWidget { + Future _openTextFile(BuildContext context) async { + final XTypeGroup typeGroup = XTypeGroup( + label: 'text', + extensions: ['txt', 'json'], + ); + final XFile? file = await FileSelectorPlatform.instance + .openFile(acceptedTypeGroups: [typeGroup]); + if (file == null) { + // Operation was canceled by the user. + return; + } + final String fileName = file.name; + final String fileContent = await file.readAsString(); + + await showDialog( + context: context, + builder: (BuildContext context) => TextDisplay(fileName, fileContent), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open a text file'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.blue, + onPrimary: Colors.white, + ), + child: const Text('Press to open a text file (json, txt)'), + onPressed: () => _openTextFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class TextDisplay extends StatelessWidget { + /// Default Constructor. + const TextDisplay(this.fileName, this.fileContent); + + /// The name of the selected file. + final String fileName; + + /// The contents of the text file. + final String fileContent; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(fileName), + content: Scrollbar( + child: SingleChildScrollView( + child: Text(fileContent), + ), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () => Navigator.pop(context), + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart new file mode 100644 index 000000000000..3989c62b7442 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/lib/save_text_page.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 'dart:typed_data'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select a save location using `getSavePath`, +/// then writes text to a file at that location. +class SaveTextPage extends StatelessWidget { + final TextEditingController _nameController = TextEditingController(); + final TextEditingController _contentController = TextEditingController(); + + Future _saveFile() async { + final String fileName = _nameController.text; + final String? path = await FileSelectorPlatform.instance.getSavePath( + suggestedName: fileName, + ); + if (path == null) { + // Operation was canceled by the user. + return; + } + final String text = _contentController.text; + final Uint8List fileData = Uint8List.fromList(text.codeUnits); + const String fileMimeType = 'text/plain'; + final XFile textFile = + XFile.fromData(fileData, mimeType: fileMimeType, name: fileName); + await textFile.saveTo(path); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Save text into a file'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 300, + child: TextField( + minLines: 1, + maxLines: 12, + controller: _nameController, + decoration: const InputDecoration( + hintText: '(Optional) Suggest File Name', + ), + ), + ), + Container( + width: 300, + child: TextField( + minLines: 1, + maxLines: 12, + controller: _contentController, + decoration: const InputDecoration( + hintText: 'Enter File Contents', + ), + ), + ), + const SizedBox(height: 10), + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: Colors.blue, + onPrimary: Colors.white, + ), + child: const Text('Press to save a text file'), + onPressed: _saveFile, + ), + ], + ), + ), + ); + } +} diff --git a/packages/file_selector/file_selector_macos/example/macos/.gitignore b/packages/file_selector/file_selector_macos/example/macos/.gitignore new file mode 100644 index 000000000000..d2fd3772308c --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/.gitignore @@ -0,0 +1,6 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/xcuserdata/ diff --git a/packages/file_selector/file_selector_macos/example/macos/Flutter/Flutter-Debug.xcconfig b/packages/file_selector/file_selector_macos/example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 000000000000..4b81f9b2d200 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/file_selector/file_selector_macos/example/macos/Flutter/Flutter-Release.xcconfig b/packages/file_selector/file_selector_macos/example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 000000000000..5caa9d1579e4 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/file_selector/file_selector_macos/example/macos/Podfile b/packages/file_selector/file_selector_macos/example/macos/Podfile new file mode 100644 index 000000000000..dade8dfad0dc --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Podfile @@ -0,0 +1,40 @@ +platform :osx, '10.11' + +# 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', 'ephemeral', '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 Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/project.pbxproj b/packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..fa8d272d4ee0 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,767 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 338EA5D426EFE72B0071837A /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 338EA5D326EFE72B0071837A /* RunnerTests.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 4CE8B69FE511476B98B4816C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B61FB9CDECD72211FAB708CA /* Pods_Runner.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 338EA5D626EFE72B0071837A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 338EA5D126EFE72B0071837A /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 338EA5D326EFE72B0071837A /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 338EA5D526EFE72B0071837A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + B61FB9CDECD72211FAB708CA /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BA03A11192D3E8EEA888D495 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + C03B6D624A05212E07A5D41E /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + D921BBD60B6562B7A5F559AC /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 338EA5CE26EFE72B0071837A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4CE8B69FE511476B98B4816C /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 338EA5D226EFE72B0071837A /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 338EA5D326EFE72B0071837A /* RunnerTests.swift */, + 338EA5D526EFE72B0071837A /* Info.plist */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 338EA5D226EFE72B0071837A /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + CAED34175B65FC224CC4F18C /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* example.app */, + 338EA5D126EFE72B0071837A /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + CAED34175B65FC224CC4F18C /* Pods */ = { + isa = PBXGroup; + children = ( + D921BBD60B6562B7A5F559AC /* Pods-Runner.debug.xcconfig */, + C03B6D624A05212E07A5D41E /* Pods-Runner.release.xcconfig */, + BA03A11192D3E8EEA888D495 /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + B61FB9CDECD72211FAB708CA /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 338EA5D026EFE72B0071837A /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 338EA5DB26EFE72B0071837A /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 338EA5CD26EFE72B0071837A /* Sources */, + 338EA5CE26EFE72B0071837A /* Frameworks */, + 338EA5CF26EFE72B0071837A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 338EA5D726EFE72B0071837A /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 338EA5D126EFE72B0071837A /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 8F1744F37738365955F17998 /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + A8D2084B0509A3B3053F3AF7 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1250; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = "The Flutter Authors"; + TargetAttributes = { + 338EA5D026EFE72B0071837A = { + CreatedOnToolsVersion = 12.5; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + 338EA5D026EFE72B0071837A /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 338EA5CF26EFE72B0071837A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; + 8F1744F37738365955F17998 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + A8D2084B0509A3B3053F3AF7 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 338EA5CD26EFE72B0071837A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 338EA5D426EFE72B0071837A /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 338EA5D726EFE72B0071837A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 338EA5D626EFE72B0071837A /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 338EA5D826EFE72B0071837A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/Contents/MacOS/example"; + }; + name = Debug; + }; + 338EA5D926EFE72B0071837A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/Contents/MacOS/example"; + }; + name = Release; + }; + 338EA5DA26EFE72B0071837A /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/Contents/MacOS/example"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 338EA5DB26EFE72B0071837A /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 338EA5D826EFE72B0071837A /* Debug */, + 338EA5D926EFE72B0071837A /* Release */, + 338EA5DA26EFE72B0071837A /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..57d6538229d5 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/file_selector/file_selector_macos/example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/file_selector/file_selector_macos/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/AppDelegate.swift b/packages/file_selector/file_selector_macos/example/macos/Runner/AppDelegate.swift new file mode 100644 index 000000000000..5cec4c48f620 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/AppDelegate.swift @@ -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 Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..a2ec33f19f11 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..3c4935a7ca84f0976aca34b7f2895d65fb94d1ea GIT binary patch literal 46993 zcmZ5|3p`X?`~OCwR3s6~xD(})N~M}fiXn6%NvKp3QYhuNN0*apqmfHdR7#ShNQ99j zQi+P9nwlXbmnktZ_WnO>bl&&<{m*;O=RK!cd#$zCdM@AR`#jH%+2~+BeX7b-48x|= zZLBt9*d+MZNtpCx_&asa{+CselLUV<<&ceQ5QfRjLjQDSL-t4eq}5znmIXDtfA|D+VRV$*2jxU)JopC)!37FtD<6L^&{ia zgVf1p(e;c3|HY;%uD5<-oSFkC2JRh- z&2RTL)HBG`)j5di8ys|$z_9LSm^22*uH-%MmUJs|nHKLHxy4xTmG+)JoA`BN7#6IN zK-ylvs+~KN#4NWaH~o5Wuwd@W?H@diExdcTl0!JJq9ZOA24b|-TkkeG=Q(pJw7O;i z`@q+n|@eeW7@ z&*NP+)wOyu^5oNJ=yi4~s_+N)#M|@8nfw=2#^BpML$~dJ6yu}2JNuq!)!;Uwxic(z zM@Wa-v|U{v|GX4;P+s#=_1PD7h<%8ey$kxVsS1xt&%8M}eOF98&Rx7W<)gY(fCdmo{y*FPC{My!t`i=PS1cdV7DD=3S1J?b2<5BevW7!rWJ%6Q?D9UljULd*7SxX05PP^5AklWu^y` z-m9&Oq-XNSRjd|)hZ44DK?3>G%kFHSJ8|ZXbAcRb`gH~jk}Iwkl$@lqg!vu)ihSl= zjhBh%%Hq|`Vm>T7+SYyf4bI-MgiBq4mZlZmsKv+S>p$uAOoNxPT)R6owU%t*#aV}B z5@)X8nhtaBhH=={w;Du=-S*xvcPz26EI!gt{(hf;TllHrvku`^8wMj7-9=By>n{b= zHzQ?Wn|y=;)XM#St@o%#8idxfc`!oVz@Lv_=y(t-kUC`W)c0H2TX}Lop4121;RHE(PPHKfe_e_@DoHiPbVP%JzNudGc$|EnIv`qww1F5HwF#@l(=V zyM!JQO>Rt_PTRF1hI|u^2Uo#w*rdF*LXJky0?|fhl4-M%zN_2RP#HFhSATE3&{sos zIE_?MdIn!sUH*vjs(teJ$7^7#|M_7m`T>r>qHw>TQh?yhhc8=TJk2B;KNXw3HhnQs za(Uaz2VwP;82rTy(T3FJNKA86Y7;L(K=~BW_Q=jjRh=-k_=wh-$`nY+#au+v^C4VV z)U?X(v-_#i=3bAylP1S*pM_y*DB z2fR!imng6Dk$>dl*K@AIj<~zw_f$T!-xLO8r{OkE(l?W#W<={460Y02*K#)O4xp?W zAN+isO}!*|mN7B#jUt&!KNyFOpUxv&ybM>jmkfn8z^llBslztv!!`TBEPwu;#eR3d z@_VDa)|ByvXx1V=^Up4{;M8ji3FC7gm(C7Ty-#1gs+U<{Ouc(iV67{< zam#KwvR&s=k4W<13`}DxzJ9{TUa97N-cgWkCDc+C339)EEnC@^HQK6OvKDSCvNz(S zOFAF_6omgG!+zaPC8fBO3kH8YVBx9_AoM?->pv~@$saf(Myo|e@onD`a=;kO*Utem ze=eUH&;JB2I4}?Pm@=VnE+yb$PD~sA5+)|iH3bi|s?ExIePeoAMd(Z4Z%$mCu{t;B9(sgdG~Q}0ShAwe!l8nw0tJn zJ+m?ogrgty$3=T&6+JJa!1oS3AtQQ1gJ z3gR1<=hXU>{SB-zq!okl4c+V9N;vo4{fyGeqtgBIt%TPC1P&k!pR-GZ7O8b}9=%>3 zQrV%FQdB+CcCRKK)0}v>U25rbQk(1^9Ax|WcAo5?L(H&H@%zAoT2RH$iN6boyXpsYqME}WJZI6T%OMlkWXK>R`^7AHG&31 z&MIU}igQ7$;)7AEm#dXA+!I&6ymb7n6D;F7c$tO3Ql(`ht z1sFrzIk_q5#=!#D(e~#SdWz5K;tPF*R883Yu>*@jTeOGUjQekw zM+7HlfP{y8p}jA9bLfyKC_Ti8k#;AVp@RML^9MQp-E+Ns-Y zKA!aAZV-sfm<23fy#@TZZlQVQxH%R7rD}00LxHPUF!Yg3%OX ziDe4m<4fp{7ivBS?*AlJz$~vw5m)Ei8`|+~xOSqJ$waA0+Yys$z$9iN9TIXu8 zaYacjd09uRAsU|)g|03w`F|b1Xg#K~*Mp2X^K^)r3P^juoc}-me&YhkW3#G|H<~jK zoKD?lE@jOw7>4cpKkh!8qU!bF(i~Oa8a!EGy-j46eZYbKUvF=^^nq`EtWFK}gwrsB zeu<6~?mk+;+$whP)8ud8vjqh+NofU+Nu`~|pb&CN1y_idxxf6cGbT=fBZR_hl&G)GgnW$*oDrN-zz;cKs18n+dAn95w z)Y>l6!5eYpebJGw7it~Q5m}8$7@%p&KS=VtydFj4HPJ{xqUVS_Ih}c(^4nUdwG|0% zw8Fnm{IT`8MqoL(1BNtu_#7alS@3WSUUOFT@U*`V!zrPIeCbbO=pE%|g92$EU|lw; z^;^AqMVWVf-R5^OI79TzIyYf}HX%0Y)=aYH;EKo}?=R~ZM&s&F;W>u%hFUfNafb;- z8OkmkK3k||J#3`xdLuMJAhj9oPI?Cjt}cDN7hw26n7irWS0hsy`fs&Y?Y&(QF*Nu! z!p`NggHXaBU6$P42LkqnKsPG@363DHYGXg{!|z6VMAQt??>FK1B4x4{j;iY8A+7o% z*!0qt&w+w#Ob@pQp;q)u0;v^9FlY=AK>2!qku)!%TO<^lNBr!6R8X)iXgXi^1p`T8 z6sU@Y_Fsp6E89E1*jz~Tm2kF=mjYz_q99r^v0h-l7SP6azzL%woM6!7>IFWyizrNwAqoia3nN0q343q zFztMPh0)?ugQg5Izbk{5$EGcMzt*|=S8ZFK%O&^YV@V;ZRL>f!iG?s5z{(*Xq20c^ z(hkk~PljBo%U`$q>mz!ir7chKlE-oHA2&0i@hn4O5scsI&nIWsM>sYg;Ph5IO~VpT z%c-3_{^N>4kECzk?2~Z@V|jWio&a&no;boiNxqXOpS;ph)gEDFJ6E=zPJ$>y5w`U0 z;h9_6ncIEY?#j1+IDUuixRg&(hw+QSSEmFi%_$ua$^K%(*jUynGU@FlvsyThxqMRw z7_ALpqTj~jOSu2_(@wc_Z?>X&(5jezB6w-@0X_34f&cZ=cA-t%#}>L7Q3QRx1$qyh zG>NF=Ts>)wA)fZIlk-kz%Xa;)SE(PLu(oEC8>9GUBgd$(^_(G6Y((Hi{fsV; zt*!IBWx_$5D4D&ezICAdtEU!WS3`YmC_?+o&1RDSfTbuOx<*v`G<2SP;5Q4TqFV&q zJL=90Lcm^TL7a9xck}XPMRnQ`l0%w-fi@bRI&c*VDj!W4nj=qaQd$2U?^9RTT{*qS_)Q9OL>s}2P3&da^Pf(*?> z#&2bt;Q7N2`P{{KH@>)Tf5&za?crRmQ%8xZi<9f=EV3={K zwMet=oA0-@`8F;u`8j-!8G~0TiH5yKemY+HU@Zw3``1nT>D ziK465-m?Nm^~@G@RW2xH&*C#PrvCWU)#M4jQ`I*>_^BZB_c!z5Wn9W&eCBE(oc1pw zmMr)iu74Xl5>pf&D7Ml>%uhpFGJGyj6Mx=t#`}Mt3tDZQDn~K`gp0d)P>>4{FGiP$sPK*ExVs!1)aGgAX z6eA;-9@@Muti3xYv$8U{?*NxlHxs?)(6%!Iw&&l79K86h+Z8;)m9+(zzX?cS zH*~)yk)X^H1?AfL!xctY-8T0G0Vh~kcP=8%Wg*zZxm*;eb)TEh&lGuNkqJib_}i;l z*35qQ@}I#v;EwCGM2phE1{=^T4gT63m`;UEf5x2Get-WSWmt6%T6NJM`|tk-~4<#HHwCXuduB4+vW!BywlH8murH@|32CNxx7} zAoF?Gu02vpSl|q1IFO0tNEvKwyH5V^3ZtEO(su1sIYOr{t@Tr-Ot@&N*enq;Je38} zOY+C1bZ?P~1=Qb%oStI-HcO#|WHrpgIDR0GY|t)QhhTg*pMA|%C~>;R4t_~H1J3!i zyvQeDi&|930wZlA$`Wa9)m(cB!lPKD>+Ag$5v-}9%87`|7mxoNbq7r^U!%%ctxiNS zM6pV6?m~jCQEKtF3vLnpag``|bx+eJ8h=(8b;R+8rzueQvXgFhAW*9y$!DgSJgJj% zWIm~}9(R6LdlXEg{Y3g_i7dP^98=-3qa z$*j&xC_$5btF!80{D&2*mp(`rNLAM$JhkB@3al3s=1k^Ud6HHontlcZw&y?`uPT#a za8$RD%e8!ph8Ow7kqI@_vd7lgRhkMvpzp@4XJ`9dA@+Xk1wYf`0Dk!hIrBxhnRR(_ z%jd(~x^oqA>r>`~!TEyhSyrwNA(i}={W+feUD^8XtX^7^Z#c7att{ot#q6B;;t~oq zct7WAa?UK0rj0yhRuY$7RPVoO29JV$o1Z|sJzG5<%;7pCu%L-deUon-X_wAtzY@_d z6S}&5xXBtsf8TZ13chR&vOMYs0F1?SJcvPn>SFe#+P3r=6=VIqcCU7<6-vxR*BZUm zO^DkE{(r8!e56)2U;+8jH4tuD2c(ptk0R{@wWK?%Wz?fJckr9vpIU27^UN*Q$}VyHWx)reWgmEls}t+2#Zm z_I5?+htcQl)}OTqF<`wht89>W*2f6e)-ewk^XU5!sW2A2VtaI=lggR&I z;Rw{xd)WMqw`VUPbhrx!!1Eg_*O0Si6t@ny)~X^Gu8wZZDockr)5)6tm+<=z+rYu? zCof+;!nq6r9MAfh zp4|^2w^-3vFK~{JFX|F5BIWecBJkkEuE%iP8AZ z^&e|C+VEH&i(4Y|oWPCa#C3T$129o5xaJa=y8f(!k&q+x=M|rq{?Zw_n?1X-bt&bP zD{*>Io`F4(i+5eE2oEo6iF}jNAZ52VN&Cp>LD{MyB=mCeiwP+v#gRvr%W)}?JBTMY z_hc2r8*SksC%(pp$KGmWSa|fx;r^9c;~Q(Jqw1%;$#azZf}#Fca9NZOh{*YxV9(1ivVA^2Wz>!A&Xvmm-~{y8n!^Jdl8c>`J#=2~!P{ zC1g_5Ye3={{fB`R%Q|%9<1p1;XmPo5lH5PHvX$bCIYzQhGqj7hZ?@P4M0^mkejD|H zVzARm7LRy|8`jSG^GpxRIs=aD>Y{Cb>^IwGEKCMd5LAoI;b{Q<-G}x*e>86R8dNAV z<@jb1q%@QQanW1S72kOQ$9_E#O?o}l{mHd=%Dl{WQcPio$baXZN!j{2m)TH1hfAp{ zM`EQ=4J`fMj4c&T+xKT!I0CfT^UpcgJK22vC962ulgV7FrUrII5!rx1;{@FMg(dIf zAC}stNqooiVol%%TegMuWnOkWKKA}hg6c)ssp~EnTUVUI98;a}_8UeTgT|<%G3J=n zKL;GzAhIQ_@$rDqqc1PljwpfUwiB)w!#cLAkgR_af;>}(BhnC9N zqL|q8-?jsO&Srv54TxVuJ=rfcX=C7{JNV zSmW@s0;$(#!hNuU0|YyXLs{9$_y2^fRmM&g#toh}!K8P}tlJvYyrs6yjTtHU>TB0} zNy9~t5F47ocE_+%V1(D!mKNBQc{bnrAbfPC2KO?qdnCv8DJzEBeDbW}gd!g2pyRyK`H6TVU^~K# z488@^*&{foHKthLu?AF6l-wEE&g1CTKV|hN7nP+KJnkd0sagHm&k{^SE-woW9^fYD z7y?g*jh+ELt;$OgP>Se3o#~w9qS}!%#vBvB?|I-;GM63oYrJ}HFRW6D+{54v@PN8K z2kG8`!VVc+DHl^8y#cevo4VCnTaPTzCB%*)sr&+=p{Hh#(MwaJbeuvvd!5fd67J_W za`oKxTR=mtM7P}i2qHG8=A(39l)_rHHKduDVA@^_Ueb7bq1A5#zHAi**|^H@fD`_W z#URdSG86hhQ#&S-Vf_8b`TIAmM55XhaHX7}Ci-^(ZDs*yb-WrWV&(oAQu3vMv%u$5 zc;!ADkeNBN_@47r!;%G3iFzo;?k)xTS-;1D-YeS5QXN7`p2PzGK~e6ib;8COBa5)p zfMn}dA--&A12~zr&GVk?qnBGfIEo`5yir;-Q;ZLn{Fimdrk;e!)q`sAkYh^~^>4Q@ zN5RT>s38+`V{|6@k&vZW!W0*BEqV&~34d+Ev8h)ObYL7Bd_hgbUzjdJaXP=S@Dp6X z)i013q3K4Gr5d%2YIp>218pYK!xwH;k)j?uUrT-yVKLg*L3y~=a+qd!RWGTL`z>29 z-Zb4Y{%pT%`R-iA#?T58c-i@?jf-Ckol9O>HAZPUxN%Z=<4ad9BL7n`_kH0i#E(m& zaNb039+z~ONUCLsf_a|x*&ptU?`=R*n}rm-tOdCDrS!@>>xBg)B3Sy8?x^e=U=i8< zy7H-^BPfM}$hf*d_`Qhk_V$dRYZw<)_mbC~gPPxf0$EeXhl-!(ZH3rkDnf`Nrf4$+ zh?jsRS+?Zc9Cx7Vzg?q53ffpp43po22^8i1Obih&$oBufMR;cT2bHlSZ#fDMZZr~u zXIfM5SRjBj4N1}#0Ez|lHjSPQoL&QiT4mZn=SxHJg~R`ZjP!+hJ?&~tf$N!spvKPi zfY;x~laI9X`&#i#Z}RJ`0+MO_j^3#3TQJu2r;A-maLD8xfI+2Y*iDf4LsQ$9xiu?~ z?^wHEf^qlgtjdj(u_(W5sbGx1;maVPDHvI-76u2uUywf;>()=e>0le;bO0LIvs)iy z*lJTO+7gyf^)2uS-PhS_O-+RToQmc6VT>ej^y^stNkwIxUg?E|YMAAwQ}U!dC&cXL ziXKU?zT~xbh6C};rICGbdX~;8Z%L~Jdg|`senVEJo-CiDsX47Kc`;EiXWO<9o)(`4 zGj(9@c+Me=F~y(HUehcAy!tkoM&e1y#(qqCkE(0lik_U>wg8vOhGR(=gBGFSbR`mh zn-%j3VTD4 zwA1Kqw!OSgi_v0;6?=Bk4Z{l-7Fl4`ZT535OC{73{rBwpNHMPH>((4G`sh zZhr!v{zM@4Q$5?8)Jm;v$A2v$Yp9qFG7y`9j7O-zhzC+7wr3Cb8sS$O{yOFOODdL) zV2pU{=nHne51{?^kh%a$WEro~o(rKQmM!p?#>5Pt`;!{0$2jkmVzsl|Nr^UF^IHxG z8?HmZEVMY~ec%Ow6hjfg6!9hCC4xY?V;5Ipo-myV=3TmfT^@XkKME`+=_inm4h7ki z->K~a+20?)zic^zc&7h=0)T{Aa24FU_}(O|9DMW3Bf>MW=O%~8{unFxp4}B+>>_KN zU%rKs3Va&&27&OX4-o&y2ie|sN2p-=S^V<2wa2NUQ4)?0e|hgna*1R7(#R_ys3xmG zE#(ry+q=O~&t|RX@ZMD`-)0QmE*x%SBc(Yvq60JtCQ4RL(gdA(@=}0rYo5yKz36bW zkvLOosP6I?7qH!rce(}q@cH-{oM2ThKV2RZe+{{25hkc?T>=Tky12xHr0jmfH@SZi zLHPJ@^Oo^Zo%`gZk_hrbCzS+t|=O!Bt zWi|>M8mz~sD|Z>C1ZPf_Cs&R!S5E2qK+@j*UpP>;5_|+h+y{gb=zub7#QKSUabet# zFH2H0ul;zO+uc+V=W_W@_Ig-791T7J9&=5)wrBE?JEHS_A6P~VQ)u6s1)Pu|VxP(aYJV*(e<)(42R zm3AK>dr1QLbC1RMoQ|M5k+TWBjY9q+_vY=K-tUte35m4RWl51A<4O0ptqV3)KzL7U z0gpp-I1)|zvtA8V7-e-o9H)lB_Rx6;Bu7A2yE)6)SuDqWDs}~Ojfk?DFwI% z3E1(>LbbB7I(&E@B7nlulhvY=Wa1mGXD@ijD7WF^y@L1e55h)-hzoq}eWe!fh9m3V{)x^6F8?ed1z>+4;qW6A4hYYj zZCYP=c#I8+$pAIVyiY*#%!j3ySAnH`tp|=^lh{)#JimWaP_rXK40A0WcsEUj`G1}O zG?XQ~qK4F!lqauv6-BL_Up3+-l1=kVfD;D*C)yr>o9>W=%mIyATtn_OBLK+h@p)j5jRAb;m&Ok?TZH-5Q)~#UwdYFp~rEE{judWa9E)z zE>135C-xMdHYY&AZGR)tb`K}s0CK9 z1!))p^ZaUC*e50t`sL+)@`)#kJ}?C_cCMH@k{f4wh~0`OFnGQ2nzUuuu;=r4BYRcI z){G#a6Y$S(mIc6B#YS;jFcU{0`c)Raa$nG+hV(K|2|^ZWOI566zlF0N;t~$jD<_AX zjnD?HN-G>xRmHwtL3BcJX7)Q^YGfc?cS4Nj=yYl5MB(uBD?r@VTB|mIYs=au$e)e{ zLHWd!+EN*v2*(=y%G1JzyQdY&%|?~R5NPb)`S2dw1AJW8O;L=p?yVxJs=X?U#-l1O zk6xh8yyY;OTR7aF{P=kQ>y`*EFivnw%rQioA-I67WS+~hVamG4_sI)(Jo4vHS|@F@ zqrBHbxHd_Y8+?8Gfq=Z1O^Fs5moGayCHVUHY^8)^j)Aj*RB!S2-FA?4#-`puwBW`` zJ_6OQj(FGo8DotHYRKq;;$4xDn9=4rgw}5xvxhi)?n?W5{*%4%h9Tg)zlQl&fN~Z1)gL(Dn7X!P428I zwA+U-x5!cQ57g1N=2bLqAWF z!&cbvsD)dvYoqP5vaQz%rL@kv*J>0AMzWAKn~Mxi5g2GlI7qvVZo)Z5oj=#O!M&*O z`3O3)uvrjNTeremC}nW@(m%#E-sITB>j-!yBM#(=FN`~c#@XjL3e)SjR9&%QO%tUg zzGv=SLH()`ZIt?Ayym;9VG1Muq+a+7Zo+59?SuRu_`k>@S4!yS3roMnq+SDO?`C7V#2 z8vHf4&0k;{kLT)fa==7EILSu3e|ZnxtFO;1 zGqP-;Xo(>_QKcYUhsi-X72BqH#7Zb-TsiNIF>G9xOHT3XoA*qX^10+#XCU0)UO4_%A_s_vO=uDd3_Q%D{OsvLMW9wGvuuRnF52{2vH06D~7N672!bIMt@it_D}& zwjZ7gV!RzZ86*wbEB5cnMJRbEqMM{G!K)bfJjyPH^9nGnrOI9S{~!dm4~P#&b*~)h zCMwM8mR+y5i~E5*JAopwZ>F`=ORfA&IF%O8(aS<}^H6wcY1g^=lYLPtFpyvW9F z3;FCS-TGFYPr#Y$ue>}?rTYrmWr^VbUu>!eL$cEdh1e>5_UDnZ@Mu$l*KVo_NDEu^ zBn*!qVnzYv>t|<(>nt8%CoNPhN!qGP|sANRN^#+2YSSYHa>R1mss->c0f=#g@U58@? zA4sUbrA7)&KrTddS0M6pTSRaz)wqUgsT3&8-0eG|d;ULOUztdaiD3~>!10H`rRHWY z1iNu6=UaA8LUBoaH9G*;m`Mzm6d1d+A#I8sdkl*zfvbmV0}+u` zDMv=HJJm?IOwbP;f~yn|AI_J7`~+5&bPq6Iv?ILo2kk$%vIlGsI0%nf1z9Mth8cy! zWumMn=RL1O9^~bVEFJ}QVvss?tHIwci#ldC`~&KFS~DU5K5zzneq_Q91T~%-SVU4S zJ6nVI5jeqfh~*2{AY#b(R*Ny95RQBGIp^fxDK{I9nG0uHCqc-Ib;pUUh$t0-4wX*< z=RzW~;iR3xfRnW<>5Jr5O1MP)brA3+ei@H8Hjkt7yuYIpd7c-4j%U=8vn8HD#TPJo zSe+7~Db}4U3Y^4dl1)4XuKZ67f(ZP;?TYg9te>hbAr4R_0K$oq3y5m-gb?fR$UtF9 zS~S^=aDyFSE}9W2;Okj%uoG-Um^&Qo^bB#!W?|%=6+P>``bumeA2E7ti7Aj%Fr~qm z2gbOY{WTyX$!s5_0jPGPQQ0#&zQ0Zj0=_74X8|(#FMzl`&9G_zX*j$NMf?i3M;FCU z6EUr4vnUOnZd`*)Uw#6yI!hSIXr%OF5H z5QlF8$-|yjc^Y89Qfl!Er_H$@khM6&N*VKjIZ15?&DB?);muI`r;7r0{mI03v9#31 z#4O*vNqb=1b}TjLY`&ww@u^SE{4ZiO=jOP3!|6cKUV2*@kI9Aw0ASwn-OAV~0843$1_FGl7}eF6C57dJb3grW)*jtoUd zpqXvfJSCIv4G*_@XZE?> z4Lt=jTSc*hG3`qVq!PVMR2~G-1P{%amYoIg!8Odf4~nv6wnEVrBt-R5Au=g~4=X|n zHRJGVd|$>4@y#w;g!wz>+z%x?XM^xY%iw%QoqY@`vSqg0c>n_}g^lrV))+9n$zGOP zs%d&JWT2Jjxaz`_V%XtANP$#kLLlW=OG2?!Q%#ThY#Sj}*XzMsYis2HiU2OlfeC>d z8n8j-{Npr1ri$Jv2E_QqKsbc$6vedBiugD~S`_0QjTTtX(mS}j6)6e;xdh*sp5U0aMpuN}qTP=^_Qn zh~0padPWs&aXmf6b~}{7Raglc)$~p?G89N4)&a}`izf|bA)IUmFLQ8UM$T!6siQxr z=%)pPsWYXWCNdGMS3fK6cxVuhp7>mug|>DVtxGd~O8v@NFz<+l`8^#e^KS3})bovWb^ zILp4a_9#%Y*b6m$VH8#)2NL@6a9|q!@#XOXyU-oAe)RR$Auj6?p2LEp*lD!KP{%(- z@5}`S$R)Kxf@m68b}Tr7eUTO=dh2wBjlx;PuO~gbbS2~9KK1szxbz$R|Frl8NqGn= z2RDp@$u5Obk&sxp!<;h=C=ZKPZB+jk zBxrCc_gxabNnh6Gl;RR6>Yt8c$vkv>_o@KDMFW1bM-3krWm|>RG>U`VedjCz2lAB1 zg(qb_C@Z~^cR=_BmGB@f;-Is3Z=*>wR2?r({x}qymVe?YnczkKG%k?McZ2v3OVpT* z(O$vnv}*Tle9WVK_@X@%tR^Z!3?FT_3s@jb3KBVf#)4!p~AFGgmn%1fBbZe3T53$_+UX_A!@Kz63qSLeH@8(augJDJ;RA>6rNxQYkd6t(sqK=*zv4j;O#N(%*2cdD z3FjN6`owjbF%UFbCO=haP<;Y1KozVgUy(nnnoV7{_l5OYK>DKEgy%~)Rjb0meL49X z7Fg;d!~;Wh63AcY--x{1XWn^J%DQMg*;dLKxs$;db`_0so$qO!>~yPDNd-CrdN!ea zMgHt24mD%(w>*7*z-@bNFaTJlz;N0SU4@J(zDH*@!0V00y{QfFTt>Vx7y5o2Mv9*( z1J#J27gHPEI3{!^cbKr^;T8 z{knt%bS@nrExJq1{mz2x~tc$Dm+yw=~vZD|A3q>d534za^{X9e7qF29H5yu};J)vlJkKq}< zXObu*@ioXGp!F=WVG3eUtfIA$GGgv0N?d&3C47`Zo)ms*qO}A9BAEke!nh#AfQ0d_ z&_N)E>5BsoR0rPqZb)YN}b~6Ppjyev;MMis-HkWF!az%G? z#&it84hv!%_Q>bnwch!nZKxB05M=jgiFaB^M=e-sj1xR?dPYUzZ#jua`ggyCAcWY> z-L$r#a{=;JP5X}9(ZPC&PdG~h5>_8SueX($_)Qu(;()N3*ZQH(VGnkWq^C}0r)~G3_?a10y*LsFz zokU5AKsW9DUr-ylK61shLS#4@vPcteK-Ga9xvRnPq=xSD_zC=Q_%6IuM?GpL(9aDx z|8d_;^6_D4{IQ1ndMAcFz5ZaT+Ww0wWN`xP(U#^=POs(BpKm;(H(lmYp+XCb7Kaw0 z;LT945Ev3IkhP6$lQBiMgr+vAL}{8xO&IObqJBEP4Y^x&V?iGC=1lVIbH^Z!eXxr@ zz)D7Fon`z~N|Pq>Bsue&_T9d;G+d8#@k^cq~F^I8ETsZ*cGOf*gZ4ghlAzW|aZ;WA13^B!Tlr0sWA zosgXD-%zvO-*GLU@hVV(bbQ`s@f~Ux=4}(@7O)%o5EH((gYflccBC@jbLF3IgPozv zglX2IL}kL1rtn4mu~`J(MMY83Rz6gc1}cX4RB+tZO2~;3FI# z@dU(xa5J_KvL0)oSkvwz9|!QcEA$jKR@a-4^SU3O449TrO+x$1fkBU<<=E_IHnF6> zPmZ7I2E+9A_>j6og$>Nih~b2F_^@6ef|Hm-K2(>`6ag{Vpd`g35n`yW|Jme78-cSy z2Jz7V#5=~u#0eLSh3U4uM3Smk31>xEh^-Os%&5tK6hSAX83jJi%5l!MmL4E?=FerNG#3lj^;-F1VISY!4E)__J~gY zP{o~Xo!8DW{5lsBFKL~OJiQoH>yBZ+b^};UL&UUs!Hbu7Gsf<9sLAsOPD4?-3CP{Q zIDu8jLk6(U3VQPyTP{Esf)1-trW5Mi#zfpgoc-!H>F$J#8uDRwDwOaohB(_I%SuHg zGP)11((V9rRAG>80NrW}d`=G(Kh>nzPa1M?sP;UNfGQaOMG1@_D0EMIWhIn#$u2_$ zlG-ED(PU+v<1Dd?q-O#bsA)LwrwL>q#_&75H)_X4sJK{n%SGvVsWH7@1QZqq|LM`l zDhX8m%Pe5`p1qR{^wuQ&>A+{{KWhXs<4RD< z=qU6)+btESL>kZWH8w}Q%=>NJTj=b%SKV3q%jSW>r*Qv1j$bX>}sQ%KO7Il zm?7>4%Q6Nk!2^z})Kchu%6lv-7i=rS26q7)-02q?2$yNt7Y={z<^<+wy6ja-_X6P4 zoqZ1PW#`qSqD4qH&UR57+z0-hm1lRO2-*(xN-42|%wl2i^h8I{d8lS+b=v9_>2C2> zz(-(%#s*fpe18pFi+EIHHeQvxJT*^HFj2QyP0cHJw?Kg+hC?21K&4>=jmwcu-dOqEs{%c+yaQ z2z6rB>nPdwuUR*j{BvM-)_XMd^S1U|6kOQ$rR`lHO3z~*QZ71(y(42g`csRZ1M@K7 zGeZ27hWA%v`&zQExDnc@cm9?ZO?$?0mWaO7E(Js|3_MAlXFB$^4#Zpo;x~xOEbay( zq=N;ZD9RVV7`dZNzz+p@YqH@dW*ij8g053Cbd=Mo!Ad8*L<5m1c4Kk ziuca5CyQ05z7gOMecqu!vU=y93p+$+;m=;s-(45taf_P(2%vER<8q3}actBuhfk)( zf7nccmO{8zL?N5oynmJM4T?8E))e;;+HfHZHr` zdK}~!JG}R#5Bk%M5FlTSPv}Eb9qs1r0ZH{tSk@I{KB|$|16@&`0h3m7S+)$k*3QbQ zasW2`9>hwc)dVNgx46{Io zZ}aJHHNf1?!K|P;>g7(>TefcLJk%!vM`gH8V3!b= z>YS+)1nw9U(G&;7;PV4eIl{=6DT^Vw<2Elnox;u@xF5ad*9Fo|yKgq<>*?C$jaG2j z|29>K)fI^U!v?55+kQ*d2#3}*libC4>Dl4 zIo3Jvsk?)edMnpH<|*l<*0Pf{2#KedIt>~-QiB{4+KEpSjUAYOhGDpn3H_N9$lxaP ztZwagSRY~x@81bqe^3fb;|_A7{FmMBvwHN*Xu006qKo{1i!RbN__2q!Q*A;U*g-Mz zg)-3FZ`VJdognZ~WrWW^2J$ArQAr1&jl~kWhn+osG5wAlE5W&V%GI{8iMQ!5lmV~# zeb3SKZ@?7p;?7{uviY6`Oz16t0=B70`im=`D@xJa16j2eHoCtElU*~7={YUzN41sE z#Th>DvJq-#UwEpJGKx;;wfDhShgO0cM|e!Ej){RX#~>a?)c2|7Hjhh2d=)VUVJL<^Aq|>_df4DX>b9W2$_DM zTjF#j(9?Co`yor?pK<16@{h#F&F8~1PG|qQNZPX^b!L*L&?PH#W8za0c~v6I2W($Jderl%4gufl z#s;C*7APQJP46xHqw;mUyKp3}W^hjJ-Dj>h%`^XS7WAab^C^aRu1?*vh-k2df&y9E z=0p*sn0<83UL4w30FqnZ0EvXCBIMVSY9Zf?H1%IrwQybOvn~4*NKYubcyVkBZ4F$z zkqcP*S>k6!_MiTKIdGlG+pfw>o{ni`;Z7pup#g z4tDx3Kl$)-msHd1r(YpVz7`VW=fx9{ zP}U8rJ-IP)m}~5t&0Y$~Quyjflm!-eXC?_LMGCkZtNDZf0?w<{f^zp&@U@sQxcPOZ zBbfQTFDWL_>HytC*QQG_=K7ZRbL!`q{m8IjE0cz(t`V0Ee}v!C74^!Fy~-~?@}rdn zABORRmgOLz8{r!anhFgghZc>0l7EpqWKU|tG$`VM=141@!EQ$=@Zmjc zTs`)!A&yNGY6WfKa?)h>zHn!)=Jd73@T^(m_j|Z;f?avJ{EOr~O~Q2gox6dkyY@%M zBU+#=T?P8tvGG|D5JTR}XXwjgbH(uwnW%W?9<-OQU9|6H{09v#+jmnxwaQ-V;q{v% zA8srmJX7Fn@7mr*ZQ@)haPjWVN@e3K z_`+@X$k*ocx*uF^_mTqJpwpuhBX~CSu=zPE(Sy%fYz&lzZmz3xo4~-xBBvU0Ao?;I-81*Z%8Do+*}pqg>bt^{w-`V6Sj>{Znj+ z70GS2evXinf|S#9=NNoXoS;$BTW*G0!xuTSZUY45yPE+~*&a-XC+3_YPqhd*&aQ>f z$oMUq^jjA;x#?iJKrpAqa<2<21h*_lx9a}VMib;a6c$~=PJOj6XJXJ|+rc7O7PEN5uE7!4n9nllo@BI4$VW2Nf_jqnkz%cvU4O4umV z#n6oXGWOt3tuIjmX*b!!$t~94@a@QgybLpQo3icAyU`iNbY~XNAArFAn$nFJ()d-U zFaO#nxxVF-%J{UB**uRo0*+?S>=^il)1m7v-u`PDy*ln%|3E-{3U~R=QcE&zhiG_c zDnGMgf1}3h1gWz8IV0Oc7FmEt>6W?Eva;J`(!;IIny}PvD?vztz`F6su_tUO`M%K5 z%C#=nXbX})#uE!zcq2mB;hPUVU1!`9^2K303XfOIVS{mlnMqJyt}FV=$&fgoquO+N zU6!gWoL%3N1kyrhd^3!u>?l6|cIl*t4$Z$=ihyzD7FFY~U~{RaZmfyO4+$kC7+m zo+-*f-VwpUjTi_Idyl~efx)!$GpE!h+in4G1WQkoUr<#2BtxLNn*2A>a-2BL#z%QO@w0v^{s=`*I6=ew2nUj1=mvi%^U@2#Wf& zs1@q6l8WqrqGm!)Yr|*``||#A+4#du6`mR^_#?CymIr}O!8Zm?(XY$u-RGH;?HFMGIEYVuA1& z`3RlG_y0%Mo5w@-_W$E&#>g6j5|y1)2$hg(6k<{&NsACgQQ0c8&8Tdth-{@srKE*I zAW64%AvJJ+Z-|I~8`+eWv&+k8vhdJk5%jolc%e`^%_vul0~U8t)>=bU&^ z6qXW&GDP%~1{L1-nKK>IsFgDJrh>!wr3?Vu-cmi#wn`;F`$GNc_>D|>RSuC8Vh21N z|G;J1%1YxwLZDD400Ggw+FirsoXVWYtOwg-srm}6woBb!8@OIc`P$!?kH>E55zbMB z8rdpODYfVmf>cF`1;>9N>Fl(Rov!pm=okW>I(GNJoNZ6jfIunKna-h6zXZPoZ9E2PythpyYk3HRN%xhq2c?gT$?4}Ybl42kip$QiA+ab zf-!EqBXkT1OLW>C4;|irG4sMfh;hYVSD_t6!MISn-IW)w#8kgY0cI>A`yl?j@x)hc z=wMU^=%71lcELG|Q-og8R{RC9cZ%6f7a#815zaPmyWPN*LS3co#vcvJ%G+>a3sYE`9Xc&ucfU0bB}c_3*W#V7btcG|iC>LctSZUfMOK zlIUt>NBmx6Ed}w_WQARG+9fLiRjS1;g49srN1Xi&DRd|r+zz*OPLWOu>M?V>@!i49 zPLZ3Q(99%(t|l%5=+9=t$slX0Pq(K@S`^n|MKTZL_Sj+DUZY?GU8sG=*6xu)k5V3v zd-flrufs*;j-rU9;qM zyJMlz(uBh0IkV<(HkUxJ747~|gDR6xFu?QvXn`Kr|IWY-Y!UsDCEqsE#Jp*RQpnc# z8y3RX%c2lY9D*aL!VS`xgQ^u0rvl#61yjg03CBER7-#t7Z++5h_4pw{ZZ~j0n_S_g zR=eVrlZDiH4y2}EZMq2(0#uU|XHnU!+}(H*l~J&)BUDN~&$ju@&a=s$tH5L`_wLeB z944k;)JIH^T9GEFlXiNJ6JRymqtLGZc?#Mqk2XIWMuGIt#z#*kJtnk+uS;Gp}zp$(O%LOC|U4ibw%ce-6>id$j5^y?wv zp1At~Sp7Fp_z24oIbOREU!Mji-M;a|15$#ZnBpa^h+HS&4TCU-ul0{^n1aPzkSi1i zuGcMSC@(3Ac6tdQ&TkMI|5n7(6P4(qUTCr)vt5F&iIj9_%tlb|fQ{DyVu!X(gn<3c zCN6?RwFjgCJ2EfV&6mjcfgKQ^rpUedLTsEu8z7=q;WsYb>)E}8qeLhxjhj9K**-Ti z9Z2A=gg+}6%r9HXF!Z~du|jPz&{zgWHpcE+j@p0WhyHpkA6`@q{wXl6g6rL5Z|j~G zbBS~X7QXr3Pq0$@mUH1Snk^1WJ0Fx2nTyCGkWKok$bJZV0*W?kjT|mkUpK<)_!_K^OoTjMc+CWc^~{ZP8vgm`f&=ppzKtw}cxwV^gppu}^df1|va7Q?@=(076-( z4KJVmu?l(aQwmQ*y_mke>YLW^^Rsj@diLY$uUBHL3yGMwNwb7OR3VD%%4tDW(nC984jBWCd90yY(GEdE8s(j>(uPfknLwh!i6*LX}@vvrRCG`c?EdB8uYU zqgsI4=akCeC+&iMNpVu56Fj2xZQHs6SdWssIF#Q@u@f9kab0&y*PlG+PynjHy`}GT zg%aTjRs2+7CknhTQKI%YZhFq1quSM{u24Oy2As@4g(bpbi%y1i0^TwI)%1Whpa~qE zX4MD(PgFEK@jZBPXkFd437aL6#COs$WrNT#U=er-X1FX{{v9!0AS$HR{!_u;zldwY zKko!`w2u@($c&k_3uLFE0Z*2vms?uw1A{AqZw^jwg$|D7jAY20j`s*l##=4Ne_K5) zOtu6_kziEF@vPsS7+@UwqOW6>OUwF$j{r4=nOSf-{UC(rEKidie7IUn>5`UoNJ9k) zxJXXEBQifng+Pte3mPQ76pVlZ<`jnI##F1*YFA*)ZCEncvgF-%)0dUXV*pXTT^L`n zL=?A5Vty#{R9W4K)m$`me~*_(&a88M?Eon$P-YdVG}#Gq4=hh#w=`>8f`9}}zhv;~ za?I=Gb3v$Ln?-SDTBow0J5Tt&xPlw|%`*VTyVee1Oh<-&;mA|;$ zoPl;^f7Q~}km#_#HT2|!;LEqORn%~KJaM)r#x_{PstSGOiZ!zX2c}^!ea3+HSWrwE z=6SJ!7sNDPdbVr#vnUf}hr&g@7_Yj&=sY=q(v^BwLKQm|oSB}172GpPlj?a3GqX#B zJko4zRRttIY>Fv#2b#A<_DLx=T@eUj+f}!u?p)hmN)u4(Jp(`9j58ze{&~rV?WVbP z%A=|J96mQjtD037%>=yk3lkF5EOIYwcE;uQ5J6wRfI^P3{9U$(b>BlcJF$2O;>-{+a1l4;FSlb z_LRpoy$L%S<&ATf#SE z;L?-lQlUDX_s&jz;Q1Lr@5>p_RPPReGnBNxgpD!5R#3)#thAI3ufgc^L)u%Rr+Hlb zT(pLDt%wP7<%z(utq=l%1M78jveI@T$dF#su(&>JkE(#=f4;D54l*%(-^(nfbCUQe)FV9non9F%K+KZ(4_`uOciy82CO)OolxisUd0m^cqueIRnY< z;BgA4S1&XC3uUP?U$}4o&r|0VCC7fkuMZBa|2n4asR>*5`zBaOJPWT$bNn(W_CK%L$c2AsfSlwq?A8Q6 zhK&USSV=^-4vZ^5<}pnAOb&IKseHNxv_!|B{g@d^&w%{?x;i3iSo)+vt^VnMmS!v) zM)W)05vXqzH5^hOWWw~$#&7HoIw}}DD3bCQgc=I8Rv|G5fM8O^58?--_-*>%Nwk)j zIfvfok0n05!w%tZ=-dpffezI7(+}yX5XhwYk#0@KW%PkR;%#t|P6Ze_K*N6ns%jOt zNeW(bRsv0BK7ah~9U~UBAVA_L34F+;14x6-;I|o=%>?sS3@dpRv|GKxilsa#7N#@! z!RX~>&JX&r{A^^>S~n_hPKkPR_(~~g>SuPj5Kx6VI%8BOa(Iit&xSMU8B#EY-Wr?9 zOaRPw0PEbVSW@Wk{8kkVn34;D1pV2mUXnXWp{V-M9+d}|qfb6F`!a9JQO_-wlH?zf z4Sn0F4-q-tzkaJ?1fV0+cJBF$f0g6*DL6U3y`Tr`1wzCiwY#muw7Q-Ki)uN}{MoCWP%tQ@~J4}tyr1^_bV9PScNKQHK=BZFV!`0gRe?mVxhcA4hW5?p0B<5oK+?vG^NM%B%NDOvu0FMq#)u&zt_-g&2 z7?z%~p&32OAUSQV{<=pc_j2^<;)`8$zxCEomh=rvMiliShS?ahdYI1grE-M&+qkK_ zD=5Hexi<&8qb4hgtgj81OD(tfX3EJSqy9KFcxpeBerG`apI4!#93xpEFT??vLt>kf zac28;86CpMu=BWIe$NOT~+Es!y#+$ zvm2s*c`J9Gy*ERvLSI<9<=j*O=0xUG>7rYh^R4bGsvz;j-SBO|P^OQ1>G9_akF}D; zlRmB@k3c5!s|Vz3OMZ8M*n0AMTiSt5ZpRy+R1|ckna&w`UQjklt9f&0Z~=->XImVA zLXizO2h=<|wM~w>%}3q1!E{oSq7LBPwQ~93p-peDq-W?wCm8NOKgTSz-P)|cm}S5&HBsx#C@Ba5;hzi#Yw@y-kC~)@u4}Rf?KV0$lPjv}} zcFpNy=YJfsS||9&!-JFjw=@NU96ESzU^gme0_oNy?})II`>Sy>bUCHs_(m&)vn^&isCl+`F~qu8elAO z)-ZP7`gYE2H(1)5tKalz&NJbcutAU&&JFV~$Jrai31^j>vZ|HV1f}#C1<5>F8 zS1RWIzM%b{@2dAF^$+i4p>TC8-weiLAPN+Aa#(bxXo9%Vz2NEkgF&s#_>V?YPye^_ z`` z-h3Cv^m6K%28I$e2i=cFdhZN?JTWhqJC{Q9mg0Vg|FiPEWDl&K)_;Bz_K`jH7W7QX^d$WQF*iF@#4_P*D36w9&iJr2E{w?LRFapwZIIVHGH ziTp*5>T{=;(E}z{1VL4;_H`BAXA~&zpeWX!gN9m|AfcJ{`!XVz48O^&+0Gd|w;udP zzU|DbGTS|7qZoEoDZEH9Kb0%DZvCaWDzuJ=8jZz}pqPn+I!c_+*~>m>BQqN2560*< z$6sx_y8WRqj$SugYGip+et$;iJ!SQAx=HgVSh_3e)MOFHuXD@sg>Yi_p8Sh`{lP=5 zo?AFv1h;KqR`Yj!8Pjji3lr+qae2|a1GmlxE*su%_V)K0Xu0(#2LcO!*k11w*V12$ z;f~i{kI#9PzvFLZ3pz@d558HeK2BTvk*JvS^J8L^_?q4q z);;4Z!DsV!P*M>F>FiF*{|p_nUgy;pDh?J8vwO;emgOAAcxrgDXiSDS5ag?0l*jj< z(khZ3-)>eiwPwpb6T9meeL)!2C-K@z9fF`0j|t@;^f5+dx86R3ZM{bnx9Hm1O$s)N zk$OvZR0u2`Z^QP8V%{8sEhW~_xbZMad2jtz&0+ekxmp;9`ae;_f%-ltk5E%)VT*a6 zRbMnpCLPnalu+1TafJ4M0xNV8g}U4Mjk{le6MA|0y0rk)is}M%Z9tUU22SvIAh7`w zTysd{Pztfkk=jD^*!lA+rBcqb)Fx`A5iaU2tl&XdL1D)U@pLEXdu%#YB*ol1N?4ti zHBQcU#_%UqiQ1)J^u-ovU@-7l?`YzYFvA2#tM0mEh3?CpyEh_NUuVajD16t zyg$C*5du9R=K~6mCJ`W+dFI$9WZZauO)p2H)*SKpHVsIu2CxfJvi2>; zcit#57RP7DpSwMF-VBm|4V5d=tRgX7RM9%KQ0JRo6d<)RmiIPWe2zh6tmswP`fs^) zwy};#jk|NXMqCSfwIR3QZ#W2`(%sJ>qvk=53CYoLmQt9q|2Gm$sB;rEuBqGJA1OUM zoyl4Wy-HYn0J6L=cad8o)R!Ea^;`rSMg9hYo3?Fw6B9dUq75a-MSb56n8~AAsS(JP zZ!1khPu}!GRpsj+jvl`N1tDD8m1myJCI3c-c<9U-1Vg`xJO~}5_wvPXYh^=Boo^|V z3Tp}|lH!9m4Ipa_$p;b8fjUd=zc4iO7vr)M&Xs0_m$fgY@+hB9%K~4*9$p0d)m2bO ze5JH`W0fnIKdcW!oO#^g1YceSQ4u->{>u@>tLi!fky)o&$h(=he?Fe_6?}O~iSf(F zV&(P~*5h>BW{3e1H%8*7#_%L1#>W97b0@jHtliES^w6w5oldI7QL+?I(Pl$DaN>~d5nXx z;CO1E+S?3E2PLq~)-?ygkHAO1m&hOYmj7?;2XM!$D^f0l9K4P{n}mgb{CoYH6RJ8o ztydc6dNqA)`CG?=Gd~EIbi`UM)eyzGF^+i?&TOdyW~mFH_^Gye(D}clDVFQ@V2Tvy z7rQIaq8Xx`kC;AO-_{k%VI2e6X@bIy^mupEX%{u0=KDUGu~r6lS*7GOeppy{&I&Ly zjOTz=9~jC|qWXznRbrfjg!1`cE!Hzyjzw6l{%>X)TK(UEGi9Uy3f9D6bbn0gT-s`< z8%$Msh!^8WidX7S;)n2jh_n1-QCtSyOAKcPQc(Xlf0*Q|5CSBjo(I-u!R0GJgzTkL z|6QdQRrUMbUO|q0dQ%+d^4)*Mjbm$R}RUcz(7|E0Bq-bAYY@)OsM<+2>}CV zzPBgeD~kBHE(Y+@l2orJrdtV7XXq_V8IETas%7OCYo`oi)+h&v#YN!Qpp7drXFS>6 z?r-q7px+(rIy+bo1uU#I2A5s@ASe01FgGMbouFkhbkm-9yZ8Q2@Q1vuhDQ3D3L+zA z(uz8^rc24VmE5r0Gbd;yOrXnQKAEBfa3@T7fcF$#QYv^00)VZPYehpSc@?^8we}o{ zlX0~o_I<`xSfI8xF(WXO-DX1>wJ`XN?4rw@}_RLD*${$}UaXL=oM(=SDMIxZj1Ji#jAcrH7nYG`r z#ewodj>F5Bf9j(j`a;>)=*2j_ZN}vf!~Hq`2Eyt;9UH1_(yjq1OUO(1M0lI3FZ2j-fU9)L59v&OiQ>5$;d!jg?Fo{Svf5t5FCZbb?)* zJN=Q!?2BztV$7)CWtG0MO~Lr4E5>aoHD5N4(+@~gQEbZTc4s3HrIl_G23PCng4Y3f zbLZK1A-x9x!)WwuI=UBkQ5QyE^&Nrw?@fsRKK41G9-xq=#VyO%CEo`{_eioDj%M!3x=>I zfOPFiFX{1t-|+3E@?UuK=0miGN04hW0=JnJrEyWw{Bg-jMvAA}cg<5LN1c5BQdrIZ z#+bxj9Jbu`11@IUjU|RKfL(UzRlVB4XT ze|(WaxL$KiRqkgCr3^Al(19!_Y7b=E(4Xm7LCO$y5+k;Fu6B#=OSzW`-7p{zRv-_) zPr!|km?8aF}+3hm)QG92YaI+jctX&5IrvTUGf{Y$)TK6)s9v!SMhU=HIpEC~2 z4>o14mG$El2sTA(Ct?xS!l*x7^)oo}|3+BF8QNe;bBHcqdHVmb?#cbS*NqZ%mYS~z z`KLoq7B#KULt%9a#DE%VTEo4TV03T2nr`FK5jUTA$FP0JH6F9oD*|0z1Yf2b5?H0_ zD|K|_5Zk`uu?ZN0U! z_mL>>F;mnHU=@to!Vv*s4;TQr9y)L@1BXXz^a85NSifPTL4h6I>+m_S3~FkXB{N?E zS<3ue_(wqaIS5;4e9{HB`Okl9Y}iFiju+oTqb)BY)QT?~3Oag7nGu-NB5VCOFsiRs zs@m%Ruwl^FuJ1b}g^=*_R?=SYJQ@7o>c9j>)1HgB zyN9LI9ifwu{Shlb6QO2#MWhxq~IG!U^I!6%5}(sbi>=bq8!8@s;4Iaun#kvh7NPwX34Rjbp2f!D)cF&sNIO%9~;C`cs&ZY2=d@c3PpN$YZjUT}X7rY`dlWX$yc znw(7=fzWapI=KzQnJ(6!o0K_aDk!^dZ#)pSTif+jQtQXga$bPApM z=);jZ5c*?*GoeGMnV0=RrZucRRYBjx>tx`A3OuY)#tp2w7mh}&kj)SKoAvbbf;uO! z?+RItUow0xc*6StuO4D--+qY!o}Isy}s;ts5aM5X~eJUZoLOq@dGv=a4hHJD<* z5q{dZSN{bv_(Vj#pFm7Q<$C;MwL|Qizm~QCFx~xQyJoCOZ$`sYD}}q>PwRZjb<=E< zAeMP?qVfM>xu2}Il2xT6={KBdDIstxY-`5IWXN zUiWV&Oiy5R_=2X9Y$ug9Ee=ZSCaza!>dWBMYWrq7uqp>25`btLn^@ydwz?+v?-?2V z?yVwD=rAO!JEABUU1hQ|cY+_OZ14Hb-Ef`qemxp+ZSK?Z;r!gDkJ}&ayJBx+7>#~^ zTm<>LzxR^t-P;1x3$h;-xzQgveY$^C28?jNM6@8$uJiY81sCwNi~+F=78qJZ@bIsz1CO! zgtPM~p6kaCR~-M>zpRCpQI}kUfaiZS`ez6%P6%*!$YCfF=sn}dg!593GFRw>OV2nQ ztTF6uB&}1J`r>gJuBP(z%KW{I^Uz%(^r5#$SK~%w1agl)Gg9Zy9fSK0kyLE24Z(34 zYtihZMQO^*=eY=<5R6LztHaB1AcuIrXoFuQ=7&C}L{c?Z$rto$%n=!whqoqG>#vvC z2%J5LVkU%Ta8hoM($p1WqN}wurA!d@#mQGU5Nb>~#XC84EYH)Zf&DZR!uY+-;VqS< z@q?$ggdX#auS#%%%oS^EN)?JhSR4JYpSgGRQZD<9!YvvF+zp0>C#$!x*x}l8U|Bb& zv?v*im5Bq_(5Wi40b1^nKun$XTST(a8yOAcqQZmKTgGLo)Ig6JuEh5J9NnqJXin@Gxzz-k6xXWYJ&@=JZw=$+ zFPGde%HsR`gI+y`rtiPaMYwbtyp!sVb!pX~;c3zLoPO0eaZSV+O_z z%9H@UhqNowzBTPcMfL6kC>LRaFF6KVaSv1R@%4}rtleX!EMnL`rethYrhTLj1x$tj z;)H!fKo08&T(;i|FT&rPgZ*D0d=B2dXuO_(Uaoi9+vEhs4%{AD{Fl@4^|`X=PvH(s zI7$6bWJiWndP$;&!kSCIR1l57F2?yzmZm~lA5%JKVb;1rQwj*O=^WW~`+n*+fQkK0 zydInOU1Be2`jhA!rnk1iRWR=1SOZpzFoU5{OPpc&A#j6Oc?D&>fAw=>x@H7?SN;d^ z-o&}WR;E|OR`QKItu(y4mT)%Pgqju-3uyH?Y@5>oSLO2Y(0(P!?_xOL=@5+R7rWw# z3J8%Hb@%Pzf^`=J6fEJ_aG6+e7>OUnhaO1(R1<6>f}L z?d@Wnqw9?^;2?q(b@?Wd=T6r_8a@Z4)*_@Q7A`+ zW3w?j!HW0KbhxF%D`9d2HpvIrBxM!36W3Yh5=8_0qYfnHm*yiLB?Ay|V10N%F9XYq zanaDtDk$rS+|_H_r|a${C}C7b{E)Ii20-a?Grff$E?&|gWF<#Ern2GqhCiS0~Y%knIi8zY^lE4qLaR-3M;_Rkz(s;wu z9207W1PXIe#4h4Zw}dvdV&FYcnUlD5_C4hzJ@bPSBVBLpl$&52mi+wwH;svyVIzAB zoA+NQ;Hpqh?A}^Et~xhl>YQNQwh20!muW{ zq}|Pg3jHZWnDBN?r1KhiVG$%Sm-4+=Q2MZzlNr3{#Abqb9j}KK%sHZj{Vr2y4~GIQ zA3Mz1DjQ3q(CC~OyCaZn0M2!){)S!!L~t>-wA&%01?-*H5?nzW?LJB`{r&)vLB4!K zrSm({8SeZ0w(bL9%ZZAZ*^jf=8mAjK^ZR0q9004|3%73z#`-Npqx*X^Ozbja!C1MW z-M~84#=rU1r>p{+h9JU<#K_x$eWqJ+aP%e?7KTSK&1>dlxwhQmkr69uG~0iD@y|L- zlY0vSR2|IhZoS6PpfUai_AhKo2HfdD&mhv#k51CX;T z*sU)XbDyfKjxYC$*_^(U)2-c0>GJ(zVm$CihHKlFSw&1A$mq$vsRt-!$jJe3GTaZ6 z3GcVvmwZ0D>`U+f3i*pQ>${p1UeyF~G9g~g-n{ThVOuC#9=ok`Zgz@qKCSN!1&P`N z=pdlGNwal%9;)ujwWH*#K6CQG*fJDAQiKlO2vKJHeA1lj&WQC+VU^@ea8$#~UOX$*Q!V^8L- zL0$W5(Y3=??%&j_WUq6*x>=?BfmI*d8fmDF*-!XVvxL8p7$r+}Igd_(&`|D*;Z#GE zqm{tHx&aHBpXw&~l6>7-FlyiSPJtTJblAjLU5Ho$FeN0mDguFAq?r+6^~o6|b+rfE zGVcZ&O-X~tE3liGcdI~hHSCT+&F&uH8rr&f{6pr^1y5061`fu~=^_|Idrgti5+*U7 zQOb9G?Rz$j-G0Y}x+i{HB0!4ZmKzykB<0;Rbmo2)T4|VdcwujI_otLG@@8OOKg3kw zP|0ST0D4@zT?O=(0Pikp)Rpwxw_VsmW4!^j^sFd6r5l zw}SG_HQPs>ae%Bq{sye_SaBX%|F-}&^)Wz@Xi<)YNbO?lPs7z@3c;$b^Aw@>E%mOj zW^c%IdtC(Kk@s*}9NbKxEf8SZtP+32ZTxjnrNWS7;W&D~ft{QY?oqOmxlV7JP!kW!Yj`Ur{QbbM1h=0KMaIAmWiISb7TKd4=gMeo+Tcz2>e#NihnOV%iNdx` zeiuoOK^{}D+M+p(Y7EC=&-`$B0F< zQ=zHaM;&QQR4jM$sG=N&sqOvD_Bx*drQ6c@u0()g05cwl`Xm{!S_Nuaa2KlL*rmmk z51yPE)q?Bl$sNM474Y!=zZ zc{EVGpdJ!Su{Qq%llR5O6#zK8l(ld*UVl87@|iaH@C3+*;XBxjEg&fsQrzpMo3EEG zv*Tpms7a;7!|iz8WY7={0a$0ItO-(ajXl;wX_$$yzEF5k9nc>L3wv!p{8h2)G0W?h z{v6vH=7+>$Ho^+)9hDtCd+S_yh8pzS9$)hYev-=eDu?lGIR;-fgz+dr+wcmM-^dZp z9}`&kAf$~z1ovF)>Hgxc!Xe3cju-jQRluCm;c_1=PYQygb?Oxe z!QG0L3sT_k=WpfOPL#|EPlD^t;ENCC39O?tHd<(kfx7SOcxl+E#;ff19_+{vbkZSvbS$I{#>31KZj^$n%ayX0jj}EvsgnHg16P z_A6Y)pdp>kLW<;PtR*Vs#mVb%)ao7AXw{O&hBDmD;?mc3iMH;Ac@rZZ_BQa8CQ~|0 z&d1L{in-z--lBO|pxqc%bqy^~LAGv=E*eaVU~OeuVV{d`Vv#-_W7EYdTDzVraG9H+LC_dWcgZMn~KcP)XvKWbcr5&d+=a>{*(Ha6Y1$==bR z{O-?$7H;`2dt0B%Vm?6`_?ZOjJkyu9ZJsh^WH*+es&^@KDcR%Zej%3PJ*XovgyhTbaH(!H1H_OF~=*f55Jr8A%uW zz5IoAB~1e2-tDGp9}`MnavAMy?jgPM5F%y`%$}dFLrz_* zIrO=afT8+AkK5B1s3{ZDVP$g6y$-*U*=?-fh!cNyn3q6YhNhfRxW&GLIJ2#>9bYMD7-F%{|Iw%@a=DoAAU;3k9p$`V zImKm{5HU~wq|nQFwab)_7lNckW#1z2$|oW5x7vDbBURVjw8674P?L1ogMKpHoV>;# zO%*1OwI|($UOr#hL(*M~qsn3PF%_|15uc%Hy9@D>_~N|?<%lig6yKX0a#1s$o(^Laj8bF#5fGPOFMGmMiUaxSwE}Qf#SG_f79d2Iv=TFBXzTpr$^avJ?=|arh2<+ce}&248Kw0} zhlva`wD6X~s7|37la4FnFOgIHhBiFo`lw~?lSbk{>)P(3jyVhM4O)a=GX3(sW1vIC zz0mJ>;J{!eN5#nf2>$u=3Kq>`7u9QnChi8>CjONBN-b+W_UQIuN#{N$Q<$}IOvpQP zB&5ZrY{V&D=4)voh;6<1U`PFA>V%XUW73S9D^J>cQYfzIyIV5i35WNb5K9c^|M}=* zN_C3rnjCZP1^v{;EaGK7Tp5z~B#?f5NZaAsFUOLK)mI~bJTaL8DF_eRikE{%^J?y9-n_U32EKHPCkB^ZN2*zk{bC=GM%_I z61}nkr+Plg6S0V=mY>H_KQU&)P~=y3$#$*U8FunXkb_e1O-7t@m$5re%u!_G%^?_| zRIJzg+lX$}+ba|qx)Ec6c^ip;`_QfQrD~SPa4MoyRUOtX&~^XWcO^a}KBkXK9J{ZFOA~rovYa0!7btTC*=xNQrwJ)$Eu`TT$;%V&2@y@$ISdNn ztbM7|nO+U9r;ae{{;QiNEYpe4nrFq_x3 z4Tvf^b(I@_3odwhVe!aC0X&~inrYFu# zh)+eF__8ly&nLr4KlLWl%B_ZMo=zCH2QfO^$lJ zBvU*LQ#M(5HQ}2Z9_^y~i@C#h)1C*?N3v68pY+7DD09nxowdG#_AAM5z&*|-9NcB{ z_xKUY>Ya7>TO#Bat}yM}o(~8Ck^!QHnIj8N9}c*uyIs}IEqGn`xP;q3vhW6gsqUe>`m1 z)~ad@y1=?H`1SNl?ANCs5ZD`8tG&Hi=j|R%pP(%gB8pd)Q--E?hWU@)e?>SLV4s(- z!_I^oVC0x97@I(;cnEm$ttKBnI3gXE>>`K?vAq~SK?0YSBsx{@s1ZdiKfFb|zf}ju z7@rJb3mC{U`$R`YS(Z#KyxQx_*nU`kf;}QL%bw17%5~6!mMao^-{FFmX}|ItFuR~F zAAvTF%f4XKYo>2-PJ~ro@Ly#t@Sf69CrA+rmMRpihqH7V&SXX+$Sw`HZF`I*_3Vjz z%kPMyN0J3sl>X{-h12)j&XRhAAI;Aou%%z}gI>G+32z*qpZg{m`CezFrzg#&yc<1` z%j~}PN!F5Ddq(>R{+t0v{j6v^0XwWGu@5+`-$m`_>pCzM`r}wz*8Qv=$|P0R$%tJp z>D+N4GZ|Tg>XL<6XP9_wQRGDs^1icY*5GP4>*7mGMr;V zI%kT_^_SQml6$#uRE4Ps>}?ES)_XI8m-%GN{o^itb^S7e_bM$-wo_Ws)W? zx4_6#*X;T$n2N==N0#xzb~BQU#%^NF6|~898JGDbQxjK(ex;Q}_Qn@?Y>!kkUYUeY z&VclG1#eDPU78K@^p3tAUvZi1(nFfk6AAVHWt)Wbi7dPbjA4isOY~?*1&asp!wg#Q zSpSI6*!TGn3|-%vuJE<9V_1EKkz_0%z}Mb7;E!uz)+0^k;@x+<5tzj5 z!InbRtc`YwNCbCac{plY&Y}hWp#PC{o@5UsBj#tv3f^ns^`;$MVN?>q!pW+MYeC7= zkWr1kAX(0xVQ<{qny&CO*|g1{Mk_yE>1t}_YT<5#p8P7QXf;o|s>XQ#SoA&!ddE+8 zOM&VsxsRGS(Spli?P$^pK7Ty{v86RP_6h|MU^J z`J>vn0|BG3Vf!uR0zM|GwtiTPZNb;a@@1+V5+$P4GI_&$%6m!YRGL=lz5kh?z#5f55 z76COi1`R(5p69;ThuQnJ$R3w?I?jigai2arApagd=^tT~oMUWp^u|H_@zXBjpI)Dv zEFc^_`mVu5U*;ClT?x-t9{#fto_+92GF^dotz0sFWTDwZ`s40AY@mv+Qh5c-Ts8Zp z!(v7!zPvFhUZ-xkR!IvaW`{PqN|k)L4*anbtmK+UU&K*awl?DhxRalbtmDw`$#VzK zYFaG}?$F)1j`Qx7wbn|XzMJ&g@3Ai#u5M?%CLPghk;lD^)-|21{Sr+M(suBU4}6CMTMxc_tD;X;z<1-{FeHte=kh1B9O6Hl z!v2i$d1VFC&z&58zU0`G#7^K3Cs@9LYN16O%Vz)?-iQL!G6&sg6aaX>DBZmm@lFrRJpcL{K3(;+`$9GDFDw62Mud@LZjabzVC=w$dx>TQa}U z-{dhKYTYx*C=Fio`ez@wrzx+p%Fk3i&v?6ENXMb3p^?;_&huLLueDwr zpRqHbU%i;9TmexFxCS8F1rPo-ea3!}!ew7{(($76Rdnfa`~$9{8H@f7U&0&HjZ3TZ zuBc||%FljS_e&wNZ$1ezT$*})XAfm??$_cY_?13vM^tT0EKY2ptb+v5P10}a%aTk_ zh8@_T{ns2@jTFhv`)-Vxh}u(0DiL0MUi(We_eic$;gCoqj(T_S{jDo^PahnKJUp3@ zMOk+%weP*c%K6VFXR2icY`J~-&fVMYUg6fsFI->jlA|9`+07y~$Fsz}^;w;mNk$ms zu?y)VA@QH__tvYDudhEWuDD20H&uvrf_boY{($?5{s-SDjyRxSC%%2Xs5d2dpjdk$ zU*NURD#ovwIfd^H{fXR@UuaooJtQr7$d0+(K+1UEwtG9_T?sb$ExV$e-bpf}a@YUe zuzInI59w!x;<)>Be;a7ukLW>V=8~J6nKU<0@H+SQ!Be;1Za_pw#hiuW_PMPBo8W2G z*WDtiIAN<>HQOmh)DMi{s-0H^GmV3QMf4Zu(zXT!-c;2)uv4gUwt(-}-N*|KUOo$h z+Ak^R)h8yB5UD8 zsSjHgY}KguNi?xV=tdCWqJR!~dDpFQoRJOwxrWH^vfRq4%)v;sDfIjsLXF^)uy>!i z*S8Njd7yfa`+7(|8H9j73Rh|TwFpF(8H-p;RLLIU>k<*qI%A*SL{u$%<=X@Jm1QFe zVkQ(X8P4Tohl?_tSO__^aqaI?k$CC8uNLv2mp_zD@4oDaZfEN5;3#XY!L{8B!;Dtt zb~Zge@JF|#Gsk^5$-|(OPI73po|WZh<`UxaH#Y2!&p05Ph?H)d3Bc3J4sDi$f(6K`?&D&~eHVuE@_Prkt>_&8&aq=OzoN!ANkvho;qIX(g|d#EKQbJ@;-%_iARmgSF1fEK z@B4W@5mDME7AzfL**c&2#B7xO9>rA4x$rM{N=%0=goumK1kL{TF@CSk0yvqR2oo&m z)?nyiL$9~Jt(qnEuWt9Hc_duim%|zJQYiaF*~orVNDvJB;`%ZW_2x%Uu01LeX-JP& zD&fas6d3=igAgcfeki79{5!XPHHYR#nfLYRKv^wkv~cnEbLHMwQ8%yCZI^rK!D2qT zk40Vg;e!_!3d56&umIuidN?6MTZFzHot}AdqKzDh#w0s`)cV!2A74RSH1@lDXtC38 z+UhO4A9?oZEOV{bIgGd1{2qMR&xT+}q!=I8m)W23v!W2WPC?Tf!F!e%_(m^lQZtq* zYwi}gY(KZ*Y^OWRNj$Ph#uEEBM+wtN8QFQ@^`GDOln^ioNrmtvzNNi*qS5lPHxI96#sMil*teLVaa%$msF>@5p#SjT%q8|<4ZOUB#!-kG+|eFSED z!|3c8fXaym9qH`L;pmqTWcG}WE$(h1sZ3seM>)E3ptoP<;~h~qe6XA)lGVanf&->P zjZwi;_;Dt+bYdAeD_XSQ-DgXRXqLv`3Wcgl}myA-JlzBBIh zWq4Q*9#(zjAk_H8VS_AJ`?OS*^gB-rp|~qt;v(C5ef=SErv;~zL64hW`#g!UZQcvZ zF6Ra@S@YhVSkSWVAY=Z1w)w-hfJDRwKTUH0o-OG5TlW0HDH36hIjnP=?A+8u1)Qyy5U8Gi$! zt^!vy|f=YHfQ`ZRK?D zXXn*kItRg50vr2+_hV5kjOleg#s~z(J2p#`=1Tq4#JS`MC^e4p&s7Ir=3m(K$LW#` z=ULCoWtna!so+QQ*JHb~6Ps9_&Ag>9qsUskp0pKbi`n?(u3&@QT!?}N}rXn z>1eHi6(@LicU*AR1obe+nbzTCD#VTJ`PFLRT(nc$NWrhsgRwFni*D(#?W^x=J6?|b zENSc^D}s>Y55)PzFs2d_2;yh89E0ZIgs&>6JV=pL6k9g_(`$04EoY+Zjn}}8e#n83 zJ=zB>BU<253Erdo$wE4^+@QQJFZyAj#(InFlN;!UGg96R@{Y&%OlGG;dM)^X8=Ddw@&2Vx?zui$tO z-{zgaU7&F!xs=e`Mn}r+xrdIAmkraRN_7P1?qu1|TZ%1QR(Mn?k+pq`Xys2v9Gs=a z?r@g&;UKcM#?36r9k*eVD(}9qe8?irotsn0+eHH8*4 zPX@Lusr)$J%8jarx5ssEJ?twFyu4kAbrf`96_z{6at^&UkyDzFa69RXP>PeK+dAWqE5<5P+aHa zs<<*+OO_2ObTXau%y)Nn{(p5`XIPWlvi|asjYcui;E@)Ig{YKBXi}spqC!-P5owwL z3L*+9;0C0G!xoN;4KNfDaElv>1#DMDglI&MAVoK2+c2Pr8&sl*1dYj=^>NRS`{O&%YV25@5*eoOvpD_(xdKsnqb^`T}bm;n0BN9ben1Ynyi*OOf;qLpf^ z!T{}GzkXSszN_Xqzp>}S*Im)_Y8~2|B*ybw(U=Q)5_NcMkT;)1&52YQJB)Tn%kPK! z@3;^AI){B(&UOv<{v9KKJrInkdcXV0%O1%1=7vYV*j?v(Kp~arZio$#(A@$kYB3aM zRdm4!^Je15%66($EkCIWGhi@=kNAyLJ3ydlJnCpPuxH0+OA}J)+t8d7nT->##Nz4w-L=S7ExQt=Rx}S*mpT91(>t~qe7tM%e|O)TIO^dP zfo61GNS=cJbLutqUh84?7X#bq)bv57s&D_zm{+xNv7vHjb=_}j-Lrj-Ss*pcD@ts$ z)5Dol8Z_&*1@JdAQE7SL$*!TXI|YE7q=YGkIiUeLvT0)14Q-ivs|+cqeT6DTi9eQ)h?Pu9pqmH51B* zFMd|;l2@D4*56|EhMFlDxl2i<8qq=c+AhMYS3(A28#3DZ;_Ln>RA3q#IAdJq7M#N> zTZ8t=_>lq0=W&w|bdQ^sy&m^@KR)mNi3|1<6|OL(0KLtP#I6ix$2b{-Y9GP5I7 z8AJUSCnlia5vWawX%ZLWTC2UV$cn^sfv68W!6)QO;ZjnX=7#`$ZPRG~irfl)ZUJ^D z{lUk?(*SU7XIiS^H{Lpxn%542#PgxdeG)Ociej#(uvX)z;Z3)<16Yhd z-sv?qQ5D4a)ZYoYPRep2Zvom@U)HKq*54ZEwdaEq^FZG#(CyG!=Vw(0j8CCmP~`_z z=OR^i&WkDCf2cLvWm@d?)mEgme{hA(o#xAL023LZ3(82SGRg6jJF7$kZ4! z6*FTm4y6v~CP!3$+fxg{QeFo24<3iucgI!oyjV|9Dsx}r~4X@lt^VaH$u zD?87}1Jh=?G8OYg*ts2k;X9{f*Za?yu8IUUfyuQ**wbcWT+KncjD^qQ3h&w2+S(Mj zZM~?Ot%ggTIHwkBkL-4&jI5R=B+MCOR42bKzC2M>l?1%x2Iv7amIfQ1B#wwfD`z|m z+E?G+o(tde*Ws?;Wo4p#Yy>Nnf|*b<nj@-s(rZ)-U@ z(Xe(qZ1(_dH|J3yWu|bAPINK}DwF(kZ>FKx(?ZmU^KFC6*bh$;FKGh~pH1 zozA+kgcIk9@2aAwEJ=VYizT!sxDXX$N?XDiGKaaT-OU@Ib=~4DmgEk&{2D@IvyjF* zuF@sDcuuqx_FAgx;B@@8gqjMh!kQeEKA*y4+q+^4&uc0|>M;$Xb+ z@X%eUx1m%$WSP}Qchx68NQ?dO!h`6;Quq+A1(RORsQ-;6bZ90vj#^0(7>cLR+-_;9 zCd@b~B5V>$tpjkQU#BD%9^zu7-l>U8nzt+XuX5cYDCHYaX5t~~3?lpa;)Mr>q;5XW zu(Th;fr}-GkP`K)u97(#UB|L3f;H7Cd#Pox+auV`=m?a=mSv1v)(V!E=$%gkIJZ;` zZj{Lb@bhs%bRa znZw9cD$cDFVHPtpXwY1K)wys@LS~;!qdqkR>@&RtP>?M^>xe{4N#EtZy4zZ5Ar$ZF zV=X=(!xin-58MC<+b~;jk8Q|3B3THGIA$cM8Bg)Yd6ygP#i?4VrX3OvP_k5i{Cppw z-{$XwrJ-+X$ccJ(Q{|?T@U9=-?qlsfA43%8t247KZn?`+C4e`b-e^(df*iW66=Oc2 z3w9UhohfdY@pH1MZ}vc<1osV(2CGG)Ree$E-T;8>$zw*>x-505b&4(shMGIjbAfLS zEZ3ys(`SmCWc(75)^=aKer}>67qj^nGKtCK{35I|tA}wQa!uM!suX%Gb~ylORGGc( ze^|m|N!}G0#Ph|;wSXz`SByQM>lPM#8>mdSQs`7RxkXaSAADYA24u6xWqkIXY?o%z z%TEFL+wNW^&nrvaA1_#P%&Hbzrjl!*hIft>F0@g0IVydUU4MJgS3_3Js8{*>|G2jC z4%n#cOy9b2Xf&Pw=14;0Dtf00C^Z$I-v05OqtvN9>sAC&oV1Tk;;ku7VR`sQK4oFq zQ8)yoZNuTwV$t13|GCUIC{ID_r7M5&R*zhsxbrkg;EgMtL|9ne=^}BM!dxV!KDeXkWA^MfQTkQEt8~t>JznNh%ULvn@dbQ2cyf} z|C%ns#NJU}SHU(7Pg$<&8uDK>d5GZJ&`;CcfGP(~b-#UusXevc^q!km1X6_wVMqGk z^m&ZS6#42?p4c_t1TA$_+}h1L2c<<=$k%;v+D!<@j5hs|{>d18>~~v#oq4yGyS@QP zgTX2oJbEy@eJbo-f{ZQ>-nmB-#AqWcHbMQXFi*T)0n!(HIexz=pp<(O*DMh7CMupX z)ei1ZYuIW~E={-ND*nD;okiZdm!?^|LjLZhs*FHZvWld5TDj zcvWB)`-1Me9bu`*4M=CO6ye=pMgxlgYvsh2rV#5Z$hFKw0GX30%oufb=hJ0BFIJH` z+Fii4gQ+7!)8K^yc*PVEW^#f!|BW0Q5*`IewQ5YDFh?{x1L7tlaUAX@3Y+D>6FPVf zJzOGex~H34`8eq+TL$FsHm+27RS>3$CG;>0Jj4*1ukX$za})*b^S5p}I2jbFCHLsA zzYwAyftMz`uo2c8ieQcy-p&9iP3fMk(uRw+OlBPm`KCLei6g!|Vnk*-kjs>A25MTE z5GLDMV$70AC0j-tx*0sCruvKh{fSM)3X}13U>m|KeaOb`9^}v^44!$`06-JHf@L4EKyxV)M!8cL zi5p9kF97RiAT92!e?%9CP=qX3wyv^A8q!w%07d(9f-U))uDgsr4FDVL;|%r)fw}-@ zlB$F79X^EKYF%8J7mU?3VzJoYQ0<;NczW1jH4=4kEh_)q|^9wj zIsn-SsmRx0_EJ7(6WypwptIwZ)-T<__UgUu?BXt zoIf|a!5`?&JEb$w2PZSqhA>J;GIA^rJ-Cpz8MKX~bcqZNOUzPtu|NMvEP>+cO;V*W zNQ8YPENkr!)lN+tlxB79RUD20$)+_P6Jc`+4q@%Kno{F+#1qR*zrj%T>nTSceO?a5 zyqGDa59#G6k*RXu6+#=e=e!~i1Y&15!cHmE6sLh_K%Ppv$tFE-Le3RQs-nx5LB>gy z5A))kwkxWSy73{@I{%{DY8X+2o{CLJb~R$3r=oT^P~Xo$2lKz8?Z!3QLn$5l#L2k2 zb1=?UT&c<8!&9gW1M&jI!5%dhJbD3nQXpaeNJ>=zR+EL!4iY(nMBQI+|2J+Hw-WMr z08Mt9h8(PGbY?zKtk=cqw(yW}1A#htn* z8&}5Y>$uc>Lv!bSuWQ5UB&ct7*jiZAFpxz|%xO&5kg zzlf?6xy7H3G^*wvP5scW*Wf(<&eP!YIUf%&HT?K)RWmKg$G^=mSoi~;&9dU%{o}WV z#BX;9+q)fpVU`>Vdo~AtYK)`7z*H;dc-e|q6Qt;3J0APUL!~g&Q literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..ed4cc16421680a50164ba74381b4b35ceaa0ccfc GIT binary patch literal 3276 zcmZ`*X*|?x8~)E?#xi3t91%vcMKbnsIy2_j%QE2ziLq8HEtbf{7%?Q-9a%z_Y^9`> zEHh*&vUG%uWkg7pKTS-`$veH@-Vg8ZdG7oAJ@<88AMX3Z{d}TU-4*=KI1-hF6u>DKF2moPt09c{` zfN3rO$X+gJI&oA$AbgKoTL8PiPI1eFOhHBDvW+$&oPl1s$+O5y3$30Jx9nC_?fg%8Om)@;^P;Ee~8ibejUNlSR{FL7-+ zCzU}3UT98m{kYI^@`mgCOJ))+D#erb#$UWt&((j-5*t1id2Zak{`aS^W*K5^gM02# zUAhZn-JAUK>i+SNuFbWWd*7n1^!}>7qZ1CqCl*T+WoAy&z9pm~0AUt1cCV24f z3M@&G~UKrjVHa zjcE@a`2;M>eV&ocly&W3h{`Kt`1Fpp?_h~9!Uj5>0eXw@$opV(@!pixIux}s5pvEqF5$OEMG0;c zAfMxC(-;nx_`}8!F?OqK19MeaswOomKeifCG-!9PiHSU$yamJhcjXiq)-}9`M<&Au|H!nKY(0`^x16f205i2i;E%(4!?0lLq0sH_%)Wzij)B{HZxYWRl3DLaN5`)L zx=x=|^RA?d*TRCwF%`zN6wn_1C4n;lZG(9kT;2Uhl&2jQYtC1TbwQlP^BZHY!MoHm zjQ9)uu_K)ObgvvPb}!SIXFCtN!-%sBQe{6NU=&AtZJS%}eE$i}FIll!r>~b$6gt)V z7x>OFE}YetHPc-tWeu!P@qIWb@Z$bd!*!*udxwO6&gJ)q24$RSU^2Mb%-_`dR2`nW z)}7_4=iR`Tp$TPfd+uieo)8B}Q9#?Szmy!`gcROB@NIehK|?!3`r^1>av?}e<$Qo` zo{Qn#X4ktRy<-+f#c@vILAm;*sfS}r(3rl+{op?Hx|~DU#qsDcQDTvP*!c>h*nXU6 zR=Un;i9D!LcnC(AQ$lTUv^pgv4Z`T@vRP3{&xb^drmjvOruIBJ%3rQAFLl7d9_S64 zN-Uv?R`EzkbYIo)af7_M=X$2p`!u?nr?XqQ_*F-@@(V zFbNeVEzbr;i2fefJ@Gir3-s`syC93he_krL1eb;r(}0yUkuEK34aYvC@(yGi`*oq? zw5g_abg=`5Fdh1Z+clSv*N*Jifmh&3Ghm0A=^s4be*z5N!i^FzLiShgkrkwsHfMjf z*7&-G@W>p6En#dk<^s@G?$7gi_l)y7k`ZY=?ThvvVKL~kM{ehG7-q6=#%Q8F&VsB* zeW^I zUq+tV(~D&Ii_=gn-2QbF3;Fx#%ajjgO05lfF8#kIllzHc=P}a3$S_XsuZI0?0__%O zjiL!@(C0$Nr+r$>bHk(_oc!BUz;)>Xm!s*C!32m1W<*z$^&xRwa+AaAG= z9t4X~7UJht1-z88yEKjJ68HSze5|nKKF9(Chw`{OoG{eG0mo`^93gaJmAP_i_jF8a z({|&fX70PXVE(#wb11j&g4f{_n>)wUYIY#vo>Rit(J=`A-NYYowTnl(N6&9XKIV(G z1aD!>hY!RCd^Sy#GL^0IgYF~)b-lczn+X}+eaa)%FFw41P#f8n2fm9=-4j7}ULi@Z zm=H8~9;)ShkOUAitb!1fvv%;2Q+o)<;_YA1O=??ie>JmIiTy6g+1B-1#A(NAr$JNL znVhfBc8=aoz&yqgrN|{VlpAniZVM?>0%bwB6>}S1n_OURps$}g1t%)YmCA6+5)W#B z=G^KX>C7x|X|$~;K;cc2x8RGO2{{zmjPFrfkr6AVEeW2$J9*~H-4~G&}~b+Pb}JJdODU|$n1<7GPa_>l>;{NmA^y_eXTiv z)T61teOA9Q$_5GEA_ox`1gjz>3lT2b?YY_0UJayin z64qq|Nb7^UhikaEz3M8BKhNDhLIf};)NMeS8(8?3U$ThSMIh0HG;;CW$lAp0db@s0 zu&jbmCCLGE*NktXVfP3NB;MQ>p?;*$-|htv>R`#4>OG<$_n)YvUN7bwzbWEsxAGF~ zn0Vfs?Dn4}Vd|Cf5T-#a52Knf0f*#2D4Lq>-Su4g`$q={+5L$Ta|N8yfZ}rgQm;&b z0A4?$Hg5UkzI)29=>XSzdH4wH8B@_KE{mSc>e3{yGbeiBY_+?^t_a#2^*x_AmN&J$ zf9@<5N15~ty+uwrz0g5k$sL9*mKQazK2h19UW~#H_X83ap-GAGf#8Q5b8n@B8N2HvTiZu&Mg+xhthyG3#0uIny33r?t&kzBuyI$igd`%RIcO8{s$$R3+Z zt{ENUO)pqm_&<(vPf*$q1FvC}W&G)HQOJd%x4PbxogX2a4eW-%KqA5+x#x`g)fN&@ zLjG8|!rCj3y0%N)NkbJVJgDu5tOdMWS|y|Tsb)Z04-oAVZ%Mb311P}}SG#!q_ffMV z@*L#25zW6Ho?-x~8pKw4u9X)qFI7TRC)LlEL6oQ9#!*0k{=p?Vf_^?4YR(M z`uD+8&I-M*`sz5af#gd$8rr|oRMVgeI~soPKB{Q{FwV-FW)>BlS?inI8girWs=mo5b18{#~CJz!miCgQYU>KtCPt()StN;x)c2P3bMVB$o(QUh z$cRQlo_?#k`7A{Tw z!~_YKSd(%1dBM+KE!5I2)ZZsGz|`+*fB*n}yxtKVyx14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>GbI`Jdw*pGcA%L+*Q#&*YQOJ$_%U#(BDn``;rKxi&&)LfRxIZ*98z8UWRslDo@Xu)QVh}rB>bKwe@Bjzwg%m$hd zG)gFMgHZlPxGcm3paLLb44yHI|Ag0wdp!_yD5R<|B29Ui~27`?vfy#ktk_KyHWMDA42{J=Uq-o}i z*%kZ@45mQ-Rw?0?K+z{&5KFc}xc5Q%1PFAbL_xCmpj?JNAm>L6SjrCMpiK}5LG0ZE zO>_%)r1c48n{Iv*t(u1=&kH zeO=ifbFy+6aSK)V_5t;NKhE#$Iz=+Oii|KDJ}W>g}0%`Svgra*tnS6TRU4iTH*e=dj~I` zym|EM*}I1?pT2#3`oZ(|3I-Y$DkeHMN=8~%YSR?;>=X?(Emci*ZIz9+t<|S1>hE8$ zVa1LmTh{DZv}x6@Wz!a}+qZDz%AHHMuHCzM^XlEpr!QPzf9QzkS_0!&1MPx*ICxe}RFdTH+c}l9E`G zYL#4+3Zxi}3=A!G4S>ir#L(2r)WFKnP}jiR%D`ZOPH`@ZhTQy=%(P0}8ZH)|z6jL7 N;OXk;vd$@?2>?>Ex^Vyi literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000000000000000000000000000000000000..bcbf36df2f2aaaa0a63c7dabc94e600184229d0d GIT binary patch literal 5933 zcmZ{Idpwix|Np(&m_yAF>K&UIn{t*2ZOdsShYs(MibU!|=pZCJq~7E>B$QJr)hC5| zmk?V?ES039lQ~RC!kjkl-TU4?|NZ{>J$CPLUH9vHy`Hbhhnc~SD_vpzBp6Xw4`$%jfmPw(;etLCccvfU-s)1A zLl8-RiSx!#?Kwzd0E&>h;Fc z^;S84cUH7gMe#2}MHYcDXgbkI+Qh^X4BV~6y<@s`gMSNX!4@g8?ojjj5hZj5X4g9D zavr_NoeZ=4vim%!Y`GnF-?2_Gb)g$xAo>#zCOLB-jPww8a%c|r&DC=eVdE;y+HwH@ zy`JK(oq+Yw^-hLvWO4B8orWwLiKT!hX!?xw`kz%INd5f)>k1PZ`ZfM&&Ngw)HiXA| ze=+%KkiLe1hd>h!ZO2O$45alH0O|E+>G2oCiJ|3y2c$;XedBozx93BprOr$#d{W5sb*hQQ~M@+v_m!8s?9+{Q0adM?ip3qQ*P5$R~dFvP+5KOH_^A+l-qu5flE*KLJp!rtjqTVqJsmpc1 zo>T>*ja-V&ma7)K?CE9RTsKQKk7lhx$L`9d6-Gq`_zKDa6*>csToQ{&0rWf$mD7x~S3{oA z1wUZl&^{qbX>y*T71~3NWd1Wfgjg)<~BnK96Ro#om&~8mU{}D!Fu# zTrKKSM8gY^*47b2Vr|ZZe&m9Y`n+Y8lHvtlBbIjNl3pGxU{!#Crl5RPIO~!L5Y({ym~8%Ox-9g>IW8 zSz2G6D#F|L^lcotrZx4cFdfw6f){tqITj6>HSW&ijlgTJTGbc7Q#=)*Be0-s0$fCk z^YaG;7Q1dfJq#p|EJ~YYmqjs`M0jPl=E`Id{+h%Lo*|8xp6K7yfgjqiH7{61$4x~A zNnH+65?QCtL;_w(|mDNJXybin=rOy-i7A@lXEu z&jY(5jhjlP{TsjMe$*b^2kp8LeAXu~*q&5;|3v|4w4Ij_4c{4GG8={;=K#lh{#C8v z&t9d7bf{@9aUaE94V~4wtQ|LMT*Ruuu0Ndjj*vh2pWW@|KeeXi(vt!YXi~I6?r5PG z$_{M*wrccE6x42nPaJUO#tBu$l#MInrZhej_Tqki{;BT0VZeb$Ba%;>L!##cvieb2 zwn(_+o!zhMk@l~$$}hivyebloEnNQmOy6biopy`GL?=hN&2)hsA0@fj=A^uEv~TFE z<|ZJIWplBEmufYI)<>IXMv(c+I^y6qBthESbAnk?0N(PI>4{ASayV1ErZ&dsM4Z@E-)F&V0>tIF+Oubl zin^4Qx@`Un4kRiPq+LX5{4*+twI#F~PE7g{FpJ`{)K()FH+VG^>)C-VgK>S=PH!m^ zE$+Cfz!Ja`s^Vo(fd&+U{W|K$e(|{YG;^9{D|UdadmUW;j;&V!rU)W_@kqQj*Frp~ z7=kRxk)d1$$38B03-E_|v=<*~p3>)2w*eXo(vk%HCXeT5lf_Z+D}(Uju=(WdZ4xa( zg>98lC^Z_`s-=ra9ZC^lAF?rIvQZpAMz8-#EgX;`lc6*53ckpxG}(pJp~0XBd9?RP zq!J-f`h0dC*nWxKUh~8YqN{SjiJ6vLBkMRo?;|eA(I!akhGm^}JXoL_sHYkGEQWWf zTR_u*Ga~Y!hUuqb`h|`DS-T)yCiF#s<KR}hC~F%m)?xjzj6w#Za%~XsXFS@P0E3t*qs)tR43%!OUxs(|FTR4Sjz(N zppN>{Ip2l3esk9rtB#+To92s~*WGK`G+ECt6D>Bvm|0`>Img`jUr$r@##&!1Ud{r| zgC@cPkNL_na`74%fIk)NaP-0UGq`|9gB}oHRoRU7U>Uqe!U61fY7*Nj(JiFa-B7Av z;VNDv7Xx&CTwh(C2ZT{ot`!E~1i1kK;VtIh?;a1iLWifv8121n6X!{C%kw|h-Z8_U z9Y8M38M2QG^=h+dW*$CJFmuVcrvD*0hbFOD=~wU?C5VqNiIgAs#4axofE*WFYd|K;Et18?xaI|v-0hN#D#7j z5I{XH)+v0)ZYF=-qloGQ>!)q_2S(Lg3<=UsLn%O)V-mhI-nc_cJZu(QWRY)*1il%n zOR5Kdi)zL-5w~lOixilSSF9YQ29*H+Br2*T2lJ?aSLKBwv7}*ZfICEb$t>z&A+O3C z^@_rpf0S7MO<3?73G5{LWrDWfhy-c7%M}E>0!Q(Iu71MYB(|gk$2`jH?!>ND0?xZu z1V|&*VsEG9U zm)!4#oTcgOO6Hqt3^vcHx>n}%pyf|NSNyTZX*f+TODT`F%IyvCpY?BGELP#s<|D{U z9lUTj%P6>^0Y$fvIdSj5*=&VVMy&nms=!=2y<5DP8x;Z13#YXf7}G)sc$_TQQ=4BD zQ1Le^y+BwHl7T6)`Q&9H&A2fJ@IPa;On5n!VNqWUiA*XXOnvoSjEIKW<$V~1?#zts>enlSTQaG2A|Ck4WkZWQoeOu(te znV;souKbA2W=)YWldqW@fV^$6EuB`lFmXYm%WqI}X?I1I7(mQ8U-pm+Ya* z|7o6wac&1>GuQfIvzU7YHIz_|V;J*CMLJolXMx^9CI;I+{Nph?sf2pX@%OKT;N@Uz9Y zzuNq11Ccdwtr(TDLx}N!>?weLLkv~i!xfI0HGWff*!12E*?7QzzZT%TX{5b7{8^*A z3ut^C4uxSDf=~t4wZ%L%gO_WS7SR4Ok7hJ;tvZ9QBfVE%2)6hE>xu9y*2%X5y%g$8 z*8&(XxwN?dO?2b4VSa@On~5A?zZZ{^s3rXm54Cfi-%4hBFSk|zY9u(3d1ButJuZ1@ zfOHtpSt)uJnL`zg9bBvUkjbPO0xNr{^{h0~$I$XQzel_OIEkgT5L!dW1uSnKsEMVp z9t^dfkxq=BneR9`%b#nWSdj)u1G=Ehv0$L@xe_eG$Ac%f7 zy`*X(p0r3FdCTa1AX^BtmPJNR4%S1nyu-AM-8)~t-KII9GEJU)W^ng7C@3%&3lj$2 z4niLa8)fJ2g>%`;;!re+Vh{3V^}9osx@pH8>b0#d8p`Dgm{I?y@dUJ4QcSB<+FAuT)O9gMlwrERIy z6)DFLaEhJkQ7S4^Qr!JA6*SYni$THFtE)0@%!vAw%X7y~!#k0?-|&6VIpFY9>5GhK zr;nM-Z`Omh>1>7;&?VC5JQoKi<`!BU_&GLzR%92V$kMohNpMDB=&NzMB&w-^SF~_# zNsTca>J{Y555+z|IT75yW;wi5A1Z zyzv|4l|xZ-Oy8r8_c8X)h%|a8#(oWcgS5P6gtuCA_vA!t=)IFTL{nnh8iW!B$i=Kd zj1ILrL;ht_4aRKF(l1%^dUyVxgK!2QsL)-{x$`q5wWjjN6B!Cj)jB=bii;9&Ee-;< zJfVk(8EOrbM&5mUciP49{Z43|TLoE#j(nQN_MaKt16dp#T6jF7z?^5*KwoT-Y`rs$ z?}8)#5Dg-Rx!PTa2R5; zx0zhW{BOpx_wKPlTu;4ev-0dUwp;g3qqIi|UMC@A?zEb3RXY`z_}gbwju zzlNht0WR%g@R5CVvg#+fb)o!I*Zpe?{_+oGq*wOmCWQ=(Ra-Q9mx#6SsqWAp*-Jzb zKvuPthpH(Fn_k>2XPu!=+C{vZsF8<9p!T}U+ICbNtO}IAqxa57*L&T>M6I0ogt&l> z^3k#b#S1--$byAaU&sZL$6(6mrf)OqZXpUPbVW%T|4T}20q9SQ&;3?oRz6rSDP4`b z(}J^?+mzbp>MQDD{ziSS0K(2^V4_anz9JV|Y_5{kF3spgW%EO6JpJ(rnnIN%;xkKf zn~;I&OGHKII3ZQ&?sHlEy)jqCyfeusjPMo7sLVr~??NAknqCbuDmo+7tp8vrKykMb z(y`R)pVp}ZgTErmi+z`UyQU*G5stQRsx*J^XW}LHi_af?(bJ8DPho0b)^PT|(`_A$ zFCYCCF={BknK&KYTAVaHE{lqJs4g6B@O&^5oTPLkmqAB#T#m!l9?wz!C}#a6w)Z~Z z6jx{dsXhI(|D)x%Yu49%ioD-~4}+hCA8Q;w_A$79%n+X84jbf?Nh?kRNRzyAi{_oV zU)LqH-yRdPxp;>vBAWqH4E z(WL)}-rb<_R^B~fI%ddj?Qxhp^5_~)6-aB`D~Nd$S`LY_O&&Fme>Id)+iI>%9V-68 z3crl=15^%0qA~}ksw@^dpZ`p;m=ury;-OV63*;zQyRs4?1?8lbUL!bR+C~2Zz1O+E@6ZQW!wvv z|NLqSP0^*J2Twq@yws%~V0^h05B8BMNHv_ZZT+=d%T#i{faiqN+ut5Bc`uQPM zgO+b1uj;)i!N94RJ>5RjTNXN{gAZel|L8S4r!NT{7)_=|`}D~ElU#2er}8~UE$Q>g zZryBhOd|J-U72{1q;Lb!^3mf+H$x6(hJHn$ZJRqCp^In_PD+>6KWnCnCXA35(}g!X z;3YI1luR&*1IvESL~*aF8(?4deU`9!cxB{8IO?PpZ{O5&uY<0DIERh2wEoAP@bayv z#$WTjR*$bN8^~AGZu+85uHo&AulFjmh*pupai?o?+>rZ7@@Xk4muI}ZqH`n&<@_Vn zvT!GF-_Ngd$B7kLge~&3qC;TE=tEid(nQB*qzXI0m46ma*2d(Sd*M%@Zc{kCFcs;1 zky%U)Pyg3wm_g12J`lS4n+Sg=L)-Y`bU705E5wk&zVEZw`eM#~AHHW96@D>bz#7?- zV`xlac^e`Zh_O+B5-kO=$04{<cKUG?R&#bnF}-?4(Jq+?Ph!9g zx@s~F)Uwub>Ratv&v85!6}3{n$bYb+p!w(l8Na6cSyEx#{r7>^YvIj8L?c*{mcB^x zqnv*lu-B1ORFtrmhfe}$I8~h*3!Ys%FNQv!P2tA^wjbH f$KZHO*s&vt|9^w-6P?|#0pRK8NSwWJ?9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!ItFh?!xdN1Q+aGJ{c&& zS>O>_%)r1c48n{Iv*t(u1=&kHeO=ifbFy+6aSK)V_AxLppYn8Z42d|rc6w}vOsL55 z`t&mC&y2@JTEyg!eDiFX^k#CC!jq%>erB=yHqUP0XcDOTw6ko}L zX;EmMrq(fKk*eygEuA616;0)>@A{TK|55PV@70 z$OfzS*(VJxQev3J?yY?O=ul(v`fp}?u9z`JK3ugibK>)DyCwImZOF4d{xK%%Ks1*} zv$oa)9anR%lXIBUqYnhLmT>VOzHfNP?ZwJNZ!5$s9M08RynIvaXw>@G^T9@r9^KH1 zVy??F&uuk)bH9Y4pQY!hP58i_H6 znl-NcuCpLV6ZWU;4C zu@9exF&OZi`Bovq_m%T+WhU2kvkz@^_LpycBvqm3bMpLw8X-Or5sL>0AKE1$(k_L=_Zc=CUq#=x1-QZf)G7nHu@fmsQ1eN_N3+nTEz`4HI4Z6uVlE zJH+X&det8JU?tO?upcM4Z=cV!JV;yF>FfL5Q$M|W_2Z!P`S=}Wzp|_1^#d%e?_H`> zV@%vA$+bFVqhw9`U;TfP|5|PD{||OiYdor8P*i??|NJcb%kzT_73*7WE?Ua5hAnR2 z=7WE=PhTlJ#ZeRznjTUb;`E(wkMZrj4e|Hilz-mK>9cZHQY**5TUPw~u}k;u73KI}xAx!0m-)GVia|x^d3p~s_9gh83jA&Ra<8rM%`>U3x69t&NzbwWY}7Ar?)FK#IZ0z|d0H0EkRO w3{9;}4Xg|ebq&m|3=9_N6z8I7$jwj5OsmAL;bP(Gi$Dzwp00i_>zopr02+f8CIA2c literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/file_selector/file_selector_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000000000000000000000000000000000000..e71a726136a47ed24125c7efc79d68a4a01961b4 GIT binary patch literal 14800 zcmZ{Lc|26@`~R6Crm_qwyCLMMh!)vm)F@HWt|+6V6lE=CaHfcnn4;2x(VilEl9-V} zsce-cGK|WaF}4{T=lt&J`Fy_L-|vs#>v^7+XU=`!*L|PszSj43o%o$Dj`9mM7C;ar z@3hrnHw59q|KcHn4EQr~{_70*BYk4yj*SqM&s>NcnFoIBdT-sm1A@YrK@dF#f+SPu z{Sb8441xx|AjtYQ1gQq5z1g(^49Fba=I8)nl7BMGpQeB(^8>dY41u79Dw6+j(A_jO z@K83?X~$;S-ud$gYZfZg5|bdvlI`TMaqs!>e}3%9HXev<6;dZZT8Yx`&;pKnN*iCJ z&x_ycWo9{*O}Gc$JHU`%s*$C%@v73hd+Mf%%9ph_Y1juXamcTAHd9tkwoua7yBu?V zgROzw>LbxAw3^;bZU~ZGnnHW?=7r9ZAK#wxT;0O<*z~_>^uV+VCU9B@)|r z*z^v>$!oH7%WZYrwf)zjGU|(8I%9PoktcsH8`z^%$48u z(O_}1U25s@Q*9{-3O!+t?w*QHo;~P99;6-KTGO{Cb#ADDYWF!eATsx{xh-!YMBiuE z%bJc7j^^B$Sa|27XRxg(XTaxWoFI}VFfV>0py8mMM;b^vH}49j;kwCA+Lw=q8lptk z?Pe`{wHI39A&xYkltf5*y%;-DF>5v`-lm0vydYtmqo0sClh5ueHCLJ+6$0y67Z zO-_LCT|JXi3tN7fB-!0_Kn#I+=tyUj87uR5*0>|SZ zy3x2;aql87`{aPZ@UbBwY0;Z-a*lYL90YApOAMKur7YgOiqA~Cne6%b&{V-t>Am2c z{eyEuKl!GsA*jF2H_gvX?bP~v46%3ax$r~B$HnZQ;UiCmRl`ROK8v>;Zs~upH9}qu1ZA3kn-AY2k2@CaH=Qh7K6`nU z3ib(Bk%H*^_omL6N4_G5NpY20UXGi}a$!}#lf<&J4~nhRwRM5cCB3Zvv#6+N1$g@W zj9?qmQ`zz-G9HTpoNl~bCOaEQqlTVYi7G0WmB5E34;f{SGcLvFpOb`+Zm)C(wjqLA z2;+nmB6~QDXbxZGWKLt38I%X$Q!;h zup9S~byxKv=$x|^YEV;l0l67jH~E8BU45ft_7xomac-48oq4PZpSNJbw<7DTM4mmz z!$)z#04cy%b8w@cOvjmb36o;gwYIOLwy+{I#3dJj#W4QdOWwJQ2#20AL49`hSFUa7 zFNAN3OD==G3_kbr1d96>l`_cI`<=thKNh5>hgg7FV>5TfC6d#u)9BNXi@p1K*;2Is zz+x;l4GbSt#*%>1iq}jGIebXYJY5;PGG0y(^{>SSuZY89aL`sDghOM&&pyP6ABJ#w zYwK~4^1eUQD)4!GL>`zrWeHV z-W!6JZbW*Ngo;Edhp_cOysYr!uhKS}vIg_UC}x z=jXxQfV@4B3`5 z!u#byBVXV5GtrSx_8bnT@iKv=Uc6n)Zpa`<9N>+!J~Loxptl5$Z`!u<3a)-+P)say z#=jc7^mJzPMI2;yMhCmN7YN78E7-^S(t8E}FklC;z|4PL{bO|JieM#p1mBjwyZMEm zkX^A1RXPGeS2YqtPMX~~t^$~oeFfWAU#jVLi%Z@l2hle^3|e(q?(uS=BVauF?VF{j z(owKLJuze;_@5p1OtRyrT`EFXf)NfMYb-)E8RVVdr<@}M>4R&~P=;B`c1L%o|8YfB z-a(LB-i8jc5!&B5cowyI2~M^YID&@Xt(D9v{|DB z959W z*vEA77fh3*w*UJ`4Y(bxsoEy6hm7_Wc5gT0^cvso%Ow>9<&@9Q>mxb6-^pv)5yc>n zQ~^!qY(lPQ1EDGkr%_*y*D8T^YbCa52^MVqYpTLhgJ;N5PfCQ{SXk|plD#Sm+g4c- zFeL2Dih35W4{_qb75U`4Rb#S0FEo%F85dOhXSX0huPOxdAid{&p6P;+9}I)XU7^=3RZu9M(g0dLyz_7$8K{`AddBLOfU&B_QNHtmsnNXq`hy~% zvJ{vtz~Yt9X|o}5vXX)9ZCHaRq8iAb zUDj8%(MpzJN39LferYKvIc!)z^5T-eW@j3h9a6d%WZ!%@2^@4+6%Z9W1GHZbOj|sb z0cU$}*~G$fYvDC|XulSC_;m}?KC2jg5pxES$Bt!hA|@EX*2+O!UEb5sn_^d>z;>;r~ zmO3BivdXboPY*}amsO&`xk|e)S*u=`o67MC(1WTB;OwG+ua4UV7T5Wvy%?U{Pa5cO zMoLG>#@chO{Oc72XPyX8f3jC7P`$j4$)0wc(b50COaDP3_Cm}aPAglUa7kRXAqmo5 z0KDD7G>Gmnpons40WJNYn+pxko92GXy@PvSErKE-Ou3)3UiRr7!L4+0%+5}sD{bf)uj^ounQ-Yn2%%JoZ%FjUv%yjS?Ks4u_88Jh%tNliYW~817IV@fqd1T zi(?;Fv-s3rQEn=9G*E-QzSl%YS|^fe*yn}Aqh!&P<5%#oB?*{wZMa5$PYa*A{VA8! zbOfS1W!W}cTo%g~iP$>WhE_x7#O4?h$jq=>{M77>bTAK_ z6uU0tl6HARboGi}=4krr6WP`9`aAt&P5ON1v(+H{T?jZuJ}B{L-=z3VX)}mZwzrqH zpf?T!k&$?{&{0_p>b`kdJbSb(p~tFcuG4zh6}hfl@ues6CfJu<-P+!>FlYMlD_3!E z9$6VE==tlxNYe(s;@8@+4c4jQ$R2g8t0QwE>Et|)5)@kJj6^yaqFYY?0LEM2C!+7+ z+FN|UxR1GCy1KA`{T_%24U+Vserchr5h`;U7TZPr@43x#MMN{@vV?KSII}R@5k`7cVK}E;c)$f~_{ZLDOoL|-01p~oafxi4F zG$?Wha&a*rTnz-nTI-bAJ*SLb!5(L!#iRdvLEyo>7D_=H78-qZrm=6{hkUR{tR{H! z`ZTOV$Oi6^qX5=_{f}V9h}WJAO%h9)kEUF#*-JyYDbOGZ>Nfs%7L}4p zopIul&&Bbn!C9o83ypC6W4F$X=_|pex$V4!Whm#48Wfm3*oAW0Gc&#&b+oq<8>aZR z2BLpouQQwyf$aHpQUK3pMRj(mS^^t#s$IC3{j*m9&l7sQt@RU{o_}N-xI_lh`rND^ zX~-8$o(;p^wf3_5-WZ^qgW`e8T@37{`J)e2KJdSSCUpX6KZu0Ga&U*+u3*PDAs1uK zpl)40+fROA@Vo#vK?^@Pq%w8DO9HdfmH+~vNinZ$5GRz?sD|k246NepqZd`>81P^P z#x#3kUS-}x4k%&~iEUrsb&-X#_;;?y9oCP4crMkC`=q58#NxQ| z*NXNA;GR4X=GiGXwab5=&M3j04fQw%2UxM`S(aE)_PlgJttBX96$$lY@Q%0xV^IbcHqzw^Uk&E=vFB;EQ@kzVIeM8lDIW_Q_ zrfy)l6s2QBApF;J2xTD_@wuNMlwDfsdfMyzRq)<>qG{M)Yt}9F1{1HaI_X7=F=7>& zYB54VaKlxu0lIgS;Ac&25Aw(tcf@K~(cvPi8(OChzhlYp6}#<_MVhU95sD&)n0FtL zmxm4w$~s(S9jmHOgyovpG!x4uLfJsMsJn^QMraKAa1Ix?{zkV!a7{f%-!u2{NqZ&) zo+^XB`eFQ4 zk-(;_>T#pTKyvW${yL|XXbcv?CE2Tp<3(PjeXhu^Jrp6^Mj}lg_)jamK{g;C+q^Da ztb!gV!q5)B7G1%lVanA2b>Xs?%hzCgJ{Hc!ldr9dnz7k^xG#4pDpr|0ZmxxiUVl}j zbD_rg3yAFQ>nnc)0>71D==715jRj4XsRb2#_lJoSOwky&c4957V-|m)@>b^Nak1!8 z@DsIOS8>Oe^T>tgB)WX3Y^I^65Uae+2M;$RxX_C)Aoo0dltvoRRIVQkpnegWj;D#G z+TwFIRUN%bZW3(K{8yN8!(1i0O!X3YN?Zo08L5D~)_tWQA8&|CvuQb8Od?p_x=GMF z-B@v9iNLYS1lUsbb`!%f5+1ev8RFPk7xyx5*G;ybRw(PW*yEZ$unu2`wpH)7b@ZXEz4Jr{?KZKYl!+3^)Q z)~^g?KlPGtT!{yQU&(Z&^rVjPu>ueeZN86AnhRwc)m|;5NvM&W3xD%n`+Hjg5$e8M zKh1Ju82L~&^ z-IQ5bYhsjqJfr38iwi~8<{oeREh|3l)*Enj4&Q$+mM$15YqwXeufK9P^(O=pj=F-1 zD+&REgwY~!W#ZPccSEi(*jiKJ5)Q|zX;hP}S2T9j_);epH9JQs{n>RG}{Nak)vIbfa zFQm?H;D+tzrBN2)6{?Mo%fzN6;6d_h0Qyn61)+XT63=!T*WQyRUoB_x0_)Ir`$FtS zak07C(mOaWN5m%bk?F9X&@mEVKN%{R6obt(9qw&p>w&p;R*l2th9$D^*`pC}NmB+v z>bk;OJ(C8p$G;jNvRsBbt=a!!tKnjJ`9*yQFgjEN1HcC<&>u9aStT3>Oq=MOQV!#WOZ6{cv$YVmlJdovPRV}<=IZUPeBVh5DC z91-?kimq3JUr;UMQ@0?h52gupvG=~(5AVdP(2(%*sL8!#K1-L$9B7MrWGdt(h&whR@vz~0oEHF8u3U1Q zdGdaIytJj4x@eF*E+^zgi{nPCA8tkjN}UoR8WhDzM3-zLqx0z?2tTdDKyENM={fp8VC@3Dt`AiK$;K#H$K2{08mrHG%jgEOLX3MCsG>afZm_0mLPS4jmYUJp~Dm! z5AUe_vEaOAT3zWdwl#cLvqwd1^lwW?gt7(92wEsOE6c#<0}{szFV4(uO70?3>=((! zQr}1{J?Wx2ZmjxYL_8OB*m&mimfojzYn~PiJ2g8R&ZRx-i^yF#sdhEWXAUIZ@J?T$ zs3PgT2<&Ki>Bob_n(@S>kUIvE+nY~ti9~6j;O9VAG#{oZ!DZCW)}i6iA!Tgsyz+hC z1VVyvbQ_nwgdZSEP=U4d#U`2*`e~d4y8uM4Bcmm%!jidaee#4WqN!ZnlBmbYpuaO! z!rU3`Kl2 z0O7PD&fQ|_b)Ub!g9^s;C2e>1i*2&?1$6yEn?~Y zI)-WIN8N(5s9;grW+J@K@I%g#?G&hzmlgV=L}ZA{f>3YCMx^P{u@c5Z;U1qmdk#)L zvX6z1!sL>+@vxO8qVn#k3YxYi?8ggV){?Rn@j$+Fd4-QkuH1@)j#3-=f82GZ!nl~{ zzZ(?kO`ANttVeHSo%xmH!NmNZECh*{s!-8S>ALoe5xOPs>|P5BbUmP@rlV8`d(c=7 zypcpLaI*FM^;GM%@q`GAb8kO`$oE|R48yn)?p(c1t>5;Wwn5r6ck&uw4}TnT80jI`IS~J%q8CpaVgIze<8IykSpVBg8~E! zW_tGqB;GO47r_er05y+Kwrcn{VLxL*1;HMv@*sd}MB6DH4zaP~u4Y;>@Nw7?F8S?c zfVIY(^ntnGgWlD|idzGz$Y+Oh(Ra=&VIf4!K2W*a)(%5%78s}8qxOknAGtDAq+HMO zM+Nu;0OgQRn36 zA@~a8`uVQ~v9?d!BxnsVaB-z-djypO44BjQAmg7&eVoaew|~)wH$SgefJ2$7_RiY+ z_7ACGoFM6Lhvho+eUG@pU&0X(Uy(*j;9pr?ET?FHTXadlfXC|MReZoU5>AG`mTM<% zc~*I@E*u0|hwVTdFA~4^b2VT7_~}~tCueNY{de3og=ASFQ`)0dhC2~Ne<}}Rc?ptA zi}+bQE%N9o*hpSUMH)9xt%Zlz&^p&5=cW}{m#f85iVX64^{!(vhClT<I)+c)RuiyrZqIw4v`z%YK&;_Fh4_+0B?qAGxMfAM`LzG_bjD>ib4;KGT4_1I>sxvL&&qp40ajgQOqIE^9=Az4w#ymo)bW-Vg{T!n=l&|nR_ zw+wcH|FxUH63)~{M;goHepmD{Fe?W9sO|eJP9L$G<{e_7FxxuXQ+)(Z^@;X8I1=%k zTK$gbHA1^4W<`q~ubQ0M_C^CA5#Z&*nGc(T?4Y_2jLu&FJDQYpCSiRny->$+nC9Jl z?avTW`ZXYT51%SrEq!}dXNM&!pM6nmL^lce=%S7{_TS)ckN8;{p*LT~LMgmlE~dpL zEBQy-jDj%cSK6N3)|CCR0LQ$N6iDM~+-1Oz|LAdkip(VZcO`gqCuJ+(Mm{m6@P%_; zBtF|MMVMP;E`5NJ{&@4j^JE5j&}(Jq{lCGL(P^#uqvbD`2)FVyfNgy|pvT!XY;02Z zZWbgGsvi6#!*$Zxwd{Xk6_M{+^yV_K@%_SAW(x)Lg|*AuG-%g2#GQYk8F?W&8|2dU z;00ppzrQnnYXnT`(S%_qF2#QNz&@Y$zcq+O8p>Gto2&4z8(^#cY?DuQwBQP4Fe?qUK_-yh4xT{8O@gb`uh` z>Q%jrgPAnANn4_)->n;w{Mei#J)F+`12&+-MLKSRzF6bL3;4O~oy~v7 zL0K-=m?>>(^qDCgvFRLBI@`04EGdTxe5}xBg#7#Wb!aUED;?5BLDEvZ@tai4*Rh8& z4V)cOr}DJ0&(FjWH%50Y+&=WtB42^eEVsmaHG)Il#j265oK&Bot(+-IIn`6InmuE# z;)qXs+X{fSb8^rYb#46X5?KCzH9X0>ppBQi(aKS--;4yA%0N|D<#8RZlOS(8n26=u zv~y;KC>`ypW=aqj`&x9 z0Zm>NKp}hPJu1+QDo(_U(Gt0SZ`IJWnp%QK`pye>Bm!w{sG>;VU^2 z4lZhV1}tCE8(?zu#j99|l3-qRBcz3bG+DlyxPGB$^6B^ssc_qYQ6lG0q~EAI?1$?( zahfn%etVvuKwB7R=>JDQluP97nLDM6*5;b0Ox#b{4nIgZA*+?IvyDN{K9WGnlA=Ju z+)6hjr}{;GxQQIDr3*lf32lRp{nHP8uiz^Fa|K+dUc@wD4Kf5RPxVkUZFCdtZH{+=c$AC)G2T-Qn@BPbr zZigIhKhKrVYy`!Mlc#HVr=CURVrhUjExhI~gZ%a=WM9BwvnN?=z!_ZQ$(sP?X;2Jy zyI$}H^^SvH2tf6+Uk$pJww@ngzPp856-l9g6WtW+%Yf>N^A}->#1W2n=WJ%sZ0<){Z&#% z^Kzl$>Km)sIxKLFjtc;}bZeoaZSpL4>`jCmAeRM-NP9sQ&-mi@p0j7Iq>1n&z@8?M z%dM7K^SgE5z)@i5w#rLE4+8%|^J`a6wYr`3BlvdD>7xW?Dd>`0HC0o{w7r_ot~h*G z2gI7Y!AUZ6YN+z$=GNzns@Tu7BxgAb3MBha30-ZG7a%rckU5}y{df`lj@^+34kr5> z988PPbWYdHye~=?>uZ4N&MN@4RBLk_?9W*b$}jqt0j%>yO9QOV(*!#cX~=wRdVL&S zhPQ{${0CGU-rfdS&b@u|IK{hV2Z=(*B2d0?&jwWfT=?Gk`4T9TfMQ)CfNgpLQa#>Q z%6A$w#QNc&qOtrHAbqY>J782@!X{9Y@N(HMSr;PP^;0DlJNxfC`oMB%Ocg zC*hnEsF|p*=CVe^dT)>BTL0yff)uo!U<+_2o3p)CE8quU1JI(=6)9$KxVdJYD*S*~ zzNeSkzFIQyqK}578+qq6X8rrRdgX z4k&R=AGex~a)MoB0pK&|yA<(*J#P&tR?ImBVD)ZTA4VH5L5DxXe<-*s`Aox%H1{-^Qa`kG_DGXD%QX-;l1#&#IVQP6>kir ztO@~ZvJDPnTvKt>fc*(j$W^)JhWk{4kWwbpFIXzuPt2V%M4H19-i5Gn*6(D`4_c1+ zYoI1@yT^~9JF~t>2eVM6p=GP3b*;daJpQOhAMNO|LKnwE2B5n8y9mf;q=)-L_FfD0 z<}YIRBO{k)6AHAn8iG>pYT+3bJ7jvP9}LSMR1nZW$5HR%PD1rFz z{4XE^Vmi-QX#?|Farz=CYS_8!%$E#G%4j2+;Avz|9QBj|YIExYk?y-1(j}0h{$$MnC_*F0U2*ExSi1ZCb_S9aV zTgyGP0Cl=m`emxM4Qih1E{`J{4oJo8K}WnH`@js^pR7Z-vTBK5F5JIFCDN}7pU^_nV>NTz@2$|Kcc5o+L&^Db_AQ);F?)X5BF*QJRCdLI-a%gW z++DZM)x=6*fNrSaUA&hf&CUqC$F*y^CJC-MAm9gd*5#^mh;-dR1?a&<3-hp3@}XN! z&8dcwo6=MQua%0KFvYbi>O{j)RrbDQo3S*y!oEJ~2=}^-v%zn~@hnmKGOvX6JLr;>DNC3)={8OM9n5Zs*(DlS*|%JTniJX2Uav7sOFT0vdIiUOC5pEtY?EF)@Fh9pCfD%N zXskZ8b^ldI{HHj{-l?iWo@IW6Nr`hAS>f8S*8FGc*gmcK^f2JS+>I&r#Gcewy=-JM zv0*w<5qBa6UQB@`esOG*4*t@7c9AkrTpM`v=eY?cO#z17H9B%Xy4m!}LhW}*iZ27w1?HrevgB1SZ1q2X$mm@FK@Qt7o z!s~Lio^IRdwzyvQ80{5iYeTV@mAo=2o5>KepRH0d{*Szlg~n%w2)S5v2|K8}pj;c{ zoDRLvYJO1@?x-=mq+LVhD{l-1-Dw4`7M?3@+ z`fu7?1#9W++6Y46N=H0+bD|CJH~q*CdEBm8D##VS7`cXy4~+x=ZC17rJeBh zI~qW^&FU`+e!{AKO3(>z5Ghh14bUT$=4B>@DVm(cj* zSLA*j!?z!=SLuVvAPh_EFKx}JE8T8;Gx)LH^H136=#Jn3Bo*@?=S`5M{WJPY&~ODs z+^V57DhJ2kD^Z|&;H}eoN~sxS8~cN5u1eW{t&y{!ouH`%p4(yDZaqw$%dlm4A0f0| z8H}XZFDs?3QuqI^PEy}T;r!5+QpfKEt&V|D)Z*xoJ?XXZ+k!sU2X!rcTF4tg8vWPM zr-JE>iu9DZK`#R5gQO{nyGDALY!l@M&eZsc*j*H~l4lD)8S?R*nrdxn?ELUR4kxK? zH(t9IM~^mfPs9WxR>J{agadQg@N6%=tUQ8Bn++TC|Hbqn*q;WydeNIS@gt|3j!P`w zxCKoeKQ*WBlF%l4-apIhERKl(hXS1vVk$U?Wifi)&lL6vF@bmFXmQEe{=$iG)Zt*l z0df@_)B-P_^K2P7h=>OIQ6f0Q-E@|M?$Z5n^oN>2_sBCpN>q(LnqUoef{tm^5^L$# z{<SL zKmH78cHX`4cBKIY8u1x*lwrgP^fJ%E&&AmHrRY7^hH*=2OA9K?!+|~Aeia=nAA`5~ z#zI=h#I>@FXaGk(n)0uqelNY;A5I9obE~OjsuW!%^NxK*52CfBPWYuw--v<1v|B>h z8R=#$TS-Pt3?d@P+xqmYpL4oB8- z>w99}%xqy9W!A^ODfLq8iA@z}10u?o#nG#MXumSaybi(S{`wIM z&nE3n2gWWMu93EvtofWzvG2{v;$ysuw^8q?3n}y=pB1vUr5gi++PjiyBH3jzKBRny zSO~O++1ZLdy7v7VzS&$yY;^Z7*j_#BI`PK`dAzJa9G1{9ahPqPi1C}ti+L)WHii*= z+RZ^+at-tlatc4|akPa&9H;%gn9aS`X_kfb>n>#NTyUVM6m4NCIfLm(28>qaYv7}t zn`M;XcONtXoa3#u3{L-ytd_&g z2mO$8CnE?460w#eSm|smlnNwFHM;A&IxSKLzVkV7nNVqZ*A`)eI{Nbg6WxsarAFuc=FFf1z|%#eTvBgUhY}N zsCT>`_YO>14i^vFX0KXbARLItzT{TeD%N~=ovGtZ6j{>PxkuYlHNTe0!u>rgw#?td z{)n=QrGvgCDE6BUem$Rh(1y!$@(Bn!k3E0|>PQ(8O==zN`?yBhAqlWyq+c%+h?p^- zE&OtLind}^_=>pbhxOgOIC0q9{cLK6p6*eg_|S+p9$W~_u4wzx@N?$QmFg2S)m~^R znni$X{U*!lHgdS@fI;|Owl=9Gwi?dr0m#>yL<8<}bLW_Kpl| zSGesADX&n?qmHC`2GyIev^hi~ka}ISZ^Y4w-yUzyPxaJB0mm%ww^>if3<;P^U+L5=s+cifT-ct*;!dOOk#SOZNv@a^J|DrS3YtSn8EEAlabX1NV3RfHwZn_41Xa z4;$taa6JJR()-FQ<#0G~WlML<l5I+IPnqDpW(PP>hRcQ+S2zU?tbG^(y z1K_?1R){jF;OKGw0WYjnm>aPxnmr5?bP?^B-|Fv`TT4ecH3O`Z3`X_r;vgFn>t1tE zGE6W2PODPKUj+@a%3lB;lS?srE5lp(tZ;uvzrPb){f~n7v_^z! z=16!Vdm!Q0q#?jy0qY%#0d^J8D9o)A;Rj!~j%u>KPs-tB08{4s1ry9VS>gW~5o^L; z7vyjmfXDGRVFa@-mis2!a$GI@9kE*pe3y_C3-$iVGUTQzZE+%>vT0=r|2%xMDBC@>WlkGU4CjoWs@D(rZ zS1NB#e69fvI^O#5r$Hj;bhHPEE4)4q5*t5Gyjzyc{)o459VkEhJ$%hJUC&67k z7gdo`Q*Jm3R&?ueqBezPTa}OI9wqcc;FRTcfVXob^z|dNIB0hMkHV26$zA%YgR$sM zTKM61S}#wJ#u+0UDE3N+U*~Tz1nnV;W<8Akz&6M7-6mIF(Pq`wJ1A%loYL( zIS;&2((xbyL7zoyaY2Sa%BBYBxo6Aa*53`~e@|RA`MP+?iI4KZ+y4EU&I zS_|(#*&j2hxpELa3r0O7ok&5!ijRiRu9i-_3cdnydZU9Mp6Y);skv%!$~`i-J7e-g zj@EoHf+gtcrKf;tY5`4iLnWSHa)9brUM$XmEzG3T0BXTG_+0}p7uGLs^(uYh0j$;~ zT1&~S%_Y5VImvf1EkD7vP-@F%hRlBe{a@T!SW(4WEQd1!O47*Crf@u-TS==48iR5x z!*`Ul4AJI^vIVaN3u5UifXBX{fJ@z>4Q2#1?jpcdLocwymBgKrZ+^Cb@QuIxl58B* zD{t-W3;M;{MGHm_@&n(6A-AsD;JO#>J3o4ru{hy;k;8?=rkp0tadEEcHNECoTI(W31`El-CI0eWQ zWD4&2ehvACkLCjG`82T`L^cNNC4Oo2IH(T4e;C75IwkJ&`|ArqSKD}TX_-E*eeiU& ziUuAC)A?d>-;@9Jcmsdca>@q1`6vzo^3etEH%1Gco&gvC{;Y-qyJ$Re`#A!5Kd((5 z6sSiKnA20uPX0**Mu&6tNgTunUR1sodoNmDst1&wz8v7AG3=^huypTi`S7+GrO$D6 z)0Ja-y5r?QQ+&jVQBjitIZ`z2Ia}iXWf#=#>nU+ zL29$)Q>f#o<#4deo!Kuo@WX{G(`eLaf%(_Nc}E`q=BXHMS(Os{!g%(|&tTDIczE_# z5y%wjCp9S?&*8bS3imJi_9_COC)-_;6D9~8Om@?U2PGQpM^7LKG7Q~(AoSRgP#tZfVDF_zr;_U*!F9qsbVQ@un9O2>T4M5tr0B~~v_@a=w^8h510a#=L z;8+9zhV}57uajb+9DbZm1G`_NqOuKN`bQ2fw9A*v*Kdb_E-SA`?2 z)OFIY-%uD`JZUZg?D4lHtNegKgWr!1m%hOpu5`R+bZ2K#&)*R-7ElKYo0$0xYxIL8 zLg%u|4oZixz}ILB-@aS4=XOe)z!VL6@?dX{LW^YCPjKtyw44)xT=H;h(fmFr>R?p%r5*}W z7_bo0drVDRq9V9QL4_!dazughK6t}tVVvBq={T0+3(1zmb>f+|;{D%J?^xnZcqio5 z%H?@L+L-CIdO=x6QrALL9&PwvjrZi5NS)1e<*%V8ntw~S2PF}zH}B5f_DHyB=I3m@ z_;^TpN|sesCU}qxQ`~jIwF>#8wGvxg9kdMT$}us8BM&W>OzZ|ry2BB)+UY*_yH+&L zl_=Jy9BNzIZs}D~Yv_H%HPjVGNV=xT3xpIW!Np1F^G#9Y8X zl)c_V1(DhYu-v%H3-m&n%M_}}c{E5Wu+6*>R24gW_A7$(U=9D|H$r;;;@o zJ)c_CmVf9l*;4SyJ}E{+4)}^C>SIJ*_bul7OJ{v&0oO>jG(5xzYP0$I%*YH|Mwu#r zubNW5VZ9^X#Phw<;?=^G?Kg&C)^x1FVsKGZ*n+{C1znj~YHSP?6PS(k5e9qGvS4X* z=1kA_27(iV65a(i+Sicmd@Vzf^2@*Wed-`aYQ~em=-h%Pu`gHfz)&@$hpr<&mNO={ zl^kI0HP0wTbbh{d(>5a#;zT2_=ppef?;D4;2^}&kZjB^yl%LBJ;|> zkLc)JEg*5rpQ;_)w?PnKynWtv!@ z>}+am{@(g$KKM+e$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/AppInfo.xcconfig b/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 000000000000..ef311e2bba6f --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.fileSelectorExample + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2020 The Flutter Authors. All rights reserved. diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Debug.xcconfig b/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 000000000000..36b0fd9464f4 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Release.xcconfig b/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 000000000000..dff4f49561c8 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Warnings.xcconfig b/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 000000000000..42bcbf4780b1 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/DebugProfile.entitlements b/packages/file_selector/file_selector_macos/example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 000000000000..d138bd5b0451 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + com.apple.security.files.user-selected.read-write + + + diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Info.plist b/packages/file_selector/file_selector_macos/example/macos/Runner/Info.plist new file mode 100644 index 000000000000..4789daa6a443 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/MainFlutterWindow.swift b/packages/file_selector/file_selector_macos/example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 000000000000..32aaeedceb1f --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,19 @@ +// 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 Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/packages/file_selector/file_selector_macos/example/macos/Runner/Release.entitlements b/packages/file_selector/file_selector_macos/example/macos/Runner/Release.entitlements new file mode 100644 index 000000000000..19afff14a08c --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/Runner/Release.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-write + + + diff --git a/packages/file_selector/file_selector_macos/example/macos/RunnerTests/Info.plist b/packages/file_selector/file_selector_macos/example/macos/RunnerTests/Info.plist new file mode 100644 index 000000000000..64d65ca49577 --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/RunnerTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/packages/file_selector/file_selector_macos/example/macos/RunnerTests/RunnerTests.swift b/packages/file_selector/file_selector_macos/example/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 000000000000..bffc3452c49d --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,283 @@ +// 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. + +@testable import file_selector_macos +import FlutterMacOS +import XCTest + +class TestPanelController: NSObject, PanelController { + // The last panels that the relevant display methods were called on. + public var savePanel: NSSavePanel? + public var openPanel: NSOpenPanel? + + // Mock return values for the display methods. + public var saveURL: URL? + public var openURLs: [URL]? + + func display(_ panel: NSSavePanel, for window: NSWindow?, completionHandler handler: @escaping (URL?) -> Void) { + savePanel = panel + handler(saveURL) + } + + func display(_ panel: NSOpenPanel, for window: NSWindow?, completionHandler handler: @escaping ([URL]?) -> Void) { + openPanel = panel + handler(openURLs) + } +} + +class TestViewProvider: NSObject, ViewProvider { + var view: NSView? { + get { + window?.contentView + } + } + var window: NSWindow? = NSWindow() +} + +class exampleTests: XCTestCase { + + func testOpenSimple() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let returnPath = "/foo/bar" + panelController.openURLs = [URL(fileURLWithPath: returnPath)] + + let called = XCTestExpectation() + let call = FlutterMethodCall(methodName: "openFile", arguments: [:]) + plugin.handle(call) { result in + XCTAssertEqual((result as! [String]?)![0], returnPath) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.openPanel) + if let panel = panelController.openPanel { + XCTAssertTrue(panel.canChooseFiles) + // For consistency across platforms, directory selection is disabled. + XCTAssertFalse(panel.canChooseDirectories) + } + } + + func testOpenWithArguments() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let returnPath = "/foo/bar" + panelController.openURLs = [URL(fileURLWithPath: returnPath)] + + let called = XCTestExpectation() + let call = FlutterMethodCall( + methodName: "openFile", + arguments: [ + "initialDirectory": "/some/dir", + "suggestedName": "a name", + "confirmButtonText": "Open it!", + ] + ) + plugin.handle(call) { result in + XCTAssertEqual((result as! [String]?)![0], returnPath) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.openPanel) + if let panel = panelController.openPanel { + XCTAssertEqual(panel.directoryURL?.path, "/some/dir") + XCTAssertEqual(panel.nameFieldStringValue, "a name") + XCTAssertEqual(panel.prompt, "Open it!") + } + } + + func testOpenMultiple() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let returnPaths = ["/foo/bar", "/foo/baz"] + panelController.openURLs = returnPaths.map({ path in URL(fileURLWithPath: path) }) + + let called = XCTestExpectation() + let call = FlutterMethodCall( + methodName: "openFile", + arguments: ["multiple": true] + ) + plugin.handle(call) { result in + let paths = (result as! [String]?)! + XCTAssertEqual(paths.count, returnPaths.count) + XCTAssertEqual(paths[0], returnPaths[0]) + XCTAssertEqual(paths[1], returnPaths[1]) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.openPanel) + } + + func testOpenWithFilter() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let returnPath = "/foo/bar" + panelController.openURLs = [URL(fileURLWithPath: returnPath)] + + let called = XCTestExpectation() + let call = FlutterMethodCall( + methodName: "openFile", + arguments: [ + "acceptedTypes": [ + "extensions": ["txt", "json"], + "UTIs": ["public.text", "public.image"], + ] + ] + ) + plugin.handle(call) { result in + XCTAssertEqual((result as! [String]?)![0], returnPath) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.openPanel) + if let panel = panelController.openPanel { + XCTAssertEqual(panel.allowedFileTypes, ["txt", "json", "public.text", "public.image"]) + } + } + + func testOpenCancel() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let called = XCTestExpectation() + let call = FlutterMethodCall(methodName: "openFile", arguments: [:]) + plugin.handle(call) { result in + XCTAssertNil(result) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.openPanel) + } + + func testSaveSimple() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let returnPath = "/foo/bar" + panelController.saveURL = URL(fileURLWithPath: returnPath) + + let called = XCTestExpectation() + let call = FlutterMethodCall(methodName: "getSavePath", arguments: [:]) + plugin.handle(call) { result in + XCTAssertEqual(result as! String?, returnPath) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.savePanel) + } + + func testSaveWithArguments() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let returnPath = "/foo/bar" + panelController.saveURL = URL(fileURLWithPath: returnPath) + + let called = XCTestExpectation() + let call = FlutterMethodCall( + methodName: "getSavePath", + arguments: [ + "initialDirectory": "/some/dir", + "confirmButtonText": "Save it!", + ] + ) + plugin.handle(call) { result in + XCTAssertEqual(result as! String?, returnPath) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.savePanel) + if let panel = panelController.savePanel { + XCTAssertEqual(panel.directoryURL?.path, "/some/dir") + XCTAssertEqual(panel.prompt, "Save it!") + } + } + + func testSaveCancel() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let called = XCTestExpectation() + let call = FlutterMethodCall(methodName: "getSavePath", arguments: [:]) + plugin.handle(call) { result in + XCTAssertNil(result) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.savePanel) + } + + func testGetDirectorySimple() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let returnPath = "/foo/bar" + panelController.openURLs = [URL(fileURLWithPath: returnPath)] + + let called = XCTestExpectation() + let call = FlutterMethodCall(methodName: "getDirectoryPath", arguments: [:]) + plugin.handle(call) { result in + XCTAssertEqual(result as! String?, returnPath) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.openPanel) + if let panel = panelController.openPanel { + XCTAssertTrue(panel.canChooseDirectories) + // For consistency across platforms, file selection is disabled. + XCTAssertFalse(panel.canChooseFiles) + // The Dart API only allows a single directory to be returned, so users shouldn't be allowed + // to select multiple. + XCTAssertFalse(panel.allowsMultipleSelection) + } + } + + func testGetDirectoryCancel() throws { + let panelController = TestPanelController() + let plugin = FileSelectorPlugin( + viewProvider: TestViewProvider(), + panelController: panelController) + + let called = XCTestExpectation() + let call = FlutterMethodCall(methodName: "getDirectoryPath", arguments: [:]) + plugin.handle(call) { result in + XCTAssertNil(result) + called.fulfill() + } + + wait(for: [called], timeout: 0.5) + XCTAssertNotNil(panelController.openPanel) + } + +} diff --git a/packages/file_selector/file_selector_macos/example/pubspec.yaml b/packages/file_selector/file_selector_macos/example/pubspec.yaml new file mode 100644 index 000000000000..2a11958e85cb --- /dev/null +++ b/packages/file_selector/file_selector_macos/example/pubspec.yaml @@ -0,0 +1,27 @@ +name: example +description: Example for file_selector_macos implementation. +publish_to: 'none' +version: 1.0.0 + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=2.0.0" + +dependencies: + file_selector_macos: + # When depending on this package from a real application you should use: + # file_selector_macos: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # 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: .. + file_selector_platform_interface: ^2.0.0 + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/file_selector/file_selector_macos/lib/file_selector_macos.dart b/packages/file_selector/file_selector_macos/lib/file_selector_macos.dart new file mode 100644 index 000000000000..e321d331961b --- /dev/null +++ b/packages/file_selector/file_selector_macos/lib/file_selector_macos.dart @@ -0,0 +1,121 @@ +// 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:cross_file/cross_file.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart' show visibleForTesting; +import 'package:flutter/services.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.io/file_selector_macos'); + +/// An implementation of [FileSelectorPlatform] for macOS. +class FileSelectorMacOS extends FileSelectorPlatform { + /// The MethodChannel that is being used by this implementation of the plugin. + @visibleForTesting + MethodChannel get channel => _channel; + + /// Registers the macOS implementation. + static void registerWith() { + FileSelectorPlatform.instance = FileSelectorMacOS(); + } + + @override + Future openFile({ + List? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText, + }) async { + final List? path = await _channel.invokeListMethod( + 'openFile', + { + 'acceptedTypes': _allowedTypeListFromTypeGroups(acceptedTypeGroups), + 'initialDirectory': initialDirectory, + 'confirmButtonText': confirmButtonText, + 'multiple': false, + }, + ); + return path == null ? null : XFile(path.first); + } + + @override + Future> openFiles({ + List? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText, + }) async { + final List? pathList = await _channel.invokeListMethod( + 'openFile', + { + 'acceptedTypes': _allowedTypeListFromTypeGroups(acceptedTypeGroups), + 'initialDirectory': initialDirectory, + 'confirmButtonText': confirmButtonText, + 'multiple': true, + }, + ); + return pathList?.map((String path) => XFile(path)).toList() ?? []; + } + + @override + Future getSavePath({ + List? acceptedTypeGroups, + String? initialDirectory, + String? suggestedName, + String? confirmButtonText, + }) async { + return _channel.invokeMethod( + 'getSavePath', + { + 'acceptedTypes': _allowedTypeListFromTypeGroups(acceptedTypeGroups), + 'initialDirectory': initialDirectory, + 'suggestedName': suggestedName, + 'confirmButtonText': confirmButtonText, + }, + ); + } + + @override + Future getDirectoryPath({ + String? initialDirectory, + String? confirmButtonText, + }) async { + return _channel.invokeMethod( + 'getDirectoryPath', + { + 'initialDirectory': initialDirectory, + 'confirmButtonText': confirmButtonText, + }, + ); + } + + // Converts the type group list into a flat list of all allowed types, since + // macOS doesn't support filter groups. + Map>? _allowedTypeListFromTypeGroups( + List? typeGroups) { + const String extensionKey = 'extensions'; + const String mimeTypeKey = 'mimeTypes'; + const String utiKey = 'UTIs'; + if (typeGroups == null || typeGroups.isEmpty) { + return null; + } + final Map> allowedTypes = >{ + extensionKey: [], + mimeTypeKey: [], + utiKey: [], + }; + for (final XTypeGroup typeGroup in typeGroups) { + // If any group allows everything, no filtering should be done. + if ((typeGroup.extensions?.isEmpty ?? true) && + (typeGroup.macUTIs?.isEmpty ?? true) && + (typeGroup.mimeTypes?.isEmpty ?? true)) { + return null; + } + allowedTypes[extensionKey]!.addAll(typeGroup.extensions ?? []); + allowedTypes[mimeTypeKey]!.addAll(typeGroup.mimeTypes ?? []); + allowedTypes[utiKey]!.addAll(typeGroup.macUTIs ?? []); + } + + return allowedTypes; + } +} diff --git a/packages/file_selector/file_selector_macos/macos/Classes/FileSelectorPlugin.swift b/packages/file_selector/file_selector_macos/macos/Classes/FileSelectorPlugin.swift new file mode 100644 index 000000000000..9551671d1575 --- /dev/null +++ b/packages/file_selector/file_selector_macos/macos/Classes/FileSelectorPlugin.swift @@ -0,0 +1,218 @@ +// 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 FlutterMacOS +import Foundation + +/// Protocol for showing panels, allowing for depenedency injection in tests. +protocol PanelController { + /// Displays the given save panel, and provides the selected URL, or nil if the panel is + /// cancelled, to the handler. + /// - Parameters: + /// - panel: The panel to show. + /// - window: The window to display the panel for. + /// - completionHandler: The completion handler to receive the results. + func display( + _ panel: NSSavePanel, + for window: NSWindow?, + completionHandler: @escaping (URL?) -> Void); + + /// Displays the given open panel, and provides the selected URLs, or nil if the panel is + /// cancelled, to the handler. + /// - Parameters: + /// - panel: The panel to show. + /// - window: The window to display the panel for. + /// - completionHandler: The completion handler to receive the results. + func display( + _ panel: NSOpenPanel, + for window: NSWindow?, + completionHandler: @escaping ([URL]?) -> Void); +} + +/// Protocol to provide access to the Flutter view, allowing for dependency injection in tests. +/// +/// This is necessary because Swift doesn't allow for only partially implementing a protocol, so +/// a stub implementation of FlutterPluginRegistrar for tests would break any time something was +/// added to that protocol. +protocol ViewProvider { + /// Returns the view associated with the Flutter content. + var view: NSView? { get } +} + +public class FileSelectorPlugin: NSObject, FlutterPlugin { + private let viewProvider: ViewProvider + private let panelController: PanelController + + private let openMethod = "openFile" + private let openDirectoryMethod = "getDirectoryPath" + private let saveMethod = "getSavePath" + + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel( + name: "plugins.flutter.io/file_selector_macos", + binaryMessenger: registrar.messenger) + let instance = FileSelectorPlugin( + viewProvider: DefaultViewProvider(registrar: registrar), + panelController: DefaultPanelController()) + registrar.addMethodCallDelegate(instance, channel: channel) + } + + init(viewProvider: ViewProvider, panelController: PanelController) { + self.viewProvider = viewProvider + self.panelController = panelController + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + let arguments = (call.arguments ?? [:]) as! [String: Any] + switch call.method { + case openMethod, + openDirectoryMethod: + let choosingDirectory = call.method == openDirectoryMethod + let panel = NSOpenPanel() + configure(panel: panel, with: arguments) + configure(openPanel: panel, with: arguments, choosingDirectory: choosingDirectory) + panelController.display(panel, for: viewProvider.view?.window) { (selection: [URL]?) in + if (choosingDirectory) { + result(selection?.first?.path) + } else { + result(selection?.map({ item in item.path })) + } + } + case saveMethod: + let panel = NSSavePanel() + configure(panel: panel, with: arguments) + panelController.display(panel, for: viewProvider.view?.window) { (selection: URL?) in + result(selection?.path) + } + default: + result(FlutterMethodNotImplemented) + } + } + + /// Configures an NSSavePanel based on channel method call arguments. + /// - Parameters: + /// - panel: The panel to configure. + /// - arguments: The arguments dictionary from a FlutterMethodCall to this plugin. + private func configure(panel: NSSavePanel, with arguments: [String: Any]) { + if let initialDirectory = getNonNullStringValue(for: "initialDirectory", from: arguments) { + panel.directoryURL = URL(fileURLWithPath: initialDirectory) + } + if let suggestedName = getNonNullStringValue(for: "suggestedName", from: arguments) { + panel.nameFieldStringValue = suggestedName + } + if let confirmButtonText = getNonNullStringValue(for: "confirmButtonText", from: arguments) { + panel.prompt = confirmButtonText + } + + let acceptedTypes = getNonNullValue( + for: "acceptedTypes", + from: arguments + ) as! [String: Any]? + if let acceptedTypes = acceptedTypes { + var allowedTypes: [String] = [] + let extensions = getNonNullStringArrayValue(for: "extensions", from: acceptedTypes) + let UTIs = getNonNullStringArrayValue(for: "UTIs", from: acceptedTypes) + allowedTypes.append(contentsOf: extensions) + allowedTypes.append(contentsOf: UTIs) + // TODO: Add support for mimeTypes in macOS 11+. + + if !allowedTypes.isEmpty { + panel.allowedFileTypes = allowedTypes + } + } + } + + /// Configures an NSOpenPanel based on channel method call arguments. + /// - Parameters: + /// - panel: The panel to configure. + /// - arguments: The arguments dictionary from a FlutterMethodCall to this plugin. + /// - choosingDirectory: True if the panel should allow choosing directories rather than files. + private func configure( + openPanel panel: NSOpenPanel, + with arguments: [String: Any], + choosingDirectory: Bool + ) { + panel.allowsMultipleSelection = + getNonNullValue(for: "multiple", from: arguments) as! Bool? ?? false + panel.canChooseDirectories = choosingDirectory; + panel.canChooseFiles = !choosingDirectory; + } +} + +/// Non-test implementation of PanelController that calls the standard methods to display the panel +/// either as a sheet (if a window is provided) or modal (if not). +private class DefaultPanelController: PanelController { + func display( + _ panel: NSSavePanel, + for window: NSWindow?, + completionHandler: @escaping (URL?) -> Void + ) { + let completionAdapter = { response in + completionHandler((response == NSApplication.ModalResponse.OK) ? panel.url : nil) + } + if let window = window { + panel.beginSheetModal(for: window, completionHandler: completionAdapter) + } else { + completionAdapter(panel.runModal()) + } + } + + func display( + _ panel: NSOpenPanel, + for window: NSWindow?, + completionHandler: @escaping ([URL]?) -> Void + ) { + let completionAdapter = { response in + completionHandler((response == NSApplication.ModalResponse.OK) ? panel.urls : nil) + } + if let window = window { + panel.beginSheetModal(for: window, completionHandler: completionAdapter) + } else { + completionAdapter(panel.runModal()) + } + } +} + +/// Non-test implementation of PanelController that forwards to the plugin registrar. +private class DefaultViewProvider: ViewProvider { + private let registrar: FlutterPluginRegistrar + + init(registrar: FlutterPluginRegistrar) { + self.registrar = registrar + } + + var view: NSView? { + get { + registrar.view + } + } +} + +/// Returns the value for the given key from the provided dictionary, unless the value is NSNull +/// in which case it returns nil. +/// - Parameters: +/// - key: The key to get a value for. +/// - dictionary: The dictionary to get the value from. +/// - Returns: The value, or nil for NSNull. +private func getNonNullValue(for key: String, from dictionary: [String: Any]) -> Any? { + let value = dictionary[key]; + return value is NSNull ? nil : value; +} + +/// A convenience wrapper for getNonNullValue for string values. +private func getNonNullStringValue(for key: String, from dictionary: [String: Any]) -> String? { + return getNonNullValue(for: key, from: dictionary) as! String? +} + +/// A convenience wrapper for getNonNullValue for array-of-string values. +/// - Parameters: +/// - key: The key to get a value for. +/// - dictionary: The dictionary to get the value from. +/// - Returns: The value, or an empty array for nil for NSNull. +private func getNonNullStringArrayValue( + for key: String, + from dictionary: [String: Any] +) -> [String] { + return getNonNullValue(for: key, from: dictionary) as! [String]? ?? [] +} diff --git a/packages/file_selector/file_selector_macos/macos/file_selector_macos.podspec b/packages/file_selector/file_selector_macos/macos/file_selector_macos.podspec new file mode 100644 index 000000000000..3533c3a422ec --- /dev/null +++ b/packages/file_selector/file_selector_macos/macos/file_selector_macos.podspec @@ -0,0 +1,21 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html +# +Pod::Spec.new do |s| + s.name = 'file_selector_macos' + s.version = '0.0.1' + s.summary = 'macOS implementation of file_selector.' + s.description = <<-DESC +Displays native macOS open and save panels. + DESC + s.license = { :type => 'BSD', :file => '../LICENSE' } + s.homepage = 'https://github.com/flutter/plugins/tree/main/packages/file_selector' + s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_macos' } + s.source_files = 'Classes/**/*' + s.dependency 'FlutterMacOS' + + s.platform = :osx, '10.11' + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } + s.swift_version = '5.0' +end diff --git a/packages/file_selector/file_selector_macos/pubspec.yaml b/packages/file_selector/file_selector_macos/pubspec.yaml new file mode 100644 index 000000000000..071d261c4bf8 --- /dev/null +++ b/packages/file_selector/file_selector_macos/pubspec.yaml @@ -0,0 +1,25 @@ +name: file_selector_macos +description: macOS implementation of the file_selector plugin. +repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_macos +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 +version: 0.8.2 + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=2.0.0" + +flutter: + plugin: + implements: file_selector + platforms: + macos: + dartPluginClass: FileSelectorMacOS + pluginClass: FileSelectorPlugin + +dependencies: + cross_file: ^0.3.1 + file_selector_platform_interface: ^2.0.4 + flutter: + sdk: flutter + flutter_test: + sdk: flutter diff --git a/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart b/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart new file mode 100644 index 000000000000..1c1b9c11e069 --- /dev/null +++ b/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart @@ -0,0 +1,288 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_macos/file_selector_macos.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + final FileSelectorMacOS plugin = FileSelectorMacOS(); + + final List log = []; + + setUp(() { + plugin.channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return null; + }); + + log.clear(); + }); + + test('registered instance', () { + FileSelectorMacOS.registerWith(); + expect(FileSelectorPlatform.instance, isA()); + }); + + group('openFile', () { + test('passes the accepted type groups correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + extensions: ['txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); + + final XTypeGroup groupTwo = XTypeGroup( + label: 'image', + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image'], + webWildCards: ['image/*']); + + await plugin.openFile(acceptedTypeGroups: [group, groupTwo]); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypes': { + 'extensions': ['txt', 'jpg'], + 'mimeTypes': ['text/plain', 'image/jpg'], + 'UTIs': ['public.text', 'public.image'], + }, + 'initialDirectory': null, + 'confirmButtonText': null, + 'multiple': false, + }), + ], + ); + }); + test('passes initialDirectory correctly', () async { + await plugin.openFile(initialDirectory: '/example/directory'); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypes': null, + 'initialDirectory': '/example/directory', + 'confirmButtonText': null, + 'multiple': false, + }), + ], + ); + }); + test('passes confirmButtonText correctly', () async { + await plugin.openFile(confirmButtonText: 'Open File'); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypes': null, + 'initialDirectory': null, + 'confirmButtonText': 'Open File', + 'multiple': false, + }), + ], + ); + }); + }); + group('openFiles', () { + test('passes the accepted type groups correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + extensions: ['txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); + + final XTypeGroup groupTwo = XTypeGroup( + label: 'image', + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image'], + webWildCards: ['image/*']); + + await plugin.openFiles(acceptedTypeGroups: [group, groupTwo]); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypes': >{ + 'extensions': ['txt', 'jpg'], + 'mimeTypes': ['text/plain', 'image/jpg'], + 'UTIs': ['public.text', 'public.image'], + }, + 'initialDirectory': null, + 'confirmButtonText': null, + 'multiple': true, + }), + ], + ); + }); + test('passes initialDirectory correctly', () async { + await plugin.openFiles(initialDirectory: '/example/directory'); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypes': null, + 'initialDirectory': '/example/directory', + 'confirmButtonText': null, + 'multiple': true, + }), + ], + ); + }); + test('passes confirmButtonText correctly', () async { + await plugin.openFiles(confirmButtonText: 'Open File'); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypes': null, + 'initialDirectory': null, + 'confirmButtonText': 'Open File', + 'multiple': true, + }), + ], + ); + }); + }); + + group('getSavePath', () { + test('passes the accepted type groups correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + extensions: ['txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); + + final XTypeGroup groupTwo = XTypeGroup( + label: 'image', + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image'], + webWildCards: ['image/*']); + + await plugin + .getSavePath(acceptedTypeGroups: [group, groupTwo]); + + expect( + log, + [ + isMethodCall('getSavePath', arguments: { + 'acceptedTypes': >{ + 'extensions': ['txt', 'jpg'], + 'mimeTypes': ['text/plain', 'image/jpg'], + 'UTIs': ['public.text', 'public.image'], + }, + 'initialDirectory': null, + 'suggestedName': null, + 'confirmButtonText': null, + }), + ], + ); + }); + test('passes initialDirectory correctly', () async { + await plugin.getSavePath(initialDirectory: '/example/directory'); + + expect( + log, + [ + isMethodCall('getSavePath', arguments: { + 'acceptedTypes': null, + 'initialDirectory': '/example/directory', + 'suggestedName': null, + 'confirmButtonText': null, + }), + ], + ); + }); + test('passes confirmButtonText correctly', () async { + await plugin.getSavePath(confirmButtonText: 'Open File'); + + expect( + log, + [ + isMethodCall('getSavePath', arguments: { + 'acceptedTypes': null, + 'initialDirectory': null, + 'suggestedName': null, + 'confirmButtonText': 'Open File', + }), + ], + ); + }); + group('getDirectoryPath', () { + test('passes initialDirectory correctly', () async { + await plugin.getDirectoryPath(initialDirectory: '/example/directory'); + + expect( + log, + [ + isMethodCall('getDirectoryPath', arguments: { + 'initialDirectory': '/example/directory', + 'confirmButtonText': null, + }), + ], + ); + }); + test('passes confirmButtonText correctly', () async { + await plugin.getDirectoryPath(confirmButtonText: 'Open File'); + + expect( + log, + [ + isMethodCall('getDirectoryPath', arguments: { + 'initialDirectory': null, + 'confirmButtonText': 'Open File', + }), + ], + ); + }); + }); + }); + + test('ignores all type groups if any of them is a wildcard', () async { + await plugin.getSavePath(acceptedTypeGroups: [ + XTypeGroup( + label: 'text', + extensions: ['txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ), + XTypeGroup( + label: 'image', + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image'], + ), + XTypeGroup( + label: 'any', + ), + ]); + + expect( + log, + [ + isMethodCall('getSavePath', arguments: { + 'acceptedTypes': null, + 'initialDirectory': null, + 'suggestedName': null, + 'confirmButtonText': null, + }), + ], + ); + }); +} diff --git a/script/configs/exclude_integration_macos.yaml b/script/configs/exclude_integration_macos.yaml new file mode 100644 index 000000000000..bb58d463f7d9 --- /dev/null +++ b/script/configs/exclude_integration_macos.yaml @@ -0,0 +1,2 @@ +# Can't use Flutter integration tests due to native modal UI. +- file_selector_macos From f32730db20f5c44c99f4be8de7bcdbdddbd04482 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 4 Mar 2022 13:46:17 -0500 Subject: [PATCH 005/844] [file_selector] Endorse macOS (#5001) --- .../file_selector/file_selector/CHANGELOG.md | 4 + .../file_selector/example/macos/.gitignore | 7 + .../macos/Flutter/Flutter-Debug.xcconfig | 2 + .../macos/Flutter/Flutter-Release.xcconfig | 2 + .../file_selector/example/macos/Podfile | 40 ++ .../macos/Runner.xcodeproj/project.pbxproj | 572 ++++++++++++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 87 +++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 68 +++ .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 343 +++++++++++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 19 + .../example/macos/Runner/Release.entitlements | 8 + .../file_selector/file_selector/pubspec.yaml | 5 +- script/configs/exclude_integration_macos.yaml | 1 + 30 files changed, 1268 insertions(+), 1 deletion(-) create mode 100644 packages/file_selector/file_selector/example/macos/.gitignore create mode 100644 packages/file_selector/file_selector/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/file_selector/file_selector/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/file_selector/file_selector/example/macos/Podfile create mode 100644 packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/file_selector/file_selector/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/file_selector/file_selector/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/file_selector/file_selector/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/file_selector/file_selector/example/macos/Runner/AppDelegate.swift create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/file_selector/file_selector/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Info.plist create mode 100644 packages/file_selector/file_selector/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/file_selector/file_selector/example/macos/Runner/Release.entitlements diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 65c41651935c..dde818dbce52 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.4 + +* Adds an endorsed macOS implementation. + ## 0.8.3 * Adds an endorsed Windows implementation. diff --git a/packages/file_selector/file_selector/example/macos/.gitignore b/packages/file_selector/file_selector/example/macos/.gitignore new file mode 100644 index 000000000000..746adbb6b9e1 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/packages/file_selector/file_selector/example/macos/Flutter/Flutter-Debug.xcconfig b/packages/file_selector/file_selector/example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 000000000000..4b81f9b2d200 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/file_selector/file_selector/example/macos/Flutter/Flutter-Release.xcconfig b/packages/file_selector/file_selector/example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 000000000000..5caa9d1579e4 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/file_selector/file_selector/example/macos/Podfile b/packages/file_selector/file_selector/example/macos/Podfile new file mode 100644 index 000000000000..dade8dfad0dc --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Podfile @@ -0,0 +1,40 @@ +platform :osx, '10.11' + +# 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', 'ephemeral', '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 Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj b/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..c84862c67576 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,572 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* example.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..fb7259e17785 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..1d526a16ed0f --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/file_selector/file_selector/example/macos/Runner/AppDelegate.swift b/packages/file_selector/file_selector/example/macos/Runner/AppDelegate.swift new file mode 100644 index 000000000000..5cec4c48f620 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/AppDelegate.swift @@ -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 Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..a2ec33f19f11 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..3c4935a7ca84f0976aca34b7f2895d65fb94d1ea GIT binary patch literal 46993 zcmZ5|3p`X?`~OCwR3s6~xD(})N~M}fiXn6%NvKp3QYhuNN0*apqmfHdR7#ShNQ99j zQi+P9nwlXbmnktZ_WnO>bl&&<{m*;O=RK!cd#$zCdM@AR`#jH%+2~+BeX7b-48x|= zZLBt9*d+MZNtpCx_&asa{+CselLUV<<&ceQ5QfRjLjQDSL-t4eq}5znmIXDtfA|D+VRV$*2jxU)JopC)!37FtD<6L^&{ia zgVf1p(e;c3|HY;%uD5<-oSFkC2JRh- z&2RTL)HBG`)j5di8ys|$z_9LSm^22*uH-%MmUJs|nHKLHxy4xTmG+)JoA`BN7#6IN zK-ylvs+~KN#4NWaH~o5Wuwd@W?H@diExdcTl0!JJq9ZOA24b|-TkkeG=Q(pJw7O;i z`@q+n|@eeW7@ z&*NP+)wOyu^5oNJ=yi4~s_+N)#M|@8nfw=2#^BpML$~dJ6yu}2JNuq!)!;Uwxic(z zM@Wa-v|U{v|GX4;P+s#=_1PD7h<%8ey$kxVsS1xt&%8M}eOF98&Rx7W<)gY(fCdmo{y*FPC{My!t`i=PS1cdV7DD=3S1J?b2<5BevW7!rWJ%6Q?D9UljULd*7SxX05PP^5AklWu^y` z-m9&Oq-XNSRjd|)hZ44DK?3>G%kFHSJ8|ZXbAcRb`gH~jk}Iwkl$@lqg!vu)ihSl= zjhBh%%Hq|`Vm>T7+SYyf4bI-MgiBq4mZlZmsKv+S>p$uAOoNxPT)R6owU%t*#aV}B z5@)X8nhtaBhH=={w;Du=-S*xvcPz26EI!gt{(hf;TllHrvku`^8wMj7-9=By>n{b= zHzQ?Wn|y=;)XM#St@o%#8idxfc`!oVz@Lv_=y(t-kUC`W)c0H2TX}Lop4121;RHE(PPHKfe_e_@DoHiPbVP%JzNudGc$|EnIv`qww1F5HwF#@l(=V zyM!JQO>Rt_PTRF1hI|u^2Uo#w*rdF*LXJky0?|fhl4-M%zN_2RP#HFhSATE3&{sos zIE_?MdIn!sUH*vjs(teJ$7^7#|M_7m`T>r>qHw>TQh?yhhc8=TJk2B;KNXw3HhnQs za(Uaz2VwP;82rTy(T3FJNKA86Y7;L(K=~BW_Q=jjRh=-k_=wh-$`nY+#au+v^C4VV z)U?X(v-_#i=3bAylP1S*pM_y*DB z2fR!imng6Dk$>dl*K@AIj<~zw_f$T!-xLO8r{OkE(l?W#W<={460Y02*K#)O4xp?W zAN+isO}!*|mN7B#jUt&!KNyFOpUxv&ybM>jmkfn8z^llBslztv!!`TBEPwu;#eR3d z@_VDa)|ByvXx1V=^Up4{;M8ji3FC7gm(C7Ty-#1gs+U<{Ouc(iV67{< zam#KwvR&s=k4W<13`}DxzJ9{TUa97N-cgWkCDc+C339)EEnC@^HQK6OvKDSCvNz(S zOFAF_6omgG!+zaPC8fBO3kH8YVBx9_AoM?->pv~@$saf(Myo|e@onD`a=;kO*Utem ze=eUH&;JB2I4}?Pm@=VnE+yb$PD~sA5+)|iH3bi|s?ExIePeoAMd(Z4Z%$mCu{t;B9(sgdG~Q}0ShAwe!l8nw0tJn zJ+m?ogrgty$3=T&6+JJa!1oS3AtQQ1gJ z3gR1<=hXU>{SB-zq!okl4c+V9N;vo4{fyGeqtgBIt%TPC1P&k!pR-GZ7O8b}9=%>3 zQrV%FQdB+CcCRKK)0}v>U25rbQk(1^9Ax|WcAo5?L(H&H@%zAoT2RH$iN6boyXpsYqME}WJZI6T%OMlkWXK>R`^7AHG&31 z&MIU}igQ7$;)7AEm#dXA+!I&6ymb7n6D;F7c$tO3Ql(`ht z1sFrzIk_q5#=!#D(e~#SdWz5K;tPF*R883Yu>*@jTeOGUjQekw zM+7HlfP{y8p}jA9bLfyKC_Ti8k#;AVp@RML^9MQp-E+Ns-Y zKA!aAZV-sfm<23fy#@TZZlQVQxH%R7rD}00LxHPUF!Yg3%OX ziDe4m<4fp{7ivBS?*AlJz$~vw5m)Ei8`|+~xOSqJ$waA0+Yys$z$9iN9TIXu8 zaYacjd09uRAsU|)g|03w`F|b1Xg#K~*Mp2X^K^)r3P^juoc}-me&YhkW3#G|H<~jK zoKD?lE@jOw7>4cpKkh!8qU!bF(i~Oa8a!EGy-j46eZYbKUvF=^^nq`EtWFK}gwrsB zeu<6~?mk+;+$whP)8ud8vjqh+NofU+Nu`~|pb&CN1y_idxxf6cGbT=fBZR_hl&G)GgnW$*oDrN-zz;cKs18n+dAn95w z)Y>l6!5eYpebJGw7it~Q5m}8$7@%p&KS=VtydFj4HPJ{xqUVS_Ih}c(^4nUdwG|0% zw8Fnm{IT`8MqoL(1BNtu_#7alS@3WSUUOFT@U*`V!zrPIeCbbO=pE%|g92$EU|lw; z^;^AqMVWVf-R5^OI79TzIyYf}HX%0Y)=aYH;EKo}?=R~ZM&s&F;W>u%hFUfNafb;- z8OkmkK3k||J#3`xdLuMJAhj9oPI?Cjt}cDN7hw26n7irWS0hsy`fs&Y?Y&(QF*Nu! z!p`NggHXaBU6$P42LkqnKsPG@363DHYGXg{!|z6VMAQt??>FK1B4x4{j;iY8A+7o% z*!0qt&w+w#Ob@pQp;q)u0;v^9FlY=AK>2!qku)!%TO<^lNBr!6R8X)iXgXi^1p`T8 z6sU@Y_Fsp6E89E1*jz~Tm2kF=mjYz_q99r^v0h-l7SP6azzL%woM6!7>IFWyizrNwAqoia3nN0q343q zFztMPh0)?ugQg5Izbk{5$EGcMzt*|=S8ZFK%O&^YV@V;ZRL>f!iG?s5z{(*Xq20c^ z(hkk~PljBo%U`$q>mz!ir7chKlE-oHA2&0i@hn4O5scsI&nIWsM>sYg;Ph5IO~VpT z%c-3_{^N>4kECzk?2~Z@V|jWio&a&no;boiNxqXOpS;ph)gEDFJ6E=zPJ$>y5w`U0 z;h9_6ncIEY?#j1+IDUuixRg&(hw+QSSEmFi%_$ua$^K%(*jUynGU@FlvsyThxqMRw z7_ALpqTj~jOSu2_(@wc_Z?>X&(5jezB6w-@0X_34f&cZ=cA-t%#}>L7Q3QRx1$qyh zG>NF=Ts>)wA)fZIlk-kz%Xa;)SE(PLu(oEC8>9GUBgd$(^_(G6Y((Hi{fsV; zt*!IBWx_$5D4D&ezICAdtEU!WS3`YmC_?+o&1RDSfTbuOx<*v`G<2SP;5Q4TqFV&q zJL=90Lcm^TL7a9xck}XPMRnQ`l0%w-fi@bRI&c*VDj!W4nj=qaQd$2U?^9RTT{*qS_)Q9OL>s}2P3&da^Pf(*?> z#&2bt;Q7N2`P{{KH@>)Tf5&za?crRmQ%8xZi<9f=EV3={K zwMet=oA0-@`8F;u`8j-!8G~0TiH5yKemY+HU@Zw3``1nT>D ziK465-m?Nm^~@G@RW2xH&*C#PrvCWU)#M4jQ`I*>_^BZB_c!z5Wn9W&eCBE(oc1pw zmMr)iu74Xl5>pf&D7Ml>%uhpFGJGyj6Mx=t#`}Mt3tDZQDn~K`gp0d)P>>4{FGiP$sPK*ExVs!1)aGgAX z6eA;-9@@Muti3xYv$8U{?*NxlHxs?)(6%!Iw&&l79K86h+Z8;)m9+(zzX?cS zH*~)yk)X^H1?AfL!xctY-8T0G0Vh~kcP=8%Wg*zZxm*;eb)TEh&lGuNkqJib_}i;l z*35qQ@}I#v;EwCGM2phE1{=^T4gT63m`;UEf5x2Get-WSWmt6%T6NJM`|tk-~4<#HHwCXuduB4+vW!BywlH8murH@|32CNxx7} zAoF?Gu02vpSl|q1IFO0tNEvKwyH5V^3ZtEO(su1sIYOr{t@Tr-Ot@&N*enq;Je38} zOY+C1bZ?P~1=Qb%oStI-HcO#|WHrpgIDR0GY|t)QhhTg*pMA|%C~>;R4t_~H1J3!i zyvQeDi&|930wZlA$`Wa9)m(cB!lPKD>+Ag$5v-}9%87`|7mxoNbq7r^U!%%ctxiNS zM6pV6?m~jCQEKtF3vLnpag``|bx+eJ8h=(8b;R+8rzueQvXgFhAW*9y$!DgSJgJj% zWIm~}9(R6LdlXEg{Y3g_i7dP^98=-3qa z$*j&xC_$5btF!80{D&2*mp(`rNLAM$JhkB@3al3s=1k^Ud6HHontlcZw&y?`uPT#a za8$RD%e8!ph8Ow7kqI@_vd7lgRhkMvpzp@4XJ`9dA@+Xk1wYf`0Dk!hIrBxhnRR(_ z%jd(~x^oqA>r>`~!TEyhSyrwNA(i}={W+feUD^8XtX^7^Z#c7att{ot#q6B;;t~oq zct7WAa?UK0rj0yhRuY$7RPVoO29JV$o1Z|sJzG5<%;7pCu%L-deUon-X_wAtzY@_d z6S}&5xXBtsf8TZ13chR&vOMYs0F1?SJcvPn>SFe#+P3r=6=VIqcCU7<6-vxR*BZUm zO^DkE{(r8!e56)2U;+8jH4tuD2c(ptk0R{@wWK?%Wz?fJckr9vpIU27^UN*Q$}VyHWx)reWgmEls}t+2#Zm z_I5?+htcQl)}OTqF<`wht89>W*2f6e)-ewk^XU5!sW2A2VtaI=lggR&I z;Rw{xd)WMqw`VUPbhrx!!1Eg_*O0Si6t@ny)~X^Gu8wZZDockr)5)6tm+<=z+rYu? zCof+;!nq6r9MAfh zp4|^2w^-3vFK~{JFX|F5BIWecBJkkEuE%iP8AZ z^&e|C+VEH&i(4Y|oWPCa#C3T$129o5xaJa=y8f(!k&q+x=M|rq{?Zw_n?1X-bt&bP zD{*>Io`F4(i+5eE2oEo6iF}jNAZ52VN&Cp>LD{MyB=mCeiwP+v#gRvr%W)}?JBTMY z_hc2r8*SksC%(pp$KGmWSa|fx;r^9c;~Q(Jqw1%;$#azZf}#Fca9NZOh{*YxV9(1ivVA^2Wz>!A&Xvmm-~{y8n!^Jdl8c>`J#=2~!P{ zC1g_5Ye3={{fB`R%Q|%9<1p1;XmPo5lH5PHvX$bCIYzQhGqj7hZ?@P4M0^mkejD|H zVzARm7LRy|8`jSG^GpxRIs=aD>Y{Cb>^IwGEKCMd5LAoI;b{Q<-G}x*e>86R8dNAV z<@jb1q%@QQanW1S72kOQ$9_E#O?o}l{mHd=%Dl{WQcPio$baXZN!j{2m)TH1hfAp{ zM`EQ=4J`fMj4c&T+xKT!I0CfT^UpcgJK22vC962ulgV7FrUrII5!rx1;{@FMg(dIf zAC}stNqooiVol%%TegMuWnOkWKKA}hg6c)ssp~EnTUVUI98;a}_8UeTgT|<%G3J=n zKL;GzAhIQ_@$rDqqc1PljwpfUwiB)w!#cLAkgR_af;>}(BhnC9N zqL|q8-?jsO&Srv54TxVuJ=rfcX=C7{JNV zSmW@s0;$(#!hNuU0|YyXLs{9$_y2^fRmM&g#toh}!K8P}tlJvYyrs6yjTtHU>TB0} zNy9~t5F47ocE_+%V1(D!mKNBQc{bnrAbfPC2KO?qdnCv8DJzEBeDbW}gd!g2pyRyK`H6TVU^~K# z488@^*&{foHKthLu?AF6l-wEE&g1CTKV|hN7nP+KJnkd0sagHm&k{^SE-woW9^fYD z7y?g*jh+ELt;$OgP>Se3o#~w9qS}!%#vBvB?|I-;GM63oYrJ}HFRW6D+{54v@PN8K z2kG8`!VVc+DHl^8y#cevo4VCnTaPTzCB%*)sr&+=p{Hh#(MwaJbeuvvd!5fd67J_W za`oKxTR=mtM7P}i2qHG8=A(39l)_rHHKduDVA@^_Ueb7bq1A5#zHAi**|^H@fD`_W z#URdSG86hhQ#&S-Vf_8b`TIAmM55XhaHX7}Ci-^(ZDs*yb-WrWV&(oAQu3vMv%u$5 zc;!ADkeNBN_@47r!;%G3iFzo;?k)xTS-;1D-YeS5QXN7`p2PzGK~e6ib;8COBa5)p zfMn}dA--&A12~zr&GVk?qnBGfIEo`5yir;-Q;ZLn{Fimdrk;e!)q`sAkYh^~^>4Q@ zN5RT>s38+`V{|6@k&vZW!W0*BEqV&~34d+Ev8h)ObYL7Bd_hgbUzjdJaXP=S@Dp6X z)i013q3K4Gr5d%2YIp>218pYK!xwH;k)j?uUrT-yVKLg*L3y~=a+qd!RWGTL`z>29 z-Zb4Y{%pT%`R-iA#?T58c-i@?jf-Ckol9O>HAZPUxN%Z=<4ad9BL7n`_kH0i#E(m& zaNb039+z~ONUCLsf_a|x*&ptU?`=R*n}rm-tOdCDrS!@>>xBg)B3Sy8?x^e=U=i8< zy7H-^BPfM}$hf*d_`Qhk_V$dRYZw<)_mbC~gPPxf0$EeXhl-!(ZH3rkDnf`Nrf4$+ zh?jsRS+?Zc9Cx7Vzg?q53ffpp43po22^8i1Obih&$oBufMR;cT2bHlSZ#fDMZZr~u zXIfM5SRjBj4N1}#0Ez|lHjSPQoL&QiT4mZn=SxHJg~R`ZjP!+hJ?&~tf$N!spvKPi zfY;x~laI9X`&#i#Z}RJ`0+MO_j^3#3TQJu2r;A-maLD8xfI+2Y*iDf4LsQ$9xiu?~ z?^wHEf^qlgtjdj(u_(W5sbGx1;maVPDHvI-76u2uUywf;>()=e>0le;bO0LIvs)iy z*lJTO+7gyf^)2uS-PhS_O-+RToQmc6VT>ej^y^stNkwIxUg?E|YMAAwQ}U!dC&cXL ziXKU?zT~xbh6C};rICGbdX~;8Z%L~Jdg|`senVEJo-CiDsX47Kc`;EiXWO<9o)(`4 zGj(9@c+Me=F~y(HUehcAy!tkoM&e1y#(qqCkE(0lik_U>wg8vOhGR(=gBGFSbR`mh zn-%j3VTD4 zwA1Kqw!OSgi_v0;6?=Bk4Z{l-7Fl4`ZT535OC{73{rBwpNHMPH>((4G`sh zZhr!v{zM@4Q$5?8)Jm;v$A2v$Yp9qFG7y`9j7O-zhzC+7wr3Cb8sS$O{yOFOODdL) zV2pU{=nHne51{?^kh%a$WEro~o(rKQmM!p?#>5Pt`;!{0$2jkmVzsl|Nr^UF^IHxG z8?HmZEVMY~ec%Ow6hjfg6!9hCC4xY?V;5Ipo-myV=3TmfT^@XkKME`+=_inm4h7ki z->K~a+20?)zic^zc&7h=0)T{Aa24FU_}(O|9DMW3Bf>MW=O%~8{unFxp4}B+>>_KN zU%rKs3Va&&27&OX4-o&y2ie|sN2p-=S^V<2wa2NUQ4)?0e|hgna*1R7(#R_ys3xmG zE#(ry+q=O~&t|RX@ZMD`-)0QmE*x%SBc(Yvq60JtCQ4RL(gdA(@=}0rYo5yKz36bW zkvLOosP6I?7qH!rce(}q@cH-{oM2ThKV2RZe+{{25hkc?T>=Tky12xHr0jmfH@SZi zLHPJ@^Oo^Zo%`gZk_hrbCzS+t|=O!Bt zWi|>M8mz~sD|Z>C1ZPf_Cs&R!S5E2qK+@j*UpP>;5_|+h+y{gb=zub7#QKSUabet# zFH2H0ul;zO+uc+V=W_W@_Ig-791T7J9&=5)wrBE?JEHS_A6P~VQ)u6s1)Pu|VxP(aYJV*(e<)(42R zm3AK>dr1QLbC1RMoQ|M5k+TWBjY9q+_vY=K-tUte35m4RWl51A<4O0ptqV3)KzL7U z0gpp-I1)|zvtA8V7-e-o9H)lB_Rx6;Bu7A2yE)6)SuDqWDs}~Ojfk?DFwI% z3E1(>LbbB7I(&E@B7nlulhvY=Wa1mGXD@ijD7WF^y@L1e55h)-hzoq}eWe!fh9m3V{)x^6F8?ed1z>+4;qW6A4hYYj zZCYP=c#I8+$pAIVyiY*#%!j3ySAnH`tp|=^lh{)#JimWaP_rXK40A0WcsEUj`G1}O zG?XQ~qK4F!lqauv6-BL_Up3+-l1=kVfD;D*C)yr>o9>W=%mIyATtn_OBLK+h@p)j5jRAb;m&Ok?TZH-5Q)~#UwdYFp~rEE{judWa9E)z zE>135C-xMdHYY&AZGR)tb`K}s0CK9 z1!))p^ZaUC*e50t`sL+)@`)#kJ}?C_cCMH@k{f4wh~0`OFnGQ2nzUuuu;=r4BYRcI z){G#a6Y$S(mIc6B#YS;jFcU{0`c)Raa$nG+hV(K|2|^ZWOI566zlF0N;t~$jD<_AX zjnD?HN-G>xRmHwtL3BcJX7)Q^YGfc?cS4Nj=yYl5MB(uBD?r@VTB|mIYs=au$e)e{ zLHWd!+EN*v2*(=y%G1JzyQdY&%|?~R5NPb)`S2dw1AJW8O;L=p?yVxJs=X?U#-l1O zk6xh8yyY;OTR7aF{P=kQ>y`*EFivnw%rQioA-I67WS+~hVamG4_sI)(Jo4vHS|@F@ zqrBHbxHd_Y8+?8Gfq=Z1O^Fs5moGayCHVUHY^8)^j)Aj*RB!S2-FA?4#-`puwBW`` zJ_6OQj(FGo8DotHYRKq;;$4xDn9=4rgw}5xvxhi)?n?W5{*%4%h9Tg)zlQl&fN~Z1)gL(Dn7X!P428I zwA+U-x5!cQ57g1N=2bLqAWF z!&cbvsD)dvYoqP5vaQz%rL@kv*J>0AMzWAKn~Mxi5g2GlI7qvVZo)Z5oj=#O!M&*O z`3O3)uvrjNTeremC}nW@(m%#E-sITB>j-!yBM#(=FN`~c#@XjL3e)SjR9&%QO%tUg zzGv=SLH()`ZIt?Ayym;9VG1Muq+a+7Zo+59?SuRu_`k>@S4!yS3roMnq+SDO?`C7V#2 z8vHf4&0k;{kLT)fa==7EILSu3e|ZnxtFO;1 zGqP-;Xo(>_QKcYUhsi-X72BqH#7Zb-TsiNIF>G9xOHT3XoA*qX^10+#XCU0)UO4_%A_s_vO=uDd3_Q%D{OsvLMW9wGvuuRnF52{2vH06D~7N672!bIMt@it_D}& zwjZ7gV!RzZ86*wbEB5cnMJRbEqMM{G!K)bfJjyPH^9nGnrOI9S{~!dm4~P#&b*~)h zCMwM8mR+y5i~E5*JAopwZ>F`=ORfA&IF%O8(aS<}^H6wcY1g^=lYLPtFpyvW9F z3;FCS-TGFYPr#Y$ue>}?rTYrmWr^VbUu>!eL$cEdh1e>5_UDnZ@Mu$l*KVo_NDEu^ zBn*!qVnzYv>t|<(>nt8%CoNPhN!qGP|sANRN^#+2YSSYHa>R1mss->c0f=#g@U58@? zA4sUbrA7)&KrTddS0M6pTSRaz)wqUgsT3&8-0eG|d;ULOUztdaiD3~>!10H`rRHWY z1iNu6=UaA8LUBoaH9G*;m`Mzm6d1d+A#I8sdkl*zfvbmV0}+u` zDMv=HJJm?IOwbP;f~yn|AI_J7`~+5&bPq6Iv?ILo2kk$%vIlGsI0%nf1z9Mth8cy! zWumMn=RL1O9^~bVEFJ}QVvss?tHIwci#ldC`~&KFS~DU5K5zzneq_Q91T~%-SVU4S zJ6nVI5jeqfh~*2{AY#b(R*Ny95RQBGIp^fxDK{I9nG0uHCqc-Ib;pUUh$t0-4wX*< z=RzW~;iR3xfRnW<>5Jr5O1MP)brA3+ei@H8Hjkt7yuYIpd7c-4j%U=8vn8HD#TPJo zSe+7~Db}4U3Y^4dl1)4XuKZ67f(ZP;?TYg9te>hbAr4R_0K$oq3y5m-gb?fR$UtF9 zS~S^=aDyFSE}9W2;Okj%uoG-Um^&Qo^bB#!W?|%=6+P>``bumeA2E7ti7Aj%Fr~qm z2gbOY{WTyX$!s5_0jPGPQQ0#&zQ0Zj0=_74X8|(#FMzl`&9G_zX*j$NMf?i3M;FCU z6EUr4vnUOnZd`*)Uw#6yI!hSIXr%OF5H z5QlF8$-|yjc^Y89Qfl!Er_H$@khM6&N*VKjIZ15?&DB?);muI`r;7r0{mI03v9#31 z#4O*vNqb=1b}TjLY`&ww@u^SE{4ZiO=jOP3!|6cKUV2*@kI9Aw0ASwn-OAV~0843$1_FGl7}eF6C57dJb3grW)*jtoUd zpqXvfJSCIv4G*_@XZE?> z4Lt=jTSc*hG3`qVq!PVMR2~G-1P{%amYoIg!8Odf4~nv6wnEVrBt-R5Au=g~4=X|n zHRJGVd|$>4@y#w;g!wz>+z%x?XM^xY%iw%QoqY@`vSqg0c>n_}g^lrV))+9n$zGOP zs%d&JWT2Jjxaz`_V%XtANP$#kLLlW=OG2?!Q%#ThY#Sj}*XzMsYis2HiU2OlfeC>d z8n8j-{Npr1ri$Jv2E_QqKsbc$6vedBiugD~S`_0QjTTtX(mS}j6)6e;xdh*sp5U0aMpuN}qTP=^_Qn zh~0padPWs&aXmf6b~}{7Raglc)$~p?G89N4)&a}`izf|bA)IUmFLQ8UM$T!6siQxr z=%)pPsWYXWCNdGMS3fK6cxVuhp7>mug|>DVtxGd~O8v@NFz<+l`8^#e^KS3})bovWb^ zILp4a_9#%Y*b6m$VH8#)2NL@6a9|q!@#XOXyU-oAe)RR$Auj6?p2LEp*lD!KP{%(- z@5}`S$R)Kxf@m68b}Tr7eUTO=dh2wBjlx;PuO~gbbS2~9KK1szxbz$R|Frl8NqGn= z2RDp@$u5Obk&sxp!<;h=C=ZKPZB+jk zBxrCc_gxabNnh6Gl;RR6>Yt8c$vkv>_o@KDMFW1bM-3krWm|>RG>U`VedjCz2lAB1 zg(qb_C@Z~^cR=_BmGB@f;-Is3Z=*>wR2?r({x}qymVe?YnczkKG%k?McZ2v3OVpT* z(O$vnv}*Tle9WVK_@X@%tR^Z!3?FT_3s@jb3KBVf#)4!p~AFGgmn%1fBbZe3T53$_+UX_A!@Kz63qSLeH@8(augJDJ;RA>6rNxQYkd6t(sqK=*zv4j;O#N(%*2cdD z3FjN6`owjbF%UFbCO=haP<;Y1KozVgUy(nnnoV7{_l5OYK>DKEgy%~)Rjb0meL49X z7Fg;d!~;Wh63AcY--x{1XWn^J%DQMg*;dLKxs$;db`_0so$qO!>~yPDNd-CrdN!ea zMgHt24mD%(w>*7*z-@bNFaTJlz;N0SU4@J(zDH*@!0V00y{QfFTt>Vx7y5o2Mv9*( z1J#J27gHPEI3{!^cbKr^;T8 z{knt%bS@nrExJq1{mz2x~tc$Dm+yw=~vZD|A3q>d534za^{X9e7qF29H5yu};J)vlJkKq}< zXObu*@ioXGp!F=WVG3eUtfIA$GGgv0N?d&3C47`Zo)ms*qO}A9BAEke!nh#AfQ0d_ z&_N)E>5BsoR0rPqZb)YN}b~6Ppjyev;MMis-HkWF!az%G? z#&it84hv!%_Q>bnwch!nZKxB05M=jgiFaB^M=e-sj1xR?dPYUzZ#jua`ggyCAcWY> z-L$r#a{=;JP5X}9(ZPC&PdG~h5>_8SueX($_)Qu(;()N3*ZQH(VGnkWq^C}0r)~G3_?a10y*LsFz zokU5AKsW9DUr-ylK61shLS#4@vPcteK-Ga9xvRnPq=xSD_zC=Q_%6IuM?GpL(9aDx z|8d_;^6_D4{IQ1ndMAcFz5ZaT+Ww0wWN`xP(U#^=POs(BpKm;(H(lmYp+XCb7Kaw0 z;LT945Ev3IkhP6$lQBiMgr+vAL}{8xO&IObqJBEP4Y^x&V?iGC=1lVIbH^Z!eXxr@ zz)D7Fon`z~N|Pq>Bsue&_T9d;G+d8#@k^cq~F^I8ETsZ*cGOf*gZ4ghlAzW|aZ;WA13^B!Tlr0sWA zosgXD-%zvO-*GLU@hVV(bbQ`s@f~Ux=4}(@7O)%o5EH((gYflccBC@jbLF3IgPozv zglX2IL}kL1rtn4mu~`J(MMY83Rz6gc1}cX4RB+tZO2~;3FI# z@dU(xa5J_KvL0)oSkvwz9|!QcEA$jKR@a-4^SU3O449TrO+x$1fkBU<<=E_IHnF6> zPmZ7I2E+9A_>j6og$>Nih~b2F_^@6ef|Hm-K2(>`6ag{Vpd`g35n`yW|Jme78-cSy z2Jz7V#5=~u#0eLSh3U4uM3Smk31>xEh^-Os%&5tK6hSAX83jJi%5l!MmL4E?=FerNG#3lj^;-F1VISY!4E)__J~gY zP{o~Xo!8DW{5lsBFKL~OJiQoH>yBZ+b^};UL&UUs!Hbu7Gsf<9sLAsOPD4?-3CP{Q zIDu8jLk6(U3VQPyTP{Esf)1-trW5Mi#zfpgoc-!H>F$J#8uDRwDwOaohB(_I%SuHg zGP)11((V9rRAG>80NrW}d`=G(Kh>nzPa1M?sP;UNfGQaOMG1@_D0EMIWhIn#$u2_$ zlG-ED(PU+v<1Dd?q-O#bsA)LwrwL>q#_&75H)_X4sJK{n%SGvVsWH7@1QZqq|LM`l zDhX8m%Pe5`p1qR{^wuQ&>A+{{KWhXs<4RD< z=qU6)+btESL>kZWH8w}Q%=>NJTj=b%SKV3q%jSW>r*Qv1j$bX>}sQ%KO7Il zm?7>4%Q6Nk!2^z})Kchu%6lv-7i=rS26q7)-02q?2$yNt7Y={z<^<+wy6ja-_X6P4 zoqZ1PW#`qSqD4qH&UR57+z0-hm1lRO2-*(xN-42|%wl2i^h8I{d8lS+b=v9_>2C2> zz(-(%#s*fpe18pFi+EIHHeQvxJT*^HFj2QyP0cHJw?Kg+hC?21K&4>=jmwcu-dOqEs{%c+yaQ z2z6rB>nPdwuUR*j{BvM-)_XMd^S1U|6kOQ$rR`lHO3z~*QZ71(y(42g`csRZ1M@K7 zGeZ27hWA%v`&zQExDnc@cm9?ZO?$?0mWaO7E(Js|3_MAlXFB$^4#Zpo;x~xOEbay( zq=N;ZD9RVV7`dZNzz+p@YqH@dW*ij8g053Cbd=Mo!Ad8*L<5m1c4Kk ziuca5CyQ05z7gOMecqu!vU=y93p+$+;m=;s-(45taf_P(2%vER<8q3}actBuhfk)( zf7nccmO{8zL?N5oynmJM4T?8E))e;;+HfHZHr` zdK}~!JG}R#5Bk%M5FlTSPv}Eb9qs1r0ZH{tSk@I{KB|$|16@&`0h3m7S+)$k*3QbQ zasW2`9>hwc)dVNgx46{Io zZ}aJHHNf1?!K|P;>g7(>TefcLJk%!vM`gH8V3!b= z>YS+)1nw9U(G&;7;PV4eIl{=6DT^Vw<2Elnox;u@xF5ad*9Fo|yKgq<>*?C$jaG2j z|29>K)fI^U!v?55+kQ*d2#3}*libC4>Dl4 zIo3Jvsk?)edMnpH<|*l<*0Pf{2#KedIt>~-QiB{4+KEpSjUAYOhGDpn3H_N9$lxaP ztZwagSRY~x@81bqe^3fb;|_A7{FmMBvwHN*Xu006qKo{1i!RbN__2q!Q*A;U*g-Mz zg)-3FZ`VJdognZ~WrWW^2J$ArQAr1&jl~kWhn+osG5wAlE5W&V%GI{8iMQ!5lmV~# zeb3SKZ@?7p;?7{uviY6`Oz16t0=B70`im=`D@xJa16j2eHoCtElU*~7={YUzN41sE z#Th>DvJq-#UwEpJGKx;;wfDhShgO0cM|e!Ej){RX#~>a?)c2|7Hjhh2d=)VUVJL<^Aq|>_df4DX>b9W2$_DM zTjF#j(9?Co`yor?pK<16@{h#F&F8~1PG|qQNZPX^b!L*L&?PH#W8za0c~v6I2W($Jderl%4gufl z#s;C*7APQJP46xHqw;mUyKp3}W^hjJ-Dj>h%`^XS7WAab^C^aRu1?*vh-k2df&y9E z=0p*sn0<83UL4w30FqnZ0EvXCBIMVSY9Zf?H1%IrwQybOvn~4*NKYubcyVkBZ4F$z zkqcP*S>k6!_MiTKIdGlG+pfw>o{ni`;Z7pup#g z4tDx3Kl$)-msHd1r(YpVz7`VW=fx9{ zP}U8rJ-IP)m}~5t&0Y$~Quyjflm!-eXC?_LMGCkZtNDZf0?w<{f^zp&@U@sQxcPOZ zBbfQTFDWL_>HytC*QQG_=K7ZRbL!`q{m8IjE0cz(t`V0Ee}v!C74^!Fy~-~?@}rdn zABORRmgOLz8{r!anhFgghZc>0l7EpqWKU|tG$`VM=141@!EQ$=@Zmjc zTs`)!A&yNGY6WfKa?)h>zHn!)=Jd73@T^(m_j|Z;f?avJ{EOr~O~Q2gox6dkyY@%M zBU+#=T?P8tvGG|D5JTR}XXwjgbH(uwnW%W?9<-OQU9|6H{09v#+jmnxwaQ-V;q{v% zA8srmJX7Fn@7mr*ZQ@)haPjWVN@e3K z_`+@X$k*ocx*uF^_mTqJpwpuhBX~CSu=zPE(Sy%fYz&lzZmz3xo4~-xBBvU0Ao?;I-81*Z%8Do+*}pqg>bt^{w-`V6Sj>{Znj+ z70GS2evXinf|S#9=NNoXoS;$BTW*G0!xuTSZUY45yPE+~*&a-XC+3_YPqhd*&aQ>f z$oMUq^jjA;x#?iJKrpAqa<2<21h*_lx9a}VMib;a6c$~=PJOj6XJXJ|+rc7O7PEN5uE7!4n9nllo@BI4$VW2Nf_jqnkz%cvU4O4umV z#n6oXGWOt3tuIjmX*b!!$t~94@a@QgybLpQo3icAyU`iNbY~XNAArFAn$nFJ()d-U zFaO#nxxVF-%J{UB**uRo0*+?S>=^il)1m7v-u`PDy*ln%|3E-{3U~R=QcE&zhiG_c zDnGMgf1}3h1gWz8IV0Oc7FmEt>6W?Eva;J`(!;IIny}PvD?vztz`F6su_tUO`M%K5 z%C#=nXbX})#uE!zcq2mB;hPUVU1!`9^2K303XfOIVS{mlnMqJyt}FV=$&fgoquO+N zU6!gWoL%3N1kyrhd^3!u>?l6|cIl*t4$Z$=ihyzD7FFY~U~{RaZmfyO4+$kC7+m zo+-*f-VwpUjTi_Idyl~efx)!$GpE!h+in4G1WQkoUr<#2BtxLNn*2A>a-2BL#z%QO@w0v^{s=`*I6=ew2nUj1=mvi%^U@2#Wf& zs1@q6l8WqrqGm!)Yr|*``||#A+4#du6`mR^_#?CymIr}O!8Zm?(XY$u-RGH;?HFMGIEYVuA1& z`3RlG_y0%Mo5w@-_W$E&#>g6j5|y1)2$hg(6k<{&NsACgQQ0c8&8Tdth-{@srKE*I zAW64%AvJJ+Z-|I~8`+eWv&+k8vhdJk5%jolc%e`^%_vul0~U8t)>=bU&^ z6qXW&GDP%~1{L1-nKK>IsFgDJrh>!wr3?Vu-cmi#wn`;F`$GNc_>D|>RSuC8Vh21N z|G;J1%1YxwLZDD400Ggw+FirsoXVWYtOwg-srm}6woBb!8@OIc`P$!?kH>E55zbMB z8rdpODYfVmf>cF`1;>9N>Fl(Rov!pm=okW>I(GNJoNZ6jfIunKna-h6zXZPoZ9E2PythpyYk3HRN%xhq2c?gT$?4}Ybl42kip$QiA+ab zf-!EqBXkT1OLW>C4;|irG4sMfh;hYVSD_t6!MISn-IW)w#8kgY0cI>A`yl?j@x)hc z=wMU^=%71lcELG|Q-og8R{RC9cZ%6f7a#815zaPmyWPN*LS3co#vcvJ%G+>a3sYE`9Xc&ucfU0bB}c_3*W#V7btcG|iC>LctSZUfMOK zlIUt>NBmx6Ed}w_WQARG+9fLiRjS1;g49srN1Xi&DRd|r+zz*OPLWOu>M?V>@!i49 zPLZ3Q(99%(t|l%5=+9=t$slX0Pq(K@S`^n|MKTZL_Sj+DUZY?GU8sG=*6xu)k5V3v zd-flrufs*;j-rU9;qM zyJMlz(uBh0IkV<(HkUxJ747~|gDR6xFu?QvXn`Kr|IWY-Y!UsDCEqsE#Jp*RQpnc# z8y3RX%c2lY9D*aL!VS`xgQ^u0rvl#61yjg03CBER7-#t7Z++5h_4pw{ZZ~j0n_S_g zR=eVrlZDiH4y2}EZMq2(0#uU|XHnU!+}(H*l~J&)BUDN~&$ju@&a=s$tH5L`_wLeB z944k;)JIH^T9GEFlXiNJ6JRymqtLGZc?#Mqk2XIWMuGIt#z#*kJtnk+uS;Gp}zp$(O%LOC|U4ibw%ce-6>id$j5^y?wv zp1At~Sp7Fp_z24oIbOREU!Mji-M;a|15$#ZnBpa^h+HS&4TCU-ul0{^n1aPzkSi1i zuGcMSC@(3Ac6tdQ&TkMI|5n7(6P4(qUTCr)vt5F&iIj9_%tlb|fQ{DyVu!X(gn<3c zCN6?RwFjgCJ2EfV&6mjcfgKQ^rpUedLTsEu8z7=q;WsYb>)E}8qeLhxjhj9K**-Ti z9Z2A=gg+}6%r9HXF!Z~du|jPz&{zgWHpcE+j@p0WhyHpkA6`@q{wXl6g6rL5Z|j~G zbBS~X7QXr3Pq0$@mUH1Snk^1WJ0Fx2nTyCGkWKok$bJZV0*W?kjT|mkUpK<)_!_K^OoTjMc+CWc^~{ZP8vgm`f&=ppzKtw}cxwV^gppu}^df1|va7Q?@=(076-( z4KJVmu?l(aQwmQ*y_mke>YLW^^Rsj@diLY$uUBHL3yGMwNwb7OR3VD%%4tDW(nC984jBWCd90yY(GEdE8s(j>(uPfknLwh!i6*LX}@vvrRCG`c?EdB8uYU zqgsI4=akCeC+&iMNpVu56Fj2xZQHs6SdWssIF#Q@u@f9kab0&y*PlG+PynjHy`}GT zg%aTjRs2+7CknhTQKI%YZhFq1quSM{u24Oy2As@4g(bpbi%y1i0^TwI)%1Whpa~qE zX4MD(PgFEK@jZBPXkFd437aL6#COs$WrNT#U=er-X1FX{{v9!0AS$HR{!_u;zldwY zKko!`w2u@($c&k_3uLFE0Z*2vms?uw1A{AqZw^jwg$|D7jAY20j`s*l##=4Ne_K5) zOtu6_kziEF@vPsS7+@UwqOW6>OUwF$j{r4=nOSf-{UC(rEKidie7IUn>5`UoNJ9k) zxJXXEBQifng+Pte3mPQ76pVlZ<`jnI##F1*YFA*)ZCEncvgF-%)0dUXV*pXTT^L`n zL=?A5Vty#{R9W4K)m$`me~*_(&a88M?Eon$P-YdVG}#Gq4=hh#w=`>8f`9}}zhv;~ za?I=Gb3v$Ln?-SDTBow0J5Tt&xPlw|%`*VTyVee1Oh<-&;mA|;$ zoPl;^f7Q~}km#_#HT2|!;LEqORn%~KJaM)r#x_{PstSGOiZ!zX2c}^!ea3+HSWrwE z=6SJ!7sNDPdbVr#vnUf}hr&g@7_Yj&=sY=q(v^BwLKQm|oSB}172GpPlj?a3GqX#B zJko4zRRttIY>Fv#2b#A<_DLx=T@eUj+f}!u?p)hmN)u4(Jp(`9j58ze{&~rV?WVbP z%A=|J96mQjtD037%>=yk3lkF5EOIYwcE;uQ5J6wRfI^P3{9U$(b>BlcJF$2O;>-{+a1l4;FSlb z_LRpoy$L%S<&ATf#SE z;L?-lQlUDX_s&jz;Q1Lr@5>p_RPPReGnBNxgpD!5R#3)#thAI3ufgc^L)u%Rr+Hlb zT(pLDt%wP7<%z(utq=l%1M78jveI@T$dF#su(&>JkE(#=f4;D54l*%(-^(nfbCUQe)FV9non9F%K+KZ(4_`uOciy82CO)OolxisUd0m^cqueIRnY< z;BgA4S1&XC3uUP?U$}4o&r|0VCC7fkuMZBa|2n4asR>*5`zBaOJPWT$bNn(W_CK%L$c2AsfSlwq?A8Q6 zhK&USSV=^-4vZ^5<}pnAOb&IKseHNxv_!|B{g@d^&w%{?x;i3iSo)+vt^VnMmS!v) zM)W)05vXqzH5^hOWWw~$#&7HoIw}}DD3bCQgc=I8Rv|G5fM8O^58?--_-*>%Nwk)j zIfvfok0n05!w%tZ=-dpffezI7(+}yX5XhwYk#0@KW%PkR;%#t|P6Ze_K*N6ns%jOt zNeW(bRsv0BK7ah~9U~UBAVA_L34F+;14x6-;I|o=%>?sS3@dpRv|GKxilsa#7N#@! z!RX~>&JX&r{A^^>S~n_hPKkPR_(~~g>SuPj5Kx6VI%8BOa(Iit&xSMU8B#EY-Wr?9 zOaRPw0PEbVSW@Wk{8kkVn34;D1pV2mUXnXWp{V-M9+d}|qfb6F`!a9JQO_-wlH?zf z4Sn0F4-q-tzkaJ?1fV0+cJBF$f0g6*DL6U3y`Tr`1wzCiwY#muw7Q-Ki)uN}{MoCWP%tQ@~J4}tyr1^_bV9PScNKQHK=BZFV!`0gRe?mVxhcA4hW5?p0B<5oK+?vG^NM%B%NDOvu0FMq#)u&zt_-g&2 z7?z%~p&32OAUSQV{<=pc_j2^<;)`8$zxCEomh=rvMiliShS?ahdYI1grE-M&+qkK_ zD=5Hexi<&8qb4hgtgj81OD(tfX3EJSqy9KFcxpeBerG`apI4!#93xpEFT??vLt>kf zac28;86CpMu=BWIe$NOT~+Es!y#+$ zvm2s*c`J9Gy*ERvLSI<9<=j*O=0xUG>7rYh^R4bGsvz;j-SBO|P^OQ1>G9_akF}D; zlRmB@k3c5!s|Vz3OMZ8M*n0AMTiSt5ZpRy+R1|ckna&w`UQjklt9f&0Z~=->XImVA zLXizO2h=<|wM~w>%}3q1!E{oSq7LBPwQ~93p-peDq-W?wCm8NOKgTSz-P)|cm}S5&HBsx#C@Ba5;hzi#Yw@y-kC~)@u4}Rf?KV0$lPjv}} zcFpNy=YJfsS||9&!-JFjw=@NU96ESzU^gme0_oNy?})II`>Sy>bUCHs_(m&)vn^&isCl+`F~qu8elAO z)-ZP7`gYE2H(1)5tKalz&NJbcutAU&&JFV~$Jrai31^j>vZ|HV1f}#C1<5>F8 zS1RWIzM%b{@2dAF^$+i4p>TC8-weiLAPN+Aa#(bxXo9%Vz2NEkgF&s#_>V?YPye^_ z`` z-h3Cv^m6K%28I$e2i=cFdhZN?JTWhqJC{Q9mg0Vg|FiPEWDl&K)_;Bz_K`jH7W7QX^d$WQF*iF@#4_P*D36w9&iJr2E{w?LRFapwZIIVHGH ziTp*5>T{=;(E}z{1VL4;_H`BAXA~&zpeWX!gN9m|AfcJ{`!XVz48O^&+0Gd|w;udP zzU|DbGTS|7qZoEoDZEH9Kb0%DZvCaWDzuJ=8jZz}pqPn+I!c_+*~>m>BQqN2560*< z$6sx_y8WRqj$SugYGip+et$;iJ!SQAx=HgVSh_3e)MOFHuXD@sg>Yi_p8Sh`{lP=5 zo?AFv1h;KqR`Yj!8Pjji3lr+qae2|a1GmlxE*su%_V)K0Xu0(#2LcO!*k11w*V12$ z;f~i{kI#9PzvFLZ3pz@d558HeK2BTvk*JvS^J8L^_?q4q z);;4Z!DsV!P*M>F>FiF*{|p_nUgy;pDh?J8vwO;emgOAAcxrgDXiSDS5ag?0l*jj< z(khZ3-)>eiwPwpb6T9meeL)!2C-K@z9fF`0j|t@;^f5+dx86R3ZM{bnx9Hm1O$s)N zk$OvZR0u2`Z^QP8V%{8sEhW~_xbZMad2jtz&0+ekxmp;9`ae;_f%-ltk5E%)VT*a6 zRbMnpCLPnalu+1TafJ4M0xNV8g}U4Mjk{le6MA|0y0rk)is}M%Z9tUU22SvIAh7`w zTysd{Pztfkk=jD^*!lA+rBcqb)Fx`A5iaU2tl&XdL1D)U@pLEXdu%#YB*ol1N?4ti zHBQcU#_%UqiQ1)J^u-ovU@-7l?`YzYFvA2#tM0mEh3?CpyEh_NUuVajD16t zyg$C*5du9R=K~6mCJ`W+dFI$9WZZauO)p2H)*SKpHVsIu2CxfJvi2>; zcit#57RP7DpSwMF-VBm|4V5d=tRgX7RM9%KQ0JRo6d<)RmiIPWe2zh6tmswP`fs^) zwy};#jk|NXMqCSfwIR3QZ#W2`(%sJ>qvk=53CYoLmQt9q|2Gm$sB;rEuBqGJA1OUM zoyl4Wy-HYn0J6L=cad8o)R!Ea^;`rSMg9hYo3?Fw6B9dUq75a-MSb56n8~AAsS(JP zZ!1khPu}!GRpsj+jvl`N1tDD8m1myJCI3c-c<9U-1Vg`xJO~}5_wvPXYh^=Boo^|V z3Tp}|lH!9m4Ipa_$p;b8fjUd=zc4iO7vr)M&Xs0_m$fgY@+hB9%K~4*9$p0d)m2bO ze5JH`W0fnIKdcW!oO#^g1YceSQ4u->{>u@>tLi!fky)o&$h(=he?Fe_6?}O~iSf(F zV&(P~*5h>BW{3e1H%8*7#_%L1#>W97b0@jHtliES^w6w5oldI7QL+?I(Pl$DaN>~d5nXx z;CO1E+S?3E2PLq~)-?ygkHAO1m&hOYmj7?;2XM!$D^f0l9K4P{n}mgb{CoYH6RJ8o ztydc6dNqA)`CG?=Gd~EIbi`UM)eyzGF^+i?&TOdyW~mFH_^Gye(D}clDVFQ@V2Tvy z7rQIaq8Xx`kC;AO-_{k%VI2e6X@bIy^mupEX%{u0=KDUGu~r6lS*7GOeppy{&I&Ly zjOTz=9~jC|qWXznRbrfjg!1`cE!Hzyjzw6l{%>X)TK(UEGi9Uy3f9D6bbn0gT-s`< z8%$Msh!^8WidX7S;)n2jh_n1-QCtSyOAKcPQc(Xlf0*Q|5CSBjo(I-u!R0GJgzTkL z|6QdQRrUMbUO|q0dQ%+d^4)*Mjbm$R}RUcz(7|E0Bq-bAYY@)OsM<+2>}CV zzPBgeD~kBHE(Y+@l2orJrdtV7XXq_V8IETas%7OCYo`oi)+h&v#YN!Qpp7drXFS>6 z?r-q7px+(rIy+bo1uU#I2A5s@ASe01FgGMbouFkhbkm-9yZ8Q2@Q1vuhDQ3D3L+zA z(uz8^rc24VmE5r0Gbd;yOrXnQKAEBfa3@T7fcF$#QYv^00)VZPYehpSc@?^8we}o{ zlX0~o_I<`xSfI8xF(WXO-DX1>wJ`XN?4rw@}_RLD*${$}UaXL=oM(=SDMIxZj1Ji#jAcrH7nYG`r z#ewodj>F5Bf9j(j`a;>)=*2j_ZN}vf!~Hq`2Eyt;9UH1_(yjq1OUO(1M0lI3FZ2j-fU9)L59v&OiQ>5$;d!jg?Fo{Svf5t5FCZbb?)* zJN=Q!?2BztV$7)CWtG0MO~Lr4E5>aoHD5N4(+@~gQEbZTc4s3HrIl_G23PCng4Y3f zbLZK1A-x9x!)WwuI=UBkQ5QyE^&Nrw?@fsRKK41G9-xq=#VyO%CEo`{_eioDj%M!3x=>I zfOPFiFX{1t-|+3E@?UuK=0miGN04hW0=JnJrEyWw{Bg-jMvAA}cg<5LN1c5BQdrIZ z#+bxj9Jbu`11@IUjU|RKfL(UzRlVB4XT ze|(WaxL$KiRqkgCr3^Al(19!_Y7b=E(4Xm7LCO$y5+k;Fu6B#=OSzW`-7p{zRv-_) zPr!|km?8aF}+3hm)QG92YaI+jctX&5IrvTUGf{Y$)TK6)s9v!SMhU=HIpEC~2 z4>o14mG$El2sTA(Ct?xS!l*x7^)oo}|3+BF8QNe;bBHcqdHVmb?#cbS*NqZ%mYS~z z`KLoq7B#KULt%9a#DE%VTEo4TV03T2nr`FK5jUTA$FP0JH6F9oD*|0z1Yf2b5?H0_ zD|K|_5Zk`uu?ZN0U! z_mL>>F;mnHU=@to!Vv*s4;TQr9y)L@1BXXz^a85NSifPTL4h6I>+m_S3~FkXB{N?E zS<3ue_(wqaIS5;4e9{HB`Okl9Y}iFiju+oTqb)BY)QT?~3Oag7nGu-NB5VCOFsiRs zs@m%Ruwl^FuJ1b}g^=*_R?=SYJQ@7o>c9j>)1HgB zyN9LI9ifwu{Shlb6QO2#MWhxq~IG!U^I!6%5}(sbi>=bq8!8@s;4Iaun#kvh7NPwX34Rjbp2f!D)cF&sNIO%9~;C`cs&ZY2=d@c3PpN$YZjUT}X7rY`dlWX$yc znw(7=fzWapI=KzQnJ(6!o0K_aDk!^dZ#)pSTif+jQtQXga$bPApM z=);jZ5c*?*GoeGMnV0=RrZucRRYBjx>tx`A3OuY)#tp2w7mh}&kj)SKoAvbbf;uO! z?+RItUow0xc*6StuO4D--+qY!o}Isy}s;ts5aM5X~eJUZoLOq@dGv=a4hHJD<* z5q{dZSN{bv_(Vj#pFm7Q<$C;MwL|Qizm~QCFx~xQyJoCOZ$`sYD}}q>PwRZjb<=E< zAeMP?qVfM>xu2}Il2xT6={KBdDIstxY-`5IWXN zUiWV&Oiy5R_=2X9Y$ug9Ee=ZSCaza!>dWBMYWrq7uqp>25`btLn^@ydwz?+v?-?2V z?yVwD=rAO!JEABUU1hQ|cY+_OZ14Hb-Ef`qemxp+ZSK?Z;r!gDkJ}&ayJBx+7>#~^ zTm<>LzxR^t-P;1x3$h;-xzQgveY$^C28?jNM6@8$uJiY81sCwNi~+F=78qJZ@bIsz1CO! zgtPM~p6kaCR~-M>zpRCpQI}kUfaiZS`ez6%P6%*!$YCfF=sn}dg!593GFRw>OV2nQ ztTF6uB&}1J`r>gJuBP(z%KW{I^Uz%(^r5#$SK~%w1agl)Gg9Zy9fSK0kyLE24Z(34 zYtihZMQO^*=eY=<5R6LztHaB1AcuIrXoFuQ=7&C}L{c?Z$rto$%n=!whqoqG>#vvC z2%J5LVkU%Ta8hoM($p1WqN}wurA!d@#mQGU5Nb>~#XC84EYH)Zf&DZR!uY+-;VqS< z@q?$ggdX#auS#%%%oS^EN)?JhSR4JYpSgGRQZD<9!YvvF+zp0>C#$!x*x}l8U|Bb& zv?v*im5Bq_(5Wi40b1^nKun$XTST(a8yOAcqQZmKTgGLo)Ig6JuEh5J9NnqJXin@Gxzz-k6xXWYJ&@=JZw=$+ zFPGde%HsR`gI+y`rtiPaMYwbtyp!sVb!pX~;c3zLoPO0eaZSV+O_z z%9H@UhqNowzBTPcMfL6kC>LRaFF6KVaSv1R@%4}rtleX!EMnL`rethYrhTLj1x$tj z;)H!fKo08&T(;i|FT&rPgZ*D0d=B2dXuO_(Uaoi9+vEhs4%{AD{Fl@4^|`X=PvH(s zI7$6bWJiWndP$;&!kSCIR1l57F2?yzmZm~lA5%JKVb;1rQwj*O=^WW~`+n*+fQkK0 zydInOU1Be2`jhA!rnk1iRWR=1SOZpzFoU5{OPpc&A#j6Oc?D&>fAw=>x@H7?SN;d^ z-o&}WR;E|OR`QKItu(y4mT)%Pgqju-3uyH?Y@5>oSLO2Y(0(P!?_xOL=@5+R7rWw# z3J8%Hb@%Pzf^`=J6fEJ_aG6+e7>OUnhaO1(R1<6>f}L z?d@Wnqw9?^;2?q(b@?Wd=T6r_8a@Z4)*_@Q7A`+ zW3w?j!HW0KbhxF%D`9d2HpvIrBxM!36W3Yh5=8_0qYfnHm*yiLB?Ay|V10N%F9XYq zanaDtDk$rS+|_H_r|a${C}C7b{E)Ii20-a?Grff$E?&|gWF<#Ern2GqhCiS0~Y%knIi8zY^lE4qLaR-3M;_Rkz(s;wu z9207W1PXIe#4h4Zw}dvdV&FYcnUlD5_C4hzJ@bPSBVBLpl$&52mi+wwH;svyVIzAB zoA+NQ;Hpqh?A}^Et~xhl>YQNQwh20!muW{ zq}|Pg3jHZWnDBN?r1KhiVG$%Sm-4+=Q2MZzlNr3{#Abqb9j}KK%sHZj{Vr2y4~GIQ zA3Mz1DjQ3q(CC~OyCaZn0M2!){)S!!L~t>-wA&%01?-*H5?nzW?LJB`{r&)vLB4!K zrSm({8SeZ0w(bL9%ZZAZ*^jf=8mAjK^ZR0q9004|3%73z#`-Npqx*X^Ozbja!C1MW z-M~84#=rU1r>p{+h9JU<#K_x$eWqJ+aP%e?7KTSK&1>dlxwhQmkr69uG~0iD@y|L- zlY0vSR2|IhZoS6PpfUai_AhKo2HfdD&mhv#k51CX;T z*sU)XbDyfKjxYC$*_^(U)2-c0>GJ(zVm$CihHKlFSw&1A$mq$vsRt-!$jJe3GTaZ6 z3GcVvmwZ0D>`U+f3i*pQ>${p1UeyF~G9g~g-n{ThVOuC#9=ok`Zgz@qKCSN!1&P`N z=pdlGNwal%9;)ujwWH*#K6CQG*fJDAQiKlO2vKJHeA1lj&WQC+VU^@ea8$#~UOX$*Q!V^8L- zL0$W5(Y3=??%&j_WUq6*x>=?BfmI*d8fmDF*-!XVvxL8p7$r+}Igd_(&`|D*;Z#GE zqm{tHx&aHBpXw&~l6>7-FlyiSPJtTJblAjLU5Ho$FeN0mDguFAq?r+6^~o6|b+rfE zGVcZ&O-X~tE3liGcdI~hHSCT+&F&uH8rr&f{6pr^1y5061`fu~=^_|Idrgti5+*U7 zQOb9G?Rz$j-G0Y}x+i{HB0!4ZmKzykB<0;Rbmo2)T4|VdcwujI_otLG@@8OOKg3kw zP|0ST0D4@zT?O=(0Pikp)Rpwxw_VsmW4!^j^sFd6r5l zw}SG_HQPs>ae%Bq{sye_SaBX%|F-}&^)Wz@Xi<)YNbO?lPs7z@3c;$b^Aw@>E%mOj zW^c%IdtC(Kk@s*}9NbKxEf8SZtP+32ZTxjnrNWS7;W&D~ft{QY?oqOmxlV7JP!kW!Yj`Ur{QbbM1h=0KMaIAmWiISb7TKd4=gMeo+Tcz2>e#NihnOV%iNdx` zeiuoOK^{}D+M+p(Y7EC=&-`$B0F< zQ=zHaM;&QQR4jM$sG=N&sqOvD_Bx*drQ6c@u0()g05cwl`Xm{!S_Nuaa2KlL*rmmk z51yPE)q?Bl$sNM474Y!=zZ zc{EVGpdJ!Su{Qq%llR5O6#zK8l(ld*UVl87@|iaH@C3+*;XBxjEg&fsQrzpMo3EEG zv*Tpms7a;7!|iz8WY7={0a$0ItO-(ajXl;wX_$$yzEF5k9nc>L3wv!p{8h2)G0W?h z{v6vH=7+>$Ho^+)9hDtCd+S_yh8pzS9$)hYev-=eDu?lGIR;-fgz+dr+wcmM-^dZp z9}`&kAf$~z1ovF)>Hgxc!Xe3cju-jQRluCm;c_1=PYQygb?Oxe z!QG0L3sT_k=WpfOPL#|EPlD^t;ENCC39O?tHd<(kfx7SOcxl+E#;ff19_+{vbkZSvbS$I{#>31KZj^$n%ayX0jj}EvsgnHg16P z_A6Y)pdp>kLW<;PtR*Vs#mVb%)ao7AXw{O&hBDmD;?mc3iMH;Ac@rZZ_BQa8CQ~|0 z&d1L{in-z--lBO|pxqc%bqy^~LAGv=E*eaVU~OeuVV{d`Vv#-_W7EYdTDzVraG9H+LC_dWcgZMn~KcP)XvKWbcr5&d+=a>{*(Ha6Y1$==bR z{O-?$7H;`2dt0B%Vm?6`_?ZOjJkyu9ZJsh^WH*+es&^@KDcR%Zej%3PJ*XovgyhTbaH(!H1H_OF~=*f55Jr8A%uW zz5IoAB~1e2-tDGp9}`MnavAMy?jgPM5F%y`%$}dFLrz_* zIrO=afT8+AkK5B1s3{ZDVP$g6y$-*U*=?-fh!cNyn3q6YhNhfRxW&GLIJ2#>9bYMD7-F%{|Iw%@a=DoAAU;3k9p$`V zImKm{5HU~wq|nQFwab)_7lNckW#1z2$|oW5x7vDbBURVjw8674P?L1ogMKpHoV>;# zO%*1OwI|($UOr#hL(*M~qsn3PF%_|15uc%Hy9@D>_~N|?<%lig6yKX0a#1s$o(^Laj8bF#5fGPOFMGmMiUaxSwE}Qf#SG_f79d2Iv=TFBXzTpr$^avJ?=|arh2<+ce}&248Kw0} zhlva`wD6X~s7|37la4FnFOgIHhBiFo`lw~?lSbk{>)P(3jyVhM4O)a=GX3(sW1vIC zz0mJ>;J{!eN5#nf2>$u=3Kq>`7u9QnChi8>CjONBN-b+W_UQIuN#{N$Q<$}IOvpQP zB&5ZrY{V&D=4)voh;6<1U`PFA>V%XUW73S9D^J>cQYfzIyIV5i35WNb5K9c^|M}=* zN_C3rnjCZP1^v{;EaGK7Tp5z~B#?f5NZaAsFUOLK)mI~bJTaL8DF_eRikE{%^J?y9-n_U32EKHPCkB^ZN2*zk{bC=GM%_I z61}nkr+Plg6S0V=mY>H_KQU&)P~=y3$#$*U8FunXkb_e1O-7t@m$5re%u!_G%^?_| zRIJzg+lX$}+ba|qx)Ec6c^ip;`_QfQrD~SPa4MoyRUOtX&~^XWcO^a}KBkXK9J{ZFOA~rovYa0!7btTC*=xNQrwJ)$Eu`TT$;%V&2@y@$ISdNn ztbM7|nO+U9r;ae{{;QiNEYpe4nrFq_x3 z4Tvf^b(I@_3odwhVe!aC0X&~inrYFu# zh)+eF__8ly&nLr4KlLWl%B_ZMo=zCH2QfO^$lJ zBvU*LQ#M(5HQ}2Z9_^y~i@C#h)1C*?N3v68pY+7DD09nxowdG#_AAM5z&*|-9NcB{ z_xKUY>Ya7>TO#Bat}yM}o(~8Ck^!QHnIj8N9}c*uyIs}IEqGn`xP;q3vhW6gsqUe>`m1 z)~ad@y1=?H`1SNl?ANCs5ZD`8tG&Hi=j|R%pP(%gB8pd)Q--E?hWU@)e?>SLV4s(- z!_I^oVC0x97@I(;cnEm$ttKBnI3gXE>>`K?vAq~SK?0YSBsx{@s1ZdiKfFb|zf}ju z7@rJb3mC{U`$R`YS(Z#KyxQx_*nU`kf;}QL%bw17%5~6!mMao^-{FFmX}|ItFuR~F zAAvTF%f4XKYo>2-PJ~ro@Ly#t@Sf69CrA+rmMRpihqH7V&SXX+$Sw`HZF`I*_3Vjz z%kPMyN0J3sl>X{-h12)j&XRhAAI;Aou%%z}gI>G+32z*qpZg{m`CezFrzg#&yc<1` z%j~}PN!F5Ddq(>R{+t0v{j6v^0XwWGu@5+`-$m`_>pCzM`r}wz*8Qv=$|P0R$%tJp z>D+N4GZ|Tg>XL<6XP9_wQRGDs^1icY*5GP4>*7mGMr;V zI%kT_^_SQml6$#uRE4Ps>}?ES)_XI8m-%GN{o^itb^S7e_bM$-wo_Ws)W? zx4_6#*X;T$n2N==N0#xzb~BQU#%^NF6|~898JGDbQxjK(ex;Q}_Qn@?Y>!kkUYUeY z&VclG1#eDPU78K@^p3tAUvZi1(nFfk6AAVHWt)Wbi7dPbjA4isOY~?*1&asp!wg#Q zSpSI6*!TGn3|-%vuJE<9V_1EKkz_0%z}Mb7;E!uz)+0^k;@x+<5tzj5 z!InbRtc`YwNCbCac{plY&Y}hWp#PC{o@5UsBj#tv3f^ns^`;$MVN?>q!pW+MYeC7= zkWr1kAX(0xVQ<{qny&CO*|g1{Mk_yE>1t}_YT<5#p8P7QXf;o|s>XQ#SoA&!ddE+8 zOM&VsxsRGS(Spli?P$^pK7Ty{v86RP_6h|MU^J z`J>vn0|BG3Vf!uR0zM|GwtiTPZNb;a@@1+V5+$P4GI_&$%6m!YRGL=lz5kh?z#5f55 z76COi1`R(5p69;ThuQnJ$R3w?I?jigai2arApagd=^tT~oMUWp^u|H_@zXBjpI)Dv zEFc^_`mVu5U*;ClT?x-t9{#fto_+92GF^dotz0sFWTDwZ`s40AY@mv+Qh5c-Ts8Zp z!(v7!zPvFhUZ-xkR!IvaW`{PqN|k)L4*anbtmK+UU&K*awl?DhxRalbtmDw`$#VzK zYFaG}?$F)1j`Qx7wbn|XzMJ&g@3Ai#u5M?%CLPghk;lD^)-|21{Sr+M(suBU4}6CMTMxc_tD;X;z<1-{FeHte=kh1B9O6Hl z!v2i$d1VFC&z&58zU0`G#7^K3Cs@9LYN16O%Vz)?-iQL!G6&sg6aaX>DBZmm@lFrRJpcL{K3(;+`$9GDFDw62Mud@LZjabzVC=w$dx>TQa}U z-{dhKYTYx*C=Fio`ez@wrzx+p%Fk3i&v?6ENXMb3p^?;_&huLLueDwr zpRqHbU%i;9TmexFxCS8F1rPo-ea3!}!ew7{(($76Rdnfa`~$9{8H@f7U&0&HjZ3TZ zuBc||%FljS_e&wNZ$1ezT$*})XAfm??$_cY_?13vM^tT0EKY2ptb+v5P10}a%aTk_ zh8@_T{ns2@jTFhv`)-Vxh}u(0DiL0MUi(We_eic$;gCoqj(T_S{jDo^PahnKJUp3@ zMOk+%weP*c%K6VFXR2icY`J~-&fVMYUg6fsFI->jlA|9`+07y~$Fsz}^;w;mNk$ms zu?y)VA@QH__tvYDudhEWuDD20H&uvrf_boY{($?5{s-SDjyRxSC%%2Xs5d2dpjdk$ zU*NURD#ovwIfd^H{fXR@UuaooJtQr7$d0+(K+1UEwtG9_T?sb$ExV$e-bpf}a@YUe zuzInI59w!x;<)>Be;a7ukLW>V=8~J6nKU<0@H+SQ!Be;1Za_pw#hiuW_PMPBo8W2G z*WDtiIAN<>HQOmh)DMi{s-0H^GmV3QMf4Zu(zXT!-c;2)uv4gUwt(-}-N*|KUOo$h z+Ak^R)h8yB5UD8 zsSjHgY}KguNi?xV=tdCWqJR!~dDpFQoRJOwxrWH^vfRq4%)v;sDfIjsLXF^)uy>!i z*S8Njd7yfa`+7(|8H9j73Rh|TwFpF(8H-p;RLLIU>k<*qI%A*SL{u$%<=X@Jm1QFe zVkQ(X8P4Tohl?_tSO__^aqaI?k$CC8uNLv2mp_zD@4oDaZfEN5;3#XY!L{8B!;Dtt zb~Zge@JF|#Gsk^5$-|(OPI73po|WZh<`UxaH#Y2!&p05Ph?H)d3Bc3J4sDi$f(6K`?&D&~eHVuE@_Prkt>_&8&aq=OzoN!ANkvho;qIX(g|d#EKQbJ@;-%_iARmgSF1fEK z@B4W@5mDME7AzfL**c&2#B7xO9>rA4x$rM{N=%0=goumK1kL{TF@CSk0yvqR2oo&m z)?nyiL$9~Jt(qnEuWt9Hc_duim%|zJQYiaF*~orVNDvJB;`%ZW_2x%Uu01LeX-JP& zD&fas6d3=igAgcfeki79{5!XPHHYR#nfLYRKv^wkv~cnEbLHMwQ8%yCZI^rK!D2qT zk40Vg;e!_!3d56&umIuidN?6MTZFzHot}AdqKzDh#w0s`)cV!2A74RSH1@lDXtC38 z+UhO4A9?oZEOV{bIgGd1{2qMR&xT+}q!=I8m)W23v!W2WPC?Tf!F!e%_(m^lQZtq* zYwi}gY(KZ*Y^OWRNj$Ph#uEEBM+wtN8QFQ@^`GDOln^ioNrmtvzNNi*qS5lPHxI96#sMil*teLVaa%$msF>@5p#SjT%q8|<4ZOUB#!-kG+|eFSED z!|3c8fXaym9qH`L;pmqTWcG}WE$(h1sZ3seM>)E3ptoP<;~h~qe6XA)lGVanf&->P zjZwi;_;Dt+bYdAeD_XSQ-DgXRXqLv`3Wcgl}myA-JlzBBIh zWq4Q*9#(zjAk_H8VS_AJ`?OS*^gB-rp|~qt;v(C5ef=SErv;~zL64hW`#g!UZQcvZ zF6Ra@S@YhVSkSWVAY=Z1w)w-hfJDRwKTUH0o-OG5TlW0HDH36hIjnP=?A+8u1)Qyy5U8Gi$! zt^!vy|f=YHfQ`ZRK?D zXXn*kItRg50vr2+_hV5kjOleg#s~z(J2p#`=1Tq4#JS`MC^e4p&s7Ir=3m(K$LW#` z=ULCoWtna!so+QQ*JHb~6Ps9_&Ag>9qsUskp0pKbi`n?(u3&@QT!?}N}rXn z>1eHi6(@LicU*AR1obe+nbzTCD#VTJ`PFLRT(nc$NWrhsgRwFni*D(#?W^x=J6?|b zENSc^D}s>Y55)PzFs2d_2;yh89E0ZIgs&>6JV=pL6k9g_(`$04EoY+Zjn}}8e#n83 zJ=zB>BU<253Erdo$wE4^+@QQJFZyAj#(InFlN;!UGg96R@{Y&%OlGG;dM)^X8=Ddw@&2Vx?zui$tO z-{zgaU7&F!xs=e`Mn}r+xrdIAmkraRN_7P1?qu1|TZ%1QR(Mn?k+pq`Xys2v9Gs=a z?r@g&;UKcM#?36r9k*eVD(}9qe8?irotsn0+eHH8*4 zPX@Lusr)$J%8jarx5ssEJ?twFyu4kAbrf`96_z{6at^&UkyDzFa69RXP>PeK+dAWqE5<5P+aHa zs<<*+OO_2ObTXau%y)Nn{(p5`XIPWlvi|asjYcui;E@)Ig{YKBXi}spqC!-P5owwL z3L*+9;0C0G!xoN;4KNfDaElv>1#DMDglI&MAVoK2+c2Pr8&sl*1dYj=^>NRS`{O&%YV25@5*eoOvpD_(xdKsnqb^`T}bm;n0BN9ben1Ynyi*OOf;qLpf^ z!T{}GzkXSszN_Xqzp>}S*Im)_Y8~2|B*ybw(U=Q)5_NcMkT;)1&52YQJB)Tn%kPK! z@3;^AI){B(&UOv<{v9KKJrInkdcXV0%O1%1=7vYV*j?v(Kp~arZio$#(A@$kYB3aM zRdm4!^Je15%66($EkCIWGhi@=kNAyLJ3ydlJnCpPuxH0+OA}J)+t8d7nT->##Nz4w-L=S7ExQt=Rx}S*mpT91(>t~qe7tM%e|O)TIO^dP zfo61GNS=cJbLutqUh84?7X#bq)bv57s&D_zm{+xNv7vHjb=_}j-Lrj-Ss*pcD@ts$ z)5Dol8Z_&*1@JdAQE7SL$*!TXI|YE7q=YGkIiUeLvT0)14Q-ivs|+cqeT6DTi9eQ)h?Pu9pqmH51B* zFMd|;l2@D4*56|EhMFlDxl2i<8qq=c+AhMYS3(A28#3DZ;_Ln>RA3q#IAdJq7M#N> zTZ8t=_>lq0=W&w|bdQ^sy&m^@KR)mNi3|1<6|OL(0KLtP#I6ix$2b{-Y9GP5I7 z8AJUSCnlia5vWawX%ZLWTC2UV$cn^sfv68W!6)QO;ZjnX=7#`$ZPRG~irfl)ZUJ^D z{lUk?(*SU7XIiS^H{Lpxn%542#PgxdeG)Ociej#(uvX)z;Z3)<16Yhd z-sv?qQ5D4a)ZYoYPRep2Zvom@U)HKq*54ZEwdaEq^FZG#(CyG!=Vw(0j8CCmP~`_z z=OR^i&WkDCf2cLvWm@d?)mEgme{hA(o#xAL023LZ3(82SGRg6jJF7$kZ4! z6*FTm4y6v~CP!3$+fxg{QeFo24<3iucgI!oyjV|9Dsx}r~4X@lt^VaH$u zD?87}1Jh=?G8OYg*ts2k;X9{f*Za?yu8IUUfyuQ**wbcWT+KncjD^qQ3h&w2+S(Mj zZM~?Ot%ggTIHwkBkL-4&jI5R=B+MCOR42bKzC2M>l?1%x2Iv7amIfQ1B#wwfD`z|m z+E?G+o(tde*Ws?;Wo4p#Yy>Nnf|*b<nj@-s(rZ)-U@ z(Xe(qZ1(_dH|J3yWu|bAPINK}DwF(kZ>FKx(?ZmU^KFC6*bh$;FKGh~pH1 zozA+kgcIk9@2aAwEJ=VYizT!sxDXX$N?XDiGKaaT-OU@Ib=~4DmgEk&{2D@IvyjF* zuF@sDcuuqx_FAgx;B@@8gqjMh!kQeEKA*y4+q+^4&uc0|>M;$Xb+ z@X%eUx1m%$WSP}Qchx68NQ?dO!h`6;Quq+A1(RORsQ-;6bZ90vj#^0(7>cLR+-_;9 zCd@b~B5V>$tpjkQU#BD%9^zu7-l>U8nzt+XuX5cYDCHYaX5t~~3?lpa;)Mr>q;5XW zu(Th;fr}-GkP`K)u97(#UB|L3f;H7Cd#Pox+auV`=m?a=mSv1v)(V!E=$%gkIJZ;` zZj{Lb@bhs%bRa znZw9cD$cDFVHPtpXwY1K)wys@LS~;!qdqkR>@&RtP>?M^>xe{4N#EtZy4zZ5Ar$ZF zV=X=(!xin-58MC<+b~;jk8Q|3B3THGIA$cM8Bg)Yd6ygP#i?4VrX3OvP_k5i{Cppw z-{$XwrJ-+X$ccJ(Q{|?T@U9=-?qlsfA43%8t247KZn?`+C4e`b-e^(df*iW66=Oc2 z3w9UhohfdY@pH1MZ}vc<1osV(2CGG)Ree$E-T;8>$zw*>x-505b&4(shMGIjbAfLS zEZ3ys(`SmCWc(75)^=aKer}>67qj^nGKtCK{35I|tA}wQa!uM!suX%Gb~ylORGGc( ze^|m|N!}G0#Ph|;wSXz`SByQM>lPM#8>mdSQs`7RxkXaSAADYA24u6xWqkIXY?o%z z%TEFL+wNW^&nrvaA1_#P%&Hbzrjl!*hIft>F0@g0IVydUU4MJgS3_3Js8{*>|G2jC z4%n#cOy9b2Xf&Pw=14;0Dtf00C^Z$I-v05OqtvN9>sAC&oV1Tk;;ku7VR`sQK4oFq zQ8)yoZNuTwV$t13|GCUIC{ID_r7M5&R*zhsxbrkg;EgMtL|9ne=^}BM!dxV!KDeXkWA^MfQTkQEt8~t>JznNh%ULvn@dbQ2cyf} z|C%ns#NJU}SHU(7Pg$<&8uDK>d5GZJ&`;CcfGP(~b-#UusXevc^q!km1X6_wVMqGk z^m&ZS6#42?p4c_t1TA$_+}h1L2c<<=$k%;v+D!<@j5hs|{>d18>~~v#oq4yGyS@QP zgTX2oJbEy@eJbo-f{ZQ>-nmB-#AqWcHbMQXFi*T)0n!(HIexz=pp<(O*DMh7CMupX z)ei1ZYuIW~E={-ND*nD;okiZdm!?^|LjLZhs*FHZvWld5TDj zcvWB)`-1Me9bu`*4M=CO6ye=pMgxlgYvsh2rV#5Z$hFKw0GX30%oufb=hJ0BFIJH` z+Fii4gQ+7!)8K^yc*PVEW^#f!|BW0Q5*`IewQ5YDFh?{x1L7tlaUAX@3Y+D>6FPVf zJzOGex~H34`8eq+TL$FsHm+27RS>3$CG;>0Jj4*1ukX$za})*b^S5p}I2jbFCHLsA zzYwAyftMz`uo2c8ieQcy-p&9iP3fMk(uRw+OlBPm`KCLei6g!|Vnk*-kjs>A25MTE z5GLDMV$70AC0j-tx*0sCruvKh{fSM)3X}13U>m|KeaOb`9^}v^44!$`06-JHf@L4EKyxV)M!8cL zi5p9kF97RiAT92!e?%9CP=qX3wyv^A8q!w%07d(9f-U))uDgsr4FDVL;|%r)fw}-@ zlB$F79X^EKYF%8J7mU?3VzJoYQ0<;NczW1jH4=4kEh_)q|^9wj zIsn-SsmRx0_EJ7(6WypwptIwZ)-T<__UgUu?BXt zoIf|a!5`?&JEb$w2PZSqhA>J;GIA^rJ-Cpz8MKX~bcqZNOUzPtu|NMvEP>+cO;V*W zNQ8YPENkr!)lN+tlxB79RUD20$)+_P6Jc`+4q@%Kno{F+#1qR*zrj%T>nTSceO?a5 zyqGDa59#G6k*RXu6+#=e=e!~i1Y&15!cHmE6sLh_K%Ppv$tFE-Le3RQs-nx5LB>gy z5A))kwkxWSy73{@I{%{DY8X+2o{CLJb~R$3r=oT^P~Xo$2lKz8?Z!3QLn$5l#L2k2 zb1=?UT&c<8!&9gW1M&jI!5%dhJbD3nQXpaeNJ>=zR+EL!4iY(nMBQI+|2J+Hw-WMr z08Mt9h8(PGbY?zKtk=cqw(yW}1A#htn* z8&}5Y>$uc>Lv!bSuWQ5UB&ct7*jiZAFpxz|%xO&5kg zzlf?6xy7H3G^*wvP5scW*Wf(<&eP!YIUf%&HT?K)RWmKg$G^=mSoi~;&9dU%{o}WV z#BX;9+q)fpVU`>Vdo~AtYK)`7z*H;dc-e|q6Qt;3J0APUL!~g&Q literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..ed4cc16421680a50164ba74381b4b35ceaa0ccfc GIT binary patch literal 3276 zcmZ`*X*|?x8~)E?#xi3t91%vcMKbnsIy2_j%QE2ziLq8HEtbf{7%?Q-9a%z_Y^9`> zEHh*&vUG%uWkg7pKTS-`$veH@-Vg8ZdG7oAJ@<88AMX3Z{d}TU-4*=KI1-hF6u>DKF2moPt09c{` zfN3rO$X+gJI&oA$AbgKoTL8PiPI1eFOhHBDvW+$&oPl1s$+O5y3$30Jx9nC_?fg%8Om)@;^P;Ee~8ibejUNlSR{FL7-+ zCzU}3UT98m{kYI^@`mgCOJ))+D#erb#$UWt&((j-5*t1id2Zak{`aS^W*K5^gM02# zUAhZn-JAUK>i+SNuFbWWd*7n1^!}>7qZ1CqCl*T+WoAy&z9pm~0AUt1cCV24f z3M@&G~UKrjVHa zjcE@a`2;M>eV&ocly&W3h{`Kt`1Fpp?_h~9!Uj5>0eXw@$opV(@!pixIux}s5pvEqF5$OEMG0;c zAfMxC(-;nx_`}8!F?OqK19MeaswOomKeifCG-!9PiHSU$yamJhcjXiq)-}9`M<&Au|H!nKY(0`^x16f205i2i;E%(4!?0lLq0sH_%)Wzij)B{HZxYWRl3DLaN5`)L zx=x=|^RA?d*TRCwF%`zN6wn_1C4n;lZG(9kT;2Uhl&2jQYtC1TbwQlP^BZHY!MoHm zjQ9)uu_K)ObgvvPb}!SIXFCtN!-%sBQe{6NU=&AtZJS%}eE$i}FIll!r>~b$6gt)V z7x>OFE}YetHPc-tWeu!P@qIWb@Z$bd!*!*udxwO6&gJ)q24$RSU^2Mb%-_`dR2`nW z)}7_4=iR`Tp$TPfd+uieo)8B}Q9#?Szmy!`gcROB@NIehK|?!3`r^1>av?}e<$Qo` zo{Qn#X4ktRy<-+f#c@vILAm;*sfS}r(3rl+{op?Hx|~DU#qsDcQDTvP*!c>h*nXU6 zR=Un;i9D!LcnC(AQ$lTUv^pgv4Z`T@vRP3{&xb^drmjvOruIBJ%3rQAFLl7d9_S64 zN-Uv?R`EzkbYIo)af7_M=X$2p`!u?nr?XqQ_*F-@@(V zFbNeVEzbr;i2fefJ@Gir3-s`syC93he_krL1eb;r(}0yUkuEK34aYvC@(yGi`*oq? zw5g_abg=`5Fdh1Z+clSv*N*Jifmh&3Ghm0A=^s4be*z5N!i^FzLiShgkrkwsHfMjf z*7&-G@W>p6En#dk<^s@G?$7gi_l)y7k`ZY=?ThvvVKL~kM{ehG7-q6=#%Q8F&VsB* zeW^I zUq+tV(~D&Ii_=gn-2QbF3;Fx#%ajjgO05lfF8#kIllzHc=P}a3$S_XsuZI0?0__%O zjiL!@(C0$Nr+r$>bHk(_oc!BUz;)>Xm!s*C!32m1W<*z$^&xRwa+AaAG= z9t4X~7UJht1-z88yEKjJ68HSze5|nKKF9(Chw`{OoG{eG0mo`^93gaJmAP_i_jF8a z({|&fX70PXVE(#wb11j&g4f{_n>)wUYIY#vo>Rit(J=`A-NYYowTnl(N6&9XKIV(G z1aD!>hY!RCd^Sy#GL^0IgYF~)b-lczn+X}+eaa)%FFw41P#f8n2fm9=-4j7}ULi@Z zm=H8~9;)ShkOUAitb!1fvv%;2Q+o)<;_YA1O=??ie>JmIiTy6g+1B-1#A(NAr$JNL znVhfBc8=aoz&yqgrN|{VlpAniZVM?>0%bwB6>}S1n_OURps$}g1t%)YmCA6+5)W#B z=G^KX>C7x|X|$~;K;cc2x8RGO2{{zmjPFrfkr6AVEeW2$J9*~H-4~G&}~b+Pb}JJdODU|$n1<7GPa_>l>;{NmA^y_eXTiv z)T61teOA9Q$_5GEA_ox`1gjz>3lT2b?YY_0UJayin z64qq|Nb7^UhikaEz3M8BKhNDhLIf};)NMeS8(8?3U$ThSMIh0HG;;CW$lAp0db@s0 zu&jbmCCLGE*NktXVfP3NB;MQ>p?;*$-|htv>R`#4>OG<$_n)YvUN7bwzbWEsxAGF~ zn0Vfs?Dn4}Vd|Cf5T-#a52Knf0f*#2D4Lq>-Su4g`$q={+5L$Ta|N8yfZ}rgQm;&b z0A4?$Hg5UkzI)29=>XSzdH4wH8B@_KE{mSc>e3{yGbeiBY_+?^t_a#2^*x_AmN&J$ zf9@<5N15~ty+uwrz0g5k$sL9*mKQazK2h19UW~#H_X83ap-GAGf#8Q5b8n@B8N2HvTiZu&Mg+xhthyG3#0uIny33r?t&kzBuyI$igd`%RIcO8{s$$R3+Z zt{ENUO)pqm_&<(vPf*$q1FvC}W&G)HQOJd%x4PbxogX2a4eW-%KqA5+x#x`g)fN&@ zLjG8|!rCj3y0%N)NkbJVJgDu5tOdMWS|y|Tsb)Z04-oAVZ%Mb311P}}SG#!q_ffMV z@*L#25zW6Ho?-x~8pKw4u9X)qFI7TRC)LlEL6oQ9#!*0k{=p?Vf_^?4YR(M z`uD+8&I-M*`sz5af#gd$8rr|oRMVgeI~soPKB{Q{FwV-FW)>BlS?inI8girWs=mo5b18{#~CJz!miCgQYU>KtCPt()StN;x)c2P3bMVB$o(QUh z$cRQlo_?#k`7A{Tw z!~_YKSd(%1dBM+KE!5I2)ZZsGz|`+*fB*n}yxtKVyx14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>GbI`Jdw*pGcA%L+*Q#&*YQOJ$_%U#(BDn``;rKxi&&)LfRxIZ*98z8UWRslDo@Xu)QVh}rB>bKwe@Bjzwg%m$hd zG)gFMgHZlPxGcm3paLLb44yHI|Ag0wdp!_yD5R<|B29Ui~27`?vfy#ktk_KyHWMDA42{J=Uq-o}i z*%kZ@45mQ-Rw?0?K+z{&5KFc}xc5Q%1PFAbL_xCmpj?JNAm>L6SjrCMpiK}5LG0ZE zO>_%)r1c48n{Iv*t(u1=&kH zeO=ifbFy+6aSK)V_5t;NKhE#$Iz=+Oii|KDJ}W>g}0%`Svgra*tnS6TRU4iTH*e=dj~I` zym|EM*}I1?pT2#3`oZ(|3I-Y$DkeHMN=8~%YSR?;>=X?(Emci*ZIz9+t<|S1>hE8$ zVa1LmTh{DZv}x6@Wz!a}+qZDz%AHHMuHCzM^XlEpr!QPzf9QzkS_0!&1MPx*ICxe}RFdTH+c}l9E`G zYL#4+3Zxi}3=A!G4S>ir#L(2r)WFKnP}jiR%D`ZOPH`@ZhTQy=%(P0}8ZH)|z6jL7 N;OXk;vd$@?2>?>Ex^Vyi literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000000000000000000000000000000000000..bcbf36df2f2aaaa0a63c7dabc94e600184229d0d GIT binary patch literal 5933 zcmZ{Idpwix|Np(&m_yAF>K&UIn{t*2ZOdsShYs(MibU!|=pZCJq~7E>B$QJr)hC5| zmk?V?ES039lQ~RC!kjkl-TU4?|NZ{>J$CPLUH9vHy`Hbhhnc~SD_vpzBp6Xw4`$%jfmPw(;etLCccvfU-s)1A zLl8-RiSx!#?Kwzd0E&>h;Fc z^;S84cUH7gMe#2}MHYcDXgbkI+Qh^X4BV~6y<@s`gMSNX!4@g8?ojjj5hZj5X4g9D zavr_NoeZ=4vim%!Y`GnF-?2_Gb)g$xAo>#zCOLB-jPww8a%c|r&DC=eVdE;y+HwH@ zy`JK(oq+Yw^-hLvWO4B8orWwLiKT!hX!?xw`kz%INd5f)>k1PZ`ZfM&&Ngw)HiXA| ze=+%KkiLe1hd>h!ZO2O$45alH0O|E+>G2oCiJ|3y2c$;XedBozx93BprOr$#d{W5sb*hQQ~M@+v_m!8s?9+{Q0adM?ip3qQ*P5$R~dFvP+5KOH_^A+l-qu5flE*KLJp!rtjqTVqJsmpc1 zo>T>*ja-V&ma7)K?CE9RTsKQKk7lhx$L`9d6-Gq`_zKDa6*>csToQ{&0rWf$mD7x~S3{oA z1wUZl&^{qbX>y*T71~3NWd1Wfgjg)<~BnK96Ro#om&~8mU{}D!Fu# zTrKKSM8gY^*47b2Vr|ZZe&m9Y`n+Y8lHvtlBbIjNl3pGxU{!#Crl5RPIO~!L5Y({ym~8%Ox-9g>IW8 zSz2G6D#F|L^lcotrZx4cFdfw6f){tqITj6>HSW&ijlgTJTGbc7Q#=)*Be0-s0$fCk z^YaG;7Q1dfJq#p|EJ~YYmqjs`M0jPl=E`Id{+h%Lo*|8xp6K7yfgjqiH7{61$4x~A zNnH+65?QCtL;_w(|mDNJXybin=rOy-i7A@lXEu z&jY(5jhjlP{TsjMe$*b^2kp8LeAXu~*q&5;|3v|4w4Ij_4c{4GG8={;=K#lh{#C8v z&t9d7bf{@9aUaE94V~4wtQ|LMT*Ruuu0Ndjj*vh2pWW@|KeeXi(vt!YXi~I6?r5PG z$_{M*wrccE6x42nPaJUO#tBu$l#MInrZhej_Tqki{;BT0VZeb$Ba%;>L!##cvieb2 zwn(_+o!zhMk@l~$$}hivyebloEnNQmOy6biopy`GL?=hN&2)hsA0@fj=A^uEv~TFE z<|ZJIWplBEmufYI)<>IXMv(c+I^y6qBthESbAnk?0N(PI>4{ASayV1ErZ&dsM4Z@E-)F&V0>tIF+Oubl zin^4Qx@`Un4kRiPq+LX5{4*+twI#F~PE7g{FpJ`{)K()FH+VG^>)C-VgK>S=PH!m^ zE$+Cfz!Ja`s^Vo(fd&+U{W|K$e(|{YG;^9{D|UdadmUW;j;&V!rU)W_@kqQj*Frp~ z7=kRxk)d1$$38B03-E_|v=<*~p3>)2w*eXo(vk%HCXeT5lf_Z+D}(Uju=(WdZ4xa( zg>98lC^Z_`s-=ra9ZC^lAF?rIvQZpAMz8-#EgX;`lc6*53ckpxG}(pJp~0XBd9?RP zq!J-f`h0dC*nWxKUh~8YqN{SjiJ6vLBkMRo?;|eA(I!akhGm^}JXoL_sHYkGEQWWf zTR_u*Ga~Y!hUuqb`h|`DS-T)yCiF#s<KR}hC~F%m)?xjzj6w#Za%~XsXFS@P0E3t*qs)tR43%!OUxs(|FTR4Sjz(N zppN>{Ip2l3esk9rtB#+To92s~*WGK`G+ECt6D>Bvm|0`>Img`jUr$r@##&!1Ud{r| zgC@cPkNL_na`74%fIk)NaP-0UGq`|9gB}oHRoRU7U>Uqe!U61fY7*Nj(JiFa-B7Av z;VNDv7Xx&CTwh(C2ZT{ot`!E~1i1kK;VtIh?;a1iLWifv8121n6X!{C%kw|h-Z8_U z9Y8M38M2QG^=h+dW*$CJFmuVcrvD*0hbFOD=~wU?C5VqNiIgAs#4axofE*WFYd|K;Et18?xaI|v-0hN#D#7j z5I{XH)+v0)ZYF=-qloGQ>!)q_2S(Lg3<=UsLn%O)V-mhI-nc_cJZu(QWRY)*1il%n zOR5Kdi)zL-5w~lOixilSSF9YQ29*H+Br2*T2lJ?aSLKBwv7}*ZfICEb$t>z&A+O3C z^@_rpf0S7MO<3?73G5{LWrDWfhy-c7%M}E>0!Q(Iu71MYB(|gk$2`jH?!>ND0?xZu z1V|&*VsEG9U zm)!4#oTcgOO6Hqt3^vcHx>n}%pyf|NSNyTZX*f+TODT`F%IyvCpY?BGELP#s<|D{U z9lUTj%P6>^0Y$fvIdSj5*=&VVMy&nms=!=2y<5DP8x;Z13#YXf7}G)sc$_TQQ=4BD zQ1Le^y+BwHl7T6)`Q&9H&A2fJ@IPa;On5n!VNqWUiA*XXOnvoSjEIKW<$V~1?#zts>enlSTQaG2A|Ck4WkZWQoeOu(te znV;souKbA2W=)YWldqW@fV^$6EuB`lFmXYm%WqI}X?I1I7(mQ8U-pm+Ya* z|7o6wac&1>GuQfIvzU7YHIz_|V;J*CMLJolXMx^9CI;I+{Nph?sf2pX@%OKT;N@Uz9Y zzuNq11Ccdwtr(TDLx}N!>?weLLkv~i!xfI0HGWff*!12E*?7QzzZT%TX{5b7{8^*A z3ut^C4uxSDf=~t4wZ%L%gO_WS7SR4Ok7hJ;tvZ9QBfVE%2)6hE>xu9y*2%X5y%g$8 z*8&(XxwN?dO?2b4VSa@On~5A?zZZ{^s3rXm54Cfi-%4hBFSk|zY9u(3d1ButJuZ1@ zfOHtpSt)uJnL`zg9bBvUkjbPO0xNr{^{h0~$I$XQzel_OIEkgT5L!dW1uSnKsEMVp z9t^dfkxq=BneR9`%b#nWSdj)u1G=Ehv0$L@xe_eG$Ac%f7 zy`*X(p0r3FdCTa1AX^BtmPJNR4%S1nyu-AM-8)~t-KII9GEJU)W^ng7C@3%&3lj$2 z4niLa8)fJ2g>%`;;!re+Vh{3V^}9osx@pH8>b0#d8p`Dgm{I?y@dUJ4QcSB<+FAuT)O9gMlwrERIy z6)DFLaEhJkQ7S4^Qr!JA6*SYni$THFtE)0@%!vAw%X7y~!#k0?-|&6VIpFY9>5GhK zr;nM-Z`Omh>1>7;&?VC5JQoKi<`!BU_&GLzR%92V$kMohNpMDB=&NzMB&w-^SF~_# zNsTca>J{Y555+z|IT75yW;wi5A1Z zyzv|4l|xZ-Oy8r8_c8X)h%|a8#(oWcgS5P6gtuCA_vA!t=)IFTL{nnh8iW!B$i=Kd zj1ILrL;ht_4aRKF(l1%^dUyVxgK!2QsL)-{x$`q5wWjjN6B!Cj)jB=bii;9&Ee-;< zJfVk(8EOrbM&5mUciP49{Z43|TLoE#j(nQN_MaKt16dp#T6jF7z?^5*KwoT-Y`rs$ z?}8)#5Dg-Rx!PTa2R5; zx0zhW{BOpx_wKPlTu;4ev-0dUwp;g3qqIi|UMC@A?zEb3RXY`z_}gbwju zzlNht0WR%g@R5CVvg#+fb)o!I*Zpe?{_+oGq*wOmCWQ=(Ra-Q9mx#6SsqWAp*-Jzb zKvuPthpH(Fn_k>2XPu!=+C{vZsF8<9p!T}U+ICbNtO}IAqxa57*L&T>M6I0ogt&l> z^3k#b#S1--$byAaU&sZL$6(6mrf)OqZXpUPbVW%T|4T}20q9SQ&;3?oRz6rSDP4`b z(}J^?+mzbp>MQDD{ziSS0K(2^V4_anz9JV|Y_5{kF3spgW%EO6JpJ(rnnIN%;xkKf zn~;I&OGHKII3ZQ&?sHlEy)jqCyfeusjPMo7sLVr~??NAknqCbuDmo+7tp8vrKykMb z(y`R)pVp}ZgTErmi+z`UyQU*G5stQRsx*J^XW}LHi_af?(bJ8DPho0b)^PT|(`_A$ zFCYCCF={BknK&KYTAVaHE{lqJs4g6B@O&^5oTPLkmqAB#T#m!l9?wz!C}#a6w)Z~Z z6jx{dsXhI(|D)x%Yu49%ioD-~4}+hCA8Q;w_A$79%n+X84jbf?Nh?kRNRzyAi{_oV zU)LqH-yRdPxp;>vBAWqH4E z(WL)}-rb<_R^B~fI%ddj?Qxhp^5_~)6-aB`D~Nd$S`LY_O&&Fme>Id)+iI>%9V-68 z3crl=15^%0qA~}ksw@^dpZ`p;m=ury;-OV63*;zQyRs4?1?8lbUL!bR+C~2Zz1O+E@6ZQW!wvv z|NLqSP0^*J2Twq@yws%~V0^h05B8BMNHv_ZZT+=d%T#i{faiqN+ut5Bc`uQPM zgO+b1uj;)i!N94RJ>5RjTNXN{gAZel|L8S4r!NT{7)_=|`}D~ElU#2er}8~UE$Q>g zZryBhOd|J-U72{1q;Lb!^3mf+H$x6(hJHn$ZJRqCp^In_PD+>6KWnCnCXA35(}g!X z;3YI1luR&*1IvESL~*aF8(?4deU`9!cxB{8IO?PpZ{O5&uY<0DIERh2wEoAP@bayv z#$WTjR*$bN8^~AGZu+85uHo&AulFjmh*pupai?o?+>rZ7@@Xk4muI}ZqH`n&<@_Vn zvT!GF-_Ngd$B7kLge~&3qC;TE=tEid(nQB*qzXI0m46ma*2d(Sd*M%@Zc{kCFcs;1 zky%U)Pyg3wm_g12J`lS4n+Sg=L)-Y`bU705E5wk&zVEZw`eM#~AHHW96@D>bz#7?- zV`xlac^e`Zh_O+B5-kO=$04{<cKUG?R&#bnF}-?4(Jq+?Ph!9g zx@s~F)Uwub>Ratv&v85!6}3{n$bYb+p!w(l8Na6cSyEx#{r7>^YvIj8L?c*{mcB^x zqnv*lu-B1ORFtrmhfe}$I8~h*3!Ys%FNQv!P2tA^wjbH f$KZHO*s&vt|9^w-6P?|#0pRK8NSwWJ?9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!ItFh?!xdN1Q+aGJ{c&& zS>O>_%)r1c48n{Iv*t(u1=&kHeO=ifbFy+6aSK)V_AxLppYn8Z42d|rc6w}vOsL55 z`t&mC&y2@JTEyg!eDiFX^k#CC!jq%>erB=yHqUP0XcDOTw6ko}L zX;EmMrq(fKk*eygEuA616;0)>@A{TK|55PV@70 z$OfzS*(VJxQev3J?yY?O=ul(v`fp}?u9z`JK3ugibK>)DyCwImZOF4d{xK%%Ks1*} zv$oa)9anR%lXIBUqYnhLmT>VOzHfNP?ZwJNZ!5$s9M08RynIvaXw>@G^T9@r9^KH1 zVy??F&uuk)bH9Y4pQY!hP58i_H6 znl-NcuCpLV6ZWU;4C zu@9exF&OZi`Bovq_m%T+WhU2kvkz@^_LpycBvqm3bMpLw8X-Or5sL>0AKE1$(k_L=_Zc=CUq#=x1-QZf)G7nHu@fmsQ1eN_N3+nTEz`4HI4Z6uVlE zJH+X&det8JU?tO?upcM4Z=cV!JV;yF>FfL5Q$M|W_2Z!P`S=}Wzp|_1^#d%e?_H`> zV@%vA$+bFVqhw9`U;TfP|5|PD{||OiYdor8P*i??|NJcb%kzT_73*7WE?Ua5hAnR2 z=7WE=PhTlJ#ZeRznjTUb;`E(wkMZrj4e|Hilz-mK>9cZHQY**5TUPw~u}k;u73KI}xAx!0m-)GVia|x^d3p~s_9gh83jA&Ra<8rM%`>U3x69t&NzbwWY}7Ar?)FK#IZ0z|d0H0EkRO w3{9;}4Xg|ebq&m|3=9_N6z8I7$jwj5OsmAL;bP(Gi$Dzwp00i_>zopr02+f8CIA2c literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/file_selector/file_selector/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000000000000000000000000000000000000..e71a726136a47ed24125c7efc79d68a4a01961b4 GIT binary patch literal 14800 zcmZ{Lc|26@`~R6Crm_qwyCLMMh!)vm)F@HWt|+6V6lE=CaHfcnn4;2x(VilEl9-V} zsce-cGK|WaF}4{T=lt&J`Fy_L-|vs#>v^7+XU=`!*L|PszSj43o%o$Dj`9mM7C;ar z@3hrnHw59q|KcHn4EQr~{_70*BYk4yj*SqM&s>NcnFoIBdT-sm1A@YrK@dF#f+SPu z{Sb8441xx|AjtYQ1gQq5z1g(^49Fba=I8)nl7BMGpQeB(^8>dY41u79Dw6+j(A_jO z@K83?X~$;S-ud$gYZfZg5|bdvlI`TMaqs!>e}3%9HXev<6;dZZT8Yx`&;pKnN*iCJ z&x_ycWo9{*O}Gc$JHU`%s*$C%@v73hd+Mf%%9ph_Y1juXamcTAHd9tkwoua7yBu?V zgROzw>LbxAw3^;bZU~ZGnnHW?=7r9ZAK#wxT;0O<*z~_>^uV+VCU9B@)|r z*z^v>$!oH7%WZYrwf)zjGU|(8I%9PoktcsH8`z^%$48u z(O_}1U25s@Q*9{-3O!+t?w*QHo;~P99;6-KTGO{Cb#ADDYWF!eATsx{xh-!YMBiuE z%bJc7j^^B$Sa|27XRxg(XTaxWoFI}VFfV>0py8mMM;b^vH}49j;kwCA+Lw=q8lptk z?Pe`{wHI39A&xYkltf5*y%;-DF>5v`-lm0vydYtmqo0sClh5ueHCLJ+6$0y67Z zO-_LCT|JXi3tN7fB-!0_Kn#I+=tyUj87uR5*0>|SZ zy3x2;aql87`{aPZ@UbBwY0;Z-a*lYL90YApOAMKur7YgOiqA~Cne6%b&{V-t>Am2c z{eyEuKl!GsA*jF2H_gvX?bP~v46%3ax$r~B$HnZQ;UiCmRl`ROK8v>;Zs~upH9}qu1ZA3kn-AY2k2@CaH=Qh7K6`nU z3ib(Bk%H*^_omL6N4_G5NpY20UXGi}a$!}#lf<&J4~nhRwRM5cCB3Zvv#6+N1$g@W zj9?qmQ`zz-G9HTpoNl~bCOaEQqlTVYi7G0WmB5E34;f{SGcLvFpOb`+Zm)C(wjqLA z2;+nmB6~QDXbxZGWKLt38I%X$Q!;h zup9S~byxKv=$x|^YEV;l0l67jH~E8BU45ft_7xomac-48oq4PZpSNJbw<7DTM4mmz z!$)z#04cy%b8w@cOvjmb36o;gwYIOLwy+{I#3dJj#W4QdOWwJQ2#20AL49`hSFUa7 zFNAN3OD==G3_kbr1d96>l`_cI`<=thKNh5>hgg7FV>5TfC6d#u)9BNXi@p1K*;2Is zz+x;l4GbSt#*%>1iq}jGIebXYJY5;PGG0y(^{>SSuZY89aL`sDghOM&&pyP6ABJ#w zYwK~4^1eUQD)4!GL>`zrWeHV z-W!6JZbW*Ngo;Edhp_cOysYr!uhKS}vIg_UC}x z=jXxQfV@4B3`5 z!u#byBVXV5GtrSx_8bnT@iKv=Uc6n)Zpa`<9N>+!J~Loxptl5$Z`!u<3a)-+P)say z#=jc7^mJzPMI2;yMhCmN7YN78E7-^S(t8E}FklC;z|4PL{bO|JieM#p1mBjwyZMEm zkX^A1RXPGeS2YqtPMX~~t^$~oeFfWAU#jVLi%Z@l2hle^3|e(q?(uS=BVauF?VF{j z(owKLJuze;_@5p1OtRyrT`EFXf)NfMYb-)E8RVVdr<@}M>4R&~P=;B`c1L%o|8YfB z-a(LB-i8jc5!&B5cowyI2~M^YID&@Xt(D9v{|DB z959W z*vEA77fh3*w*UJ`4Y(bxsoEy6hm7_Wc5gT0^cvso%Ow>9<&@9Q>mxb6-^pv)5yc>n zQ~^!qY(lPQ1EDGkr%_*y*D8T^YbCa52^MVqYpTLhgJ;N5PfCQ{SXk|plD#Sm+g4c- zFeL2Dih35W4{_qb75U`4Rb#S0FEo%F85dOhXSX0huPOxdAid{&p6P;+9}I)XU7^=3RZu9M(g0dLyz_7$8K{`AddBLOfU&B_QNHtmsnNXq`hy~% zvJ{vtz~Yt9X|o}5vXX)9ZCHaRq8iAb zUDj8%(MpzJN39LferYKvIc!)z^5T-eW@j3h9a6d%WZ!%@2^@4+6%Z9W1GHZbOj|sb z0cU$}*~G$fYvDC|XulSC_;m}?KC2jg5pxES$Bt!hA|@EX*2+O!UEb5sn_^d>z;>;r~ zmO3BivdXboPY*}amsO&`xk|e)S*u=`o67MC(1WTB;OwG+ua4UV7T5Wvy%?U{Pa5cO zMoLG>#@chO{Oc72XPyX8f3jC7P`$j4$)0wc(b50COaDP3_Cm}aPAglUa7kRXAqmo5 z0KDD7G>Gmnpons40WJNYn+pxko92GXy@PvSErKE-Ou3)3UiRr7!L4+0%+5}sD{bf)uj^ounQ-Yn2%%JoZ%FjUv%yjS?Ks4u_88Jh%tNliYW~817IV@fqd1T zi(?;Fv-s3rQEn=9G*E-QzSl%YS|^fe*yn}Aqh!&P<5%#oB?*{wZMa5$PYa*A{VA8! zbOfS1W!W}cTo%g~iP$>WhE_x7#O4?h$jq=>{M77>bTAK_ z6uU0tl6HARboGi}=4krr6WP`9`aAt&P5ON1v(+H{T?jZuJ}B{L-=z3VX)}mZwzrqH zpf?T!k&$?{&{0_p>b`kdJbSb(p~tFcuG4zh6}hfl@ues6CfJu<-P+!>FlYMlD_3!E z9$6VE==tlxNYe(s;@8@+4c4jQ$R2g8t0QwE>Et|)5)@kJj6^yaqFYY?0LEM2C!+7+ z+FN|UxR1GCy1KA`{T_%24U+Vserchr5h`;U7TZPr@43x#MMN{@vV?KSII}R@5k`7cVK}E;c)$f~_{ZLDOoL|-01p~oafxi4F zG$?Wha&a*rTnz-nTI-bAJ*SLb!5(L!#iRdvLEyo>7D_=H78-qZrm=6{hkUR{tR{H! z`ZTOV$Oi6^qX5=_{f}V9h}WJAO%h9)kEUF#*-JyYDbOGZ>Nfs%7L}4p zopIul&&Bbn!C9o83ypC6W4F$X=_|pex$V4!Whm#48Wfm3*oAW0Gc&#&b+oq<8>aZR z2BLpouQQwyf$aHpQUK3pMRj(mS^^t#s$IC3{j*m9&l7sQt@RU{o_}N-xI_lh`rND^ zX~-8$o(;p^wf3_5-WZ^qgW`e8T@37{`J)e2KJdSSCUpX6KZu0Ga&U*+u3*PDAs1uK zpl)40+fROA@Vo#vK?^@Pq%w8DO9HdfmH+~vNinZ$5GRz?sD|k246NepqZd`>81P^P z#x#3kUS-}x4k%&~iEUrsb&-X#_;;?y9oCP4crMkC`=q58#NxQ| z*NXNA;GR4X=GiGXwab5=&M3j04fQw%2UxM`S(aE)_PlgJttBX96$$lY@Q%0xV^IbcHqzw^Uk&E=vFB;EQ@kzVIeM8lDIW_Q_ zrfy)l6s2QBApF;J2xTD_@wuNMlwDfsdfMyzRq)<>qG{M)Yt}9F1{1HaI_X7=F=7>& zYB54VaKlxu0lIgS;Ac&25Aw(tcf@K~(cvPi8(OChzhlYp6}#<_MVhU95sD&)n0FtL zmxm4w$~s(S9jmHOgyovpG!x4uLfJsMsJn^QMraKAa1Ix?{zkV!a7{f%-!u2{NqZ&) zo+^XB`eFQ4 zk-(;_>T#pTKyvW${yL|XXbcv?CE2Tp<3(PjeXhu^Jrp6^Mj}lg_)jamK{g;C+q^Da ztb!gV!q5)B7G1%lVanA2b>Xs?%hzCgJ{Hc!ldr9dnz7k^xG#4pDpr|0ZmxxiUVl}j zbD_rg3yAFQ>nnc)0>71D==715jRj4XsRb2#_lJoSOwky&c4957V-|m)@>b^Nak1!8 z@DsIOS8>Oe^T>tgB)WX3Y^I^65Uae+2M;$RxX_C)Aoo0dltvoRRIVQkpnegWj;D#G z+TwFIRUN%bZW3(K{8yN8!(1i0O!X3YN?Zo08L5D~)_tWQA8&|CvuQb8Od?p_x=GMF z-B@v9iNLYS1lUsbb`!%f5+1ev8RFPk7xyx5*G;ybRw(PW*yEZ$unu2`wpH)7b@ZXEz4Jr{?KZKYl!+3^)Q z)~^g?KlPGtT!{yQU&(Z&^rVjPu>ueeZN86AnhRwc)m|;5NvM&W3xD%n`+Hjg5$e8M zKh1Ju82L~&^ z-IQ5bYhsjqJfr38iwi~8<{oeREh|3l)*Enj4&Q$+mM$15YqwXeufK9P^(O=pj=F-1 zD+&REgwY~!W#ZPccSEi(*jiKJ5)Q|zX;hP}S2T9j_);epH9JQs{n>RG}{Nak)vIbfa zFQm?H;D+tzrBN2)6{?Mo%fzN6;6d_h0Qyn61)+XT63=!T*WQyRUoB_x0_)Ir`$FtS zak07C(mOaWN5m%bk?F9X&@mEVKN%{R6obt(9qw&p>w&p;R*l2th9$D^*`pC}NmB+v z>bk;OJ(C8p$G;jNvRsBbt=a!!tKnjJ`9*yQFgjEN1HcC<&>u9aStT3>Oq=MOQV!#WOZ6{cv$YVmlJdovPRV}<=IZUPeBVh5DC z91-?kimq3JUr;UMQ@0?h52gupvG=~(5AVdP(2(%*sL8!#K1-L$9B7MrWGdt(h&whR@vz~0oEHF8u3U1Q zdGdaIytJj4x@eF*E+^zgi{nPCA8tkjN}UoR8WhDzM3-zLqx0z?2tTdDKyENM={fp8VC@3Dt`AiK$;K#H$K2{08mrHG%jgEOLX3MCsG>afZm_0mLPS4jmYUJp~Dm! z5AUe_vEaOAT3zWdwl#cLvqwd1^lwW?gt7(92wEsOE6c#<0}{szFV4(uO70?3>=((! zQr}1{J?Wx2ZmjxYL_8OB*m&mimfojzYn~PiJ2g8R&ZRx-i^yF#sdhEWXAUIZ@J?T$ zs3PgT2<&Ki>Bob_n(@S>kUIvE+nY~ti9~6j;O9VAG#{oZ!DZCW)}i6iA!Tgsyz+hC z1VVyvbQ_nwgdZSEP=U4d#U`2*`e~d4y8uM4Bcmm%!jidaee#4WqN!ZnlBmbYpuaO! z!rU3`Kl2 z0O7PD&fQ|_b)Ub!g9^s;C2e>1i*2&?1$6yEn?~Y zI)-WIN8N(5s9;grW+J@K@I%g#?G&hzmlgV=L}ZA{f>3YCMx^P{u@c5Z;U1qmdk#)L zvX6z1!sL>+@vxO8qVn#k3YxYi?8ggV){?Rn@j$+Fd4-QkuH1@)j#3-=f82GZ!nl~{ zzZ(?kO`ANttVeHSo%xmH!NmNZECh*{s!-8S>ALoe5xOPs>|P5BbUmP@rlV8`d(c=7 zypcpLaI*FM^;GM%@q`GAb8kO`$oE|R48yn)?p(c1t>5;Wwn5r6ck&uw4}TnT80jI`IS~J%q8CpaVgIze<8IykSpVBg8~E! zW_tGqB;GO47r_er05y+Kwrcn{VLxL*1;HMv@*sd}MB6DH4zaP~u4Y;>@Nw7?F8S?c zfVIY(^ntnGgWlD|idzGz$Y+Oh(Ra=&VIf4!K2W*a)(%5%78s}8qxOknAGtDAq+HMO zM+Nu;0OgQRn36 zA@~a8`uVQ~v9?d!BxnsVaB-z-djypO44BjQAmg7&eVoaew|~)wH$SgefJ2$7_RiY+ z_7ACGoFM6Lhvho+eUG@pU&0X(Uy(*j;9pr?ET?FHTXadlfXC|MReZoU5>AG`mTM<% zc~*I@E*u0|hwVTdFA~4^b2VT7_~}~tCueNY{de3og=ASFQ`)0dhC2~Ne<}}Rc?ptA zi}+bQE%N9o*hpSUMH)9xt%Zlz&^p&5=cW}{m#f85iVX64^{!(vhClT<I)+c)RuiyrZqIw4v`z%YK&;_Fh4_+0B?qAGxMfAM`LzG_bjD>ib4;KGT4_1I>sxvL&&qp40ajgQOqIE^9=Az4w#ymo)bW-Vg{T!n=l&|nR_ zw+wcH|FxUH63)~{M;goHepmD{Fe?W9sO|eJP9L$G<{e_7FxxuXQ+)(Z^@;X8I1=%k zTK$gbHA1^4W<`q~ubQ0M_C^CA5#Z&*nGc(T?4Y_2jLu&FJDQYpCSiRny->$+nC9Jl z?avTW`ZXYT51%SrEq!}dXNM&!pM6nmL^lce=%S7{_TS)ckN8;{p*LT~LMgmlE~dpL zEBQy-jDj%cSK6N3)|CCR0LQ$N6iDM~+-1Oz|LAdkip(VZcO`gqCuJ+(Mm{m6@P%_; zBtF|MMVMP;E`5NJ{&@4j^JE5j&}(Jq{lCGL(P^#uqvbD`2)FVyfNgy|pvT!XY;02Z zZWbgGsvi6#!*$Zxwd{Xk6_M{+^yV_K@%_SAW(x)Lg|*AuG-%g2#GQYk8F?W&8|2dU z;00ppzrQnnYXnT`(S%_qF2#QNz&@Y$zcq+O8p>Gto2&4z8(^#cY?DuQwBQP4Fe?qUK_-yh4xT{8O@gb`uh` z>Q%jrgPAnANn4_)->n;w{Mei#J)F+`12&+-MLKSRzF6bL3;4O~oy~v7 zL0K-=m?>>(^qDCgvFRLBI@`04EGdTxe5}xBg#7#Wb!aUED;?5BLDEvZ@tai4*Rh8& z4V)cOr}DJ0&(FjWH%50Y+&=WtB42^eEVsmaHG)Il#j265oK&Bot(+-IIn`6InmuE# z;)qXs+X{fSb8^rYb#46X5?KCzH9X0>ppBQi(aKS--;4yA%0N|D<#8RZlOS(8n26=u zv~y;KC>`ypW=aqj`&x9 z0Zm>NKp}hPJu1+QDo(_U(Gt0SZ`IJWnp%QK`pye>Bm!w{sG>;VU^2 z4lZhV1}tCE8(?zu#j99|l3-qRBcz3bG+DlyxPGB$^6B^ssc_qYQ6lG0q~EAI?1$?( zahfn%etVvuKwB7R=>JDQluP97nLDM6*5;b0Ox#b{4nIgZA*+?IvyDN{K9WGnlA=Ju z+)6hjr}{;GxQQIDr3*lf32lRp{nHP8uiz^Fa|K+dUc@wD4Kf5RPxVkUZFCdtZH{+=c$AC)G2T-Qn@BPbr zZigIhKhKrVYy`!Mlc#HVr=CURVrhUjExhI~gZ%a=WM9BwvnN?=z!_ZQ$(sP?X;2Jy zyI$}H^^SvH2tf6+Uk$pJww@ngzPp856-l9g6WtW+%Yf>N^A}->#1W2n=WJ%sZ0<){Z&#% z^Kzl$>Km)sIxKLFjtc;}bZeoaZSpL4>`jCmAeRM-NP9sQ&-mi@p0j7Iq>1n&z@8?M z%dM7K^SgE5z)@i5w#rLE4+8%|^J`a6wYr`3BlvdD>7xW?Dd>`0HC0o{w7r_ot~h*G z2gI7Y!AUZ6YN+z$=GNzns@Tu7BxgAb3MBha30-ZG7a%rckU5}y{df`lj@^+34kr5> z988PPbWYdHye~=?>uZ4N&MN@4RBLk_?9W*b$}jqt0j%>yO9QOV(*!#cX~=wRdVL&S zhPQ{${0CGU-rfdS&b@u|IK{hV2Z=(*B2d0?&jwWfT=?Gk`4T9TfMQ)CfNgpLQa#>Q z%6A$w#QNc&qOtrHAbqY>J782@!X{9Y@N(HMSr;PP^;0DlJNxfC`oMB%Ocg zC*hnEsF|p*=CVe^dT)>BTL0yff)uo!U<+_2o3p)CE8quU1JI(=6)9$KxVdJYD*S*~ zzNeSkzFIQyqK}578+qq6X8rrRdgX z4k&R=AGex~a)MoB0pK&|yA<(*J#P&tR?ImBVD)ZTA4VH5L5DxXe<-*s`Aox%H1{-^Qa`kG_DGXD%QX-;l1#&#IVQP6>kir ztO@~ZvJDPnTvKt>fc*(j$W^)JhWk{4kWwbpFIXzuPt2V%M4H19-i5Gn*6(D`4_c1+ zYoI1@yT^~9JF~t>2eVM6p=GP3b*;daJpQOhAMNO|LKnwE2B5n8y9mf;q=)-L_FfD0 z<}YIRBO{k)6AHAn8iG>pYT+3bJ7jvP9}LSMR1nZW$5HR%PD1rFz z{4XE^Vmi-QX#?|Farz=CYS_8!%$E#G%4j2+;Avz|9QBj|YIExYk?y-1(j}0h{$$MnC_*F0U2*ExSi1ZCb_S9aV zTgyGP0Cl=m`emxM4Qih1E{`J{4oJo8K}WnH`@js^pR7Z-vTBK5F5JIFCDN}7pU^_nV>NTz@2$|Kcc5o+L&^Db_AQ);F?)X5BF*QJRCdLI-a%gW z++DZM)x=6*fNrSaUA&hf&CUqC$F*y^CJC-MAm9gd*5#^mh;-dR1?a&<3-hp3@}XN! z&8dcwo6=MQua%0KFvYbi>O{j)RrbDQo3S*y!oEJ~2=}^-v%zn~@hnmKGOvX6JLr;>DNC3)={8OM9n5Zs*(DlS*|%JTniJX2Uav7sOFT0vdIiUOC5pEtY?EF)@Fh9pCfD%N zXskZ8b^ldI{HHj{-l?iWo@IW6Nr`hAS>f8S*8FGc*gmcK^f2JS+>I&r#Gcewy=-JM zv0*w<5qBa6UQB@`esOG*4*t@7c9AkrTpM`v=eY?cO#z17H9B%Xy4m!}LhW}*iZ27w1?HrevgB1SZ1q2X$mm@FK@Qt7o z!s~Lio^IRdwzyvQ80{5iYeTV@mAo=2o5>KepRH0d{*Szlg~n%w2)S5v2|K8}pj;c{ zoDRLvYJO1@?x-=mq+LVhD{l-1-Dw4`7M?3@+ z`fu7?1#9W++6Y46N=H0+bD|CJH~q*CdEBm8D##VS7`cXy4~+x=ZC17rJeBh zI~qW^&FU`+e!{AKO3(>z5Ghh14bUT$=4B>@DVm(cj* zSLA*j!?z!=SLuVvAPh_EFKx}JE8T8;Gx)LH^H136=#Jn3Bo*@?=S`5M{WJPY&~ODs z+^V57DhJ2kD^Z|&;H}eoN~sxS8~cN5u1eW{t&y{!ouH`%p4(yDZaqw$%dlm4A0f0| z8H}XZFDs?3QuqI^PEy}T;r!5+QpfKEt&V|D)Z*xoJ?XXZ+k!sU2X!rcTF4tg8vWPM zr-JE>iu9DZK`#R5gQO{nyGDALY!l@M&eZsc*j*H~l4lD)8S?R*nrdxn?ELUR4kxK? zH(t9IM~^mfPs9WxR>J{agadQg@N6%=tUQ8Bn++TC|Hbqn*q;WydeNIS@gt|3j!P`w zxCKoeKQ*WBlF%l4-apIhERKl(hXS1vVk$U?Wifi)&lL6vF@bmFXmQEe{=$iG)Zt*l z0df@_)B-P_^K2P7h=>OIQ6f0Q-E@|M?$Z5n^oN>2_sBCpN>q(LnqUoef{tm^5^L$# z{<SL zKmH78cHX`4cBKIY8u1x*lwrgP^fJ%E&&AmHrRY7^hH*=2OA9K?!+|~Aeia=nAA`5~ z#zI=h#I>@FXaGk(n)0uqelNY;A5I9obE~OjsuW!%^NxK*52CfBPWYuw--v<1v|B>h z8R=#$TS-Pt3?d@P+xqmYpL4oB8- z>w99}%xqy9W!A^ODfLq8iA@z}10u?o#nG#MXumSaybi(S{`wIM z&nE3n2gWWMu93EvtofWzvG2{v;$ysuw^8q?3n}y=pB1vUr5gi++PjiyBH3jzKBRny zSO~O++1ZLdy7v7VzS&$yY;^Z7*j_#BI`PK`dAzJa9G1{9ahPqPi1C}ti+L)WHii*= z+RZ^+at-tlatc4|akPa&9H;%gn9aS`X_kfb>n>#NTyUVM6m4NCIfLm(28>qaYv7}t zn`M;XcONtXoa3#u3{L-ytd_&g z2mO$8CnE?460w#eSm|smlnNwFHM;A&IxSKLzVkV7nNVqZ*A`)eI{Nbg6WxsarAFuc=FFf1z|%#eTvBgUhY}N zsCT>`_YO>14i^vFX0KXbARLItzT{TeD%N~=ovGtZ6j{>PxkuYlHNTe0!u>rgw#?td z{)n=QrGvgCDE6BUem$Rh(1y!$@(Bn!k3E0|>PQ(8O==zN`?yBhAqlWyq+c%+h?p^- zE&OtLind}^_=>pbhxOgOIC0q9{cLK6p6*eg_|S+p9$W~_u4wzx@N?$QmFg2S)m~^R znni$X{U*!lHgdS@fI;|Owl=9Gwi?dr0m#>yL<8<}bLW_Kpl| zSGesADX&n?qmHC`2GyIev^hi~ka}ISZ^Y4w-yUzyPxaJB0mm%ww^>if3<;P^U+L5=s+cifT-ct*;!dOOk#SOZNv@a^J|DrS3YtSn8EEAlabX1NV3RfHwZn_41Xa z4;$taa6JJR()-FQ<#0G~WlML<l5I+IPnqDpW(PP>hRcQ+S2zU?tbG^(y z1K_?1R){jF;OKGw0WYjnm>aPxnmr5?bP?^B-|Fv`TT4ecH3O`Z3`X_r;vgFn>t1tE zGE6W2PODPKUj+@a%3lB;lS?srE5lp(tZ;uvzrPb){f~n7v_^z! z=16!Vdm!Q0q#?jy0qY%#0d^J8D9o)A;Rj!~j%u>KPs-tB08{4s1ry9VS>gW~5o^L; z7vyjmfXDGRVFa@-mis2!a$GI@9kE*pe3y_C3-$iVGUTQzZE+%>vT0=r|2%xMDBC@>WlkGU4CjoWs@D(rZ zS1NB#e69fvI^O#5r$Hj;bhHPEE4)4q5*t5Gyjzyc{)o459VkEhJ$%hJUC&67k z7gdo`Q*Jm3R&?ueqBezPTa}OI9wqcc;FRTcfVXob^z|dNIB0hMkHV26$zA%YgR$sM zTKM61S}#wJ#u+0UDE3N+U*~Tz1nnV;W<8Akz&6M7-6mIF(Pq`wJ1A%loYL( zIS;&2((xbyL7zoyaY2Sa%BBYBxo6Aa*53`~e@|RA`MP+?iI4KZ+y4EU&I zS_|(#*&j2hxpELa3r0O7ok&5!ijRiRu9i-_3cdnydZU9Mp6Y);skv%!$~`i-J7e-g zj@EoHf+gtcrKf;tY5`4iLnWSHa)9brUM$XmEzG3T0BXTG_+0}p7uGLs^(uYh0j$;~ zT1&~S%_Y5VImvf1EkD7vP-@F%hRlBe{a@T!SW(4WEQd1!O47*Crf@u-TS==48iR5x z!*`Ul4AJI^vIVaN3u5UifXBX{fJ@z>4Q2#1?jpcdLocwymBgKrZ+^Cb@QuIxl58B* zD{t-W3;M;{MGHm_@&n(6A-AsD;JO#>J3o4ru{hy;k;8?=rkp0tadEEcHNECoTI(W31`El-CI0eWQ zWD4&2ehvACkLCjG`82T`L^cNNC4Oo2IH(T4e;C75IwkJ&`|ArqSKD}TX_-E*eeiU& ziUuAC)A?d>-;@9Jcmsdca>@q1`6vzo^3etEH%1Gco&gvC{;Y-qyJ$Re`#A!5Kd((5 z6sSiKnA20uPX0**Mu&6tNgTunUR1sodoNmDst1&wz8v7AG3=^huypTi`S7+GrO$D6 z)0Ja-y5r?QQ+&jVQBjitIZ`z2Ia}iXWf#=#>nU+ zL29$)Q>f#o<#4deo!Kuo@WX{G(`eLaf%(_Nc}E`q=BXHMS(Os{!g%(|&tTDIczE_# z5y%wjCp9S?&*8bS3imJi_9_COC)-_;6D9~8Om@?U2PGQpM^7LKG7Q~(AoSRgP#tZfVDF_zr;_U*!F9qsbVQ@un9O2>T4M5tr0B~~v_@a=w^8h510a#=L z;8+9zhV}57uajb+9DbZm1G`_NqOuKN`bQ2fw9A*v*Kdb_E-SA`?2 z)OFIY-%uD`JZUZg?D4lHtNegKgWr!1m%hOpu5`R+bZ2K#&)*R-7ElKYo0$0xYxIL8 zLg%u|4oZixz}ILB-@aS4=XOe)z!VL6@?dX{LW^YCPjKtyw44)xT=H;h(fmFr>R?p%r5*}W z7_bo0drVDRq9V9QL4_!dazughK6t}tVVvBq={T0+3(1zmb>f+|;{D%J?^xnZcqio5 z%H?@L+L-CIdO=x6QrALL9&PwvjrZi5NS)1e<*%V8ntw~S2PF}zH}B5f_DHyB=I3m@ z_;^TpN|sesCU}qxQ`~jIwF>#8wGvxg9kdMT$}us8BM&W>OzZ|ry2BB)+UY*_yH+&L zl_=Jy9BNzIZs}D~Yv_H%HPjVGNV=xT3xpIW!Np1F^G#9Y8X zl)c_V1(DhYu-v%H3-m&n%M_}}c{E5Wu+6*>R24gW_A7$(U=9D|H$r;;;@o zJ)c_CmVf9l*;4SyJ}E{+4)}^C>SIJ*_bul7OJ{v&0oO>jG(5xzYP0$I%*YH|Mwu#r zubNW5VZ9^X#Phw<;?=^G?Kg&C)^x1FVsKGZ*n+{C1znj~YHSP?6PS(k5e9qGvS4X* z=1kA_27(iV65a(i+Sicmd@Vzf^2@*Wed-`aYQ~em=-h%Pu`gHfz)&@$hpr<&mNO={ zl^kI0HP0wTbbh{d(>5a#;zT2_=ppef?;D4;2^}&kZjB^yl%LBJ;|> zkLc)JEg*5rpQ;_)w?PnKynWtv!@ z>}+am{@(g$KKM+e$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/file_selector/file_selector/example/macos/Runner/Configs/AppInfo.xcconfig b/packages/file_selector/file_selector/example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 000000000000..8b42559e8758 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.example + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2022 com.example. All rights reserved. diff --git a/packages/file_selector/file_selector/example/macos/Runner/Configs/Debug.xcconfig b/packages/file_selector/file_selector/example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 000000000000..36b0fd9464f4 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/file_selector/file_selector/example/macos/Runner/Configs/Release.xcconfig b/packages/file_selector/file_selector/example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 000000000000..dff4f49561c8 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/file_selector/file_selector/example/macos/Runner/Configs/Warnings.xcconfig b/packages/file_selector/file_selector/example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 000000000000..42bcbf4780b1 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/packages/file_selector/file_selector/example/macos/Runner/DebugProfile.entitlements b/packages/file_selector/file_selector/example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 000000000000..dddb8a30c851 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/packages/file_selector/file_selector/example/macos/Runner/Info.plist b/packages/file_selector/file_selector/example/macos/Runner/Info.plist new file mode 100644 index 000000000000..4789daa6a443 --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/packages/file_selector/file_selector/example/macos/Runner/MainFlutterWindow.swift b/packages/file_selector/file_selector/example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 000000000000..32aaeedceb1f --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,19 @@ +// 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 Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/packages/file_selector/file_selector/example/macos/Runner/Release.entitlements b/packages/file_selector/file_selector/example/macos/Runner/Release.entitlements new file mode 100644 index 000000000000..852fa1a4728a --- /dev/null +++ b/packages/file_selector/file_selector/example/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index 41ff35307867..14ff92997c00 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.3 +version: 0.8.4 environment: sdk: ">=2.12.0 <3.0.0" @@ -12,12 +12,15 @@ environment: flutter: plugin: platforms: + macos: + default_package: file_selector_macos web: default_package: file_selector_web windows: default_package: file_selector_windows dependencies: + file_selector_macos: ^0.8.2 file_selector_platform_interface: ^2.0.0 file_selector_web: ^0.8.1 file_selector_windows: ^0.8.2 diff --git a/script/configs/exclude_integration_macos.yaml b/script/configs/exclude_integration_macos.yaml index bb58d463f7d9..7a9e287da05f 100644 --- a/script/configs/exclude_integration_macos.yaml +++ b/script/configs/exclude_integration_macos.yaml @@ -1,2 +1,3 @@ # Can't use Flutter integration tests due to native modal UI. +- file_selector - file_selector_macos From 35295552b8a4a74536cb8cc585e108a9bc89ea37 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 4 Mar 2022 15:01:24 -0500 Subject: [PATCH 006/844] Roll Flutter from fbf6a58bf19c to 163a7ac116f4 (3 revisions) (#5003) --- .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 57e681ad1a34..2c1ddd66becc 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fbf6a58bf19c52b6b2dba2dd218f046eedeabc6d +163a7ac116f438c101ffbd06da515dd7d50d16a0 From c0db021f1d4061c5f9e401379bcfdf3d62885569 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 4 Mar 2022 17:11:23 -0500 Subject: [PATCH 007/844] Roll Flutter from 163a7ac116f4 to 671aa9e95e97 (5 revisions) (#5006) --- .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 2c1ddd66becc..675770f0cdd2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -163a7ac116f438c101ffbd06da515dd7d50d16a0 +671aa9e95e979f09318cf34a8637733a6eccba79 From 30075f294c46c2733705f8f8ed6b47d195d321ba Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 4 Mar 2022 17:51:23 -0500 Subject: [PATCH 008/844] [quick_actions] Publish federated version (#5005) --- .../quick_actions/quick_actions/CHANGELOG.md | 4 ++++ .../quick_actions/quick_actions/pubspec.yaml | 18 +++++------------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index 33e80498a63c..fe596087b990 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.0+10 + +* Moves Android and iOS implementations to federated packages. + ## 0.6.0+9 * Updates Android compileSdkVersion to 31. diff --git a/packages/quick_actions/quick_actions/pubspec.yaml b/packages/quick_actions/quick_actions/pubspec.yaml index b2a9f7498a95..8ef2d3ab4e02 100644 --- a/packages/quick_actions/quick_actions/pubspec.yaml +++ b/packages/quick_actions/quick_actions/pubspec.yaml @@ -3,15 +3,11 @@ description: Flutter plugin for creating shortcuts on home screen, also known as Quick Actions on iOS and App Shortcuts on Android. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%22 -version: 0.6.0+9 - -# Temporarily disable publishing to allow moving Android and iOS -# implementations. -publish_to: none +version: 0.6.0+10 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: @@ -24,11 +20,8 @@ flutter: dependencies: flutter: sdk: flutter - # Temporary path dependencies to allow moving Android and iOS implementations. - quick_actions_android: - path: ../quick_actions_android - quick_actions_ios: - path: ../quick_actions_ios + quick_actions_android: ^0.6.0+9 + quick_actions_ios: ^0.6.0+9 quick_actions_platform_interface: ^1.0.0 dev_dependencies: @@ -36,6 +29,5 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - mockito: ^5.0.0-nullsafety.7 - pedantic: ^1.11.0 + mockito: ^5.0.0 plugin_platform_interface: ^2.0.0 From 675f91b3a3e2c7bf2754c921667dd589f9010d92 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 4 Mar 2022 18:31:20 -0500 Subject: [PATCH 009/844] Roll Flutter from 671aa9e95e97 to 513df67edb69 (2 revisions) (#5007) --- .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 675770f0cdd2..7f2096675aba 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -671aa9e95e979f09318cf34a8637733a6eccba79 +513df67edb690f913caf83c31311317cbc8ddc99 From 091e0cf8a9f5b2519f1caacf1865dfc274fdddfc Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 7 Mar 2022 15:59:40 -0500 Subject: [PATCH 010/844] [ci] Update FTL key (#5012) Replaces the expired key with the updated key so that Android tests work again. --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index 1184ac7e00a1..f9454cb9f315 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -197,7 +197,7 @@ task: CHANNEL: "master" CHANNEL: "stable" MAPS_API_KEY: ENCRYPTED[596a9f6bca436694625ac50851dc5da6b4d34cba8025f7db5bc9465142e8cd44e15f69e3507787753accebfc4910d550] - GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[de02374f8d2d14d50792c6b521af2dfb86cbb522efed104f905002e4332546104d387d2bb8710956b729b4bd6533bba0] + GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[4c11f1a80a5741d51e92ab609bc7214ab2aa015e68a490e4d6777ebdf84f9c899b97c0ded2f4b2e6adf2c8b5ead1e3c5] build_script: # Unsetting CIRRUS_CHANGE_MESSAGE and CIRRUS_COMMIT_MESSAGE as they # might include non-ASCII characters which makes Gradle crash. From 18b269e3a1430bf9d1abeb57f6661573dc610547 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 7 Mar 2022 16:36:23 -0500 Subject: [PATCH 011/844] Roll Flutter from 513df67edb69 to 03c92a99fdf8 (5 revisions) (#5010) --- .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 7f2096675aba..4eb3f36a7157 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -513df67edb690f913caf83c31311317cbc8ddc99 +03c92a99fdf8d91db89fa8dde19912b7ef341b67 From 90e741c88c1231ad959b65eb9d6f6699d0ae51b1 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 7 Mar 2022 17:41:22 -0500 Subject: [PATCH 012/844] Roll Flutter from 03c92a99fdf8 to a491a81f7873 (26 revisions) (#5014) --- .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 4eb3f36a7157..d34ceb4a0991 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -03c92a99fdf8d91db89fa8dde19912b7ef341b67 +a491a81f78739dc1c801529b98239adc6a2dd361 From b906ea50c90072a24cca3ddea78fa8d163bac4fe Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Mon, 7 Mar 2022 15:41:23 -0800 Subject: [PATCH 013/844] [camera]remove QueueHelper class (#4983) --- packages/camera/camera/CHANGELOG.md | 4 ++++ .../example/ios/Runner.xcodeproj/project.pbxproj | 10 +++++----- .../{QueueHelperTests.m => QueueUtilsTests.m} | 12 ++++++------ packages/camera/camera/ios/Classes/CameraPlugin.m | 2 +- .../camera/ios/Classes/CameraPlugin.modulemap | 2 +- packages/camera/camera/ios/Classes/FLTCam.m | 2 +- .../camera/ios/Classes/FLTThreadSafeEventChannel.m | 6 +++--- .../ios/Classes/FLTThreadSafeFlutterResult.m | 6 +++--- .../ios/Classes/FLTThreadSafeMethodChannel.m | 6 +++--- .../ios/Classes/FLTThreadSafeTextureRegistry.m | 14 +++++++------- .../ios/Classes/{QueueHelper.h => QueueUtils.h} | 14 +++++--------- .../ios/Classes/{QueueHelper.m => QueueUtils.m} | 8 ++------ 12 files changed, 41 insertions(+), 45 deletions(-) rename packages/camera/camera/example/ios/RunnerTests/{QueueHelperTests.m => QueueUtilsTests.m} (84%) rename packages/camera/camera/ios/Classes/{QueueHelper.h => QueueUtils.h} (61%) rename packages/camera/camera/ios/Classes/{QueueHelper.m => QueueUtils.m} (75%) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 9ef626a8ad2b..c088bd5b5373 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Minor iOS internal code cleanup related to queue helper functions. + ## 0.9.4+14 * Restores compatibility with Flutter 2.5 and 2.8. diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index 5f788fc9b9f9..8c102bfa38b1 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -20,7 +20,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 */; }; - E01EE4A82799F3A5008C1950 /* QueueHelperTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E01EE4A72799F3A5008C1950 /* QueueHelperTests.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 */; }; E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */; }; @@ -83,7 +83,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 = ""; }; - E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = QueueHelperTests.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 = ""; }; E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTCamPhotoCaptureTests.m; sourceTree = ""; }; @@ -131,7 +131,7 @@ E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */, E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */, E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */, - E01EE4A72799F3A5008C1950 /* QueueHelperTests.m */, + E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */, E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */, F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */, F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */, @@ -413,7 +413,7 @@ E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */, E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */, - E01EE4A82799F3A5008C1950 /* QueueHelperTests.m in Sources */, + E01EE4A82799F3A5008C1950 /* QueueUtilsTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/packages/camera/camera/example/ios/RunnerTests/QueueHelperTests.m b/packages/camera/camera/example/ios/RunnerTests/QueueUtilsTests.m similarity index 84% rename from packages/camera/camera/example/ios/RunnerTests/QueueHelperTests.m rename to packages/camera/camera/example/ios/RunnerTests/QueueUtilsTests.m index c5f377f7efa9..de11b4f6961f 100644 --- a/packages/camera/camera/example/ios/RunnerTests/QueueHelperTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/QueueUtilsTests.m @@ -5,20 +5,20 @@ @import camera; @import XCTest; -@interface QueueHelperTests : XCTestCase +@interface QueueUtilsTests : XCTestCase @end -@implementation QueueHelperTests +@implementation QueueUtilsTests - (void)testShouldStayOnMainQueueIfCalledFromMainQueue { XCTestExpectation *expectation = [self expectationWithDescription:@"Block must be run on the main queue."]; - [QueueHelper ensureToRunOnMainQueue:^{ + FLTEnsureToRunOnMainQueue(^{ if (NSThread.isMainThread) { [expectation fulfill]; } - }]; + }); [self waitForExpectationsWithTimeout:1 handler:nil]; } @@ -26,11 +26,11 @@ - (void)testShouldDispatchToMainQueueIfCalledFromBackgroundQueue { XCTestExpectation *expectation = [self expectationWithDescription:@"Block must be run on the main queue."]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [QueueHelper ensureToRunOnMainQueue:^{ + FLTEnsureToRunOnMainQueue(^{ if (NSThread.isMainThread) { [expectation fulfill]; } - }]; + }); }); [self waitForExpectationsWithTimeout:1 handler:nil]; } diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index fcea190de705..634aa699a01a 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -13,7 +13,7 @@ #import "FLTThreadSafeFlutterResult.h" #import "FLTThreadSafeMethodChannel.h" #import "FLTThreadSafeTextureRegistry.h" -#import "QueueHelper.h" +#import "QueueUtils.h" @interface CameraPlugin () @property(readonly, nonatomic) FLTThreadSafeTextureRegistry *registry; diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.modulemap b/packages/camera/camera/ios/Classes/CameraPlugin.modulemap index 529c6580e908..a23848aaccfc 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.modulemap +++ b/packages/camera/camera/ios/Classes/CameraPlugin.modulemap @@ -14,6 +14,6 @@ framework module camera { header "FLTThreadSafeFlutterResult.h" header "FLTThreadSafeMethodChannel.h" header "FLTThreadSafeTextureRegistry.h" - header "QueueHelper.h" + header "QueueUtils.h" } } diff --git a/packages/camera/camera/ios/Classes/FLTCam.m b/packages/camera/camera/ios/Classes/FLTCam.m index 31a9decd59b9..669ce334059e 100644 --- a/packages/camera/camera/ios/Classes/FLTCam.m +++ b/packages/camera/camera/ios/Classes/FLTCam.m @@ -5,7 +5,7 @@ #import "FLTCam.h" #import "FLTCam_Test.h" #import "FLTSavePhotoDelegate.h" -#import "QueueHelper.h" +#import "QueueUtils.h" @import CoreMotion; #import diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m b/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m index 02a36f152bd8..46941bb18dd6 100644 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m @@ -3,7 +3,7 @@ // found in the LICENSE file. #import "FLTThreadSafeEventChannel.h" -#import "QueueHelper.h" +#import "QueueUtils.h" @interface FLTThreadSafeEventChannel () @property(nonatomic, strong) FlutterEventChannel *channel; @@ -21,10 +21,10 @@ - (instancetype)initWithEventChannel:(FlutterEventChannel *)channel { - (void)setStreamHandler:(NSObject *)handler completion:(void (^)(void))completion { - [QueueHelper ensureToRunOnMainQueue:^{ + FLTEnsureToRunOnMainQueue(^{ [self.channel setStreamHandler:handler]; completion(); - }]; + }); } @end diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m index 821d7561f782..58c2e788cdc0 100644 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m @@ -4,7 +4,7 @@ #import "FLTThreadSafeFlutterResult.h" #import -#import "QueueHelper.h" +#import "QueueUtils.h" @implementation FLTThreadSafeFlutterResult { } @@ -47,9 +47,9 @@ - (void)sendNotImplemented { * Sends result to flutterResult on the main thread. */ - (void)send:(id _Nullable)result { - [QueueHelper ensureToRunOnMainQueue:^{ + FLTEnsureToRunOnMainQueue(^{ self.flutterResult(result); - }]; + }); } @end diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m b/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m index ad4da87c8e91..5b29b70ee432 100644 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m @@ -3,7 +3,7 @@ // found in the LICENSE file. #import "FLTThreadSafeMethodChannel.h" -#import "QueueHelper.h" +#import "QueueUtils.h" @interface FLTThreadSafeMethodChannel () @property(nonatomic, strong) FlutterMethodChannel *channel; @@ -20,9 +20,9 @@ - (instancetype)initWithMethodChannel:(FlutterMethodChannel *)channel { } - (void)invokeMethod:(NSString *)method arguments:(id)arguments { - [QueueHelper ensureToRunOnMainQueue:^{ + FLTEnsureToRunOnMainQueue(^{ [self.channel invokeMethod:method arguments:arguments]; - }]; + }); } @end diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m b/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m index 5eb2443e4ee2..349b89fdc117 100644 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m @@ -3,7 +3,7 @@ // found in the LICENSE file. #import "FLTThreadSafeTextureRegistry.h" -#import "QueueHelper.h" +#import "QueueUtils.h" @interface FLTThreadSafeTextureRegistry () @property(nonatomic, strong) NSObject *registry; @@ -21,21 +21,21 @@ - (instancetype)initWithTextureRegistry:(NSObject *)regi - (void)registerTexture:(NSObject *)texture completion:(void (^)(int64_t))completion { - [QueueHelper ensureToRunOnMainQueue:^{ + FLTEnsureToRunOnMainQueue(^{ completion([self.registry registerTexture:texture]); - }]; + }); } - (void)textureFrameAvailable:(int64_t)textureId { - [QueueHelper ensureToRunOnMainQueue:^{ + FLTEnsureToRunOnMainQueue(^{ [self.registry textureFrameAvailable:textureId]; - }]; + }); } - (void)unregisterTexture:(int64_t)textureId { - [QueueHelper ensureToRunOnMainQueue:^{ + FLTEnsureToRunOnMainQueue(^{ [self.registry unregisterTexture:textureId]; - }]; + }); } @end diff --git a/packages/camera/camera/ios/Classes/QueueHelper.h b/packages/camera/camera/ios/Classes/QueueUtils.h similarity index 61% rename from packages/camera/camera/ios/Classes/QueueHelper.h rename to packages/camera/camera/ios/Classes/QueueUtils.h index dc7373220521..a7e22da716d0 100644 --- a/packages/camera/camera/ios/Classes/QueueHelper.h +++ b/packages/camera/camera/ios/Classes/QueueUtils.h @@ -7,17 +7,13 @@ NS_ASSUME_NONNULL_BEGIN /// Queue-specific context data to be associated with the capture session queue. -extern const char *FLTCaptureSessionQueueSpecific; - -/// A class that contains dispatch queue related helper functions. -@interface QueueHelper : NSObject +extern const char* FLTCaptureSessionQueueSpecific; /// Ensures the given block to be run on the main queue. -/// If caller site is already on the main queue, the block will be run synchronously. Otherwise, the -/// block will be dispatched asynchronously to the main queue. +/// If caller site is already on the main queue, the block will be run +/// synchronously. Otherwise, the block will be dispatched asynchronously to the +/// main queue. /// @param block the block to be run on the main queue. -+ (void)ensureToRunOnMainQueue:(void (^)(void))block; - -@end +extern void FLTEnsureToRunOnMainQueue(dispatch_block_t block); NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/QueueHelper.m b/packages/camera/camera/ios/Classes/QueueUtils.m similarity index 75% rename from packages/camera/camera/ios/Classes/QueueHelper.m rename to packages/camera/camera/ios/Classes/QueueUtils.m index 2cef7b677bfa..1fd54cd52cb3 100644 --- a/packages/camera/camera/ios/Classes/QueueHelper.m +++ b/packages/camera/camera/ios/Classes/QueueUtils.m @@ -2,18 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "QueueHelper.h" +#import "QueueUtils.h" const char *FLTCaptureSessionQueueSpecific = "capture_session_queue"; -@implementation QueueHelper - -+ (void)ensureToRunOnMainQueue:(void (^)(void))block { +void FLTEnsureToRunOnMainQueue(dispatch_block_t block) { if (!NSThread.isMainThread) { dispatch_async(dispatch_get_main_queue(), block); } else { block(); } } - -@end From 4e1c90e08200ff09125201331fef7fa2d3894d5a Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Tue, 8 Mar 2022 11:35:21 -0800 Subject: [PATCH 014/844] [camera]remove CAS operation and use dispatch queue instead (#4973) --- packages/camera/camera/CHANGELOG.md | 3 +- .../ios/Runner.xcodeproj/project.pbxproj | 6 ++ .../camera/camera/example/ios/Runner/main.m | 2 +- .../example/ios/RunnerTests/CameraTestUtils.h | 18 ++++++ .../example/ios/RunnerTests/CameraTestUtils.m | 44 ++++++++++++++ .../ios/RunnerTests/FLTCamPhotoCaptureTests.m | 25 ++------ .../ios/RunnerTests/FLTCamSampleBufferTests.m | 36 ++++++------ .../RunnerTests/FLTSavePhotoDelegateTests.m | 6 +- packages/camera/camera/ios/Classes/FLTCam.h | 7 +++ packages/camera/camera/ios/Classes/FLTCam.m | 57 ++++++++++++++----- .../camera/camera/ios/Classes/FLTCam_Test.h | 18 +++++- packages/camera/camera/pubspec.yaml | 2 +- 12 files changed, 166 insertions(+), 58 deletions(-) create mode 100644 packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.h create mode 100644 packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.m diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index c088bd5b5373..4b96fd6493e5 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.9.4+15 +* Uses dispatch queue for pixel buffer synchronization on iOS. * Minor iOS internal code cleanup related to queue helper functions. ## 0.9.4+14 diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index 8c102bfa38b1..6c171505a8ca 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */; }; E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */; }; E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; + E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */; }; E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */; }; E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */; }; F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */ = {isa = PBXBuildFile; fileRef = F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */; }; @@ -91,6 +92,8 @@ E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeMethodChannelTests.m; sourceTree = ""; }; E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeTextureRegistryTests.m; sourceTree = ""; }; E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = ""; }; + E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CameraTestUtils.h; sourceTree = ""; }; + E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraTestUtils.m; sourceTree = ""; }; E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPropertiesTests.m; sourceTree = ""; }; E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPreviewPauseTests.m; sourceTree = ""; }; F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockFLTThreadSafeFlutterResult.h; sourceTree = ""; }; @@ -132,6 +135,8 @@ E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */, E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */, E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */, + E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */, + E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */, E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */, F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */, F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */, @@ -408,6 +413,7 @@ E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */, E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */, F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */, + E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */, 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */, E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */, E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, diff --git a/packages/camera/camera/example/ios/Runner/main.m b/packages/camera/camera/example/ios/Runner/main.m index 8b3fb8d5b361..d1224fea37ed 100644 --- a/packages/camera/camera/example/ios/Runner/main.m +++ b/packages/camera/camera/example/ios/Runner/main.m @@ -11,7 +11,7 @@ int main(int argc, char *argv[]) { // The setup logic in `AppDelegate::didFinishLaunchingWithOptions:` eventually sends camera // operations on the background queue, which would run concurrently with the test cases during // unit tests, making the debugging process confusing. This setup is actually not necessary for - // the unit tests, so here we want to skip the AppDelegate when running unit tests. + // the unit tests, so it is better to skip the AppDelegate when running unit tests. BOOL isTesting = NSClassFromString(@"XCTestCase") != nil; return UIApplicationMain(argc, argv, nil, isTesting ? nil : NSStringFromClass([AppDelegate class])); diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.h b/packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.h new file mode 100644 index 000000000000..9fe67dc650e2 --- /dev/null +++ b/packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.h @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import camera; + +NS_ASSUME_NONNULL_BEGIN + +/// Creates an `FLTCam` that runs its capture session operations on a given queue. +/// @param captureSessionQueue the capture session queue +/// @return an FLTCam object. +extern FLTCam *FLTCreateCamWithCaptureSessionQueue(dispatch_queue_t captureSessionQueue); + +/// Creates a test sample buffer. +/// @return a test sample buffer. +extern CMSampleBufferRef FLTCreateTestSampleBuffer(void); + +NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.m b/packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.m new file mode 100644 index 000000000000..0ae4887eb631 --- /dev/null +++ b/packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.m @@ -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 "CameraTestUtils.h" +#import +@import AVFoundation; + +FLTCam *FLTCreateCamWithCaptureSessionQueue(dispatch_queue_t captureSessionQueue) { + id inputMock = OCMClassMock([AVCaptureDeviceInput class]); + OCMStub([inputMock deviceInputWithDevice:[OCMArg any] error:[OCMArg setTo:nil]]) + .andReturn(inputMock); + + id sessionMock = OCMClassMock([AVCaptureSession class]); + OCMStub([sessionMock addInputWithNoConnections:[OCMArg any]]); // no-op + OCMStub([sessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); + + return [[FLTCam alloc] initWithCameraName:@"camera" + resolutionPreset:@"medium" + enableAudio:true + orientation:UIDeviceOrientationPortrait + captureSession:sessionMock + captureSessionQueue:captureSessionQueue + error:nil]; +} + +CMSampleBufferRef FLTCreateTestSampleBuffer(void) { + CVPixelBufferRef pixelBuffer; + CVPixelBufferCreate(kCFAllocatorDefault, 100, 100, kCVPixelFormatType_32BGRA, NULL, &pixelBuffer); + + CMFormatDescriptionRef formatDescription; + CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer, + &formatDescription); + + CMSampleTimingInfo timingInfo = {CMTimeMake(1, 44100), kCMTimeZero, kCMTimeInvalid}; + + CMSampleBufferRef sampleBuffer; + CMSampleBufferCreateReadyWithImageBuffer(kCFAllocatorDefault, pixelBuffer, formatDescription, + &timingInfo, &sampleBuffer); + + CFRelease(pixelBuffer); + CFRelease(formatDescription); + return sampleBuffer; +} diff --git a/packages/camera/camera/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m b/packages/camera/camera/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m index fdb2abd4933e..ed3e6a9793fd 100644 --- a/packages/camera/camera/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m @@ -7,7 +7,9 @@ @import AVFoundation; @import XCTest; #import +#import "CameraTestUtils.h" +/// Includes test cases related to photo capture operations for FLTCam class. @interface FLTCamPhotoCaptureTests : XCTestCase @end @@ -22,7 +24,7 @@ - (void)testCaptureToFile_mustReportErrorToResultIfSavePhotoDelegateCompletionsW dispatch_queue_t captureSessionQueue = dispatch_queue_create("capture_session_queue", NULL); dispatch_queue_set_specific(captureSessionQueue, FLTCaptureSessionQueueSpecific, (void *)FLTCaptureSessionQueueSpecific, NULL); - FLTCam *cam = [self createFLTCamWithCaptureSessionQueue:captureSessionQueue]; + FLTCam *cam = FLTCreateCamWithCaptureSessionQueue(captureSessionQueue); AVCapturePhotoSettings *settings = [AVCapturePhotoSettings photoSettings]; id mockSettings = OCMClassMock([AVCapturePhotoSettings class]); OCMStub([mockSettings photoSettings]).andReturn(settings); @@ -61,7 +63,7 @@ - (void)testCaptureToFile_mustReportPathToResultIfSavePhotoDelegateCompletionsWi dispatch_queue_t captureSessionQueue = dispatch_queue_create("capture_session_queue", NULL); dispatch_queue_set_specific(captureSessionQueue, FLTCaptureSessionQueueSpecific, (void *)FLTCaptureSessionQueueSpecific, NULL); - FLTCam *cam = [self createFLTCamWithCaptureSessionQueue:captureSessionQueue]; + FLTCam *cam = FLTCreateCamWithCaptureSessionQueue(captureSessionQueue); AVCapturePhotoSettings *settings = [AVCapturePhotoSettings photoSettings]; id mockSettings = OCMClassMock([AVCapturePhotoSettings class]); @@ -92,23 +94,4 @@ - (void)testCaptureToFile_mustReportPathToResultIfSavePhotoDelegateCompletionsWi [self waitForExpectationsWithTimeout:1 handler:nil]; } -/// Creates an `FLTCam` that runs its operations on a given capture session queue. -- (FLTCam *)createFLTCamWithCaptureSessionQueue:(dispatch_queue_t)captureSessionQueue { - id inputMock = OCMClassMock([AVCaptureDeviceInput class]); - OCMStub([inputMock deviceInputWithDevice:[OCMArg any] error:[OCMArg setTo:nil]]) - .andReturn(inputMock); - - id sessionMock = OCMClassMock([AVCaptureSession class]); - OCMStub([sessionMock alloc]).andReturn(sessionMock); - OCMStub([sessionMock addInputWithNoConnections:[OCMArg any]]); // no-op - OCMStub([sessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); - - return [[FLTCam alloc] initWithCameraName:@"camera" - resolutionPreset:@"medium" - enableAudio:true - orientation:UIDeviceOrientationPortrait - captureSessionQueue:captureSessionQueue - error:nil]; -} - @end diff --git a/packages/camera/camera/example/ios/RunnerTests/FLTCamSampleBufferTests.m b/packages/camera/camera/example/ios/RunnerTests/FLTCamSampleBufferTests.m index ccc8de5b23bd..8f65c4e9eaa5 100644 --- a/packages/camera/camera/example/ios/RunnerTests/FLTCamSampleBufferTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/FLTCamSampleBufferTests.m @@ -7,7 +7,9 @@ @import AVFoundation; @import XCTest; #import +#import "CameraTestUtils.h" +/// Includes test cases related to sample buffer handling for FLTCam class. @interface FLTCamSampleBufferTests : XCTestCase @end @@ -15,23 +17,25 @@ @interface FLTCamSampleBufferTests : XCTestCase @implementation FLTCamSampleBufferTests - (void)testSampleBufferCallbackQueueMustBeCaptureSessionQueue { - id inputMock = OCMClassMock([AVCaptureDeviceInput class]); - OCMStub([inputMock deviceInputWithDevice:[OCMArg any] error:[OCMArg setTo:nil]]) - .andReturn(inputMock); - - id sessionMock = OCMClassMock([AVCaptureSession class]); - OCMStub([sessionMock alloc]).andReturn(sessionMock); - OCMStub([sessionMock addInputWithNoConnections:[OCMArg any]]); // no-op - OCMStub([sessionMock canSetSessionPreset:[OCMArg any]]).andReturn(YES); - dispatch_queue_t captureSessionQueue = dispatch_queue_create("testing", NULL); - FLTCam *cam = [[FLTCam alloc] initWithCameraName:@"camera" - resolutionPreset:@"medium" - enableAudio:true - orientation:UIDeviceOrientationPortrait - captureSessionQueue:captureSessionQueue - error:nil]; - XCTAssertEqual(captureSessionQueue, cam.captureVideoOutput.sampleBufferCallbackQueue); + FLTCam *cam = FLTCreateCamWithCaptureSessionQueue(captureSessionQueue); + XCTAssertEqual(captureSessionQueue, cam.captureVideoOutput.sampleBufferCallbackQueue, + @"Sample buffer callback queue must be the capture session queue."); +} + +- (void)testCopyPixelBuffer { + FLTCam *cam = FLTCreateCamWithCaptureSessionQueue(dispatch_queue_create("test", NULL)); + CMSampleBufferRef capturedSampleBuffer = FLTCreateTestSampleBuffer(); + CVPixelBufferRef capturedPixelBuffer = CMSampleBufferGetImageBuffer(capturedSampleBuffer); + // Mimic sample buffer callback when captured a new video sample + [cam captureOutput:cam.captureVideoOutput + didOutputSampleBuffer:capturedSampleBuffer + fromConnection:OCMClassMock([AVCaptureConnection class])]; + CVPixelBufferRef deliveriedPixelBuffer = [cam copyPixelBuffer]; + XCTAssertEqual(deliveriedPixelBuffer, capturedPixelBuffer, + @"FLTCam must deliver the latest captured pixel buffer to copyPixelBuffer API."); + CFRelease(capturedSampleBuffer); + CFRelease(deliveriedPixelBuffer); } @end diff --git a/packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m b/packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m index 9e8e2441f0b9..a70a572ac0f2 100644 --- a/packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m @@ -53,7 +53,7 @@ - (void)testHandlePhotoCaptureResult_mustCompleteWithErrorIfFailedToWrite { [completionExpectation fulfill]; }]; - // We can't use OCMClassMock for NSData because some XCTest APIs uses NSData (e.g. + // Do not use OCMClassMock for NSData because some XCTest APIs uses NSData (e.g. // `XCTRunnerIDESession::logDebugMessage:`) on a private queue. id mockData = OCMPartialMock([NSData data]); OCMStub([mockData writeToFile:OCMOCK_ANY @@ -82,7 +82,7 @@ - (void)testHandlePhotoCaptureResult_mustCompleteWithFilePathIfSuccessToWrite { [completionExpectation fulfill]; }]; - // We can't use OCMClassMock for NSData because some XCTest APIs uses NSData (e.g. + // Do not use OCMClassMock for NSData because some XCTest APIs uses NSData (e.g. // `XCTRunnerIDESession::logDebugMessage:`) on a private queue. id mockData = OCMPartialMock([NSData data]); OCMStub([mockData writeToFile:filePath options:NSDataWritingAtomic error:[OCMArg setTo:nil]]) @@ -107,7 +107,7 @@ - (void)testHandlePhotoCaptureResult_bothProvideDataAndSaveFileMustRunOnIOQueue const char *ioQueueSpecific = "io_queue_specific"; dispatch_queue_set_specific(ioQueue, ioQueueSpecific, (void *)ioQueueSpecific, NULL); - // We can't use OCMClassMock for NSData because some XCTest APIs uses NSData (e.g. + // Do not use OCMClassMock for NSData because some XCTest APIs uses NSData (e.g. // `XCTRunnerIDESession::logDebugMessage:`) on a private queue. id mockData = OCMPartialMock([NSData data]); OCMStub([mockData writeToFile:OCMOCK_ANY options:NSDataWritingAtomic error:[OCMArg setTo:nil]]) diff --git a/packages/camera/camera/ios/Classes/FLTCam.h b/packages/camera/camera/ios/Classes/FLTCam.h index 417a1d74db21..0cd135e0e41f 100644 --- a/packages/camera/camera/ios/Classes/FLTCam.h +++ b/packages/camera/camera/ios/Classes/FLTCam.h @@ -31,6 +31,13 @@ NS_ASSUME_NONNULL_BEGIN // Format used for video and image streaming. @property(assign, nonatomic) FourCharCode videoFormat; +/// Initializes an `FLTCam` instance. +/// @param cameraName a name used to uniquely identify the camera. +/// @param resolutionPreset the resolution preset +/// @param enableAudio YES if audio should be enabled for video capturing; NO otherwise. +/// @param orientation the orientation of camera +/// @param captureSessionQueue the queue on which camera's capture session operations happen. +/// @param error report to the caller if any error happened creating the camera. - (instancetype)initWithCameraName:(NSString *)cameraName resolutionPreset:(NSString *)resolutionPreset enableAudio:(BOOL)enableAudio diff --git a/packages/camera/camera/ios/Classes/FLTCam.m b/packages/camera/camera/ios/Classes/FLTCam.m index 669ce334059e..30c177bd4b2a 100644 --- a/packages/camera/camera/ios/Classes/FLTCam.m +++ b/packages/camera/camera/ios/Classes/FLTCam.m @@ -52,7 +52,9 @@ @interface FLTCam () *inProgressSavePhotoDelegates; +/// Delegate callback when receiving a new video or audio sample. +/// Exposed for unit tests. +- (void)captureOutput:(AVCaptureOutput *)output + didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer + fromConnection:(AVCaptureConnection *)connection; + +/// Initializes a camera instance. +/// Allows for injecting dependencies that are usually internal. +- (instancetype)initWithCameraName:(NSString *)cameraName + resolutionPreset:(NSString *)resolutionPreset + enableAudio:(BOOL)enableAudio + orientation:(UIDeviceOrientation)orientation + captureSession:(AVCaptureSession *)captureSession + captureSessionQueue:(dispatch_queue_t)captureSessionQueue + error:(NSError **)error; + @end diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 7bc0e452bb29..fcdce02e947f 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+14 +version: 0.9.4+15 environment: sdk: ">=2.14.0 <3.0.0" From 715fdbe82394c90799532cdec0f775198d44e1bd Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 8 Mar 2022 15:15:27 -0500 Subject: [PATCH 015/844] Roll Flutter from a491a81f7873 to 10805ca3fe6b (4 revisions) (#5016) --- .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 d34ceb4a0991..ec4b42230e12 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a491a81f78739dc1c801529b98239adc6a2dd361 +10805ca3fe6bf22bba2454a8c2d26e1bbe2cd2c3 From 5691d52d93f65bd5408f202149af1779b427e868 Mon Sep 17 00:00:00 2001 From: carman247 <31341463+carman247@users.noreply.github.com> Date: Tue, 8 Mar 2022 22:20:23 +0100 Subject: [PATCH 016/844] [camera] Fix for CameraAccessException that prevents image capture on certain devices running Android 7/8 (#4572) --- packages/camera/camera/CHANGELOG.md | 7 ++- .../io/flutter/plugins/camera/Camera.java | 61 ++++++++++++++++++- .../io/flutter/plugins/camera/CameraTest.java | 55 +++++++++++++++++ packages/camera/camera/pubspec.yaml | 2 +- 4 files changed, 120 insertions(+), 5 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 4b96fd6493e5..35a958e026ff 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,7 +1,12 @@ +## 0.9.4+16 + +* Fixes a bug resulting in a `CameraAccessException` that prevents image + capture on some Android devices. + ## 0.9.4+15 * Uses dispatch queue for pixel buffer synchronization on iOS. -* Minor iOS internal code cleanup related to queue helper functions. +* Minor iOS internal code cleanup related to queue helper functions. ## 0.9.4+14 diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java index 6a70ea0d10ea..0521c422d794 100644 --- a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -79,6 +79,24 @@ interface ErrorCallback { void onError(String errorCode, String errorMessage); } +/** A mockable wrapper for CameraDevice calls. */ +interface CameraDeviceWrapper { + @NonNull + CaptureRequest.Builder createCaptureRequest(int templateType) throws CameraAccessException; + + @TargetApi(VERSION_CODES.P) + void createCaptureSession(SessionConfiguration config) throws CameraAccessException; + + @TargetApi(VERSION_CODES.LOLLIPOP) + void createCaptureSession( + @NonNull List outputs, + @NonNull CameraCaptureSession.StateCallback callback, + @Nullable Handler handler) + throws CameraAccessException; + + void close(); +} + class Camera implements CameraCaptureCallback.CameraCaptureStateListener, ImageReader.OnImageAvailableListener { @@ -114,7 +132,7 @@ class Camera /** An additional thread for running tasks that shouldn't block the UI. */ private HandlerThread backgroundHandlerThread; - private CameraDevice cameraDevice; + private CameraDeviceWrapper cameraDevice; private CameraCaptureSession captureSession; private ImageReader pictureImageReader; private ImageReader imageStreamReader; @@ -136,6 +154,44 @@ class Camera private MethodChannel.Result flutterResult; + /** A CameraDeviceWrapper implementation that forwards calls to a CameraDevice. */ + private class DefaultCameraDeviceWrapper implements CameraDeviceWrapper { + private final CameraDevice cameraDevice; + + private DefaultCameraDeviceWrapper(CameraDevice cameraDevice) { + this.cameraDevice = cameraDevice; + } + + @NonNull + @Override + public CaptureRequest.Builder createCaptureRequest(int templateType) + throws CameraAccessException { + return cameraDevice.createCaptureRequest(templateType); + } + + @TargetApi(VERSION_CODES.P) + @Override + public void createCaptureSession(SessionConfiguration config) throws CameraAccessException { + cameraDevice.createCaptureSession(config); + } + + @TargetApi(VERSION_CODES.LOLLIPOP) + @SuppressWarnings("deprecation") + @Override + public void createCaptureSession( + @NonNull List outputs, + @NonNull CameraCaptureSession.StateCallback callback, + @Nullable Handler handler) + throws CameraAccessException { + cameraDevice.createCaptureSession(outputs, callback, backgroundHandler); + } + + @Override + public void close() { + cameraDevice.close(); + } + } + public Camera( final Activity activity, final SurfaceTextureEntry flutterTexture, @@ -261,7 +317,7 @@ public void open(String imageFormatGroup) throws CameraAccessException { new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice device) { - cameraDevice = device; + cameraDevice = new DefaultCameraDeviceWrapper(device); try { startPreview(); dartMessenger.sendCameraInitializedEvent( @@ -584,7 +640,6 @@ public void onCaptureCompleted( try { captureSession.stopRepeating(); - captureSession.abortCaptures(); Log.i(TAG, "sending capture request"); captureSession.capture(stillBuilder.build(), captureCallback, backgroundHandler); } catch (CameraAccessException e) { diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest.java index 1ed2e4c11d7b..167733b9dcca 100644 --- a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest.java +++ b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest.java @@ -22,11 +22,15 @@ import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.params.SessionConfiguration; +import android.media.ImageReader; import android.media.MediaRecorder; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; +import android.view.Surface; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.lifecycle.LifecycleObserver; import io.flutter.embedding.engine.systemchannels.PlatformChannel; import io.flutter.plugin.common.MethodChannel; @@ -50,11 +54,39 @@ import io.flutter.plugins.camera.features.zoomlevel.ZoomLevelFeature; import io.flutter.plugins.camera.utils.TestUtils; import io.flutter.view.TextureRegistry; +import java.util.ArrayList; +import java.util.List; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.MockedStatic; +class FakeCameraDeviceWrapper implements CameraDeviceWrapper { + final List captureRequests; + + FakeCameraDeviceWrapper(List captureRequests) { + this.captureRequests = captureRequests; + } + + @NonNull + @Override + public CaptureRequest.Builder createCaptureRequest(int var1) { + return captureRequests.remove(0); + } + + @Override + public void createCaptureSession(SessionConfiguration config) {} + + @Override + public void createCaptureSession( + @NonNull List outputs, + @NonNull CameraCaptureSession.StateCallback callback, + @Nullable Handler handler) {} + + @Override + public void close() {} +} + public class CameraTest { private CameraProperties mockCameraProperties; private CameraFeatureFactory mockCameraFeatureFactory; @@ -801,6 +833,29 @@ public void startBackgroundThread_shouldNotStartNewThreadWhenAlreadyCreated() { verify(mockHandlerThread, times(1)).start(); } + @Test + public void onConverge_shouldTakePictureWithoutAbortingSession() throws CameraAccessException { + ArrayList mockRequestBuilders = new ArrayList<>(); + mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); + CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); + // Stub out other features used by the flow. + TestUtils.setPrivateField(camera, "cameraDevice", fakeCamera); + TestUtils.setPrivateField(camera, "pictureImageReader", mock(ImageReader.class)); + SensorOrientationFeature mockSensorOrientationFeature = + mockCameraFeatureFactory.createSensorOrientationFeature(mockCameraProperties, null, null); + DeviceOrientationManager mockDeviceOrientationManager = mock(DeviceOrientationManager.class); + when(mockSensorOrientationFeature.getDeviceOrientationManager()) + .thenReturn(mockDeviceOrientationManager); + + // Simulate a post-precapture flow. + camera.onConverged(); + // A picture should be taken. + verify(mockCaptureSession, times(1)).capture(any(), any(), any()); + // The session shuold not be aborted as part of this flow, as this breaks capture on some + // devices, and causes delays on others. + verify(mockCaptureSession, never()).abortCaptures(); + } + private static class TestCameraFeatureFactory implements CameraFeatureFactory { private final AutoFocusFeature mockAutoFocusFeature; private final ExposureLockFeature mockExposureLockFeature; diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index fcdce02e947f..2baab09c5dcb 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+15 +version: 0.9.4+16 environment: sdk: ">=2.14.0 <3.0.0" From b6dea96eb18db6ae393d077aabf625e632a9a5f9 Mon Sep 17 00:00:00 2001 From: EconomicEgret <101129562+EconomicEgret@users.noreply.github.com> Date: Tue, 8 Mar 2022 17:05:20 -0500 Subject: [PATCH 017/844] [video_player] Add new option for allowBackgroundPlayback (#5013) --- .../video_player/video_player/CHANGELOG.md | 4 + .../video_player/lib/video_player.dart | 12 ++- .../video_player/video_player/pubspec.yaml | 4 +- .../video_player/test/video_player_test.dart | 77 +++++++++++++++++-- 4 files changed, 83 insertions(+), 14 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index ee0accb22c79..a44f0e352e66 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.0 + +* Adds `allowBackgroundPlayback` to `VideoPlayerOptions`. + ## 2.2.19 * Internal code cleanup for stricter analysis options. diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 672ba2efcde3..b77c530b5831 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -295,7 +295,7 @@ class VideoPlayerController extends ValueNotifier { bool _isDisposed = false; Completer? _creatingCompleter; StreamSubscription? _eventSubscription; - late _VideoAppLifeCycleObserver _lifeCycleObserver; + _VideoAppLifeCycleObserver? _lifeCycleObserver; /// The id of a texture that hasn't been initialized. @visibleForTesting @@ -309,8 +309,12 @@ class VideoPlayerController extends ValueNotifier { /// Attempts to open the given [dataSource] and load metadata about the video. Future initialize() async { - _lifeCycleObserver = _VideoAppLifeCycleObserver(this); - _lifeCycleObserver.initialize(); + final bool allowBackgroundPlayback = + videoPlayerOptions?.allowBackgroundPlayback ?? false; + if (!allowBackgroundPlayback) { + _lifeCycleObserver = _VideoAppLifeCycleObserver(this); + } + _lifeCycleObserver?.initialize(); _creatingCompleter = Completer(); late DataSource dataSourceDescription; @@ -423,7 +427,7 @@ class VideoPlayerController extends ValueNotifier { await _eventSubscription?.cancel(); await _videoPlayerPlatform.dispose(_textureId); } - _lifeCycleObserver.dispose(); + _lifeCycleObserver?.dispose(); } _isDisposed = true; super.dispose(); diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 2f6e28919d00..d58de120541b 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/plugins/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.2.19 +version: 2.3.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -25,7 +25,7 @@ dependencies: html: ^0.15.0 video_player_android: ^2.2.17 video_player_avfoundation: ^2.2.17 - video_player_platform_interface: ">=4.2.0 <6.0.0" + video_player_platform_interface: ^5.1.0 video_player_web: ^2.0.0 dev_dependencies: diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index 07afd0f65402..5e31e22c7010 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -98,6 +98,19 @@ class _FakeClosedCaptionFile extends ClosedCaptionFile { } void main() { + void _verifyPlayStateRespondsToLifecycle( + VideoPlayerController controller, { + required bool shouldPlayInBackground, + }) { + expect(controller.value.isPlaying, true); + _ambiguate(WidgetsBinding.instance)! + .handleAppLifecycleStateChanged(AppLifecycleState.paused); + expect(controller.value.isPlaying, shouldPlayInBackground); + _ambiguate(WidgetsBinding.instance)! + .handleAppLifecycleStateChanged(AppLifecycleState.resumed); + expect(controller.value.isPlaying, true); + } + testWidgets('update texture', (WidgetTester tester) async { final FakeController controller = FakeController(); await tester.pumpWidget(VideoPlayer(controller)); @@ -194,6 +207,16 @@ void main() { }); group('initialize', () { + test('started app lifecycle observing', () async { + final VideoPlayerController controller = VideoPlayerController.network( + 'https://127.0.0.1', + ); + await controller.initialize(); + await controller.play(); + _verifyPlayStateRespondsToLifecycle(controller, + shouldPlayInBackground: false); + }); + test('asset', () async { final VideoPlayerController controller = VideoPlayerController.asset( 'a.avi', @@ -900,6 +923,46 @@ void main() { }); }); + group('VideoPlayerOptions', () { + test('setMixWithOthers', () async { + final VideoPlayerController controller = VideoPlayerController.file( + File(''), + videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true)); + await controller.initialize(); + expect(controller.videoPlayerOptions!.mixWithOthers, true); + }); + + test('true allowBackgroundPlayback continues playback', () async { + final VideoPlayerController controller = VideoPlayerController.file( + File(''), + videoPlayerOptions: VideoPlayerOptions( + allowBackgroundPlayback: true, + ), + ); + await controller.initialize(); + await controller.play(); + _verifyPlayStateRespondsToLifecycle( + controller, + shouldPlayInBackground: true, + ); + }); + + test('false allowBackgroundPlayback pauses playback', () async { + final VideoPlayerController controller = VideoPlayerController.file( + File(''), + videoPlayerOptions: VideoPlayerOptions( + allowBackgroundPlayback: false, + ), + ); + await controller.initialize(); + await controller.play(); + _verifyPlayStateRespondsToLifecycle( + controller, + shouldPlayInBackground: false, + ); + }); + }); + test('VideoProgressColors', () { const Color playedColor = Color.fromRGBO(0, 0, 255, 0.75); const Color bufferedColor = Color.fromRGBO(0, 255, 0, 0.5); @@ -914,14 +977,6 @@ void main() { expect(colors.bufferedColor, bufferedColor); expect(colors.backgroundColor, backgroundColor); }); - - test('setMixWithOthers', () async { - final VideoPlayerController controller = VideoPlayerController.file( - File(''), - videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true)); - await controller.initialize(); - expect(controller.videoPlayerOptions!.mixWithOthers, true); - }); } class FakeVideoPlayerPlatform extends VideoPlayerPlatform { @@ -1010,3 +1065,9 @@ class FakeVideoPlayerPlatform extends VideoPlayerPlatform { calls.add('setMixWithOthers'); } } + +/// This allows a value of type T or T? to be treated as a value of type T?. +/// +/// 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; From 19634a20b70591a8f7b9864e886296543ed54dc7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 8 Mar 2022 17:45:15 -0500 Subject: [PATCH 018/844] Roll Flutter from 10805ca3fe6b to 06fb863ae5a4 (13 revisions) (#5018) --- .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 ec4b42230e12..9611fc14409b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -10805ca3fe6bf22bba2454a8c2d26e1bbe2cd2c3 +06fb863ae5a4be5e0b9b3ad604ce79d6619a58be From 71ee4a6ad1fda6d95c8c07d07acd900b143795a3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 8 Mar 2022 18:50:25 -0500 Subject: [PATCH 019/844] Roll Flutter from 06fb863ae5a4 to bb1b2fd07277 (1 revision) (#5019) --- .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 9611fc14409b..cffb9e8fa21b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -06fb863ae5a4be5e0b9b3ad604ce79d6619a58be +bb1b2fd07277a596e9e3d7a63ffa8e475e8d5ece From e8739ac83136ebe982aa2510cf8f0cb9cbd0c681 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 8 Mar 2022 19:55:23 -0500 Subject: [PATCH 020/844] Roll Flutter from bb1b2fd07277 to 9240c9a02a87 (3 revisions) (#5020) --- .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 cffb9e8fa21b..73c99045dccb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -bb1b2fd07277a596e9e3d7a63ffa8e475e8d5ece +9240c9a02a87af35361c7accaa82b09fa31e539c From c7feb7d4e2fe61c3de3d5cacea95aa523a2440aa Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 8 Mar 2022 22:35:23 -0500 Subject: [PATCH 021/844] Roll Flutter from 9240c9a02a87 to c8538873c80e (1 revision) (#5021) --- .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 73c99045dccb..de430e2ba568 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -9240c9a02a87af35361c7accaa82b09fa31e539c +c8538873c80e34231d7a93f9ca1e5ec22c8804b1 From eb6ad34c32b71dd7c2ba03fd6d1f8b4ef9b4124c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 8 Mar 2022 23:40:17 -0500 Subject: [PATCH 022/844] Roll Flutter from c8538873c80e to 3172065fbab0 (3 revisions) (#5022) --- .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 de430e2ba568..c029efbdcf05 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c8538873c80e34231d7a93f9ca1e5ec22c8804b1 +3172065fbab0cbd180187396d4033dd8432fe575 From 6852d0a311a8c501b1718ca54b382b97c4db1a22 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 9 Mar 2022 11:35:23 -0500 Subject: [PATCH 023/844] [ci] Run analysis with older versions of Flutter (#5000) --- .cirrus.yml | 24 +++++-- packages/camera/camera_web/CHANGELOG.md | 4 ++ packages/camera/camera_web/pubspec.yaml | 2 +- .../google_sign_in_web/CHANGELOG.md | 4 ++ .../google_sign_in_web/example/pubspec.yaml | 2 +- .../google_sign_in_web/pubspec.yaml | 2 +- .../quick_actions/quick_actions/CHANGELOG.md | 4 ++ .../video_player/video_player/CHANGELOG.md | 4 ++ .../video_player/example/pubspec.yaml | 2 +- .../video_player/video_player/pubspec.yaml | 2 +- script/tool/CHANGELOG.md | 3 + script/tool/lib/src/analyze_command.dart | 49 ++++--------- .../src/common/package_looping_command.dart | 41 ++++++++++- .../tool/lib/src/common/plugin_command.dart | 3 + script/tool/test/analyze_command_test.dart | 8 +-- .../common/package_looping_command_test.dart | 28 ++++++++ .../tool/test/common/plugin_command_test.dart | 31 +++++++- script/tool/test/util.dart | 70 ++++++++++++++----- 18 files changed, 213 insertions(+), 70 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index f9454cb9f315..8f6a6f7f6ebe 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -28,10 +28,10 @@ flutter_upgrade_template: &FLUTTER_UPGRADE_TEMPLATE - git fetch origin # Switch to the requested channel. - git checkout $TARGET_TREEISH - # When using a branch rather than a hash, reset to the upstream branch - # rather than using pull, since the base image can sometimes be in a state - # where it has diverged from upstream (!). - - if [[ "$TARGET_TREEISH" == "$CHANNEL" ]]; then + # When using a branch rather than a hash or version tag, reset to the + # upstream branch rather than using pull, since the base image can sometimes + # be in a state where it has diverged from upstream (!). + - if [[ "$TARGET_TREEISH" == "$CHANNEL" ]] && [[ "$CHANNEL" != *"."* ]]; then - git reset --hard @{u} - fi # Run doctor to allow auditing of what version of Flutter the run is using. @@ -138,6 +138,22 @@ task: # Restore the tree to a clean state, to avoid accidental issues if # other script steps are added to this task. - git checkout . + # Does a sanity check that plugins at least pass analysis on the N-1 and N-2 + # versions of Flutter stable if the plugin claims to support that version. + # This is to minimize accidentally making changes that break old versions + # (which we don't commit to supporting, but don't want to actively break) + # without updating the constraints. + # Note: The versions below should be manually updated after a new stable + # version comes out. + - name: legacy-version-analyze + depends_on: analyze + env: + matrix: + CHANNEL: "2.5.3" + CHANNEL: "2.8.1" + analyze_script: + - ./script/tool_runner.sh analyze --skip-if-not-supporting-flutter-version="$CHANNEL" --custom-analysis=script/configs/custom_analysis.yaml + - echo "If this test fails, the minumum Flutter version should be updated" ### Web tasks ### - name: web-build_all_plugins env: diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index c72e31fc0f18..9e486667c103 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version for changes in 0.2.1+3. + ## 0.2.1+3 * Internal code cleanup for stricter analysis options. diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 6d6f110157bc..cb6aa19c49f1 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.2.1+3 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: 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 c1a1606d3a2f..e35caf75ea1f 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 @@ +## NEXT + +* Updates minimum Flutter version to 2.8. + ## 0.10.0+5 * Internal code cleanup for stricter analysis options. 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 5a51cd567275..1bdb2f09c465 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 @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.2.0" + flutter: ">=2.8.0" dependencies: flutter: 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 3bc05d14e34e..d97a7c4da6f1 100644 --- a/packages/google_sign_in/google_sign_in_web/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/pubspec.yaml @@ -7,7 +7,7 @@ version: 0.10.0+5 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index fe596087b990..e95d56c53e9d 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.8. + ## 0.6.0+10 * Moves Android and iOS implementations to federated packages. diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index a44f0e352e66..5d56b1585a80 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.3.0 * Adds `allowBackgroundPlayback` to `VideoPlayerOptions`. diff --git a/packages/video_player/video_player/example/pubspec.yaml b/packages/video_player/video_player/example/pubspec.yaml index 6032f3ceed65..e6b51f99e115 100644 --- a/packages/video_player/video_player/example/pubspec.yaml +++ b/packages/video_player/video_player/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.12.13+hotfix.5" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index d58de120541b..88e45c5be057 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -7,7 +7,7 @@ version: 2.3.0 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 8f2807f0dd03..8e5ae210ce0d 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -19,6 +19,9 @@ `flutter` behavior. - Validates `default_package` entries in plugins. - Removes `allow-warnings` from the `podspecs` command. +- Adds `skip-if-not-supporting-flutter-version` to allow running tests using a + version of Flutter that not all packages support. (E.g., to allow for running + some tests against old versions of Flutter to help avoid accidental breakage.) ## 0.7.3 diff --git a/script/tool/lib/src/analyze_command.dart b/script/tool/lib/src/analyze_command.dart index faad7f4736eb..d8b17bfd5626 100644 --- a/script/tool/lib/src/analyze_command.dart +++ b/script/tool/lib/src/analyze_command.dart @@ -10,12 +10,9 @@ import 'package:yaml/yaml.dart'; import 'common/core.dart'; import 'common/package_looping_command.dart'; -import 'common/plugin_command.dart'; import 'common/process_runner.dart'; import 'common/repository_package.dart'; -const int _exitPackagesGetFailed = 3; - /// A command to run Dart analysis on packages. class AnalyzeCommand extends PackageLoopingCommand { /// Creates a analysis command instance. @@ -84,41 +81,8 @@ class AnalyzeCommand extends PackageLoopingCommand { return false; } - /// Ensures that the dependent packages have been fetched for all packages - /// (including their sub-packages) that will be analyzed. - Future _runPackagesGetOnTargetPackages() async { - final List packageDirectories = - await getTargetPackagesAndSubpackages() - .map((PackageEnumerationEntry entry) => entry.package.directory) - .toList(); - final Set packagePaths = - packageDirectories.map((Directory dir) => dir.path).toSet(); - packageDirectories.removeWhere((Directory directory) { - // Remove the 'example' subdirectories; 'flutter packages get' - // automatically runs 'pub get' there as part of handling the parent - // directory. - return directory.basename == 'example' && - packagePaths.contains(directory.parent.path); - }); - for (final Directory package in packageDirectories) { - final int exitCode = await processRunner.runAndStream( - flutterCommand, ['packages', 'get'], - workingDir: package); - if (exitCode != 0) { - return false; - } - } - return true; - } - @override Future initializeRun() async { - print('Fetching dependencies...'); - if (!await _runPackagesGetOnTargetPackages()) { - printError('Unable to get dependencies.'); - throw ToolExit(_exitPackagesGetFailed); - } - _allowedCustomAnalysisDirectories = getStringListArg(_customAnalysisFlag).expand((String item) { if (item.endsWith('.yaml')) { @@ -138,6 +102,19 @@ class AnalyzeCommand extends PackageLoopingCommand { @override Future runForPackage(RepositoryPackage package) async { + // For non-example packages, fetch dependencies. 'flutter packages get' + // automatically runs 'pub get' in examples as part of handling the parent + // directory, which is guaranteed to come first in the package enumeration. + if (package.directory.basename != 'example' || + !RepositoryPackage(package.directory.parent).pubspecFile.existsSync()) { + final int exitCode = await processRunner.runAndStream( + flutterCommand, ['packages', 'get'], + workingDir: package.directory); + if (exitCode != 0) { + return PackageResult.fail(['Unable to get dependencies']); + } + } + if (_hasUnexpecetdAnalysisOptions(package)) { return PackageResult.fail(['Unexpected local analysis options']); } diff --git a/script/tool/lib/src/common/package_looping_command.dart b/script/tool/lib/src/common/package_looping_command.dart index bfee71a0f4c2..b75aaa4a4a49 100644 --- a/script/tool/lib/src/common/package_looping_command.dart +++ b/script/tool/lib/src/common/package_looping_command.dart @@ -9,6 +9,8 @@ import 'package:file/file.dart'; import 'package:git/git.dart'; import 'package:path/path.dart' as p; import 'package:platform/platform.dart'; +import 'package:pub_semver/pub_semver.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; import 'core.dart'; import 'plugin_command.dart'; @@ -75,7 +77,16 @@ abstract class PackageLoopingCommand extends PluginCommand { Platform platform = const LocalPlatform(), GitDir? gitDir, }) : super(packagesDir, - processRunner: processRunner, platform: platform, gitDir: gitDir); + processRunner: processRunner, platform: platform, gitDir: gitDir) { + argParser.addOption( + _skipByFlutterVersionArg, + help: 'Skip any packages that require a Flutter version newer than ' + 'the provided version.', + ); + } + + static const String _skipByFlutterVersionArg = + 'skip-if-not-supporting-flutter-version'; /// Packages that had at least one [logWarning] call. final Set _packagesWithWarnings = @@ -219,6 +230,11 @@ abstract class PackageLoopingCommand extends PluginCommand { _otherWarningCount = 0; _currentPackageEntry = null; + final String minFlutterVersionArg = getStringArg(_skipByFlutterVersionArg); + final Version? minFlutterVersion = minFlutterVersionArg.isEmpty + ? null + : Version.parse(minFlutterVersionArg); + final DateTime runStart = DateTime.now(); await initializeRun(); @@ -242,7 +258,8 @@ abstract class PackageLoopingCommand extends PluginCommand { PackageResult result; try { - result = await runForPackage(entry.package); + result = await _runForPackageIfSupported(entry.package, + minFlutterVersion: minFlutterVersion); } catch (e, stack) { printError(e.toString()); printError(stack.toString()); @@ -285,6 +302,26 @@ abstract class PackageLoopingCommand extends PluginCommand { return true; } + /// Returns the result of running [runForPackage] if the package is supported + /// by any run constraints, or a skip result if it is not. + Future _runForPackageIfSupported( + RepositoryPackage package, { + Version? minFlutterVersion, + }) async { + if (minFlutterVersion != null) { + final Pubspec pubspec = package.parsePubspec(); + final VersionConstraint? flutterConstraint = + pubspec.environment?['flutter']; + if (flutterConstraint != null && + !flutterConstraint.allows(minFlutterVersion)) { + return PackageResult.skip( + 'Does not support Flutter ${minFlutterVersion.toString()}'); + } + } + + return await runForPackage(package); + } + void _printSuccess(String message) { captureOutput ? print(message) : printSuccess(message); } diff --git a/script/tool/lib/src/common/plugin_command.dart b/script/tool/lib/src/common/plugin_command.dart index 184663568224..fcc87c94ef91 100644 --- a/script/tool/lib/src/common/plugin_command.dart +++ b/script/tool/lib/src/common/plugin_command.dart @@ -409,6 +409,9 @@ abstract class PluginCommand extends Command { /// /// By default, packages excluded via --exclude will not be in the stream, but /// they can be included by passing false for [filterExcluded]. + /// + /// Subpackages are guaranteed to be after the containing package in the + /// stream. Stream getTargetPackagesAndSubpackages( {bool filterExcluded = true}) async* { await for (final PackageEnumerationEntry plugin diff --git a/script/tool/test/analyze_command_test.dart b/script/tool/test/analyze_command_test.dart index 878facd83c06..087e5ada37b0 100644 --- a/script/tool/test/analyze_command_test.dart +++ b/script/tool/test/analyze_command_test.dart @@ -47,10 +47,10 @@ void main() { orderedEquals([ ProcessCall( 'flutter', const ['packages', 'get'], plugin1Dir.path), - ProcessCall( - 'flutter', const ['packages', 'get'], plugin2Dir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], plugin1Dir.path), + ProcessCall( + 'flutter', const ['packages', 'get'], plugin2Dir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], plugin2Dir.path), ])); @@ -82,10 +82,10 @@ void main() { orderedEquals([ ProcessCall( 'flutter', const ['packages', 'get'], plugin1Dir.path), - ProcessCall( - 'flutter', const ['packages', 'get'], plugin2Dir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], plugin1Dir.path), + ProcessCall( + 'flutter', const ['packages', 'get'], plugin2Dir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], plugin2Dir.path), ])); diff --git a/script/tool/test/common/package_looping_command_test.dart b/script/tool/test/common/package_looping_command_test.dart index 6e46a3330cc8..ea02bd4ea387 100644 --- a/script/tool/test/common/package_looping_command_test.dart +++ b/script/tool/test/common/package_looping_command_test.dart @@ -236,6 +236,34 @@ void main() { expect(command.checkedPackages, isNot(contains(excluded.childDirectory('example2').path))); }); + + test('skips unsupported versions when requested', () async { + final Directory excluded = createFakePlugin('a_plugin', packagesDir, + flutterConstraint: '>=2.10.0'); + final Directory included = createFakePackage('a_package', packagesDir); + + final TestPackageLoopingCommand command = + createTestCommand(includeSubpackages: true, hasLongOutput: false); + final List output = await runCommand(command, arguments: [ + '--skip-if-not-supporting-flutter-version=2.5.0' + ]); + + expect( + command.checkedPackages, + unorderedEquals([ + included.path, + included.childDirectory('example').path, + ])); + expect(command.checkedPackages, isNot(contains(excluded.path))); + + expect( + output, + containsAllInOrder([ + '${_startHeadingColor}Running for a_package...$_endColor', + '${_startHeadingColor}Running for a_plugin...$_endColor', + '$_startSkipColor SKIPPING: Does not support Flutter 2.5.0$_endColor', + ])); + }); }); group('output', () { diff --git a/script/tool/test/common/plugin_command_test.dart b/script/tool/test/common/plugin_command_test.dart index 28a03c61d59f..782ea7267ae7 100644 --- a/script/tool/test/common/plugin_command_test.dart +++ b/script/tool/test/common/plugin_command_test.dart @@ -253,6 +253,29 @@ packages/plugin1/plugin1/plugin1.dart unorderedEquals([platformInterfacePackage.path])); }); + test('returns subpackages after the enclosing package', () async { + final SamplePluginCommand localCommand = SamplePluginCommand( + packagesDir, + processRunner: processRunner, + platform: mockPlatform, + gitDir: MockGitDir(), + includeSubpackages: true, + ); + final CommandRunner localRunner = + CommandRunner('common_command', 'subpackage testing'); + localRunner.addCommand(localCommand); + + final Directory package = createFakePackage('apackage', packagesDir); + + await runCapturingPrint(localRunner, ['sample']); + expect( + localCommand.plugins, + containsAllInOrder([ + package.path, + package.childDirectory('example').path, + ])); + }); + group('conflicting package selection', () { test('does not allow --packages with --run-on-changed-packages', () async { @@ -893,11 +916,14 @@ class SamplePluginCommand extends PluginCommand { ProcessRunner processRunner = const ProcessRunner(), Platform platform = const LocalPlatform(), GitDir? gitDir, + this.includeSubpackages = false, }) : super(packagesDir, processRunner: processRunner, platform: platform, gitDir: gitDir); final List plugins = []; + final bool includeSubpackages; + @override final String name = 'sample'; @@ -906,7 +932,10 @@ class SamplePluginCommand extends PluginCommand { @override Future run() async { - await for (final PackageEnumerationEntry entry in getTargetPackages()) { + final Stream packages = includeSubpackages + ? getTargetPackagesAndSubpackages() + : getTargetPackages(); + await for (final PackageEnumerationEntry entry in packages) { plugins.add(entry.package.path); } } diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index 91d21c1d9bb3..6f7d86e054e9 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -85,12 +85,14 @@ Directory createFakePlugin( Map platformSupport = const {}, String? version = '0.0.1', + String flutterConstraint = '>=2.5.0', }) { final Directory pluginDirectory = createFakePackage(name, parentDirectory, isFlutter: true, examples: examples, extraFiles: extraFiles, - version: version); + version: version, + flutterConstraint: flutterConstraint); createFakePubspec( pluginDirectory, @@ -99,6 +101,7 @@ Directory createFakePlugin( isPlugin: true, platformSupport: platformSupport, version: version, + flutterConstraint: flutterConstraint, ); return pluginDirectory; @@ -116,12 +119,16 @@ Directory createFakePackage( List extraFiles = const [], bool isFlutter = false, String? version = '0.0.1', + String flutterConstraint = '>=2.5.0', }) { final Directory packageDirectory = parentDirectory.childDirectory(name); packageDirectory.createSync(recursive: true); createFakePubspec(packageDirectory, - name: name, isFlutter: isFlutter, version: version); + name: name, + isFlutter: isFlutter, + version: version, + flutterConstraint: flutterConstraint); createFakeCHANGELOG(packageDirectory, ''' ## $version * Some changes. @@ -132,7 +139,10 @@ Directory createFakePackage( final Directory exampleDir = packageDirectory.childDirectory(examples.first) ..createSync(); createFakePubspec(exampleDir, - name: '${name}_example', isFlutter: isFlutter, publishTo: 'none'); + name: '${name}_example', + isFlutter: isFlutter, + publishTo: 'none', + flutterConstraint: flutterConstraint); } else if (examples.isNotEmpty) { final Directory exampleDir = packageDirectory.childDirectory('example') ..createSync(); @@ -140,7 +150,10 @@ Directory createFakePackage( final Directory currentExample = exampleDir.childDirectory(example) ..createSync(); createFakePubspec(currentExample, - name: example, isFlutter: isFlutter, publishTo: 'none'); + name: example, + isFlutter: isFlutter, + publishTo: 'none', + flutterConstraint: flutterConstraint); } } @@ -172,40 +185,61 @@ void createFakePubspec( const {}, String publishTo = 'http://no_pub_server.com', String? version, + String dartConstraint = '>=2.0.0 <3.0.0', + String flutterConstraint = '>=2.5.0', }) { isPlugin |= platformSupport.isNotEmpty; - parent.childFile('pubspec.yaml').createSync(); - String yaml = ''' -name: $name + + String environmentSection = ''' +environment: + sdk: "$dartConstraint" '''; + String dependenciesSection = ''' +dependencies: +'''; + String pluginSection = ''; + + // Add Flutter-specific entries if requested. if (isFlutter) { + environmentSection += ''' + flutter: "$flutterConstraint" +'''; + dependenciesSection += ''' + flutter: + sdk: flutter +'''; + if (isPlugin) { - yaml += ''' + pluginSection += ''' flutter: plugin: platforms: '''; for (final MapEntry platform in platformSupport.entries) { - yaml += _pluginPlatformSection(platform.key, platform.value, name); + pluginSection += + _pluginPlatformSection(platform.key, platform.value, name); } } - yaml += ''' -dependencies: - flutter: - sdk: flutter -'''; } - if (version != null) { - yaml += ''' -version: $version + + String yaml = ''' +name: $name +${(version != null) ? 'version: $version' : ''} + +$environmentSection + +$dependenciesSection + +$pluginSection '''; - } + if (publishTo.isNotEmpty) { yaml += ''' publish_to: $publishTo # Hardcoded safeguard to prevent this from somehow being published by a broken test. '''; } + parent.childFile('pubspec.yaml').createSync(); parent.childFile('pubspec.yaml').writeAsStringSync(yaml); } From 430bb19e5cf2d6060ad1dae0d91a3968f56a58e1 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 9 Mar 2022 12:15:16 -0500 Subject: [PATCH 024/844] Roll Flutter from 3172065fbab0 to bf6fcee17345 (2 revisions) (#5024) --- .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 c029efbdcf05..ce6c88411e98 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3172065fbab0cbd180187396d4033dd8432fe575 +bf6fcee173451b27cace6f1fecf44856315e1888 From 4e75d75374a04a1f3d7d1e3e61d6ce883c5e3ef7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 9 Mar 2022 13:20:23 -0500 Subject: [PATCH 025/844] Roll Flutter from bf6fcee17345 to 9cfaabaed09e (6 revisions) (#5025) --- .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 ce6c88411e98..30773b6f7886 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -bf6fcee173451b27cace6f1fecf44856315e1888 +9cfaabaed09e1c6606c9148b0f716a78146ec4e6 From e4a4da45710e31eac7612a9a027ad57e79b8434d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 9 Mar 2022 16:35:25 -0500 Subject: [PATCH 026/844] Roll Flutter from 9cfaabaed09e to e8af40f06b24 (6 revisions) (#5030) --- .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 30773b6f7886..6bff39fd9372 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -9cfaabaed09e1c6606c9148b0f716a78146ec4e6 +e8af40f06b24a0b4bba2bd6e08679e7fc95b0153 From 197af08482bc74a61c5487ef411807c6788e1626 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 9 Mar 2022 17:39:25 -0800 Subject: [PATCH 027/844] [webview_flutter_wkwebview] Add support for WKNavigationDelegate (#4893) --- .../lib/src/foundation/foundation.dart | 24 +++ .../lib/src/web_kit/web_kit.dart | 121 +++++++++++++- .../lib/src/web_kit_webview_widget.dart | 92 +++++++++++ .../webview_flutter_wkwebview/pubspec.yaml | 2 +- .../test/src/web_kit_webview_widget_test.dart | 151 ++++++++++++++++++ .../web_kit_webview_widget_test.mocks.dart | 74 ++++++++- 6 files changed, 458 insertions(+), 6 deletions(-) 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 9615fc5a9a51..dc933a072b7f 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 @@ -33,3 +33,27 @@ class NSUrlRequest { /// All of the HTTP header fields for a request. final Map allHttpHeaderFields; } + +/// Information about an error condition. +/// +/// Wraps [NSError](https://developer.apple.com/documentation/foundation/nserror?language=objc). +@immutable +class NSError { + /// Constructs an [NSError]. + const NSError({ + required this.code, + required this.domain, + required this.localizedDescription, + }); + + /// The error code. + /// + /// Note that errors are domain-specific. + final int code; + + /// A string containing the error domain. + final String domain; + + /// A string containing the localized description of the error. + final String localizedDescription; +} 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 d71509597f3f..85881e0e512e 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 @@ -46,6 +46,53 @@ enum WKAudiovisualMediaType { all, } +/// Indicate whether to allow or cancel navigation to a webpage. +/// +/// Wraps [WKNavigationActionPolicy](https://developer.apple.com/documentation/webkit/wknavigationactionpolicy?language=objc). +enum WKNavigationActionPolicy { + /// Allow navigation to continue. + /// + /// See https://developer.apple.com/documentation/webkit/wknavigationactionpolicy/wknavigationactionpolicyallow?language=objc. + allow, + + /// Cancel navigation. + /// + /// See https://developer.apple.com/documentation/webkit/wknavigationactionpolicy/wknavigationactionpolicycancel?language=objc. + cancel, +} + +/// Possible error values that WebKit APIs can return. +/// +/// See https://developer.apple.com/documentation/webkit/wkerrorcode. +class WKErrorCode { + WKErrorCode._(); + + /// Indicates an unknown issue occurred. + /// + /// See https://developer.apple.com/documentation/webkit/wkerrorcode/wkerrorunknown. + static const int unknown = 1; + + /// Indicates the web process that contains the content is no longer running. + /// + /// See https://developer.apple.com/documentation/webkit/wkerrorcode/wkerrorwebcontentprocessterminated. + static const int webContentProcessTerminated = 2; + + /// Indicates the web view was invalidated. + /// + /// See https://developer.apple.com/documentation/webkit/wkerrorcode/wkerrorwebviewinvalidated. + static const int webViewInvalidated = 3; + + /// Indicates a JavaScript exception occurred. + /// + /// See https://developer.apple.com/documentation/webkit/wkerrorcode/wkerrorjavascriptexceptionoccurred. + static const int javaScriptExceptionOccurred = 4; + + /// Indicates the result of JavaScript execution could not be returned. + /// + /// See https://developer.apple.com/documentation/webkit/wkerrorcode/wkerrorjavascriptresulttypeisunsupported. + static const int javaScriptResultTypeIsUnsupported = 5; +} + /// An object that contains information about an action that causes navigation to occur. /// /// Wraps [WKNavigationAction](https://developer.apple.com/documentation/webkit/wknavigationaction?language=objc). @@ -128,7 +175,7 @@ class WKScriptMessageHandler { void Function( WKUserContentController userContentController, WKScriptMessage message, - ) + )? didReceiveScriptMessage, ) { throw UnimplementedError(); @@ -247,13 +294,71 @@ class WKUIDelegate { void Function( WKWebViewConfiguration configuration, WKNavigationAction navigationAction, - ) + )? onCreateeWebView, ) { throw UnimplementedError(); } } +/// Methods for handling navigation changes and tracking navigation requests. +/// +/// Set the methods of the [WKNavigationDelegate] in the object you use to +/// coordinate changes in your web view’s main frame. +/// +/// Wraps [WKNavigationDelegate](https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc). +class WKNavigationDelegate { + /// Called when navigation from the main frame has started. + set didStartProvisionalNavigation( + void Function( + WKWebView webView, + String? url, + )? + didStartProvisionalNavigation, + ) { + throw UnimplementedError(); + } + + /// Called when navigation is complete. + set didFinishNavigation( + void Function(WKWebView webView, String? url)? didFinishNavigation, + ) { + throw UnimplementedError(); + } + + /// Called when permission is needed to navigate to new content. + set decidePolicyForNavigationAction( + Future Function( + WKWebView webView, + WKNavigationAction navigationAction, + )? + decidePolicyForNavigationAction) { + throw UnimplementedError(); + } + + /// Called when an error occurred during navigation. + set didFailNavigation( + void Function(WKWebView webView, NSError error)? didFailNavigation, + ) { + throw UnimplementedError(); + } + + /// Called when an error occurred during the early navigation process. + set didFailProvisionalNavigation( + void Function(WKWebView webView, NSError error)? + didFailProvisionalNavigation, + ) { + throw UnimplementedError(); + } + + /// Called when the web view’s content process was terminated. + set webViewWebContentProcessDidTerminate( + void Function(WKWebView webView)? webViewWebContentProcessDidTerminate, + ) { + throw UnimplementedError(); + } +} + /// Object that displays interactive web content, such as for an in-app browser. /// /// Wraps [WKWebView](https://developer.apple.com/documentation/webkit/wkwebview?language=objc). @@ -286,7 +391,17 @@ class WKWebView { WKWebViewConfiguration._fromWebView(this); /// Used to integrate custom user interface elements into web view interactions. - set uiDelegate(WKUIDelegate delegate) { + set uiDelegate(WKUIDelegate? delegate) { + throw UnimplementedError(); + } + + /// The object you use to manage navigation behavior for the web view. + set navigationDelegate(WKNavigationDelegate? delegate) { + throw UnimplementedError(); + } + + /// The URL for the current webpage. + Future get url { throw UnimplementedError(); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index d44dd7bcc148..89a258e75767 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'foundation/foundation.dart'; import 'web_kit/web_kit.dart'; /// A [Widget] that displays a [WKWebView]. @@ -120,6 +121,32 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { @visibleForTesting late final WKUIDelegate uiDelegate = webViewProxy.createUIDelgate(); + /// Methods for handling navigation changes and tracking navigation requests. + @visibleForTesting + late final WKNavigationDelegate navigationDelegate = + webViewProxy.createNavigationDelegate() + ..didStartProvisionalNavigation = (WKWebView webView, String? url) { + callbacksHandler.onPageStarted(url ?? ''); + } + ..didFinishNavigation = (WKWebView webView, String? url) { + callbacksHandler.onPageFinished(url ?? ''); + } + ..didFailNavigation = (WKWebView webView, NSError error) { + callbacksHandler.onWebResourceError(_toWebResourceError(error)); + } + ..didFailProvisionalNavigation = (WKWebView webView, NSError error) { + callbacksHandler.onWebResourceError(_toWebResourceError(error)); + } + ..webViewWebContentProcessDidTerminate = (WKWebView webView) { + callbacksHandler.onWebResourceError(WebResourceError( + errorCode: WKErrorCode.webContentProcessTerminated, + // Value from https://developer.apple.com/documentation/webkit/wkerrordomain?language=objc. + domain: 'WKErrorDomain', + description: '', + errorType: WebResourceErrorType.webContentProcessTerminated, + )); + }; + Future _setCreationParams( CreationParams params, { required WKWebViewConfiguration configuration, @@ -133,6 +160,12 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { webView = webViewProxy.createWebView(configuration); await addJavascriptChannels(params.javascriptChannelNames); + + webView.navigationDelegate = navigationDelegate; + + if (params.webSettings != null) { + updateSettings(params.webSettings!); + } } void _setWebViewConfiguration( @@ -161,6 +194,13 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { }; } + @override + Future updateSettings(WebSettings setting) async { + if (setting.hasNavigationDelegate != null) { + _setHasNavigationDelegate(setting.hasNavigationDelegate!); + } + } + @override Future addJavascriptChannels(Set javascriptChannelNames) async { await Future.wait( @@ -224,6 +264,53 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { await addJavascriptChannels(remainingNames); } + + void _setHasNavigationDelegate(bool hasNavigationDelegate) { + if (hasNavigationDelegate) { + navigationDelegate.decidePolicyForNavigationAction = + (WKWebView webView, WKNavigationAction action) async { + final bool allow = await callbacksHandler.onNavigationRequest( + url: action.request.url, + isForMainFrame: action.targetFrame.isMainFrame, + ); + + return allow + ? WKNavigationActionPolicy.allow + : WKNavigationActionPolicy.cancel; + }; + } else { + navigationDelegate.decidePolicyForNavigationAction = null; + } + } + + static WebResourceError _toWebResourceError(NSError error) { + WebResourceErrorType? errorType; + + switch (error.code) { + case WKErrorCode.unknown: + errorType = WebResourceErrorType.unknown; + break; + case WKErrorCode.webContentProcessTerminated: + errorType = WebResourceErrorType.webContentProcessTerminated; + break; + case WKErrorCode.webViewInvalidated: + errorType = WebResourceErrorType.webViewInvalidated; + break; + case WKErrorCode.javaScriptExceptionOccurred: + errorType = WebResourceErrorType.javaScriptExceptionOccurred; + break; + case WKErrorCode.javaScriptResultTypeIsUnsupported: + errorType = WebResourceErrorType.javaScriptResultTypeIsUnsupported; + break; + } + + return WebResourceError( + errorCode: error.code, + domain: error.domain, + description: error.localizedDescription, + errorType: errorType, + ); + } } /// Handles constructing objects and calling static methods. @@ -248,4 +335,9 @@ class WebViewWidgetProxy { WKUIDelegate createUIDelgate() { return WKUIDelegate(); } + + /// Constructs a [WKNavigationDelegate]. + WKNavigationDelegate createNavigationDelegate() { + return WKNavigationDelegate(); + } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 26d14ea9d468..91760df02c66 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -26,5 +26,5 @@ dev_dependencies: sdk: flutter flutter_test: sdk: flutter - mockito: ^5.0.16 + mockito: ^5.1.0 pedantic: ^1.10.0 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 30ba3ee9cb8c..6642b4561cfc 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -16,6 +16,7 @@ import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart'; import 'web_kit_webview_widget_test.mocks.dart'; @GenerateMocks([ + WKNavigationDelegate, WKScriptMessageHandler, WKWebView, WKWebViewConfiguration, @@ -34,6 +35,7 @@ void main() { late MockWKUserContentController mockUserContentController; late MockWKWebViewConfiguration mockWebViewConfiguration; late MockWKUIDelegate mockUIDelegate; + late MockWKNavigationDelegate mockNavigationDelegate; late MockWebViewPlatformCallbacksHandler mockCallbacksHandler; late MockJavascriptChannelRegistry mockJavascriptChannelRegistry; @@ -45,10 +47,13 @@ void main() { mockWebViewConfiguration = MockWKWebViewConfiguration(); mockUserContentController = MockWKUserContentController(); mockUIDelegate = MockWKUIDelegate(); + mockNavigationDelegate = MockWKNavigationDelegate(); mockWebViewWidgetProxy = MockWebViewWidgetProxy(); when(mockWebViewWidgetProxy.createWebView(any)).thenReturn(mockWebView); when(mockWebViewWidgetProxy.createUIDelgate()).thenReturn(mockUIDelegate); + when(mockWebViewWidgetProxy.createNavigationDelegate()) + .thenReturn(mockNavigationDelegate); when(mockWebView.configuration).thenReturn(mockWebViewConfiguration); when(mockWebViewConfiguration.userContentController).thenReturn( mockUserContentController, @@ -280,6 +285,152 @@ void main() { }); }); + group('$WebViewPlatformCallbacksHandler', () { + testWidgets('onPageStarted', (WidgetTester tester) async { + await buildWidget(tester); + + final dynamic didStartProvisionalNavigation = verify( + mockNavigationDelegate.didStartProvisionalNavigation = + captureAny) + .captured + .single as void Function(WKWebView, String); + didStartProvisionalNavigation(mockWebView, 'https://google.com'); + + verify(mockCallbacksHandler.onPageStarted('https://google.com')); + }); + + testWidgets('onPageFinished', (WidgetTester tester) async { + await buildWidget(tester); + + final dynamic didFinishNavigation = + verify(mockNavigationDelegate.didFinishNavigation = captureAny) + .captured + .single as void Function(WKWebView, String); + didFinishNavigation(mockWebView, 'https://google.com'); + + verify(mockCallbacksHandler.onPageFinished('https://google.com')); + }); + + testWidgets('onWebResourceError from didFailNavigation', + (WidgetTester tester) async { + await buildWidget(tester); + + final dynamic didFailNavigation = + verify(mockNavigationDelegate.didFailNavigation = captureAny) + .captured + .single as void Function(WKWebView, NSError); + + didFailNavigation( + mockWebView, + const NSError( + code: WKErrorCode.webViewInvalidated, + domain: 'domain', + localizedDescription: 'my desc', + ), + ); + + final WebResourceError error = + verify(mockCallbacksHandler.onWebResourceError(captureAny)) + .captured + .single as WebResourceError; + expect(error.description, 'my desc'); + expect(error.errorCode, WKErrorCode.webViewInvalidated); + expect(error.domain, 'domain'); + expect(error.errorType, WebResourceErrorType.webViewInvalidated); + }); + + testWidgets('onWebResourceError from didFailProvisionalNavigation', + (WidgetTester tester) async { + await buildWidget(tester); + + final dynamic didFailProvisionalNavigation = verify( + mockNavigationDelegate.didFailProvisionalNavigation = + captureAny) + .captured + .single as void Function(WKWebView, NSError); + + didFailProvisionalNavigation( + mockWebView, + const NSError( + code: WKErrorCode.webContentProcessTerminated, + domain: 'domain', + localizedDescription: 'my desc', + ), + ); + + final WebResourceError error = + verify(mockCallbacksHandler.onWebResourceError(captureAny)) + .captured + .single as WebResourceError; + expect(error.description, 'my desc'); + expect(error.errorCode, WKErrorCode.webContentProcessTerminated); + expect(error.domain, 'domain'); + expect( + error.errorType, + WebResourceErrorType.webContentProcessTerminated, + ); + }); + + testWidgets( + 'onWebResourceError from webViewWebContentProcessDidTerminate', + (WidgetTester tester) async { + await buildWidget(tester); + + final dynamic webViewWebContentProcessDidTerminate = verify( + mockNavigationDelegate.webViewWebContentProcessDidTerminate = + captureAny) + .captured + .single as void Function(WKWebView); + webViewWebContentProcessDidTerminate(mockWebView); + + final WebResourceError error = + verify(mockCallbacksHandler.onWebResourceError(captureAny)) + .captured + .single as WebResourceError; + expect(error.description, ''); + expect(error.errorCode, WKErrorCode.webContentProcessTerminated); + expect(error.domain, 'WKErrorDomain'); + expect( + error.errorType, + WebResourceErrorType.webContentProcessTerminated, + ); + }); + + testWidgets('onNavigationRequest from decidePolicyForNavigationAction', + (WidgetTester tester) async { + await buildWidget(tester, hasNavigationDelegate: true); + + final dynamic decidePolicyForNavigationAction = verify( + mockNavigationDelegate.decidePolicyForNavigationAction = + captureAny) + .captured + .single + as Future Function( + WKWebView, WKNavigationAction); + + when(mockCallbacksHandler.onNavigationRequest( + isForMainFrame: argThat(isFalse, named: 'isForMainFrame'), + url: 'https://google.com', + )).thenReturn(true); + + expect( + decidePolicyForNavigationAction( + mockWebView, + const WKNavigationAction( + request: NSUrlRequest(url: 'https://google.com'), + targetFrame: WKFrameInfo(isMainFrame: false), + ), + ), + completion(WKNavigationActionPolicy.allow), + ); + + verify(mockCallbacksHandler.onNavigationRequest( + url: 'https://google.com', + isForMainFrame: false, + )); + }); + }); + group('$JavascriptChannelRegistry', () { testWidgets('onJavascriptChannelMessage', (WidgetTester tester) async { when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn( diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index 0138995fd148..9579b23c84a0 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -1,5 +1,5 @@ -// Mocks generated by Mockito 5.0.17 from annotations -// in webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. +// Mocks generated by Mockito 5.1.0 from annotations +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. // Do not manually edit this file. import 'dart:async' as _i3; @@ -16,6 +16,7 @@ import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i2; import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart' as _i8; +// ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references @@ -38,6 +39,64 @@ class _FakeWKScriptMessageHandler_3 extends _i1.Fake class _FakeWKUIDelegate_4 extends _i1.Fake implements _i2.WKUIDelegate {} +class _FakeWKNavigationDelegate_5 extends _i1.Fake + implements _i2.WKNavigationDelegate {} + +/// A class which mocks [WKNavigationDelegate]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWKNavigationDelegate extends _i1.Mock + implements _i2.WKNavigationDelegate { + MockWKNavigationDelegate() { + _i1.throwOnMissingStub(this); + } + + @override + set didStartProvisionalNavigation( + void Function(_i2.WKWebView, String?)? + didStartProvisionalNavigation) => + super.noSuchMethod( + Invocation.setter( + #didStartProvisionalNavigation, didStartProvisionalNavigation), + returnValueForMissingStub: null); + @override + set didFinishNavigation( + void Function(_i2.WKWebView, String?)? didFinishNavigation) => + super.noSuchMethod( + Invocation.setter(#didFinishNavigation, didFinishNavigation), + returnValueForMissingStub: null); + @override + set decidePolicyForNavigationAction( + _i3.Future<_i2.WKNavigationActionPolicy> Function( + _i2.WKWebView, _i2.WKNavigationAction)? + decidePolicyForNavigationAction) => + super.noSuchMethod( + Invocation.setter(#decidePolicyForNavigationAction, + decidePolicyForNavigationAction), + returnValueForMissingStub: null); + @override + set didFailNavigation( + void Function(_i2.WKWebView, _i4.NSError)? didFailNavigation) => + super.noSuchMethod( + Invocation.setter(#didFailNavigation, didFailNavigation), + returnValueForMissingStub: null); + @override + set didFailProvisionalNavigation( + void Function(_i2.WKWebView, _i4.NSError)? + didFailProvisionalNavigation) => + super.noSuchMethod( + Invocation.setter( + #didFailProvisionalNavigation, didFailProvisionalNavigation), + returnValueForMissingStub: null); + @override + set webViewWebContentProcessDidTerminate( + void Function(_i2.WKWebView)? webViewWebContentProcessDidTerminate) => + super.noSuchMethod( + Invocation.setter(#webViewWebContentProcessDidTerminate, + webViewWebContentProcessDidTerminate), + returnValueForMissingStub: null); +} + /// A class which mocks [WKScriptMessageHandler]. /// /// See the documentation for Mockito's code generation for more information. @@ -74,6 +133,13 @@ class MockWKWebView extends _i1.Mock implements _i2.WKWebView { super.noSuchMethod(Invocation.setter(#uiDelegate, delegate), returnValueForMissingStub: null); @override + set navigationDelegate(_i2.WKNavigationDelegate? delegate) => + super.noSuchMethod(Invocation.setter(#navigationDelegate, delegate), + returnValueForMissingStub: null); + @override + _i3.Future get url => (super.noSuchMethod(Invocation.getter(#url), + returnValue: Future.value()) as _i3.Future); + @override _i3.Future loadRequest(_i4.NSUrlRequest? request) => (super.noSuchMethod(Invocation.method(#loadRequest, [request]), returnValue: Future.value(), @@ -247,4 +313,8 @@ class MockWebViewWidgetProxy extends _i1.Mock _i2.WKUIDelegate createUIDelgate() => (super.noSuchMethod(Invocation.method(#createUIDelgate, []), returnValue: _FakeWKUIDelegate_4()) as _i2.WKUIDelegate); + @override + _i2.WKNavigationDelegate createNavigationDelegate() => (super.noSuchMethod( + Invocation.method(#createNavigationDelegate, []), + returnValue: _FakeWKNavigationDelegate_5()) as _i2.WKNavigationDelegate); } From 2fefbd7885c93ca6e2f062de61e58aa6c5b749f6 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 9 Mar 2022 18:20:15 -0800 Subject: [PATCH 028/844] [webview_flutter_android] Change relative path to uri in generated pigeon test file (#5029) --- .../webview_flutter_android/CHANGELOG.md | 4 +++ .../webview_flutter_android/README.md | 15 ++++++++++ .../webview_flutter_android/pubspec.yaml | 2 +- .../test/android_webview.pigeon.dart | 2 +- .../test/android_webview_test.mocks.dart | 29 ++----------------- ...iew_android_cookie_manager_test.mocks.dart | 5 ++-- .../webview_android_widget_test.mocks.dart | 23 ++------------- 7 files changed, 27 insertions(+), 53 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 26fb5ab624f8..12d20b00f534 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes bug preventing `mockito` code generation for tests. + ## 2.8.3 * Fixes a bug causing `debuggingEnabled` to always be set to true. diff --git a/packages/webview_flutter/webview_flutter_android/README.md b/packages/webview_flutter/webview_flutter_android/README.md index 492ef913bbc7..020f1fcfbbf1 100644 --- a/packages/webview_flutter/webview_flutter_android/README.md +++ b/packages/webview_flutter/webview_flutter_android/README.md @@ -11,6 +11,21 @@ normally. This package will be automatically included in your app when you do. This package uses [pigeon][3] to generate the communication layer between Flutter and the host platform (Android). The communication interface is defined in the `pigeons/android_webview.dart` file. After editing the communication interface regenerate the communication layer by running the `./generatePigeons.sh` shell script. +Due to [flutter/flutter#97744](https://github.com/flutter/flutter/issues/97744), the generated test +pigeon file needs one of its imports updated to properly work with `mockito`. + +In `test/android_webview.pigeon.dart`, change + +```dart +import '../lib/src/android_webview.pigeon.dart'; +``` + +to + +```dart +import 'package:webview_flutter_android/src/android_webview.pigeon.dart'; +``` + Besides [pigeon][3] this package also uses [mockito][4] to generate mock objects for testing purposes. To generate the mock objects run the following command: ```bash flutter packages pub run build_runner build --delete-conflicting-outputs diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 704dc6719a7e..35f0e2f2a5a0 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -27,6 +27,6 @@ dev_dependencies: sdk: flutter flutter_test: sdk: flutter - mockito: ^5.0.16 + mockito: ^5.1.0 pedantic: ^1.10.0 pigeon: 1.0.9 diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart index 720fe408d96c..746126346750 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart @@ -12,7 +12,7 @@ import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../lib/src/android_webview.pigeon.dart'; +import 'package:webview_flutter_android/src/android_webview.pigeon.dart'; class _TestWebViewHostApiCodec extends StandardMessageCodec { const _TestWebViewHostApiCodec(); 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 2134de54a415..d6023d7de3aa 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 @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.0.16 from annotations +// Mocks generated by Mockito 5.1.0 from annotations // in webview_flutter_android/test/android_webview_test.dart. // Do not manually edit this file. @@ -12,6 +12,7 @@ import 'package:webview_flutter_android/src/android_webview.pigeon.dart' as _i3; import 'android_webview.pigeon.dart' as _i5; +// ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references @@ -41,8 +42,6 @@ class MockCookieManagerHostApi extends _i1.Mock (super.noSuchMethod(Invocation.method(#setCookie, [arg_url, arg_value]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); - @override - String toString() => super.toString(); } /// A class which mocks [DownloadListener]. @@ -60,8 +59,6 @@ class MockDownloadListener extends _i1.Mock implements _i2.DownloadListener { Invocation.method(#onDownloadStart, [url, userAgent, contentDisposition, mimetype, contentLength]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [JavaScriptChannel]. @@ -80,8 +77,6 @@ class MockJavaScriptChannel extends _i1.Mock implements _i2.JavaScriptChannel { void postMessage(String? message) => super.noSuchMethod(Invocation.method(#postMessage, [message]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [TestDownloadListenerHostApi]. @@ -97,8 +92,6 @@ class MockTestDownloadListenerHostApi extends _i1.Mock void create(int? instanceId) => super.noSuchMethod(Invocation.method(#create, [instanceId]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [TestJavaScriptChannelHostApi]. @@ -114,8 +107,6 @@ class MockTestJavaScriptChannelHostApi extends _i1.Mock void create(int? instanceId, String? channelName) => super.noSuchMethod(Invocation.method(#create, [instanceId, channelName]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [TestWebChromeClientHostApi]. @@ -132,8 +123,6 @@ class MockTestWebChromeClientHostApi extends _i1.Mock super.noSuchMethod( Invocation.method(#create, [instanceId, webViewClientInstanceId]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [TestWebSettingsHostApi]. @@ -210,8 +199,6 @@ class MockTestWebSettingsHostApi extends _i1.Mock void setAllowFileAccess(int? instanceId, bool? enabled) => super.noSuchMethod( Invocation.method(#setAllowFileAccess, [instanceId, enabled]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [TestWebViewClientHostApi]. @@ -228,8 +215,6 @@ class MockTestWebViewClientHostApi extends _i1.Mock super.noSuchMethod( Invocation.method(#create, [instanceId, shouldOverrideUrlLoading]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [TestWebViewHostApi]. @@ -368,8 +353,6 @@ class MockTestWebViewHostApi extends _i1.Mock void setBackgroundColor(int? instanceId, int? color) => super.noSuchMethod( Invocation.method(#setBackgroundColor, [instanceId, color]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [TestAssetManagerHostApi]. @@ -389,8 +372,6 @@ class MockTestAssetManagerHostApi extends _i1.Mock String getAssetFilePathByName(String? name) => (super.noSuchMethod(Invocation.method(#getAssetFilePathByName, [name]), returnValue: '') as String); - @override - String toString() => super.toString(); } /// A class which mocks [WebChromeClient]. @@ -405,8 +386,6 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { void onProgressChanged(_i2.WebView? webView, int? progress) => super .noSuchMethod(Invocation.method(#onProgressChanged, [webView, progress]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [WebView]. @@ -557,8 +536,6 @@ class MockWebView extends _i1.Mock implements _i2.WebView { (super.noSuchMethod(Invocation.method(#release, []), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); - @override - String toString() => super.toString(); } /// A class which mocks [WebViewClient]. @@ -602,6 +579,4 @@ class MockWebViewClient extends _i1.Mock implements _i2.WebViewClient { void urlLoading(_i2.WebView? webView, String? url) => super.noSuchMethod(Invocation.method(#urlLoading, [webView, url]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart index 131977bd3dfd..ff9974b1a10b 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.0.16 from annotations +// Mocks generated by Mockito 5.1.0 from annotations // in webview_flutter_android/test/webview_android_cookie_manager_test.dart. // Do not manually edit this file. @@ -7,6 +7,7 @@ import 'dart:async' as _i3; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_android/src/android_webview.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 @@ -33,6 +34,4 @@ class MockCookieManager extends _i1.Mock implements _i2.CookieManager { _i3.Future clearCookies() => (super.noSuchMethod(Invocation.method(#clearCookies, []), returnValue: Future.value(false)) as _i3.Future); - @override - String toString() => super.toString(); } diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart index 12e993bafa31..f4d9abbd2d3c 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.0.16 from annotations +// Mocks generated by Mockito 5.1.0 from annotations // in webview_flutter_android/test/webview_android_widget_test.dart. // Do not manually edit this file. @@ -12,6 +12,7 @@ import 'package:webview_flutter_android/webview_android_widget.dart' as _i7; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart' as _i3; +// ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references @@ -46,8 +47,6 @@ class MockFlutterAssetManager extends _i1.Mock _i4.Future getAssetFilePathByName(String? name) => (super.noSuchMethod(Invocation.method(#getAssetFilePathByName, [name]), returnValue: Future.value('')) as _i4.Future); - @override - String toString() => super.toString(); } /// A class which mocks [WebSettings]. @@ -120,8 +119,6 @@ class MockWebSettings extends _i1.Mock implements _i2.WebSettings { (super.noSuchMethod(Invocation.method(#setAllowFileAccess, [enabled]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); - @override - String toString() => super.toString(); } /// A class which mocks [WebView]. @@ -272,8 +269,6 @@ class MockWebView extends _i1.Mock implements _i2.WebView { (super.noSuchMethod(Invocation.method(#release, []), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); - @override - String toString() => super.toString(); } /// A class which mocks [WebViewAndroidDownloadListener]. @@ -298,8 +293,6 @@ class MockWebViewAndroidDownloadListener extends _i1.Mock Invocation.method(#onDownloadStart, [url, userAgent, contentDisposition, mimetype, contentLength]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [WebViewAndroidJavaScriptChannel]. @@ -324,8 +317,6 @@ class MockWebViewAndroidJavaScriptChannel extends _i1.Mock void postMessage(String? message) => super.noSuchMethod(Invocation.method(#postMessage, [message]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [WebViewAndroidWebChromeClient]. @@ -341,8 +332,6 @@ class MockWebViewAndroidWebChromeClient extends _i1.Mock void onProgressChanged(_i2.WebView? webView, int? progress) => super .noSuchMethod(Invocation.method(#onProgressChanged, [webView, progress]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [WebViewAndroidWebViewClient]. @@ -411,8 +400,6 @@ class MockWebViewAndroidWebViewClient extends _i1.Mock void requestLoading(_i2.WebView? webView, _i2.WebResourceRequest? request) => super.noSuchMethod(Invocation.method(#requestLoading, [webView, request]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [JavascriptChannelRegistry]. @@ -439,8 +426,6 @@ class MockJavascriptChannelRegistry extends _i1.Mock super.noSuchMethod( Invocation.method(#updateJavascriptChannelsFromSet, [channels]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [WebViewPlatformCallbacksHandler]. @@ -474,8 +459,6 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock void onWebResourceError(_i3.WebResourceError? error) => super.noSuchMethod(Invocation.method(#onWebResourceError, [error]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [WebViewProxy]. @@ -498,6 +481,4 @@ class MockWebViewProxy extends _i1.Mock implements _i7.WebViewProxy { Invocation.method(#setWebContentsDebuggingEnabled, [enabled]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); - @override - String toString() => super.toString(); } From f4aaeeb5f2260e4c7a31b7c67f2690ea0f99b146 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 9 Mar 2022 21:55:15 -0500 Subject: [PATCH 029/844] Roll Flutter from e8af40f06b24 to bb2cca5f59a1 (6 revisions) (#5032) --- .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 6bff39fd9372..5e8e64c6c6b7 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e8af40f06b24a0b4bba2bd6e08679e7fc95b0153 +bb2cca5f59a1f559a5d686e68fb89dfffe7a05e0 From 20e231b0529cbe94e2f372cc53020512873d9c66 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 10 Mar 2022 00:35:22 -0500 Subject: [PATCH 030/844] Roll Flutter from bb2cca5f59a1 to 57aa49028db7 (4 revisions) (#5033) --- .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 5e8e64c6c6b7..5afb34058b93 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -bb2cca5f59a1f559a5d686e68fb89dfffe7a05e0 +57aa49028db77a4edf0fbc6efc70cceea2d2c6ed From 6ff862fa711b0e58040d879b8feb6acc7e933872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rulong=20Chen=EF=BC=88=E9=99=88=E6=B1=9D=E9=BE=99=EF=BC=89?= Date: Fri, 11 Mar 2022 00:00:24 +0800 Subject: [PATCH 031/844] [image_picker] Fixes activity leak (#4439) --- AUTHORS | 1 + .../image_picker/image_picker/CHANGELOG.md | 4 + .../imagepicker/ImagePickerPlugin.java | 148 +++++++++++++----- .../imagepicker/ImagePickerPluginTest.java | 33 +++- .../image_picker/image_picker/pubspec.yaml | 2 +- 5 files changed, 143 insertions(+), 45 deletions(-) diff --git a/AUTHORS b/AUTHORS index f5dc82340bc8..41a31ed39cd4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -66,3 +66,4 @@ Alex Li Rahul Raj <64.rahulraj@gmail.com> Daniel Roek TheOneWithTheBraid +Rulong Chen(陈汝龙) diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 9ab762a3de13..2ba5a2ce4ce7 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.4+11 + +* Fixes Activity leak. + ## 0.8.4+10 * iOS: allows picking images with WebP format. diff --git a/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java b/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java index 577675bd433a..311ef19103ac 100644 --- a/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java +++ b/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java @@ -85,11 +85,98 @@ public void onActivityDestroyed(Activity activity) { @Override public void onActivityStopped(Activity activity) { if (thisActivity == activity) { - delegate.saveStateBeforeResult(); + activityState.getDelegate().saveStateBeforeResult(); } } } + /** + * Move all activity-lifetime-bound states into this helper object, so that {@code setup} and + * {@code tearDown} would just become constructor and finalize calls of the helper object. + */ + private class ActivityState { + private Application application; + private Activity activity; + private ImagePickerDelegate delegate; + private MethodChannel channel; + private LifeCycleObserver observer; + private ActivityPluginBinding activityBinding; + + // This is null when not using v2 embedding; + private Lifecycle lifecycle; + + // Default constructor + ActivityState( + final Application application, + final Activity activity, + final BinaryMessenger messenger, + final MethodChannel.MethodCallHandler handler, + final PluginRegistry.Registrar registrar, + final ActivityPluginBinding activityBinding) { + this.application = application; + this.activity = activity; + this.activityBinding = activityBinding; + + delegate = constructDelegate(activity); + channel = new MethodChannel(messenger, CHANNEL); + channel.setMethodCallHandler(handler); + observer = new LifeCycleObserver(activity); + if (registrar != null) { + // V1 embedding setup for activity listeners. + application.registerActivityLifecycleCallbacks(observer); + registrar.addActivityResultListener(delegate); + registrar.addRequestPermissionsResultListener(delegate); + } else { + // V2 embedding setup for activity listeners. + activityBinding.addActivityResultListener(delegate); + activityBinding.addRequestPermissionsResultListener(delegate); + lifecycle = FlutterLifecycleAdapter.getActivityLifecycle(activityBinding); + lifecycle.addObserver(observer); + } + } + + // Only invoked by {@link #ImagePickerPlugin(ImagePickerDelegate, Activity)} for testing. + ActivityState(final ImagePickerDelegate delegate, final Activity activity) { + this.activity = activity; + this.delegate = delegate; + } + + void release() { + if (activityBinding != null) { + activityBinding.removeActivityResultListener(delegate); + activityBinding.removeRequestPermissionsResultListener(delegate); + activityBinding = null; + } + + if (lifecycle != null) { + lifecycle.removeObserver(observer); + lifecycle = null; + } + + if (channel != null) { + channel.setMethodCallHandler(null); + channel = null; + } + + if (application != null) { + application.unregisterActivityLifecycleCallbacks(observer); + application = null; + } + + activity = null; + observer = null; + delegate = null; + } + + Activity getActivity() { + return activity; + } + + ImagePickerDelegate getDelegate() { + return delegate; + } + } + static final String METHOD_CALL_IMAGE = "pickImage"; static final String METHOD_CALL_MULTI_IMAGE = "pickMultiImage"; static final String METHOD_CALL_VIDEO = "pickVideo"; @@ -101,15 +188,8 @@ public void onActivityStopped(Activity activity) { private static final int SOURCE_CAMERA = 0; private static final int SOURCE_GALLERY = 1; - private MethodChannel channel; - private ImagePickerDelegate delegate; private FlutterPluginBinding pluginBinding; - private ActivityPluginBinding activityBinding; - private Application application; - private Activity activity; - // This is null when not using v2 embedding; - private Lifecycle lifecycle; - private LifeCycleObserver observer; + private ActivityState activityState; @SuppressWarnings("deprecation") public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registrar registrar) { @@ -137,8 +217,12 @@ public ImagePickerPlugin() {} @VisibleForTesting ImagePickerPlugin(final ImagePickerDelegate delegate, final Activity activity) { - this.delegate = delegate; - this.activity = activity; + activityState = new ActivityState(delegate, activity); + } + + @VisibleForTesting + final ActivityState getActivityState() { + return activityState; } @Override @@ -153,13 +237,12 @@ public void onDetachedFromEngine(FlutterPluginBinding binding) { @Override public void onAttachedToActivity(ActivityPluginBinding binding) { - activityBinding = binding; setup( pluginBinding.getBinaryMessenger(), (Application) pluginBinding.getApplicationContext(), - activityBinding.getActivity(), + binding.getActivity(), null, - activityBinding); + binding); } @Override @@ -183,37 +266,15 @@ private void setup( final Activity activity, final PluginRegistry.Registrar registrar, final ActivityPluginBinding activityBinding) { - this.activity = activity; - this.application = application; - this.delegate = constructDelegate(activity); - channel = new MethodChannel(messenger, CHANNEL); - channel.setMethodCallHandler(this); - observer = new LifeCycleObserver(activity); - if (registrar != null) { - // V1 embedding setup for activity listeners. - application.registerActivityLifecycleCallbacks(observer); - registrar.addActivityResultListener(delegate); - registrar.addRequestPermissionsResultListener(delegate); - } else { - // V2 embedding setup for activity listeners. - activityBinding.addActivityResultListener(delegate); - activityBinding.addRequestPermissionsResultListener(delegate); - lifecycle = FlutterLifecycleAdapter.getActivityLifecycle(activityBinding); - lifecycle.addObserver(observer); - } + activityState = + new ActivityState(application, activity, messenger, this, registrar, activityBinding); } private void tearDown() { - activityBinding.removeActivityResultListener(delegate); - activityBinding.removeRequestPermissionsResultListener(delegate); - activityBinding = null; - lifecycle.removeObserver(observer); - lifecycle = null; - delegate = null; - channel.setMethodCallHandler(null); - channel = null; - application.unregisterActivityLifecycleCallbacks(observer); - application = null; + if (activityState != null) { + activityState.release(); + activityState = null; + } } @VisibleForTesting @@ -273,12 +334,13 @@ public void run() { @Override public void onMethodCall(MethodCall call, MethodChannel.Result rawResult) { - if (activity == null) { + if (activityState == null || activityState.getActivity() == null) { rawResult.error("no_activity", "image_picker plugin requires a foreground activity.", null); return; } MethodChannel.Result result = new MethodResultWrapper(rawResult); int imageSource; + ImagePickerDelegate delegate = activityState.getDelegate(); if (call.argument("cameraDevice") != null) { CameraDevice device; int deviceIntValue = call.argument("cameraDevice"); diff --git a/packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java b/packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java index 422b8be74f7c..ce41343e3d2c 100644 --- a/packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java +++ b/packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java @@ -5,10 +5,13 @@ package io.flutter.plugins.imagepicker; import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -16,6 +19,11 @@ import android.app.Activity; import android.app.Application; +import androidx.lifecycle.Lifecycle; +import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding; +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; +import io.flutter.embedding.engine.plugins.lifecycle.HiddenLifecycleReference; +import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import java.io.File; @@ -41,6 +49,9 @@ public class ImagePickerPluginTest { @Mock io.flutter.plugin.common.PluginRegistry.Registrar mockRegistrar; + @Mock ActivityPluginBinding mockActivityBinding; + @Mock FlutterPluginBinding mockPluginBinding; + @Mock Activity mockActivity; @Mock Application mockApplication; @Mock ImagePickerDelegate mockImagePickerDelegate; @@ -52,7 +63,8 @@ public class ImagePickerPluginTest { public void setUp() { MockitoAnnotations.initMocks(this); when(mockRegistrar.context()).thenReturn(mockApplication); - + when(mockActivityBinding.getActivity()).thenReturn(mockActivity); + when(mockPluginBinding.getApplicationContext()).thenReturn(mockApplication); plugin = new ImagePickerPlugin(mockImagePickerDelegate, mockActivity); } @@ -176,6 +188,25 @@ public void constructDelegate_ShouldUseInternalCacheDirectory() { equalTo(mockDirectory)); } + @Test + public void onDetachedFromActivity_ShouldReleaseActivityState() { + final BinaryMessenger mockBinaryMessenger = mock(BinaryMessenger.class); + when(mockPluginBinding.getBinaryMessenger()).thenReturn(mockBinaryMessenger); + + final HiddenLifecycleReference mockLifecycleReference = mock(HiddenLifecycleReference.class); + when(mockActivityBinding.getLifecycle()).thenReturn(mockLifecycleReference); + + final Lifecycle mockLifecycle = mock(Lifecycle.class); + when(mockLifecycleReference.getLifecycle()).thenReturn(mockLifecycle); + + plugin.onAttachedToEngine(mockPluginBinding); + plugin.onAttachedToActivity(mockActivityBinding); + assertNotNull(plugin.getActivityState()); + + plugin.onDetachedFromActivity(); + assertNull(plugin.getActivityState()); + } + private MethodCall buildMethodCall(String method, final int source) { final Map arguments = new HashMap<>(); arguments.put("source", source); diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 4774711129da..553599f7306f 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.4+10 +version: 0.8.4+11 environment: sdk: ">=2.14.0 <3.0.0" From 73ab699a4a8ab9e80179fa201d4c1a059538f065 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 10 Mar 2022 13:40:24 -0500 Subject: [PATCH 032/844] Roll Flutter from 57aa49028db7 to 48172589e5ac (2 revisions) (#5037) --- .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 5afb34058b93..be362674817b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -57aa49028db77a4edf0fbc6efc70cceea2d2c6ed +48172589e5acfc5eb1da7edb13b4f351e6e66275 From 0ade756afed0d9c0f3b4df58ad9871ead9abda50 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 10 Mar 2022 16:55:21 -0500 Subject: [PATCH 033/844] Roll Flutter from 48172589e5ac to 2ed7cdf35a0e (5 revisions) (#5040) --- .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 be362674817b..fe665f2a53de 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -48172589e5acfc5eb1da7edb13b4f351e6e66275 +2ed7cdf35a0eb3cb25eb3a54a002339aad1fdaec From d3638944ba6f778349cfbceff1f34fe08062705e Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 11 Mar 2022 15:15:26 -0500 Subject: [PATCH 034/844] [flutter_plugin_tools] Fix subpackage analysis (#5027) --- script/tool/CHANGELOG.md | 5 +++ script/tool/lib/src/analyze_command.dart | 29 +++++++++------ .../tool/lib/src/common/plugin_command.dart | 20 +++++++---- .../lib/src/make_deps_path_based_command.dart | 4 +++ script/tool/pubspec.yaml | 2 +- script/tool/test/analyze_command_test.dart | 25 +++++++++++++ .../make_deps_path_based_command_test.dart | 36 +++++++++++++++++++ 7 files changed, 103 insertions(+), 18 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 8e5ae210ce0d..35786f4a8b1b 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.1 + +- Fixes an `analyze` regression in 0.8.0 with packages that have non-`example` + sub-packages. + ## 0.8.0 - Ensures that `firebase-test-lab` runs include an `integration_test` runner. diff --git a/script/tool/lib/src/analyze_command.dart b/script/tool/lib/src/analyze_command.dart index d8b17bfd5626..824d766f4ebd 100644 --- a/script/tool/lib/src/analyze_command.dart +++ b/script/tool/lib/src/analyze_command.dart @@ -102,16 +102,25 @@ class AnalyzeCommand extends PackageLoopingCommand { @override Future runForPackage(RepositoryPackage package) async { - // For non-example packages, fetch dependencies. 'flutter packages get' - // automatically runs 'pub get' in examples as part of handling the parent - // directory, which is guaranteed to come first in the package enumeration. - if (package.directory.basename != 'example' || - !RepositoryPackage(package.directory.parent).pubspecFile.existsSync()) { - final int exitCode = await processRunner.runAndStream( - flutterCommand, ['packages', 'get'], - workingDir: package.directory); - if (exitCode != 0) { - return PackageResult.fail(['Unable to get dependencies']); + // Analysis runs over the package and all subpackages, so all of them need + // `flutter packages get` run before analyzing. `example` packages can be + // skipped since 'flutter packages get' automatically runs `pub get` in + // examples as part of handling the parent directory. + final List packagesToGet = [ + package, + ...await getSubpackages(package).toList(), + ]; + for (final RepositoryPackage packageToGet in packagesToGet) { + if (packageToGet.directory.basename != 'example' || + !RepositoryPackage(packageToGet.directory.parent) + .pubspecFile + .existsSync()) { + final int exitCode = await processRunner.runAndStream( + flutterCommand, ['packages', 'get'], + workingDir: packageToGet.directory); + if (exitCode != 0) { + return PackageResult.fail(['Unable to get dependencies']); + } } } diff --git a/script/tool/lib/src/common/plugin_command.dart b/script/tool/lib/src/common/plugin_command.dart index fcc87c94ef91..d21002899669 100644 --- a/script/tool/lib/src/common/plugin_command.dart +++ b/script/tool/lib/src/common/plugin_command.dart @@ -417,16 +417,22 @@ abstract class PluginCommand extends Command { await for (final PackageEnumerationEntry plugin in getTargetPackages(filterExcluded: filterExcluded)) { yield plugin; - yield* plugin.package.directory - .list(recursive: true, followLinks: false) - .where(_isDartPackage) - .map((FileSystemEntity directory) => PackageEnumerationEntry( - // _isDartPackage guarantees that this cast is valid. - RepositoryPackage(directory as Directory), - excluded: plugin.excluded)); + yield* getSubpackages(plugin.package).map((RepositoryPackage package) => + PackageEnumerationEntry(package, excluded: plugin.excluded)); } } + /// Returns all Dart package folders (e.g., examples) under the given package. + Stream getSubpackages(RepositoryPackage package, + {bool filterExcluded = true}) async* { + yield* package.directory + .list(recursive: true, followLinks: false) + .where(_isDartPackage) + .map((FileSystemEntity directory) => + // _isDartPackage guarantees that this cast is valid. + RepositoryPackage(directory as Directory)); + } + /// Returns the files contained, recursively, within the packages /// involved in this command execution. Stream getFiles() { diff --git a/script/tool/lib/src/make_deps_path_based_command.dart b/script/tool/lib/src/make_deps_path_based_command.dart index 5ee42848a81b..c09060310e97 100644 --- a/script/tool/lib/src/make_deps_path_based_command.dart +++ b/script/tool/lib/src/make_deps_path_based_command.dart @@ -207,6 +207,10 @@ dependency_overrides: allComponents.contains('example')) { continue; } + if (!allComponents.contains(packagesDir.basename)) { + print(' Skipping $changedPath; not in packages directory.'); + continue; + } final RepositoryPackage package = RepositoryPackage(packagesDir.fileSystem.file(changedPath).parent); // Ignored deleted packages, as they won't be published. diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 9ca5e2b77580..93a1a87ca337 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.8.0 +version: 0.8.1 dependencies: args: ^2.1.0 diff --git a/script/tool/test/analyze_command_test.dart b/script/tool/test/analyze_command_test.dart index 087e5ada37b0..56f4ab9576c6 100644 --- a/script/tool/test/analyze_command_test.dart +++ b/script/tool/test/analyze_command_test.dart @@ -71,6 +71,31 @@ void main() { ])); }); + test('runs flutter pub get for non-example subpackages', () async { + final Directory mainPackageDir = createFakePackage('a', packagesDir); + final Directory otherPackages = + mainPackageDir.childDirectory('other_packages'); + final Directory subpackage1 = + createFakePackage('subpackage1', otherPackages); + final Directory subpackage2 = + createFakePackage('subpackage2', otherPackages); + + await runCapturingPrint(runner, ['analyze']); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall('flutter', const ['packages', 'get'], + mainPackageDir.path), + ProcessCall( + 'flutter', const ['packages', 'get'], subpackage1.path), + ProcessCall( + 'flutter', const ['packages', 'get'], subpackage2.path), + ProcessCall('dart', const ['analyze', '--fatal-infos'], + mainPackageDir.path), + ])); + }); + test('don\'t elide a non-contained example package', () async { final Directory plugin1Dir = createFakePlugin('a', packagesDir); final Directory plugin2Dir = createFakePlugin('example', packagesDir); diff --git a/script/tool/test/make_deps_path_based_command_test.dart b/script/tool/test/make_deps_path_based_command_test.dart index bdd2139b237c..2021f24079e3 100644 --- a/script/tool/test/make_deps_path_based_command_test.dart +++ b/script/tool/test/make_deps_path_based_command_test.dart @@ -323,5 +323,41 @@ void main() { ]), ); }); + + test('skips anything outside of the packages directory', () async { + final Directory toolDir = packagesDir.parent.childDirectory('tool'); + const String newVersion = '1.1.0'; + final Directory package = createFakePackage( + 'flutter_plugin_tools', toolDir, + version: newVersion); + + // Simulate a minor version change so it would be a target. + final File pubspecFile = RepositoryPackage(package).pubspecFile; + final String changedFileOutput = [ + pubspecFile, + ].map((File file) => file.path).join('\n'); + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: changedFileOutput), + ]; + final String gitPubspecContents = + pubspecFile.readAsStringSync().replaceAll(newVersion, '1.0.0'); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: gitPubspecContents), + ]; + + final List output = await runCapturingPrint(runner, [ + 'make-deps-path-based', + '--target-dependencies-with-non-breaking-updates' + ]); + + expect( + output, + containsAllInOrder([ + contains( + 'Skipping /tool/flutter_plugin_tools/pubspec.yaml; not in packages directory.'), + contains('No target dependencies'), + ]), + ); + }); }); } From 0d5cf74c437e7f922ae27b82bb580e5043f57ceb Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 11 Mar 2022 16:00:23 -0500 Subject: [PATCH 035/844] Roll Flutter from 2ed7cdf35a0e to 201a64c98f35 (5 revisions) (#5043) --- .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 fe665f2a53de..d17adfe06468 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2ed7cdf35a0eb3cb25eb3a54a002339aad1fdaec +201a64c98f350dbc88c6fd3f8a6050864f2259cf From 8189a2ceae92973bd1378d049292f3a832958b72 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 14 Mar 2022 16:10:23 -0400 Subject: [PATCH 036/844] [file_selector] Update macOS README and example (#5034) --- .../file_selector/file_selector/CHANGELOG.md | 5 ++ .../file_selector/file_selector/README.md | 17 ++++- .../macos/Runner.xcodeproj/project.pbxproj | 62 ++++++++++++++++++- .../contents.xcworkspacedata | 3 + .../macos/Runner/DebugProfile.entitlements | 2 + .../example/macos/Runner/Release.entitlements | 2 + .../file_selector/file_selector/pubspec.yaml | 2 +- 7 files changed, 90 insertions(+), 3 deletions(-) diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index dde818dbce52..65b3a64475e9 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.4+1 + +* Adds README information about macOS entitlements. +* Adds necessary entitlement to macOS example. + ## 0.8.4 * Adds an endorsed macOS implementation. diff --git a/packages/file_selector/file_selector/README.md b/packages/file_selector/file_selector/README.md index 22ae7073ca2d..863164ebf286 100644 --- a/packages/file_selector/file_selector/README.md +++ b/packages/file_selector/file_selector/README.md @@ -7,8 +7,22 @@ A Flutter plugin that manages files and interactions with file dialogs. ## Usage To use this plugin, add `file_selector` as a [dependency in your pubspec.yaml file](https://flutter.dev/platform-plugins/). +### macOS + +You will need to [add an entitlement][entitlement] for either read-only access: +``` + com.apple.security.files.user-selected.read-only + +``` +or read/write access: +``` + com.apple.security.files.user-selected.read-write + +``` +depending on your use case. + ### Examples -Here are small examples that show you how to use the API. +Here are small examples that show you how to use the API. Please also take a look at our [example][example] app. #### Open a single file @@ -34,3 +48,4 @@ await file.saveTo(path); ``` [example]:./example +[entitlement]: https://docs.flutter.dev/desktop#entitlements-and-the-app-sandbox diff --git a/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj b/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj index c84862c67576..c450a1d06cf5 100644 --- a/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/file_selector/file_selector/example/macos/Runner.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 6BA632E5BE2B856B0D473EBF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C6D20B684858422917AB21A6 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -52,9 +53,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 17DF935FF296A265D8BE378B /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -66,8 +68,11 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 453A41FF685B9AACDF48F0C6 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 6FA96861AA2D76C12832F6C9 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + C6D20B684858422917AB21A6 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -75,6 +80,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 6BA632E5BE2B856B0D473EBF /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -99,6 +105,7 @@ 33CEB47122A05771004F2AC0 /* Flutter */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, + 58708F6C9D1522F09C51DA54 /* Pods */, ); sourceTree = ""; }; @@ -145,9 +152,21 @@ path = Runner; sourceTree = ""; }; + 58708F6C9D1522F09C51DA54 /* Pods */ = { + isa = PBXGroup; + children = ( + 453A41FF685B9AACDF48F0C6 /* Pods-Runner.debug.xcconfig */, + 17DF935FF296A265D8BE378B /* Pods-Runner.release.xcconfig */, + 6FA96861AA2D76C12832F6C9 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( + C6D20B684858422917AB21A6 /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = ""; @@ -159,11 +178,13 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + A778864BDDD7B12C41D66FBB /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, + 028A8DA36859BD4F05694F96 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -233,6 +254,23 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 028A8DA36859BD4F05694F96 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -270,6 +308,28 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; + A778864BDDD7B12C41D66FBB /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/contents.xcworkspacedata index 1d526a16ed0f..21a3cc14c74e 100644 --- a/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/contents.xcworkspacedata +++ b/packages/file_selector/file_selector/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/packages/file_selector/file_selector/example/macos/Runner/DebugProfile.entitlements b/packages/file_selector/file_selector/example/macos/Runner/DebugProfile.entitlements index dddb8a30c851..d138bd5b0451 100644 --- a/packages/file_selector/file_selector/example/macos/Runner/DebugProfile.entitlements +++ b/packages/file_selector/file_selector/example/macos/Runner/DebugProfile.entitlements @@ -8,5 +8,7 @@ com.apple.security.network.server + com.apple.security.files.user-selected.read-write + diff --git a/packages/file_selector/file_selector/example/macos/Runner/Release.entitlements b/packages/file_selector/file_selector/example/macos/Runner/Release.entitlements index 852fa1a4728a..19afff14a08c 100644 --- a/packages/file_selector/file_selector/example/macos/Runner/Release.entitlements +++ b/packages/file_selector/file_selector/example/macos/Runner/Release.entitlements @@ -4,5 +4,7 @@ com.apple.security.app-sandbox + com.apple.security.files.user-selected.read-write + diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index 14ff92997c00..c05900f650cf 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.4 +version: 0.8.4+1 environment: sdk: ">=2.12.0 <3.0.0" From dcacf4c66a51b408b6e3c279686ae6822586c737 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 14 Mar 2022 21:20:22 -0400 Subject: [PATCH 037/844] [video_player] Rename internal method channels (#5045) --- .../video_player_android/CHANGELOG.md | 6 + .../flutter/plugins/videoplayer/Messages.java | 69 ++++--- .../videoplayer/VideoPlayerPlugin.java | 9 +- .../lib/src/android_video_player.dart | 2 +- .../lib/src/messages.g.dart | 77 ++++--- .../pigeons/messages.dart | 2 +- .../video_player_android/pubspec.yaml | 4 +- .../video_player_android/test/test_api.dart | 65 +++--- .../video_player_avfoundation/CHANGELOG.md | 6 + .../ios/RunnerTests/VideoPlayerTests.m | 2 +- .../ios/Classes/FLTVideoPlayerPlugin.m | 6 +- .../ios/Classes/messages.g.h | 15 +- .../ios/Classes/messages.g.m | 194 ++++++++++-------- .../lib/src/avfoundation_video_player.dart | 2 +- .../lib/src/messages.g.dart | 77 ++++--- .../pigeons/messages.dart | 2 +- .../video_player_avfoundation/pubspec.yaml | 4 +- .../test/test_api.dart | 66 +++--- 18 files changed, 327 insertions(+), 281 deletions(-) diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index ec526ee1c7c9..774589c05f84 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,9 @@ +## 2.3.1 + +* Renames internal method channels to avoid potential confusion with the + default implementation's method channel. +* Updates Pigeon to 2.0.1. + ## 2.3.0 * Updates Pigeon to ^1.0.16. diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java index 0cdf3564d334..6593ebf9c22a 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/Messages.java @@ -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 (v1.0.16), do not edit directly. +// Autogenerated from Pigeon (v2.0.1), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.videoplayer; @@ -557,10 +557,10 @@ Map toMap() { } } - private static class VideoPlayerApiCodec extends StandardMessageCodec { - public static final VideoPlayerApiCodec INSTANCE = new VideoPlayerApiCodec(); + private static class AndroidVideoPlayerApiCodec extends StandardMessageCodec { + public static final AndroidVideoPlayerApiCodec INSTANCE = new AndroidVideoPlayerApiCodec(); - private VideoPlayerApiCodec() {} + private AndroidVideoPlayerApiCodec() {} @Override protected Object readValueOfType(byte type, ByteBuffer buffer) { @@ -621,40 +621,45 @@ protected void writeValue(ByteArrayOutputStream stream, Object value) { } /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ - public interface VideoPlayerApi { + public interface AndroidVideoPlayerApi { void initialize(); - TextureMessage create(CreateMessage msg); + @NonNull + TextureMessage create(@NonNull CreateMessage msg); - void dispose(TextureMessage msg); + void dispose(@NonNull TextureMessage msg); - void setLooping(LoopingMessage msg); + void setLooping(@NonNull LoopingMessage msg); - void setVolume(VolumeMessage msg); + void setVolume(@NonNull VolumeMessage msg); - void setPlaybackSpeed(PlaybackSpeedMessage msg); + void setPlaybackSpeed(@NonNull PlaybackSpeedMessage msg); - void play(TextureMessage msg); + void play(@NonNull TextureMessage msg); - PositionMessage position(TextureMessage msg); + @NonNull + PositionMessage position(@NonNull TextureMessage msg); - void seekTo(PositionMessage msg); + void seekTo(@NonNull PositionMessage msg); - void pause(TextureMessage msg); + void pause(@NonNull TextureMessage msg); - void setMixWithOthers(MixWithOthersMessage msg); + void setMixWithOthers(@NonNull MixWithOthersMessage msg); - /** The codec used by VideoPlayerApi. */ + /** The codec used by AndroidVideoPlayerApi. */ static MessageCodec getCodec() { - return VideoPlayerApiCodec.INSTANCE; + return AndroidVideoPlayerApiCodec.INSTANCE; } - /** Sets up an instance of `VideoPlayerApi` to handle messages through the `binaryMessenger`. */ - static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { + /** + * Sets up an instance of `AndroidVideoPlayerApi` to handle messages through the + * `binaryMessenger`. + */ + static void setup(BinaryMessenger binaryMessenger, AndroidVideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.initialize", getCodec()); + binaryMessenger, "dev.flutter.pigeon.AndroidVideoPlayerApi.initialize", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -674,7 +679,7 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.create", getCodec()); + binaryMessenger, "dev.flutter.pigeon.AndroidVideoPlayerApi.create", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -699,7 +704,7 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.dispose", getCodec()); + binaryMessenger, "dev.flutter.pigeon.AndroidVideoPlayerApi.dispose", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -724,7 +729,7 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.setLooping", getCodec()); + binaryMessenger, "dev.flutter.pigeon.AndroidVideoPlayerApi.setLooping", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -749,7 +754,7 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.setVolume", getCodec()); + binaryMessenger, "dev.flutter.pigeon.AndroidVideoPlayerApi.setVolume", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -774,7 +779,9 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed", getCodec()); + binaryMessenger, + "dev.flutter.pigeon.AndroidVideoPlayerApi.setPlaybackSpeed", + getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -799,7 +806,7 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.play", getCodec()); + binaryMessenger, "dev.flutter.pigeon.AndroidVideoPlayerApi.play", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -824,7 +831,7 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.position", getCodec()); + binaryMessenger, "dev.flutter.pigeon.AndroidVideoPlayerApi.position", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -849,7 +856,7 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.seekTo", getCodec()); + binaryMessenger, "dev.flutter.pigeon.AndroidVideoPlayerApi.seekTo", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -874,7 +881,7 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.pause", getCodec()); + binaryMessenger, "dev.flutter.pigeon.AndroidVideoPlayerApi.pause", getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -899,7 +906,9 @@ static void setup(BinaryMessenger binaryMessenger, VideoPlayerApi api) { { BasicMessageChannel channel = new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers", getCodec()); + binaryMessenger, + "dev.flutter.pigeon.AndroidVideoPlayerApi.setMixWithOthers", + getCodec()); if (api != null) { channel.setMessageHandler( (message, reply) -> { diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java index 168d90d8a57a..56fabecd3a96 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayerPlugin.java @@ -12,13 +12,13 @@ import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.EventChannel; +import io.flutter.plugins.videoplayer.Messages.AndroidVideoPlayerApi; import io.flutter.plugins.videoplayer.Messages.CreateMessage; import io.flutter.plugins.videoplayer.Messages.LoopingMessage; import io.flutter.plugins.videoplayer.Messages.MixWithOthersMessage; import io.flutter.plugins.videoplayer.Messages.PlaybackSpeedMessage; import io.flutter.plugins.videoplayer.Messages.PositionMessage; import io.flutter.plugins.videoplayer.Messages.TextureMessage; -import io.flutter.plugins.videoplayer.Messages.VideoPlayerApi; import io.flutter.plugins.videoplayer.Messages.VolumeMessage; import io.flutter.view.TextureRegistry; import java.security.KeyManagementException; @@ -27,7 +27,7 @@ import javax.net.ssl.HttpsURLConnection; /** Android platform implementation of the VideoPlayerPlugin. */ -public class VideoPlayerPlugin implements FlutterPlugin, VideoPlayerApi { +public class VideoPlayerPlugin implements FlutterPlugin, AndroidVideoPlayerApi { private static final String TAG = "VideoPlayerPlugin"; private final LongSparseArray videoPlayers = new LongSparseArray<>(); private FlutterState flutterState; @@ -61,7 +61,6 @@ public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registra @Override public void onAttachedToEngine(FlutterPluginBinding binding) { - if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { try { HttpsURLConnection.setDefaultSSLSocketFactory(new CustomSSLSocketFactory()); @@ -241,11 +240,11 @@ private static final class FlutterState { } void startListening(VideoPlayerPlugin methodCallHandler, BinaryMessenger messenger) { - VideoPlayerApi.setup(messenger, methodCallHandler); + AndroidVideoPlayerApi.setup(messenger, methodCallHandler); } void stopListening(BinaryMessenger messenger) { - VideoPlayerApi.setup(messenger, null); + AndroidVideoPlayerApi.setup(messenger, null); } } } diff --git a/packages/video_player/video_player_android/lib/src/android_video_player.dart b/packages/video_player/video_player_android/lib/src/android_video_player.dart index d713b282d197..31d0744e51dc 100644 --- a/packages/video_player/video_player_android/lib/src/android_video_player.dart +++ b/packages/video_player/video_player_android/lib/src/android_video_player.dart @@ -14,7 +14,7 @@ import 'messages.g.dart'; /// An Android implementation of [VideoPlayerPlatform] that uses the /// Pigeon-generated [VideoPlayerApi]. class AndroidVideoPlayer extends VideoPlayerPlatform { - final VideoPlayerApi _api = VideoPlayerApi(); + final AndroidVideoPlayerApi _api = AndroidVideoPlayerApi(); /// Registers this class as the default instance of [PathProviderPlatform]. static void registerWith() { diff --git a/packages/video_player/video_player_android/lib/src/messages.g.dart b/packages/video_player/video_player_android/lib/src/messages.g.dart index 5fa09e33bc7e..0dadd2efc67e 100644 --- a/packages/video_player/video_player_android/lib/src/messages.g.dart +++ b/packages/video_player/video_player_android/lib/src/messages.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v1.0.16), do not edit directly. +// Autogenerated from Pigeon (v2.0.1), 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 // @dart = 2.12 @@ -191,8 +191,8 @@ class MixWithOthersMessage { } } -class _VideoPlayerApiCodec extends StandardMessageCodec { - const _VideoPlayerApiCodec(); +class _AndroidVideoPlayerApiCodec extends StandardMessageCodec { + const _AndroidVideoPlayerApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { if (value is CreateMessage) { @@ -251,20 +251,20 @@ class _VideoPlayerApiCodec extends StandardMessageCodec { } } -class VideoPlayerApi { - /// Constructor for [VideoPlayerApi]. The [binaryMessenger] named argument is +class AndroidVideoPlayerApi { + /// Constructor for [AndroidVideoPlayerApi]. 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. - VideoPlayerApi({BinaryMessenger? binaryMessenger}) + AndroidVideoPlayerApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; - static const MessageCodec codec = _VideoPlayerApiCodec(); + static const MessageCodec codec = _AndroidVideoPlayerApiCodec(); Future initialize() async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.initialize', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.initialize', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send(null) as Map?; @@ -272,7 +272,6 @@ class VideoPlayerApi { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -289,15 +288,14 @@ class VideoPlayerApi { Future create(CreateMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.create', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -307,6 +305,11 @@ class VideoPlayerApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as TextureMessage?)!; } @@ -314,15 +317,14 @@ class VideoPlayerApi { Future dispose(TextureMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.dispose', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.dispose', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -339,15 +341,14 @@ class VideoPlayerApi { Future setLooping(LoopingMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setLooping', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.setLooping', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -364,15 +365,14 @@ class VideoPlayerApi { Future setVolume(VolumeMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setVolume', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.setVolume', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -389,15 +389,14 @@ class VideoPlayerApi { Future setPlaybackSpeed(PlaybackSpeedMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.setPlaybackSpeed', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -414,15 +413,14 @@ class VideoPlayerApi { Future play(TextureMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.play', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.play', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -439,15 +437,14 @@ class VideoPlayerApi { Future position(TextureMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.position', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.position', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -457,6 +454,11 @@ class VideoPlayerApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as PositionMessage?)!; } @@ -464,15 +466,14 @@ class VideoPlayerApi { Future seekTo(PositionMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.seekTo', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.seekTo', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -489,15 +490,14 @@ class VideoPlayerApi { Future pause(TextureMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.pause', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.pause', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -514,15 +514,14 @@ class VideoPlayerApi { Future setMixWithOthers(MixWithOthersMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.setMixWithOthers', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = diff --git a/packages/video_player/video_player_android/pigeons/messages.dart b/packages/video_player/video_player_android/pigeons/messages.dart index 9efbfc947b5c..bf552f9369df 100644 --- a/packages/video_player/video_player_android/pigeons/messages.dart +++ b/packages/video_player/video_player_android/pigeons/messages.dart @@ -57,7 +57,7 @@ class MixWithOthersMessage { } @HostApi(dartHostTestHandler: 'TestHostVideoPlayerApi') -abstract class VideoPlayerApi { +abstract class AndroidVideoPlayerApi { void initialize(); TextureMessage create(CreateMessage msg); void dispose(TextureMessage msg); diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index c40d5185e854..15c063f83d40 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.0 +version: 2.3.1 environment: sdk: ">=2.14.0 <3.0.0" @@ -25,4 +25,4 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pigeon: ^1.0.16 + pigeon: ^2.0.1 diff --git a/packages/video_player/video_player_android/test/test_api.dart b/packages/video_player/video_player_android/test/test_api.dart index e8bc9d85283f..c0ff23de9c22 100644 --- a/packages/video_player/video_player_android/test/test_api.dart +++ b/packages/video_player/video_player_android/test/test_api.dart @@ -1,9 +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. -// Autogenerated from Pigeon (v1.0.16), do not edit directly. +// Autogenerated from Pigeon (v2.0.1), 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 +// ignore_for_file: avoid_relative_lib_imports // @dart = 2.12 import 'dart:async'; import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; @@ -92,7 +93,7 @@ abstract class TestHostVideoPlayerApi { {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.initialize', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.initialize', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); @@ -106,18 +107,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.create', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.create', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.create was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.create was null.'); final List args = (message as List?)!; final CreateMessage? arg_msg = (args[0] as CreateMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.create was null, expected non-null CreateMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.create was null, expected non-null CreateMessage.'); final TextureMessage output = api.create(arg_msg!); return {'result': output}; }); @@ -125,18 +126,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.dispose', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.dispose', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.dispose was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.dispose was null.'); final List args = (message as List?)!; final TextureMessage? arg_msg = (args[0] as TextureMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.dispose was null, expected non-null TextureMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.dispose was null, expected non-null TextureMessage.'); api.dispose(arg_msg!); return {}; }); @@ -144,18 +145,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setLooping', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.setLooping', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setLooping was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.setLooping was null.'); final List args = (message as List?)!; final LoopingMessage? arg_msg = (args[0] as LoopingMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setLooping was null, expected non-null LoopingMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.setLooping was null, expected non-null LoopingMessage.'); api.setLooping(arg_msg!); return {}; }); @@ -163,18 +164,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setVolume', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.setVolume', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setVolume was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.setVolume was null.'); final List args = (message as List?)!; final VolumeMessage? arg_msg = (args[0] as VolumeMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setVolume was null, expected non-null VolumeMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.setVolume was null, expected non-null VolumeMessage.'); api.setVolume(arg_msg!); return {}; }); @@ -182,19 +183,19 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.setPlaybackSpeed', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.setPlaybackSpeed was null.'); final List args = (message as List?)!; final PlaybackSpeedMessage? arg_msg = (args[0] as PlaybackSpeedMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed was null, expected non-null PlaybackSpeedMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.setPlaybackSpeed was null, expected non-null PlaybackSpeedMessage.'); api.setPlaybackSpeed(arg_msg!); return {}; }); @@ -202,18 +203,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.play', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.play', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.play was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.play was null.'); final List args = (message as List?)!; final TextureMessage? arg_msg = (args[0] as TextureMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.play was null, expected non-null TextureMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.play was null, expected non-null TextureMessage.'); api.play(arg_msg!); return {}; }); @@ -221,18 +222,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.position', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.position', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.position was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.position was null.'); final List args = (message as List?)!; final TextureMessage? arg_msg = (args[0] as TextureMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.position was null, expected non-null TextureMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.position was null, expected non-null TextureMessage.'); final PositionMessage output = api.position(arg_msg!); return {'result': output}; }); @@ -240,18 +241,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.seekTo', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.seekTo', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.seekTo was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.seekTo was null.'); final List args = (message as List?)!; final PositionMessage? arg_msg = (args[0] as PositionMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.seekTo was null, expected non-null PositionMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.seekTo was null, expected non-null PositionMessage.'); api.seekTo(arg_msg!); return {}; }); @@ -259,18 +260,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.pause', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.pause', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.pause was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.pause was null.'); final List args = (message as List?)!; final TextureMessage? arg_msg = (args[0] as TextureMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.pause was null, expected non-null TextureMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.pause was null, expected non-null TextureMessage.'); api.pause(arg_msg!); return {}; }); @@ -278,19 +279,19 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', codec, + 'dev.flutter.pigeon.AndroidVideoPlayerApi.setMixWithOthers', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers was null.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.setMixWithOthers was null.'); final List args = (message as List?)!; final MixWithOthersMessage? arg_msg = (args[0] as MixWithOthersMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers was null, expected non-null MixWithOthersMessage.'); + 'Argument for dev.flutter.pigeon.AndroidVideoPlayerApi.setMixWithOthers was null, expected non-null MixWithOthersMessage.'); api.setMixWithOthers(arg_msg!); return {}; }); diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index d6ecabd4b9ad..c36c9878d1a1 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,9 @@ +## 2.3.1 + +* Renames internal method channels to avoid potential confusion with the + default implementation's method channel. +* Updates Pigeon to 2.0.1. + ## 2.3.0 * Updates Pigeon to ^1.0.16. diff --git a/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m index 6d8b3965fd94..cbf2866aa071 100644 --- a/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m @@ -12,7 +12,7 @@ @interface FLTVideoPlayer : NSObject @property(readonly, nonatomic) AVPlayer *player; @end -@interface FLTVideoPlayerPlugin (Test) +@interface FLTVideoPlayerPlugin (Test) @property(readonly, strong, nonatomic) NSMutableDictionary *playersByTextureId; @end diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m index 026b576cdb10..2fb9f7f55600 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m @@ -499,7 +499,7 @@ - (void)dispose { @end -@interface FLTVideoPlayerPlugin () +@interface FLTVideoPlayerPlugin () @property(readonly, weak, nonatomic) NSObject *registry; @property(readonly, weak, nonatomic) NSObject *messenger; @property(readonly, strong, nonatomic) @@ -511,7 +511,7 @@ @implementation FLTVideoPlayerPlugin + (void)registerWithRegistrar:(NSObject *)registrar { FLTVideoPlayerPlugin *instance = [[FLTVideoPlayerPlugin alloc] initWithRegistrar:registrar]; [registrar publish:instance]; - FLTVideoPlayerApiSetup(registrar.messenger, instance); + FLTAVFoundationVideoPlayerApiSetup(registrar.messenger, instance); } - (instancetype)initWithRegistrar:(NSObject *)registrar { @@ -530,7 +530,7 @@ - (void)detachFromEngineForRegistrar:(NSObject *)registr // TODO(57151): This should be commented out when 57151's fix lands on stable. // This is the correct behavior we never did it in the past and the engine // doesn't currently support it. - // FLTVideoPlayerApiSetup(registrar.messenger, nil); + // FLTAVFoundationVideoPlayerApiSetup(registrar.messenger, nil); } - (FLTTextureMessage *)onPlayerSetup:(FLTVideoPlayer *)player diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.h b/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.h index 96d02d2f7360..130d4849f372 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.h +++ b/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.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 (v1.0.17), do not edit directly. +// Autogenerated from Pigeon (v2.0.1), do not edit directly. // See also: https://pub.dev/packages/pigeon #import @protocol FlutterBinaryMessenger; @@ -80,11 +80,12 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, strong) NSNumber *mixWithOthers; @end -/// The codec used by FLTVideoPlayerApi. -NSObject *FLTVideoPlayerApiGetCodec(void); +/// The codec used by FLTAVFoundationVideoPlayerApi. +NSObject *FLTAVFoundationVideoPlayerApiGetCodec(void); -@protocol FLTVideoPlayerApi +@protocol FLTAVFoundationVideoPlayerApi - (void)initialize:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. - (nullable FLTTextureMessage *)create:(FLTCreateMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error; - (void)dispose:(FLTTextureMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error; @@ -93,6 +94,7 @@ NSObject *FLTVideoPlayerApiGetCodec(void); - (void)setPlaybackSpeed:(FLTPlaybackSpeedMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error; - (void)play:(FLTTextureMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. - (nullable FLTPositionMessage *)position:(FLTTextureMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error; - (void)seekTo:(FLTPositionMessage *)msg error:(FlutterError *_Nullable *_Nonnull)error; @@ -101,7 +103,8 @@ NSObject *FLTVideoPlayerApiGetCodec(void); error:(FlutterError *_Nullable *_Nonnull)error; @end -extern void FLTVideoPlayerApiSetup(id binaryMessenger, - NSObject *_Nullable api); +extern void FLTAVFoundationVideoPlayerApiSetup( + id binaryMessenger, + NSObject *_Nullable api); NS_ASSUME_NONNULL_END diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.m b/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.m index c9acee44593c..d82dc386878d 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/messages.g.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 (v1.0.17), do not edit directly. +// Autogenerated from Pigeon (v2.0.1), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "messages.g.h" #import @@ -28,6 +28,10 @@ static id GetNullableObject(NSDictionary *dict, id key) { id result = dict[key]; return (result == [NSNull null]) ? nil : result; } +static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { + id result = array[key]; + return (result == [NSNull null]) ? nil : result; +} @interface FLTTextureMessage () + (FLTTextureMessage *)fromMap:(NSDictionary *)dict; @@ -223,9 +227,9 @@ - (NSDictionary *)toMap { } @end -@interface FLTVideoPlayerApiCodecReader : FlutterStandardReader +@interface FLTAVFoundationVideoPlayerApiCodecReader : FlutterStandardReader @end -@implementation FLTVideoPlayerApiCodecReader +@implementation FLTAVFoundationVideoPlayerApiCodecReader - (nullable id)readValueOfType:(UInt8)type { switch (type) { case 128: @@ -255,9 +259,9 @@ - (nullable id)readValueOfType:(UInt8)type { } @end -@interface FLTVideoPlayerApiCodecWriter : FlutterStandardWriter +@interface FLTAVFoundationVideoPlayerApiCodecWriter : FlutterStandardWriter @end -@implementation FLTVideoPlayerApiCodecWriter +@implementation FLTAVFoundationVideoPlayerApiCodecWriter - (void)writeValue:(id)value { if ([value isKindOfClass:[FLTCreateMessage class]]) { [self writeByte:128]; @@ -286,38 +290,39 @@ - (void)writeValue:(id)value { } @end -@interface FLTVideoPlayerApiCodecReaderWriter : FlutterStandardReaderWriter +@interface FLTAVFoundationVideoPlayerApiCodecReaderWriter : FlutterStandardReaderWriter @end -@implementation FLTVideoPlayerApiCodecReaderWriter +@implementation FLTAVFoundationVideoPlayerApiCodecReaderWriter - (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { - return [[FLTVideoPlayerApiCodecWriter alloc] initWithData:data]; + return [[FLTAVFoundationVideoPlayerApiCodecWriter alloc] initWithData:data]; } - (FlutterStandardReader *)readerWithData:(NSData *)data { - return [[FLTVideoPlayerApiCodecReader alloc] initWithData:data]; + return [[FLTAVFoundationVideoPlayerApiCodecReader alloc] initWithData:data]; } @end -NSObject *FLTVideoPlayerApiGetCodec() { +NSObject *FLTAVFoundationVideoPlayerApiGetCodec() { static dispatch_once_t sPred = 0; static FlutterStandardMessageCodec *sSharedObject = nil; dispatch_once(&sPred, ^{ - FLTVideoPlayerApiCodecReaderWriter *readerWriter = - [[FLTVideoPlayerApiCodecReaderWriter alloc] init]; + FLTAVFoundationVideoPlayerApiCodecReaderWriter *readerWriter = + [[FLTAVFoundationVideoPlayerApiCodecReaderWriter alloc] init]; sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; }); return sSharedObject; } -void FLTVideoPlayerApiSetup(id binaryMessenger, - NSObject *api) { +void FLTAVFoundationVideoPlayerApiSetup(id binaryMessenger, + NSObject *api) { { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.initialize" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.initialize" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(initialize:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(initialize:)", api); + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(initialize:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { FlutterError *error; [api initialize:&error]; @@ -328,16 +333,18 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.create" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.create" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(create:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(create:error:)", api); + NSCAssert( + [api respondsToSelector:@selector(create:error:)], + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(create:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTCreateMessage *arg_msg = args[0]; + FLTCreateMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; FLTTextureMessage *output = [api create:arg_msg error:&error]; callback(wrapResult(output, error)); @@ -347,16 +354,18 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.dispose" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.dispose" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(dispose:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(dispose:error:)", api); + NSCAssert( + [api respondsToSelector:@selector(dispose:error:)], + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(dispose:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTTextureMessage *arg_msg = args[0]; + FLTTextureMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; [api dispose:arg_msg error:&error]; callback(wrapResult(nil, error)); @@ -366,16 +375,18 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.setLooping" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.setLooping" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(setLooping:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(setLooping:error:)", api); + NSCAssert( + [api respondsToSelector:@selector(setLooping:error:)], + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(setLooping:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTLoopingMessage *arg_msg = args[0]; + FLTLoopingMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; [api setLooping:arg_msg error:&error]; callback(wrapResult(nil, error)); @@ -385,16 +396,18 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.setVolume" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.setVolume" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(setVolume:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(setVolume:error:)", api); + NSCAssert( + [api respondsToSelector:@selector(setVolume:error:)], + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(setVolume:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTVolumeMessage *arg_msg = args[0]; + FLTVolumeMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; [api setVolume:arg_msg error:&error]; callback(wrapResult(nil, error)); @@ -404,17 +417,18 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.setPlaybackSpeed" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(setPlaybackSpeed:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(setPlaybackSpeed:error:)", + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to " + @"@selector(setPlaybackSpeed:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTPlaybackSpeedMessage *arg_msg = args[0]; + FLTPlaybackSpeedMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; [api setPlaybackSpeed:arg_msg error:&error]; callback(wrapResult(nil, error)); @@ -424,16 +438,17 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = - [FlutterBasicMessageChannel messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.play" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.play" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(play:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(play:error:)", api); + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(play:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTTextureMessage *arg_msg = args[0]; + FLTTextureMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; [api play:arg_msg error:&error]; callback(wrapResult(nil, error)); @@ -443,16 +458,18 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.position" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.position" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(position:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(position:error:)", api); + NSCAssert( + [api respondsToSelector:@selector(position:error:)], + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(position:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTTextureMessage *arg_msg = args[0]; + FLTTextureMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; FLTPositionMessage *output = [api position:arg_msg error:&error]; callback(wrapResult(output, error)); @@ -462,16 +479,18 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.seekTo" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.seekTo" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(seekTo:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(seekTo:error:)", api); + NSCAssert( + [api respondsToSelector:@selector(seekTo:error:)], + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(seekTo:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTPositionMessage *arg_msg = args[0]; + FLTPositionMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; [api seekTo:arg_msg error:&error]; callback(wrapResult(nil, error)); @@ -481,16 +500,18 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.pause" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.pause" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(pause:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(pause:error:)", api); + NSCAssert( + [api respondsToSelector:@selector(pause:error:)], + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to @selector(pause:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTTextureMessage *arg_msg = args[0]; + FLTTextureMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; [api pause:arg_msg error:&error]; callback(wrapResult(nil, error)); @@ -500,17 +521,18 @@ void FLTVideoPlayerApiSetup(id binaryMessenger, } } { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers" - binaryMessenger:binaryMessenger - codec:FLTVideoPlayerApiGetCodec()]; + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.AVFoundationVideoPlayerApi.setMixWithOthers" + binaryMessenger:binaryMessenger + codec:FLTAVFoundationVideoPlayerApiGetCodec()]; if (api) { NSCAssert([api respondsToSelector:@selector(setMixWithOthers:error:)], - @"FLTVideoPlayerApi api (%@) doesn't respond to @selector(setMixWithOthers:error:)", + @"FLTAVFoundationVideoPlayerApi api (%@) doesn't respond to " + @"@selector(setMixWithOthers:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - FLTMixWithOthersMessage *arg_msg = args[0]; + FLTMixWithOthersMessage *arg_msg = GetNullableObjectAtIndex(args, 0); FlutterError *error; [api setMixWithOthers:arg_msg error:&error]; callback(wrapResult(nil, error)); diff --git a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart index bf1518dfa32a..5dc6862c41df 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart @@ -14,7 +14,7 @@ import 'messages.g.dart'; /// An iOS implementation of [VideoPlayerPlatform] that uses the /// Pigeon-generated [VideoPlayerApi]. class AVFoundationVideoPlayer extends VideoPlayerPlatform { - final VideoPlayerApi _api = VideoPlayerApi(); + final AVFoundationVideoPlayerApi _api = AVFoundationVideoPlayerApi(); /// Registers this class as the default instance of [VideoPlayerPlatform]. static void registerWith() { diff --git a/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart b/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart index 1a679d5915b4..a745c66322d4 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/messages.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v1.0.17), do not edit directly. +// Autogenerated from Pigeon (v2.0.1), 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 // @dart = 2.12 @@ -191,8 +191,8 @@ class MixWithOthersMessage { } } -class _VideoPlayerApiCodec extends StandardMessageCodec { - const _VideoPlayerApiCodec(); +class _AVFoundationVideoPlayerApiCodec extends StandardMessageCodec { + const _AVFoundationVideoPlayerApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { if (value is CreateMessage) { @@ -251,20 +251,20 @@ class _VideoPlayerApiCodec extends StandardMessageCodec { } } -class VideoPlayerApi { - /// Constructor for [VideoPlayerApi]. The [binaryMessenger] named argument is +class AVFoundationVideoPlayerApi { + /// Constructor for [AVFoundationVideoPlayerApi]. 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. - VideoPlayerApi({BinaryMessenger? binaryMessenger}) + AVFoundationVideoPlayerApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; - static const MessageCodec codec = _VideoPlayerApiCodec(); + static const MessageCodec codec = _AVFoundationVideoPlayerApiCodec(); Future initialize() async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.initialize', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.initialize', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send(null) as Map?; @@ -272,7 +272,6 @@ class VideoPlayerApi { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -289,15 +288,14 @@ class VideoPlayerApi { Future create(CreateMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.create', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -307,6 +305,11 @@ class VideoPlayerApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as TextureMessage?)!; } @@ -314,15 +317,14 @@ class VideoPlayerApi { Future dispose(TextureMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.dispose', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.dispose', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -339,15 +341,14 @@ class VideoPlayerApi { Future setLooping(LoopingMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setLooping', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.setLooping', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -364,15 +365,14 @@ class VideoPlayerApi { Future setVolume(VolumeMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setVolume', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.setVolume', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -389,15 +389,14 @@ class VideoPlayerApi { Future setPlaybackSpeed(PlaybackSpeedMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.setPlaybackSpeed', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -414,15 +413,14 @@ class VideoPlayerApi { Future play(TextureMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.play', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.play', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -439,15 +437,14 @@ class VideoPlayerApi { Future position(TextureMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.position', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.position', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -457,6 +454,11 @@ class VideoPlayerApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as PositionMessage?)!; } @@ -464,15 +466,14 @@ class VideoPlayerApi { Future seekTo(PositionMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.seekTo', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.seekTo', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -489,15 +490,14 @@ class VideoPlayerApi { Future pause(TextureMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.pause', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.pause', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -514,15 +514,14 @@ class VideoPlayerApi { Future setMixWithOthers(MixWithOthersMessage arg_msg) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.setMixWithOthers', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_msg]) as Map?; + await channel.send([arg_msg]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = diff --git a/packages/video_player/video_player_avfoundation/pigeons/messages.dart b/packages/video_player/video_player_avfoundation/pigeons/messages.dart index d357caf81e3d..e6eda5960f29 100644 --- a/packages/video_player/video_player_avfoundation/pigeons/messages.dart +++ b/packages/video_player/video_player_avfoundation/pigeons/messages.dart @@ -58,7 +58,7 @@ class MixWithOthersMessage { } @HostApi(dartHostTestHandler: 'TestHostVideoPlayerApi') -abstract class VideoPlayerApi { +abstract class AVFoundationVideoPlayerApi { @ObjCSelector('initialize') void initialize(); @ObjCSelector('create:') diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index 044d15b890b2..0e42c26de829 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.0 +version: 2.3.1 environment: sdk: ">=2.14.0 <3.0.0" @@ -24,4 +24,4 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pigeon: ^1.0.17 + pigeon: ^2.0.1 diff --git a/packages/video_player/video_player_avfoundation/test/test_api.dart b/packages/video_player/video_player_avfoundation/test/test_api.dart index 191358e23024..af6d072107c8 100644 --- a/packages/video_player/video_player_avfoundation/test/test_api.dart +++ b/packages/video_player/video_player_avfoundation/test/test_api.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 (v1.0.17), do not edit directly. +// Autogenerated from Pigeon (v2.0.1), 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 // ignore_for_file: avoid_relative_lib_imports @@ -93,7 +93,7 @@ abstract class TestHostVideoPlayerApi { {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.initialize', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.initialize', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); @@ -107,18 +107,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.create', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.create', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.create was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.create was null.'); final List args = (message as List?)!; final CreateMessage? arg_msg = (args[0] as CreateMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.create was null, expected non-null CreateMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.create was null, expected non-null CreateMessage.'); final TextureMessage output = api.create(arg_msg!); return {'result': output}; }); @@ -126,18 +126,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.dispose', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.dispose', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.dispose was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.dispose was null.'); final List args = (message as List?)!; final TextureMessage? arg_msg = (args[0] as TextureMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.dispose was null, expected non-null TextureMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.dispose was null, expected non-null TextureMessage.'); api.dispose(arg_msg!); return {}; }); @@ -145,18 +145,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setLooping', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.setLooping', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setLooping was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.setLooping was null.'); final List args = (message as List?)!; final LoopingMessage? arg_msg = (args[0] as LoopingMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setLooping was null, expected non-null LoopingMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.setLooping was null, expected non-null LoopingMessage.'); api.setLooping(arg_msg!); return {}; }); @@ -164,18 +164,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setVolume', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.setVolume', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setVolume was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.setVolume was null.'); final List args = (message as List?)!; final VolumeMessage? arg_msg = (args[0] as VolumeMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setVolume was null, expected non-null VolumeMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.setVolume was null, expected non-null VolumeMessage.'); api.setVolume(arg_msg!); return {}; }); @@ -183,19 +183,20 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.setPlaybackSpeed', + codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.setPlaybackSpeed was null.'); final List args = (message as List?)!; final PlaybackSpeedMessage? arg_msg = (args[0] as PlaybackSpeedMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed was null, expected non-null PlaybackSpeedMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.setPlaybackSpeed was null, expected non-null PlaybackSpeedMessage.'); api.setPlaybackSpeed(arg_msg!); return {}; }); @@ -203,18 +204,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.play', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.play', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.play was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.play was null.'); final List args = (message as List?)!; final TextureMessage? arg_msg = (args[0] as TextureMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.play was null, expected non-null TextureMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.play was null, expected non-null TextureMessage.'); api.play(arg_msg!); return {}; }); @@ -222,18 +223,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.position', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.position', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.position was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.position was null.'); final List args = (message as List?)!; final TextureMessage? arg_msg = (args[0] as TextureMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.position was null, expected non-null TextureMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.position was null, expected non-null TextureMessage.'); final PositionMessage output = api.position(arg_msg!); return {'result': output}; }); @@ -241,18 +242,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.seekTo', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.seekTo', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.seekTo was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.seekTo was null.'); final List args = (message as List?)!; final PositionMessage? arg_msg = (args[0] as PositionMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.seekTo was null, expected non-null PositionMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.seekTo was null, expected non-null PositionMessage.'); api.seekTo(arg_msg!); return {}; }); @@ -260,18 +261,18 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.pause', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.pause', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.pause was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.pause was null.'); final List args = (message as List?)!; final TextureMessage? arg_msg = (args[0] as TextureMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.pause was null, expected non-null TextureMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.pause was null, expected non-null TextureMessage.'); api.pause(arg_msg!); return {}; }); @@ -279,19 +280,20 @@ abstract class TestHostVideoPlayerApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', codec, + 'dev.flutter.pigeon.AVFoundationVideoPlayerApi.setMixWithOthers', + codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers was null.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.setMixWithOthers was null.'); final List args = (message as List?)!; final MixWithOthersMessage? arg_msg = (args[0] as MixWithOthersMessage?); assert(arg_msg != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers was null, expected non-null MixWithOthersMessage.'); + 'Argument for dev.flutter.pigeon.AVFoundationVideoPlayerApi.setMixWithOthers was null, expected non-null MixWithOthersMessage.'); api.setMixWithOthers(arg_msg!); return {}; }); From 5cd65fb4a6265277a83433e7e6511b7d9eac7c36 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 14 Mar 2022 22:00:21 -0400 Subject: [PATCH 038/844] Roll Flutter from 201a64c98f35 to 2f73173c36f8 (3 revisions) (#5047) --- .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 d17adfe06468..46c80834c439 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -201a64c98f350dbc88c6fd3f8a6050864f2259cf +2f73173c36f806c7f30e705a90722082f85a2c8b From 3becd1c22c9c3ac18123421fd16b0067bf95e5b1 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 15 Mar 2022 08:40:16 -0400 Subject: [PATCH 039/844] Roll Flutter from 2f73173c36f8 to 2bd3e0d91485 (16 revisions) (#5055) --- .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 46c80834c439..fe8efe52a540 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2f73173c36f806c7f30e705a90722082f85a2c8b +2bd3e0d914854aa8c12e933f25c5fd8532ae5571 From f62168dd52612649f57de0d3b8bb21e4bbe5a0f3 Mon Sep 17 00:00:00 2001 From: godofredoc Date: Tue, 15 Mar 2022 08:40:17 -0700 Subject: [PATCH 040/844] Update scorecard versions. (#5050) --- .github/workflows/scorecards-analysis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 5148f2e19028..32c80cd247a2 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -20,12 +20,12 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0 + uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 with: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@b614d455ee90608b5e36e3299cd50d457eb37d5f # v1.0.3 + uses: ossf/scorecard-action@c1aec4ac820532bab364f02a81873c555a0ba3a1 with: results_file: results.sarif results_format: sarif @@ -40,7 +40,7 @@ jobs: # Upload the results as artifacts (optional). - name: "Upload artifact" - uses: actions/upload-artifact@82c141cc518b40d92cc801eee768e7aafc9c2fa2 # v2.3.1 + uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 with: name: SARIF file path: results.sarif @@ -48,6 +48,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 # v1.0.26 + uses: github/codeql-action/upload-sarif@f5d822707ee6e8fb81b04a5c0040b736da22e587 with: sarif_file: results.sarif From 6f6cd7826849b88fbfb36b740fa603f36315d452 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Tue, 15 Mar 2022 21:30:08 +0100 Subject: [PATCH 041/844] [in_app_purchase] Adds additional explanation on the importance of completing a purchase (#5017) --- .../in_app_purchase/CHANGELOG.md | 4 ++++ .../in_app_purchase/in_app_purchase/README.md | 20 +++++++++++++------ .../in_app_purchase/pubspec.yaml | 2 +- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 134fd9052a63..6253a23f48db 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.0.2 + +* Adds additional explanation on why it is important to complete a purchase. + ## 3.0.1 * Internal code cleanup for stricter analysis options. diff --git a/packages/in_app_purchase/in_app_purchase/README.md b/packages/in_app_purchase/in_app_purchase/README.md index b67f9e5b52ac..dbc8e8a17bc4 100644 --- a/packages/in_app_purchase/in_app_purchase/README.md +++ b/packages/in_app_purchase/in_app_purchase/README.md @@ -32,6 +32,10 @@ your app with each store. Both stores have extensive guides: * [App Store documentation](https://developer.apple.com/in-app-purchase/) * [Google Play documentation](https://developer.android.com/google/play/billing/billing_overview) +> NOTE: Further in this document the App Store and Google Play will be referred +> to as "the store" or "the underlying store", except when a feature is specific +> to a particular store. + For a list of steps for configuring in-app purchases in both stores, see the [example app README](https://github.com/flutter/plugins/blob/master/packages/in_app_purchase/in_app_purchase/example/README.md). @@ -190,11 +194,15 @@ if (_isConsumable(productDetails)) { ### Completing a purchase -The `InAppPurchase.purchaseStream` will send purchase updates after -you initiate the purchase flow using `InAppPurchase.buyConsumable` -or `InAppPurchase.buyNonConsumable`. After delivering the content to -the user, call `InAppPurchase.completePurchase` to tell the App Store -and Google Play that the purchase has been finished. +The `InAppPurchase.purchaseStream` will send purchase updates after initiating +the purchase flow using `InAppPurchase.buyConsumable` or +`InAppPurchase.buyNonConsumable`. After verifying the purchase receipt and the +delivering the content to the user it is important to call +`InAppPurchase.completePurchase` to tell the underlying store that the +purchase has been completed. Calling `InAppPurchase.completePurchase` will +inform the underlying store that the app verified and processed the +purchase and the store can proceed to finalize the transaction and bill +the end user's payment account. > **Warning:** Failure to call `InAppPurchase.completePurchase` and > get a successful response within 3 days of the purchase will result a refund. @@ -229,7 +237,7 @@ InAppPurchase.instance When the price of a subscription is changed the consumer will need to confirm that price change. If the consumer does not confirm the price change the subscription will not be auto-renewed. By default on both iOS and Android the consumer will -automatically get a popup to confirm the price change, but App developers can override this mechanism and show the popup on a later moment so it doesn't interrupt the critical flow of the App. This works different on the Apple App Store and on the Google Play Store. +automatically get a popup to confirm the price change, but App developers can override this mechanism and show the popup on a later moment so it doesn't interrupt the critical flow of the App. This works different for each of the stores. #### Google Play Store (Android) When the subscription price is raised, the consumer should approve the price change within 7 days. The official diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index 139d58318780..af8f5f3c2773 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 3.0.1 +version: 3.0.2 environment: sdk: ">=2.12.0 <3.0.0" From 43cf7608f867a4f88fdba6925ae2c4197a6fbf7a Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 15 Mar 2022 17:15:07 -0400 Subject: [PATCH 042/844] [flutter_plugin_tools] packages get -> pub get (#5046) --- script/tool/CHANGELOG.md | 4 +++ script/tool/lib/src/analyze_command.dart | 4 +-- script/tool/test/analyze_command_test.dart | 35 ++++++++----------- .../test/build_examples_command_test.dart | 4 +-- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 35786f4a8b1b..7e950352facb 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +- Switches from deprecated `flutter packages` alias to `flutter pub`. + ## 0.8.1 - Fixes an `analyze` regression in 0.8.0 with packages that have non-`example` diff --git a/script/tool/lib/src/analyze_command.dart b/script/tool/lib/src/analyze_command.dart index 824d766f4ebd..1cd85af076fc 100644 --- a/script/tool/lib/src/analyze_command.dart +++ b/script/tool/lib/src/analyze_command.dart @@ -103,7 +103,7 @@ class AnalyzeCommand extends PackageLoopingCommand { @override Future runForPackage(RepositoryPackage package) async { // Analysis runs over the package and all subpackages, so all of them need - // `flutter packages get` run before analyzing. `example` packages can be + // `flutter pub get` run before analyzing. `example` packages can be // skipped since 'flutter packages get' automatically runs `pub get` in // examples as part of handling the parent directory. final List packagesToGet = [ @@ -116,7 +116,7 @@ class AnalyzeCommand extends PackageLoopingCommand { .pubspecFile .existsSync()) { final int exitCode = await processRunner.runAndStream( - flutterCommand, ['packages', 'get'], + flutterCommand, ['pub', 'get'], workingDir: packageToGet.directory); if (exitCode != 0) { return PackageResult.fail(['Unable to get dependencies']); diff --git a/script/tool/test/analyze_command_test.dart b/script/tool/test/analyze_command_test.dart index 56f4ab9576c6..98fb9382a3aa 100644 --- a/script/tool/test/analyze_command_test.dart +++ b/script/tool/test/analyze_command_test.dart @@ -45,12 +45,10 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall( - 'flutter', const ['packages', 'get'], plugin1Dir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin1Dir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], plugin1Dir.path), - ProcessCall( - 'flutter', const ['packages', 'get'], plugin2Dir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin2Dir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], plugin2Dir.path), ])); @@ -64,8 +62,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall( - 'flutter', const ['packages', 'get'], plugin1Dir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin1Dir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], plugin1Dir.path), ])); @@ -85,12 +82,12 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall('flutter', const ['packages', 'get'], - mainPackageDir.path), ProcessCall( - 'flutter', const ['packages', 'get'], subpackage1.path), + 'flutter', const ['pub', 'get'], mainPackageDir.path), ProcessCall( - 'flutter', const ['packages', 'get'], subpackage2.path), + 'flutter', const ['pub', 'get'], subpackage1.path), + ProcessCall( + 'flutter', const ['pub', 'get'], subpackage2.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], mainPackageDir.path), ])); @@ -105,12 +102,10 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall( - 'flutter', const ['packages', 'get'], plugin1Dir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin1Dir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], plugin1Dir.path), - ProcessCall( - 'flutter', const ['packages', 'get'], plugin2Dir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin2Dir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], plugin2Dir.path), ])); @@ -127,7 +122,7 @@ void main() { orderedEquals([ ProcessCall( 'flutter', - const ['packages', 'get'], + const ['pub', 'get'], pluginDir.path, ), ProcessCall( @@ -195,7 +190,7 @@ void main() { processRunner.recordedCalls, orderedEquals([ ProcessCall( - 'flutter', const ['packages', 'get'], pluginDir.path), + 'flutter', const ['pub', 'get'], pluginDir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], pluginDir.path), ])); @@ -214,7 +209,7 @@ void main() { processRunner.recordedCalls, orderedEquals([ ProcessCall( - 'flutter', const ['packages', 'get'], pluginDir.path), + 'flutter', const ['pub', 'get'], pluginDir.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], pluginDir.path), ])); @@ -232,11 +227,11 @@ void main() { }); }); - test('fails if "packages get" fails', () async { + test('fails if "pub get" fails', () async { createFakePlugin('foo', packagesDir); processRunner.mockProcessesForExecutable['flutter'] = [ - MockProcess(exitCode: 1) // flutter packages get + MockProcess(exitCode: 1) // flutter pub get ]; Error? commandError; @@ -304,7 +299,7 @@ void main() { orderedEquals([ ProcessCall( 'flutter', - const ['packages', 'get'], + const ['pub', 'get'], pluginDir.path, ), ProcessCall( diff --git a/script/tool/test/build_examples_command_test.dart b/script/tool/test/build_examples_command_test.dart index 6d8f0b9d6486..29a879071657 100644 --- a/script/tool/test/build_examples_command_test.dart +++ b/script/tool/test/build_examples_command_test.dart @@ -63,7 +63,7 @@ void main() { processRunner .mockProcessesForExecutable[getFlutterCommand(mockPlatform)] = [ - MockProcess(exitCode: 1) // flutter packages get + MockProcess(exitCode: 1) // flutter pub get ]; Error? commandError; @@ -92,7 +92,7 @@ void main() { processRunner .mockProcessesForExecutable[getFlutterCommand(mockPlatform)] = [ - MockProcess(exitCode: 1) // flutter packages get + MockProcess(exitCode: 1) // flutter pub get ]; Error? commandError; From 35ee5989ead0957ec4b26b0aca79bd4f34fd436e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 15 Mar 2022 17:55:18 -0400 Subject: [PATCH 043/844] Roll Flutter from 2bd3e0d91485 to c9755336785e (2 revisions) (#5059) --- .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 fe8efe52a540..d59ed45b30df 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2bd3e0d914854aa8c12e933f25c5fd8532ae5571 +c9755336785ecf5f8adc7bbdc50420b7ea3b0d81 From 62caf7493c8d61f5662b56d07a01acf7af8b4e02 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 15 Mar 2022 19:00:16 -0400 Subject: [PATCH 044/844] Roll Flutter from c9755336785e to 722cbc52b85b (3 revisions) (#5060) --- .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 d59ed45b30df..9e90835c985e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c9755336785ecf5f8adc7bbdc50420b7ea3b0d81 +722cbc52b85b7365396aac59e6006d13855c1e60 From f552e59875982f6b9fa0f8d2ee02f6cadf42ce6f Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 15 Mar 2022 21:10:20 -0400 Subject: [PATCH 045/844] Roll Flutter from 722cbc52b85b to fd25dc848324 (3 revisions) (#5062) --- .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 9e90835c985e..7ef2641e3fb8 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -722cbc52b85b7365396aac59e6006d13855c1e60 +fd25dc84832469523768377c826b5549085c9636 From f25e370447f654a4480a8ee84635ab79cad53804 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 16 Mar 2022 01:35:21 -0400 Subject: [PATCH 046/844] Roll Flutter from fd25dc848324 to 509ddfda5c1b (5 revisions) (#5066) --- .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 7ef2641e3fb8..09a4cea57f76 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fd25dc84832469523768377c826b5549085c9636 +509ddfda5c1bda7c01cf51fe24607da19423d5f9 From 04f64ec89b65f135fc43e8856af95b7d66e3e38a Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Thu, 17 Mar 2022 17:25:29 +0100 Subject: [PATCH 047/844] [in_app_purchase] Implements transaction caching for StoreKit (#4985) --- .../in_app_purchase_storekit/CHANGELOG.md | 4 + .../ios/Runner.xcodeproj/project.pbxproj | 8 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../RunnerTests/FIATransactionCacheTests.m | 63 ++++ .../RunnerTests/InAppPurchasePluginTests.m | 355 +++++++++--------- .../ios/RunnerTests/PaymentQueueTests.m | 232 +++++++++++- .../ios/Classes/FIAPaymentQueueHandler.h | 74 +++- .../ios/Classes/FIAPaymentQueueHandler.m | 101 ++++- .../ios/Classes/FIATransactionCache.h | 31 ++ .../ios/Classes/FIATransactionCache.m | 40 ++ .../ios/Classes/InAppPurchasePlugin.m | 3 +- .../in_app_purchase_storekit/pubspec.yaml | 2 +- 12 files changed, 712 insertions(+), 203 deletions(-) create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/FIATransactionCacheTests.m create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIATransactionCache.h create mode 100644 packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIATransactionCache.m 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 c5df52151411..087c6e3095db 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.0+3 + +* Implements transaction caching for StoreKit ensuring transactions are delivered to the Flutter client. + ## 0.3.0+2 * Internal code cleanup for stricter analysis options. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj index a88050193053..3977d549af12 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -22,6 +22,7 @@ A5279298219369C600FF69E6 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5279297219369C600FF69E6 /* StoreKit.framework */; }; A59001A721E69658004A3E5E /* InAppPurchasePluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A59001A621E69658004A3E5E /* InAppPurchasePluginTests.m */; }; F67646F82681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F67646F72681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m */; }; + F6995BDD27CF73000050EA78 /* FIATransactionCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F6995BDC27CF73000050EA78 /* FIATransactionCacheTests.m */; }; F78AF3142342BC89008449C7 /* PaymentQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F78AF3132342BC89008449C7 /* PaymentQueueTests.m */; }; /* End PBXBuildFile section */ @@ -78,6 +79,7 @@ A59001A821E69658004A3E5E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E4F9651425A612301059769C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; F67646F72681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIAPPaymentQueueDeleteTests.m; sourceTree = ""; }; + F6995BDC27CF73000050EA78 /* FIATransactionCacheTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIATransactionCacheTests.m; sourceTree = ""; }; F6E5D5F926131C4800C68BED /* Configuration.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Configuration.storekit; sourceTree = ""; }; F78AF3132342BC89008449C7 /* PaymentQueueTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PaymentQueueTests.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -190,6 +192,7 @@ F78AF3132342BC89008449C7 /* PaymentQueueTests.m */, 688DE35021F2A5A100EA2684 /* TranslatorTests.m */, F67646F72681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m */, + F6995BDC27CF73000050EA78 /* FIATransactionCacheTests.m */, ); path = RunnerTests; sourceTree = ""; @@ -254,7 +257,7 @@ isa = PBXProject; attributes = { DefaultBuildSystemTypeForWorkspace = Original; - LastUpgradeCheck = 1100; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -406,6 +409,7 @@ F67646F82681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m in Sources */, 6896B34621E9363700D37AEF /* ProductRequestHandlerTests.m in Sources */, 688DE35121F2A5A100EA2684 /* TranslatorTests.m in Sources */, + F6995BDD27CF73000050EA78 /* FIATransactionCacheTests.m in Sources */, A59001A721E69658004A3E5E /* InAppPurchasePluginTests.m in Sources */, 6896B34C21EEB4B800D37AEF /* Stubs.m in Sources */, ); 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 3bd47ecb9ec0..a8adf88572cd 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 @@ -1,6 +1,6 @@ + +@import in_app_purchase_storekit; + +@interface FIATransactionCacheTests : XCTestCase + +@end + +@implementation FIATransactionCacheTests + +- (void)testAddObjectsForNewKey { + NSArray *dummyArray = @[ @1, @2, @3 ]; + FIATransactionCache *cache = [[FIATransactionCache alloc] init]; + [cache addObjects:dummyArray forKey:TransactionCacheKeyUpdatedTransactions]; + + XCTAssertEqual(dummyArray, [cache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]); +} + +- (void)testAddObjectsForExistingKey { + NSArray *dummyArray = @[ @1, @2, @3 ]; + FIATransactionCache *cache = [[FIATransactionCache alloc] init]; + [cache addObjects:dummyArray forKey:TransactionCacheKeyUpdatedTransactions]; + + XCTAssertEqual(dummyArray, [cache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]); + + [cache addObjects:@[ @4, @5, @6 ] forKey:TransactionCacheKeyUpdatedTransactions]; + + NSArray *expected = @[ @1, @2, @3, @4, @5, @6 ]; + XCTAssertEqualObjects(expected, [cache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]); +} + +- (void)testGetObjectsForNonExistingKey { + FIATransactionCache *cache = [[FIATransactionCache alloc] init]; + XCTAssertNil([cache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]); +} + +- (void)testClear { + NSArray *fakeUpdatedTransactions = @[ @1, @2, @3 ]; + NSArray *fakeRemovedTransactions = @[ @"Remove 1", @"Remove 2", @"Remove 3" ]; + NSArray *fakeUpdatedDownloads = @[ @"Download 1", @"Download 2" ]; + FIATransactionCache *cache = [[FIATransactionCache alloc] init]; + [cache addObjects:fakeUpdatedTransactions forKey:TransactionCacheKeyUpdatedTransactions]; + [cache addObjects:fakeRemovedTransactions forKey:TransactionCacheKeyRemovedTransactions]; + [cache addObjects:fakeUpdatedDownloads forKey:TransactionCacheKeyUpdatedDownloads]; + + XCTAssertEqual(fakeUpdatedTransactions, + [cache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]); + XCTAssertEqual(fakeRemovedTransactions, + [cache getObjectsForKey:TransactionCacheKeyRemovedTransactions]); + XCTAssertEqual(fakeUpdatedDownloads, + [cache getObjectsForKey:TransactionCacheKeyUpdatedDownloads]); + + [cache clear]; + + XCTAssertNil([cache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]); + XCTAssertNil([cache getObjectsForKey:TransactionCacheKeyRemovedTransactions]); + XCTAssertNil([cache getObjectsForKey:TransactionCacheKeyUpdatedDownloads]); +} +@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/InAppPurchasePluginTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/InAppPurchasePluginTests.m index fcb2c9425d69..c89589c6a9e5 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/InAppPurchasePluginTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/InAppPurchasePluginTests.m @@ -74,138 +74,84 @@ - (void)testGetProductResponse { XCTAssertTrue([resultArray.firstObject[@"productIdentifier"] isEqualToString:@"123"]); } -- (void)testAddPaymentFailure { +- (void)testAddPaymentShouldReturnFlutterErrorWhenArgumentsAreInvalid { XCTestExpectation *expectation = - [self expectationWithDescription:@"result should return failed state"]; + [self expectationWithDescription: + @"Result should contain a FlutterError when invalid parameters are passed in."]; + NSString *argument = @"Invalid argument"; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" - arguments:@{ - @"productIdentifier" : @"123", - @"quantity" : @(1), - @"simulatesAskToBuyInSandbox" : @YES, - }]; - SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; - queue.testState = SKPaymentTransactionStateFailed; - __block SKPaymentTransaction *transactionForUpdateBlock; - self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray *_Nonnull transactions) { - SKPaymentTransaction *transaction = transactions[0]; - if (transaction.transactionState == SKPaymentTransactionStateFailed) { - transactionForUpdateBlock = transaction; - [expectation fulfill]; - } - } - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:nil]; - [queue addTransactionObserver:self.plugin.paymentQueueHandler]; - + arguments:argument]; [self.plugin handleMethodCall:call - result:^(id r){ + result:^(id _Nullable result) { + FlutterError *error = result; + XCTAssertEqualObjects(@"storekit_invalid_argument", error.code); + XCTAssertEqualObjects(@"Argument type of addPayment is not a Dictionary", + error.message); + XCTAssertEqualObjects(argument, error.details); + [expectation fulfill]; }]; + [self waitForExpectations:@[ expectation ] timeout:5]; - XCTAssertEqual(transactionForUpdateBlock.transactionState, SKPaymentTransactionStateFailed); } -- (void)testAddPaymentSuccessWithoutPaymentDiscount { +- (void)testAddPaymentShouldReturnFlutterErrorWhenPaymentFails { + NSDictionary *arguments = @{ + @"productIdentifier" : @"123", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : @YES, + }; XCTestExpectation *expectation = - [self expectationWithDescription:@"result should return success state"]; + [self expectationWithDescription:@"Result should return failed state."]; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" - arguments:@{ - @"productIdentifier" : @"123", - @"quantity" : @(1), - @"simulatesAskToBuyInSandbox" : @YES, - }]; - SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; - queue.testState = SKPaymentTransactionStatePurchased; - __block SKPaymentTransaction *transactionForUpdateBlock; - self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray *_Nonnull transactions) { - SKPaymentTransaction *transaction = transactions[0]; - if (transaction.transactionState == SKPaymentTransactionStatePurchased) { - transactionForUpdateBlock = transaction; - if (@available(iOS 12.2, *)) { - XCTAssertNil(transaction.payment.paymentDiscount); - } - [expectation fulfill]; - } - } - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:nil]; - [queue addTransactionObserver:self.plugin.paymentQueueHandler]; + arguments:arguments]; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(NO); + self.plugin.paymentQueueHandler = mockHandler; + [self.plugin handleMethodCall:call - result:^(id r){ + result:^(id _Nullable result) { + FlutterError *error = result; + XCTAssertEqualObjects(@"storekit_duplicate_product_object", error.code); + XCTAssertEqualObjects( + @"There is a pending transaction for the same product identifier. " + @"Please either wait for it to be finished or finish it manually " + @"using `completePurchase` to avoid edge cases.", + error.message); + XCTAssertEqualObjects(arguments, error.details); + [expectation fulfill]; }]; + [self waitForExpectations:@[ expectation ] timeout:5]; - XCTAssertEqual(transactionForUpdateBlock.transactionState, SKPaymentTransactionStatePurchased); + OCMVerify(times(1), [mockHandler addPayment:[OCMArg any]]); } -- (void)testAddPaymentSuccessWithPaymentDiscount { +- (void)testAddPaymentSuccessWithoutPaymentDiscount { XCTestExpectation *expectation = - [self expectationWithDescription:@"result should return success state"]; + [self expectationWithDescription:@"Result should return success state"]; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" arguments:@{ @"productIdentifier" : @"123", @"quantity" : @(1), @"simulatesAskToBuyInSandbox" : @YES, - @"paymentDiscount" : @{ - @"identifier" : @"test_identifier", - @"keyIdentifier" : @"test_key_identifier", - @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", - @"signature" : @"test_signature", - @"timestamp" : @(1635847102), - } }]; - SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; - queue.testState = SKPaymentTransactionStatePurchased; - __block SKPaymentTransaction *transactionForUpdateBlock; - self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray *_Nonnull transactions) { - SKPaymentTransaction *transaction = transactions[0]; - if (transaction.transactionState == SKPaymentTransactionStatePurchased) { - transactionForUpdateBlock = transaction; - if (@available(iOS 12.2, *)) { - SKPaymentDiscount *paymentDiscount = transaction.payment.paymentDiscount; - XCTAssertEqual(paymentDiscount.identifier, @"test_identifier"); - XCTAssertEqual(paymentDiscount.keyIdentifier, @"test_key_identifier"); - XCTAssertEqualObjects( - paymentDiscount.nonce, - [[NSUUID alloc] initWithUUIDString:@"4a11a9cc-3bc3-11ec-8d3d-0242ac130003"]); - XCTAssertEqual(paymentDiscount.signature, @"test_signature"); - XCTAssertEqual(paymentDiscount.timestamp, @(1635847102)); - } - [expectation fulfill]; - } - } - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:nil]; - [queue addTransactionObserver:self.plugin.paymentQueueHandler]; + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); + self.plugin.paymentQueueHandler = mockHandler; [self.plugin handleMethodCall:call - result:^(id r){ + result:^(id _Nullable result) { + XCTAssertNil(result); + [expectation fulfill]; }]; [self waitForExpectations:@[ expectation ] timeout:5]; - XCTAssertEqual(transactionForUpdateBlock.transactionState, SKPaymentTransactionStatePurchased); } -- (void)testAddPaymentFailureWithInvalidPaymentDiscount { +- (void)testAddPaymentSuccessWithPaymentDiscount { XCTestExpectation *expectation = - [self expectationWithDescription:@"result should return success state"]; + [self expectationWithDescription:@"Result should return success state"]; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" arguments:@{ @@ -213,6 +159,7 @@ - (void)testAddPaymentFailureWithInvalidPaymentDiscount { @"quantity" : @(1), @"simulatesAskToBuyInSandbox" : @YES, @"paymentDiscount" : @{ + @"identifier" : @"test_identifier", @"keyIdentifier" : @"test_key_identifier", @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", @"signature" : @"test_signature", @@ -220,28 +167,85 @@ - (void)testAddPaymentFailureWithInvalidPaymentDiscount { } }]; - [self.plugin - handleMethodCall:call - result:^(id r) { - XCTAssertTrue([r isKindOfClass:FlutterError.class]); - FlutterError *result = r; - XCTAssertEqualObjects(result.code, @"storekit_invalid_payment_discount_object"); - XCTAssertEqualObjects(result.message, - @"You have requested a payment and specified a payment " - @"discount with invalid properties. When specifying a " - @"payment discount the 'identifier' field is mandatory."); - XCTAssertEqualObjects(result.details, call.arguments); - [expectation fulfill]; - }]; - + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); + self.plugin.paymentQueueHandler = mockHandler; + [self.plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertNil(result); + [expectation fulfill]; + }]; [self waitForExpectations:@[ expectation ] timeout:5]; + OCMVerify( + times(1), + [mockHandler + addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { + SKPayment *payment = obj; + if (@available(iOS 12.2, *)) { + SKPaymentDiscount *discount = payment.paymentDiscount; + + return [discount.identifier isEqual:@"test_identifier"] && + [discount.keyIdentifier isEqual:@"test_key_identifier"] && + [discount.nonce + isEqual:[[NSUUID alloc] + initWithUUIDString:@"4a11a9cc-3bc3-11ec-8d3d-0242ac130003"]] && + [discount.signature isEqual:@"test_signature"] && + [discount.timestamp isEqual:@(1635847102)]; + } + + return YES; + }]]); +} + +- (void)testAddPaymentFailureWithInvalidPaymentDiscount { + // Support for payment discount is only available on iOS 12.2 and higher. + if (@available(iOS 12.2, *)) { + XCTestExpectation *expectation = + [self expectationWithDescription:@"Result should return success state"]; + NSDictionary *arguments = @{ + @"productIdentifier" : @"123", + @"quantity" : @(1), + @"simulatesAskToBuyInSandbox" : @YES, + @"paymentDiscount" : @{ + @"keyIdentifier" : @"test_key_identifier", + @"nonce" : @"4a11a9cc-3bc3-11ec-8d3d-0242ac130003", + @"signature" : @"test_signature", + @"timestamp" : @(1635847102), + } + }; + FlutterMethodCall *call = + [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" + arguments:arguments]; + + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + id translator = OCMClassMock(FIAObjectTranslator.class); + + NSString *error = @"Some error occurred"; + OCMStub(ClassMethod([translator + getSKPaymentDiscountFromMap:[OCMArg any] + withError:(NSString __autoreleasing **)[OCMArg setTo:error]])) + .andReturn(nil); + self.plugin.paymentQueueHandler = mockHandler; + [self.plugin + handleMethodCall:call + result:^(id _Nullable result) { + FlutterError *error = result; + XCTAssertEqualObjects(@"storekit_invalid_payment_discount_object", error.code); + XCTAssertEqualObjects( + @"You have requested a payment and specified a " + @"payment discount with invalid properties. Some error occurred", + error.message); + XCTAssertEqualObjects(arguments, error.details); + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:5]; + OCMVerify(never(), [mockHandler addPayment:[OCMArg any]]); + } } - (void)testAddPaymentWithNullSandboxArgument { XCTestExpectation *expectation = [self expectationWithDescription:@"result should return success state"]; - XCTestExpectation *simulatesAskToBuyInSandboxExpectation = - [self expectationWithDescription:@"payment isn't simulatesAskToBuyInSandbox"]; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"-[InAppPurchasePlugin addPayment:result:]" arguments:@{ @@ -249,33 +253,19 @@ - (void)testAddPaymentWithNullSandboxArgument { @"quantity" : @(1), @"simulatesAskToBuyInSandbox" : [NSNull null], }]; - SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; - queue.testState = SKPaymentTransactionStatePurchased; - __block SKPaymentTransaction *transactionForUpdateBlock; - self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:^(NSArray *_Nonnull transactions) { - SKPaymentTransaction *transaction = transactions[0]; - if (transaction.transactionState == SKPaymentTransactionStatePurchased) { - transactionForUpdateBlock = transaction; - [expectation fulfill]; - } - if (!transaction.payment.simulatesAskToBuyInSandbox) { - [simulatesAskToBuyInSandboxExpectation fulfill]; - } - } - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:nil]; - [queue addTransactionObserver:self.plugin.paymentQueueHandler]; + FIAPaymentQueueHandler *mockHandler = OCMClassMock(FIAPaymentQueueHandler.class); + OCMStub([mockHandler addPayment:[OCMArg any]]).andReturn(YES); + self.plugin.paymentQueueHandler = mockHandler; [self.plugin handleMethodCall:call - result:^(id r){ + result:^(id _Nullable result) { + XCTAssertNil(result); + [expectation fulfill]; }]; - [self waitForExpectations:@[ expectation, simulatesAskToBuyInSandboxExpectation ] timeout:5]; - XCTAssertEqual(transactionForUpdateBlock.transactionState, SKPaymentTransactionStatePurchased); + [self waitForExpectations:@[ expectation ] timeout:5]; + OCMVerify(times(1), [mockHandler addPayment:[OCMArg checkWithBlock:^BOOL(id obj) { + SKPayment *payment = obj; + return !payment.simulatesAskToBuyInSandbox; + }]]); } - (void)testRestoreTransactions { @@ -297,7 +287,8 @@ - (void)testRestoreTransactions { [expectation fulfill]; } shouldAddStorePayment:nil - updatedDownloads:nil]; + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; [queue addTransactionObserver:self.plugin.paymentQueueHandler]; [self.plugin handleMethodCall:call result:^(id r){ @@ -393,13 +384,15 @@ - (void)testGetPendingTransactions { initWithMap:transactionMap] ]); __block NSArray *resultArray; - self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue - transactionsUpdated:nil - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:nil - updatedDownloads:nil]; + self.plugin.paymentQueueHandler = + [[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue + transactionsUpdated:nil + transactionRemoved:nil + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:nil + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; [self.plugin handleMethodCall:call result:^(id r) { resultArray = r; @@ -409,46 +402,40 @@ - (void)testGetPendingTransactions { XCTAssertEqualObjects(resultArray, @[ transactionMap ]); } -- (void)testStartAndStopObservingPaymentQueue { +- (void)testStartObservingPaymentQueue { + XCTestExpectation *expectation = + [self expectationWithDescription:@"Should return success result"]; FlutterMethodCall *startCall = [FlutterMethodCall methodCallWithMethodName:@"-[SKPaymentQueue startObservingTransactionQueue]" arguments:nil]; - FlutterMethodCall *stopCall = - [FlutterMethodCall methodCallWithMethodName:@"-[SKPaymentQueue stopObservingTransactionQueue]" - arguments:nil]; - - SKPaymentQueueStub *queue = [SKPaymentQueueStub new]; - - self.plugin.paymentQueueHandler = - [[FIAPaymentQueueHandler alloc] initWithQueue:queue - transactionsUpdated:nil - transactionRemoved:nil - restoreTransactionFailed:nil - restoreCompletedTransactionsFinished:nil - shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, - SKProduct *_Nonnull product) { - return YES; - } - updatedDownloads:nil]; - - // Check that there is no observer to start with. - XCTAssertNil(queue.observer); - - // Start observing + FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); + self.plugin.paymentQueueHandler = mockHandler; [self.plugin handleMethodCall:startCall - result:^(id r){ + result:^(id _Nullable result) { + XCTAssertNil(result); + [expectation fulfill]; }]; - // Observer should be set - XCTAssertNotNil(queue.observer); + [self waitForExpectations:@[ expectation ] timeout:5]; + OCMVerify(times(1), [mockHandler startObservingPaymentQueue]); +} - // Stop observing +- (void)testStopObservingPaymentQueue { + XCTestExpectation *expectation = + [self expectationWithDescription:@"Should return success result"]; + FlutterMethodCall *stopCall = + [FlutterMethodCall methodCallWithMethodName:@"-[SKPaymentQueue stopObservingTransactionQueue]" + arguments:nil]; + FIAPaymentQueueHandler *mockHandler = OCMClassMock([FIAPaymentQueueHandler class]); + self.plugin.paymentQueueHandler = mockHandler; [self.plugin handleMethodCall:stopCall - result:^(id r){ + result:^(id _Nullable result) { + XCTAssertNil(result); + [expectation fulfill]; }]; - // No observer should be set - XCTAssertNil(queue.observer); + [self waitForExpectations:@[ expectation ] timeout:5]; + OCMVerify(times(1), [mockHandler stopObservingPaymentQueue]); } - (void)testRegisterPaymentQueueDelegate { @@ -464,7 +451,8 @@ - (void)testRegisterPaymentQueueDelegate { restoreTransactionFailed:nil restoreCompletedTransactionsFinished:nil shouldAddStorePayment:nil - updatedDownloads:nil]; + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; // Verify the delegate is nil before we register one. XCTAssertNil(self.plugin.paymentQueueHandler.delegate); @@ -491,7 +479,8 @@ - (void)testRemovePaymentQueueDelegate { restoreTransactionFailed:nil restoreCompletedTransactionsFinished:nil shouldAddStorePayment:nil - updatedDownloads:nil]; + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; self.plugin.paymentQueueHandler.delegate = OCMProtocolMock(@protocol(SKPaymentQueueDelegate)); // Verify the delegate is not nil before removing it. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/PaymentQueueTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/PaymentQueueTests.m index e267be1da54b..2f8d5857c8d8 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/PaymentQueueTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/PaymentQueueTests.m @@ -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 #import #import "Stubs.h" @@ -59,10 +60,11 @@ - (void)testTransactionPurchased { shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { return YES; } - updatedDownloads:nil]; - [queue addTransactionObserver:handler]; + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; SKPayment *payment = [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; + [handler startObservingPaymentQueue]; [handler addPayment:payment]; [self waitForExpectations:@[ expectation ] timeout:5]; XCTAssertEqual(tran.transactionState, SKPaymentTransactionStatePurchased); @@ -87,10 +89,12 @@ - (void)testTransactionFailed { shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { return YES; } - updatedDownloads:nil]; - [queue addTransactionObserver:handler]; + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + SKPayment *payment = [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; + [handler startObservingPaymentQueue]; [handler addPayment:payment]; [self waitForExpectations:@[ expectation ] timeout:5]; XCTAssertEqual(tran.transactionState, SKPaymentTransactionStateFailed); @@ -115,10 +119,12 @@ - (void)testTransactionRestored { shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { return YES; } - updatedDownloads:nil]; - [queue addTransactionObserver:handler]; + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + SKPayment *payment = [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; + [handler startObservingPaymentQueue]; [handler addPayment:payment]; [self waitForExpectations:@[ expectation ] timeout:5]; XCTAssertEqual(tran.transactionState, SKPaymentTransactionStateRestored); @@ -143,10 +149,12 @@ - (void)testTransactionPurchasing { shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { return YES; } - updatedDownloads:nil]; - [queue addTransactionObserver:handler]; + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; + SKPayment *payment = [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; + [handler startObservingPaymentQueue]; [handler addPayment:payment]; [self waitForExpectations:@[ expectation ] timeout:5]; XCTAssertEqual(tran.transactionState, SKPaymentTransactionStatePurchasing); @@ -171,10 +179,11 @@ - (void)testTransactionDeferred { shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { return YES; } - updatedDownloads:nil]; - [queue addTransactionObserver:handler]; + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; SKPayment *payment = [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; + [handler startObservingPaymentQueue]; [handler addPayment:payment]; [self waitForExpectations:@[ expectation ] timeout:5]; XCTAssertEqual(tran.transactionState, SKPaymentTransactionStateDeferred); @@ -201,12 +210,211 @@ - (void)testFinishTransaction { shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { return YES; } - updatedDownloads:nil]; - [queue addTransactionObserver:handler]; + updatedDownloads:nil + transactionCache:OCMClassMock(FIATransactionCache.class)]; SKPayment *payment = [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; + [handler startObservingPaymentQueue]; [handler addPayment:payment]; [self waitForExpectations:@[ expectation ] timeout:5]; } +- (void)testStartObservingPaymentQueueShouldNotProcessTransactionsWhenCacheIsEmpty { + FIATransactionCache *mockCache = OCMClassMock(FIATransactionCache.class); + FIAPaymentQueueHandler *handler = + [[FIAPaymentQueueHandler alloc] initWithQueue:[[SKPaymentQueueStub alloc] init] + transactionsUpdated:^(NSArray *_Nonnull transactions) { + XCTFail("transactionsUpdated callback should not be called when cache is empty."); + } + transactionRemoved:^(NSArray *_Nonnull transactions) { + XCTFail("transactionRemoved callback should not be called when cache is empty."); + } + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { + return YES; + } + updatedDownloads:^(NSArray *_Nonnull downloads) { + XCTFail("updatedDownloads callback should not be called when cache is empty."); + } + transactionCache:mockCache]; + + [handler startObservingPaymentQueue]; + + OCMVerify(times(1), [mockCache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]); + OCMVerify(times(1), [mockCache getObjectsForKey:TransactionCacheKeyUpdatedDownloads]); + OCMVerify(times(1), [mockCache getObjectsForKey:TransactionCacheKeyRemovedTransactions]); +} + +- (void) + testStartObservingPaymentQueueShouldNotProcessTransactionsWhenCacheContainsEmptyTransactionArrays { + FIATransactionCache *mockCache = OCMClassMock(FIATransactionCache.class); + FIAPaymentQueueHandler *handler = + [[FIAPaymentQueueHandler alloc] initWithQueue:[[SKPaymentQueueStub alloc] init] + transactionsUpdated:^(NSArray *_Nonnull transactions) { + XCTFail("transactionsUpdated callback should not be called when cache is empty."); + } + transactionRemoved:^(NSArray *_Nonnull transactions) { + XCTFail("transactionRemoved callback should not be called when cache is empty."); + } + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { + return YES; + } + updatedDownloads:^(NSArray *_Nonnull downloads) { + XCTFail("updatedDownloads callback should not be called when cache is empty."); + } + transactionCache:mockCache]; + + OCMStub([mockCache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]).andReturn(@[]); + OCMStub([mockCache getObjectsForKey:TransactionCacheKeyUpdatedDownloads]).andReturn(@[]); + OCMStub([mockCache getObjectsForKey:TransactionCacheKeyRemovedTransactions]).andReturn(@[]); + + [handler startObservingPaymentQueue]; + + OCMVerify(times(1), [mockCache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]); + OCMVerify(times(1), [mockCache getObjectsForKey:TransactionCacheKeyUpdatedDownloads]); + OCMVerify(times(1), [mockCache getObjectsForKey:TransactionCacheKeyRemovedTransactions]); +} + +- (void)testStartObservingPaymentQueueShouldProcessTransactionsForItemsInCache { + XCTestExpectation *updateTransactionsExpectation = + [self expectationWithDescription: + @"transactionsUpdated callback should be called with one transaction."]; + XCTestExpectation *removeTransactionsExpectation = + [self expectationWithDescription: + @"transactionsRemoved callback should be called with one transaction."]; + XCTestExpectation *updateDownloadsExpectation = + [self expectationWithDescription: + @"downloadsUpdated callback should be called with one transaction."]; + SKPaymentTransaction *mockTransaction = OCMClassMock(SKPaymentTransaction.class); + SKDownload *mockDownload = OCMClassMock(SKDownload.class); + FIATransactionCache *mockCache = OCMClassMock(FIATransactionCache.class); + FIAPaymentQueueHandler *handler = + [[FIAPaymentQueueHandler alloc] initWithQueue:[[SKPaymentQueueStub alloc] init] + transactionsUpdated:^(NSArray *_Nonnull transactions) { + XCTAssertEqualObjects(transactions, @[ mockTransaction ]); + [updateTransactionsExpectation fulfill]; + } + transactionRemoved:^(NSArray *_Nonnull transactions) { + XCTAssertEqualObjects(transactions, @[ mockTransaction ]); + [removeTransactionsExpectation fulfill]; + } + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { + return YES; + } + updatedDownloads:^(NSArray *_Nonnull downloads) { + XCTAssertEqualObjects(downloads, @[ mockDownload ]); + [updateDownloadsExpectation fulfill]; + } + transactionCache:mockCache]; + + OCMStub([mockCache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]).andReturn(@[ + mockTransaction + ]); + OCMStub([mockCache getObjectsForKey:TransactionCacheKeyUpdatedDownloads]).andReturn(@[ + mockDownload + ]); + OCMStub([mockCache getObjectsForKey:TransactionCacheKeyRemovedTransactions]).andReturn(@[ + mockTransaction + ]); + + [handler startObservingPaymentQueue]; + + [self waitForExpectations:@[ + updateTransactionsExpectation, removeTransactionsExpectation, updateDownloadsExpectation + ] + timeout:5]; + OCMVerify(times(1), [mockCache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]); + OCMVerify(times(1), [mockCache getObjectsForKey:TransactionCacheKeyUpdatedDownloads]); + OCMVerify(times(1), [mockCache getObjectsForKey:TransactionCacheKeyRemovedTransactions]); + OCMVerify(times(1), [mockCache clear]); +} + +- (void)testTransactionsShouldBeCachedWhenNotObserving { + SKPaymentQueueStub *queue = [[SKPaymentQueueStub alloc] init]; + FIATransactionCache *mockCache = OCMClassMock(FIATransactionCache.class); + FIAPaymentQueueHandler *handler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue + transactionsUpdated:^(NSArray *_Nonnull transactions) { + XCTFail("transactionsUpdated callback should not be called when cache is empty."); + } + transactionRemoved:^(NSArray *_Nonnull transactions) { + XCTFail("transactionRemoved callback should not be called when cache is empty."); + } + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { + return YES; + } + updatedDownloads:^(NSArray *_Nonnull downloads) { + XCTFail("updatedDownloads callback should not be called when cache is empty."); + } + transactionCache:mockCache]; + + SKPayment *payment = + [SKPayment paymentWithProduct:[[SKProductStub alloc] initWithMap:self.productResponseMap]]; + [handler addPayment:payment]; + + OCMVerify(times(1), [mockCache addObjects:[OCMArg any] + forKey:TransactionCacheKeyUpdatedTransactions]); + OCMVerify(never(), [mockCache addObjects:[OCMArg any] + forKey:TransactionCacheKeyUpdatedDownloads]); + OCMVerify(never(), [mockCache addObjects:[OCMArg any] + forKey:TransactionCacheKeyRemovedTransactions]); +} + +- (void)testTransactionsShouldNotBeCachedWhenObserving { + XCTestExpectation *updateTransactionsExpectation = + [self expectationWithDescription: + @"transactionsUpdated callback should be called with one transaction."]; + XCTestExpectation *removeTransactionsExpectation = + [self expectationWithDescription: + @"transactionsRemoved callback should be called with one transaction."]; + XCTestExpectation *updateDownloadsExpectation = + [self expectationWithDescription: + @"downloadsUpdated callback should be called with one transaction."]; + SKPaymentTransaction *mockTransaction = OCMClassMock(SKPaymentTransaction.class); + SKDownload *mockDownload = OCMClassMock(SKDownload.class); + SKPaymentQueueStub *queue = [[SKPaymentQueueStub alloc] init]; + queue.testState = SKPaymentTransactionStatePurchased; + FIATransactionCache *mockCache = OCMClassMock(FIATransactionCache.class); + FIAPaymentQueueHandler *handler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue + transactionsUpdated:^(NSArray *_Nonnull transactions) { + XCTAssertEqualObjects(transactions, @[ mockTransaction ]); + [updateTransactionsExpectation fulfill]; + } + transactionRemoved:^(NSArray *_Nonnull transactions) { + XCTAssertEqualObjects(transactions, @[ mockTransaction ]); + [removeTransactionsExpectation fulfill]; + } + restoreTransactionFailed:nil + restoreCompletedTransactionsFinished:nil + shouldAddStorePayment:^BOOL(SKPayment *_Nonnull payment, SKProduct *_Nonnull product) { + return YES; + } + updatedDownloads:^(NSArray *_Nonnull downloads) { + XCTAssertEqualObjects(downloads, @[ mockDownload ]); + [updateDownloadsExpectation fulfill]; + } + transactionCache:mockCache]; + + [handler startObservingPaymentQueue]; + [handler paymentQueue:queue updatedTransactions:@[ mockTransaction ]]; + [handler paymentQueue:queue removedTransactions:@[ mockTransaction ]]; + [handler paymentQueue:queue updatedDownloads:@[ mockDownload ]]; + + [self waitForExpectations:@[ + updateTransactionsExpectation, removeTransactionsExpectation, updateDownloadsExpectation + ] + timeout:5]; + OCMVerify(never(), [mockCache addObjects:[OCMArg any] + forKey:TransactionCacheKeyUpdatedTransactions]); + OCMVerify(never(), [mockCache addObjects:[OCMArg any] + forKey:TransactionCacheKeyUpdatedDownloads]); + OCMVerify(never(), [mockCache addObjects:[OCMArg any] + forKey:TransactionCacheKeyRemovedTransactions]); +} @end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAPaymentQueueHandler.h b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAPaymentQueueHandler.h index 8019831d6355..bb074aa6c577 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAPaymentQueueHandler.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAPaymentQueueHandler.h @@ -4,6 +4,7 @@ #import #import +#import "FIATransactionCache.h" @class SKPaymentTransaction; @@ -21,6 +22,27 @@ typedef void (^UpdatedDownloads)(NSArray *downloads); @property(NS_NONATOMIC_IOSONLY, weak, nullable) id delegate API_AVAILABLE( ios(13.0), macos(10.15), watchos(6.2)); +/// Creates a new FIAPaymentQueueHandler initialized with an empty +/// FIATransactionCache. +/// +/// @param queue The SKPaymentQueue instance connected to the App Store and +/// responsible for processing transactions. +/// @param transactionsUpdated Callback method that is called each time the App +/// Store indicates transactions are updated. +/// @param transactionsRemoved Callback method that is called each time the App +/// Store indicates transactions are removed. +/// @param restoreTransactionFailed Callback method that is called each time +/// the App Store indicates transactions failed +/// to restore. +/// @param restoreCompletedTransactionsFinished Callback method that is called +/// each time the App Store +/// indicates restoring of +/// transactions has finished. +/// @param shouldAddStorePayment Callback method that is called each time an +/// in-app purchase has been initiated from the +/// App Store. +/// @param updatedDownloads Callback method that is called each time the App +/// Store indicates downloads are updated. - (instancetype)initWithQueue:(nonnull SKPaymentQueue *)queue transactionsUpdated:(nullable TransactionsUpdated)transactionsUpdated transactionRemoved:(nullable TransactionsRemoved)transactionsRemoved @@ -28,7 +50,57 @@ typedef void (^UpdatedDownloads)(NSArray *downloads); restoreCompletedTransactionsFinished: (nullable RestoreCompletedTransactionsFinished)restoreCompletedTransactionsFinished shouldAddStorePayment:(nullable ShouldAddStorePayment)shouldAddStorePayment - updatedDownloads:(nullable UpdatedDownloads)updatedDownloads; + updatedDownloads:(nullable UpdatedDownloads)updatedDownloads + DEPRECATED_MSG_ATTRIBUTE( + "Use the " + "'initWithQueue:transactionsUpdated:transactionsRemoved:restoreTransactionsFinished:" + "shouldAddStorePayment:updatedDownloads:transactionCache:' message instead."); + +/// Creates a new FIAPaymentQueueHandler. +/// +/// The "transactionsUpdated", "transactionsRemoved" and "updatedDownloads" +/// callbacks are only called while actively observing transactions. To start +/// observing transactions send the "startObservingPaymentQueue" message. +/// Sending the "stopObservingPaymentQueue" message will stop actively +/// observing transactions. When transactions are not observed they are cached +/// to the "transactionCache" and will be delivered via the +/// "transactionsUpdated", "transactionsRemoved" and "updatedDownloads" +/// callbacks as soon as the "startObservingPaymentQueue" message arrives. +/// +/// Note: cached transactions that are not processed when the application is +/// killed will be delivered again by the App Store as soon as the application +/// starts again. +/// +/// @param queue The SKPaymentQueue instance connected to the App Store and +/// responsible for processing transactions. +/// @param transactionsUpdated Callback method that is called each time the App +/// Store indicates transactions are updated. +/// @param transactionsRemoved Callback method that is called each time the App +/// Store indicates transactions are removed. +/// @param restoreTransactionFailed Callback method that is called each time +/// the App Store indicates transactions failed +/// to restore. +/// @param restoreCompletedTransactionsFinished Callback method that is called +/// each time the App Store +/// indicates restoring of +/// transactions has finished. +/// @param shouldAddStorePayment Callback method that is called each time an +/// in-app purchase has been initiated from the +/// App Store. +/// @param updatedDownloads Callback method that is called each time the App +/// Store indicates downloads are updated. +/// @param transactionCache An empty [FIATransactionCache] instance that is +/// responsible for keeping track of transactions that +/// arrive when not actively observing transactions. +- (instancetype)initWithQueue:(nonnull SKPaymentQueue *)queue + transactionsUpdated:(nullable TransactionsUpdated)transactionsUpdated + transactionRemoved:(nullable TransactionsRemoved)transactionsRemoved + restoreTransactionFailed:(nullable RestoreTransactionFailed)restoreTransactionFailed + restoreCompletedTransactionsFinished: + (nullable RestoreCompletedTransactionsFinished)restoreCompletedTransactionsFinished + shouldAddStorePayment:(nullable ShouldAddStorePayment)shouldAddStorePayment + updatedDownloads:(nullable UpdatedDownloads)updatedDownloads + transactionCache:(nonnull FIATransactionCache *)transactionCache; // Can throw exceptions if the transaction type is purchasing, should always used in a @try block. - (void)finishTransaction:(nonnull SKPaymentTransaction *)transaction; - (void)restoreTransactions:(nullable NSString *)applicationName; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAPaymentQueueHandler.m b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAPaymentQueueHandler.m index 21667954cf8d..59fdceded2bc 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAPaymentQueueHandler.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAPaymentQueueHandler.m @@ -4,18 +4,49 @@ #import "FIAPaymentQueueHandler.h" #import "FIAPPaymentQueueDelegate.h" +#import "FIATransactionCache.h" @interface FIAPaymentQueueHandler () +/// The SKPaymentQueue instance connected to the App Store and responsible for processing +/// transactions. @property(strong, nonatomic) SKPaymentQueue *queue; + +/// Callback method that is called each time the App Store indicates transactions are updated. @property(nullable, copy, nonatomic) TransactionsUpdated transactionsUpdated; + +/// Callback method that is called each time the App Store indicates transactions are removed. @property(nullable, copy, nonatomic) TransactionsRemoved transactionsRemoved; + +/// Callback method that is called each time the App Store indicates transactions failed to restore. @property(nullable, copy, nonatomic) RestoreTransactionFailed restoreTransactionFailed; + +/// Callback method that is called each time the App Store indicates restoring of transactions has +/// finished. @property(nullable, copy, nonatomic) RestoreCompletedTransactionsFinished paymentQueueRestoreCompletedTransactionsFinished; + +/// Callback method that is called each time an in-app purchase has been initiated from the App +/// Store. @property(nullable, copy, nonatomic) ShouldAddStorePayment shouldAddStorePayment; + +/// Callback method that is called each time the App Store indicates downloads are updated. @property(nullable, copy, nonatomic) UpdatedDownloads updatedDownloads; +/// The transaction cache responsible for caching transactions. +/// +/// Keeps track of transactions that arrive when the Flutter client is not +/// actively observing for transactions. +@property(strong, nonatomic, nonnull) FIATransactionCache *transactionCache; + +/// Indicates if the Flutter client is observing transactions. +/// +/// When the client is not observing, transactions are cached and send to the +/// client as soon as it starts observing. The Flutter client can start +/// observing by sending a startObservingPaymentQueue message and stop by +/// sending a stopObservingPaymentQueue message. +@property(atomic, assign, readwrite, getter=isObservingTransactions) BOOL observingTransactions; + @end @implementation FIAPaymentQueueHandler @@ -28,6 +59,25 @@ - (instancetype)initWithQueue:(nonnull SKPaymentQueue *)queue (nullable RestoreCompletedTransactionsFinished)restoreCompletedTransactionsFinished shouldAddStorePayment:(nullable ShouldAddStorePayment)shouldAddStorePayment updatedDownloads:(nullable UpdatedDownloads)updatedDownloads { + return [[FIAPaymentQueueHandler alloc] initWithQueue:queue + transactionsUpdated:transactionsUpdated + transactionRemoved:transactionsRemoved + restoreTransactionFailed:restoreTransactionFailed + restoreCompletedTransactionsFinished:restoreCompletedTransactionsFinished + shouldAddStorePayment:shouldAddStorePayment + updatedDownloads:updatedDownloads + transactionCache:[[FIATransactionCache alloc] init]]; +} + +- (instancetype)initWithQueue:(nonnull SKPaymentQueue *)queue + transactionsUpdated:(nullable TransactionsUpdated)transactionsUpdated + transactionRemoved:(nullable TransactionsRemoved)transactionsRemoved + restoreTransactionFailed:(nullable RestoreTransactionFailed)restoreTransactionFailed + restoreCompletedTransactionsFinished: + (nullable RestoreCompletedTransactionsFinished)restoreCompletedTransactionsFinished + shouldAddStorePayment:(nullable ShouldAddStorePayment)shouldAddStorePayment + updatedDownloads:(nullable UpdatedDownloads)updatedDownloads + transactionCache:(nonnull FIATransactionCache *)transactionCache { self = [super init]; if (self) { _queue = queue; @@ -37,7 +87,9 @@ - (instancetype)initWithQueue:(nonnull SKPaymentQueue *)queue _paymentQueueRestoreCompletedTransactionsFinished = restoreCompletedTransactionsFinished; _shouldAddStorePayment = shouldAddStorePayment; _updatedDownloads = updatedDownloads; + _transactionCache = transactionCache; + [_queue addTransactionObserver:self]; if (@available(iOS 13.0, macOS 10.15, *)) { queue.delegate = self.delegate; } @@ -46,11 +98,43 @@ - (instancetype)initWithQueue:(nonnull SKPaymentQueue *)queue } - (void)startObservingPaymentQueue { - [_queue addTransactionObserver:self]; + self.observingTransactions = YES; + + [self processCachedTransactions]; } - (void)stopObservingPaymentQueue { - [_queue removeTransactionObserver:self]; + // When the client stops observing transaction, the transaction observer is + // not removed from the SKPaymentQueue. The FIAPaymentQueueHandler will cache + // trasnactions in memory when the client is not observing, allowing the app + // to process these transactions if it starts observing again during the same + // lifetime of the app. + // + // If the app is killed, cached transactions will be removed from memory; + // however, the App Store will re-deliver the transactions as soon as the app + // is started again, since the cached transactions have not been acknowledged + // by the client (by sending the `finishTransaction` message). + self.observingTransactions = NO; +} + +- (void)processCachedTransactions { + NSArray *cachedObjects = + [self.transactionCache getObjectsForKey:TransactionCacheKeyUpdatedTransactions]; + if (cachedObjects.count != 0) { + self.transactionsUpdated(cachedObjects); + } + + cachedObjects = [self.transactionCache getObjectsForKey:TransactionCacheKeyUpdatedDownloads]; + if (cachedObjects.count != 0) { + self.updatedDownloads(cachedObjects); + } + + cachedObjects = [self.transactionCache getObjectsForKey:TransactionCacheKeyRemovedTransactions]; + if (cachedObjects.count != 0) { + self.transactionsRemoved(cachedObjects); + } + + [self.transactionCache clear]; } - (BOOL)addPayment:(SKPayment *)payment { @@ -93,6 +177,11 @@ - (void)showPriceConsentIfNeeded { // state of transactions and finish as appropriate. - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { + if (!self.observingTransactions) { + [_transactionCache addObjects:transactions forKey:TransactionCacheKeyUpdatedTransactions]; + return; + } + // notify dart through callbacks. self.transactionsUpdated(transactions); } @@ -100,6 +189,10 @@ - (void)paymentQueue:(SKPaymentQueue *)queue // Sent when transactions are removed from the queue (via finishTransaction:). - (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions { + if (!self.observingTransactions) { + [_transactionCache addObjects:transactions forKey:TransactionCacheKeyRemovedTransactions]; + return; + } self.transactionsRemoved(transactions); } @@ -118,6 +211,10 @@ - (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue // Sent when the download state has changed. - (void)paymentQueue:(SKPaymentQueue *)queue updatedDownloads:(NSArray *)downloads { + if (!self.observingTransactions) { + [_transactionCache addObjects:downloads forKey:TransactionCacheKeyUpdatedDownloads]; + return; + } self.updatedDownloads(downloads); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIATransactionCache.h b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIATransactionCache.h new file mode 100644 index 000000000000..dea3c2d85d14 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIATransactionCache.h @@ -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. + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, TransactionCacheKey) { + TransactionCacheKeyUpdatedDownloads, + TransactionCacheKeyUpdatedTransactions, + TransactionCacheKeyRemovedTransactions +}; + +@interface FIATransactionCache : NSObject + +/// Adds objects to the transaction cache. +/// +/// If the cache already contains an array of objects on the specified key, the supplied +/// array will be appended to the existing array. +- (void)addObjects:(NSArray *)objects forKey:(TransactionCacheKey)key; + +/// Gets the array of objects stored at the given key. +/// +/// If there are no objects associated with the given key nil is returned. +- (NSArray *)getObjectsForKey:(TransactionCacheKey)key; + +/// Removes all objects from the transaction cache. +- (void)clear; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIATransactionCache.m b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIATransactionCache.m new file mode 100644 index 000000000000..f80b9c40c7bc --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIATransactionCache.m @@ -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 "FIATransactionCache.h" + +@interface FIATransactionCache () + +/// A NSMutableDictionary storing the objects that are cached. +@property(nonatomic, strong, nonnull) NSMutableDictionary *cache; + +@end + +@implementation FIATransactionCache + +- (instancetype)init { + self = [super init]; + if (self) { + self.cache = [[NSMutableDictionary alloc] init]; + } + + return self; +} + +- (void)addObjects:(NSArray *)objects forKey:(TransactionCacheKey)key { + NSArray *cachedObjects = self.cache[@(key)]; + + self.cache[@(key)] = + cachedObjects ? [cachedObjects arrayByAddingObjectsFromArray:objects] : objects; +} + +- (NSArray *)getObjectsForKey:(TransactionCacheKey)key { + return self.cache[@(key)]; +} + +- (void)clear { + [self.cache removeAllObjects]; +} + +@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m index 661f57f432d8..a580a46b011d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m @@ -79,7 +79,8 @@ - (instancetype)initWithRegistrar:(NSObject *)registrar } updatedDownloads:^void(NSArray *_Nonnull downloads) { [weakSelf updatedDownloads:downloads]; - }]; + } + transactionCache:[[FIATransactionCache alloc] init]]; _transactionObserverCallbackChannel = [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/in_app_purchase" 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 7e701686e00d..51b5ce7f1614 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 platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/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.0+2 +version: 0.3.0+3 environment: sdk: ">=2.14.0 <3.0.0" From fef72e08f12e43a3ac1a7f90eea142bd683fe2c7 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 18 Mar 2022 11:10:25 -0400 Subject: [PATCH 048/844] [flutter_plugin_tool] Add custom-test command (#5058) --- script/tool/CHANGELOG.md | 3 +- script/tool/lib/src/custom_test_command.dart | 77 +++++ script/tool/lib/src/main.dart | 2 + script/tool/pubspec.yaml | 2 +- .../tool/test/custom_test_command_test.dart | 281 ++++++++++++++++++ 5 files changed, 363 insertions(+), 2 deletions(-) create mode 100644 script/tool/lib/src/custom_test_command.dart create mode 100644 script/tool/test/custom_test_command_test.dart diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 7e950352facb..214eb68b2f21 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.8.2 +- Adds a new `custom-test` command. - Switches from deprecated `flutter packages` alias to `flutter pub`. ## 0.8.1 diff --git a/script/tool/lib/src/custom_test_command.dart b/script/tool/lib/src/custom_test_command.dart new file mode 100644 index 000000000000..1c1dfc068a11 --- /dev/null +++ b/script/tool/lib/src/custom_test_command.dart @@ -0,0 +1,77 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file/file.dart'; +import 'package:platform/platform.dart'; + +import 'common/package_looping_command.dart'; +import 'common/process_runner.dart'; +import 'common/repository_package.dart'; + +const String _scriptName = 'run_tests.dart'; +const String _legacyScriptName = 'run_tests.sh'; + +/// A command to run custom, package-local tests on packages. +/// +/// This is an escape hatch for adding tests that this tooling doesn't support. +/// It should be used sparingly; prefer instead to add functionality to this +/// tooling to eliminate the need for bespoke tests. +class CustomTestCommand extends PackageLoopingCommand { + /// Creates a custom test command instance. + CustomTestCommand( + Directory packagesDir, { + ProcessRunner processRunner = const ProcessRunner(), + Platform platform = const LocalPlatform(), + }) : super(packagesDir, processRunner: processRunner, platform: platform); + + @override + final String name = 'custom-test'; + + @override + final String description = 'Runs package-specific custom tests defined in ' + 'a package\'s tool/$_scriptName file.\n\n' + 'This command requires "dart" to be in your path.'; + + @override + Future runForPackage(RepositoryPackage package) async { + final File script = + package.directory.childDirectory('tool').childFile(_scriptName); + final File legacyScript = package.directory.childFile(_legacyScriptName); + String? customSkipReason; + bool ranTests = false; + + // Run the custom Dart script if presest. + if (script.existsSync()) { + final int exitCode = await processRunner.runAndStream( + 'dart', ['run', 'tool/$_scriptName'], + workingDir: package.directory); + if (exitCode != 0) { + return PackageResult.fail(); + } + ranTests = true; + } + + // Run the legacy script if present. + if (legacyScript.existsSync()) { + if (platform.isWindows) { + customSkipReason = '$_legacyScriptName is not supported on Windows. ' + 'Please migrate to $_scriptName.'; + } else { + final int exitCode = await processRunner.runAndStream( + legacyScript.path, [], + workingDir: package.directory); + if (exitCode != 0) { + return PackageResult.fail(); + } + ranTests = true; + } + } + + if (!ranTests) { + return PackageResult.skip(customSkipReason ?? 'No custom tests'); + } + + return PackageResult.success(); + } +} diff --git a/script/tool/lib/src/main.dart b/script/tool/lib/src/main.dart index 3e8f19b044dd..5a71a0a8889d 100644 --- a/script/tool/lib/src/main.dart +++ b/script/tool/lib/src/main.dart @@ -12,6 +12,7 @@ import 'analyze_command.dart'; import 'build_examples_command.dart'; import 'common/core.dart'; import 'create_all_plugins_app_command.dart'; +import 'custom_test_command.dart'; import 'drive_examples_command.dart'; import 'federation_safety_check_command.dart'; import 'firebase_test_lab_command.dart'; @@ -50,6 +51,7 @@ void main(List args) { ..addCommand(AnalyzeCommand(packagesDir)) ..addCommand(BuildExamplesCommand(packagesDir)) ..addCommand(CreateAllPluginsAppCommand(packagesDir)) + ..addCommand(CustomTestCommand(packagesDir)) ..addCommand(DriveExamplesCommand(packagesDir)) ..addCommand(FederationSafetyCheckCommand(packagesDir)) ..addCommand(FirebaseTestLabCommand(packagesDir)) diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 93a1a87ca337..f005c565be17 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.8.1 +version: 0.8.2 dependencies: args: ^2.1.0 diff --git a/script/tool/test/custom_test_command_test.dart b/script/tool/test/custom_test_command_test.dart new file mode 100644 index 000000000000..6a34c2689f6b --- /dev/null +++ b/script/tool/test/custom_test_command_test.dart @@ -0,0 +1,281 @@ +// 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:io' as io; + +import 'package:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_plugin_tools/src/common/core.dart'; +import 'package:flutter_plugin_tools/src/custom_test_command.dart'; +import 'package:test/test.dart'; + +import 'mocks.dart'; +import 'util.dart'; + +void main() { + late FileSystem fileSystem; + late MockPlatform mockPlatform; + late Directory packagesDir; + late RecordingProcessRunner processRunner; + late CommandRunner runner; + + group('posix', () { + setUp(() { + fileSystem = MemoryFileSystem(); + mockPlatform = MockPlatform(); + packagesDir = createPackagesDirectory(fileSystem: fileSystem); + processRunner = RecordingProcessRunner(); + final CustomTestCommand analyzeCommand = CustomTestCommand( + packagesDir, + processRunner: processRunner, + platform: mockPlatform, + ); + + runner = CommandRunner( + 'custom_test_command', 'Test for custom_test_command'); + runner.addCommand(analyzeCommand); + }); + + test('runs both new and legacy when both are present', () async { + final Directory package = + createFakePlugin('a_package', packagesDir, extraFiles: [ + 'tool/run_tests.dart', + 'run_tests.sh', + ]); + + final List output = + await runCapturingPrint(runner, ['custom-test']); + + expect( + processRunner.recordedCalls, + containsAll([ + ProcessCall(package.childFile('run_tests.sh').path, + const [], package.path), + ProcessCall('dart', const ['run', 'tool/run_tests.dart'], + package.path), + ])); + + expect( + output, + containsAllInOrder([ + contains('Ran for 1 package(s)'), + ])); + }); + + test('runs when only new is present', () async { + final Directory package = createFakePlugin('a_package', packagesDir, + extraFiles: ['tool/run_tests.dart']); + + final List output = + await runCapturingPrint(runner, ['custom-test']); + + expect( + processRunner.recordedCalls, + containsAll([ + ProcessCall('dart', const ['run', 'tool/run_tests.dart'], + package.path), + ])); + + expect( + output, + containsAllInOrder([ + contains('Ran for 1 package(s)'), + ])); + }); + + test('runs when only legacy is present', () async { + final Directory package = createFakePlugin('a_package', packagesDir, + extraFiles: ['run_tests.sh']); + + final List output = + await runCapturingPrint(runner, ['custom-test']); + + expect( + processRunner.recordedCalls, + containsAll([ + ProcessCall(package.childFile('run_tests.sh').path, + const [], package.path), + ])); + + expect( + output, + containsAllInOrder([ + contains('Ran for 1 package(s)'), + ])); + }); + + test('skips when neither is present', () async { + createFakePlugin('a_package', packagesDir); + + final List output = + await runCapturingPrint(runner, ['custom-test']); + + expect(processRunner.recordedCalls, isEmpty); + + expect( + output, + containsAllInOrder([ + contains('Skipped 1 package(s)'), + ])); + }); + + test('fails if new fails', () async { + createFakePlugin('a_package', packagesDir, extraFiles: [ + 'tool/run_tests.dart', + 'run_tests.sh', + ]); + + processRunner.mockProcessesForExecutable['dart'] = [ + MockProcess(exitCode: 1), + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['custom-test'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The following packages had errors:'), + contains('a_package') + ])); + }); + + test('fails if legacy fails', () async { + final Directory package = + createFakePlugin('a_package', packagesDir, extraFiles: [ + 'tool/run_tests.dart', + 'run_tests.sh', + ]); + + processRunner.mockProcessesForExecutable[ + package.childFile('run_tests.sh').path] = [ + MockProcess(exitCode: 1), + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['custom-test'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The following packages had errors:'), + contains('a_package') + ])); + }); + }); + + group('Windows', () { + setUp(() { + fileSystem = MemoryFileSystem(style: FileSystemStyle.windows); + mockPlatform = MockPlatform(isWindows: true); + packagesDir = createPackagesDirectory(fileSystem: fileSystem); + processRunner = RecordingProcessRunner(); + final CustomTestCommand analyzeCommand = CustomTestCommand( + packagesDir, + processRunner: processRunner, + platform: mockPlatform, + ); + + runner = CommandRunner( + 'custom_test_command', 'Test for custom_test_command'); + runner.addCommand(analyzeCommand); + }); + + test('runs new and skips old when both are present', () async { + final Directory package = + createFakePlugin('a_package', packagesDir, extraFiles: [ + 'tool/run_tests.dart', + 'run_tests.sh', + ]); + + final List output = + await runCapturingPrint(runner, ['custom-test']); + + expect( + processRunner.recordedCalls, + containsAll([ + ProcessCall('dart', const ['run', 'tool/run_tests.dart'], + package.path), + ])); + + expect( + output, + containsAllInOrder([ + contains('Ran for 1 package(s)'), + ])); + }); + + test('runs when only new is present', () async { + final Directory package = createFakePlugin('a_package', packagesDir, + extraFiles: ['tool/run_tests.dart']); + + final List output = + await runCapturingPrint(runner, ['custom-test']); + + expect( + processRunner.recordedCalls, + containsAll([ + ProcessCall('dart', const ['run', 'tool/run_tests.dart'], + package.path), + ])); + + expect( + output, + containsAllInOrder([ + contains('Ran for 1 package(s)'), + ])); + }); + + test('skips package when only legacy is present', () async { + createFakePlugin('a_package', packagesDir, + extraFiles: ['run_tests.sh']); + + final List output = + await runCapturingPrint(runner, ['custom-test']); + + expect(processRunner.recordedCalls, isEmpty); + + expect( + output, + containsAllInOrder([ + contains('run_tests.sh is not supported on Windows'), + contains('Skipped 1 package(s)'), + ])); + }); + + test('fails if new fails', () async { + createFakePlugin('a_package', packagesDir, extraFiles: [ + 'tool/run_tests.dart', + 'run_tests.sh', + ]); + + processRunner.mockProcessesForExecutable['dart'] = [ + MockProcess(exitCode: 1), + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['custom-test'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The following packages had errors:'), + contains('a_package') + ])); + }); + }); +} From d922d349154b1306b5a6d04848184cda19b41c37 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 18 Mar 2022 12:00:24 -0400 Subject: [PATCH 049/844] Roll Flutter from 509ddfda5c1b to 1c2c9421121f (1 revision) (#5067) --- .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 09a4cea57f76..4270a8dd49ef 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -509ddfda5c1bda7c01cf51fe24607da19423d5f9 +1c2c9421121f345392162e3dde0a784d0cbdb69b From 466dc0f04829a6fba14e5b8a2f5455c0e9bfeb90 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 18 Mar 2022 15:15:18 -0400 Subject: [PATCH 050/844] Roll Flutter from 1c2c9421121f to a217bbb6defe (46 revisions) (#5072) --- .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 4270a8dd49ef..ab7480eb27b4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1c2c9421121f345392162e3dde0a784d0cbdb69b +a217bbb6defed97c84bc26372193f5617ea683e3 From ef314fe762f86b691f68634f53f2f87e9618b81d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 18 Mar 2022 17:25:23 -0400 Subject: [PATCH 051/844] Roll Flutter from a217bbb6defe to 5b3e42afd019 (5 revisions) (#5074) --- .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 ab7480eb27b4..02d26f6161c8 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a217bbb6defed97c84bc26372193f5617ea683e3 +5b3e42afd0195fb7e633b751ec39eeb9169efa72 From 9f7e31236d8a8bf643e55f8d4691053c4799f10d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 19 Mar 2022 02:15:15 -0400 Subject: [PATCH 052/844] Roll Flutter from 5b3e42afd019 to 1de874e7364a (6 revisions) (#5080) --- .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 02d26f6161c8..48198acf04f3 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -5b3e42afd0195fb7e633b751ec39eeb9169efa72 +1de874e7364ade61c7735a96969ea1e4419e325a From 085a5dc19e80a077404a5f7b92030360275ee2e9 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 19 Mar 2022 11:50:23 -0400 Subject: [PATCH 053/844] Roll Flutter from 1de874e7364a to 2c1dbba9344c (2 revisions) (#5083) --- .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 48198acf04f3..07d3daa46075 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1de874e7364ade61c7735a96969ea1e4419e325a +2c1dbba9344ce96128cedd69015f1cea56948f9d From 12af8a349c3b74c6dbd3aeef7f8d9b5101d6f73e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 19 Mar 2022 16:40:13 -0400 Subject: [PATCH 054/844] Roll Flutter from 2c1dbba9344c to d1c11213493c (2 revisions) (#5085) --- .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 07d3daa46075..18689a2bdcc2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2c1dbba9344ce96128cedd69015f1cea56948f9d +d1c11213493c4dd1add018dd26b6238ad40efb41 From 1c61a217014c32a7d1d09436becd39f84504b5d2 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 20 Mar 2022 02:50:25 -0400 Subject: [PATCH 055/844] Roll Flutter from d1c11213493c to 6af40a7004f8 (1 revision) (#5089) --- .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 18689a2bdcc2..655584a08576 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -d1c11213493c4dd1add018dd26b6238ad40efb41 +6af40a7004f886c8b8b87475a40107611bc5bb0a From 5b49d0f76c9f9b3aa8d578a4bb0282981d226653 Mon Sep 17 00:00:00 2001 From: Guy Kogus Date: Mon, 21 Mar 2022 19:55:19 +0100 Subject: [PATCH 056/844] [google_maps_flutter] Fix iOS crash by observing map frame change only once (#3426) --- .../google_maps_flutter/CHANGELOG.md | 4 +++ .../google_maps_flutter/example/ios/Podfile | 2 ++ .../ios/Runner.xcodeproj/project.pbxproj | 10 +++++- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../example/ios/RunnerTests/GoogleMapsTests.m | 24 +++++++++++++ .../ios/RunnerTests/PartiallyMockedMapView.h | 17 ++++++++++ .../ios/RunnerTests/PartiallyMockedMapView.m | 34 +++++++++++++++++++ .../ios/Classes/GoogleMapController.m | 24 +++++++------ .../ios/Classes/GoogleMapController_Test.h | 27 +++++++++++++++ .../Classes/google_maps_flutter-umbrella.h | 11 ++++++ .../ios/Classes/google_maps_flutter.modulemap | 10 ++++++ .../ios/google_maps_flutter.podspec | 3 +- .../google_maps_flutter/pubspec.yaml | 2 +- 13 files changed, 155 insertions(+), 15 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.h create mode 100644 packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.m create mode 100644 packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController_Test.h create mode 100644 packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter-umbrella.h create mode 100644 packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter.modulemap diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 565bf8412c61..f05de47b3036 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.3 + +* Fixes iOS crash on `EXC_BAD_ACCESS KERN_PROTECTION_FAILURE` if the map frame changes long after creation. + ## 2.1.2 * Removes dependencies from `pubspec.yaml` that are only needed in `example/pubspec.yaml` diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile b/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile index 9686afaf3c99..29bfe631a3e7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile @@ -31,6 +31,8 @@ target 'Runner' do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) target 'RunnerTests' do inherit! :search_paths + + pod 'OCMock', '~> 3.9.1' end end diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj index fbb006aeded0..6a0466c3c6d9 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -15,6 +15,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 */; }; + 982F2A6C27BADE17003C81F4 /* PartiallyMockedMapView.m in Sources */ = {isa = PBXBuildFile; fileRef = 982F2A6B27BADE17003C81F4 /* PartiallyMockedMapView.m */; }; F7151F13265D7ED70028CB91 /* GoogleMapsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */; }; F7151F21265D7EE50028CB91 /* GoogleMapsUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F20265D7EE50028CB91 /* GoogleMapsUITests.m */; }; FC8F35FC8CD533B128950487 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F267F68029D1A4E2E4C572A7 /* libPods-RunnerTests.a */; }; @@ -67,6 +68,8 @@ 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 = ""; }; + 982F2A6A27BADE17003C81F4 /* PartiallyMockedMapView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PartiallyMockedMapView.h; sourceTree = ""; }; + 982F2A6B27BADE17003C81F4 /* PartiallyMockedMapView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PartiallyMockedMapView.m; sourceTree = ""; }; B7AFC65E3DD5AC60D834D83D /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; E52C6A6210A56F027C582EF9 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; EA0E91726245EDC22B97E8B9 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; @@ -188,6 +191,8 @@ isa = PBXGroup; children = ( F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */, + 982F2A6A27BADE17003C81F4 /* PartiallyMockedMapView.h */, + 982F2A6B27BADE17003C81F4 /* PartiallyMockedMapView.m */, F7151F14265D7ED70028CB91 /* Info.plist */, ); path = RunnerTests; @@ -270,7 +275,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1100; + LastUpgradeCheck = 1320; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -441,6 +446,7 @@ buildActionMask = 2147483647; files = ( F7151F13265D7ED70028CB91 /* GoogleMapsTests.m in Sources */, + 982F2A6C27BADE17003C81F4 /* PartiallyMockedMapView.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -511,6 +517,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -567,6 +574,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index afdb55fdfbdd..c983bfc640ff 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ +#import "PartiallyMockedMapView.h" + @interface GoogleMapsTests : XCTestCase @end @@ -15,4 +19,24 @@ - (void)testPlugin { XCTAssertNotNil(plugin); } +- (void)testFrameObserver { + id registrar = OCMProtocolMock(@protocol(FlutterPluginRegistrar)); + CGRect frame = CGRectMake(0, 0, 100, 100); + PartiallyMockedMapView *mapView = [[PartiallyMockedMapView alloc] + initWithFrame:frame + camera:[[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]]; + FLTGoogleMapController *controller = [[FLTGoogleMapController alloc] initWithMapView:mapView + viewIdentifier:0 + arguments:nil + registrar:registrar]; + + for (NSInteger i = 0; i < 10; ++i) { + [controller view]; + } + XCTAssertEqual(mapView.frameObserverCount, 1); + + mapView.frame = frame; + XCTAssertEqual(mapView.frameObserverCount, 0); +} + @end diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.h b/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.h new file mode 100644 index 000000000000..4288401cf90d --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.h @@ -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 GoogleMaps; + +/** + * Defines a map view used for testing key-value observing. + */ +@interface PartiallyMockedMapView : GMSMapView + +/** + * The number of times that the `frame` KVO has been added. + */ +@property(nonatomic, assign, readonly) NSInteger frameObserverCount; + +@end diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.m b/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.m new file mode 100644 index 000000000000..202a18d128c0 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.m @@ -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 "PartiallyMockedMapView.h" + +@interface PartiallyMockedMapView () + +@property(nonatomic, assign) NSInteger frameObserverCount; + +@end + +@implementation PartiallyMockedMapView + +- (void)addObserver:(NSObject *)observer + forKeyPath:(NSString *)keyPath + options:(NSKeyValueObservingOptions)options + context:(void *)context { + [super addObserver:observer forKeyPath:keyPath options:options context:context]; + + if ([keyPath isEqualToString:@"frame"]) { + ++self.frameObserverCount; + } +} + +- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath { + [super removeObserver:observer forKeyPath:keyPath]; + + if ([keyPath isEqualToString:@"frame"]) { + --self.frameObserverCount; + } +} + +@end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m index df4e8761e6b2..ca8068129566 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m @@ -51,7 +51,6 @@ @implementation FLTGoogleMapController { FlutterMethodChannel *_channel; BOOL _trackCameraPosition; NSObject *_registrar; - BOOL _cameraDidInitialSetup; FLTMarkersController *_markersController; FLTPolygonsController *_polygonsController; FLTPolylinesController *_polylinesController; @@ -63,11 +62,19 @@ - (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args registrar:(NSObject *)registrar { + GMSCameraPosition *camera = ToOptionalCameraPosition(args[@"initialCameraPosition"]); + GMSMapView *mapView = [GMSMapView mapWithFrame:frame camera:camera]; + return [self initWithMapView:mapView viewIdentifier:viewId arguments:args registrar:registrar]; +} + +- (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args + registrar:(NSObject *_Nonnull)registrar { if (self = [super init]) { + _mapView = mapView; _viewId = viewId; - GMSCameraPosition *camera = ToOptionalCameraPosition(args[@"initialCameraPosition"]); - _mapView = [GMSMapView mapWithFrame:frame camera:camera]; _mapView.accessibilityElementsHidden = NO; _trackCameraPosition = NO; InterpretMapOptions(args[@"options"], self); @@ -83,7 +90,6 @@ - (instancetype)initWithFrame:(CGRect)frame }]; _mapView.delegate = weakSelf; _registrar = registrar; - _cameraDidInitialSetup = NO; _markersController = [[FLTMarkersController alloc] init:_channel mapView:_mapView registrar:registrar]; @@ -119,12 +125,13 @@ - (instancetype)initWithFrame:(CGRect)frame if ([tileOverlaysToAdd isKindOfClass:[NSArray class]]) { [_tileOverlaysController addTileOverlays:tileOverlaysToAdd]; } + + [_mapView addObserver:self forKeyPath:@"frame" options:0 context:nil]; } return self; } - (UIView *)view { - [_mapView addObserver:self forKeyPath:@"frame" options:0 context:nil]; return _mapView; } @@ -132,11 +139,6 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if (_cameraDidInitialSetup) { - // We only observe the frame for initial setup. - [_mapView removeObserver:self forKeyPath:@"frame"]; - return; - } if (object == _mapView && [keyPath isEqualToString:@"frame"]) { CGRect bounds = _mapView.bounds; if (CGRectEqualToRect(bounds, CGRectZero)) { @@ -146,7 +148,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath // zero. return; } - _cameraDidInitialSetup = YES; + // We only observe the frame for initial setup. [_mapView removeObserver:self forKeyPath:@"frame"]; [_mapView moveCamera:[GMSCameraUpdate setCamera:_mapView.camera]]; } else { diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController_Test.h b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController_Test.h new file mode 100644 index 000000000000..84f6f7ca485f --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController_Test.h @@ -0,0 +1,27 @@ +// 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 + +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. + */ +- (instancetype)initWithMapView:(GMSMapView *)mapView + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args + registrar:(NSObject *)registrar; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter-umbrella.h b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter-umbrella.h new file mode 100644 index 000000000000..50880a2b9e9d --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter-umbrella.h @@ -0,0 +1,11 @@ +// 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 +#import +#import + +FOUNDATION_EXPORT double google_maps_flutterVersionNumber; +FOUNDATION_EXPORT const unsigned char google_maps_flutterVersionString[]; diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter.modulemap b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter.modulemap new file mode 100644 index 000000000000..19513f4a7602 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter.modulemap @@ -0,0 +1,10 @@ +framework module google_maps_flutter { + umbrella header "google_maps_flutter-umbrella.h" + + export * + module * { export * } + + explicit module Test { + header "GoogleMapController_Test.h" + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/google_maps_flutter.podspec b/packages/google_maps_flutter/google_maps_flutter/ios/google_maps_flutter.podspec index f2ed5fc56ea0..e34919c30484 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/google_maps_flutter.podspec +++ b/packages/google_maps_flutter/google_maps_flutter/ios/google_maps_flutter.podspec @@ -14,8 +14,9 @@ Downloaded by pub (not CocoaPods). s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter' } s.documentation_url = 'https://pub.dev/packages/google_maps_flutter' - s.source_files = 'Classes/**/*' + s.source_files = 'Classes/**/*.{h,m}' s.public_header_files = 'Classes/**/*.h' + s.module_map = 'Classes/google_maps_flutter.modulemap' s.dependency 'Flutter' s.dependency 'GoogleMaps' s.static_framework = true diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index 849019cbdb2d..741fe6910037 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/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.1.2 +version: 2.1.3 environment: sdk: ">=2.14.0 <3.0.0" From 941641198fdfbe57e21e93bb4c477ab39dd3a916 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 21 Mar 2022 15:35:26 -0400 Subject: [PATCH 057/844] Roll Flutter from 6af40a7004f8 to 8c1c2f6af555 (1 revision) (#5092) --- .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 655584a08576..7a43a6594b72 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -6af40a7004f886c8b8b87475a40107611bc5bb0a +8c1c2f6af5558c27946e8376541649090073d50c From ada413d49d8743e495148a47b73e784742314329 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 21 Mar 2022 17:45:24 -0400 Subject: [PATCH 058/844] Roll Flutter from 8c1c2f6af555 to 97258979df13 (8 revisions) (#5094) --- .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 7a43a6594b72..116f41f89d39 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8c1c2f6af5558c27946e8376541649090073d50c +97258979df13c5ea40490cd2615803f26ae6e507 From 3069bc3b84104d642ec2f69a9b0584719fe34e01 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 21 Mar 2022 18:50:25 -0400 Subject: [PATCH 059/844] Roll Flutter from 97258979df13 to 9671b7ddee8c (1 revision) (#5095) --- .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 116f41f89d39..3bf43fd4c61a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -97258979df13c5ea40490cd2615803f26ae6e507 +9671b7ddee8c5110cf6adcf7b7697991512218e9 From 52995e83c87947d8e0ca0f79fe86a120415dcf81 Mon Sep 17 00:00:00 2001 From: Kyle Finlinson Date: Tue, 22 Mar 2022 14:00:22 -0600 Subject: [PATCH 060/844] [video_player] Platform interface changes to fix Android rotation for videos recorded in landscapeRight (#4634) --- .../CHANGELOG.md | 4 ++++ .../lib/method_channel_video_player.dart | 1 + .../lib/video_player_platform_interface.dart | 12 +++++++++-- .../pubspec.yaml | 2 +- .../method_channel_video_player_test.dart | 21 +++++++++++++++++++ 5 files changed, 37 insertions(+), 3 deletions(-) diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index 52612207d8f3..6595a5ce55db 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.1.1 + +* Adds `rotationCorrection` (for Android playing videos recorded in landscapeRight [#60327](https://github.com/flutter/flutter/issues/60327)). + ## 5.1.0 * Adds `allowBackgroundPlayback` to `VideoPlayerOptions`. diff --git a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart b/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart index 2aa7fb30e5f2..097ab29c48a3 100644 --- a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart +++ b/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart @@ -115,6 +115,7 @@ class MethodChannelVideoPlayer extends VideoPlayerPlatform { duration: Duration(milliseconds: map['duration']! as int), size: Size((map['width'] as num?)?.toDouble() ?? 0.0, (map['height'] as num?)?.toDouble() ?? 0.0), + rotationCorrection: map['rotationCorrection'] as int? ?? 0, ); case 'completed': return VideoEvent( diff --git a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart index 8a61005c429e..706ec61abf7c 100644 --- a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart +++ b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart @@ -199,8 +199,8 @@ class VideoEvent { /// /// The [eventType] argument is required. /// - /// Depending on the [eventType], the [duration], [size] and [buffered] - /// arguments can be null. + /// Depending on the [eventType], the [duration], [size], + /// [rotationCorrection], and [buffered] arguments can be null. // TODO(stuartmorgan): Temporarily suppress warnings about not using const // in all of the other video player packages, fix this, and then update // the other packages to use const. @@ -209,6 +209,7 @@ class VideoEvent { required this.eventType, this.duration, this.size, + this.rotationCorrection, this.buffered, }); @@ -225,6 +226,11 @@ class VideoEvent { /// Only used if [eventType] is [VideoEventType.initialized]. final Size? size; + /// Degrees to rotate the video (clockwise) so it is displayed correctly. + /// + /// Only used if [eventType] is [VideoEventType.initialized]. + final int? rotationCorrection; + /// Buffered parts of the video. /// /// Only used if [eventType] is [VideoEventType.bufferingUpdate]. @@ -238,6 +244,7 @@ class VideoEvent { eventType == other.eventType && duration == other.duration && size == other.size && + rotationCorrection == other.rotationCorrection && listEquals(buffered, other.buffered); } @@ -246,6 +253,7 @@ class VideoEvent { eventType.hashCode ^ duration.hashCode ^ size.hashCode ^ + rotationCorrection.hashCode ^ buffered.hashCode; } diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml index b66fa0b46d75..ea3b17be1ea5 100644 --- a/packages/video_player/video_player_platform_interface/pubspec.yaml +++ b/packages/video_player/video_player_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/video_player/v issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%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: 5.1.0 +version: 5.1.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart b/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart index 75baba45f763..ca17196cf523 100644 --- a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart +++ b/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart @@ -257,6 +257,20 @@ void main() { }), (ByteData? data) {}); + await _ambiguate(ServicesBinding.instance) + ?.defaultBinaryMessenger + .handlePlatformMessage( + 'flutter.io/videoPlayer/videoEvents123', + const StandardMethodCodec() + .encodeSuccessEnvelope({ + 'event': 'initialized', + 'duration': 98765, + 'width': 1920, + 'height': 1080, + 'rotationCorrection': 180, + }), + (ByteData? data) {}); + await _ambiguate(ServicesBinding.instance) ?.defaultBinaryMessenger .handlePlatformMessage( @@ -316,6 +330,13 @@ void main() { eventType: VideoEventType.initialized, duration: const Duration(milliseconds: 98765), size: const Size(1920, 1080), + rotationCorrection: 0, + ), + VideoEvent( + eventType: VideoEventType.initialized, + duration: const Duration(milliseconds: 98765), + size: const Size(1920, 1080), + rotationCorrection: 180, ), VideoEvent(eventType: VideoEventType.completed), VideoEvent( From 1bb37bd57d7cdfe98e964f55134c8cc561e0ccaf Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 22 Mar 2022 17:20:45 -0400 Subject: [PATCH 061/844] [webview_flutter_wkwebview] Add support to clear cache (#4991) --- .../lib/src/web_kit/web_kit.dart | 60 +++++++++++++++++++ .../lib/src/web_kit_webview_widget.dart | 13 ++++ .../test/src/web_kit_webview_widget_test.dart | 21 +++++++ .../web_kit_webview_widget_test.mocks.dart | 45 +++++++++++--- 4 files changed, 131 insertions(+), 8 deletions(-) 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 85881e0e512e..d7fdf1db68c6 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 @@ -46,6 +46,35 @@ enum WKAudiovisualMediaType { all, } +/// Types of data that websites store. +/// +/// See https://developer.apple.com/documentation/webkit/wkwebsitedatarecord/data_store_record_types?language=objc. +enum WKWebsiteDataTypes { + /// Cookies. + cookies, + + /// In-memory caches. + memoryCache, + + /// On-disk caches. + diskCache, + + /// HTML offline web app caches. + offlineWebApplicationCache, + + /// HTML local storage. + localStroage, + + /// HTML session storage. + sessionStorage, + + /// WebSQL databases. + sqlDatabases, + + /// IndexedDB databases. + indexedDBDatabases, +} + /// Indicate whether to allow or cancel navigation to a webpage. /// /// Wraps [WKNavigationActionPolicy](https://developer.apple.com/documentation/webkit/wknavigationactionpolicy?language=objc). @@ -162,6 +191,25 @@ class WKScriptMessage { final Object? body; } +/// Manages cookies, disk and memory caches, and other types of data for a web view. +/// +/// Wraps [WKWebsiteDataStore](https://developer.apple.com/documentation/webkit/wkwebsitedatastore?language=objc). +class WKWebsiteDataStore { + WKWebsiteDataStore._fromWebViewConfiguration( + // TODO(bparrishMines): Remove ignore once constructor is implemented. + // ignore: avoid_unused_constructor_parameters + WKWebViewConfiguration configuration, + ); + + /// Removes website data that changed after the specified date. + Future removeDataOfTypes( + Set dataTypes, + DateTime since, + ) { + throw UnimplementedError(); + } +} + /// An interface for receiving messages from JavaScript code running in a webpage. /// /// Wraps [WKScriptMessageHandler](https://developer.apple.com/documentation/webkit/wkscriptmessagehandler?language=objc) @@ -268,6 +316,18 @@ class WKWebViewConfiguration { /// Coordinates interactions between your app’s code and the webpage’s scripts and other content. late final WKUserContentController userContentController; + late WKWebsiteDataStore _websiteDataStore = + WKWebsiteDataStore._fromWebViewConfiguration(this); + + /// Used to get and set the site’s cookies and to track the cached data objects. + WKWebsiteDataStore get webSiteDataStore => _websiteDataStore; + + /// Used to get and set the site’s cookies and to track the cached data objects. + set webSiteDataStore(WKWebsiteDataStore websiteDataStore) { + _websiteDataStore = websiteDataStore; + throw UnimplementedError(); + } + /// Indicates whether HTML5 videos play inline or use the native full-screen controller. set allowsInlineMediaPlayback(bool allow) { throw UnimplementedError(); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 89a258e75767..87baeb45c368 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -194,6 +194,19 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { }; } + @override + Future clearCache() { + return webView.configuration.webSiteDataStore.removeDataOfTypes( + { + WKWebsiteDataTypes.memoryCache, + WKWebsiteDataTypes.diskCache, + WKWebsiteDataTypes.offlineWebApplicationCache, + WKWebsiteDataTypes.localStroage, + }, + DateTime.fromMillisecondsSinceEpoch(0), + ); + } + @override Future updateSettings(WebSettings setting) async { if (setting.hasNavigationDelegate != null) { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 6642b4561cfc..358df898403a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -20,6 +20,7 @@ import 'web_kit_webview_widget_test.mocks.dart'; WKScriptMessageHandler, WKWebView, WKWebViewConfiguration, + WKWebsiteDataStore, WKUIDelegate, WKUserContentController, JavascriptChannelRegistry, @@ -35,6 +36,7 @@ void main() { late MockWKUserContentController mockUserContentController; late MockWKWebViewConfiguration mockWebViewConfiguration; late MockWKUIDelegate mockUIDelegate; + late MockWKWebsiteDataStore mockWebsiteDataStore; late MockWKNavigationDelegate mockNavigationDelegate; late MockWebViewPlatformCallbacksHandler mockCallbacksHandler; @@ -47,6 +49,7 @@ void main() { mockWebViewConfiguration = MockWKWebViewConfiguration(); mockUserContentController = MockWKUserContentController(); mockUIDelegate = MockWKUIDelegate(); + mockWebsiteDataStore = MockWKWebsiteDataStore(); mockNavigationDelegate = MockWKNavigationDelegate(); mockWebViewWidgetProxy = MockWebViewWidgetProxy(); @@ -58,6 +61,9 @@ void main() { when(mockWebViewConfiguration.userContentController).thenReturn( mockUserContentController, ); + when(mockWebViewConfiguration.webSiteDataStore).thenReturn( + mockWebsiteDataStore, + ); mockCallbacksHandler = MockWebViewPlatformCallbacksHandler(); mockJavascriptChannelRegistry = MockJavascriptChannelRegistry(); @@ -206,6 +212,21 @@ void main() { }); group('$WebKitWebViewPlatformController', () { + testWidgets('clearCache', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.clearCache(); + verify(mockWebsiteDataStore.removeDataOfTypes( + { + WKWebsiteDataTypes.memoryCache, + WKWebsiteDataTypes.diskCache, + WKWebsiteDataTypes.offlineWebApplicationCache, + WKWebsiteDataTypes.localStroage, + }, + DateTime.fromMillisecondsSinceEpoch(0), + )); + }); + testWidgets('addJavascriptChannels', (WidgetTester tester) async { when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn( MockWKScriptMessageHandler(), diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index 9579b23c84a0..32f1731701a9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -32,14 +32,17 @@ class _FakeWKWebViewConfiguration_0 extends _i1.Fake class _FakeWKUserContentController_1 extends _i1.Fake implements _i2.WKUserContentController {} -class _FakeWKWebView_2 extends _i1.Fake implements _i2.WKWebView {} +class _FakeWKWebsiteDataStore_2 extends _i1.Fake + implements _i2.WKWebsiteDataStore {} -class _FakeWKScriptMessageHandler_3 extends _i1.Fake +class _FakeWKWebView_3 extends _i1.Fake implements _i2.WKWebView {} + +class _FakeWKScriptMessageHandler_4 extends _i1.Fake implements _i2.WKScriptMessageHandler {} -class _FakeWKUIDelegate_4 extends _i1.Fake implements _i2.WKUIDelegate {} +class _FakeWKUIDelegate_5 extends _i1.Fake implements _i2.WKUIDelegate {} -class _FakeWKNavigationDelegate_5 extends _i1.Fake +class _FakeWKNavigationDelegate_6 extends _i1.Fake implements _i2.WKNavigationDelegate {} /// A class which mocks [WKNavigationDelegate]. @@ -167,6 +170,14 @@ class MockWKWebViewConfiguration extends _i1.Mock Invocation.setter(#userContentController, _userContentController), returnValueForMissingStub: null); @override + _i2.WKWebsiteDataStore get webSiteDataStore => + (super.noSuchMethod(Invocation.getter(#webSiteDataStore), + returnValue: _FakeWKWebsiteDataStore_2()) as _i2.WKWebsiteDataStore); + @override + set webSiteDataStore(_i2.WKWebsiteDataStore? websiteDataStore) => + super.noSuchMethod(Invocation.setter(#webSiteDataStore, websiteDataStore), + returnValueForMissingStub: null); + @override set allowsInlineMediaPlayback(bool? allow) => super.noSuchMethod(Invocation.setter(#allowsInlineMediaPlayback, allow), returnValueForMissingStub: null); @@ -178,6 +189,24 @@ class MockWKWebViewConfiguration extends _i1.Mock returnValueForMissingStub: null); } +/// A class which mocks [WKWebsiteDataStore]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWKWebsiteDataStore extends _i1.Mock + implements _i2.WKWebsiteDataStore { + MockWKWebsiteDataStore() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future removeDataOfTypes( + Set<_i2.WKWebsiteDataTypes>? dataTypes, DateTime? since) => + (super.noSuchMethod( + Invocation.method(#removeDataOfTypes, [dataTypes, since]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); +} + /// A class which mocks [WKUIDelegate]. /// /// See the documentation for Mockito's code generation for more information. @@ -303,18 +332,18 @@ class MockWebViewWidgetProxy extends _i1.Mock @override _i2.WKWebView createWebView(_i2.WKWebViewConfiguration? configuration) => (super.noSuchMethod(Invocation.method(#createWebView, [configuration]), - returnValue: _FakeWKWebView_2()) as _i2.WKWebView); + returnValue: _FakeWKWebView_3()) as _i2.WKWebView); @override _i2.WKScriptMessageHandler createScriptMessageHandler() => (super.noSuchMethod(Invocation.method(#createScriptMessageHandler, []), - returnValue: _FakeWKScriptMessageHandler_3()) + returnValue: _FakeWKScriptMessageHandler_4()) as _i2.WKScriptMessageHandler); @override _i2.WKUIDelegate createUIDelgate() => (super.noSuchMethod(Invocation.method(#createUIDelgate, []), - returnValue: _FakeWKUIDelegate_4()) as _i2.WKUIDelegate); + returnValue: _FakeWKUIDelegate_5()) as _i2.WKUIDelegate); @override _i2.WKNavigationDelegate createNavigationDelegate() => (super.noSuchMethod( Invocation.method(#createNavigationDelegate, []), - returnValue: _FakeWKNavigationDelegate_5()) as _i2.WKNavigationDelegate); + returnValue: _FakeWKNavigationDelegate_6()) as _i2.WKNavigationDelegate); } From 8624a5f50852f853f5d98468a930d116f2eff01e Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 23 Mar 2022 16:12:36 -0400 Subject: [PATCH 062/844] [video_player] Reduce server dependencies in tests (#5103) Reduce test dependencies on servers that the Flutter team doesn't control: - Switches webm URL to the GitHub URL for the example asset, matching the existing mp4 setup - Switches m3u8 URL to the assets-for-api-docs URL that was recently added for a native iOS video_player test Ideally these would use local web severs, but initial attempts to serve binary assets via the local web server didn't work, and I won't have time to investigate further in the short term. This is an incremental improvement, and adds TODOs to fully convert. --- .../integration_test/video_player_test.dart | 16 +++++++--------- .../integration_test/video_player_test.dart | 7 +++++-- .../integration_test/video_player_test.dart | 7 +++++-- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/video_player/video_player/example/integration_test/video_player_test.dart b/packages/video_player/video_player/example/integration_test/video_player_test.dart index 746c63fcbfd6..151eb93149ee 100644 --- a/packages/video_player/video_player/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player/example/integration_test/video_player_test.dart @@ -21,10 +21,13 @@ const String _videoAssetKey = kIsWeb ? 'assets/Butterfly-209.webm' : 'assets/Butterfly-209.mp4'; // Returns the URL to load an asset from this example app as a network source. +// +// TODO(stuartmorgan): Convert this to a local `HttpServer` that vends the +// assets directly, https://github.com/flutter/flutter/issues/95420 String getUrlForAssetAsNetworkSource(String assetKey) { return 'https://github.com/flutter/plugins/blob/' // This hash can be rolled forward to pick up newly-added assets. - 'cba393233e559c925a4daf71b06b4bb01c606762' + 'cb381ced070d356799dddf24aca38ce0579d3d7b' '/packages/video_player/video_player/example/' '$assetKey' '?raw=true'; @@ -56,7 +59,7 @@ void main() { (WidgetTester tester) async { final VideoPlayerController networkController = VideoPlayerController.network( - 'https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8', + 'https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8', ); await networkController.initialize(); @@ -229,13 +232,8 @@ void main() { group('network videos', () { setUp(() { - // TODO(stuartmorgan): Remove this conditional and update the hash in - // getUrlForAssetAsNetworkSource as a follow-up, once the webm asset is - // checked in. - final String videoUrl = kIsWeb - ? 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm' - : getUrlForAssetAsNetworkSource(_videoAssetKey); - _controller = VideoPlayerController.network(videoUrl); + _controller = VideoPlayerController.network( + getUrlForAssetAsNetworkSource(_videoAssetKey)); }); testWidgets( diff --git a/packages/video_player/video_player_android/example/integration_test/video_player_test.dart b/packages/video_player/video_player_android/example/integration_test/video_player_test.dart index b80ed745a6f9..77a618bbbdfc 100644 --- a/packages/video_player/video_player_android/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player_android/example/integration_test/video_player_test.dart @@ -24,10 +24,13 @@ const Duration _playDuration = Duration(seconds: 1); const String _videoAssetKey = 'assets/Butterfly-209.mp4'; // Returns the URL to load an asset from this example app as a network source. +// +// TODO(stuartmorgan): Convert this to a local `HttpServer` that vends the +// assets directly, https://github.com/flutter/flutter/issues/95420 String getUrlForAssetAsNetworkSource(String assetKey) { return 'https://github.com/flutter/plugins/blob/' // This hash can be rolled forward to pick up newly-added assets. - 'cba393233e559c925a4daf71b06b4bb01c606762' + 'cb381ced070d356799dddf24aca38ce0579d3d7b' '/packages/video_player/video_player/example/' '$assetKey' '?raw=true'; @@ -155,7 +158,7 @@ void main() { testWidgets('live stream duration != 0', (WidgetTester tester) async { final MiniController livestreamController = MiniController.network( - 'https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8', + 'https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8', ); await livestreamController.initialize(); diff --git a/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart b/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart index a457d9226e3e..528723d092b4 100644 --- a/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart @@ -24,10 +24,13 @@ const Duration _playDuration = Duration(seconds: 1); const String _videoAssetKey = 'assets/Butterfly-209.mp4'; // Returns the URL to load an asset from this example app as a network source. +// +// TODO(stuartmorgan): Convert this to a local `HttpServer` that vends the +// assets directly, https://github.com/flutter/flutter/issues/95420 String getUrlForAssetAsNetworkSource(String assetKey) { return 'https://github.com/flutter/plugins/blob/' // This hash can be rolled forward to pick up newly-added assets. - 'cba393233e559c925a4daf71b06b4bb01c606762' + 'cb381ced070d356799dddf24aca38ce0579d3d7b' '/packages/video_player/video_player/example/' '$assetKey' '?raw=true'; @@ -167,7 +170,7 @@ void main() { testWidgets('live stream duration != 0', (WidgetTester tester) async { final MiniController livestreamController = MiniController.network( - 'https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8', + 'https://flutter.github.io/assets-for-api-docs/assets/videos/hls/bee.m3u8', ); await livestreamController.initialize(); From 86b36210ed03e18f2b9f900671618c803443da26 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Wed, 23 Mar 2022 17:25:10 -0700 Subject: [PATCH 063/844] [ci] Update Chrome install script and version. (#5098) --- script/install_chromium.sh | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/script/install_chromium.sh b/script/install_chromium.sh index 1cb38af05496..b7d787626d55 100755 --- a/script/install_chromium.sh +++ b/script/install_chromium.sh @@ -10,24 +10,35 @@ readonly TARGET_DIR=$1 # The build of Chromium used to test web functionality. # # Chromium builds can be located here: https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Linux_x64/ -readonly CHROMIUM_BUILD=768968 -# The ChromeDriver version corresponding to the build above. See -# https://chromedriver.chromium.org/downloads -# for versions mappings when updating Chromium. -readonly CHROME_DRIVER_VERSION=84.0.4147.30 +# +# Check: https://github.com/flutter/engine/blob/master/lib/web_ui/dev/browser_lock.yaml +readonly CHROMIUM_BUILD=929514 + +# The correct ChromeDriver is distributed alongside the chromium build above, as +# `chromedriver_linux64.zip`, so no need to hardcode any extra info about it. +readonly DOWNLOAD_ROOT="https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2F${CHROMIUM_BUILD}%2F" # Install Chromium. mkdir "$TARGET_DIR" -wget --no-verbose "https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2F${CHROMIUM_BUILD}%2Fchrome-linux.zip?alt=media" -O "$TARGET_DIR"/chromium.zip -unzip "$TARGET_DIR"/chromium.zip -d "$TARGET_DIR"/ +readonly CHROMIUM_ZIP_FILE="$TARGET_DIR/chromium.zip" +wget --no-verbose "${DOWNLOAD_ROOT}chrome-linux.zip?alt=media" -O "$CHROMIUM_ZIP_FILE" +unzip -q "$CHROMIUM_ZIP_FILE" -d "$TARGET_DIR/" # Install ChromeDriver. readonly DRIVER_ZIP_FILE="$TARGET_DIR/chromedriver.zip" -wget --no-verbose "https://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip" -O "$DRIVER_ZIP_FILE" -unzip "$DRIVER_ZIP_FILE" -d "$TARGET_DIR/chromedriver" +wget --no-verbose "${DOWNLOAD_ROOT}chromedriver_linux64.zip?alt=media" -O "$DRIVER_ZIP_FILE" +unzip -q "$DRIVER_ZIP_FILE" -d "$TARGET_DIR/" +# Rename TARGET_DIR/chromedriver_linux64 to the expected TARGET_DIR/chromedriver +mv -T "$TARGET_DIR/chromedriver_linux64" "$TARGET_DIR/chromedriver" + +export CHROME_EXECUTABLE="$TARGET_DIR/chrome-linux/chrome" # Echo info at the end for ease of debugging. -export CHROME_EXECUTABLE="$TARGET_DIR"/chrome-linux/chrome -echo $CHROME_EXECUTABLE -$CHROME_EXECUTABLE --version -echo "ChromeDriver $CHROME_DRIVER_VERSION" +set +x +echo +readonly CHROMEDRIVER_EXECUTABLE="$TARGET_DIR/chromedriver/chromedriver" +echo "$CHROME_EXECUTABLE" +"$CHROME_EXECUTABLE" --version +echo "$CHROMEDRIVER_EXECUTABLE" +"$CHROMEDRIVER_EXECUTABLE" --version +echo From 431cac92ab1b3821a3fd5d89ceee62a834da4999 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 24 Mar 2022 11:45:09 -0400 Subject: [PATCH 064/844] [camera] Update README (#4988) --- packages/camera/camera/CHANGELOG.md | 4 ++++ packages/camera/camera/README.md | 19 ++++++++++--------- packages/camera/camera/pubspec.yaml | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 35a958e026ff..d19b500af2d7 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+17 + +* Removes obsolete information from README, and adds OS support table. + ## 0.9.4+16 * Fixes a bug resulting in a `CameraAccessException` that prevents image diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index 6cca254f8690..a313c3a1655f 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -4,7 +4,9 @@ A Flutter plugin for iOS, Android and Web allowing access to the device cameras. -*Note*: This plugin is still under development, and some APIs might not be available yet. We are working on a refactor which can be followed here: [issue](https://github.com/flutter/flutter/issues/31225) +| | Android | iOS | Web | +|----------------|---------|----------|------------------------| +| **Support** | SDK 21+ | iOS 10+* | [See `camera_web `][1] | ## Features @@ -19,8 +21,9 @@ First, add `camera` as a [dependency in your pubspec.yaml file](https://flutter. ### iOS -The camera plugin functionality works on iOS 10.0 or higher. If compiling for any version lower than 10.0, -make sure to programmatically check the version of iOS running on the device before using any camera plugin features. +\* The camera plugin compiles for any version of iOS, but its functionality +requires iOS 10 or higher. If compiling for iOS 9, make sure to programmatically +check the version of iOS running on the device before using any camera plugin features. The [device_info_plus](https://pub.dev/packages/device_info_plus) plugin, for example, can be used to check the iOS version. Add two rows to the `ios/Runner/Info.plist`: @@ -28,13 +31,13 @@ Add two rows to the `ios/Runner/Info.plist`: * one with the key `Privacy - Camera Usage Description` and a usage description. * and one with the key `Privacy - Microphone Usage Description` and a usage description. -Or in text format add the key: +If editing `Info.plist` as text, add: ```xml NSCameraUsageDescription -Can I use the camera please? +your usage description here NSMicrophoneUsageDescription -Can I use the mic please? +your usage description here ``` ### Android @@ -132,6 +135,4 @@ class _CameraAppState extends State { For a more elaborate usage example see [here](https://github.com/flutter/plugins/tree/main/packages/camera/camera/example). -*Note*: This plugin is still under development, and some APIs might not be available yet. -[Feedback welcome](https://github.com/flutter/flutter/issues) and -[Pull Requests](https://github.com/flutter/plugins/pulls) are most welcome! +[1]: https://pub.dev/packages/camera_web#limitations-on-the-web-platform diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 2baab09c5dcb..0e684a743ba3 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+16 +version: 0.9.4+17 environment: sdk: ">=2.14.0 <3.0.0" From 4ec6e57632b651d5fc35fede038199e3ab0efb68 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 24 Mar 2022 10:27:26 -0700 Subject: [PATCH 065/844] [webview_flutter_wkwebview] Add WKWebView method stubs (#4990) --- .../lib/src/ui_kit/ui_kit.dart | 38 ++ .../lib/src/web_kit/web_kit.dart | 79 ++++ .../lib/src/web_kit_webview_widget.dart | 163 ++++++++ .../webview_flutter_wkwebview/pubspec.yaml | 1 + .../test/src/web_kit_webview_widget_test.dart | 349 ++++++++++++++++++ .../web_kit_webview_widget_test.mocks.dart | 265 ++++++++----- 6 files changed, 810 insertions(+), 85 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart new file mode 100644 index 000000000000..def52b78f6b5 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.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 'dart:async'; +import 'dart:math'; + +import '../web_kit/web_kit.dart'; + +/// A view that allows the scrolling and zooming of its contained views. +/// +/// Wraps [UIScrollView](https://developer.apple.com/documentation/uikit/uiscrollview?language=objc). +class UIScrollView { + /// Constructs a [UIScrollView] that is owned by [webView]. + // TODO(bparrishMines): Remove ignore once constructor is implemented. + // ignore: avoid_unused_constructor_parameters + UIScrollView.fromWebView(WKWebView webView); + + /// Point at which the origin of the content view is offset from the origin of the scroll view. + Future> get contentOffset { + throw UnimplementedError(); + } + + /// Move the scrolled position of this view. + /// + /// This method is not a part of UIKit and is only a helper method to make + /// scrollBy atomic. + Future scrollBy(Point offset) { + throw UnimplementedError(); + } + + /// Set point at which the origin of the content view is offset from the origin of the scroll view. + /// + /// The default value is `Point(0.0, 0.0)`. + set contentOffset(FutureOr> offset) { + throw UnimplementedError(); + } +} 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 d7fdf1db68c6..cd7e4a9aadcb 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 @@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart'; import '../foundation/foundation.dart'; +import '../ui_kit/ui_kit.dart'; /// Times at which to inject script content into a webpage. /// @@ -450,6 +451,9 @@ class WKWebView { late final WKWebViewConfiguration configuration = WKWebViewConfiguration._fromWebView(this); + /// The scrollable view associated with the web view. + late final UIScrollView scrollView = UIScrollView.fromWebView(this); + /// Used to integrate custom user interface elements into web view interactions. set uiDelegate(WKUIDelegate? delegate) { throw UnimplementedError(); @@ -472,4 +476,79 @@ class WKWebView { Future loadRequest(NSUrlRequest request) { throw UnimplementedError(); } + + /// Loads the contents of the specified HTML string and navigates to it. + Future loadHtmlString(String string, {String? baseUrl}) { + throw UnimplementedError(); + } + + /// Loads the web content from the specified file and navigates to it. + Future loadFileUrl(String url, {required String readAccessUrl}) { + throw UnimplementedError(); + } + + /// Loads the Flutter asset specified in the pubspec.yaml file. + /// + /// This method is not a part of WebKit and is only a Flutter specific helper + /// method. + Future loadFlutterAsset(String key) { + throw UnimplementedError(); + } + + /// Indicates whether there is a valid back item in the back-forward list. + Future get canGoBack { + throw UnimplementedError(); + } + + /// Indicates whether there is a valid forward item in the back-forward list. + Future get canGoForward { + throw UnimplementedError(); + } + + /// Navigates to the back item in the back-forward list. + Future goBack() { + throw UnimplementedError(); + } + + /// Navigates to the forward item in the back-forward list. + Future goForward() { + throw UnimplementedError(); + } + + /// Reloads the current webpage. + Future reload() { + throw UnimplementedError(); + } + + /// The page title. + Future get title { + throw UnimplementedError(); + } + + /// An estimate of what fraction of the current navigation has been loaded. + Future get estimatedProgress { + throw UnimplementedError(); + } + + /// Indicates whether horizontal swipe gestures trigger page navigation. + /// + /// The default value is false. + set allowsBackForwardNavigationGestures(bool allow) { + throw UnimplementedError(); + } + + /// The custom user agent string. + /// + /// The default value of this property is null. + set customUserAgent(String? userAgent) { + throw UnimplementedError(); + } + + /// Evaluates the specified JavaScript string. + /// + /// Throws a `PlatformException` if an error occurs or return value is not + /// supported. + Future evaluateJavaScript(String javaScriptString) { + throw UnimplementedError(); + } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 87baeb45c368..67ea6a936e42 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -3,9 +3,12 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:math'; import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; +import 'package:path/path.dart' as path; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'foundation/foundation.dart'; @@ -194,6 +197,19 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { }; } + @override + Future loadHtmlString(String html, {String? baseUrl}) { + return webView.loadHtmlString(html, baseUrl: baseUrl); + } + + @override + Future loadFile(String absoluteFilePath) async { + await webView.loadFileUrl( + absoluteFilePath, + readAccessUrl: path.dirname(absoluteFilePath), + ); + } + @override Future clearCache() { return webView.configuration.webSiteDataStore.removeDataOfTypes( @@ -207,6 +223,124 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { ); } + @override + Future loadFlutterAsset(String key) async { + assert(key.isNotEmpty); + return webView.loadFlutterAsset(key); + } + + @override + Future loadUrl(String url, Map? headers) async { + final NSUrlRequest request = NSUrlRequest( + url: url, + allHttpHeaderFields: headers ?? {}, + ); + return webView.loadRequest(request); + } + + @override + Future loadRequest(WebViewRequest request) async { + if (!request.uri.hasScheme) { + throw ArgumentError('WebViewRequest#uri is required to have a scheme.'); + } + + final NSUrlRequest urlRequest = NSUrlRequest( + url: request.uri.toString(), + allHttpHeaderFields: request.headers, + httpMethod: describeEnum(request.method), + httpBody: request.body, + ); + + return webView.loadRequest(urlRequest); + } + + @override + Future canGoBack() => webView.canGoBack; + + @override + Future canGoForward() => webView.canGoForward; + + @override + Future goBack() => webView.goBack(); + + @override + Future goForward() => webView.goForward(); + + @override + Future reload() => webView.reload(); + + @override + Future evaluateJavascript(String javascript) async { + final Object? result = await webView.evaluateJavaScript(javascript); + // The legacy implementation of webview_flutter_wkwebview would convert + // objects to strings before returning them to Dart. This method attempts + // to converts Dart objects to Strings the way it is done in Objective-C + // to avoid breaking users expecting the same String format. + return _asObjectiveCString(result); + } + + @override + Future runJavascript(String javascript) async { + try { + await webView.evaluateJavaScript(javascript); + } on PlatformException catch (exception) { + // WebKit will throw an error when the type of the evaluated value is + // unsupported. This also goes for `null` and `undefined` on iOS 14+. For + // example, when running a void function. For ease of use, this specific + // error is ignored when no return value is expected. + // TODO(bparrishMines): Ensure the platform code includes the NSError in + // the FlutterError.details. + if (exception.details is! NSError || + exception.details.code != + WKErrorCode.javaScriptResultTypeIsUnsupported) { + rethrow; + } + } + } + + @override + Future runJavascriptReturningResult(String javascript) async { + final Object? result = await webView.evaluateJavaScript(javascript); + if (result == null) { + throw ArgumentError( + 'Result of JavaScript execution returned a `null` value. ' + 'Use `runJavascript` when expecting a null return value.', + ); + } + return result.toString(); + } + + @override + Future getTitle() => webView.title; + + @override + Future scrollTo(int x, int y) async { + webView.scrollView.contentOffset = Point( + x.toDouble(), + y.toDouble(), + ); + } + + @override + Future scrollBy(int x, int y) async { + await webView.scrollView.scrollBy(Point( + x.toDouble(), + y.toDouble(), + )); + } + + @override + Future getScrollX() async { + final Point offset = await webView.scrollView.contentOffset; + return offset.x.toInt(); + } + + @override + Future getScrollY() async { + final Point offset = await webView.scrollView.contentOffset; + return offset.y.toInt(); + } + @override Future updateSettings(WebSettings setting) async { if (setting.hasNavigationDelegate != null) { @@ -324,6 +458,35 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { errorType: errorType, ); } + + String _asObjectiveCString(Object? value, {bool inContainer = false}) { + if (value == null) { + // An NSNull inside an NSArray or NSDictionary is represented as a String + // differently than a nil. + if (inContainer) { + return '""'; + } + return '(null)'; + } else if (value is List) { + final List stringValues = []; + for (final Object? listValue in value) { + stringValues.add(_asObjectiveCString(listValue, inContainer: true)); + } + return '(${stringValues.join(',')})'; + } else if (value is Map) { + final List stringValues = []; + for (final MapEntry entry in value.entries) { + stringValues.add( + '${_asObjectiveCString(entry.key, inContainer: true)} ' + '= ' + '${_asObjectiveCString(entry.value, inContainer: true)}', + ); + } + return '{${stringValues.join(';')}}'; + } + + return value.toString(); + } } /// Handles constructing objects and calling static methods. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 91760df02c66..219d6ce49f4f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -18,6 +18,7 @@ flutter: dependencies: flutter: sdk: flutter + path: ^1.8.0 webview_flutter_platform_interface: ^1.8.0 dev_dependencies: diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 358df898403a..e30e027734d0 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -3,19 +3,24 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:math'; +import 'dart:typed_data'; +import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; +import 'package:webview_flutter_wkwebview/src/ui_kit/ui_kit.dart'; import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart'; import 'web_kit_webview_widget_test.mocks.dart'; @GenerateMocks([ + UIScrollView, WKNavigationDelegate, WKScriptMessageHandler, WKWebView, @@ -36,6 +41,7 @@ void main() { late MockWKUserContentController mockUserContentController; late MockWKWebViewConfiguration mockWebViewConfiguration; late MockWKUIDelegate mockUIDelegate; + late MockUIScrollView mockScrollView; late MockWKWebsiteDataStore mockWebsiteDataStore; late MockWKNavigationDelegate mockNavigationDelegate; @@ -49,6 +55,7 @@ void main() { mockWebViewConfiguration = MockWKWebViewConfiguration(); mockUserContentController = MockWKUserContentController(); mockUIDelegate = MockWKUIDelegate(); + mockScrollView = MockUIScrollView(); mockWebsiteDataStore = MockWKWebsiteDataStore(); mockNavigationDelegate = MockWKNavigationDelegate(); mockWebViewWidgetProxy = MockWebViewWidgetProxy(); @@ -61,6 +68,9 @@ void main() { when(mockWebViewConfiguration.userContentController).thenReturn( mockUserContentController, ); + + when(mockWebView.scrollView).thenReturn(mockScrollView); + when(mockWebViewConfiguration.webSiteDataStore).thenReturn( mockWebsiteDataStore, ); @@ -212,6 +222,345 @@ void main() { }); group('$WebKitWebViewPlatformController', () { + testWidgets('loadFile', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.loadFile('/path/to/file.html'); + verify(mockWebView.loadFileUrl( + '/path/to/file.html', + readAccessUrl: '/path/to', + )); + }); + + testWidgets('loadFlutterAsset', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.loadFlutterAsset('test_assets/index.html'); + verify(mockWebView.loadFlutterAsset('test_assets/index.html')); + }); + + testWidgets('loadHtmlString', (WidgetTester tester) async { + await buildWidget(tester); + + const String htmlString = 'Test data.'; + await testController.loadHtmlString(htmlString, baseUrl: 'baseUrl'); + + verify(mockWebView.loadHtmlString( + 'Test data.', + baseUrl: 'baseUrl', + )); + }); + + testWidgets('loadUrl', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.loadUrl( + 'https://www.google.com', + {'a': 'header'}, + ); + + final NSUrlRequest request = verify(mockWebView.loadRequest(captureAny)) + .captured + .single as NSUrlRequest; + expect(request.url, 'https://www.google.com'); + expect(request.allHttpHeaderFields, {'a': 'header'}); + }); + + group('loadRequest', () { + testWidgets('Throws ArgumentError for empty scheme', + (WidgetTester tester) async { + await buildWidget(tester); + + expect( + () async => await testController.loadRequest( + WebViewRequest( + uri: Uri.parse('www.google.com'), + method: WebViewRequestMethod.get, + ), + ), + throwsA(const TypeMatcher())); + }); + + testWidgets('GET without headers', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.loadRequest(WebViewRequest( + uri: Uri.parse('https://www.google.com'), + method: WebViewRequestMethod.get, + )); + + final NSUrlRequest request = + verify(mockWebView.loadRequest(captureAny)).captured.single + as NSUrlRequest; + expect(request.url, 'https://www.google.com'); + expect(request.allHttpHeaderFields, {}); + expect(request.httpMethod, 'get'); + }); + + testWidgets('GET with headers', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.loadRequest(WebViewRequest( + uri: Uri.parse('https://www.google.com'), + method: WebViewRequestMethod.get, + headers: {'a': 'header'}, + )); + + final NSUrlRequest request = + verify(mockWebView.loadRequest(captureAny)).captured.single + as NSUrlRequest; + expect(request.url, 'https://www.google.com'); + expect(request.allHttpHeaderFields, {'a': 'header'}); + expect(request.httpMethod, 'get'); + }); + + testWidgets('POST without body', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.loadRequest(WebViewRequest( + uri: Uri.parse('https://www.google.com'), + method: WebViewRequestMethod.post, + )); + + final NSUrlRequest request = + verify(mockWebView.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); + + await testController.loadRequest(WebViewRequest( + uri: Uri.parse('https://www.google.com'), + method: WebViewRequestMethod.post, + body: Uint8List.fromList('Test Body'.codeUnits))); + + final NSUrlRequest request = + verify(mockWebView.loadRequest(captureAny)).captured.single + as NSUrlRequest; + expect(request.url, 'https://www.google.com'); + expect(request.httpMethod, 'post'); + expect( + request.httpBody, + Uint8List.fromList('Test Body'.codeUnits), + ); + }); + }); + + testWidgets('canGoBack', (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.canGoBack).thenAnswer( + (_) => Future.value(false), + ); + expect(testController.canGoBack(), completion(false)); + }); + + testWidgets('canGoForward', (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.canGoForward).thenAnswer( + (_) => Future.value(true), + ); + expect(testController.canGoForward(), completion(true)); + }); + + testWidgets('goBack', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.goBack(); + verify(mockWebView.goBack()); + }); + + testWidgets('goForward', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.goForward(); + verify(mockWebView.goForward()); + }); + + testWidgets('reload', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.reload(); + verify(mockWebView.reload()); + }); + + testWidgets('evaluateJavascript', (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value('returnString'), + ); + expect( + testController.evaluateJavascript('runJavaScript'), + completion('returnString'), + ); + }); + + testWidgets('evaluateJavascript with null return value', + (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value(), + ); + // The legacy implementation of webview_flutter_wkwebview would convert + // objects to strings before returning them to Dart. This verifies null + // is represented the way it is in Objective-C. + expect( + testController.evaluateJavascript('runJavaScript'), + completion('(null)'), + ); + }); + + testWidgets('evaluateJavascript with list return value', + (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value([1, 'string', null]), + ); + // The legacy implementation of webview_flutter_wkwebview would convert + // objects to strings before returning them to Dart. This verifies list + // is represented the way it is in Objective-C. + expect( + testController.evaluateJavascript('runJavaScript'), + completion('(1,string,"")'), + ); + }); + + testWidgets('evaluateJavascript with map return value', + (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value({ + 1: 'string', + null: null, + }), + ); + // The legacy implementation of webview_flutter_wkwebview would convert + // objects to strings before returning them to Dart. This verifies map + // is represented the way it is in Objective-C. + expect( + testController.evaluateJavascript('runJavaScript'), + completion('{1 = string;"" = ""}'), + ); + }); + + testWidgets('evaluateJavascript throws exception', + (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')) + .thenThrow(Error()); + expect( + testController.evaluateJavascript('runJavaScript'), + throwsA(isA()), + ); + }); + + testWidgets('runJavascriptReturningResult', (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value('returnString'), + ); + expect( + testController.runJavascriptReturningResult('runJavaScript'), + completion('returnString'), + ); + }); + + testWidgets( + 'runJavascriptReturningResult throws error on null return value', + (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value(null), + ); + expect( + () => testController.runJavascriptReturningResult('runJavaScript'), + throwsArgumentError, + ); + }); + + testWidgets('runJavascript', (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value('returnString'), + ); + expect( + testController.runJavascript('runJavaScript'), + completes, + ); + }); + + testWidgets( + 'runJavascript ignores exception with unsupported javascript type', + (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')) + .thenThrow(PlatformException( + code: '', + details: const NSError( + code: WKErrorCode.javaScriptResultTypeIsUnsupported, + domain: '', + localizedDescription: '', + ), + )); + expect( + testController.runJavascript('runJavaScript'), + completes, + ); + }); + + testWidgets('getTitle', (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.title) + .thenAnswer((_) => Future.value('Web Title')); + expect(testController.getTitle(), completion('Web Title')); + }); + + testWidgets('scrollTo', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.scrollTo(2, 4); + verify(mockScrollView.contentOffset = const Point(2.0, 4.0)); + }); + + testWidgets('scrollBy', (WidgetTester tester) async { + await buildWidget(tester); + + await testController.scrollBy(2, 4); + verify(mockScrollView.scrollBy(const Point(2.0, 4.0))); + }); + + testWidgets('getScrollX', (WidgetTester tester) async { + await buildWidget(tester); + + when(mockScrollView.contentOffset).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); + + when(mockScrollView.contentOffset).thenAnswer( + (_) => Future>.value(const Point(8.0, 16.0))); + expect(testController.getScrollY(), completion(16.0)); + }); + testWidgets('clearCache', (WidgetTester tester) async { await buildWidget(tester); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index 32f1731701a9..f200870ca98b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -2,19 +2,21 @@ // in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. // Do not manually edit this file. -import 'dart:async' as _i3; +import 'dart:async' as _i5; +import 'dart:math' as _i2; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_platform_interface/src/types/javascript_channel.dart' - as _i6; -import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i7; + as _i8; +import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i9; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart' - as _i5; + as _i7; import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart' - as _i4; -import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i2; + as _i6; +import 'package:webview_flutter_wkwebview/src/ui_kit/ui_kit.dart' as _i4; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i3; import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart' - as _i8; + as _i10; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -26,37 +28,65 @@ import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart' // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types -class _FakeWKWebViewConfiguration_0 extends _i1.Fake - implements _i2.WKWebViewConfiguration {} +class _FakePoint_0 extends _i1.Fake implements _i2.Point {} + +class _FakeWKWebViewConfiguration_1 extends _i1.Fake + implements _i3.WKWebViewConfiguration {} + +class _FakeUIScrollView_2 extends _i1.Fake implements _i4.UIScrollView {} -class _FakeWKUserContentController_1 extends _i1.Fake - implements _i2.WKUserContentController {} +class _FakeWKUserContentController_3 extends _i1.Fake + implements _i3.WKUserContentController {} -class _FakeWKWebsiteDataStore_2 extends _i1.Fake - implements _i2.WKWebsiteDataStore {} +class _FakeWKWebsiteDataStore_4 extends _i1.Fake + implements _i3.WKWebsiteDataStore {} -class _FakeWKWebView_3 extends _i1.Fake implements _i2.WKWebView {} +class _FakeWKWebView_5 extends _i1.Fake implements _i3.WKWebView {} -class _FakeWKScriptMessageHandler_4 extends _i1.Fake - implements _i2.WKScriptMessageHandler {} +class _FakeWKScriptMessageHandler_6 extends _i1.Fake + implements _i3.WKScriptMessageHandler {} -class _FakeWKUIDelegate_5 extends _i1.Fake implements _i2.WKUIDelegate {} +class _FakeWKUIDelegate_7 extends _i1.Fake implements _i3.WKUIDelegate {} -class _FakeWKNavigationDelegate_6 extends _i1.Fake - implements _i2.WKNavigationDelegate {} +class _FakeWKNavigationDelegate_8 extends _i1.Fake + implements _i3.WKNavigationDelegate {} + +/// A class which mocks [UIScrollView]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockUIScrollView extends _i1.Mock implements _i4.UIScrollView { + MockUIScrollView() { + _i1.throwOnMissingStub(this); + } + + @override + _i5.Future<_i2.Point> get contentOffset => (super.noSuchMethod( + Invocation.getter(#contentOffset), + returnValue: Future<_i2.Point>.value(_FakePoint_0())) + as _i5.Future<_i2.Point>); + @override + set contentOffset(_i5.FutureOr<_i2.Point>? offset) => + super.noSuchMethod(Invocation.setter(#contentOffset, offset), + returnValueForMissingStub: null); + @override + _i5.Future scrollBy(_i2.Point? offset) => + (super.noSuchMethod(Invocation.method(#scrollBy, [offset]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); +} /// A class which mocks [WKNavigationDelegate]. /// /// See the documentation for Mockito's code generation for more information. class MockWKNavigationDelegate extends _i1.Mock - implements _i2.WKNavigationDelegate { + implements _i3.WKNavigationDelegate { MockWKNavigationDelegate() { _i1.throwOnMissingStub(this); } @override set didStartProvisionalNavigation( - void Function(_i2.WKWebView, String?)? + void Function(_i3.WKWebView, String?)? didStartProvisionalNavigation) => super.noSuchMethod( Invocation.setter( @@ -64,14 +94,14 @@ class MockWKNavigationDelegate extends _i1.Mock returnValueForMissingStub: null); @override set didFinishNavigation( - void Function(_i2.WKWebView, String?)? didFinishNavigation) => + void Function(_i3.WKWebView, String?)? didFinishNavigation) => super.noSuchMethod( Invocation.setter(#didFinishNavigation, didFinishNavigation), returnValueForMissingStub: null); @override set decidePolicyForNavigationAction( - _i3.Future<_i2.WKNavigationActionPolicy> Function( - _i2.WKWebView, _i2.WKNavigationAction)? + _i5.Future<_i3.WKNavigationActionPolicy> Function( + _i3.WKWebView, _i3.WKNavigationAction)? decidePolicyForNavigationAction) => super.noSuchMethod( Invocation.setter(#decidePolicyForNavigationAction, @@ -79,13 +109,13 @@ class MockWKNavigationDelegate extends _i1.Mock returnValueForMissingStub: null); @override set didFailNavigation( - void Function(_i2.WKWebView, _i4.NSError)? didFailNavigation) => + void Function(_i3.WKWebView, _i6.NSError)? didFailNavigation) => super.noSuchMethod( Invocation.setter(#didFailNavigation, didFailNavigation), returnValueForMissingStub: null); @override set didFailProvisionalNavigation( - void Function(_i2.WKWebView, _i4.NSError)? + void Function(_i3.WKWebView, _i6.NSError)? didFailProvisionalNavigation) => super.noSuchMethod( Invocation.setter( @@ -93,7 +123,7 @@ class MockWKNavigationDelegate extends _i1.Mock returnValueForMissingStub: null); @override set webViewWebContentProcessDidTerminate( - void Function(_i2.WKWebView)? webViewWebContentProcessDidTerminate) => + void Function(_i3.WKWebView)? webViewWebContentProcessDidTerminate) => super.noSuchMethod( Invocation.setter(#webViewWebContentProcessDidTerminate, webViewWebContentProcessDidTerminate), @@ -104,14 +134,14 @@ class MockWKNavigationDelegate extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWKScriptMessageHandler extends _i1.Mock - implements _i2.WKScriptMessageHandler { + implements _i3.WKScriptMessageHandler { MockWKScriptMessageHandler() { _i1.throwOnMissingStub(this); } @override set didReceiveScriptMessage( - void Function(_i2.WKUserContentController, _i2.WKScriptMessage)? + void Function(_i3.WKUserContentController, _i3.WKScriptMessage)? didReceiveScriptMessage) => super.noSuchMethod( Invocation.setter(#didReceiveScriptMessage, didReceiveScriptMessage), @@ -121,60 +151,125 @@ class MockWKScriptMessageHandler extends _i1.Mock /// A class which mocks [WKWebView]. /// /// See the documentation for Mockito's code generation for more information. -class MockWKWebView extends _i1.Mock implements _i2.WKWebView { +class MockWKWebView extends _i1.Mock implements _i3.WKWebView { MockWKWebView() { _i1.throwOnMissingStub(this); } @override - _i2.WKWebViewConfiguration get configuration => + _i3.WKWebViewConfiguration get configuration => (super.noSuchMethod(Invocation.getter(#configuration), - returnValue: _FakeWKWebViewConfiguration_0()) - as _i2.WKWebViewConfiguration); + returnValue: _FakeWKWebViewConfiguration_1()) + as _i3.WKWebViewConfiguration); + @override + _i4.UIScrollView get scrollView => + (super.noSuchMethod(Invocation.getter(#scrollView), + returnValue: _FakeUIScrollView_2()) as _i4.UIScrollView); @override - set uiDelegate(_i2.WKUIDelegate? delegate) => + set uiDelegate(_i3.WKUIDelegate? delegate) => super.noSuchMethod(Invocation.setter(#uiDelegate, delegate), returnValueForMissingStub: null); @override - set navigationDelegate(_i2.WKNavigationDelegate? delegate) => + set navigationDelegate(_i3.WKNavigationDelegate? delegate) => super.noSuchMethod(Invocation.setter(#navigationDelegate, delegate), returnValueForMissingStub: null); @override - _i3.Future get url => (super.noSuchMethod(Invocation.getter(#url), - returnValue: Future.value()) as _i3.Future); + _i5.Future get url => (super.noSuchMethod(Invocation.getter(#url), + returnValue: Future.value()) as _i5.Future); + @override + _i5.Future get canGoBack => + (super.noSuchMethod(Invocation.getter(#canGoBack), + returnValue: Future.value(false)) as _i5.Future); + @override + _i5.Future get canGoForward => + (super.noSuchMethod(Invocation.getter(#canGoForward), + returnValue: Future.value(false)) as _i5.Future); @override - _i3.Future loadRequest(_i4.NSUrlRequest? request) => + _i5.Future get title => + (super.noSuchMethod(Invocation.getter(#title), + returnValue: Future.value()) as _i5.Future); + @override + _i5.Future get estimatedProgress => + (super.noSuchMethod(Invocation.getter(#estimatedProgress), + returnValue: Future.value(0.0)) as _i5.Future); + @override + set allowsBackForwardNavigationGestures(bool? allow) => super.noSuchMethod( + Invocation.setter(#allowsBackForwardNavigationGestures, allow), + returnValueForMissingStub: null); + @override + set customUserAgent(String? userAgent) => + super.noSuchMethod(Invocation.setter(#customUserAgent, userAgent), + returnValueForMissingStub: null); + @override + _i5.Future loadRequest(_i6.NSUrlRequest? request) => (super.noSuchMethod(Invocation.method(#loadRequest, [request]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future loadHtmlString(String? string, {String? baseUrl}) => + (super.noSuchMethod( + Invocation.method(#loadHtmlString, [string], {#baseUrl: baseUrl}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future loadFileUrl(String? url, {String? readAccessUrl}) => + (super.noSuchMethod( + Invocation.method( + #loadFileUrl, [url], {#readAccessUrl: readAccessUrl}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future loadFlutterAsset(String? key) => + (super.noSuchMethod(Invocation.method(#loadFlutterAsset, [key]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future goBack() => + (super.noSuchMethod(Invocation.method(#goBack, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future goForward() => + (super.noSuchMethod(Invocation.method(#goForward, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future reload() => + (super.noSuchMethod(Invocation.method(#reload, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future evaluateJavaScript(String? javaScriptString) => (super + .noSuchMethod(Invocation.method(#evaluateJavaScript, [javaScriptString]), + returnValue: Future.value()) as _i5.Future); } /// A class which mocks [WKWebViewConfiguration]. /// /// See the documentation for Mockito's code generation for more information. class MockWKWebViewConfiguration extends _i1.Mock - implements _i2.WKWebViewConfiguration { + implements _i3.WKWebViewConfiguration { MockWKWebViewConfiguration() { _i1.throwOnMissingStub(this); } @override - _i2.WKUserContentController get userContentController => + _i3.WKUserContentController get userContentController => (super.noSuchMethod(Invocation.getter(#userContentController), - returnValue: _FakeWKUserContentController_1()) - as _i2.WKUserContentController); + returnValue: _FakeWKUserContentController_3()) + as _i3.WKUserContentController); @override set userContentController( - _i2.WKUserContentController? _userContentController) => + _i3.WKUserContentController? _userContentController) => super.noSuchMethod( Invocation.setter(#userContentController, _userContentController), returnValueForMissingStub: null); @override - _i2.WKWebsiteDataStore get webSiteDataStore => + _i3.WKWebsiteDataStore get webSiteDataStore => (super.noSuchMethod(Invocation.getter(#webSiteDataStore), - returnValue: _FakeWKWebsiteDataStore_2()) as _i2.WKWebsiteDataStore); + returnValue: _FakeWKWebsiteDataStore_4()) as _i3.WKWebsiteDataStore); @override - set webSiteDataStore(_i2.WKWebsiteDataStore? websiteDataStore) => + set webSiteDataStore(_i3.WKWebsiteDataStore? websiteDataStore) => super.noSuchMethod(Invocation.setter(#webSiteDataStore, websiteDataStore), returnValueForMissingStub: null); @override @@ -183,7 +278,7 @@ class MockWKWebViewConfiguration extends _i1.Mock returnValueForMissingStub: null); @override set mediaTypesRequiringUserActionForPlayback( - Set<_i2.WKAudiovisualMediaType>? types) => + Set<_i3.WKAudiovisualMediaType>? types) => super.noSuchMethod( Invocation.setter(#mediaTypesRequiringUserActionForPlayback, types), returnValueForMissingStub: null); @@ -193,31 +288,31 @@ class MockWKWebViewConfiguration extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWKWebsiteDataStore extends _i1.Mock - implements _i2.WKWebsiteDataStore { + implements _i3.WKWebsiteDataStore { MockWKWebsiteDataStore() { _i1.throwOnMissingStub(this); } @override - _i3.Future removeDataOfTypes( - Set<_i2.WKWebsiteDataTypes>? dataTypes, DateTime? since) => + _i5.Future removeDataOfTypes( + Set<_i3.WKWebsiteDataTypes>? dataTypes, DateTime? since) => (super.noSuchMethod( Invocation.method(#removeDataOfTypes, [dataTypes, since]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKUIDelegate]. /// /// See the documentation for Mockito's code generation for more information. -class MockWKUIDelegate extends _i1.Mock implements _i2.WKUIDelegate { +class MockWKUIDelegate extends _i1.Mock implements _i3.WKUIDelegate { MockWKUIDelegate() { _i1.throwOnMissingStub(this); } @override set onCreateWebView( - void Function(_i2.WKWebViewConfiguration, _i2.WKNavigationAction)? + void Function(_i3.WKWebViewConfiguration, _i3.WKNavigationAction)? onCreateeWebView) => super.noSuchMethod(Invocation.setter(#onCreateWebView, onCreateeWebView), returnValueForMissingStub: null); @@ -227,61 +322,61 @@ class MockWKUIDelegate extends _i1.Mock implements _i2.WKUIDelegate { /// /// See the documentation for Mockito's code generation for more information. class MockWKUserContentController extends _i1.Mock - implements _i2.WKUserContentController { + implements _i3.WKUserContentController { MockWKUserContentController() { _i1.throwOnMissingStub(this); } @override - _i3.Future addScriptMessageHandler( - _i2.WKScriptMessageHandler? handler, String? name) => + _i5.Future addScriptMessageHandler( + _i3.WKScriptMessageHandler? handler, String? name) => (super.noSuchMethod( Invocation.method(#addScriptMessageHandler, [handler, name]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i3.Future removeScriptMessageHandler(String? name) => (super + _i5.Future removeScriptMessageHandler(String? name) => (super .noSuchMethod(Invocation.method(#removeScriptMessageHandler, [name]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i3.Future removeAllScriptMessageHandlers() => (super.noSuchMethod( + _i5.Future removeAllScriptMessageHandlers() => (super.noSuchMethod( Invocation.method(#removeAllScriptMessageHandlers, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i3.Future addUserScript(_i2.WKUserScript? userScript) => + _i5.Future addUserScript(_i3.WKUserScript? userScript) => (super.noSuchMethod(Invocation.method(#addUserScript, [userScript]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i3.Future removeAllUserScripts() => + _i5.Future removeAllUserScripts() => (super.noSuchMethod(Invocation.method(#removeAllUserScripts, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [JavascriptChannelRegistry]. /// /// See the documentation for Mockito's code generation for more information. class MockJavascriptChannelRegistry extends _i1.Mock - implements _i5.JavascriptChannelRegistry { + implements _i7.JavascriptChannelRegistry { MockJavascriptChannelRegistry() { _i1.throwOnMissingStub(this); } @override - Map get channels => + Map get channels => (super.noSuchMethod(Invocation.getter(#channels), - returnValue: {}) - as Map); + returnValue: {}) + as Map); @override void onJavascriptChannelMessage(String? channel, String? message) => super.noSuchMethod( Invocation.method(#onJavascriptChannelMessage, [channel, message]), returnValueForMissingStub: null); @override - void updateJavascriptChannelsFromSet(Set<_i6.JavascriptChannel>? channels) => + void updateJavascriptChannelsFromSet(Set<_i8.JavascriptChannel>? channels) => super.noSuchMethod( Invocation.method(#updateJavascriptChannelsFromSet, [channels]), returnValueForMissingStub: null); @@ -291,17 +386,17 @@ class MockJavascriptChannelRegistry extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewPlatformCallbacksHandler extends _i1.Mock - implements _i5.WebViewPlatformCallbacksHandler { + implements _i7.WebViewPlatformCallbacksHandler { MockWebViewPlatformCallbacksHandler() { _i1.throwOnMissingStub(this); } @override - _i3.FutureOr onNavigationRequest({String? url, bool? isForMainFrame}) => + _i5.FutureOr onNavigationRequest({String? url, bool? isForMainFrame}) => (super.noSuchMethod( Invocation.method(#onNavigationRequest, [], {#url: url, #isForMainFrame: isForMainFrame}), - returnValue: Future.value(false)) as _i3.FutureOr); + returnValue: Future.value(false)) as _i5.FutureOr); @override void onPageStarted(String? url) => super.noSuchMethod(Invocation.method(#onPageStarted, [url]), @@ -315,7 +410,7 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock super.noSuchMethod(Invocation.method(#onProgress, [progress]), returnValueForMissingStub: null); @override - void onWebResourceError(_i7.WebResourceError? error) => + void onWebResourceError(_i9.WebResourceError? error) => super.noSuchMethod(Invocation.method(#onWebResourceError, [error]), returnValueForMissingStub: null); } @@ -324,26 +419,26 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewWidgetProxy extends _i1.Mock - implements _i8.WebViewWidgetProxy { + implements _i10.WebViewWidgetProxy { MockWebViewWidgetProxy() { _i1.throwOnMissingStub(this); } @override - _i2.WKWebView createWebView(_i2.WKWebViewConfiguration? configuration) => + _i3.WKWebView createWebView(_i3.WKWebViewConfiguration? configuration) => (super.noSuchMethod(Invocation.method(#createWebView, [configuration]), - returnValue: _FakeWKWebView_3()) as _i2.WKWebView); + returnValue: _FakeWKWebView_5()) as _i3.WKWebView); @override - _i2.WKScriptMessageHandler createScriptMessageHandler() => + _i3.WKScriptMessageHandler createScriptMessageHandler() => (super.noSuchMethod(Invocation.method(#createScriptMessageHandler, []), - returnValue: _FakeWKScriptMessageHandler_4()) - as _i2.WKScriptMessageHandler); + returnValue: _FakeWKScriptMessageHandler_6()) + as _i3.WKScriptMessageHandler); @override - _i2.WKUIDelegate createUIDelgate() => + _i3.WKUIDelegate createUIDelgate() => (super.noSuchMethod(Invocation.method(#createUIDelgate, []), - returnValue: _FakeWKUIDelegate_5()) as _i2.WKUIDelegate); + returnValue: _FakeWKUIDelegate_7()) as _i3.WKUIDelegate); @override - _i2.WKNavigationDelegate createNavigationDelegate() => (super.noSuchMethod( + _i3.WKNavigationDelegate createNavigationDelegate() => (super.noSuchMethod( Invocation.method(#createNavigationDelegate, []), - returnValue: _FakeWKNavigationDelegate_6()) as _i2.WKNavigationDelegate); + returnValue: _FakeWKNavigationDelegate_8()) as _i3.WKNavigationDelegate); } From c9668c4dde62c5291b84b38d6a196a3416ed7830 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 24 Mar 2022 12:08:58 -0700 Subject: [PATCH 066/844] [webview_flutter_wkwebview] Add progress tracking support for flutter/flutter#93732 (#5101) --- .../lib/src/foundation/foundation.dart | 110 ++++++++++++++++++ .../lib/src/web_kit/web_kit.dart | 12 +- .../lib/src/web_kit_webview_widget.dart | 26 +++++ .../test/src/web_kit_webview_widget_test.dart | 27 +++++ .../web_kit_webview_widget_test.mocks.dart | 25 ++++ 5 files changed, 199 insertions(+), 1 deletion(-) 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 dc933a072b7f..2d80ea20b636 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 @@ -6,6 +6,86 @@ import 'dart:typed_data'; import 'package:flutter/foundation.dart'; +/// The values that can be returned in a change map. +/// +/// Wraps [NSKeyValueObservingOptions](https://developer.apple.com/documentation/foundation/nskeyvalueobservingoptions?language=objc). +enum NSKeyValueObservingOptions { + /// Indicates that the change map should provide the new attribute value, if applicable. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvalueobservingoptions/nskeyvalueobservingoptionnew?language=objc. + newValue, + + /// Indicates that the change map should contain the old attribute value, if applicable. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvalueobservingoptions/nskeyvalueobservingoptionold?language=objc. + oldValue, + + /// Indicates a notification should be sent to the observer immediately. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvalueobservingoptions/nskeyvalueobservingoptioninitial?language=objc. + initialValue, + + /// Whether separate notifications should be sent to the observer before and after each change. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvalueobservingoptions/nskeyvalueobservingoptionprior?language=objc. + priorNotification, +} + +/// The kinds of changes that can be observed. +/// +/// Wraps [NSKeyValueChange](https://developer.apple.com/documentation/foundation/nskeyvaluechange?language=objc). +enum NSKeyValueChange { + /// Indicates that the value of the observed key path was set to a new value. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvaluechange/nskeyvaluechangesetting?language=objc. + setting, + + /// Indicates that an object has been inserted into the to-many relationship that is being observed. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvaluechange/nskeyvaluechangeinsertion?language=objc. + insertion, + + /// Indicates that an object has been removed from the to-many relationship that is being observed. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvaluechange/nskeyvaluechangeremoval?language=objc. + removal, + + /// Indicates that an object has been replaced in the to-many relationship that is being observed. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvaluechange/nskeyvaluechangereplacement?language=objc. + replacement, +} + +/// The keys that can appear in the change map. +/// +/// Wraps [NSKeyValueChangeKey](https://developer.apple.com/documentation/foundation/nskeyvaluechangekey?language=objc). +enum NSKeyValueChangeKey { + /// Indicates changes made in a collection. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvaluechangeindexeskey?language=objc. + indexes, + + /// Indicates what sort of change has occurred. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvaluechangekindkey?language=objc. + kind, + + /// Indicates the new value for the attribute. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvaluechangenewkey?language=objc. + newValue, + + /// Indicates a notification is sent prior to a change. + /// + /// See https://developer.apple.com/documentation/foundation/nskeyvaluechangenotificationispriorkey?language=objc. + notificationIsPrior, + + /// Indicates the value of this key is the value before the attribute was changed. + /// + /// https://developer.apple.com/documentation/foundation/nskeyvaluechangeoldkey?language=objc. + oldValue, +} + /// A URL load request that is independent of protocol or URL scheme. /// /// Wraps [NSUrlRequest](https://developer.apple.com/documentation/foundation/nsurlrequest?language=objc). @@ -57,3 +137,33 @@ class NSError { /// A string containing the localized description of the error. final String localizedDescription; } + +/// The root class of most Objective-C class hierarchies. +class NSObject { + /// Registers the observer object to receive KVO notifications. + Future addObserver( + NSObject observer, { + required String keyPath, + required Set options, + }) { + assert(options.isNotEmpty); + throw UnimplementedError(); + } + + /// Stops the observer object from receiving change notifications for the property. + Future removeObserver(NSObject observer, {required String keyPath}) { + throw UnimplementedError(); + } + + /// Informs the observing object when the value at the specified key path has changed. + set observeValue( + void Function( + String keyPath, + NSObject object, + Map change, + )? + observeValue, + ) { + throw UnimplementedError(); + } +} 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 cd7e4a9aadcb..94b1533fb3b2 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 @@ -423,7 +423,7 @@ class WKNavigationDelegate { /// Object that displays interactive web content, such as for an in-app browser. /// /// Wraps [WKWebView](https://developer.apple.com/documentation/webkit/wkwebview?language=objc). -class WKWebView { +class WKWebView extends NSObject { /// Constructs a [WKWebView]. /// /// [configuration] contains the configuration details for the web view. This @@ -469,6 +469,16 @@ class WKWebView { throw UnimplementedError(); } + /// An estimate of what fraction of the current navigation has been loaded. + /// + /// This value ranges from 0.0 to 1.0. + /// + /// This method represents + /// [WKWebView.estimatedProgress](https://developer.apple.com/documentation/webkit/wkwebview/1415007-estimatedprogress?language=objc). + Future getEstimatedProgress() { + throw UnimplementedError(); + } + /// Loads the web content referenced by the specified URL request object and navigates to it. /// /// Use this method to load a page from a local or network-based URL. For diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 67ea6a936e42..58c279cd56ba 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -346,6 +346,9 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { if (setting.hasNavigationDelegate != null) { _setHasNavigationDelegate(setting.hasNavigationDelegate!); } + if (setting.hasProgressTracking != null) { + _setHasProgressTracking(setting.hasProgressTracking!); + } } @override @@ -430,6 +433,29 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { } } + Future _setHasProgressTracking(bool hasProgressTracking) { + if (hasProgressTracking) { + webView.observeValue = ( + String keyPath, + NSObject object, + Map change, + ) { + final double progress = change[NSKeyValueChangeKey.newValue]! as double; + callbacksHandler.onProgress((progress * 100).round()); + }; + return webView.addObserver( + webView, + keyPath: 'estimatedProgress', + options: { + NSKeyValueObservingOptions.newValue, + }, + ); + } else { + webView.observeValue = null; + return webView.removeObserver(webView, keyPath: 'estimatedProgress'); + } + } + static WebResourceError _toWebResourceError(NSError error) { WebResourceErrorType? errorType; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index e30e027734d0..9d8c810b9edc 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -799,6 +799,33 @@ void main() { isForMainFrame: false, )); }); + + testWidgets('onProgress', (WidgetTester tester) async { + await buildWidget(tester, hasProgressTracking: true); + final dynamic observeValue = + verify(mockWebView.observeValue = captureAny).captured.single + as void Function( + String keyPath, + NSObject object, + Map change, + ); + + verify(mockWebView.addObserver( + mockWebView, + keyPath: 'estimatedProgress', + options: { + NSKeyValueObservingOptions.newValue, + }, + )); + + observeValue( + 'estimatedProgress', + mockWebView, + {NSKeyValueChangeKey.newValue: 0.32}, + ); + + verify(mockCallbacksHandler.onProgress(32)); + }); }); group('$JavascriptChannelRegistry', () { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index f200870ca98b..c63aef9026a3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -201,6 +201,17 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { super.noSuchMethod(Invocation.setter(#customUserAgent, userAgent), returnValueForMissingStub: null); @override + set observeValue( + void Function( + String, _i6.NSObject, Map<_i6.NSKeyValueChangeKey, Object?>)? + observeValue) => + super.noSuchMethod(Invocation.setter(#observeValue, observeValue), + returnValueForMissingStub: null); + @override + _i5.Future getEstimatedProgress() => + (super.noSuchMethod(Invocation.method(#getEstimatedProgress, []), + returnValue: Future.value(0.0)) as _i5.Future); + @override _i5.Future loadRequest(_i6.NSUrlRequest? request) => (super.noSuchMethod(Invocation.method(#loadRequest, [request]), returnValue: Future.value(), @@ -242,6 +253,20 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { _i5.Future evaluateJavaScript(String? javaScriptString) => (super .noSuchMethod(Invocation.method(#evaluateJavaScript, [javaScriptString]), returnValue: Future.value()) as _i5.Future); + @override + _i5.Future addObserver(_i6.NSObject? observer, + {String? keyPath, Set<_i6.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future removeObserver(_i6.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKWebViewConfiguration]. From bb9dc2254b70a0b0bc2d797c7cc067105bf602f8 Mon Sep 17 00:00:00 2001 From: Abdelaziz Mahdy Date: Thu, 24 Mar 2022 22:15:12 +0200 Subject: [PATCH 067/844] [Video_player] Upgraded to exoPlayer 2.17.0 (#5011) --- .../video_player_android/CHANGELOG.md | 4 ++++ .../video_player_android/android/build.gradle | 8 +++---- .../plugins/videoplayer/VideoPlayer.java | 23 +++++++++---------- .../video_player_android/pubspec.yaml | 2 +- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 774589c05f84..1f0f06f739b2 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.2 + +* Updates ExoPlayer to 2.17.0. + ## 2.3.1 * Renames internal method channels to avoid potential confusion with the diff --git a/packages/video_player/video_player_android/android/build.gradle b/packages/video_player/video_player_android/android/build.gradle index 6e6e8c792150..e565a9364bd8 100644 --- a/packages/video_player/video_player_android/android/build.gradle +++ b/packages/video_player/video_player_android/android/build.gradle @@ -43,10 +43,10 @@ android { } dependencies { - implementation 'com.google.android.exoplayer:exoplayer-core:2.14.1' - implementation 'com.google.android.exoplayer:exoplayer-hls:2.14.1' - implementation 'com.google.android.exoplayer:exoplayer-dash:2.14.1' - implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.14.1' + implementation 'com.google.android.exoplayer:exoplayer-core:2.17.0' + implementation 'com.google.android.exoplayer:exoplayer-hls:2.17.0' + implementation 'com.google.android.exoplayer:exoplayer-dash:2.17.0' + implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.17.0' testImplementation 'junit:junit:4.12' testImplementation 'org.mockito:mockito-inline:3.9.0' } diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java index 33593267338c..dc7c88144583 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java @@ -12,13 +12,13 @@ import android.view.Surface; import androidx.annotation.NonNull; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.ExoPlaybackException; +import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.MediaItem; +import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player.Listener; -import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.ProgressiveMediaSource; @@ -28,7 +28,7 @@ import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource; import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; import com.google.android.exoplayer2.upstream.DataSource; -import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; +import com.google.android.exoplayer2.upstream.DefaultDataSource; import com.google.android.exoplayer2.upstream.DefaultHttpDataSource; import com.google.android.exoplayer2.util.Util; import io.flutter.plugin.common.EventChannel; @@ -45,7 +45,7 @@ final class VideoPlayer { private static final String FORMAT_HLS = "hls"; private static final String FORMAT_OTHER = "other"; - private SimpleExoPlayer exoPlayer; + private ExoPlayer exoPlayer; private Surface surface; @@ -71,10 +71,9 @@ final class VideoPlayer { this.textureEntry = textureEntry; this.options = options; - exoPlayer = new SimpleExoPlayer.Builder(context).build(); + exoPlayer = new ExoPlayer.Builder(context).build(); Uri uri = Uri.parse(dataSource); - DataSource.Factory dataSourceFactory; if (isHTTP(uri)) { DefaultHttpDataSource.Factory httpDataSourceFactory = @@ -87,7 +86,7 @@ final class VideoPlayer { } dataSourceFactory = httpDataSourceFactory; } else { - dataSourceFactory = new DefaultDataSourceFactory(context, "ExoPlayer"); + dataSourceFactory = new DefaultDataSource.Factory(context); } MediaSource mediaSource = buildMediaSource(uri, dataSourceFactory, formatHint, context); @@ -107,6 +106,7 @@ private static boolean isHTTP(Uri uri) { private MediaSource buildMediaSource( Uri uri, DataSource.Factory mediaDataSourceFactory, String formatHint, Context context) { + int type; if (formatHint == null) { type = Util.inferContentType(uri.getLastPathSegment()); @@ -133,12 +133,12 @@ private MediaSource buildMediaSource( case C.TYPE_SS: return new SsMediaSource.Factory( new DefaultSsChunkSource.Factory(mediaDataSourceFactory), - new DefaultDataSourceFactory(context, null, mediaDataSourceFactory)) + new DefaultDataSource.Factory(context, mediaDataSourceFactory)) .createMediaSource(MediaItem.fromUri(uri)); case C.TYPE_DASH: return new DashMediaSource.Factory( new DefaultDashChunkSource.Factory(mediaDataSourceFactory), - new DefaultDataSourceFactory(context, null, mediaDataSourceFactory)) + new DefaultDataSource.Factory(context, mediaDataSourceFactory)) .createMediaSource(MediaItem.fromUri(uri)); case C.TYPE_HLS: return new HlsMediaSource.Factory(mediaDataSourceFactory) @@ -207,7 +207,7 @@ public void onPlaybackStateChanged(final int playbackState) { } @Override - public void onPlayerError(final ExoPlaybackException error) { + public void onPlayerError(final PlaybackException error) { setBuffering(false); if (eventSink != null) { eventSink.error("VideoError", "Video player had error " + error, null); @@ -225,8 +225,7 @@ void sendBufferingUpdate() { eventSink.success(event); } - @SuppressWarnings("deprecation") - private static void setAudioAttributes(SimpleExoPlayer exoPlayer, boolean isMixMode) { + private static void setAudioAttributes(ExoPlayer exoPlayer, boolean isMixMode) { exoPlayer.setAudioAttributes( new AudioAttributes.Builder().setContentType(C.CONTENT_TYPE_MOVIE).build(), !isMixMode); } diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index 15c063f83d40..aa288ed71eac 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.1 +version: 2.3.2 environment: sdk: ">=2.14.0 <3.0.0" From 62be2dcb94ac52e1d5808e2c8a171480136371e4 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Thu, 24 Mar 2022 15:35:09 -0700 Subject: [PATCH 068/844] [tool] Fix typo in `publish-plugin` readme (#5107) --- script/tool/CHANGELOG.md | 4 ++++ script/tool/README.md | 17 ++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 214eb68b2f21..45f7f527e22c 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +- Updates `publish-plugin` command documentation. + ## 0.8.2 - Adds a new `custom-test` command. diff --git a/script/tool/README.md b/script/tool/README.md index 265d3868fc37..05be917014f2 100644 --- a/script/tool/README.md +++ b/script/tool/README.md @@ -109,11 +109,18 @@ dart run ./script/tool/bin/flutter_plugin_tools.dart native-test --macos --packa ### Publish a Release -``sh +**Releases are automated for `flutter/plugins` and `flutter/packages`.** + +The manual procedure described here is _deprecated_, and should only be used when +the automated process fails. Please, read +[Releasing a Plugin or Package](https://github.com/flutter/flutter/wiki/Releasing-a-Plugin-or-Package) +on the Flutter Wiki first. + +```sh cd git checkout -dart run ./script/tool/bin/flutter_plugin_tools.dart publish-plugin --package -`` +dart run ./script/tool/bin/flutter_plugin_tools.dart publish-plugin --packages +``` By default the tool tries to push tags to the `upstream` remote, but some additional settings can be configured. Run `dart run ./script/tool/bin/flutter_plugin_tools.dart @@ -127,10 +134,6 @@ _everything_, including untracked or uncommitted files in version control. directory and refuse to publish if there are any mismatched files with version control present. -Automated publishing is under development. Follow -[flutter/flutter#27258](https://github.com/flutter/flutter/issues/27258) -for updates. - ## Updating the Tool For flutter/plugins, just changing the source here is all that's needed. From 794fa825043173a38f2ae0bcd229d3d394f5b686 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Mon, 28 Mar 2022 23:57:45 -0700 Subject: [PATCH 069/844] [espresso] [video_player] Update espresso guava version (#5112) --- packages/espresso/CHANGELOG.md | 3 ++- packages/espresso/android/build.gradle | 2 +- packages/espresso/pubspec.yaml | 2 +- packages/image_picker/image_picker/example/pubspec.yaml | 4 +++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/espresso/CHANGELOG.md b/packages/espresso/CHANGELOG.md index 3f19984b9437..a141b23bd4f0 100644 --- a/packages/espresso/CHANGELOG.md +++ b/packages/espresso/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 0.2.0 * Updates compileSdkVersion to 31. +* **Breaking Change** Update guava version to latest stable: `com.google.guava:guava:31.1-android`. ## 0.1.0+4 diff --git a/packages/espresso/android/build.gradle b/packages/espresso/android/build.gradle index e5a042095c2f..097e53f1c90b 100644 --- a/packages/espresso/android/build.gradle +++ b/packages/espresso/android/build.gradle @@ -49,7 +49,7 @@ android { } dependencies { - implementation 'com.google.guava:guava:28.1-android' + implementation 'com.google.guava:guava:31.1-android' implementation 'com.squareup.okhttp3:okhttp:3.12.1' implementation 'com.google.code.gson:gson:2.8.6' androidTestImplementation 'org.hamcrest:hamcrest:2.2' diff --git a/packages/espresso/pubspec.yaml b/packages/espresso/pubspec.yaml index 1836e7afb575..0bbf3c01158e 100644 --- a/packages/espresso/pubspec.yaml +++ b/packages/espresso/pubspec.yaml @@ -3,7 +3,7 @@ description: Java classes for testing Flutter apps using Espresso. Allows driving Flutter widgets from a native Espresso test. repository: https://github.com/flutter/plugins/tree/main/packages/espresso issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+espresso%22 -version: 0.1.0+4 +version: 0.2.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker/example/pubspec.yaml b/packages/image_picker/image_picker/example/pubspec.yaml index 28b37197d8ff..92db04501f83 100755 --- a/packages/image_picker/image_picker/example/pubspec.yaml +++ b/packages/image_picker/image_picker/example/pubspec.yaml @@ -20,7 +20,9 @@ dependencies: video_player: ^2.1.4 dev_dependencies: - espresso: ^0.1.0+2 + # TODO(bparrishMines): Change version to `0.2.0` once published. + espresso: + path: ../../../espresso flutter_driver: sdk: flutter integration_test: From 972e942c00bc9847c4823c438572d52edce014eb Mon Sep 17 00:00:00 2001 From: zuvola Date: Tue, 29 Mar 2022 18:30:14 +0900 Subject: [PATCH 070/844] [camera] Fixed a crash when streaming on iOS (#4520) --- packages/camera/camera/CHANGELOG.md | 4 + .../ios/Runner.xcodeproj/project.pbxproj | 4 + .../example/ios/RunnerTests/StreamingTest.m | 85 +++++++++++++++++++ .../camera/camera/ios/Classes/CameraPlugin.m | 3 + packages/camera/camera/ios/Classes/FLTCam.h | 8 ++ packages/camera/camera/ios/Classes/FLTCam.m | 39 ++++++--- .../camera/camera/ios/Classes/FLTCam_Test.h | 20 +++++ .../camera/lib/src/camera_controller.dart | 7 ++ packages/camera/camera/pubspec.yaml | 2 +- 9 files changed, 159 insertions(+), 13 deletions(-) create mode 100644 packages/camera/camera/example/ios/RunnerTests/StreamingTest.m diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index d19b500af2d7..03b9293b6893 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+18 + +* Fixes a crash in iOS when streaming on low-performance devices. + ## 0.9.4+17 * Removes obsolete information from README, and adds OS support table. diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index 6c171505a8ca..37f56d0ed52e 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 25C3919135C3D981E6F800D0 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */; }; 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 788A065927B0E02900533D74 /* StreamingTest.m */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -70,6 +71,7 @@ 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 59848A7CA98C1FADF8840207 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 788A065927B0E02900533D74 /* StreamingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StreamingTest.m; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -142,6 +144,7 @@ F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */, E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */, E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */, + 788A065927B0E02900533D74 /* StreamingTest.m */, ); path = RunnerTests; sourceTree = ""; @@ -416,6 +419,7 @@ E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */, 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */, E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */, + 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */, E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */, E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */, diff --git a/packages/camera/camera/example/ios/RunnerTests/StreamingTest.m b/packages/camera/camera/example/ios/RunnerTests/StreamingTest.m new file mode 100644 index 000000000000..1843cce12152 --- /dev/null +++ b/packages/camera/camera/example/ios/RunnerTests/StreamingTest.m @@ -0,0 +1,85 @@ +// 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; +@import camera.Test; +@import XCTest; +@import AVFoundation; +#import +#import "CameraTestUtils.h" + +@interface StreamingTests : XCTestCase +@property(readonly, nonatomic) FLTCam *camera; +@property(readonly, nonatomic) CMSampleBufferRef sampleBuffer; +@end + +@implementation StreamingTests + +- (void)setUp { + dispatch_queue_t captureSessionQueue = dispatch_queue_create("testing", NULL); + _camera = FLTCreateCamWithCaptureSessionQueue(captureSessionQueue); + _sampleBuffer = FLTCreateTestSampleBuffer(); +} + +- (void)tearDown { + CFRelease(_sampleBuffer); +} + +- (void)testExceedMaxStreamingPendingFramesCount { + XCTestExpectation *streamingExpectation = [self + expectationWithDescription:@"Must not call handler over maxStreamingPendingFramesCount"]; + + id handlerMock = OCMClassMock([FLTImageStreamHandler class]); + OCMStub([handlerMock eventSink]).andReturn(^(id event) { + [streamingExpectation fulfill]; + }); + + id messenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); + [_camera startImageStreamWithMessenger:messenger imageStreamHandler:handlerMock]; + + XCTKVOExpectation *expectation = [[XCTKVOExpectation alloc] initWithKeyPath:@"isStreamingImages" + object:_camera + expectedValue:@YES]; + XCTWaiterResult result = [XCTWaiter waitForExpectations:@[ expectation ] timeout:1]; + XCTAssertEqual(result, XCTWaiterResultCompleted); + + streamingExpectation.expectedFulfillmentCount = 4; + for (int i = 0; i < 10; i++) { + [_camera captureOutput:nil didOutputSampleBuffer:self.sampleBuffer fromConnection:nil]; + } + + [self waitForExpectationsWithTimeout:1.0 handler:nil]; +} + +- (void)testReceivedImageStreamData { + XCTestExpectation *streamingExpectation = + [self expectationWithDescription: + @"Must be able to call the handler again when receivedImageStreamData is called"]; + + id handlerMock = OCMClassMock([FLTImageStreamHandler class]); + OCMStub([handlerMock eventSink]).andReturn(^(id event) { + [streamingExpectation fulfill]; + }); + + id messenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); + [_camera startImageStreamWithMessenger:messenger imageStreamHandler:handlerMock]; + + XCTKVOExpectation *expectation = [[XCTKVOExpectation alloc] initWithKeyPath:@"isStreamingImages" + object:_camera + expectedValue:@YES]; + XCTWaiterResult result = [XCTWaiter waitForExpectations:@[ expectation ] timeout:1]; + XCTAssertEqual(result, XCTWaiterResultCompleted); + + streamingExpectation.expectedFulfillmentCount = 5; + for (int i = 0; i < 10; i++) { + [_camera captureOutput:nil didOutputSampleBuffer:self.sampleBuffer fromConnection:nil]; + } + + [_camera receivedImageStreamData]; + [_camera captureOutput:nil didOutputSampleBuffer:self.sampleBuffer fromConnection:nil]; + + [self waitForExpectationsWithTimeout:1.0 handler:nil]; +} + +@end diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index 634aa699a01a..c0a3833dcd64 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -162,6 +162,9 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call } else if ([@"stopImageStream" isEqualToString:call.method]) { [_camera stopImageStream]; [result sendSuccess]; + } else if ([@"receivedImageStreamData" isEqualToString:call.method]) { + [_camera receivedImageStreamData]; + [result sendSuccess]; } else { NSDictionary *argsMap = call.arguments; NSUInteger cameraId = ((NSNumber *)argsMap[@"cameraId"]).unsignedIntegerValue; diff --git a/packages/camera/camera/ios/Classes/FLTCam.h b/packages/camera/camera/ios/Classes/FLTCam.h index 0cd135e0e41f..8a5dafaf8354 100644 --- a/packages/camera/camera/ios/Classes/FLTCam.h +++ b/packages/camera/camera/ios/Classes/FLTCam.h @@ -61,6 +61,14 @@ 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. + */ +- (void)receivedImageStreamData; + /** * Applies FocusMode on the AVCaptureDevice. * diff --git a/packages/camera/camera/ios/Classes/FLTCam.m b/packages/camera/camera/ios/Classes/FLTCam.m index 30c177bd4b2a..7af505b249cb 100644 --- a/packages/camera/camera/ios/Classes/FLTCam.m +++ b/packages/camera/camera/ios/Classes/FLTCam.m @@ -10,14 +10,6 @@ @import CoreMotion; #import -@interface FLTImageStreamHandler : NSObject -// The queue on which `eventSink` property should be accessed -@property(nonatomic, strong) dispatch_queue_t captureSessionQueue; -// `eventSink` property should be accessed on `captureSessionQueue`. -// The block itself should be invoked on the main queue. -@property FlutterEventSink eventSink; -@end - @implementation FLTImageStreamHandler - (instancetype)initWithCaptureSessionQueue:(dispatch_queue_t)captureSessionQueue { @@ -68,7 +60,13 @@ @interface FLTCam () *)messenger { + [self startImageStreamWithMessenger:messenger + imageStreamHandler:[[FLTImageStreamHandler alloc] + initWithCaptureSessionQueue:_captureSessionQueue]]; +} + +- (void)startImageStreamWithMessenger:(NSObject *)messenger + imageStreamHandler:(FLTImageStreamHandler *)imageStreamHandler { if (!_isStreamingImages) { FlutterEventChannel *eventChannel = [FlutterEventChannel eventChannelWithName:@"plugins.flutter.io/camera/imageStream" @@ -905,12 +916,12 @@ - (void)startImageStreamWithMessenger:(NSObject *)messen FLTThreadSafeEventChannel *threadSafeEventChannel = [[FLTThreadSafeEventChannel alloc] initWithEventChannel:eventChannel]; - _imageStreamHandler = - [[FLTImageStreamHandler alloc] initWithCaptureSessionQueue:_captureSessionQueue]; + _imageStreamHandler = imageStreamHandler; [threadSafeEventChannel setStreamHandler:_imageStreamHandler completion:^{ dispatch_async(self->_captureSessionQueue, ^{ self.isStreamingImages = YES; + self.streamingPendingFramesCount = 0; }); }]; } else { @@ -928,6 +939,10 @@ - (void)stopImageStream { } } +- (void)receivedImageStreamData { + self.streamingPendingFramesCount--; +} + - (void)getMaxZoomLevelWithResult:(FLTThreadSafeFlutterResult *)result { CGFloat maxZoomFactor = [self getMaxAvailableZoomFactor]; diff --git a/packages/camera/camera/ios/Classes/FLTCam_Test.h b/packages/camera/camera/ios/Classes/FLTCam_Test.h index a1f9f2b65981..19e284227f4f 100644 --- a/packages/camera/camera/ios/Classes/FLTCam_Test.h +++ b/packages/camera/camera/ios/Classes/FLTCam_Test.h @@ -5,6 +5,19 @@ #import "FLTCam.h" #import "FLTSavePhotoDelegate.h" +@interface FLTImageStreamHandler : NSObject + +/// The queue on which `eventSink` property should be accessed. +@property(nonatomic, strong) dispatch_queue_t captureSessionQueue; + +/// The event sink to stream camera events to Dart. +/// +/// The property should only be accessed on `captureSessionQueue`. +/// The block itself should be invoked on the main queue. +@property FlutterEventSink eventSink; + +@end + // APIs exposed for unit testing. @interface FLTCam () @@ -14,6 +27,9 @@ /// The output for photo capturing. Exposed setter for unit tests. @property(strong, nonatomic) AVCapturePhotoOutput *capturePhotoOutput API_AVAILABLE(ios(10)); +/// True when images from the camera are being streamed. +@property(assign, nonatomic) BOOL isStreamingImages; + /// A dictionary to retain all in-progress FLTSavePhotoDelegates. The key of the dictionary is the /// AVCapturePhotoSettings's uniqueID for each photo capture operation, and the value is the /// FLTSavePhotoDelegate that handles the result of each photo capture operation. Note that photo @@ -38,4 +54,8 @@ captureSessionQueue:(dispatch_queue_t)captureSessionQueue error:(NSError **)error; +/// Start streaming images. +- (void)startImageStreamWithMessenger:(NSObject *)messenger + imageStreamHandler:(FLTImageStreamHandler *)imageStreamHandler; + @end diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index 30e6221697c9..1492ca193761 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -448,6 +448,13 @@ class CameraController extends ValueNotifier { _imageStreamSubscription = cameraEventChannel.receiveBroadcastStream().listen( (dynamic imageData) { + if (defaultTargetPlatform == TargetPlatform.iOS) { + try { + _channel.invokeMethod('receivedImageStreamData'); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } onAvailable( CameraImage.fromPlatformData(imageData as Map)); }, diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 0e684a743ba3..064eb919c96a 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+17 +version: 0.9.4+18 environment: sdk: ">=2.14.0 <3.0.0" From 1ddfcc044615138f93f8aa0e45b7ad41e329a7c2 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 29 Mar 2022 06:30:12 -0400 Subject: [PATCH 071/844] Roll Flutter from 9671b7ddee8c to b4325b68a260 (121 revisions) (#5120) --- .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 3bf43fd4c61a..df0a28879011 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -9671b7ddee8c5110cf6adcf7b7697991512218e9 +b4325b68a2602335263eadec484256136b059703 From 8a5f9bf6c931120aed11305b70f1ea4e272a3755 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 29 Mar 2022 09:44:28 -0700 Subject: [PATCH 072/844] [webview_flutter_wkwebview] Update setters and getters to follow Flutter Style Guide (#5104) --- .../lib/src/ui_kit/ui_kit.dart | 8 +- .../lib/src/web_kit/web_kit.dart | 66 +++--- .../lib/src/web_kit_webview_widget.dart | 60 +++--- .../test/src/web_kit_webview_widget_test.dart | 54 ++--- .../web_kit_webview_widget_test.mocks.dart | 193 ++++++++++-------- 5 files changed, 208 insertions(+), 173 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart index def52b78f6b5..9b7973e2ace5 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart @@ -17,7 +17,9 @@ class UIScrollView { UIScrollView.fromWebView(WKWebView webView); /// Point at which the origin of the content view is offset from the origin of the scroll view. - Future> get contentOffset { + /// + /// Represents [WKWebView.contentOffset](https://developer.apple.com/documentation/uikit/uiscrollview/1619404-contentoffset?language=objc). + Future> getContentOffset() { throw UnimplementedError(); } @@ -32,7 +34,9 @@ class UIScrollView { /// Set point at which the origin of the content view is offset from the origin of the scroll view. /// /// The default value is `Point(0.0, 0.0)`. - set contentOffset(FutureOr> offset) { + /// + /// Sets [WKWebView.contentOffset](https://developer.apple.com/documentation/uikit/uiscrollview/1619404-contentoffset?language=objc). + Future setContentOffset(FutureOr> offset) { throw UnimplementedError(); } } 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 94b1533fb3b2..fb714f8a05d5 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 @@ -220,7 +220,7 @@ class WKScriptMessageHandler { /// Use this method to respond to a message sent from the webpage’s /// JavaScript code. Use the [message] parameter to get the message contents and /// to determine the originating web view. - set didReceiveScriptMessage( + Future setDidReceiveScriptMessage( void Function( WKUserContentController userContentController, WKScriptMessage message, @@ -324,13 +324,17 @@ class WKWebViewConfiguration { WKWebsiteDataStore get webSiteDataStore => _websiteDataStore; /// Used to get and set the site’s cookies and to track the cached data objects. - set webSiteDataStore(WKWebsiteDataStore websiteDataStore) { + /// + /// Sets [WKWebViewConfiguration.webSiteDataStore](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/1395661-websitedatastore?language=objc). + Future setWebSiteDataStore(WKWebsiteDataStore websiteDataStore) { _websiteDataStore = websiteDataStore; throw UnimplementedError(); } /// Indicates whether HTML5 videos play inline or use the native full-screen controller. - set allowsInlineMediaPlayback(bool allow) { + /// + /// Sets [WKWebViewConfiguration.allowsInlineMediaPlayback](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/1614793-allowsinlinemediaplayback?language=objc). + Future setAllowsInlineMediaPlayback(bool allow) { throw UnimplementedError(); } @@ -338,7 +342,9 @@ class WKWebViewConfiguration { /// /// Use [WKAudiovisualMediaType.none] to indicate that no user gestures are /// required to begin playing media. - set mediaTypesRequiringUserActionForPlayback( + /// + /// Sets [WKWebViewConfiguration.mediaTypesRequiringUserActionForPlayback](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/1851524-mediatypesrequiringuseractionfor?language=objc). + Future setMediaTypesRequiringUserActionForPlayback( Set types, ) { assert(types.isNotEmpty); @@ -351,12 +357,12 @@ class WKWebViewConfiguration { /// Wraps [WKUIDelegate](https://developer.apple.com/documentation/webkit/wkuidelegate?language=objc). class WKUIDelegate { /// Indicates a new [WebView] was requested to be created with [configuration]. - set onCreateWebView( + Future setOnCreateWebView( void Function( WKWebViewConfiguration configuration, WKNavigationAction navigationAction, )? - onCreateeWebView, + onCreateWebView, ) { throw UnimplementedError(); } @@ -370,7 +376,7 @@ class WKUIDelegate { /// Wraps [WKNavigationDelegate](https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc). class WKNavigationDelegate { /// Called when navigation from the main frame has started. - set didStartProvisionalNavigation( + Future setDidStartProvisionalNavigation( void Function( WKWebView webView, String? url, @@ -381,14 +387,14 @@ class WKNavigationDelegate { } /// Called when navigation is complete. - set didFinishNavigation( + Future setDidFinishNavigation( void Function(WKWebView webView, String? url)? didFinishNavigation, ) { throw UnimplementedError(); } /// Called when permission is needed to navigate to new content. - set decidePolicyForNavigationAction( + Future setDecidePolicyForNavigationAction( Future Function( WKWebView webView, WKNavigationAction navigationAction, @@ -398,14 +404,14 @@ class WKNavigationDelegate { } /// Called when an error occurred during navigation. - set didFailNavigation( + Future setDidFailNavigation( void Function(WKWebView webView, NSError error)? didFailNavigation, ) { throw UnimplementedError(); } /// Called when an error occurred during the early navigation process. - set didFailProvisionalNavigation( + Future setDidFailProvisionalNavigation( void Function(WKWebView webView, NSError error)? didFailProvisionalNavigation, ) { @@ -413,7 +419,7 @@ class WKNavigationDelegate { } /// Called when the web view’s content process was terminated. - set webViewWebContentProcessDidTerminate( + Future setWebViewWebContentProcessDidTerminate( void Function(WKWebView webView)? webViewWebContentProcessDidTerminate, ) { throw UnimplementedError(); @@ -455,17 +461,23 @@ class WKWebView extends NSObject { late final UIScrollView scrollView = UIScrollView.fromWebView(this); /// Used to integrate custom user interface elements into web view interactions. - set uiDelegate(WKUIDelegate? delegate) { + /// + /// Sets [WKWebView.UIDelegate](https://developer.apple.com/documentation/webkit/wkwebview/1415009-uidelegate?language=objc). + Future setUIDelegate(WKUIDelegate? delegate) { throw UnimplementedError(); } /// The object you use to manage navigation behavior for the web view. - set navigationDelegate(WKNavigationDelegate? delegate) { + /// + /// Sets [WKWebView.navigationDelegate](https://developer.apple.com/documentation/webkit/wkwebview/1414971-navigationdelegate?language=objc). + Future setNavigationDelegate(WKNavigationDelegate? delegate) { throw UnimplementedError(); } /// The URL for the current webpage. - Future get url { + /// + /// Represents [WKWebView.URL](https://developer.apple.com/documentation/webkit/wkwebview/1415005-url?language=objc). + Future getUrl() { throw UnimplementedError(); } @@ -473,8 +485,7 @@ class WKWebView extends NSObject { /// /// This value ranges from 0.0 to 1.0. /// - /// This method represents - /// [WKWebView.estimatedProgress](https://developer.apple.com/documentation/webkit/wkwebview/1415007-estimatedprogress?language=objc). + /// Represents [WKWebView.estimatedProgress](https://developer.apple.com/documentation/webkit/wkwebview/1415007-estimatedprogress?language=objc). Future getEstimatedProgress() { throw UnimplementedError(); } @@ -506,12 +517,12 @@ class WKWebView extends NSObject { } /// Indicates whether there is a valid back item in the back-forward list. - Future get canGoBack { + Future canGoBack() { throw UnimplementedError(); } /// Indicates whether there is a valid forward item in the back-forward list. - Future get canGoForward { + Future canGoForward() { throw UnimplementedError(); } @@ -531,26 +542,27 @@ class WKWebView extends NSObject { } /// The page title. - Future get title { - throw UnimplementedError(); - } - - /// An estimate of what fraction of the current navigation has been loaded. - Future get estimatedProgress { + /// + /// Represents [WKWebView.title](https://developer.apple.com/documentation/webkit/wkwebview/1415015-title?language=objc). + Future getTitle() { throw UnimplementedError(); } /// Indicates whether horizontal swipe gestures trigger page navigation. /// /// The default value is false. - set allowsBackForwardNavigationGestures(bool allow) { + /// + /// Sets [WKWebView.allowsBackForwardNavigationGestures](https://developer.apple.com/documentation/webkit/wkwebview/1414995-allowsbackforwardnavigationgestu?language=objc). + Future setAllowsBackForwardNavigationGestures(bool allow) { throw UnimplementedError(); } /// The custom user agent string. /// /// The default value of this property is null. - set customUserAgent(String? userAgent) { + /// + /// Sets [WKWebView.customUserAgent](https://developer.apple.com/documentation/webkit/wkwebview/1414950-customuseragent?language=objc). + Future setCustomUserAgent(String? userAgent) { throw UnimplementedError(); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 58c279cd56ba..67b05c1f053a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -92,15 +92,15 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { ), ); - webView.uiDelegate = uiDelegate; - uiDelegate.onCreateWebView = ( + webView.setUIDelegate(uiDelegate); + uiDelegate.setOnCreateWebView(( WKWebViewConfiguration configuration, WKNavigationAction navigationAction, ) { if (!navigationAction.targetFrame.isMainFrame) { webView.loadRequest(navigationAction.request); } - }; + }); } final Map _scriptMessageHandlers = @@ -128,19 +128,19 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { @visibleForTesting late final WKNavigationDelegate navigationDelegate = webViewProxy.createNavigationDelegate() - ..didStartProvisionalNavigation = (WKWebView webView, String? url) { + ..setDidStartProvisionalNavigation((WKWebView webView, String? url) { callbacksHandler.onPageStarted(url ?? ''); - } - ..didFinishNavigation = (WKWebView webView, String? url) { + }) + ..setDidFinishNavigation((WKWebView webView, String? url) { callbacksHandler.onPageFinished(url ?? ''); - } - ..didFailNavigation = (WKWebView webView, NSError error) { + }) + ..setDidFailNavigation((WKWebView webView, NSError error) { callbacksHandler.onWebResourceError(_toWebResourceError(error)); - } - ..didFailProvisionalNavigation = (WKWebView webView, NSError error) { + }) + ..setDidFailProvisionalNavigation((WKWebView webView, NSError error) { callbacksHandler.onWebResourceError(_toWebResourceError(error)); - } - ..webViewWebContentProcessDidTerminate = (WKWebView webView) { + }) + ..setWebViewWebContentProcessDidTerminate((WKWebView webView) { callbacksHandler.onWebResourceError(WebResourceError( errorCode: WKErrorCode.webContentProcessTerminated, // Value from https://developer.apple.com/documentation/webkit/wkerrordomain?language=objc. @@ -148,7 +148,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { description: '', errorType: WebResourceErrorType.webContentProcessTerminated, )); - }; + }); Future _setCreationParams( CreationParams params, { @@ -164,7 +164,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { await addJavascriptChannels(params.javascriptChannelNames); - webView.navigationDelegate = navigationDelegate; + webView.setNavigationDelegate(navigationDelegate); if (params.webSettings != null) { updateSettings(params.webSettings!); @@ -177,7 +177,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { required AutoMediaPlaybackPolicy autoMediaPlaybackPolicy, }) { if (allowsInlineMediaPlayback != null) { - configuration.allowsInlineMediaPlayback = allowsInlineMediaPlayback; + configuration.setAllowsInlineMediaPlayback(allowsInlineMediaPlayback); } late final bool requiresUserAction; @@ -190,11 +190,11 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { break; } - configuration.mediaTypesRequiringUserActionForPlayback = - { + configuration + .setMediaTypesRequiringUserActionForPlayback({ if (requiresUserAction) WKAudiovisualMediaType.all, if (!requiresUserAction) WKAudiovisualMediaType.none, - }; + }); } @override @@ -255,10 +255,10 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { } @override - Future canGoBack() => webView.canGoBack; + Future canGoBack() => webView.canGoBack(); @override - Future canGoForward() => webView.canGoForward; + Future canGoForward() => webView.canGoForward(); @override Future goBack() => webView.goBack(); @@ -311,14 +311,14 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { } @override - Future getTitle() => webView.title; + Future getTitle() => webView.getTitle(); @override Future scrollTo(int x, int y) async { - webView.scrollView.contentOffset = Point( + webView.scrollView.setContentOffset(Point( x.toDouble(), y.toDouble(), - ); + )); } @override @@ -331,13 +331,13 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { @override Future getScrollX() async { - final Point offset = await webView.scrollView.contentOffset; + final Point offset = await webView.scrollView.getContentOffset(); return offset.x.toInt(); } @override Future getScrollY() async { - final Point offset = await webView.scrollView.contentOffset; + final Point offset = await webView.scrollView.getContentOffset(); return offset.y.toInt(); } @@ -362,7 +362,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { (String channelName) { final WKScriptMessageHandler handler = webViewProxy.createScriptMessageHandler() - ..didReceiveScriptMessage = ( + ..setDidReceiveScriptMessage(( WKUserContentController userContentController, WKScriptMessage message, ) { @@ -370,7 +370,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { message.name, message.body!.toString(), ); - }; + }); _scriptMessageHandlers[channelName] = handler; final String wrapperSource = @@ -417,7 +417,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { void _setHasNavigationDelegate(bool hasNavigationDelegate) { if (hasNavigationDelegate) { - navigationDelegate.decidePolicyForNavigationAction = + navigationDelegate.setDecidePolicyForNavigationAction( (WKWebView webView, WKNavigationAction action) async { final bool allow = await callbacksHandler.onNavigationRequest( url: action.request.url, @@ -427,9 +427,9 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { return allow ? WKNavigationActionPolicy.allow : WKNavigationActionPolicy.cancel; - }; + }); } else { - navigationDelegate.decidePolicyForNavigationAction = null; + navigationDelegate.setDecidePolicyForNavigationAction(null); } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 9d8c810b9edc..e8b29dccb4e4 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -115,7 +115,7 @@ void main() { await buildWidget(tester); final dynamic onCreateWebView = - verify(mockUIDelegate.onCreateWebView = captureAny).captured.single + verify(mockUIDelegate.setOnCreateWebView(captureAny)).captured.single as void Function(WKWebViewConfiguration, WKNavigationAction); const NSUrlRequest request = NSUrlRequest(url: 'https://google.com'); @@ -144,11 +144,11 @@ void main() { ), ); - verify( - mockWebViewConfiguration.mediaTypesRequiringUserActionForPlayback = - { + verify(mockWebViewConfiguration + .setMediaTypesRequiringUserActionForPlayback(< + WKAudiovisualMediaType>{ WKAudiovisualMediaType.all, - }); + })); }); testWidgets('autoMediaPlaybackPolicy false', (WidgetTester tester) async { @@ -163,11 +163,11 @@ void main() { ), ); - verify( - mockWebViewConfiguration.mediaTypesRequiringUserActionForPlayback = - { + verify(mockWebViewConfiguration + .setMediaTypesRequiringUserActionForPlayback(< + WKAudiovisualMediaType>{ WKAudiovisualMediaType.none, - }); + })); }); testWidgets('javascriptChannelNames', (WidgetTester tester) async { @@ -216,7 +216,7 @@ void main() { ), ); - verify(mockWebViewConfiguration.allowsInlineMediaPlayback = true); + verify(mockWebViewConfiguration.setAllowsInlineMediaPlayback(true)); }); }); }); @@ -352,7 +352,7 @@ void main() { testWidgets('canGoBack', (WidgetTester tester) async { await buildWidget(tester); - when(mockWebView.canGoBack).thenAnswer( + when(mockWebView.canGoBack()).thenAnswer( (_) => Future.value(false), ); expect(testController.canGoBack(), completion(false)); @@ -361,7 +361,7 @@ void main() { testWidgets('canGoForward', (WidgetTester tester) async { await buildWidget(tester); - when(mockWebView.canGoForward).thenAnswer( + when(mockWebView.canGoForward()).thenAnswer( (_) => Future.value(true), ); expect(testController.canGoForward(), completion(true)); @@ -524,7 +524,7 @@ void main() { testWidgets('getTitle', (WidgetTester tester) async { await buildWidget(tester); - when(mockWebView.title) + when(mockWebView.getTitle()) .thenAnswer((_) => Future.value('Web Title')); expect(testController.getTitle(), completion('Web Title')); }); @@ -533,7 +533,7 @@ void main() { await buildWidget(tester); await testController.scrollTo(2, 4); - verify(mockScrollView.contentOffset = const Point(2.0, 4.0)); + verify(mockScrollView.setContentOffset(const Point(2.0, 4.0))); }); testWidgets('scrollBy', (WidgetTester tester) async { @@ -546,7 +546,7 @@ void main() { testWidgets('getScrollX', (WidgetTester tester) async { await buildWidget(tester); - when(mockScrollView.contentOffset).thenAnswer( + when(mockScrollView.getContentOffset()).thenAnswer( (_) => Future>.value(const Point(8.0, 16.0))); expect(testController.getScrollX(), completion(8.0)); }); @@ -556,7 +556,7 @@ void main() { await buildWidget(tester); - when(mockScrollView.contentOffset).thenAnswer( + when(mockScrollView.getContentOffset()).thenAnswer( (_) => Future>.value(const Point(8.0, 16.0))); expect(testController.getScrollY(), completion(16.0)); }); @@ -660,8 +660,8 @@ void main() { await buildWidget(tester); final dynamic didStartProvisionalNavigation = verify( - mockNavigationDelegate.didStartProvisionalNavigation = - captureAny) + mockNavigationDelegate + .setDidStartProvisionalNavigation(captureAny)) .captured .single as void Function(WKWebView, String); didStartProvisionalNavigation(mockWebView, 'https://google.com'); @@ -673,7 +673,7 @@ void main() { await buildWidget(tester); final dynamic didFinishNavigation = - verify(mockNavigationDelegate.didFinishNavigation = captureAny) + verify(mockNavigationDelegate.setDidFinishNavigation(captureAny)) .captured .single as void Function(WKWebView, String); didFinishNavigation(mockWebView, 'https://google.com'); @@ -686,7 +686,7 @@ void main() { await buildWidget(tester); final dynamic didFailNavigation = - verify(mockNavigationDelegate.didFailNavigation = captureAny) + verify(mockNavigationDelegate.setDidFailNavigation(captureAny)) .captured .single as void Function(WKWebView, NSError); @@ -714,8 +714,8 @@ void main() { await buildWidget(tester); final dynamic didFailProvisionalNavigation = verify( - mockNavigationDelegate.didFailProvisionalNavigation = - captureAny) + mockNavigationDelegate + .setDidFailProvisionalNavigation(captureAny)) .captured .single as void Function(WKWebView, NSError); @@ -747,8 +747,8 @@ void main() { await buildWidget(tester); final dynamic webViewWebContentProcessDidTerminate = verify( - mockNavigationDelegate.webViewWebContentProcessDidTerminate = - captureAny) + mockNavigationDelegate + .setWebViewWebContentProcessDidTerminate(captureAny)) .captured .single as void Function(WKWebView); webViewWebContentProcessDidTerminate(mockWebView); @@ -771,8 +771,8 @@ void main() { await buildWidget(tester, hasNavigationDelegate: true); final dynamic decidePolicyForNavigationAction = verify( - mockNavigationDelegate.decidePolicyForNavigationAction = - captureAny) + mockNavigationDelegate + .setDecidePolicyForNavigationAction(captureAny)) .captured .single as Future Function( @@ -844,7 +844,7 @@ void main() { .single as MockWKScriptMessageHandler; final dynamic didReceiveScriptMessage = - verify(messageHandler.didReceiveScriptMessage = captureAny) + verify(messageHandler.setDidReceiveScriptMessage(captureAny)) .captured .single as void Function( WKUserContentController userContentController, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index c63aef9026a3..36b8af441e2e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -60,19 +60,20 @@ class MockUIScrollView extends _i1.Mock implements _i4.UIScrollView { } @override - _i5.Future<_i2.Point> get contentOffset => (super.noSuchMethod( - Invocation.getter(#contentOffset), + _i5.Future<_i2.Point> getContentOffset() => (super.noSuchMethod( + Invocation.method(#getContentOffset, []), returnValue: Future<_i2.Point>.value(_FakePoint_0())) as _i5.Future<_i2.Point>); @override - set contentOffset(_i5.FutureOr<_i2.Point>? offset) => - super.noSuchMethod(Invocation.setter(#contentOffset, offset), - returnValueForMissingStub: null); - @override _i5.Future scrollBy(_i2.Point? offset) => (super.noSuchMethod(Invocation.method(#scrollBy, [offset]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setContentOffset(_i5.FutureOr<_i2.Point>? offset) => + (super.noSuchMethod(Invocation.method(#setContentOffset, [offset]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKNavigationDelegate]. @@ -85,49 +86,55 @@ class MockWKNavigationDelegate extends _i1.Mock } @override - set didStartProvisionalNavigation( + _i5.Future setDidStartProvisionalNavigation( void Function(_i3.WKWebView, String?)? didStartProvisionalNavigation) => - super.noSuchMethod( - Invocation.setter( - #didStartProvisionalNavigation, didStartProvisionalNavigation), - returnValueForMissingStub: null); + (super.noSuchMethod( + Invocation.method(#setDidStartProvisionalNavigation, + [didStartProvisionalNavigation]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); @override - set didFinishNavigation( + _i5.Future setDidFinishNavigation( void Function(_i3.WKWebView, String?)? didFinishNavigation) => - super.noSuchMethod( - Invocation.setter(#didFinishNavigation, didFinishNavigation), - returnValueForMissingStub: null); + (super.noSuchMethod( + Invocation.method(#setDidFinishNavigation, [didFinishNavigation]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); @override - set decidePolicyForNavigationAction( + _i5.Future setDecidePolicyForNavigationAction( _i5.Future<_i3.WKNavigationActionPolicy> Function( _i3.WKWebView, _i3.WKNavigationAction)? decidePolicyForNavigationAction) => - super.noSuchMethod( - Invocation.setter(#decidePolicyForNavigationAction, - decidePolicyForNavigationAction), - returnValueForMissingStub: null); + (super.noSuchMethod( + Invocation.method(#setDecidePolicyForNavigationAction, + [decidePolicyForNavigationAction]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); @override - set didFailNavigation( + _i5.Future setDidFailNavigation( void Function(_i3.WKWebView, _i6.NSError)? didFailNavigation) => - super.noSuchMethod( - Invocation.setter(#didFailNavigation, didFailNavigation), - returnValueForMissingStub: null); + (super.noSuchMethod( + Invocation.method(#setDidFailNavigation, [didFailNavigation]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); @override - set didFailProvisionalNavigation( + _i5.Future setDidFailProvisionalNavigation( void Function(_i3.WKWebView, _i6.NSError)? didFailProvisionalNavigation) => - super.noSuchMethod( - Invocation.setter( - #didFailProvisionalNavigation, didFailProvisionalNavigation), - returnValueForMissingStub: null); + (super.noSuchMethod( + Invocation.method( + #setDidFailProvisionalNavigation, [didFailProvisionalNavigation]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); @override - set webViewWebContentProcessDidTerminate( + _i5.Future setWebViewWebContentProcessDidTerminate( void Function(_i3.WKWebView)? webViewWebContentProcessDidTerminate) => - super.noSuchMethod( - Invocation.setter(#webViewWebContentProcessDidTerminate, - webViewWebContentProcessDidTerminate), - returnValueForMissingStub: null); + (super.noSuchMethod( + Invocation.method(#setWebViewWebContentProcessDidTerminate, + [webViewWebContentProcessDidTerminate]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKScriptMessageHandler]. @@ -140,12 +147,14 @@ class MockWKScriptMessageHandler extends _i1.Mock } @override - set didReceiveScriptMessage( + _i5.Future setDidReceiveScriptMessage( void Function(_i3.WKUserContentController, _i3.WKScriptMessage)? didReceiveScriptMessage) => - super.noSuchMethod( - Invocation.setter(#didReceiveScriptMessage, didReceiveScriptMessage), - returnValueForMissingStub: null); + (super.noSuchMethod( + Invocation.method( + #setDidReceiveScriptMessage, [didReceiveScriptMessage]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKWebView]. @@ -166,41 +175,6 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { (super.noSuchMethod(Invocation.getter(#scrollView), returnValue: _FakeUIScrollView_2()) as _i4.UIScrollView); @override - set uiDelegate(_i3.WKUIDelegate? delegate) => - super.noSuchMethod(Invocation.setter(#uiDelegate, delegate), - returnValueForMissingStub: null); - @override - set navigationDelegate(_i3.WKNavigationDelegate? delegate) => - super.noSuchMethod(Invocation.setter(#navigationDelegate, delegate), - returnValueForMissingStub: null); - @override - _i5.Future get url => (super.noSuchMethod(Invocation.getter(#url), - returnValue: Future.value()) as _i5.Future); - @override - _i5.Future get canGoBack => - (super.noSuchMethod(Invocation.getter(#canGoBack), - returnValue: Future.value(false)) as _i5.Future); - @override - _i5.Future get canGoForward => - (super.noSuchMethod(Invocation.getter(#canGoForward), - returnValue: Future.value(false)) as _i5.Future); - @override - _i5.Future get title => - (super.noSuchMethod(Invocation.getter(#title), - returnValue: Future.value()) as _i5.Future); - @override - _i5.Future get estimatedProgress => - (super.noSuchMethod(Invocation.getter(#estimatedProgress), - returnValue: Future.value(0.0)) as _i5.Future); - @override - set allowsBackForwardNavigationGestures(bool? allow) => super.noSuchMethod( - Invocation.setter(#allowsBackForwardNavigationGestures, allow), - returnValueForMissingStub: null); - @override - set customUserAgent(String? userAgent) => - super.noSuchMethod(Invocation.setter(#customUserAgent, userAgent), - returnValueForMissingStub: null); - @override set observeValue( void Function( String, _i6.NSObject, Map<_i6.NSKeyValueChangeKey, Object?>)? @@ -208,6 +182,20 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { super.noSuchMethod(Invocation.setter(#observeValue, observeValue), returnValueForMissingStub: null); @override + _i5.Future setUIDelegate(_i3.WKUIDelegate? delegate) => + (super.noSuchMethod(Invocation.method(#setUIDelegate, [delegate]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setNavigationDelegate(_i3.WKNavigationDelegate? delegate) => + (super.noSuchMethod(Invocation.method(#setNavigationDelegate, [delegate]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future getUrl() => + (super.noSuchMethod(Invocation.method(#getUrl, []), + returnValue: Future.value()) as _i5.Future); + @override _i5.Future getEstimatedProgress() => (super.noSuchMethod(Invocation.method(#getEstimatedProgress, []), returnValue: Future.value(0.0)) as _i5.Future); @@ -235,6 +223,14 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); @override + _i5.Future canGoBack() => + (super.noSuchMethod(Invocation.method(#canGoBack, []), + returnValue: Future.value(false)) as _i5.Future); + @override + _i5.Future canGoForward() => + (super.noSuchMethod(Invocation.method(#canGoForward, []), + returnValue: Future.value(false)) as _i5.Future); + @override _i5.Future goBack() => (super.noSuchMethod(Invocation.method(#goBack, []), returnValue: Future.value(), @@ -250,6 +246,21 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); @override + _i5.Future getTitle() => + (super.noSuchMethod(Invocation.method(#getTitle, []), + returnValue: Future.value()) as _i5.Future); + @override + _i5.Future setAllowsBackForwardNavigationGestures(bool? allow) => + (super.noSuchMethod( + Invocation.method(#setAllowsBackForwardNavigationGestures, [allow]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setCustomUserAgent(String? userAgent) => + (super.noSuchMethod(Invocation.method(#setCustomUserAgent, [userAgent]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override _i5.Future evaluateJavaScript(String? javaScriptString) => (super .noSuchMethod(Invocation.method(#evaluateJavaScript, [javaScriptString]), returnValue: Future.value()) as _i5.Future); @@ -294,19 +305,25 @@ class MockWKWebViewConfiguration extends _i1.Mock (super.noSuchMethod(Invocation.getter(#webSiteDataStore), returnValue: _FakeWKWebsiteDataStore_4()) as _i3.WKWebsiteDataStore); @override - set webSiteDataStore(_i3.WKWebsiteDataStore? websiteDataStore) => - super.noSuchMethod(Invocation.setter(#webSiteDataStore, websiteDataStore), - returnValueForMissingStub: null); + _i5.Future setWebSiteDataStore( + _i3.WKWebsiteDataStore? websiteDataStore) => + (super.noSuchMethod( + Invocation.method(#setWebSiteDataStore, [websiteDataStore]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); @override - set allowsInlineMediaPlayback(bool? allow) => - super.noSuchMethod(Invocation.setter(#allowsInlineMediaPlayback, allow), - returnValueForMissingStub: null); + _i5.Future setAllowsInlineMediaPlayback(bool? allow) => (super + .noSuchMethod(Invocation.method(#setAllowsInlineMediaPlayback, [allow]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); @override - set mediaTypesRequiringUserActionForPlayback( + _i5.Future setMediaTypesRequiringUserActionForPlayback( Set<_i3.WKAudiovisualMediaType>? types) => - super.noSuchMethod( - Invocation.setter(#mediaTypesRequiringUserActionForPlayback, types), - returnValueForMissingStub: null); + (super.noSuchMethod( + Invocation.method( + #setMediaTypesRequiringUserActionForPlayback, [types]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKWebsiteDataStore]. @@ -336,11 +353,13 @@ class MockWKUIDelegate extends _i1.Mock implements _i3.WKUIDelegate { } @override - set onCreateWebView( + _i5.Future setOnCreateWebView( void Function(_i3.WKWebViewConfiguration, _i3.WKNavigationAction)? - onCreateeWebView) => - super.noSuchMethod(Invocation.setter(#onCreateWebView, onCreateeWebView), - returnValueForMissingStub: null); + onCreateWebView) => + (super.noSuchMethod( + Invocation.method(#setOnCreateWebView, [onCreateWebView]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKUserContentController]. From c5f34ad891cc4d47c821bf1309da734e69a98f96 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 29 Mar 2022 13:20:10 -0400 Subject: [PATCH 073/844] Roll Flutter from b4325b68a260 to 37d619d2285e (1 revision) (#5121) --- .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 df0a28879011..df6c7e4aa309 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b4325b68a2602335263eadec484256136b059703 +37d619d2285ed719d4f4eec0df0d7d6747397082 From 2c52202ad7d8e1e0d2220e0ec50e03280ff4b97b Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 29 Mar 2022 16:35:11 -0400 Subject: [PATCH 074/844] Roll Flutter from 37d619d2285e to 43029bebc485 (4 revisions) (#5124) --- .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 df6c7e4aa309..77e6e4b9a4ea 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -37d619d2285ed719d4f4eec0df0d7d6747397082 +43029bebc485eca7b380208f1c857e557ae875bf From 611b9572b73e3259d0550691959ee50907de5164 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 29 Mar 2022 21:30:15 -0400 Subject: [PATCH 075/844] Roll Flutter from 43029bebc485 to fb60ae53e553 (5 revisions) (#5125) --- .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 77e6e4b9a4ea..1d99be09c720 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -43029bebc485eca7b380208f1c857e557ae875bf +fb60ae53e553486559e1ae0f400c322825808651 From b3e37559f96652fc91b14d3970cbf1c38c7a5c05 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 30 Mar 2022 09:45:17 -0400 Subject: [PATCH 076/844] Roll Flutter from fb60ae53e553 to f07f6fef9ae9 (1 revision) (#5126) --- .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 1d99be09c720..7ea02b41af44 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fb60ae53e553486559e1ae0f400c322825808651 +f07f6fef9ae9156c4531b62ba4a63185528f2884 From cc9d68985263ee90957b0d50acc5fb514124a486 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 31 Mar 2022 16:00:12 -0400 Subject: [PATCH 077/844] Roll Flutter from f07f6fef9ae9 to 66ed64be4fd8 (6 revisions) (#5128) --- .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 7ea02b41af44..ef23ed91e5bb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f07f6fef9ae9156c4531b62ba4a63185528f2884 +66ed64be4fd8349cf47bf9b86f4d74ae3b4198f8 From 7d1923f4528e416529f0c5d1f02a706c9adad45b Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 31 Mar 2022 13:00:51 -0700 Subject: [PATCH 078/844] [webview_flutter_android] Clear local storage when `clearCache` is called (#5086) --- .../webview_flutter_test.dart | 64 ++++++++++++++++ .../webview_flutter_android/CHANGELOG.md | 4 +- .../GeneratedAndroidWebView.java | 74 +++++++++++++++++++ .../webviewflutter/WebStorageHostApiImpl.java | 53 +++++++++++++ .../webviewflutter/WebViewFlutterPlugin.java | 4 + .../WebStorageHostApiImplTest.java | 41 ++++++++++ .../webview_flutter_test.dart | 44 +++++++++++ .../lib/src/android_webview.dart | 27 +++++++ .../lib/src/android_webview.pigeon.dart | 66 +++++++++++++++++ .../lib/src/android_webview_api_impls.dart | 27 +++++++ .../lib/webview_android_widget.dart | 17 ++++- .../pigeons/android_webview.dart | 7 ++ .../webview_flutter_android/pubspec.yaml | 2 +- .../test/android_webview.pigeon.dart | 52 +++++++++++++ .../test/android_webview_test.dart | 26 +++++++ .../test/android_webview_test.mocks.dart | 19 +++++ .../test/webview_android_widget_test.dart | 5 ++ .../webview_android_widget_test.mocks.dart | 15 ++++ 18 files changed, 543 insertions(+), 4 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImpl.java create mode 100644 packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImplTest.java diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index 02ee1976eb67..70e8179e1dd0 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -1217,6 +1217,52 @@ Future main() async { }, skip: _skipDueToIssue86757, ); + + testWidgets( + 'clearCache should clear local storage', + (WidgetTester tester) async { + final Completer controllerCompleter = + Completer(); + final Completer onPageFinished = Completer(); + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: WebView( + key: GlobalKey(), + initialUrl: primaryUrl, + javascriptMode: JavascriptMode.unrestricted, + onPageFinished: (_) => onPageFinished.complete(), + onWebViewCreated: (WebViewController controller) { + controllerCompleter.complete(controller); + }, + ), + ), + ); + + final WebViewController controller = await controllerCompleter.future; + await onPageFinished.future; + + await controller.runJavascript('localStorage.setItem("myCat", "Tom");'); + + expect( + controller.runJavascriptReturningResult( + 'localStorage.getItem("myCat");', + ), + completion(_webviewString('Tom')), + ); + + await controller.clearCache(); + + expect( + controller.runJavascriptReturningResult( + 'localStorage.getItem("myCat");', + ), + completion(_webviewNull()), + ); + }, + // TODO(bparrishMines): Unskip once https://github.com/flutter/plugins/pull/5086 lands and is published. + skip: Platform.isAndroid, + ); } // JavaScript booleans evaluate to different string values on Android and iOS. @@ -1228,6 +1274,24 @@ String _webviewBool(bool value) { return value ? 'true' : 'false'; } +// JavaScript `null` evaluate to different string values on Android and iOS. +// This utility method returns the string boolean value of the current platform. +String _webviewNull() { + if (defaultTargetPlatform == TargetPlatform.iOS) { + return ''; + } + return 'null'; +} + +// JavaScript String evaluate to different string values on Android and iOS. +// This utility method returns the string boolean value of the current platform. +String _webviewString(String value) { + if (defaultTargetPlatform == TargetPlatform.iOS) { + return value; + } + return '"$value"'; +} + /// Returns the value used for the HTTP User-Agent: request header in subsequent HTTP requests. Future _getUserAgent(WebViewController controller) async { return _runJavascriptReturningResult(controller, 'navigator.userAgent;'); diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 12d20b00f534..ad81b0f6e83d 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.8.4 * Fixes bug preventing `mockito` code generation for tests. +* Fixes regression where local storage wasn't cleared when `WebViewController.clearCache` was + called. ## 2.8.3 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 15b78b718115..afca5ee12747 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 @@ -2199,6 +2199,80 @@ public void onProgressChanged( } } + private static class WebStorageHostApiCodec extends StandardMessageCodec { + public static final WebStorageHostApiCodec INSTANCE = new WebStorageHostApiCodec(); + + private WebStorageHostApiCodec() {} + } + + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface WebStorageHostApi { + void create(Long instanceId); + + void deleteAllData(Long instanceId); + + /** The codec used by WebStorageHostApi. */ + static MessageCodec getCodec() { + return WebStorageHostApiCodec.INSTANCE; + } + + /** + * Sets up an instance of `WebStorageHostApi` to handle messages through the `binaryMessenger`. + */ + static void setup(BinaryMessenger binaryMessenger, WebStorageHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.WebStorageHostApi.create", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number instanceIdArg = (Number) args.get(0); + if (instanceIdArg == null) { + throw new NullPointerException("instanceIdArg unexpectedly null."); + } + api.create(instanceIdArg.longValue()); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.WebStorageHostApi.deleteAllData", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number instanceIdArg = (Number) args.get(0); + if (instanceIdArg == null) { + throw new NullPointerException("instanceIdArg unexpectedly null."); + } + api.deleteAllData(instanceIdArg.longValue()); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } + private static Map wrapError(Throwable exception) { Map errorMap = new HashMap<>(); errorMap.put("message", exception.toString()); diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImpl.java new file mode 100644 index 000000000000..42e7603c0279 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImpl.java @@ -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. + +package io.flutter.plugins.webviewflutter; + +import android.webkit.WebStorage; +import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebStorageHostApi; + +/** + * Host api implementation for {@link WebStorage}. + * + *

Handles creating {@link WebStorage}s that intercommunicate with a paired Dart object. + */ +public class WebStorageHostApiImpl implements WebStorageHostApi { + private final InstanceManager instanceManager; + private final WebStorageCreator webStorageCreator; + + /** Handles creating {@link WebStorage} for a {@link WebStorageHostApiImpl}. */ + public static class WebStorageCreator { + /** + * Creates a {@link WebStorage}. + * + * @return the created {@link WebStorage}. Defaults to {@link WebStorage#getInstance} + */ + public WebStorage createWebStorage() { + return WebStorage.getInstance(); + } + } + + /** + * Creates a host API that handles creating {@link WebStorage} and invoke its methods. + * + * @param instanceManager maintains instances stored to communicate with Dart objects + * @param webStorageCreator handles creating {@link WebStorage}s + */ + public WebStorageHostApiImpl( + InstanceManager instanceManager, WebStorageCreator webStorageCreator) { + this.instanceManager = instanceManager; + this.webStorageCreator = webStorageCreator; + } + + @Override + public void create(Long instanceId) { + instanceManager.addInstance(webStorageCreator.createWebStorage(), instanceId); + } + + @Override + public void deleteAllData(Long instanceId) { + final WebStorage webStorage = (WebStorage) instanceManager.getInstance(instanceId); + webStorage.deleteAllData(); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java index 4ef622fe47cb..67202ebef16d 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java @@ -19,6 +19,7 @@ import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.JavaScriptChannelHostApi; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebChromeClientHostApi; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebSettingsHostApi; +import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebStorageHostApi; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebViewClientHostApi; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebViewHostApi; @@ -116,6 +117,9 @@ private void setUp( FlutterAssetManagerHostApi.setup( binaryMessenger, new FlutterAssetManagerHostApiImpl(flutterAssetManager)); CookieManagerHostApi.setup(binaryMessenger, new CookieManagerHostApiImpl()); + WebStorageHostApi.setup( + binaryMessenger, + new WebStorageHostApiImpl(instanceManager, new WebStorageHostApiImpl.WebStorageCreator())); } @Override diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImplTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImplTest.java new file mode 100644 index 000000000000..e2845c2842a9 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImplTest.java @@ -0,0 +1,41 @@ +// 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.webviewflutter; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.webkit.WebStorage; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +public class WebStorageHostApiImplTest { + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock public WebStorage mockWebStorage; + + @Mock WebStorageHostApiImpl.WebStorageCreator mockWebStorageCreator; + + InstanceManager testInstanceManager; + WebStorageHostApiImpl testHostApiImpl; + + @Before + public void setUp() { + testInstanceManager = new InstanceManager(); + when(mockWebStorageCreator.createWebStorage()).thenReturn(mockWebStorage); + testHostApiImpl = new WebStorageHostApiImpl(testInstanceManager, mockWebStorageCreator); + testHostApiImpl.create(0L); + } + + @Test + public void deleteAllData() { + testHostApiImpl.deleteAllData(0L); + verify(mockWebStorage).deleteAllData(); + } +} 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 58f2f369bcf5..f1e95288383c 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 @@ -1377,6 +1377,50 @@ Future main() async { ); }, ); + + testWidgets( + 'clearCache should clear local storage', + (WidgetTester tester) async { + final Completer controllerCompleter = + Completer(); + final Completer onPageFinished = Completer(); + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: WebView( + key: GlobalKey(), + initialUrl: primaryUrl, + javascriptMode: JavascriptMode.unrestricted, + onPageFinished: (_) => onPageFinished.complete(), + onWebViewCreated: (WebViewController controller) { + controllerCompleter.complete(controller); + }, + ), + ), + ); + + final WebViewController controller = await controllerCompleter.future; + await onPageFinished.future; + + await controller.runJavascript('localStorage.setItem("myCat", "Tom");'); + + expect( + controller.runJavascriptReturningResult( + 'localStorage.getItem("myCat");', + ), + completion('"Tom"'), + ); + + await controller.clearCache(); + + expect( + controller.runJavascriptReturningResult( + 'localStorage.getItem("myCat");', + ), + completion('null'), + ); + }, + ); } // JavaScript booleans evaluate to different string values on Android and iOS. 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 10989321a9bb..bd50640919f9 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 @@ -852,3 +852,30 @@ class FlutterAssetManager { Future getAssetFilePathByName(String name) => api.getAssetFilePathByName(name); } + +/// Manages the JavaScript storage APIs provided by the [WebView]. +/// +/// Wraps [WebStorage](https://developer.android.com/reference/android/webkit/WebStorage). +class WebStorage { + /// Constructs a [WebStorage]. + /// + /// This constructor is only used for testing. An instance should be obtained + /// with [WebStorage.instance]. + @visibleForTesting + WebStorage() { + AndroidWebViewFlutterApis.instance.ensureSetUp(); + api.createFromInstance(this); + } + + /// Pigeon Host Api implementation for [WebStorage]. + @visibleForTesting + static WebStorageHostApiImpl api = WebStorageHostApiImpl(); + + /// The singleton instance of this class. + static WebStorage instance = WebStorage(); + + /// Clears all storage currently being used by the JavaScript storage APIs. + Future deleteAllData() { + return api.deleteAllDataFromInstance(this); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart index 20391c43d966..4a0965eaeac0 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart @@ -1860,3 +1860,69 @@ abstract class WebChromeClientFlutterApi { } } } + +class _WebStorageHostApiCodec extends StandardMessageCodec { + const _WebStorageHostApiCodec(); +} + +class WebStorageHostApi { + /// Constructor for [WebStorageHostApi]. 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. + WebStorageHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _WebStorageHostApiCodec(); + + Future create(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebStorageHostApi.create', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future deleteAllData(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebStorageHostApi.deleteAllData', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + details: null, + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} 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 ead60f6a2b35..9c980c80d58d 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 @@ -787,3 +787,30 @@ class WebChromeClientFlutterApiImpl extends WebChromeClientFlutterApi { instance!.onProgressChanged(webViewInstance!, progress); } } + +/// Host api implementation for [WebStorage]. +class WebStorageHostApiImpl extends WebStorageHostApi { + /// Constructs a [WebStorageHostApiImpl]. + WebStorageHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : super(binaryMessenger: binaryMessenger) { + this.instanceManager = instanceManager ?? InstanceManager.instance; + } + + /// Maintains instances stored to communicate with java objects. + late final InstanceManager instanceManager; + + /// Helper method to convert instances ids to objects. + Future createFromInstance(WebStorage instance) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + return create(instanceId); + } + } + + /// Helper method to convert instances ids to objects. + Future deleteAllDataFromInstance(WebStorage instance) { + return deleteAllData(instanceManager.getInstanceId(instance)!); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart index 7200aaa4a322..28d169c9cb94 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart @@ -23,6 +23,7 @@ class WebViewAndroidWidget extends StatefulWidget { @visibleForTesting this.webViewProxy = const WebViewProxy(), @visibleForTesting this.flutterAssetManager = const android_webview.FlutterAssetManager(), + @visibleForTesting this.webStorage, }); /// Initial parameters used to setup the WebView. @@ -59,6 +60,9 @@ class WebViewAndroidWidget extends StatefulWidget { final Widget Function(WebViewAndroidPlatformController controller) onBuildWidget; + /// Manages the JavaScript storage APIs. + final android_webview.WebStorage? webStorage; + @override State createState() => _WebViewAndroidWidgetState(); } @@ -76,6 +80,7 @@ class _WebViewAndroidWidgetState extends State { javascriptChannelRegistry: widget.javascriptChannelRegistry, webViewProxy: widget.webViewProxy, flutterAssetManager: widget.flutterAssetManager, + webStorage: widget.webStorage, ); } @@ -102,7 +107,9 @@ class WebViewAndroidPlatformController extends WebViewPlatformController { @visibleForTesting this.webViewProxy = const WebViewProxy(), @visibleForTesting this.flutterAssetManager = const android_webview.FlutterAssetManager(), - }) : assert(creationParams.webSettings?.hasNavigationDelegate != null), + @visibleForTesting android_webview.WebStorage? webStorage, + }) : webStorage = webStorage ?? android_webview.WebStorage.instance, + assert(creationParams.webSettings?.hasNavigationDelegate != null), super(callbacksHandler) { webView = webViewProxy.createWebView( useHybridComposition: useHybridComposition, @@ -160,6 +167,9 @@ class WebViewAndroidPlatformController extends WebViewPlatformController { late final WebViewAndroidWebChromeClient webChromeClient = WebViewAndroidWebChromeClient(); + /// Manages the JavaScript storage APIs. + final android_webview.WebStorage webStorage; + /// Receive various notifications and requests for [android_webview.WebView]. @visibleForTesting WebViewAndroidWebViewClient get webViewClient => _webViewClient; @@ -254,7 +264,10 @@ class WebViewAndroidPlatformController extends WebViewPlatformController { Future reload() => webView.reload(); @override - Future clearCache() => webView.clearCache(true); + Future clearCache() { + webView.clearCache(true); + return webStorage.deleteAllData(); + } @override Future updateSettings(WebSettings setting) async { 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 b29835266717..d3d18f64f97d 100644 --- a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart @@ -222,3 +222,10 @@ abstract class WebChromeClientFlutterApi { void onProgressChanged(int instanceId, int webViewInstanceId, int progress); } + +@HostApi(dartHostTestHandler: 'TestWebStorageHostApi') +abstract class WebStorageHostApi { + void create(int instanceId); + + void deleteAllData(int instanceId); +} diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 35f0e2f2a5a0..5c3c83abc53d 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/plugins/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: 2.8.3 +version: 2.8.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart index 746126346750..4ee4b1ddc0b7 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart @@ -1157,3 +1157,55 @@ abstract class TestAssetManagerHostApi { } } } + +class _TestWebStorageHostApiCodec extends StandardMessageCodec { + const _TestWebStorageHostApiCodec(); +} + +abstract class TestWebStorageHostApi { + static const MessageCodec codec = _TestWebStorageHostApiCodec(); + + void create(int instanceId); + void deleteAllData(int instanceId); + static void setup(TestWebStorageHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebStorageHostApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WebStorageHostApi.create 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.WebStorageHostApi.create was null, expected non-null int.'); + api.create(arg_instanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebStorageHostApi.deleteAllData', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WebStorageHostApi.deleteAllData 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.WebStorageHostApi.deleteAllData was null, expected non-null int.'); + api.deleteAllData(arg_instanceId!); + return {}; + }); + } + } + } +} 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 91385ff2d364..e2e6513dd841 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 @@ -21,6 +21,7 @@ import 'android_webview_test.mocks.dart'; TestJavaScriptChannelHostApi, TestWebChromeClientHostApi, TestWebSettingsHostApi, + TestWebStorageHostApi, TestWebViewClientHostApi, TestWebViewHostApi, TestAssetManagerHostApi, @@ -677,4 +678,29 @@ void main() { verify(CookieManager.api.clearCookies()); }); }); + + group('WebStorage', () { + late MockTestWebStorageHostApi mockPlatformHostApi; + + late WebStorage webStorage; + late int webStorageInstanceId; + + setUp(() { + mockPlatformHostApi = MockTestWebStorageHostApi(); + TestWebStorageHostApi.setup(mockPlatformHostApi); + + webStorage = WebStorage(); + webStorageInstanceId = + WebStorage.api.instanceManager.getInstanceId(webStorage)!; + }); + + test('create', () { + verify(mockPlatformHostApi.create(webStorageInstanceId)); + }); + + test('deleteAllData', () { + webStorage.deleteAllData(); + verify(mockPlatformHostApi.deleteAllData(webStorageInstanceId)); + }); + }); } 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 d6023d7de3aa..e8859c9b74c5 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 @@ -201,6 +201,25 @@ class MockTestWebSettingsHostApi extends _i1.Mock returnValueForMissingStub: null); } +/// A class which mocks [TestWebStorageHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWebStorageHostApi extends _i1.Mock + implements _i5.TestWebStorageHostApi { + MockTestWebStorageHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create(int? instanceId) => + super.noSuchMethod(Invocation.method(#create, [instanceId]), + returnValueForMissingStub: null); + @override + void deleteAllData(int? instanceId) => + super.noSuchMethod(Invocation.method(#deleteAllData, [instanceId]), + returnValueForMissingStub: null); +} + /// A class which mocks [TestWebViewClientHostApi]. /// /// See the documentation for Mockito's code generation for more information. diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart index af1093914047..46d6a29a1107 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart @@ -23,6 +23,7 @@ import 'webview_android_widget_test.mocks.dart'; @GenerateMocks([ android_webview.FlutterAssetManager, android_webview.WebSettings, + android_webview.WebStorage, android_webview.WebView, WebViewAndroidDownloadListener, WebViewAndroidJavaScriptChannel, @@ -39,6 +40,7 @@ void main() { late MockFlutterAssetManager mockFlutterAssetManager; late MockWebView mockWebView; late MockWebSettings mockWebSettings; + late MockWebStorage mockWebStorage; late MockWebViewProxy mockWebViewProxy; late MockWebViewPlatformCallbacksHandler mockCallbacksHandler; @@ -54,6 +56,7 @@ void main() { mockFlutterAssetManager = MockFlutterAssetManager(); mockWebView = MockWebView(); mockWebSettings = MockWebSettings(); + mockWebStorage = MockWebStorage(); when(mockWebView.settings).thenReturn(mockWebSettings); mockWebViewProxy = MockWebViewProxy(); @@ -86,6 +89,7 @@ void main() { javascriptChannelRegistry: mockJavascriptChannelRegistry, webViewProxy: mockWebViewProxy, flutterAssetManager: mockFlutterAssetManager, + webStorage: mockWebStorage, onBuildWidget: (WebViewAndroidPlatformController controller) { testController = controller; return Container(); @@ -590,6 +594,7 @@ void main() { await testController.clearCache(); verify(mockWebView.clearCache(true)); + verify(mockWebStorage.deleteAllData()); }); testWidgets('evaluateJavascript', (WidgetTester tester) async { diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart index f4d9abbd2d3c..3385e7998ba9 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart @@ -121,6 +121,21 @@ class MockWebSettings extends _i1.Mock implements _i2.WebSettings { returnValueForMissingStub: Future.value()) as _i4.Future); } +/// A class which mocks [WebStorage]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWebStorage extends _i1.Mock implements _i2.WebStorage { + MockWebStorage() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Future deleteAllData() => + (super.noSuchMethod(Invocation.method(#deleteAllData, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); +} + /// A class which mocks [WebView]. /// /// See the documentation for Mockito's code generation for more information. From 3647ba11cbeea77eb0f6debec181efcd7748826d Mon Sep 17 00:00:00 2001 From: Kate Lovett Date: Mon, 4 Apr 2022 12:16:13 -0500 Subject: [PATCH 079/844] [camera] Migrate deprecated Scaffold methods to ScaffoldMessenger (#5151) --- packages/camera/camera/CHANGELOG.md | 4 ++++ packages/camera/camera/example/lib/main.dart | 13 +++++++------ .../camera/camera/example/test/main_test.dart | 16 ++++++++++++++++ packages/camera/camera/pubspec.yaml | 2 +- 4 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 packages/camera/camera/example/test/main_test.dart diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 03b9293b6893..cf502221efa0 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+19 + +* Migrate deprecated Scaffold SnackBar methods to ScaffoldMessenger. + ## 0.9.4+18 * Fixes a crash in iOS when streaming on low-performance devices. diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index d47edfed69e2..f9f1378176a3 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -10,6 +10,7 @@ import 'dart:io'; import 'package:camera/camera.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; import 'package:video_player/video_player.dart'; class CameraExampleHome extends StatefulWidget { @@ -121,12 +122,9 @@ class _CameraExampleHomeState extends State } } - final GlobalKey _scaffoldKey = GlobalKey(); - @override Widget build(BuildContext context) { return Scaffold( - key: _scaffoldKey, appBar: AppBar( title: const Text('Camera example'), ), @@ -583,7 +581,10 @@ class _CameraExampleHomeState extends State }; if (cameras.isEmpty) { - return const Text('No camera found'); + _ambiguate(SchedulerBinding.instance)?.addPostFrameCallback((_) async { + showInSnackBar('No camera found.'); + }); + return const Text('None'); } else { for (final CameraDescription cameraDescription in cameras) { toggles.add( @@ -609,8 +610,8 @@ class _CameraExampleHomeState extends State String timestamp() => DateTime.now().millisecondsSinceEpoch.toString(); void showInSnackBar(String message) { - // ignore: deprecated_member_use - _scaffoldKey.currentState?.showSnackBar(SnackBar(content: Text(message))); + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text(message))); } void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) { diff --git a/packages/camera/camera/example/test/main_test.dart b/packages/camera/camera/example/test/main_test.dart new file mode 100644 index 000000000000..9a5fcdf2d5ea --- /dev/null +++ b/packages/camera/camera/example/test/main_test.dart @@ -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. + +import 'package:camera_example/main.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('Test snackbar', (WidgetTester tester) async { + WidgetsFlutterBinding.ensureInitialized(); + await tester.pumpWidget(CameraApp()); + await tester.pumpAndSettle(); + expect(find.byType(SnackBar), findsOneWidget); + }); +} diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 064eb919c96a..2533583d9a6e 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+18 +version: 0.9.4+19 environment: sdk: ">=2.14.0 <3.0.0" From 55669602815ea64ab04b46d6e40ac0e307622a95 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 4 Apr 2022 15:26:13 -0400 Subject: [PATCH 080/844] Add supported OS version tables to READMEs (#5106) --- .cirrus.yml | 1 + packages/espresso/CHANGELOG.md | 4 + packages/espresso/README.md | 5 +- .../file_selector/file_selector/CHANGELOG.md | 4 + .../file_selector/file_selector/README.md | 4 + .../CHANGELOG.md | 4 + .../README.md | 6 +- .../google_maps_flutter/CHANGELOG.md | 4 + .../google_maps_flutter/README.md | 6 +- .../google_sign_in/CHANGELOG.md | 4 + .../google_sign_in/google_sign_in/README.md | 4 + .../image_picker/image_picker/CHANGELOG.md | 4 + packages/image_picker/image_picker/README.md | 28 +- .../in_app_purchase/CHANGELOG.md | 4 + .../in_app_purchase/in_app_purchase/README.md | 16 +- packages/ios_platform_images/CHANGELOG.md | 4 + packages/ios_platform_images/README.md | 4 + packages/local_auth/local_auth/CHANGELOG.md | 4 + packages/local_auth/local_auth/README.md | 11 +- .../path_provider/path_provider/CHANGELOG.md | 4 + .../path_provider/path_provider/README.md | 7 +- .../quick_actions/quick_actions/CHANGELOG.md | 1 + .../quick_actions/quick_actions/README.md | 17 +- .../shared_preferences/CHANGELOG.md | 4 + .../shared_preferences/README.md | 24 +- .../url_launcher/url_launcher/CHANGELOG.md | 4 + packages/url_launcher/url_launcher/README.md | 9 +- .../video_player/video_player/CHANGELOG.md | 1 + packages/video_player/video_player/README.md | 8 +- .../webview_flutter/CHANGELOG.md | 4 + .../webview_flutter/webview_flutter/README.md | 6 +- script/tool/CHANGELOG.md | 1 + .../lib/src/common/repository_package.dart | 9 + script/tool/lib/src/main.dart | 2 + script/tool/lib/src/readme_check_command.dart | 152 ++++++++++ .../test/common/repository_package_test.dart | 4 + .../tool/test/readme_check_command_test.dart | 278 ++++++++++++++++++ 37 files changed, 609 insertions(+), 47 deletions(-) create mode 100644 script/tool/lib/src/readme_check_command.dart create mode 100644 script/tool/test/readme_check_command_test.dart diff --git a/.cirrus.yml b/.cirrus.yml index 8f6a6f7f6ebe..66e8336301d4 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -101,6 +101,7 @@ task: always: format_script: ./script/tool_runner.sh format --fail-on-change pubspec_script: ./script/tool_runner.sh pubspec-check + readme_script: ./script/tool_runner.sh readme-check license_script: dart $PLUGIN_TOOL license-check - name: federated_safety # This check is only meaningful for PRs, as it validates changes diff --git a/packages/espresso/CHANGELOG.md b/packages/espresso/CHANGELOG.md index a141b23bd4f0..b76cf2ab141b 100644 --- a/packages/espresso/CHANGELOG.md +++ b/packages/espresso/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 0.2.0 * Updates compileSdkVersion to 31. diff --git a/packages/espresso/README.md b/packages/espresso/README.md index 7747560682e9..68c3b55089ca 100644 --- a/packages/espresso/README.md +++ b/packages/espresso/README.md @@ -2,6 +2,10 @@ Provides bindings for Espresso tests of Flutter Android apps. +| | Android | +|-------------|---------| +| **Support** | SDK 16+ | + ## Installation Add the `espresso` package as a `dev_dependency` in your app's pubspec.yaml. If you're testing the example app of a package, add it as a dev_dependency of the main package as well. @@ -99,4 +103,3 @@ gcloud firebase test android run --type instrumentation \ --results-bucket= \ --results-dir= ``` - diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 65b3a64475e9..7eeedad37348 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 0.8.4+1 * Adds README information about macOS entitlements. diff --git a/packages/file_selector/file_selector/README.md b/packages/file_selector/file_selector/README.md index 863164ebf286..544cde8218bd 100644 --- a/packages/file_selector/file_selector/README.md +++ b/packages/file_selector/file_selector/README.md @@ -4,6 +4,10 @@ A Flutter plugin that manages files and interactions with file dialogs. +| | macOS | Web | Windows | +|-------------|--------|-----|-------------| +| **Support** | 10.11+ | Any | Windows 10+ | + ## Usage To use this plugin, add `file_selector` as a [dependency in your pubspec.yaml file](https://flutter.dev/platform-plugins/). diff --git a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md index 8b110b74acfc..8fdfc39f3bf9 100644 --- a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md +++ b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 2.0.5 * Updates compileSdkVersion to 31. diff --git a/packages/flutter_plugin_android_lifecycle/README.md b/packages/flutter_plugin_android_lifecycle/README.md index 3290140f4e5e..2475230d413b 100644 --- a/packages/flutter_plugin_android_lifecycle/README.md +++ b/packages/flutter_plugin_android_lifecycle/README.md @@ -9,6 +9,10 @@ The purpose of having this plugin instead of exposing an Android `Lifecycle` obj Android embedding plugins API is to force plugins to have a pub constraint that signifies the major version of the Android `Lifecycle` API they expect. +| | Android | +|-------------|---------| +| **Support** | SDK 16+ | + ## Installation Add `flutter_plugin_android_lifecycle` as a [dependency in your pubspec.yaml file](https://flutter.dev/using-packages/). @@ -32,7 +36,7 @@ public class MyPlugin implements FlutterPlugin, ActivityAware { Lifecycle lifecycle = FlutterLifecycleAdapter.getActivityLifecycle(binding); // Use lifecycle as desired. } - + //... } ``` diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index f05de47b3036..3ecea7c465e7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 2.1.3 * Fixes iOS crash on `EXC_BAD_ACCESS KERN_PROTECTION_FAILURE` if the map frame changes long after creation. diff --git a/packages/google_maps_flutter/google_maps_flutter/README.md b/packages/google_maps_flutter/google_maps_flutter/README.md index 038126f4fdd0..ae9a659c715f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/README.md +++ b/packages/google_maps_flutter/google_maps_flutter/README.md @@ -4,6 +4,10 @@ A Flutter plugin that provides a [Google Maps](https://developers.google.com/maps/) widget. +| | Android | iOS | +|-------------|---------|--------| +| **Support** | SDK 20+ | iOS 9+ | + ## Usage To use this plugin, add `google_maps_flutter` as a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/platform-integration/platform-channels). @@ -60,7 +64,7 @@ if (defaultTargetPlatform == TargetPlatform.android) { ### iOS -This plugin requires iOS 9.0 or higher. To set up, specify your API key in the application delegate `ios/Runner/AppDelegate.m`: +To set up, specify your API key in the application delegate `ios/Runner/AppDelegate.m`: ```objectivec #include "AppDelegate.h" diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index a46023bfd788..c0a377603b34 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 5.2.4 * Internal code cleanup for stricter analysis options. diff --git a/packages/google_sign_in/google_sign_in/README.md b/packages/google_sign_in/google_sign_in/README.md index f3787474eeef..2d6fa7cb6a12 100644 --- a/packages/google_sign_in/google_sign_in/README.md +++ b/packages/google_sign_in/google_sign_in/README.md @@ -6,6 +6,10 @@ _Note_: This plugin is still under development, and some APIs might not be available yet. [Feedback](https://github.com/flutter/flutter/issues) and [Pull Requests](https://github.com/flutter/plugins/pulls) are most welcome! +| | Android | iOS | Web | +|-------------|---------|--------|-----| +| **Support** | SDK 16+ | iOS 9+ | Any | + ## Platform integration ### Android integration diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 2ba5a2ce4ce7..aa4a28511a8a 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 0.8.4+11 * Fixes Activity leak. diff --git a/packages/image_picker/image_picker/README.md b/packages/image_picker/image_picker/README.md index 46a7795b748a..2fa20be34859 100755 --- a/packages/image_picker/image_picker/README.md +++ b/packages/image_picker/image_picker/README.md @@ -5,6 +5,10 @@ A Flutter plugin for iOS and Android for picking images from the image library, and taking new pictures with the camera. +| | Android | iOS | Web | +|-------------|---------|--------|----------------------------------| +| **Support** | SDK 21+ | iOS 9+ | [See `image_picker_for_web `][1] | + ## Installation First, add `image_picker` as a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/platform-integration/platform-channels). @@ -26,9 +30,9 @@ Add the following keys to your _Info.plist_ file, located in `/ios Starting with version **0.8.1** the Android implementation support to pick (multiple) images on Android 4.3 or higher. -No configuration required - the plugin should work out of the box. It is +No configuration required - the plugin should work out of the box. It is however highly recommended to prepare for Android killing the application when -low on memory. How to prepare for this is discussed in the [Handling +low on memory. How to prepare for this is discussed in the [Handling MainActivity destruction on Android](#handling-mainactivity-destruction-on-android) section. @@ -60,12 +64,12 @@ import 'package:image_picker/image_picker.dart'; ### Handling MainActivity destruction on Android When under high memory pressure the Android system may kill the MainActivity of -the application using the image_picker. On Android the image_picker makes use -of the default `Intent.ACTION_GET_CONTENT` or `MediaStore.ACTION_IMAGE_CAPTURE` -intents. This means that while the intent is executing the source application +the application using the image_picker. On Android the image_picker makes use +of the default `Intent.ACTION_GET_CONTENT` or `MediaStore.ACTION_IMAGE_CAPTURE` +intents. This means that while the intent is executing the source application is moved to the background and becomes eligable for cleanup when the system is -low on memory. When the intent finishes executing, Android will restart the -application. Since the data is never returned to the original call use the +low on memory. When the intent finishes executing, Android will restart the +application. Since the data is never returned to the original call use the `ImagePicker.retrieveLostData()` method to retrieve the lost data. For example: ```dart @@ -85,9 +89,9 @@ Future getLostData() async { } ``` -This check should always be run at startup in order to detect and handle this -case. Please refer to the -[example app](https://pub.dev/packages/image_picker/example) for a more +This check should always be run at startup in order to detect and handle this +case. Please refer to the +[example app](https://pub.dev/packages/image_picker/example) for a more complete example of handling this flow. ## Migrating to 0.8.2+ @@ -101,4 +105,6 @@ Starting with version **0.8.2** of the image_picker plugin, new methods have bee | `PickedFile image = await _picker.getImage(...)` | `XFile image = await _picker.pickImage(...)` | | `List images = await _picker.getMultiImage(...)` | `List images = await _picker.pickMultiImage(...)` | | `PickedFile video = await _picker.getVideo(...)` | `XFile video = await _picker.pickVideo(...)` | -| `LostData response = await _picker.getLostData()` | `LostDataResponse response = await _picker.retrieveLostData()` | \ No newline at end of file +| `LostData response = await _picker.getLostData()` | `LostDataResponse response = await _picker.retrieveLostData()` | + +[1]: https://pub.dev/packages/image_picker_for_web#limitations-on-the-web-platform diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 6253a23f48db..cfe625a0c5b4 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 3.0.2 * Adds additional explanation on why it is important to complete a purchase. diff --git a/packages/in_app_purchase/in_app_purchase/README.md b/packages/in_app_purchase/in_app_purchase/README.md index dbc8e8a17bc4..258eba7f50f4 100644 --- a/packages/in_app_purchase/in_app_purchase/README.md +++ b/packages/in_app_purchase/in_app_purchase/README.md @@ -5,6 +5,10 @@ A storefront-independent API for purchases in Flutter apps. This plugin supports in-app purchases (_IAP_) through an _underlying store_, which can be the App Store (on iOS) or Google Play (on Android). +| | Android | iOS | +|-------------|---------|------| +| **Support** | SDK 16+ | 9.0+ | +

An animated image of the iOS in-app purchase UI @@ -32,7 +36,7 @@ your app with each store. Both stores have extensive guides: * [App Store documentation](https://developer.apple.com/in-app-purchase/) * [Google Play documentation](https://developer.android.com/google/play/billing/billing_overview) -> NOTE: Further in this document the App Store and Google Play will be referred +> NOTE: Further in this document the App Store and Google Play will be referred > to as "the store" or "the underlying store", except when a feature is specific > to a particular store. @@ -195,12 +199,12 @@ if (_isConsumable(productDetails)) { ### Completing a purchase The `InAppPurchase.purchaseStream` will send purchase updates after initiating -the purchase flow using `InAppPurchase.buyConsumable` or -`InAppPurchase.buyNonConsumable`. After verifying the purchase receipt and the -delivering the content to the user it is important to call +the purchase flow using `InAppPurchase.buyConsumable` or +`InAppPurchase.buyNonConsumable`. After verifying the purchase receipt and the +delivering the content to the user it is important to call `InAppPurchase.completePurchase` to tell the underlying store that the -purchase has been completed. Calling `InAppPurchase.completePurchase` will -inform the underlying store that the app verified and processed the +purchase has been completed. Calling `InAppPurchase.completePurchase` will +inform the underlying store that the app verified and processed the purchase and the store can proceed to finalize the transaction and bill the end user's payment account. diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index 416f93661c8d..f2fb43dec1d1 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 0.2.0+4 * Internal code cleanup for stricter analysis options. diff --git a/packages/ios_platform_images/README.md b/packages/ios_platform_images/README.md index ada89fcdffec..08dfc3e40b31 100644 --- a/packages/ios_platform_images/README.md +++ b/packages/ios_platform_images/README.md @@ -8,6 +8,10 @@ Flutter images. When loading images from Image.xcassets the device specific variant is chosen ([iOS documentation](https://developer.apple.com/design/human-interface-guidelines/ios/icons-and-images/image-size-and-resolution/)). +| | iOS | +|-------------|------| +| **Support** | 9.0+ | + ## Usage ### iOS->Flutter Example diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 9387540d6795..a2f93cad6286 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 1.1.11 * Adds support `localizedFallbackTitle` in authenticateWithBiometrics on iOS. diff --git a/packages/local_auth/local_auth/README.md b/packages/local_auth/local_auth/README.md index 84470c646e6b..3a60e45a57eb 100644 --- a/packages/local_auth/local_auth/README.md +++ b/packages/local_auth/local_auth/README.md @@ -6,7 +6,11 @@ the user. This means referring to biometric authentication on iOS (Touch ID or lock code) and the fingerprint APIs on Android (introduced in Android 6.0). -## Usage in Dart +| | Android | iOS | +|-------------|-----------|------| +| **Support** | SDK 16+\* | 9.0+ | + +## Usage Import the relevant file: @@ -134,6 +138,11 @@ try { } ``` +### Android + +\* The plugin will build and run on SDK 16+, but `isDeviceSupported()` will +always return false before SDK 23 (Android 6.0). + ## iOS Integration Note that this plugin works with both Touch ID and Face ID. However, to use the latter, diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index d71ebf70c796..4714ad9ac15d 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 2.0.9 * Updates documentation on README.md. diff --git a/packages/path_provider/path_provider/README.md b/packages/path_provider/path_provider/README.md index 20d888ff8d73..e79234ffc07c 100644 --- a/packages/path_provider/path_provider/README.md +++ b/packages/path_provider/path_provider/README.md @@ -2,10 +2,14 @@ [![pub package](https://img.shields.io/pub/v/path_provider.svg)](https://pub.dev/packages/path_provider) -A Flutter plugin for finding commonly used locations on the filesystem. +A Flutter plugin for finding commonly used locations on the filesystem. Supports Android, iOS, Linux, macOS and Windows. Not all methods are supported on all platforms. +| | Android | iOS | Linux | macOS | Windows | +|-------------|---------|------|-------|--------|-------------| +| **Support** | SDK 16+ | 9.0+ | Any | 10.11+ | Windows 10+ | + ## Usage To use this plugin, add `path_provider` as a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/platform-integration/platform-channels). @@ -40,4 +44,3 @@ Directories support by platform: With that change, tests should be updated to mock `PathProviderPlatform` rather than `PlatformChannel`. See this `path_provider` [test](https://github.com/flutter/plugins/blob/master/packages/path_provider/path_provider/test/path_provider_test.dart) for an example. - diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index e95d56c53e9d..2a764a57e59b 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,6 +1,7 @@ ## NEXT * Updates minimum Flutter version to 2.8. +* Adds OS version support information to README. ## 0.6.0+10 diff --git a/packages/quick_actions/quick_actions/README.md b/packages/quick_actions/quick_actions/README.md index 46e87fa0b241..10bae164d534 100644 --- a/packages/quick_actions/quick_actions/README.md +++ b/packages/quick_actions/quick_actions/README.md @@ -7,10 +7,13 @@ Quick actions refer to the [eponymous concept](https://developer.apple.com/design/human-interface-guidelines/ios/system-capabilities/home-screen-actions/) on iOS and to the [App Shortcuts](https://developer.android.com/guide/topics/ui/shortcuts.html) APIs on -Android (introduced in Android 7.1 / API level 25). It is safe to run this plugin -with earlier versions of Android as it will produce a noop. +Android. -## Usage in Dart +| | Android | iOS | +|-------------|-----------|------| +| **Support** | SDK 16+\* | 9.0+ | + +## Usage Initialize the library early in your application's lifecycle by providing a callback, which will then be called whenever the user launches the app via a @@ -40,9 +43,7 @@ Please note, that the `type` argument should be unique within your application name of the native resource (xcassets on iOS or drawable on Android) that the app will display for the quick action. -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). +### Android -For help on editing plugin code, view the [documentation](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin). +\* The plugin will compile and run on SDK 16+, but will be a no-op below SDK 25 +(Android 7.1). diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index 36e60cc35763..84566e26e2c0 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 2.0.13 * Updates documentation on README.md. diff --git a/packages/shared_preferences/shared_preferences/README.md b/packages/shared_preferences/shared_preferences/README.md index d3295ac6f6b9..03975ff021e6 100644 --- a/packages/shared_preferences/shared_preferences/README.md +++ b/packages/shared_preferences/shared_preferences/README.md @@ -3,13 +3,17 @@ [![pub package](https://img.shields.io/pub/v/shared_preferences.svg)](https://pub.dev/packages/shared_preferences) Wraps platform-specific persistent storage for simple data -(NSUserDefaults on iOS and macOS, SharedPreferences on Android, etc.). +(NSUserDefaults on iOS and macOS, SharedPreferences on Android, etc.). Data may be persisted to disk asynchronously, and there is no guarantee that writes will be persisted to disk after returning, so this plugin must not be used for storing critical data. Supported data types are `int`, `double`, `bool`, `String` and `List`. +| | Android | iOS | Linux | macOS | Web | Windows | +|-------------|---------|------|-------|--------|-----|-------------| +| **Support** | SDK 16+ | 9.0+ | Any | 10.11+ | Any | Any | + ## Usage To use this plugin, add `shared_preferences` as a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/platform-integration/platform-channels). @@ -17,24 +21,24 @@ To use this plugin, add `shared_preferences` as a [dependency in your pubspec.ya Here are small examples that show you how to use the API. #### Write data -```dart +```dart // Obtain shared preferences. final prefs = await SharedPreferences.getInstance(); -// Save an integer value to 'counter' key. +// Save an integer value to 'counter' key. await prefs.setInt('counter', 10); -// Save an boolean value to 'repeat' key. +// Save an boolean value to 'repeat' key. await prefs.setBool('repeat', true); -// Save an double value to 'decimal' key. +// Save an double value to 'decimal' key. await prefs.setDouble('decimal', 1.5); -// Save an String value to 'action' key. +// Save an String value to 'action' key. await prefs.setString('action', 'Start'); -// Save an list of strings to 'items' key. +// Save an list of strings to 'items' key. await prefs.setStringList('items', ['Earth', 'Moon', 'Sun']); ``` #### Read data -```dart +```dart // Try reading data from the 'counter' key. If it doesn't exist, returns null. final int? counter = prefs.getInt('counter'); // Try reading data from the 'repeat' key. If it doesn't exist, returns null. @@ -48,8 +52,8 @@ final List? items = prefs.getStringList('items'); ``` #### Remove an entry -```dart -// Remove data for the 'counter' key. +```dart +// Remove data for the 'counter' key. final success = await prefs.remove('counter'); ``` diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 1634dcdab0f9..0c38f4847d9b 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 6.0.20 * Fixes a typo in `default_package` registration for Windows, macOS, and Linux. diff --git a/packages/url_launcher/url_launcher/README.md b/packages/url_launcher/url_launcher/README.md index a4ffb6241f6c..7f8699eceba7 100644 --- a/packages/url_launcher/url_launcher/README.md +++ b/packages/url_launcher/url_launcher/README.md @@ -2,8 +2,11 @@ [![pub package](https://img.shields.io/pub/v/url_launcher.svg)](https://pub.dev/packages/url_launcher) -A Flutter plugin for launching a URL. Supports -iOS, Android, web, Windows, macOS, and Linux. +A Flutter plugin for launching a URL. + +| | Android | iOS | Linux | macOS | Web | Windows | +|-------------|---------|------|-------|--------|-----|-------------| +| **Support** | SDK 16+ | 9.0+ | Any | 10.11+ | Any | Windows 10+ | ## Usage @@ -186,5 +189,5 @@ if (await File(uri.toFilePath()).exists()) { ### macOS file access configuration -If you need to access files outside of your application's sandbox, you will need to have the necessary +If you need to access files outside of your application's sandbox, you will need to have the necessary [entitlements](https://docs.flutter.dev/desktop#entitlements-and-the-app-sandbox). diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 5d56b1585a80..d9d1983fe0ad 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,6 +1,7 @@ ## NEXT * Updates minimum Flutter version to 2.10. +* Adds OS version support information to README. ## 2.3.0 diff --git a/packages/video_player/video_player/README.md b/packages/video_player/video_player/README.md index 84b3ae7dfb54..91c1bdeb7773 100644 --- a/packages/video_player/video_player/README.md +++ b/packages/video_player/video_player/README.md @@ -4,6 +4,10 @@ A Flutter plugin for iOS, Android and Web for playing back video on a Widget surface. +| | Android | iOS | Web | +|-------------|---------|------|-------| +| **Support** | SDK 16+ | 9.0+ | Any\* | + ![The example app running in iOS](https://github.com/flutter/plugins/blob/master/packages/video_player/video_player/doc/demo_ipod.gif?raw=true) ## Installation @@ -31,7 +35,7 @@ Android Manifest file, located in `/android/app/src/main/AndroidMa > 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`. -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. +\* 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. The `VideoPlayerOptions.mixWithOthers` option can't be implemented in web, at least at the moment. If you use this option in web it will be silently ignored. @@ -119,7 +123,7 @@ This is not complete as of now. You can contribute to this section by [opening a You can set the playback speed on your `_controller` (instance of `VideoPlayerController`) by calling `_controller.setPlaybackSpeed`. `setPlaybackSpeed` takes a `double` speed value indicating -the rate of playback for your video. +the rate of playback for your video. For example, when given a value of `2.0`, your video will play at 2x the regular playback speed and so on. diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 34b56d61bea8..dde4472eed57 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Adds OS version support information to README. + ## 3.0.1 * Removes a duplicate Android-specific integration test. diff --git a/packages/webview_flutter/webview_flutter/README.md b/packages/webview_flutter/webview_flutter/README.md index 8387eb685b7d..ffe91441326d 100644 --- a/packages/webview_flutter/webview_flutter/README.md +++ b/packages/webview_flutter/webview_flutter/README.md @@ -7,6 +7,10 @@ A Flutter plugin that provides a WebView widget. On iOS the WebView widget is backed by a [WKWebView](https://developer.apple.com/documentation/webkit/wkwebview); On Android the WebView widget is backed by a [WebView](https://developer.android.com/reference/android/webkit/WebView). +| | Android | iOS | +|-------------|----------------|------| +| **Support** | SDK 19+ or 20+ | 9.0+ | + ## Usage Add `webview_flutter` as a [dependency in your pubspec.yaml file](https://flutter.dev/docs/development/platform-integration/platform-channels). If you are targeting Android, make sure to read the *Android Platform Views* section below to choose the platform view mode that best suits your needs. @@ -91,4 +95,4 @@ follow the steps described in the [Enabling Material Components instructions](ht ### Setting custom headers on POST requests Currently, setting custom headers when making a post request with the WebViewController's `loadRequest` method is not supported on Android. -If you require this functionality, a workaround is to make the request manually, and then load the response data using `loadHTMLString` instead. \ No newline at end of file +If you require this functionality, a workaround is to make the request manually, and then load the response data using `loadHTMLString` instead. diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 45f7f527e22c..df76e4819e63 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +- Adds a new `readme-check` command. - Updates `publish-plugin` command documentation. ## 0.8.2 diff --git a/script/tool/lib/src/common/repository_package.dart b/script/tool/lib/src/common/repository_package.dart index e0c4e4a83bfe..72e7f948fe9e 100644 --- a/script/tool/lib/src/common/repository_package.dart +++ b/script/tool/lib/src/common/repository_package.dart @@ -48,6 +48,9 @@ class RepositoryPackage { /// The package's top-level pubspec.yaml. File get pubspecFile => directory.childFile('pubspec.yaml'); + /// The package's top-level README. + File get readmeFile => directory.childFile('README.md'); + late final Pubspec _parsedPubspec = Pubspec.parse(pubspecFile.readAsStringSync()); @@ -62,6 +65,12 @@ class RepositoryPackage { directory.parent.basename != 'packages' && directory.basename.startsWith(directory.parent.basename); + /// True if this appears to be the app-facing package of a federated plugin, + /// according to repository conventions. + bool get isAppFacing => + directory.parent.basename != 'packages' && + directory.basename == directory.parent.basename; + /// True if this appears to be a platform interface package, according to /// repository conventions. bool get isPlatformInterface => diff --git a/script/tool/lib/src/main.dart b/script/tool/lib/src/main.dart index 5a71a0a8889d..aa1cf300bd2b 100644 --- a/script/tool/lib/src/main.dart +++ b/script/tool/lib/src/main.dart @@ -26,6 +26,7 @@ import 'native_test_command.dart'; import 'publish_check_command.dart'; import 'publish_plugin_command.dart'; import 'pubspec_check_command.dart'; +import 'readme_check_command.dart'; import 'test_command.dart'; import 'version_check_command.dart'; import 'xcode_analyze_command.dart'; @@ -65,6 +66,7 @@ void main(List args) { ..addCommand(PublishCheckCommand(packagesDir)) ..addCommand(PublishPluginCommand(packagesDir)) ..addCommand(PubspecCheckCommand(packagesDir)) + ..addCommand(ReadmeCheckCommand(packagesDir)) ..addCommand(TestCommand(packagesDir)) ..addCommand(VersionCheckCommand(packagesDir)) ..addCommand(XcodeAnalyzeCommand(packagesDir)); diff --git a/script/tool/lib/src/readme_check_command.dart b/script/tool/lib/src/readme_check_command.dart new file mode 100644 index 000000000000..99e271c73388 --- /dev/null +++ b/script/tool/lib/src/readme_check_command.dart @@ -0,0 +1,152 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file/file.dart'; +import 'package:git/git.dart'; +import 'package:platform/platform.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; +import 'package:yaml/yaml.dart'; + +import 'common/core.dart'; +import 'common/package_looping_command.dart'; +import 'common/process_runner.dart'; +import 'common/repository_package.dart'; + +/// A command to enforce README conventions across the repository. +class ReadmeCheckCommand extends PackageLoopingCommand { + /// Creates an instance of the README check command. + ReadmeCheckCommand( + Directory packagesDir, { + ProcessRunner processRunner = const ProcessRunner(), + Platform platform = const LocalPlatform(), + GitDir? gitDir, + }) : super( + packagesDir, + processRunner: processRunner, + platform: platform, + gitDir: gitDir, + ); + + // Standardized capitalizations for platforms that a plugin can support. + static const Map _standardPlatformNames = { + 'android': 'Android', + 'ios': 'iOS', + 'linux': 'Linux', + 'macos': 'macOS', + 'web': 'Web', + 'windows': 'Windows', + }; + + @override + final String name = 'readme-check'; + + @override + final String description = + 'Checks that READMEs follow repository conventions.'; + + @override + bool get hasLongOutput => false; + + @override + Future runForPackage(RepositoryPackage package) async { + final File readme = package.readmeFile; + + if (!readme.existsSync()) { + return PackageResult.fail(['Missing README.md']); + } + + final List errors = []; + + final Pubspec pubspec = package.parsePubspec(); + final bool isPlugin = pubspec.flutter?['plugin'] != null; + + if (isPlugin && (!package.isFederated || package.isAppFacing)) { + final String? error = _validateSupportedPlatforms(package, pubspec); + if (error != null) { + errors.add(error); + } + } + + return errors.isEmpty + ? PackageResult.success() + : PackageResult.fail(errors); + } + + /// Validates that the plugin has a supported platforms table following the + /// expected format, returning an error string if any issues are found. + String? _validateSupportedPlatforms( + RepositoryPackage package, Pubspec pubspec) { + final List contents = package.readmeFile.readAsLinesSync(); + + // Example table following expected format: + // | | Android | iOS | Web | + // |----------------|---------|----------|------------------------| + // | **Support** | SDK 21+ | iOS 10+* | [See `camera_web `][1] | + final int detailsLineNumber = + contents.indexWhere((String line) => line.startsWith('| **Support**')); + if (detailsLineNumber == -1) { + return 'No OS support table found'; + } + final int osLineNumber = detailsLineNumber - 2; + if (osLineNumber < 0 || !contents[osLineNumber].startsWith('|')) { + return 'OS support table does not have the expected header format'; + } + + // Utility method to convert an iterable of strings to a case-insensitive + // sorted, comma-separated string of its elements. + String sortedListString(Iterable entries) { + final List entryList = entries.toList(); + entryList.sort( + (String a, String b) => a.toLowerCase().compareTo(b.toLowerCase())); + return entryList.join(', '); + } + + // Validate that the supported OS lists match. + final dynamic platformsEntry = pubspec.flutter!['plugin']!['platforms']; + if (platformsEntry == null) { + logWarning('Plugin not support any platforms'); + return null; + } + final YamlMap platformSupportMaps = platformsEntry as YamlMap; + final Set actuallySupportedPlatform = + platformSupportMaps.keys.toSet().cast(); + final Iterable documentedPlatforms = contents[osLineNumber] + .split('|') + .map((String entry) => entry.trim()) + .where((String entry) => entry.isNotEmpty); + final Set documentedPlatformsLowercase = + documentedPlatforms.map((String entry) => entry.toLowerCase()).toSet(); + if (actuallySupportedPlatform.length != documentedPlatforms.length || + actuallySupportedPlatform + .intersection(documentedPlatformsLowercase) + .length != + actuallySupportedPlatform.length) { + printError(''' +${indentation}OS support table does not match supported platforms: +${indentation * 2}Actual: ${sortedListString(actuallySupportedPlatform)} +${indentation * 2}Documented: ${sortedListString(documentedPlatformsLowercase)} +'''); + return 'Incorrect OS support table'; + } + + // Enforce a standard set of capitalizations for the OS headings. + final Iterable incorrectCapitalizations = documentedPlatforms + .toSet() + .difference(_standardPlatformNames.values.toSet()); + if (incorrectCapitalizations.isNotEmpty) { + final Iterable expectedVersions = incorrectCapitalizations + .map((String name) => _standardPlatformNames[name.toLowerCase()]!); + printError(''' +${indentation}Incorrect OS capitalization: ${sortedListString(incorrectCapitalizations)} +${indentation * 2}Please use standard capitalizations: ${sortedListString(expectedVersions)} +'''); + return 'Incorrect OS support formatting'; + } + + // TODO(stuartmorgan): Add validation that the minimums in the table are + // consistent with what the current implementations require. See + // https://github.com/flutter/flutter/issues/84200 + return null; + } +} diff --git a/script/tool/test/common/repository_package_test.dart b/script/tool/test/common/repository_package_test.dart index 29e3b5832127..0a8ea36eb905 100644 --- a/script/tool/test/common/repository_package_test.dart +++ b/script/tool/test/common/repository_package_test.dart @@ -126,6 +126,7 @@ void main() { test('all return false for a simple plugin', () { final Directory plugin = createFakePlugin('a_plugin', packagesDir); expect(RepositoryPackage(plugin).isFederated, false); + expect(RepositoryPackage(plugin).isAppFacing, false); expect(RepositoryPackage(plugin).isPlatformInterface, false); expect(RepositoryPackage(plugin).isFederated, false); }); @@ -134,6 +135,7 @@ void main() { final Directory plugin = createFakePlugin('a_plugin', packagesDir.childDirectory('a_plugin')); expect(RepositoryPackage(plugin).isFederated, true); + expect(RepositoryPackage(plugin).isAppFacing, true); expect(RepositoryPackage(plugin).isPlatformInterface, false); expect(RepositoryPackage(plugin).isPlatformImplementation, false); }); @@ -142,6 +144,7 @@ void main() { final Directory plugin = createFakePlugin('a_plugin_platform_interface', packagesDir.childDirectory('a_plugin')); expect(RepositoryPackage(plugin).isFederated, true); + expect(RepositoryPackage(plugin).isAppFacing, false); expect(RepositoryPackage(plugin).isPlatformInterface, true); expect(RepositoryPackage(plugin).isPlatformImplementation, false); }); @@ -152,6 +155,7 @@ void main() { final Directory plugin = createFakePlugin( 'a_plugin_foo', packagesDir.childDirectory('a_plugin')); expect(RepositoryPackage(plugin).isFederated, true); + expect(RepositoryPackage(plugin).isAppFacing, false); expect(RepositoryPackage(plugin).isPlatformInterface, false); expect(RepositoryPackage(plugin).isPlatformImplementation, true); }); diff --git a/script/tool/test/readme_check_command_test.dart b/script/tool/test/readme_check_command_test.dart new file mode 100644 index 000000000000..aec2fa078454 --- /dev/null +++ b/script/tool/test/readme_check_command_test.dart @@ -0,0 +1,278 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_plugin_tools/src/common/core.dart'; +import 'package:flutter_plugin_tools/src/common/plugin_utils.dart'; +import 'package:flutter_plugin_tools/src/readme_check_command.dart'; +import 'package:test/test.dart'; + +import 'mocks.dart'; +import 'util.dart'; + +void main() { + late CommandRunner runner; + late RecordingProcessRunner processRunner; + late FileSystem fileSystem; + late MockPlatform mockPlatform; + late Directory packagesDir; + + setUp(() { + fileSystem = MemoryFileSystem(); + mockPlatform = MockPlatform(); + packagesDir = fileSystem.currentDirectory.childDirectory('packages'); + createPackagesDirectory(parentDir: packagesDir.parent); + processRunner = RecordingProcessRunner(); + final ReadmeCheckCommand command = ReadmeCheckCommand( + packagesDir, + processRunner: processRunner, + platform: mockPlatform, + ); + + runner = CommandRunner( + 'readme_check_command', 'Test for readme_check_command'); + runner.addCommand(command); + }); + + test('fails when README is missing', () async { + createFakePackage('a_package', packagesDir); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Missing README.md'), + ]), + ); + }); + + group('plugin OS support', () { + test( + 'does not check support table for anything other than app-facing plugin packages', + () async { + const String federatedPluginName = 'a_federated_plugin'; + final Directory federatedDir = + packagesDir.childDirectory(federatedPluginName); + final List packageDirectories = [ + // A non-plugin package. + createFakePackage('a_package', packagesDir), + // Non-app-facing parts of a federated plugin. + createFakePlugin( + '${federatedPluginName}_platform_interface', federatedDir), + createFakePlugin('${federatedPluginName}_android', federatedDir), + ]; + + for (final Directory package in packageDirectories) { + package.childFile('README.md').writeAsStringSync(''' +A very useful package. +'''); + } + + final List output = await runCapturingPrint(runner, [ + 'readme-check', + ]); + + expect( + output, + containsAll([ + contains('Running for a_package...'), + contains('Running for a_federated_plugin_platform_interface...'), + contains('Running for a_federated_plugin_android...'), + contains('No issues found!'), + ]), + ); + }); + + test('fails when non-federated plugin is missing an OS support table', + () async { + final Directory pluginDir = createFakePlugin('a_plugin', packagesDir); + + pluginDir.childFile('README.md').writeAsStringSync(''' +A very useful plugin. +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('No OS support table found'), + ]), + ); + }); + + test( + 'fails when app-facing part of a federated plugin is missing an OS support table', + () async { + final Directory pluginDir = + createFakePlugin('a_plugin', packagesDir.childDirectory('a_plugin')); + + pluginDir.childFile('README.md').writeAsStringSync(''' +A very useful plugin. +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('No OS support table found'), + ]), + ); + }); + + test('fails the OS support table is missing the header', () async { + final Directory pluginDir = createFakePlugin('a_plugin', packagesDir); + + pluginDir.childFile('README.md').writeAsStringSync(''' +A very useful plugin. + +| **Support** | SDK 21+ | iOS 10+* | [See `camera_web `][1] | +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('OS support table does not have the expected header format'), + ]), + ); + }); + + test('fails if the OS support table is missing a supported OS', () async { + final Directory pluginDir = createFakePlugin( + 'a_plugin', + packagesDir, + platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), + platformWeb: const PlatformDetails(PlatformSupport.inline), + }, + ); + + pluginDir.childFile('README.md').writeAsStringSync(''' +A very useful plugin. + +| | Android | iOS | +|----------------|---------|----------| +| **Support** | SDK 21+ | iOS 10+* | +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains(' OS support table does not match supported platforms:\n' + ' Actual: android, ios, web\n' + ' Documented: android, ios'), + contains('Incorrect OS support table'), + ]), + ); + }); + + test('fails if the OS support table lists an extra OS', () async { + final Directory pluginDir = createFakePlugin( + 'a_plugin', + packagesDir, + platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), + }, + ); + + pluginDir.childFile('README.md').writeAsStringSync(''' +A very useful plugin. + +| | Android | iOS | Web | +|----------------|---------|----------|------------------------| +| **Support** | SDK 21+ | iOS 10+* | [See `camera_web `][1] | +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains(' OS support table does not match supported platforms:\n' + ' Actual: android, ios\n' + ' Documented: android, ios, web'), + contains('Incorrect OS support table'), + ]), + ); + }); + + test('fails if the OS support table has unexpected OS formatting', + () async { + final Directory pluginDir = createFakePlugin( + 'a_plugin', + packagesDir, + platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), + platformMacOS: const PlatformDetails(PlatformSupport.inline), + platformWeb: const PlatformDetails(PlatformSupport.inline), + }, + ); + + pluginDir.childFile('README.md').writeAsStringSync(''' +A very useful plugin. + +| | android | ios | MacOS | web | +|----------------|---------|----------|-------|------------------------| +| **Support** | SDK 21+ | iOS 10+* | 10.11 | [See `camera_web `][1] | +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains(' Incorrect OS capitalization: android, ios, MacOS, web\n' + ' Please use standard capitalizations: Android, iOS, macOS, Web\n'), + contains('Incorrect OS support formatting'), + ]), + ); + }); + }); +} From df6da95528c7df4efdfe5f607074a27019f20191 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Mon, 4 Apr 2022 22:26:10 +0200 Subject: [PATCH 081/844] [in_app_purchase] Make sure unsupported userInfo doesn't crash App (#5111) --- .../in_app_purchase_storekit/CHANGELOG.md | 4 ++ .../example/ios/RunnerTests/TranslatorTests.m | 50 +++++++++++++++++++ .../ios/Classes/FIAObjectTranslator.m | 37 +++++++++++--- .../in_app_purchase_storekit/pubspec.yaml | 2 +- 4 files changed, 85 insertions(+), 8 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 087c6e3095db..d614baaf2305 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.0+4 + +* Ensures that `NSError` instances with an unexpected value for the `userInfo` field don't crash the app, but send an explanatory message instead. + ## 0.3.0+3 * Implements transaction caching for StoreKit ensuring transactions are delivered to the Flutter client. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m index 0f689f602de8..c4e1ac1d059d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m @@ -158,6 +158,56 @@ - (void)testError { XCTAssertEqualObjects(map, self.errorMap); } +- (void)testErrorWithNSNumberAsUserInfo { + NSError *error = [NSError errorWithDomain:SKErrorDomain code:3 userInfo:@{@"key" : @42}]; + NSDictionary *expectedMap = + @{@"domain" : SKErrorDomain, @"code" : @3, @"userInfo" : @{@"key" : @42}}; + NSDictionary *map = [FIAObjectTranslator getMapFromNSError:error]; + XCTAssertEqualObjects(expectedMap, map); +} + +- (void)testErrorWithMultipleUnderlyingErrors { + NSError *underlyingErrorOne = [NSError errorWithDomain:SKErrorDomain code:2 userInfo:nil]; + NSError *underlyingErrorTwo = [NSError errorWithDomain:SKErrorDomain code:1 userInfo:nil]; + NSError *mainError = [NSError + errorWithDomain:SKErrorDomain + code:3 + userInfo:@{@"underlyingErrors" : @[ underlyingErrorOne, underlyingErrorTwo ]}]; + NSDictionary *expectedMap = @{ + @"domain" : SKErrorDomain, + @"code" : @3, + @"userInfo" : @{ + @"underlyingErrors" : @[ + @{@"domain" : SKErrorDomain, @"code" : @2, @"userInfo" : @{}}, + @{@"domain" : SKErrorDomain, @"code" : @1, @"userInfo" : @{}} + ] + } + }; + NSDictionary *map = [FIAObjectTranslator getMapFromNSError:mainError]; + XCTAssertEqualObjects(expectedMap, map); +} + +- (void)testErrorWithUnsupportedUserInfo { + NSError *error = [NSError errorWithDomain:SKErrorDomain + code:3 + userInfo:@{@"user_info" : [[NSObject alloc] init]}]; + NSDictionary *expectedMap = @{ + @"domain" : SKErrorDomain, + @"code" : @3, + @"userInfo" : @{ + @"user_info" : [NSString + stringWithFormat: + @"Unable to encode native userInfo object of type %@ to map. Please submit an " + @"issue at https://github.com/flutter/flutter/issues/new with the title " + @"\"[in_app_purchase_storekit] Unable to encode userInfo of type %@\" and add " + @"reproduction steps and the error details in the description field.", + [NSObject class], [NSObject class]] + } + }; + NSDictionary *map = [FIAObjectTranslator getMapFromNSError:error]; + XCTAssertEqualObjects(expectedMap, map); +} + - (void)testLocaleToMap { if (@available(iOS 10.0, *)) { NSLocale *system = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAObjectTranslator.m b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAObjectTranslator.m index 3ceb512abb10..5d87a68de67c 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAObjectTranslator.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAObjectTranslator.m @@ -167,20 +167,43 @@ + (NSDictionary *)getMapFromNSError:(NSError *)error { if (!error) { return nil; } + NSMutableDictionary *userInfo = [NSMutableDictionary new]; for (NSErrorUserInfoKey key in error.userInfo) { id value = error.userInfo[key]; - if ([value isKindOfClass:[NSError class]]) { - userInfo[key] = [FIAObjectTranslator getMapFromNSError:value]; - } else if ([value isKindOfClass:[NSURL class]]) { - userInfo[key] = [value absoluteString]; - } else { - userInfo[key] = value; - } + userInfo[key] = [FIAObjectTranslator encodeNSErrorUserInfo:value]; } return @{@"code" : @(error.code), @"domain" : error.domain ?: @"", @"userInfo" : userInfo}; } ++ (id)encodeNSErrorUserInfo:(id)value { + if ([value isKindOfClass:[NSError class]]) { + return [FIAObjectTranslator getMapFromNSError:value]; + } else if ([value isKindOfClass:[NSURL class]]) { + return [value absoluteString]; + } else if ([value isKindOfClass:[NSNumber class]]) { + return value; + } else if ([value isKindOfClass:[NSString class]]) { + return value; + } else if ([value isKindOfClass:[NSArray class]]) { + NSMutableArray *errors = [[NSMutableArray alloc] init]; + for (id error in value) { + [errors addObject:[FIAObjectTranslator encodeNSErrorUserInfo:error]]; + } + return errors; + } else { + return [NSString + stringWithFormat: + @"Unable to encode native userInfo object of type %@ to map. Please submit an issue at " + @"https://github.com/flutter/flutter/issues/new with the title " + @"\"[in_app_purchase_storekit] " + @"Unable to encode userInfo of type %@\" and add reproduction steps and the error " + @"details in " + @"the description field.", + [value class], [value class]]; + } +} + + (NSDictionary *)getMapFromSKStorefront:(SKStorefront *)storefront { if (!storefront) { return nil; 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 51b5ce7f1614..512d2a6ba036 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 platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/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.0+3 +version: 0.3.0+4 environment: sdk: ">=2.14.0 <3.0.0" From 7b94e2f343a8d6f2dad5a353a9586e20d82c86da Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 4 Apr 2022 17:11:09 -0400 Subject: [PATCH 082/844] [image_picker] Federate mobile implementations (#5100) --- .../image_picker/image_picker/CHANGELOG.md | 1 + .../image_picker/android/settings.gradle | 1 - .../image_picker/example/ios/Podfile | 7 - .../ios/Runner.xcodeproj/project.pbxproj | 310 +------ .../image_picker/example/pubspec.yaml | 4 +- .../image_picker/image_picker/pubspec.yaml | 14 +- .../image_picker/image_picker_android/AUTHORS | 66 ++ .../image_picker_android/CHANGELOG.md | 3 + .../image_picker/image_picker_android/LICENSE | 231 +++++ .../image_picker_android/README.md | 11 + .../android/build.gradle | 0 .../android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 0 .../plugins/imagepicker/ExifDataCopier.java | 0 .../plugins/imagepicker/FileUtils.java | 0 .../plugins/imagepicker/ImagePickerCache.java | 0 .../imagepicker/ImagePickerDelegate.java | 0 .../imagepicker/ImagePickerFileProvider.java | 0 .../imagepicker/ImagePickerPlugin.java | 0 .../plugins/imagepicker/ImagePickerUtils.java | 0 .../plugins/imagepicker/ImageResizer.java | 0 .../xml/flutter_image_picker_file_paths.xml | 0 .../plugins/imagepicker/FileUtilTest.java | 0 .../imagepicker/ImagePickerCacheTest.java | 0 .../imagepicker/ImagePickerDelegateTest.java | 0 .../imagepicker/ImagePickerPluginTest.java | 0 .../plugins/imagepicker/ImageResizerTest.java | 0 .../org.mockito.plugins.MockMaker | 0 .../android/src/test/resources/pngImage.png | Bin .../image_picker_android/example/README.md | 8 + .../example/android/app/build.gradle | 67 ++ .../gradle/wrapper/gradle-wrapper.properties | 5 + .../flutter/plugins/DartIntegrationTest.java | 14 + .../FlutterActivityTest.java | 19 + .../imagepickerexample/ImagePickerTest.java | 23 + .../android/app/src/debug/AndroidManifest.xml | 17 + .../android/app/src/main/AndroidManifest.xml | 19 + .../main/java/io/flutter/plugins/.gitignore | 1 + .../ImagePickerTestActivity.java | 20 + .../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 .../example/android/build.gradle | 29 + .../example/android/gradle.properties | 5 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../example/android/settings.gradle | 15 + .../integration_test/image_picker_test.dart | 12 + .../example/lib/main.dart | 467 ++++++++++ .../image_picker_android/example/pubspec.yaml | 31 + .../example/test_driver/integration_test.dart | 7 + .../image_picker_android/pubspec.yaml | 28 + .../image_picker/image_picker_ios/AUTHORS | 66 ++ .../image_picker_ios/CHANGELOG.md | 3 + .../image_picker/image_picker_ios/LICENSE | 231 +++++ .../image_picker/image_picker_ios/README.md | 11 + .../image_picker_ios/example/README.md | 8 + .../integration_test/image_picker_test.dart | 12 + .../ios/Flutter/AppFrameworkInfo.plist | 30 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + .../image_picker_ios/example/ios/Podfile | 45 + .../ios/Runner.xcodeproj/project.pbxproj | 796 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/xcschemes/Runner.xcscheme | 117 +++ .../xcschemes/RunnerUITests.xcscheme | 52 ++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/ios/Runner/.gitignore | 2 + .../example/ios/Runner/AppDelegate.h | 10 + .../example/ios/Runner/AppDelegate.m | 16 + .../AppIcon.appiconset/Contents.json | 121 +++ .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 bytes .../ios/Runner/Assets.xcassets/Contents.json | 6 + .../Runner/Base.lproj/LaunchScreen.storyboard | 27 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../example/ios/Runner/Info.plist | 59 ++ .../example/ios/Runner/main.m | 13 + .../ios/RunnerTests/ImagePickerPluginTests.m | 4 +- .../ios/RunnerTests/ImagePickerTestImages.h | 0 .../ios/RunnerTests/ImagePickerTestImages.m | 0 .../example/ios/RunnerTests/ImageUtilTests.m | 4 +- .../example/ios/RunnerTests/Info.plist | 0 .../ios/RunnerTests/MetaDataUtilTests.m | 4 +- .../ios/RunnerTests/PhotoAssetUtilTests.m | 4 +- .../PickerSaveImageToPathOperationTests.m | 4 +- .../ImagePickerFromGalleryUITests.m | 0 .../ImagePickerFromLimitedGalleryUITests.m | 0 .../example/ios/RunnerUITests/Info.plist | 0 .../example/ios/TestImages/gifImage.gif | Bin 0 -> 843 bytes .../example/ios/TestImages/jpgImage.jpg | Bin 0 -> 3290 bytes .../example/ios/TestImages/pngImage.png | Bin 0 -> 593 bytes .../example/ios/TestImages/webpImage.webp | Bin 0 -> 2622 bytes .../ios/image_picker_exampleTests/Info.plist | 22 + .../image_picker_ios/example/lib/main.dart | 467 ++++++++++ .../image_picker_ios/example/pubspec.yaml | 29 + .../example/test_driver/integration_test.dart | 7 + .../ios/Assets/.gitkeep | 0 .../ios/Classes/FLTImagePickerImageUtil.h | 0 .../ios/Classes/FLTImagePickerImageUtil.m | 0 .../ios/Classes/FLTImagePickerMetaDataUtil.h | 0 .../ios/Classes/FLTImagePickerMetaDataUtil.m | 0 .../Classes/FLTImagePickerPhotoAssetUtil.h | 0 .../Classes/FLTImagePickerPhotoAssetUtil.m | 0 .../ios/Classes/FLTImagePickerPlugin.h | 0 .../ios/Classes/FLTImagePickerPlugin.m | 0 .../ios/Classes/FLTImagePickerPlugin_Test.h | 4 +- .../FLTPHPickerSaveImageToPathOperation.h | 0 .../FLTPHPickerSaveImageToPathOperation.m | 0 .../ios/Classes/ImagePickerPlugin.modulemap | 6 +- .../ios/Classes/image_picker_ios-umbrella.h} | 2 +- .../ios/image_picker_ios.podspec} | 6 +- .../image_picker_ios/pubspec.yaml | 26 + 127 files changed, 3367 insertions(+), 343 deletions(-) delete mode 100755 packages/image_picker/image_picker/android/settings.gradle create mode 100644 packages/image_picker/image_picker_android/AUTHORS create mode 100644 packages/image_picker/image_picker_android/CHANGELOG.md create mode 100644 packages/image_picker/image_picker_android/LICENSE create mode 100755 packages/image_picker/image_picker_android/README.md rename packages/image_picker/{image_picker => image_picker_android}/android/build.gradle (100%) create mode 100755 packages/image_picker/image_picker_android/android/settings.gradle rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/AndroidManifest.xml (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/java/io/flutter/plugins/imagepicker/ExifDataCopier.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerCache.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerFileProvider.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/java/io/flutter/plugins/imagepicker/ImageResizer.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/main/res/xml/flutter_image_picker_file_paths.xml (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerCacheTest.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/test/java/io/flutter/plugins/imagepicker/ImageResizerTest.java (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker (100%) rename packages/image_picker/{image_picker => image_picker_android}/android/src/test/resources/pngImage.png (100%) create mode 100755 packages/image_picker/image_picker_android/example/README.md create mode 100755 packages/image_picker/image_picker_android/example/android/app/build.gradle create mode 100644 packages/image_picker/image_picker_android/example/android/app/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java create mode 100644 packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/FlutterActivityTest.java create mode 100644 packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/ImagePickerTest.java create mode 100644 packages/image_picker/image_picker_android/example/android/app/src/debug/AndroidManifest.xml create mode 100755 packages/image_picker/image_picker_android/example/android/app/src/main/AndroidManifest.xml create mode 100755 packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/.gitignore create mode 100644 packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/ImagePickerTestActivity.java create mode 100755 packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100755 packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100755 packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100755 packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100755 packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100755 packages/image_picker/image_picker_android/example/android/build.gradle create mode 100755 packages/image_picker/image_picker_android/example/android/gradle.properties create mode 100644 packages/image_picker/image_picker_android/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 packages/image_picker/image_picker_android/example/android/settings.gradle create mode 100644 packages/image_picker/image_picker_android/example/integration_test/image_picker_test.dart create mode 100755 packages/image_picker/image_picker_android/example/lib/main.dart create mode 100755 packages/image_picker/image_picker_android/example/pubspec.yaml create mode 100644 packages/image_picker/image_picker_android/example/test_driver/integration_test.dart create mode 100755 packages/image_picker/image_picker_android/pubspec.yaml create mode 100644 packages/image_picker/image_picker_ios/AUTHORS create mode 100644 packages/image_picker/image_picker_ios/CHANGELOG.md create mode 100644 packages/image_picker/image_picker_ios/LICENSE create mode 100755 packages/image_picker/image_picker_ios/README.md create mode 100755 packages/image_picker/image_picker_ios/example/README.md create mode 100644 packages/image_picker/image_picker_ios/example/integration_test/image_picker_test.dart create mode 100755 packages/image_picker/image_picker_ios/example/ios/Flutter/AppFrameworkInfo.plist create mode 100755 packages/image_picker/image_picker_ios/example/ios/Flutter/Debug.xcconfig create mode 100755 packages/image_picker/image_picker_ios/example/ios/Flutter/Release.xcconfig create mode 100644 packages/image_picker/image_picker_ios/example/ios/Podfile create mode 100644 packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.pbxproj create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/RunnerUITests.xcscheme create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/image_picker/image_picker_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/.gitignore create mode 100644 packages/image_picker/image_picker_ios/example/ios/Runner/AppDelegate.h create mode 100644 packages/image_picker/image_picker_ios/example/ios/Runner/AppDelegate.m create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/Contents.json create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Base.lproj/Main.storyboard create mode 100755 packages/image_picker/image_picker_ios/example/ios/Runner/Info.plist create mode 100644 packages/image_picker/image_picker_ios/example/ios/Runner/main.m rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerTests/ImagePickerPluginTests.m (99%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerTests/ImagePickerTestImages.h (100%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerTests/ImagePickerTestImages.m (100%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerTests/ImageUtilTests.m (97%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerTests/Info.plist (100%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerTests/MetaDataUtilTests.m (98%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerTests/PhotoAssetUtilTests.m (99%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m (98%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerUITests/ImagePickerFromGalleryUITests.m (100%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerUITests/ImagePickerFromLimitedGalleryUITests.m (100%) rename packages/image_picker/{image_picker => image_picker_ios}/example/ios/RunnerUITests/Info.plist (100%) create mode 100644 packages/image_picker/image_picker_ios/example/ios/TestImages/gifImage.gif create mode 100644 packages/image_picker/image_picker_ios/example/ios/TestImages/jpgImage.jpg create mode 100644 packages/image_picker/image_picker_ios/example/ios/TestImages/pngImage.png create mode 100644 packages/image_picker/image_picker_ios/example/ios/TestImages/webpImage.webp create mode 100644 packages/image_picker/image_picker_ios/example/ios/image_picker_exampleTests/Info.plist create mode 100755 packages/image_picker/image_picker_ios/example/lib/main.dart create mode 100755 packages/image_picker/image_picker_ios/example/pubspec.yaml create mode 100644 packages/image_picker/image_picker_ios/example/test_driver/integration_test.dart rename packages/image_picker/{image_picker => image_picker_ios}/ios/Assets/.gitkeep (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTImagePickerImageUtil.h (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTImagePickerImageUtil.m (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTImagePickerMetaDataUtil.h (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTImagePickerMetaDataUtil.m (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTImagePickerPhotoAssetUtil.h (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTImagePickerPhotoAssetUtil.m (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTImagePickerPlugin.h (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTImagePickerPlugin.m (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTImagePickerPlugin_Test.h (95%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTPHPickerSaveImageToPathOperation.h (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/FLTPHPickerSaveImageToPathOperation.m (100%) rename packages/image_picker/{image_picker => image_picker_ios}/ios/Classes/ImagePickerPlugin.modulemap (76%) rename packages/image_picker/{image_picker/ios/Classes/image_picker-umbrella.h => image_picker_ios/ios/Classes/image_picker_ios-umbrella.h} (79%) rename packages/image_picker/{image_picker/ios/image_picker.podspec => image_picker_ios/ios/image_picker_ios.podspec} (86%) create mode 100755 packages/image_picker/image_picker_ios/pubspec.yaml diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index aa4a28511a8a..d84304f0b5e9 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Moves Android and iOS implementations to federated packages. * Adds OS version support information to README. ## 0.8.4+11 diff --git a/packages/image_picker/image_picker/android/settings.gradle b/packages/image_picker/image_picker/android/settings.gradle deleted file mode 100755 index 5b9496172108..000000000000 --- a/packages/image_picker/image_picker/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'imagepicker' diff --git a/packages/image_picker/image_picker/example/ios/Podfile b/packages/image_picker/image_picker/example/ios/Podfile index 5bc7b7e85717..f7d6a5e68c3a 100644 --- a/packages/image_picker/image_picker/example/ios/Podfile +++ b/packages/image_picker/image_picker/example/ios/Podfile @@ -29,13 +29,6 @@ flutter_ios_podfile_setup target 'Runner' do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) - - target 'RunnerTests' do - platform :ios, '9.0' - inherit! :search_paths - # Pods for testing - pod 'OCMock', '~> 3.8.1' - end end post_install do |installer| diff --git a/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj b/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj index 2847bfd85046..589858f39019 100644 --- a/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/image_picker/image_picker/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,53 +3,20 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 46; objects = { /* Begin PBXBuildFile section */ - 334733FC266813EE00DCC49E /* ImageUtilTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FC8F0ED229FB90B00C8D58F /* ImageUtilTests.m */; }; - 334733FD266813F100DCC49E /* MetaDataUtilTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 680049252280D736006DD6AB /* MetaDataUtilTests.m */; }; - 334733FE266813F400DCC49E /* PhotoAssetUtilTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68F4B463228B3AB500C25614 /* PhotoAssetUtilTests.m */; }; - 334733FF266813FA00DCC49E /* ImagePickerTestImages.m in Sources */ = {isa = PBXBuildFile; fileRef = F78AF3182342D9D7008449C7 /* ImagePickerTestImages.m */; }; - 33473400266813FD00DCC49E /* ImagePickerPluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68B9AF71243E4B3F00927CE4 /* ImagePickerPluginTests.m */; }; - 3A72BAD3FAE6E0FA9D80826B /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35AE65F25E0B8C8214D8372B /* libPods-RunnerTests.a */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 5C9513011EC38BD300040975 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C9513001EC38BD300040975 /* GeneratedPluginRegistrant.m */; }; - 680049382280F2B9006DD6AB /* pngImage.png in Resources */ = {isa = PBXBuildFile; fileRef = 680049352280F2B8006DD6AB /* pngImage.png */; }; - 680049392280F2B9006DD6AB /* jpgImage.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 680049362280F2B8006DD6AB /* jpgImage.jpg */; }; - 6801C8392555D726009DAF8D /* ImagePickerFromGalleryUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6801C8382555D726009DAF8D /* ImagePickerFromGalleryUITests.m */; }; - 86430DF9272D71E9002D9D6C /* gifImage.gif in Resources */ = {isa = PBXBuildFile; fileRef = 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */; }; - 86E9A893272754860017E6E0 /* PickerSaveImageToPathOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 86E9A892272754860017E6E0 /* PickerSaveImageToPathOperationTests.m */; }; - 86E9A894272754A30017E6E0 /* webpImage.webp in Resources */ = {isa = PBXBuildFile; fileRef = 86E9A88F272747B90017E6E0 /* webpImage.webp */; }; - 86E9A895272769130017E6E0 /* pngImage.png in Resources */ = {isa = PBXBuildFile; fileRef = 680049352280F2B8006DD6AB /* pngImage.png */; }; - 86E9A896272769150017E6E0 /* jpgImage.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 680049362280F2B8006DD6AB /* jpgImage.jpg */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 9FC8F0E9229FA49E00C8D58F /* gifImage.gif in Resources */ = {isa = PBXBuildFile; fileRef = 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */; }; - 9FC8F0EC229FA68500C8D58F /* gifImage.gif in Resources */ = {isa = PBXBuildFile; fileRef = 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */; }; - BE6173D826A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = BE6173D726A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m */; }; F4F7A436CCA4BF276270A3AE /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EC32F6993F4529982D9519F1 /* libPods-Runner.a */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - 334733F72668136400DCC49E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; - 6801C83B2555D726009DAF8D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; @@ -65,27 +32,18 @@ /* Begin PBXFileReference section */ 0C7B151765FD4249454C49AD /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; - 334733F22668136400DCC49E /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 334733F62668136400DCC49E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35AE65F25E0B8C8214D8372B /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 5A9D31B91557877A0E8EF3E7 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 5C9512FF1EC38BD300040975 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 5C9513001EC38BD300040975 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 680049252280D736006DD6AB /* MetaDataUtilTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MetaDataUtilTests.m; sourceTree = ""; }; 680049352280F2B8006DD6AB /* pngImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = pngImage.png; sourceTree = ""; }; 680049362280F2B8006DD6AB /* jpgImage.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = jpgImage.jpg; sourceTree = ""; }; 6801632E632668F4349764C9 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 6801C8362555D726009DAF8D /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 6801C8382555D726009DAF8D /* ImagePickerFromGalleryUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImagePickerFromGalleryUITests.m; sourceTree = ""; }; - 6801C83A2555D726009DAF8D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 68B9AF71243E4B3F00927CE4 /* ImagePickerPluginTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImagePickerPluginTests.m; sourceTree = ""; }; - 68F4B463228B3AB500C25614 /* PhotoAssetUtilTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PhotoAssetUtilTests.m; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 86E9A88F272747B90017E6E0 /* webpImage.webp */ = {isa = PBXFileReference; lastKnownFileType = file; path = webpImage.webp; sourceTree = ""; }; - 86E9A892272754860017E6E0 /* PickerSaveImageToPathOperationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PickerSaveImageToPathOperationTests.m; 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; }; @@ -95,30 +53,11 @@ 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 = ""; }; 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = gifImage.gif; sourceTree = ""; }; - 9FC8F0ED229FB90B00C8D58F /* ImageUtilTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImageUtilTests.m; sourceTree = ""; }; - BE6173D726A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImagePickerFromLimitedGalleryUITests.m; sourceTree = ""; }; DC6FCAAD4E7580C9B3C2E21D /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; EC32F6993F4529982D9519F1 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - F78AF3172342D9D7008449C7 /* ImagePickerTestImages.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImagePickerTestImages.h; sourceTree = ""; }; - F78AF3182342D9D7008449C7 /* ImagePickerTestImages.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImagePickerTestImages.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 334733EF2668136400DCC49E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 3A72BAD3FAE6E0FA9D80826B /* libPods-RunnerTests.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 6801C8332555D726009DAF8D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -130,21 +69,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 334733F32668136400DCC49E /* RunnerTests */ = { - isa = PBXGroup; - children = ( - 9FC8F0ED229FB90B00C8D58F /* ImageUtilTests.m */, - 680049252280D736006DD6AB /* MetaDataUtilTests.m */, - 68F4B463228B3AB500C25614 /* PhotoAssetUtilTests.m */, - F78AF3172342D9D7008449C7 /* ImagePickerTestImages.h */, - F78AF3182342D9D7008449C7 /* ImagePickerTestImages.m */, - 68B9AF71243E4B3F00927CE4 /* ImagePickerPluginTests.m */, - 86E9A892272754860017E6E0 /* PickerSaveImageToPathOperationTests.m */, - 334733F62668136400DCC49E /* Info.plist */, - ); - path = RunnerTests; - sourceTree = ""; - }; 680049282280E33D006DD6AB /* TestImages */ = { isa = PBXGroup; children = ( @@ -156,16 +80,6 @@ path = TestImages; sourceTree = ""; }; - 6801C8372555D726009DAF8D /* RunnerUITests */ = { - isa = PBXGroup; - children = ( - BE6173D726A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m */, - 6801C8382555D726009DAF8D /* ImagePickerFromGalleryUITests.m */, - 6801C83A2555D726009DAF8D /* Info.plist */, - ); - path = RunnerUITests; - sourceTree = ""; - }; 840012C8B5EDBCF56B0E4AC1 /* Pods */ = { isa = PBXGroup; children = ( @@ -194,8 +108,6 @@ 680049282280E33D006DD6AB /* TestImages */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, - 334733F32668136400DCC49E /* RunnerTests */, - 6801C8372555D726009DAF8D /* RunnerUITests */, 97C146EF1CF9000F007C117D /* Products */, 840012C8B5EDBCF56B0E4AC1 /* Pods */, CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, @@ -206,8 +118,6 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, - 6801C8362555D726009DAF8D /* RunnerUITests.xctest */, - 334733F22668136400DCC49E /* RunnerTests.xctest */, ); name = Products; sourceTree = ""; @@ -248,43 +158,6 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 334733F12668136400DCC49E /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 334733F92668136400DCC49E /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - B8739A4353234497CF76B597 /* [CP] Check Pods Manifest.lock */, - 334733EE2668136400DCC49E /* Sources */, - 334733EF2668136400DCC49E /* Frameworks */, - 334733F02668136400DCC49E /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 334733F82668136400DCC49E /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = RunnerTests; - productReference = 334733F22668136400DCC49E /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 6801C8352555D726009DAF8D /* RunnerUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 6801C83F2555D726009DAF8D /* Build configuration list for PBXNativeTarget "RunnerUITests" */; - buildPhases = ( - 6801C8322555D726009DAF8D /* Sources */, - 6801C8332555D726009DAF8D /* Frameworks */, - 6801C8342555D726009DAF8D /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 6801C83C2555D726009DAF8D /* PBXTargetDependency */, - ); - name = RunnerUITests; - productName = RunnerUITests; - productReference = 6801C8362555D726009DAF8D /* RunnerUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; 97C146ED1CF9000F007C117D /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; @@ -316,16 +189,6 @@ LastUpgradeCheck = 1300; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { - 334733F12668136400DCC49E = { - CreatedOnToolsVersion = 12.5; - ProvisioningStyle = Automatic; - TestTargetID = 97C146ED1CF9000F007C117D; - }; - 6801C8352555D726009DAF8D = { - CreatedOnToolsVersion = 11.7; - ProvisioningStyle = Automatic; - TestTargetID = 97C146ED1CF9000F007C117D; - }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; SystemCapabilities = { @@ -350,34 +213,11 @@ projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, - 334733F12668136400DCC49E /* RunnerTests */, - 6801C8352555D726009DAF8D /* RunnerUITests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 334733F02668136400DCC49E /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 86430DF9272D71E9002D9D6C /* gifImage.gif in Resources */, - 86E9A894272754A30017E6E0 /* webpImage.webp in Resources */, - 86E9A895272769130017E6E0 /* pngImage.png in Resources */, - 86E9A896272769150017E6E0 /* jpgImage.jpg in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 6801C8342555D726009DAF8D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 9FC8F0EC229FA68500C8D58F /* gifImage.gif in Resources */, - 680049382280F2B9006DD6AB /* pngImage.png in Resources */, - 680049392280F2B9006DD6AB /* jpgImage.jpg in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EC1CF9000F007C117D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -438,53 +278,9 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - B8739A4353234497CF76B597 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 334733EE2668136400DCC49E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 334733FD266813F100DCC49E /* MetaDataUtilTests.m in Sources */, - 334733FF266813FA00DCC49E /* ImagePickerTestImages.m in Sources */, - 86E9A893272754860017E6E0 /* PickerSaveImageToPathOperationTests.m in Sources */, - 334733FC266813EE00DCC49E /* ImageUtilTests.m in Sources */, - 33473400266813FD00DCC49E /* ImagePickerPluginTests.m in Sources */, - 334733FE266813F400DCC49E /* PhotoAssetUtilTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 6801C8322555D726009DAF8D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 6801C8392555D726009DAF8D /* ImagePickerFromGalleryUITests.m in Sources */, - BE6173D826A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EA1CF9000F007C117D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -497,19 +293,6 @@ }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 334733F82668136400DCC49E /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = 334733F72668136400DCC49E /* PBXContainerItemProxy */; - }; - 6801C83C2555D726009DAF8D /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = 6801C83B2555D726009DAF8D /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -530,79 +313,6 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - 334733FA2668136400DCC49E /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = DC6FCAAD4E7580C9B3C2E21D /* Pods-RunnerTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RunnerTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Debug; - }; - 334733FB2668136400DCC49E /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 0C7B151765FD4249454C49AD /* Pods-RunnerTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RunnerTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Release; - }; - 6801C83D2555D726009DAF8D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = RunnerUITests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = Runner; - }; - name = Debug; - }; - 6801C83E2555D726009DAF8D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = RunnerUITests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = Runner; - }; - name = Release; - }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -754,24 +464,6 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 334733F92668136400DCC49E /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 334733FA2668136400DCC49E /* Debug */, - 334733FB2668136400DCC49E /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 6801C83F2555D726009DAF8D /* Build configuration list for PBXNativeTarget "RunnerUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 6801C83D2555D726009DAF8D /* Debug */, - 6801C83E2555D726009DAF8D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/packages/image_picker/image_picker/example/pubspec.yaml b/packages/image_picker/image_picker/example/pubspec.yaml index 92db04501f83..4fe823587398 100755 --- a/packages/image_picker/image_picker/example/pubspec.yaml +++ b/packages/image_picker/image_picker/example/pubspec.yaml @@ -20,9 +20,7 @@ dependencies: video_player: ^2.1.4 dev_dependencies: - # TODO(bparrishMines): Change version to `0.2.0` once published. - espresso: - path: ../../../espresso + espresso: ^0.2.0 flutter_driver: sdk: flutter integration_test: diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 553599f7306f..05ac189fb05d 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -4,6 +4,9 @@ description: Flutter plugin for selecting images from the Android and iOS image repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 version: 0.8.4+11 +# Temporarily disable publishing to allow moving Android and iOS +# implementations. +publish_to: none environment: sdk: ">=2.14.0 <3.0.0" @@ -13,18 +16,21 @@ flutter: plugin: platforms: android: - package: io.flutter.plugins.imagepicker - pluginClass: ImagePickerPlugin + default_package: image_picker_android ios: - pluginClass: FLTImagePickerPlugin + default_package: image_picker_ios web: default_package: image_picker_for_web dependencies: flutter: sdk: flutter - flutter_plugin_android_lifecycle: ^2.0.1 + # Temporary path dependencies to allow moving Android and iOS implementations. + image_picker_android: + path: ../image_picker_android image_picker_for_web: ^2.1.0 + image_picker_ios: + path: ../image_picker_ios image_picker_platform_interface: ^2.3.0 dev_dependencies: diff --git a/packages/image_picker/image_picker_android/AUTHORS b/packages/image_picker/image_picker_android/AUTHORS new file mode 100644 index 000000000000..493a0b4ef9c2 --- /dev/null +++ b/packages/image_picker/image_picker_android/AUTHORS @@ -0,0 +1,66 @@ +# 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. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md new file mode 100644 index 000000000000..3472ade28d5b --- /dev/null +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.8.4+11 + +* Splits from `image_picker` as a federated implementation. diff --git a/packages/image_picker/image_picker_android/LICENSE b/packages/image_picker/image_picker_android/LICENSE new file mode 100644 index 000000000000..0be8bbc3e68d --- /dev/null +++ b/packages/image_picker/image_picker_android/LICENSE @@ -0,0 +1,231 @@ +image_picker + +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. +-------------------------------------------------------------------------------- +aFileChooser + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2011 - 2013 Paul Burke + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/image_picker/image_picker_android/README.md b/packages/image_picker/image_picker_android/README.md new file mode 100755 index 000000000000..43d08c2a8b3a --- /dev/null +++ b/packages/image_picker/image_picker_android/README.md @@ -0,0 +1,11 @@ +# image\_picker\_android + +The Android implementation of [`image_picker`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `image_picker` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/image_picker +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/image_picker/image_picker/android/build.gradle b/packages/image_picker/image_picker_android/android/build.gradle similarity index 100% rename from packages/image_picker/image_picker/android/build.gradle rename to packages/image_picker/image_picker_android/android/build.gradle diff --git a/packages/image_picker/image_picker_android/android/settings.gradle b/packages/image_picker/image_picker_android/android/settings.gradle new file mode 100755 index 000000000000..3c673efcd542 --- /dev/null +++ b/packages/image_picker/image_picker_android/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'image_picker_android' diff --git a/packages/image_picker/image_picker/android/src/main/AndroidManifest.xml b/packages/image_picker/image_picker_android/android/src/main/AndroidManifest.xml similarity index 100% rename from packages/image_picker/image_picker/android/src/main/AndroidManifest.xml rename to packages/image_picker/image_picker_android/android/src/main/AndroidManifest.xml diff --git a/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ExifDataCopier.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ExifDataCopier.java similarity index 100% rename from packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ExifDataCopier.java rename to packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ExifDataCopier.java diff --git a/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java similarity index 100% rename from packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java rename to packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/FileUtils.java diff --git a/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerCache.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerCache.java similarity index 100% rename from packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerCache.java rename to packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerCache.java diff --git a/packages/image_picker/image_picker/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 similarity index 100% rename from packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java rename to packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerDelegate.java diff --git a/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerFileProvider.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerFileProvider.java similarity index 100% rename from packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerFileProvider.java rename to packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerFileProvider.java diff --git a/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java similarity index 100% rename from packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java rename to packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java diff --git a/packages/image_picker/image_picker/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 similarity index 100% rename from packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java rename to packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerUtils.java diff --git a/packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImageResizer.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImageResizer.java similarity index 100% rename from packages/image_picker/image_picker/android/src/main/java/io/flutter/plugins/imagepicker/ImageResizer.java rename to packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImageResizer.java diff --git a/packages/image_picker/image_picker/android/src/main/res/xml/flutter_image_picker_file_paths.xml b/packages/image_picker/image_picker_android/android/src/main/res/xml/flutter_image_picker_file_paths.xml similarity index 100% rename from packages/image_picker/image_picker/android/src/main/res/xml/flutter_image_picker_file_paths.xml rename to packages/image_picker/image_picker_android/android/src/main/res/xml/flutter_image_picker_file_paths.xml diff --git a/packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java similarity index 100% rename from packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java rename to packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/FileUtilTest.java diff --git a/packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerCacheTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerCacheTest.java similarity index 100% rename from packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerCacheTest.java rename to packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerCacheTest.java diff --git a/packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java similarity index 100% rename from packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java rename to packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerDelegateTest.java diff --git a/packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java similarity index 100% rename from packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java rename to packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java diff --git a/packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImageResizerTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImageResizerTest.java similarity index 100% rename from packages/image_picker/image_picker/android/src/test/java/io/flutter/plugins/imagepicker/ImageResizerTest.java rename to packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImageResizerTest.java diff --git a/packages/image_picker/image_picker/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/packages/image_picker/image_picker_android/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker similarity index 100% rename from packages/image_picker/image_picker/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker rename to packages/image_picker/image_picker_android/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker diff --git a/packages/image_picker/image_picker/android/src/test/resources/pngImage.png b/packages/image_picker/image_picker_android/android/src/test/resources/pngImage.png similarity index 100% rename from packages/image_picker/image_picker/android/src/test/resources/pngImage.png rename to packages/image_picker/image_picker_android/android/src/test/resources/pngImage.png diff --git a/packages/image_picker/image_picker_android/example/README.md b/packages/image_picker/image_picker_android/example/README.md new file mode 100755 index 000000000000..86d5c23ba209 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/README.md @@ -0,0 +1,8 @@ +# image_picker_example + +Demonstrates how to use the `image_picker` plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/image_picker/image_picker_android/example/android/app/build.gradle b/packages/image_picker/image_picker_android/example/android/app/build.gradle new file mode 100755 index 000000000000..e73e3fe01003 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/build.gradle @@ -0,0 +1,67 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 31 + testOptions.unitTests.includeAndroidResources = true + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + applicationId "io.flutter.plugins.imagepicker.example" + minSdkVersion 16 + targetSdkVersion 28 + multiDexEnabled true + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } + + testOptions { + unitTests.returnDefaultValues = true + } +} + +flutter { + source '../..' +} + +dependencies { + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + api 'androidx.test:core:1.2.0' +} diff --git a/packages/image_picker/image_picker_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/image_picker/image_picker_android/example/android/app/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..9a4163a4f5ee --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java new file mode 100644 index 000000000000..0f4298dca155 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java @@ -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. + +package io.flutter.plugins; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DartIntegrationTest {} diff --git a/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/FlutterActivityTest.java b/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/FlutterActivityTest.java new file mode 100644 index 000000000000..91e068fa8043 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/FlutterActivityTest.java @@ -0,0 +1,19 @@ +// 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.imagepickerexample; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.integration_test.FlutterTestRunner; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.plugins.DartIntegrationTest; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@DartIntegrationTest +@RunWith(FlutterTestRunner.class) +public class FlutterActivityTest { + @Rule + public ActivityTestRule rule = new ActivityTestRule<>(FlutterActivity.class); +} diff --git a/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/ImagePickerTest.java b/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/ImagePickerTest.java new file mode 100644 index 000000000000..c4a1532d940c --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/ImagePickerTest.java @@ -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. + +package io.flutter.plugins.imagepickerexample; + +import static org.junit.Assert.assertTrue; + +import androidx.test.core.app.ActivityScenario; +import io.flutter.plugins.imagepicker.ImagePickerPlugin; +import org.junit.Test; + +public class ImagePickerTest { + @Test + public void imagePickerPluginIsAdded() { + final ActivityScenario scenario = + ActivityScenario.launch(ImagePickerTestActivity.class); + scenario.onActivity( + activity -> { + assertTrue(activity.engine.getPlugins().has(ImagePickerPlugin.class)); + }); + } +} diff --git a/packages/image_picker/image_picker_android/example/android/app/src/debug/AndroidManifest.xml b/packages/image_picker/image_picker_android/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000000..6f85cefded34 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/AndroidManifest.xml b/packages/image_picker/image_picker_android/example/android/app/src/main/AndroidManifest.xml new file mode 100755 index 000000000000..543fca922e1b --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/.gitignore b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/.gitignore new file mode 100755 index 000000000000..9eb4563d2ae1 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/.gitignore @@ -0,0 +1 @@ +GeneratedPluginRegistrant.java diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/ImagePickerTestActivity.java b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/ImagePickerTestActivity.java new file mode 100644 index 000000000000..827687a10e79 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/app/src/main/java/io/flutter/plugins/imagepickerexample/ImagePickerTestActivity.java @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.imagepickerexample; + +import androidx.annotation.NonNull; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.engine.FlutterEngine; + +// Makes the FlutterEngine accessible for testing. +public class ImagePickerTestActivity extends FlutterActivity { + public FlutterEngine engine; + + @Override + public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { + super.configureFlutterEngine(flutterEngine); + engine = flutterEngine; + } +} diff --git a/packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100755 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/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100755 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/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100755 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/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/image_picker/image_picker_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100755 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/image_picker/image_picker_android/example/android/build.gradle b/packages/image_picker/image_picker_android/example/android/build.gradle new file mode 100755 index 000000000000..e101ac08df55 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.3.0' + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/packages/image_picker/image_picker_android/example/android/gradle.properties b/packages/image_picker/image_picker_android/example/android/gradle.properties new file mode 100755 index 000000000000..6effed032590 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/gradle.properties @@ -0,0 +1,5 @@ +org.gradle.jvmargs=-Xmx1536M +android.enableR8=true +android.useAndroidX=true +android.enableJetifier=true +android.enableUnitTestBinaryResources=true diff --git a/packages/image_picker/image_picker_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/image_picker/image_picker_android/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..019065d1d650 --- /dev/null +++ b/packages/image_picker/image_picker_android/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-4.10.2-all.zip diff --git a/packages/image_picker/image_picker_android/example/android/settings.gradle b/packages/image_picker/image_picker_android/example/android/settings.gradle new file mode 100755 index 000000000000..115da6cb4f4d --- /dev/null +++ b/packages/image_picker/image_picker_android/example/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withInputStream { stream -> plugins.load(stream) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/packages/image_picker/image_picker_android/example/integration_test/image_picker_test.dart b/packages/image_picker/image_picker_android/example/integration_test/image_picker_test.dart new file mode 100644 index 000000000000..2b82b4bda5e4 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/integration_test/image_picker_test.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. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('placeholder test', (WidgetTester tester) async {}); +} diff --git a/packages/image_picker/image_picker_android/example/lib/main.dart b/packages/image_picker/image_picker_android/example/lib/main.dart new file mode 100755 index 000000000000..48eee35445da --- /dev/null +++ b/packages/image_picker/image_picker_android/example/lib/main.dart @@ -0,0 +1,467 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'package:video_player/video_player.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return const MaterialApp( + title: 'Image Picker Demo', + home: MyHomePage(title: 'Image Picker Example'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({Key? key, this.title}) : super(key: key); + + final String? title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + List? _imageFileList; + + set _imageFile(XFile? value) { + _imageFileList = value == null ? null : [value]; + } + + dynamic _pickImageError; + bool isVideo = false; + + VideoPlayerController? _controller; + VideoPlayerController? _toBeDisposed; + String? _retrieveDataError; + + final ImagePickerPlatform _picker = ImagePickerPlatform.instance; + final TextEditingController maxWidthController = TextEditingController(); + final TextEditingController maxHeightController = TextEditingController(); + final TextEditingController qualityController = TextEditingController(); + + Future _playVideo(XFile? file) async { + if (file != null && mounted) { + await _disposeVideoController(); + late VideoPlayerController controller; + if (kIsWeb) { + controller = VideoPlayerController.network(file.path); + } else { + controller = VideoPlayerController.file(File(file.path)); + } + _controller = controller; + // In web, most browsers won't honor a programmatic call to .play + // if the video has a sound track (and is not muted). + // Mute the video so it auto-plays in web! + // This is not needed if the call to .play is the result of user + // interaction (clicking on a "play" button, for example). + const double volume = kIsWeb ? 0.0 : 1.0; + await controller.setVolume(volume); + await controller.initialize(); + await controller.setLooping(true); + await controller.play(); + setState(() {}); + } + } + + Future _onImageButtonPressed(ImageSource source, + {BuildContext? context, bool isMultiImage = false}) async { + if (_controller != null) { + await _controller!.setVolume(0.0); + } + if (isVideo) { + final XFile? file = await _picker.getVideo( + source: source, maxDuration: const Duration(seconds: 10)); + await _playVideo(file); + } else if (isMultiImage) { + await _displayPickImageDialog(context!, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final List? pickedFileList = await _picker.getMultiImage( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ); + setState(() { + _imageFileList = pickedFileList; + }); + } catch (e) { + setState(() { + _pickImageError = e; + }); + } + }); + } else { + await _displayPickImageDialog(context!, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final XFile? pickedFile = await _picker.getImage( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ); + setState(() { + _imageFile = pickedFile; + }); + } catch (e) { + setState(() { + _pickImageError = e; + }); + } + }); + } + } + + @override + void deactivate() { + if (_controller != null) { + _controller!.setVolume(0.0); + _controller!.pause(); + } + super.deactivate(); + } + + @override + void dispose() { + _disposeVideoController(); + maxWidthController.dispose(); + maxHeightController.dispose(); + qualityController.dispose(); + super.dispose(); + } + + Future _disposeVideoController() async { + if (_toBeDisposed != null) { + await _toBeDisposed!.dispose(); + } + _toBeDisposed = _controller; + _controller = null; + } + + Widget _previewVideo() { + final Text? retrieveError = _getRetrieveErrorWidget(); + if (retrieveError != null) { + return retrieveError; + } + if (_controller == null) { + return const Text( + 'You have not yet picked a video', + textAlign: TextAlign.center, + ); + } + return Padding( + padding: const EdgeInsets.all(10.0), + child: AspectRatioVideo(_controller), + ); + } + + Widget _previewImages() { + final Text? retrieveError = _getRetrieveErrorWidget(); + if (retrieveError != null) { + return retrieveError; + } + if (_imageFileList != null) { + return Semantics( + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + label: 'image_picker_example_picked_images'); + } else if (_pickImageError != null) { + return Text( + 'Pick image error: $_pickImageError', + textAlign: TextAlign.center, + ); + } else { + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + } + } + + Widget _handlePreview() { + if (isVideo) { + return _previewVideo(); + } else { + return _previewImages(); + } + } + + Future retrieveLostData() async { + final LostDataResponse response = await _picker.getLostData(); + if (response.isEmpty) { + return; + } + if (response.file != null) { + if (response.type == RetrieveType.video) { + isVideo = true; + await _playVideo(response.file); + } else { + isVideo = false; + setState(() { + _imageFile = response.file; + _imageFileList = response.files; + }); + } + } else { + _retrieveDataError = response.exception!.code; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title!), + ), + body: Center( + child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android + ? FutureBuilder( + future: retrieveLostData(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + switch (snapshot.connectionState) { + case ConnectionState.none: + case ConnectionState.waiting: + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + case ConnectionState.done: + return _handlePreview(); + default: + if (snapshot.hasError) { + return Text( + 'Pick image/video error: ${snapshot.error}}', + textAlign: TextAlign.center, + ); + } else { + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + } + } + }, + ) + : _handlePreview(), + ), + floatingActionButton: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Semantics( + label: 'image_picker_example_from_gallery', + child: FloatingActionButton( + onPressed: () { + isVideo = false; + _onImageButtonPressed(ImageSource.gallery, context: context); + }, + heroTag: 'image0', + tooltip: 'Pick Image from gallery', + child: const Icon(Icons.photo), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + isVideo = false; + _onImageButtonPressed( + ImageSource.gallery, + context: context, + isMultiImage: true, + ); + }, + heroTag: 'image1', + tooltip: 'Pick Multiple Image from gallery', + child: const Icon(Icons.photo_library), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + isVideo = false; + _onImageButtonPressed(ImageSource.camera, context: context); + }, + heroTag: 'image2', + tooltip: 'Take a Photo', + child: const Icon(Icons.camera_alt), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + backgroundColor: Colors.red, + onPressed: () { + isVideo = true; + _onImageButtonPressed(ImageSource.gallery); + }, + heroTag: 'video0', + tooltip: 'Pick Video from gallery', + child: const Icon(Icons.video_library), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + backgroundColor: Colors.red, + onPressed: () { + isVideo = true; + _onImageButtonPressed(ImageSource.camera); + }, + heroTag: 'video1', + tooltip: 'Take a Video', + child: const Icon(Icons.videocam), + ), + ), + ], + ), + ); + } + + Text? _getRetrieveErrorWidget() { + if (_retrieveDataError != null) { + final Text result = Text(_retrieveDataError!); + _retrieveDataError = null; + return result; + } + return null; + } + + Future _displayPickImageDialog( + BuildContext context, OnPickImageCallback onPick) async { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Add optional parameters'), + content: Column( + children: [ + TextField( + controller: maxWidthController, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxWidth if desired'), + ), + TextField( + controller: maxHeightController, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxHeight if desired'), + ), + TextField( + controller: qualityController, + keyboardType: TextInputType.number, + decoration: const InputDecoration( + hintText: 'Enter quality if desired'), + ), + ], + ), + actions: [ + TextButton( + child: const Text('CANCEL'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text('PICK'), + onPressed: () { + final double? width = maxWidthController.text.isNotEmpty + ? double.parse(maxWidthController.text) + : null; + final double? height = maxHeightController.text.isNotEmpty + ? double.parse(maxHeightController.text) + : null; + final int? quality = qualityController.text.isNotEmpty + ? int.parse(qualityController.text) + : null; + onPick(width, height, quality); + Navigator.of(context).pop(); + }), + ], + ); + }); + } +} + +typedef OnPickImageCallback = void Function( + double? maxWidth, double? maxHeight, int? quality); + +class AspectRatioVideo extends StatefulWidget { + const AspectRatioVideo(this.controller); + + final VideoPlayerController? controller; + + @override + AspectRatioVideoState createState() => AspectRatioVideoState(); +} + +class AspectRatioVideoState extends State { + VideoPlayerController? get controller => widget.controller; + bool initialized = false; + + void _onVideoControllerUpdate() { + if (!mounted) { + return; + } + if (initialized != controller!.value.isInitialized) { + initialized = controller!.value.isInitialized; + setState(() {}); + } + } + + @override + void initState() { + super.initState(); + controller!.addListener(_onVideoControllerUpdate); + } + + @override + void dispose() { + controller!.removeListener(_onVideoControllerUpdate); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (initialized) { + return Center( + child: AspectRatio( + aspectRatio: controller!.value.aspectRatio, + child: VideoPlayer(controller!), + ), + ); + } else { + return Container(); + } + } +} diff --git a/packages/image_picker/image_picker_android/example/pubspec.yaml b/packages/image_picker/image_picker_android/example/pubspec.yaml new file mode 100755 index 000000000000..0d88ae139c71 --- /dev/null +++ b/packages/image_picker/image_picker_android/example/pubspec.yaml @@ -0,0 +1,31 @@ +name: image_picker_example +description: Demonstrates how to use the image_picker plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + flutter_plugin_android_lifecycle: ^2.0.1 + image_picker_android: + # When depending on this package from a real application you should use: + # image_picker_android: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # 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: ../ + image_picker_platform_interface: ^2.3.0 + video_player: ^2.1.4 + +dev_dependencies: + espresso: ^0.2.0 + flutter_driver: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/image_picker/image_picker_android/example/test_driver/integration_test.dart b/packages/image_picker/image_picker_android/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/image_picker/image_picker_android/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/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml new file mode 100755 index 000000000000..dbeef9bed193 --- /dev/null +++ b/packages/image_picker/image_picker_android/pubspec.yaml @@ -0,0 +1,28 @@ +name: image_picker_android +description: Android implementation of the image_picker plugin. +repository: https://github.com/flutter/plugins/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.4+11 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +flutter: + plugin: + implements: image_picker + platforms: + android: + package: io.flutter.plugins.imagepicker + pluginClass: ImagePickerPlugin + +dependencies: + flutter: + sdk: flutter + flutter_plugin_android_lifecycle: ^2.0.1 + image_picker_platform_interface: ^2.3.0 + +dev_dependencies: + flutter_test: + sdk: flutter + mockito: ^5.0.0 diff --git a/packages/image_picker/image_picker_ios/AUTHORS b/packages/image_picker/image_picker_ios/AUTHORS new file mode 100644 index 000000000000..493a0b4ef9c2 --- /dev/null +++ b/packages/image_picker/image_picker_ios/AUTHORS @@ -0,0 +1,66 @@ +# 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. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md new file mode 100644 index 000000000000..3472ade28d5b --- /dev/null +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.8.4+11 + +* Splits from `image_picker` as a federated implementation. diff --git a/packages/image_picker/image_picker_ios/LICENSE b/packages/image_picker/image_picker_ios/LICENSE new file mode 100644 index 000000000000..0be8bbc3e68d --- /dev/null +++ b/packages/image_picker/image_picker_ios/LICENSE @@ -0,0 +1,231 @@ +image_picker + +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. +-------------------------------------------------------------------------------- +aFileChooser + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2011 - 2013 Paul Burke + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/image_picker/image_picker_ios/README.md b/packages/image_picker/image_picker_ios/README.md new file mode 100755 index 000000000000..e9fc2cfe61e7 --- /dev/null +++ b/packages/image_picker/image_picker_ios/README.md @@ -0,0 +1,11 @@ +# image\_picker\_ios + +The iOS implementation of [`image_picker`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `image_picker` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/image_picker +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/image_picker/image_picker_ios/example/README.md b/packages/image_picker/image_picker_ios/example/README.md new file mode 100755 index 000000000000..86d5c23ba209 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/README.md @@ -0,0 +1,8 @@ +# image_picker_example + +Demonstrates how to use the `image_picker` plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/image_picker/image_picker_ios/example/integration_test/image_picker_test.dart b/packages/image_picker/image_picker_ios/example/integration_test/image_picker_test.dart new file mode 100644 index 000000000000..2b82b4bda5e4 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/integration_test/image_picker_test.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. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('placeholder test', (WidgetTester tester) async {}); +} diff --git a/packages/image_picker/image_picker_ios/example/ios/Flutter/AppFrameworkInfo.plist b/packages/image_picker/image_picker_ios/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100755 index 000000000000..3a9c234f96d4 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + UIRequiredDeviceCapabilities + + arm64 + + MinimumOSVersion + 9.0 + + diff --git a/packages/image_picker/image_picker_ios/example/ios/Flutter/Debug.xcconfig b/packages/image_picker/image_picker_ios/example/ios/Flutter/Debug.xcconfig new file mode 100755 index 000000000000..9803018ca79d --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Generated.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" diff --git a/packages/image_picker/image_picker_ios/example/ios/Flutter/Release.xcconfig b/packages/image_picker/image_picker_ios/example/ios/Flutter/Release.xcconfig new file mode 100755 index 000000000000..a4a8c604e13d --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Generated.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" diff --git a/packages/image_picker/image_picker_ios/example/ios/Podfile b/packages/image_picker/image_picker_ios/example/ios/Podfile new file mode 100644 index 000000000000..5bc7b7e85717 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Podfile @@ -0,0 +1,45 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.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 + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + + target 'RunnerTests' do + platform :ios, '9.0' + inherit! :search_paths + # Pods for testing + pod 'OCMock', '~> 3.8.1' + 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/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..2847bfd85046 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,796 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 334733FC266813EE00DCC49E /* ImageUtilTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FC8F0ED229FB90B00C8D58F /* ImageUtilTests.m */; }; + 334733FD266813F100DCC49E /* MetaDataUtilTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 680049252280D736006DD6AB /* MetaDataUtilTests.m */; }; + 334733FE266813F400DCC49E /* PhotoAssetUtilTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68F4B463228B3AB500C25614 /* PhotoAssetUtilTests.m */; }; + 334733FF266813FA00DCC49E /* ImagePickerTestImages.m in Sources */ = {isa = PBXBuildFile; fileRef = F78AF3182342D9D7008449C7 /* ImagePickerTestImages.m */; }; + 33473400266813FD00DCC49E /* ImagePickerPluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68B9AF71243E4B3F00927CE4 /* ImagePickerPluginTests.m */; }; + 3A72BAD3FAE6E0FA9D80826B /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 35AE65F25E0B8C8214D8372B /* libPods-RunnerTests.a */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 5C9513011EC38BD300040975 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C9513001EC38BD300040975 /* GeneratedPluginRegistrant.m */; }; + 680049382280F2B9006DD6AB /* pngImage.png in Resources */ = {isa = PBXBuildFile; fileRef = 680049352280F2B8006DD6AB /* pngImage.png */; }; + 680049392280F2B9006DD6AB /* jpgImage.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 680049362280F2B8006DD6AB /* jpgImage.jpg */; }; + 6801C8392555D726009DAF8D /* ImagePickerFromGalleryUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6801C8382555D726009DAF8D /* ImagePickerFromGalleryUITests.m */; }; + 86430DF9272D71E9002D9D6C /* gifImage.gif in Resources */ = {isa = PBXBuildFile; fileRef = 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */; }; + 86E9A893272754860017E6E0 /* PickerSaveImageToPathOperationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 86E9A892272754860017E6E0 /* PickerSaveImageToPathOperationTests.m */; }; + 86E9A894272754A30017E6E0 /* webpImage.webp in Resources */ = {isa = PBXBuildFile; fileRef = 86E9A88F272747B90017E6E0 /* webpImage.webp */; }; + 86E9A895272769130017E6E0 /* pngImage.png in Resources */ = {isa = PBXBuildFile; fileRef = 680049352280F2B8006DD6AB /* pngImage.png */; }; + 86E9A896272769150017E6E0 /* jpgImage.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 680049362280F2B8006DD6AB /* jpgImage.jpg */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + 9FC8F0E9229FA49E00C8D58F /* gifImage.gif in Resources */ = {isa = PBXBuildFile; fileRef = 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */; }; + 9FC8F0EC229FA68500C8D58F /* gifImage.gif in Resources */ = {isa = PBXBuildFile; fileRef = 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */; }; + BE6173D826A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = BE6173D726A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m */; }; + F4F7A436CCA4BF276270A3AE /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EC32F6993F4529982D9519F1 /* libPods-Runner.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 334733F72668136400DCC49E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; + 6801C83B2555D726009DAF8D /* 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 */ + 0C7B151765FD4249454C49AD /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 334733F22668136400DCC49E /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 334733F62668136400DCC49E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 35AE65F25E0B8C8214D8372B /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 5A9D31B91557877A0E8EF3E7 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 5C9512FF1EC38BD300040975 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 5C9513001EC38BD300040975 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 680049252280D736006DD6AB /* MetaDataUtilTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MetaDataUtilTests.m; sourceTree = ""; }; + 680049352280F2B8006DD6AB /* pngImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = pngImage.png; sourceTree = ""; }; + 680049362280F2B8006DD6AB /* jpgImage.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = jpgImage.jpg; sourceTree = ""; }; + 6801632E632668F4349764C9 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 6801C8362555D726009DAF8D /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 6801C8382555D726009DAF8D /* ImagePickerFromGalleryUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImagePickerFromGalleryUITests.m; sourceTree = ""; }; + 6801C83A2555D726009DAF8D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 68B9AF71243E4B3F00927CE4 /* ImagePickerPluginTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImagePickerPluginTests.m; sourceTree = ""; }; + 68F4B463228B3AB500C25614 /* PhotoAssetUtilTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PhotoAssetUtilTests.m; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 86E9A88F272747B90017E6E0 /* webpImage.webp */ = {isa = PBXFileReference; lastKnownFileType = file; path = webpImage.webp; sourceTree = ""; }; + 86E9A892272754860017E6E0 /* PickerSaveImageToPathOperationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PickerSaveImageToPathOperationTests.m; 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; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 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 = ""; }; + 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = gifImage.gif; sourceTree = ""; }; + 9FC8F0ED229FB90B00C8D58F /* ImageUtilTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImageUtilTests.m; sourceTree = ""; }; + BE6173D726A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImagePickerFromLimitedGalleryUITests.m; sourceTree = ""; }; + DC6FCAAD4E7580C9B3C2E21D /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + EC32F6993F4529982D9519F1 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + F78AF3172342D9D7008449C7 /* ImagePickerTestImages.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImagePickerTestImages.h; sourceTree = ""; }; + F78AF3182342D9D7008449C7 /* ImagePickerTestImages.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ImagePickerTestImages.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 334733EF2668136400DCC49E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3A72BAD3FAE6E0FA9D80826B /* libPods-RunnerTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6801C8332555D726009DAF8D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F4F7A436CCA4BF276270A3AE /* libPods-Runner.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 334733F32668136400DCC49E /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 9FC8F0ED229FB90B00C8D58F /* ImageUtilTests.m */, + 680049252280D736006DD6AB /* MetaDataUtilTests.m */, + 68F4B463228B3AB500C25614 /* PhotoAssetUtilTests.m */, + F78AF3172342D9D7008449C7 /* ImagePickerTestImages.h */, + F78AF3182342D9D7008449C7 /* ImagePickerTestImages.m */, + 68B9AF71243E4B3F00927CE4 /* ImagePickerPluginTests.m */, + 86E9A892272754860017E6E0 /* PickerSaveImageToPathOperationTests.m */, + 334733F62668136400DCC49E /* Info.plist */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 680049282280E33D006DD6AB /* TestImages */ = { + isa = PBXGroup; + children = ( + 86E9A88F272747B90017E6E0 /* webpImage.webp */, + 9FC8F0E8229FA49E00C8D58F /* gifImage.gif */, + 680049362280F2B8006DD6AB /* jpgImage.jpg */, + 680049352280F2B8006DD6AB /* pngImage.png */, + ); + path = TestImages; + sourceTree = ""; + }; + 6801C8372555D726009DAF8D /* RunnerUITests */ = { + isa = PBXGroup; + children = ( + BE6173D726A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m */, + 6801C8382555D726009DAF8D /* ImagePickerFromGalleryUITests.m */, + 6801C83A2555D726009DAF8D /* Info.plist */, + ); + path = RunnerUITests; + sourceTree = ""; + }; + 840012C8B5EDBCF56B0E4AC1 /* Pods */ = { + isa = PBXGroup; + children = ( + 6801632E632668F4349764C9 /* Pods-Runner.debug.xcconfig */, + 5A9D31B91557877A0E8EF3E7 /* Pods-Runner.release.xcconfig */, + DC6FCAAD4E7580C9B3C2E21D /* Pods-RunnerTests.debug.xcconfig */, + 0C7B151765FD4249454C49AD /* Pods-RunnerTests.release.xcconfig */, + ); + name = Pods; + 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 = ( + 680049282280E33D006DD6AB /* TestImages */, + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 334733F32668136400DCC49E /* RunnerTests */, + 6801C8372555D726009DAF8D /* RunnerUITests */, + 97C146EF1CF9000F007C117D /* Products */, + 840012C8B5EDBCF56B0E4AC1 /* Pods */, + CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 6801C8362555D726009DAF8D /* RunnerUITests.xctest */, + 334733F22668136400DCC49E /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 5C9512FF1EC38BD300040975 /* GeneratedPluginRegistrant.h */, + 5C9513001EC38BD300040975 /* GeneratedPluginRegistrant.m */, + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + CF3B75C9A7D2FA2A4C99F110 /* Frameworks */ = { + isa = PBXGroup; + children = ( + EC32F6993F4529982D9519F1 /* libPods-Runner.a */, + 35AE65F25E0B8C8214D8372B /* libPods-RunnerTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 334733F12668136400DCC49E /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 334733F92668136400DCC49E /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + B8739A4353234497CF76B597 /* [CP] Check Pods Manifest.lock */, + 334733EE2668136400DCC49E /* Sources */, + 334733EF2668136400DCC49E /* Frameworks */, + 334733F02668136400DCC49E /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 334733F82668136400DCC49E /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 334733F22668136400DCC49E /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 6801C8352555D726009DAF8D /* RunnerUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6801C83F2555D726009DAF8D /* Build configuration list for PBXNativeTarget "RunnerUITests" */; + buildPhases = ( + 6801C8322555D726009DAF8D /* Sources */, + 6801C8332555D726009DAF8D /* Frameworks */, + 6801C8342555D726009DAF8D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 6801C83C2555D726009DAF8D /* PBXTargetDependency */, + ); + name = RunnerUITests; + productName = RunnerUITests; + productReference = 6801C8362555D726009DAF8D /* RunnerUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + AB1344B0443C71CD721E1BB7 /* [CP] Check Pods Manifest.lock */, + 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 = { + DefaultBuildSystemTypeForWorkspace = Original; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = "The Flutter Authors"; + TargetAttributes = { + 334733F12668136400DCC49E = { + CreatedOnToolsVersion = 12.5; + ProvisioningStyle = Automatic; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 6801C8352555D726009DAF8D = { + CreatedOnToolsVersion = 11.7; + ProvisioningStyle = Automatic; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + SystemCapabilities = { + com.apple.BackgroundModes = { + enabled = 1; + }; + }; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 334733F12668136400DCC49E /* RunnerTests */, + 6801C8352555D726009DAF8D /* RunnerUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 334733F02668136400DCC49E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 86430DF9272D71E9002D9D6C /* gifImage.gif in Resources */, + 86E9A894272754A30017E6E0 /* webpImage.webp in Resources */, + 86E9A895272769130017E6E0 /* pngImage.png in Resources */, + 86E9A896272769150017E6E0 /* jpgImage.jpg in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6801C8342555D726009DAF8D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9FC8F0EC229FA68500C8D58F /* gifImage.gif in Resources */, + 680049382280F2B9006DD6AB /* pngImage.png in Resources */, + 680049392280F2B9006DD6AB /* jpgImage.jpg in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 9FC8F0E9229FA49E00C8D58F /* gifImage.gif in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + 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; + 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"; + }; + AB1344B0443C71CD721E1BB7 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + B8739A4353234497CF76B597 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 334733EE2668136400DCC49E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 334733FD266813F100DCC49E /* MetaDataUtilTests.m in Sources */, + 334733FF266813FA00DCC49E /* ImagePickerTestImages.m in Sources */, + 86E9A893272754860017E6E0 /* PickerSaveImageToPathOperationTests.m in Sources */, + 334733FC266813EE00DCC49E /* ImageUtilTests.m in Sources */, + 33473400266813FD00DCC49E /* ImagePickerPluginTests.m in Sources */, + 334733FE266813F400DCC49E /* PhotoAssetUtilTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6801C8322555D726009DAF8D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6801C8392555D726009DAF8D /* ImagePickerFromGalleryUITests.m in Sources */, + BE6173D826A958B800D0974D /* ImagePickerFromLimitedGalleryUITests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 5C9513011EC38BD300040975 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 334733F82668136400DCC49E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 334733F72668136400DCC49E /* PBXContainerItemProxy */; + }; + 6801C83C2555D726009DAF8D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 6801C83B2555D726009DAF8D /* 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 */ + 334733FA2668136400DCC49E /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DC6FCAAD4E7580C9B3C2E21D /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Debug; + }; + 334733FB2668136400DCC49E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0C7B151765FD4249454C49AD /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Release; + }; + 6801C83D2555D726009DAF8D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = RunnerUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = Runner; + }; + name = Debug; + }; + 6801C83E2555D726009DAF8D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = RunnerUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = Runner; + }; + name = Release; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = 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; + 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 = 9.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; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = 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; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.imagePickerExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.imagePickerExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 334733F92668136400DCC49E /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 334733FA2668136400DCC49E /* Debug */, + 334733FB2668136400DCC49E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6801C83F2555D726009DAF8D /* Build configuration list for PBXNativeTarget "RunnerUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6801C83D2555D726009DAF8D /* Debug */, + 6801C83E2555D726009DAF8D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100755 index 000000000000..919434a6254f --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100755 index 000000000000..9b24f28c25cc --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/RunnerUITests.xcscheme b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/RunnerUITests.xcscheme new file mode 100644 index 000000000000..1a97d9638346 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/RunnerUITests.xcscheme @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/image_picker/image_picker_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100755 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/image_picker/image_picker_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/.gitignore b/packages/image_picker/image_picker_ios/example/ios/Runner/.gitignore new file mode 100755 index 000000000000..0cab08d0bdd7 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner/.gitignore @@ -0,0 +1,2 @@ +GeneratedPluginRegistrant.h +GeneratedPluginRegistrant.m diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/AppDelegate.h b/packages/image_picker/image_picker_ios/example/ios/Runner/AppDelegate.h new file mode 100644 index 000000000000..0681d288bb70 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner/AppDelegate.h @@ -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 +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/AppDelegate.m b/packages/image_picker/image_picker_ios/example/ios/Runner/AppDelegate.m new file mode 100644 index 000000000000..b790a0a52635 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner/AppDelegate.m @@ -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. + +#include "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100755 index 000000000000..d225b3c2cfe2 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,121 @@ +{ + "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" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100755 index 0000000000000000000000000000000000000000..28c6bf03016f6c994b70f38d1b7346e5831b531f GIT binary patch literal 564 zcmV-40?Yl0P)Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV7ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100755 index 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 zcmds$`#;kQ7{|XelZftyR5~xW7?MLxS4^|Hw3&P7^y)@A9Fj{Xm1~_CIV^XZ%SLBn zA;!r`GqGHg=7>xrB{?psZQs88ZaedDoagm^KF{a*>G|dJWRSe^I$DNW008I^+;Kjt z>9p3GNR^I;v>5_`+91i(*G;u5|L+Bu6M=(afLjtkya#yZ175|z$pU~>2#^Z_pCZ7o z1c6UNcv2B3?; zX%qdxCXQpdKRz=#b*q0P%b&o)5ZrNZt7$fiETSK_VaY=mb4GK`#~0K#~9^ zcY!`#Af+4h?UMR-gMKOmpuYeN5P*RKF!(tb`)oe0j2BH1l?=>y#S5pMqkx6i{*=V9JF%>N8`ewGhRE(|WohnD59R^$_36{4>S zDFlPC5|k?;SPsDo87!B{6*7eqmMdU|QZ84>6)Kd9wNfh90=y=TFQay-0__>=<4pk& zYDjgIhL-jQ9o>z32K)BgAH+HxamL{ZL~ozu)Qqe@a`FpH=oQRA8=L-m-1dam(Ix2V z?du;LdMO+ooBelr^_y4{|44tmgH^2hSzPFd;U^!1p>6d|o)(-01z{i&Kj@)z-yfWQ)V#3Uo!_U}q3u`(fOs`_f^ueFii1xBNUB z6MecwJN$CqV&vhc+)b(p4NzGGEgwWNs z@*lUV6LaduZH)4_g!cE<2G6#+hJrWd5(|p1Z;YJ7ifVHv+n49btR}dq?HHDjl{m$T z!jLZcGkb&XS2OG~u%&R$(X+Z`CWec%QKt>NGYvd5g20)PU(dOn^7%@6kQb}C(%=vr z{?RP(z~C9DPnL{q^@pVw@|Vx~@3v!9dCaBtbh2EdtoNHm4kGxp>i#ct)7p|$QJs+U z-a3qtcPvhihub?wnJqEt>zC@)2suY?%-96cYCm$Q8R%-8$PZYsx3~QOLMDf(piXMm zB=<63yQk1AdOz#-qsEDX>>c)EES%$owHKue;?B3)8aRd}m~_)>SL3h2(9X;|+2#7X z+#2)NpD%qJvCQ0a-uzZLmz*ms+l*N}w)3LRQ*6>|Ub-fyptY(keUxw+)jfwF5K{L9 z|Cl_w=`!l_o><384d&?)$6Nh(GAm=4p_;{qVn#hI8lqewW7~wUlyBM-4Z|)cZr?Rh z=xZ&Ol>4(CU85ea(CZ^aO@2N18K>ftl8>2MqetAR53_JA>Fal`^)1Y--Am~UDa4th zKfCYpcXky$XSFDWBMIl(q=Mxj$iMBX=|j9P)^fDmF(5(5$|?Cx}DKEJa&XZP%OyE`*GvvYQ4PV&!g2|L^Q z?YG}tx;sY@GzMmsY`7r$P+F_YLz)(e}% zyakqFB<6|x9R#TdoP{R$>o7y(-`$$p0NxJ6?2B8tH)4^yF(WhqGZlM3=9Ibs$%U1w zWzcss*_c0=v_+^bfb`kBFsI`d;ElwiU%frgRB%qBjn@!0U2zZehBn|{%uNIKBA7n= zzE`nnwTP85{g;8AkYxA68>#muXa!G>xH22D1I*SiD~7C?7Za+9y7j1SHiuSkKK*^O zsZ==KO(Ua#?YUpXl{ViynyT#Hzk=}5X$e04O@fsMQjb}EMuPWFO0e&8(2N(29$@Vd zn1h8Yd>6z(*p^E{c(L0Lg=wVdupg!z@WG;E0k|4a%s7Up5C0c)55XVK*|x9RQeZ1J@1v9MX;>n34(i>=YE@Iur`0Vah(inE3VUFZNqf~tSz{1fz3Fsn_x4F>o(Yo;kpqvBe-sbwH(*Y zu$JOl0b83zu$JMvy<#oH^Wl>aWL*?aDwnS0iEAwC?DK@aT)GHRLhnz2WCvf3Ba;o=aY7 z2{Asu5MEjGOY4O#Ggz@@J;q*0`kd2n8I3BeNuMmYZf{}pg=jTdTCrIIYuW~luKecn z+E-pHY%ohj@uS0%^ z&(OxwPFPD$+#~`H?fMvi9geVLci(`K?Kj|w{rZ9JgthFHV+=6vMbK~0)Ea<&WY-NC zy-PnZft_k2tfeQ*SuC=nUj4H%SQ&Y$gbH4#2sT0cU0SdFs=*W*4hKGpuR1{)mV;Qf5pw4? zfiQgy0w3fC*w&Bj#{&=7033qFR*<*61B4f9K%CQvxEn&bsWJ{&winp;FP!KBj=(P6 z4Z_n4L7cS;ao2)ax?Tm|I1pH|uLpDSRVghkA_UtFFuZ0b2#>!8;>-_0ELjQSD-DRd z4im;599VHDZYtnWZGAB25W-e(2VrzEh|etsv2YoP#VbIZ{aFkwPrzJ#JvCvA*mXS& z`}Q^v9(W4GiSs}#s7BaN!WA2bniM$0J(#;MR>uIJ^uvgD3GS^%*ikdW6-!VFUU?JV zZc2)4cMsX@j z5HQ^e3BUzOdm}yC-xA%SY``k$rbfk z;CHqifhU*jfGM@DkYCecD9vl*qr58l6x<8URB=&%{!Cu3RO*MrKZ4VO}V6R0a zZw3Eg^0iKWM1dcTYZ0>N899=r6?+adUiBKPciJw}L$=1f4cs^bio&cr9baLF>6#BM z(F}EXe-`F=f_@`A7+Q&|QaZ??Txp_dB#lg!NH=t3$G8&06MFhwR=Iu*Im0s_b2B@| znW>X}sy~m#EW)&6E&!*0%}8UAS)wjt+A(io#wGI@Z2S+Ms1Cxl%YVE800007ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100755 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100755 index 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 zcmVjJBgitF5mAp-i>4+KS_oR{|13AP->1TD4=w)g|)JHOx|a2Wk1Va z!k)vP$UcQ#mdj%wNQoaJ!w>jv_6&JPyutpQps?s5dmDQ>`%?Bvj>o<%kYG!YW6H-z zu`g$@mp`;qDR!51QaS}|ZToSuAGcJ7$2HF0z`ln4t!#Yg46>;vGG9N9{V@9z#}6v* zfP?}r6b{*-C*)(S>NECI_E~{QYzN5SXRmVnP<=gzP+_Sp(Aza_hKlZ{C1D&l*(7IKXxQC1Z9#6wx}YrGcn~g%;icdw>T0Rf^w0{ z$_wn1J+C0@!jCV<%Go5LA45e{5gY9PvZp8uM$=1}XDI+9m7!A95L>q>>oe0$nC->i zeexUIvq%Uk<-$>DiDb?!In)lAmtuMWxvWlk`2>4lNuhSsjAf2*2tjT`y;@d}($o)S zn(+W&hJ1p0xy@oxP%AM15->wPLp{H!k)BdBD$toBpJh+crWdsNV)qsHaqLg2_s|Ih z`8E9z{E3sA!}5aKu?T!#enD(wLw?IT?k-yWVHZ8Akz4k5(TZJN^zZgm&zM28sfTD2BYJ|Fde3Xzh;;S` z=GXTnY4Xc)8nYoz6&vF;P7{xRF-{|2Xs5>a5)@BrnQ}I(_x7Cgpx#5&Td^4Q9_FnQ zX5so*;#8-J8#c$OlA&JyPp$LKUhC~-e~Ij!L%uSMu!-VZG7Hx-L{m2DVR2i=GR(_% zCVD!4N`I)&Q5S`?P&fQZ=4#Dgt_v2-DzkT}K(9gF0L(owe-Id$Rc2qZVLqI_M_DyO z9@LC#U28_LU{;wGZ&))}0R2P4MhajKCd^K#D+JJ&JIXZ_p#@+7J9A&P<0kdRujtQ_ zOy>3=C$kgi6$0pW06KaLz!21oOryKM3ZUOWqppndxfH}QpgjEJ`j7Tzn5bk6K&@RA?vl##y z$?V~1E(!wB5rH`>3nc&@)|#<1dN2cMzzm=PGhQ|Yppne(C-Vlt450IXc`J4R0W@I7 zd1e5uW6juvO%ni(WX7BsKx3MLngO7rHO;^R5I~0^nE^9^E_eYLgiR9&KnJ)pBbfno zSVnW$0R+&6jOOsZ82}nJ126+c|%svPo;TeUku<2G7%?$oft zyaO;tVo}(W)VsTUhq^XmFi#2z%-W9a{7mXn{uzivYQ_d6b7VJG{77naW(vHt-uhnY zVN#d!JTqVh(7r-lhtXVU6o})aZbDt_;&wJVGl2FKYFBFpU-#9U)z#(A%=IVnqytR$SY-sO( z($oNE09{D^@OuYPz&w~?9>Fl5`g9u&ecFGhqX=^#fmR=we0CJw+5xna*@oHnkahk+ z9aWeE3v|An+O5%?4fA&$Fgu~H_YmqR!yIU!bFCk4!#pAj%(lI(A5n)n@Id#M)O9Yx zJU9oKy{sRAIV3=5>(s8n{8ryJ!;ho}%pn6hZKTKbqk=&m=f*UnK$zW3YQP*)pw$O* zIfLA^!-bmBl6%d_n$#tP8Zd_(XdA*z*WH|E_yILwjtI~;jK#v-6jMl^?<%Y%`gvpwv&cFb$||^v4D&V=aNy?NGo620jL3VZnA%s zH~I|qPzB~e(;p;b^gJr7Ure#7?8%F0m4vzzPy^^(q4q1OdthF}Fi*RmVZN1OwTsAP zn9CZP`FazX3^kG(KodIZ=Kty8DLTy--UKfa1$6XugS zk%6v$Kmxt6U!YMx0JQ)0qX*{CXwZZk$vEROidEc7=J-1;peNat!vS<3P-FT5po>iE z!l3R+<`#x|+_hw!HjQGV=8!q|76y8L7N8gP3$%0kfush|u0uU^?dKBaeRSBUpOZ0c z62;D&Mdn2}N}xHRFTRI?zRv=>=AjHgH}`2k4WK=#AHB)UFrR-J87GgX*x5fL^W2#d z=(%K8-oZfMO=i{aWRDg=FX}UubM4eotRDcn;OR#{3q=*?3mE3_oJ-~prjhxh%PgQT zyn)Qozaq0@o&|LEgS{Ind4Swsr;b`u185hZPOBLL<`d2%^Yp1?oL)=jnLi;Zo0ZDliTtQ^b5SmfIMe{T==zZkbvn$KTQGlbG8w}s@M3TZnde;1Am46P3juKb zl9GU&3F=q`>j!`?SyH#r@O59%@aMX^rx}Nxe<>NqpUp5=lX1ojGDIR*-D^SDuvCKF z?3$xG(gVUsBERef_YjPFl^rU9EtD{pt z0CXwpN7BN3!8>hajGaTVk-wl=9rxmfWtIhC{mheHgStLi^+Nz12a?4r(fz)?3A%at zMlvQmL<2-R)-@G1wJ0^zQK%mR=r4d{Y3fHp){nWXUL#|CqXl(+v+qDh>FkF9`eWrW zfr^D%LNfOcTNvtx0JXR35J0~Jpi2#P3Q&80w+nqNfc}&G0A~*)lGHKv=^FE+b(37|)zL;KLF>oiGfb(?&1 zV3XRu!Sw>@quKiab%g6jun#oZ%!>V#A%+lNc?q>6+VvyAn=kf_6z^(TZUa4Eelh{{ zqFX-#dY(EV@7l$NE&kv9u9BR8&Ojd#ZGJ6l8_BW}^r?DIS_rU2(XaGOK z225E@kH5Opf+CgD^{y29jD4gHbGf{1MD6ggQ&%>UG4WyPh5q_tb`{@_34B?xfSO*| zZv8!)q;^o-bz`MuxXk*G^}(6)ACb@=Lfs`Hxoh>`Y0NE8QRQ!*p|SH@{r8=%RKd4p z+#Ty^-0kb=-H-O`nAA3_6>2z(D=~Tbs(n8LHxD0`R0_ATFqp-SdY3(bZ3;VUM?J=O zKCNsxsgt@|&nKMC=*+ZqmLHhX1KHbAJs{nGVMs6~TiF%Q)P@>!koa$%oS zjXa=!5>P`vC-a}ln!uH1ooeI&v?=?v7?1n~P(wZ~0>xWxd_Aw;+}9#eULM7M8&E?Y zC-ZLhi3RoM92SXUb-5i-Lmt5_rfjE{6y^+24`y$1lywLyHO!)Boa7438K4#iLe?rh z2O~YGSgFUBH?og*6=r9rme=peP~ah`(8Zt7V)j5!V0KPFf_mebo3z95U8(up$-+EA^9dTRLq>Yl)YMBuch9%=e5B`Vnb>o zt03=kq;k2TgGe4|lGne&zJa~h(UGutjP_zr?a7~#b)@15XNA>Dj(m=gg2Q5V4-$)D|Q9}R#002ovPDHLkV1o7DH3k3x literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/image_picker/image_picker_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100755 index 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 zcmV-m2cP(fP)x~L`~4d)Rspd&<9kFh{hn*KP1LP0~$;u(LfAu zp%fx&qLBcRHx$G|3q(bv@+b;o0*D|jwD-Q9uQR(l*ST}s+uPgQ-MeFwZ#GS?b332? z&Tk$&_miXn3IGq)AmQ)3sisq{raD4(k*bHvpCe-TdWq^NRTEVM)i9xbgQ&ccnUVx* zEY%vS%gDcSg=!tuIK8$Th2_((_h^+7;R|G{n06&O2#6%LK`a}n?h_fL18btz<@lFG za}xS}u?#DBMB> zw^b($1Z)`9G?eP95EKi&$eOy@K%h;ryrR3la%;>|o*>CgB(s>dDcNOXg}CK9SPmD? zmr-s{0wRmxUnbDrYfRvnZ@d z6johZ2sMX{YkGSKWd}m|@V7`Degt-43=2M?+jR%8{(H$&MLLmS;-|JxnX2pnz;el1jsvqQz}pGSF<`mqEXRQ5sC4#BbwnB_4` zc5bFE-Gb#JV3tox9fp-vVEN{(tOCpRse`S+@)?%pz+zVJXSooTrNCUg`R6`hxwb{) zC@{O6MKY8tfZ5@!yy=p5Y|#+myRL=^{tc(6YgAnkg3I(Cd!r5l;|;l-MQ8B`;*SCE z{u)uP^C$lOPM z5d~UhKhRRmvv{LIa^|oavk1$QiEApSrP@~Jjbg`<*dW4TO?4qG%a%sTPUFz(QtW5( zM)lA+5)0TvH~aBaOAs|}?u2FO;yc-CZ1gNM1dAxJ?%m?YsGR`}-xk2*dxC}r5j$d* zE!#Vtbo69h>V4V`BL%_&$} z+oJAo@jQ^Tk`;%xw-4G>hhb&)B?##U+(6Fi7nno`C<|#PVA%$Y{}N-?(Gc$1%tr4Pc}}hm~yY#fTOe!@v9s-ik$dX~|ygArPhByaXn8 zpI^FUjNWMsTFKTP3X7m?UK)3m zp6rI^_zxRYrx6_QmhoWoDR`fp4R7gu6;gdO)!KexaoO2D88F9x#TM1(9Bn7g;|?|o z)~$n&Lh#hCP6_LOPD>a)NmhW})LADx2kq=X7}7wYRj-0?dXr&bHaRWCfSqvzFa=sn z-8^gSyn-RmH=BZ{AJZ~!8n5621GbUJV7Qvs%JNv&$%Q17s_X%s-41vAPfIR>;x0Wlqr5?09S>x#%Qkt>?(&XjFRY}*L6BeQ3 z<6XEBh^S7>AbwGm@XP{RkeEKj6@_o%oV?hDuUpUJ+r#JZO?!IUc;r0R?>mi)*ZpQ) z#((dn=A#i_&EQn|hd)N$#A*fjBFuiHcYvo?@y1 z5|fV=a^a~d!c-%ZbMNqkMKiSzM{Yq=7_c&1H!mXk60Uv32dV;vMg&-kQ)Q{+PFtwc zj|-uQ;b^gts??J*9VxxOro}W~Q9j4Em|zSRv)(WSO9$F$s=Ydu%Q+5DOid~lwk&we zY%W(Z@ofdwPHncEZzZgmqS|!gTj3wQq9rxQy+^eNYKr1mj&?tm@wkO*9@UtnRMG>c aR{jt9+;fr}hV%pg00001^@s67{VYS000c7NklQEG_j zup^)eW&WUIApqy$=APz8jE@awGp)!bsTjDbrJO`$x^ZR^dr;>)LW>{ zs70vpsD38v)19rI=GNk1b(0?Js9~rjsQsu*K;@SD40RB-3^gKU-MYC7G!Bw{fZsqp zih4iIi;Hr_xZ033Iu{sQxLS=}yBXgLMn40d++>aQ0#%8D1EbGZp7+ z5=mK?t31BkVYbGOxE9`i748x`YgCMwL$qMsChbSGSE1`p{nSmadR zcQ#R)(?!~dmtD0+D2!K zR9%!Xp1oOJzm(vbLvT^$IKp@+W2=-}qTzTgVtQ!#Y7Gxz}stUIm<1;oBQ^Sh2X{F4ibaOOx;5ZGSNK z0maF^@(UtV$=p6DXLgRURwF95C=|U8?osGhgOED*b z7woJ_PWXBD>V-NjQAm{~T%sjyJ{5tn2f{G%?J!KRSrrGvQ1(^`YLA5B!~eycY(e5_ z*%aa{at13SxC(=7JT7$IQF~R3sy`Nn%EMv!$-8ZEAryB*yB1k&stni)=)8-ODo41g zkJu~roIgAih94tb=YsL%iH5@^b~kU9M-=aqgXIrbtxMpFy5mekFm#edF9z7RQ6V}R zBIhbXs~pMzt0VWy1Fi$^fh+1xxLDoK09&5&MJl(q#THjPm(0=z2H2Yfm^a&E)V+a5 zbi>08u;bJsDRUKR9(INSc7XyuWv(JsD+BB*0hS)FO&l&7MdViuur@-<-EHw>kHRGY zqoT}3fDv2-m{NhBG8X}+rgOEZ;amh*DqN?jEfQdqxdj08`Sr=C-KmT)qU1 z+9Cl)a1mgXxhQiHVB}l`m;-RpmKy?0*|yl?FXvJkFxuu!fKlcmz$kN(a}i*saM3nr z0!;a~_%Xqy24IxA2rz<+08=B-Q|2PT)O4;EaxP^6qixOv7-cRh?*T?zZU`{nIM-at zTKYWr9rJ=tppQ9I#Z#mLgINVB!pO-^FOcvFw6NhV0gztuO?g ztoA*C-52Q-Z-P#xB4HAY3KQVd%dz1S4PA3vHp0aa=zAO?FCt zC_GaTyVBg2F!bBr3U@Zy2iJgIAt>1sf$JWA9kh{;L+P*HfUBX1Zy{4MgNbDfBV_ly z!y#+753arsZUt@366jIC0klaC@ckuk!qu=pAyf7&QmiBUT^L1&tOHzsK)4n|pmrVT zs2($4=?s~VejTFHbFdDOwG;_58LkIj1Fh@{glkO#F1>a==ymJS$z;gdedT1zPx4Kj ztjS`y_C}%af-RtpehdQDt3a<=W5C4$)9W@QAse;WUry$WYmr51ml9lkeunUrE`-3e zmq1SgSOPNEE-Mf+AGJ$g0M;3@w!$Ej;hMh=v=I+Lpz^n%Pg^MgwyqOkNyu2c^of)C z1~ALor3}}+RiF*K4+4{(1%1j3pif1>sv0r^mTZ?5Jd-It!tfPfiG_p$AY*Vfak%FG z4z#;wLtw&E&?}w+eKG^=#jF7HQzr8rV0mY<1YAJ_uGz~$E13p?F^fPSzXSn$8UcI$ z8er9{5w5iv0qf8%70zV71T1IBB1N}R5Kp%NO0=5wJalZt8;xYp;b{1K) zHY>2wW-`Sl{=NpR%iu3(u6l&)rc%%cSA#aV7WCowfbFR4wcc{LQZv~o1u_`}EJA3>ki`?9CKYTA!rhO)if*zRdd}Kn zEPfYbhoVE~!FI_2YbC5qAj1kq;xP6%J8+?2PAs?`V3}nyFVD#sV3+uP`pi}{$l9U^ zSz}_M9f7RgnnRhaoIJgT8us!1aB&4!*vYF07Hp&}L zCRlop0oK4DL@ISz{2_BPlezc;xj2|I z23RlDNpi9LgTG_#(w%cMaS)%N`e>~1&a3<{Xy}>?WbF>OOLuO+j&hc^YohQ$4F&ze z+hwnro1puQjnKm;vFG~o>`kCeUIlkA-2tI?WBKCFLMBY=J{hpSsQ=PDtU$=duS_hq zHpymHt^uuV1q@uc4bFb{MdG*|VoW@15Osrqt2@8ll0qO=j*uOXn{M0UJX#SUztui9FN4)K3{9!y8PC-AHHvpVTU;x|-7P+taAtyglk#rjlH2 z5Gq8ik}BPaGiM{#Woyg;*&N9R2{J0V+WGB69cEtH7F?U~Kbi6ksi*`CFXsi931q7Y zGO82?whBhN%w1iDetv%~wM*Y;E^)@Vl?VDj-f*RX>{;o_=$fU!&KAXbuadYZ46Zbg z&6jMF=49$uL^73y;;N5jaHYv)BTyfh&`qVLYn?`o6BCA_z-0niZz=qPG!vonK3MW_ zo$V96zM!+kJRs{P-5-rQVse0VBH*n6A58)4uc&gfHMa{gIhV2fGf{st>E8sKyP-$8zp~wJX^A*@DI&-;8>gANXZj zU)R+Y)PB?=)a|Kj>8NXEu^S_h^7R`~Q&7*Kn!xyvzVv&^>?^iu;S~R2e-2fJx-oUb cX)(b1KSk$MOV07*qoM6N<$f&6$jw%VRuvdN2+38CZWny1cRtlsl+0_KtW)EU14Ei(F!UtWuj4IK+3{sK@>rh zs1Z;=(DD&U6+tlyL?UnHVN^&g6QhFi2#HS+*qz;(>63G(`|jRtW|nz$Pv7qTovP!^ zP_jES{mr@O-02w%!^a?^1ZP!_KmQiz0L~jZ=W@Qt`8wzOoclQsAS<5YdH;a(4bGLE zk8s}1If(PSIgVi!XE!5kA?~z*sobvNyohr;=Q_@h2@$6Flyej3J)D-6YfheRGl`HEcPk|~huT_2-U?PfL=4BPV)f1o!%rQ!NMt_MYw-5bUSwQ9Z&zC>u zOrl~UJglJNa%f50Ok}?WB{on`Ci`p^Y!xBA?m@rcJXLxtrE0FhRF3d*ir>yzO|BD$ z3V}HpFcCh6bTzY}Nt_(W%QYd3NG)jJ4<`F<1Od) zfQblTdC&h2lCz`>y?>|9o2CdvC8qZeIZt%jN;B7Hdn2l*k4M4MFEtq`q_#5?}c$b$pf_3y{Y!cRDafZBEj-*OD|gz#PBDeu3QoueOesLzB+O zxjf2wvf6Wwz>@AiOo2mO4=TkAV+g~%_n&R;)l#!cBxjuoD$aS-`IIJv7cdX%2{WT7 zOm%5rs(wqyPE^k5SIpUZ!&Lq4<~%{*>_Hu$2|~Xa;iX*tz8~G6O3uFOS?+)tWtdi| zV2b#;zRN!m@H&jd=!$7YY6_}|=!IU@=SjvGDFtL;aCtw06U;-v^0%k0FOyESt z1Wv$={b_H&8FiRV?MrzoHWd>%v6KTRU;-v^Miiz+@q`(BoT!+<37CKhoKb)|8!+RG z6BQFU^@fRW;s8!mOf2QViKQGk0TVER6EG1`#;Nm39Do^PoT!+<37AD!%oJe86(=et zZ~|sLzU>V-qYiU6V8$0GmU7_K8|Fd0B?+9Un1BhKAz#V~Fk^`mJtlCX#{^8^M8!me z8Yg;8-~>!e<-iG;h*0B1kBKm}hItVGY6WnjVpgnTTAC$rqQ^v)4KvOtpY|sIj@WYg zyw##ZZ5AC2IKNC;^hwg9BPk0wLStlmBr;E|$5GoAo$&Ui_;S9WY62n3)i49|T%C#i017z3J=$RF|KyZWnci*@lW4 z=AKhNN6+m`Q!V3Ye68|8y@%=am>YD0nG99M)NWc20%)gwO!96j7muR}Fr&54SxKP2 zP30S~lt=a*qDlbu3+Av57=9v&vr<6g0&`!8E2fq>I|EJGKs}t|{h7+KT@)LfIV-3K zK)r_fr2?}FFyn*MYoLC>oV-J~eavL2ho4a4^r{E-8m2hi>~hA?_vIG4a*KT;2eyl1 zh_hUvUJpNCFwBvRq5BI*srSle>c6%n`#VNsyC|MGa{(P&08p=C9+WUw9Hl<1o9T4M zdD=_C0F7#o8A_bRR?sFNmU0R6tW`ElnF8p53IdHo#S9(JoZCz}fHwJ6F<&?qrpVqE zte|m%89JQD+XwaPU#%#lVs-@-OL);|MdfINd6!XwP2h(eyafTUsoRkA%&@fe?9m@jw-v(yTTiV2(*fthQH9}SqmsRPVnwwbV$1E(_lkmo&S zF-truCU914_$jpqjr(>Ha4HkM4YMT>m~NosUu&UZ>zirfHo%N6PPs9^_o$WqPA0#5 z%tG>qFCL+b*0s?sZ;Sht0nE7Kl>OVXy=gjWxxK;OJ3yGd7-pZf7JYNcZo2*1SF`u6 zHJyRRxGw9mDlOiXqVMsNe#WX`fC`vrtjSQ%KmLcl(lC>ZOQzG^%iql2w-f_K@r?OE zwCICifM#L-HJyc7Gm>Ern?+Sk3&|Khmu4(~3qa$(m6Ub^U0E5RHq49za|XklN#?kP zl;EstdW?(_4D>kwjWy2f!LM)y?F94kyU3`W!6+AyId-89v}sXJpuic^NLL7GJItl~ zsiuB98AI-(#Mnm|=A-R6&2fwJ0JVSY#Q>&3$zFh|@;#%0qeF=j5Ajq@4i0tIIW z&}sk$&fGwoJpe&u-JeGLi^r?dO`m=y(QO{@h zQqAC7$rvz&5+mo3IqE?h=a~6m>%r5Quapvzq;{y~p zJpyXOBgD9VrW7@#p6l7O?o3feml(DtSL>D^R) zZUY%T2b0-vBAFN7VB;M88!~HuOXi4KcI6aRQ&h|XQ0A?m%j2=l1f0cGP}h(oVfJ`N zz#PpmFC*ieab)zJK<4?^k=g%OjPnkANzbAbmGZHoVRk*mTfm75s_cWVa`l*f$B@xu z5E*?&@seIo#*Y~1rBm!7sF9~~u6Wrj5oICUOuz}CS)jdNIznfzCA(stJ(7$c^e5wN z?lt>eYgbA!kvAR7zYSD&*r1$b|(@;9dcZ^67R0 zXAXJKa|5Sdmj!g578Nwt6d$sXuc&MWezA0Whd`94$h{{?1IwXP4)Tx4obDK%xoFZ_Z zjjHJ_P@R_e5blG@yEjnaJb`l;s%Lb2&=8$&Ct-fV`E^4CUs)=jTk!I}2d&n!f@)bm z@ z_4Dc86+3l2*p|~;o-Sb~oXb_RuLmoifDU^&Te$*FevycC0*nE3Xws8gsWp|Rj2>SM zns)qcYj?^2sd8?N!_w~4v+f-HCF|a$TNZDoNl$I1Uq87euoNgKb6&r26TNrfkUa@o zfdiFA@p{K&mH3b8i!lcoz)V{n8Q@g(vR4ns4r6w;K z>1~ecQR0-<^J|Ndg5fvVUM9g;lbu-){#ghGw(fg>L zh)T5Ljb%lWE;V9L!;Cqk>AV1(rULYF07ZBJbGb9qbSoLAd;in9{)95YqX$J43-dY7YU*k~vrM25 zxh5_IqO0LYZW%oxQ5HOzmk4x{atE*vipUk}sh88$b2tn?!ujEHn`tQLe&vo}nMb&{ zio`xzZ&GG6&ZyN3jnaQy#iVqXE9VT(3tWY$n-)uWDQ|tc{`?fq2F`oQ{;d3aWPg4Hp-(iE{ry>MIPWL> iW8 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Base.lproj/Main.storyboard b/packages/image_picker/image_picker_ios/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100755 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/Info.plist b/packages/image_picker/image_picker_ios/example/ios/Runner/Info.plist new file mode 100755 index 000000000000..f9c1909383ca --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner/Info.plist @@ -0,0 +1,59 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + image_picker_example + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSCameraUsageDescription + Used to demonstrate image picker plugin + NSMicrophoneUsageDescription + Used to capture audio for image picker plugin + NSPhotoLibraryUsageDescription + Used to demonstrate image picker plugin + UIBackgroundModes + + remote-notification + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/packages/image_picker/image_picker_ios/example/ios/Runner/main.m b/packages/image_picker/image_picker_ios/example/ios/Runner/main.m new file mode 100644 index 000000000000..f143297b30d6 --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/Runner/main.m @@ -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 +#import +#import "AppDelegate.h" + +int main(int argc, char *argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m similarity index 99% rename from packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m rename to packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m index 5f3287400c5e..8df5299e54d9 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerPluginTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m @@ -4,8 +4,8 @@ #import "ImagePickerTestImages.h" -@import image_picker; -@import image_picker.Test; +@import image_picker_ios; +@import image_picker_ios.Test; @import XCTest; #import diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerTestImages.h b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerTestImages.h similarity index 100% rename from packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerTestImages.h rename to packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerTestImages.h diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerTestImages.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerTestImages.m similarity index 100% rename from packages/image_picker/image_picker/example/ios/RunnerTests/ImagePickerTestImages.m rename to packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerTestImages.m diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/ImageUtilTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImageUtilTests.m similarity index 97% rename from packages/image_picker/image_picker/example/ios/RunnerTests/ImageUtilTests.m rename to packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImageUtilTests.m index 9b9719f88116..e449a84b80bb 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerTests/ImageUtilTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImageUtilTests.m @@ -4,8 +4,8 @@ #import "ImagePickerTestImages.h" -@import image_picker; -@import image_picker.Test; +@import image_picker_ios; +@import image_picker_ios.Test; @import XCTest; @interface ImageUtilTests : XCTestCase diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/Info.plist b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/Info.plist similarity index 100% rename from packages/image_picker/image_picker/example/ios/RunnerTests/Info.plist rename to packages/image_picker/image_picker_ios/example/ios/RunnerTests/Info.plist diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/MetaDataUtilTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/MetaDataUtilTests.m similarity index 98% rename from packages/image_picker/image_picker/example/ios/RunnerTests/MetaDataUtilTests.m rename to packages/image_picker/image_picker_ios/example/ios/RunnerTests/MetaDataUtilTests.m index 4160c51cc600..b684a214570b 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerTests/MetaDataUtilTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/MetaDataUtilTests.m @@ -4,8 +4,8 @@ #import "ImagePickerTestImages.h" -@import image_picker; -@import image_picker.Test; +@import image_picker_ios; +@import image_picker_ios.Test; @import XCTest; @interface MetaDataUtilTests : XCTestCase diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/PhotoAssetUtilTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PhotoAssetUtilTests.m similarity index 99% rename from packages/image_picker/image_picker/example/ios/RunnerTests/PhotoAssetUtilTests.m rename to packages/image_picker/image_picker_ios/example/ios/RunnerTests/PhotoAssetUtilTests.m index 97b4b6cd8eb3..d211ea3f91df 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerTests/PhotoAssetUtilTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PhotoAssetUtilTests.m @@ -4,8 +4,8 @@ #import "ImagePickerTestImages.h" -@import image_picker; -@import image_picker.Test; +@import image_picker_ios; +@import image_picker_ios.Test; @import XCTest; @interface PhotoAssetUtilTests : XCTestCase diff --git a/packages/image_picker/image_picker/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m similarity index 98% rename from packages/image_picker/image_picker/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m rename to packages/image_picker/image_picker_ios/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m index f94db83d5696..688f5fbee032 100644 --- a/packages/image_picker/image_picker/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m @@ -5,8 +5,8 @@ #import #import -@import image_picker; -@import image_picker.Test; +@import image_picker_ios; +@import image_picker_ios.Test; @import XCTest; @interface PickerSaveImageToPathOperationTests : XCTestCase diff --git a/packages/image_picker/image_picker/example/ios/RunnerUITests/ImagePickerFromGalleryUITests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerUITests/ImagePickerFromGalleryUITests.m similarity index 100% rename from packages/image_picker/image_picker/example/ios/RunnerUITests/ImagePickerFromGalleryUITests.m rename to packages/image_picker/image_picker_ios/example/ios/RunnerUITests/ImagePickerFromGalleryUITests.m diff --git a/packages/image_picker/image_picker/example/ios/RunnerUITests/ImagePickerFromLimitedGalleryUITests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerUITests/ImagePickerFromLimitedGalleryUITests.m similarity index 100% rename from packages/image_picker/image_picker/example/ios/RunnerUITests/ImagePickerFromLimitedGalleryUITests.m rename to packages/image_picker/image_picker_ios/example/ios/RunnerUITests/ImagePickerFromLimitedGalleryUITests.m diff --git a/packages/image_picker/image_picker/example/ios/RunnerUITests/Info.plist b/packages/image_picker/image_picker_ios/example/ios/RunnerUITests/Info.plist similarity index 100% rename from packages/image_picker/image_picker/example/ios/RunnerUITests/Info.plist rename to packages/image_picker/image_picker_ios/example/ios/RunnerUITests/Info.plist diff --git a/packages/image_picker/image_picker_ios/example/ios/TestImages/gifImage.gif b/packages/image_picker/image_picker_ios/example/ios/TestImages/gifImage.gif new file mode 100644 index 0000000000000000000000000000000000000000..5f989fcf40c7de44fd1aa768dfec58c4d2c4bd72 GIT binary patch literal 843 zcmZ?wbhEHbHaF& zNj2h=DkPWW3a`$Q-yNp3KS+B|sK(54g;n{=JF_*PK2pk2-CeAq#NjYgL zAweavM$D*gWq;4W`G$e(Ed%E}29Ea(-0v8;-ZSvLVc>tmAn=Sq@FfHDM+Vjp4D6p6 z7{4+we`R3!&cOJcf$19q^LIv;FAS_-8Q8usuzzJ_`^L!mfq~;A12CkyJ~428W@Pxm z!0?lS;RhqbPevvn`HO+!7bC-OP%@+kR1VRQz 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~PZZDSry^7odplYBQ6;?oIZfZ%QLPc&)Ua?h$ ztrA#;6_5=Q)>l#hD=EpgRf_NpP;kyKN>wn`Gto0pvg1-vP_QXVNwW%aaf50H@@$nd zN=gc>^!3Zj%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUl_7?}%yCIAPA?$S;6fmsbq+8_<{faA|!*Jp+Ag29#u` zA)Jb$7bFAG2KFJuVjH-LR?bDKDVZg9hgK@M=jW&Aq}m{CgUKNrYy&dQ%D*Tx73gJ< z$LtJk^fAPs>TUEvzD5cKNDzZXfnjXNWup&|FFUTs6>@8VQ7GZ*;usljA6n>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 literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_ios/example/ios/image_picker_exampleTests/Info.plist b/packages/image_picker/image_picker_ios/example/ios/image_picker_exampleTests/Info.plist new file mode 100644 index 000000000000..6c40a6cd0c4a --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/ios/image_picker_exampleTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/packages/image_picker/image_picker_ios/example/lib/main.dart b/packages/image_picker/image_picker_ios/example/lib/main.dart new file mode 100755 index 000000000000..48eee35445da --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/lib/main.dart @@ -0,0 +1,467 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'package:video_player/video_player.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return const MaterialApp( + title: 'Image Picker Demo', + home: MyHomePage(title: 'Image Picker Example'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({Key? key, this.title}) : super(key: key); + + final String? title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + List? _imageFileList; + + set _imageFile(XFile? value) { + _imageFileList = value == null ? null : [value]; + } + + dynamic _pickImageError; + bool isVideo = false; + + VideoPlayerController? _controller; + VideoPlayerController? _toBeDisposed; + String? _retrieveDataError; + + final ImagePickerPlatform _picker = ImagePickerPlatform.instance; + final TextEditingController maxWidthController = TextEditingController(); + final TextEditingController maxHeightController = TextEditingController(); + final TextEditingController qualityController = TextEditingController(); + + Future _playVideo(XFile? file) async { + if (file != null && mounted) { + await _disposeVideoController(); + late VideoPlayerController controller; + if (kIsWeb) { + controller = VideoPlayerController.network(file.path); + } else { + controller = VideoPlayerController.file(File(file.path)); + } + _controller = controller; + // In web, most browsers won't honor a programmatic call to .play + // if the video has a sound track (and is not muted). + // Mute the video so it auto-plays in web! + // This is not needed if the call to .play is the result of user + // interaction (clicking on a "play" button, for example). + const double volume = kIsWeb ? 0.0 : 1.0; + await controller.setVolume(volume); + await controller.initialize(); + await controller.setLooping(true); + await controller.play(); + setState(() {}); + } + } + + Future _onImageButtonPressed(ImageSource source, + {BuildContext? context, bool isMultiImage = false}) async { + if (_controller != null) { + await _controller!.setVolume(0.0); + } + if (isVideo) { + final XFile? file = await _picker.getVideo( + source: source, maxDuration: const Duration(seconds: 10)); + await _playVideo(file); + } else if (isMultiImage) { + await _displayPickImageDialog(context!, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final List? pickedFileList = await _picker.getMultiImage( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ); + setState(() { + _imageFileList = pickedFileList; + }); + } catch (e) { + setState(() { + _pickImageError = e; + }); + } + }); + } else { + await _displayPickImageDialog(context!, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final XFile? pickedFile = await _picker.getImage( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ); + setState(() { + _imageFile = pickedFile; + }); + } catch (e) { + setState(() { + _pickImageError = e; + }); + } + }); + } + } + + @override + void deactivate() { + if (_controller != null) { + _controller!.setVolume(0.0); + _controller!.pause(); + } + super.deactivate(); + } + + @override + void dispose() { + _disposeVideoController(); + maxWidthController.dispose(); + maxHeightController.dispose(); + qualityController.dispose(); + super.dispose(); + } + + Future _disposeVideoController() async { + if (_toBeDisposed != null) { + await _toBeDisposed!.dispose(); + } + _toBeDisposed = _controller; + _controller = null; + } + + Widget _previewVideo() { + final Text? retrieveError = _getRetrieveErrorWidget(); + if (retrieveError != null) { + return retrieveError; + } + if (_controller == null) { + return const Text( + 'You have not yet picked a video', + textAlign: TextAlign.center, + ); + } + return Padding( + padding: const EdgeInsets.all(10.0), + child: AspectRatioVideo(_controller), + ); + } + + Widget _previewImages() { + final Text? retrieveError = _getRetrieveErrorWidget(); + if (retrieveError != null) { + return retrieveError; + } + if (_imageFileList != null) { + return Semantics( + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + label: 'image_picker_example_picked_images'); + } else if (_pickImageError != null) { + return Text( + 'Pick image error: $_pickImageError', + textAlign: TextAlign.center, + ); + } else { + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + } + } + + Widget _handlePreview() { + if (isVideo) { + return _previewVideo(); + } else { + return _previewImages(); + } + } + + Future retrieveLostData() async { + final LostDataResponse response = await _picker.getLostData(); + if (response.isEmpty) { + return; + } + if (response.file != null) { + if (response.type == RetrieveType.video) { + isVideo = true; + await _playVideo(response.file); + } else { + isVideo = false; + setState(() { + _imageFile = response.file; + _imageFileList = response.files; + }); + } + } else { + _retrieveDataError = response.exception!.code; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title!), + ), + body: Center( + child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android + ? FutureBuilder( + future: retrieveLostData(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + switch (snapshot.connectionState) { + case ConnectionState.none: + case ConnectionState.waiting: + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + case ConnectionState.done: + return _handlePreview(); + default: + if (snapshot.hasError) { + return Text( + 'Pick image/video error: ${snapshot.error}}', + textAlign: TextAlign.center, + ); + } else { + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + } + } + }, + ) + : _handlePreview(), + ), + floatingActionButton: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Semantics( + label: 'image_picker_example_from_gallery', + child: FloatingActionButton( + onPressed: () { + isVideo = false; + _onImageButtonPressed(ImageSource.gallery, context: context); + }, + heroTag: 'image0', + tooltip: 'Pick Image from gallery', + child: const Icon(Icons.photo), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + isVideo = false; + _onImageButtonPressed( + ImageSource.gallery, + context: context, + isMultiImage: true, + ); + }, + heroTag: 'image1', + tooltip: 'Pick Multiple Image from gallery', + child: const Icon(Icons.photo_library), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + isVideo = false; + _onImageButtonPressed(ImageSource.camera, context: context); + }, + heroTag: 'image2', + tooltip: 'Take a Photo', + child: const Icon(Icons.camera_alt), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + backgroundColor: Colors.red, + onPressed: () { + isVideo = true; + _onImageButtonPressed(ImageSource.gallery); + }, + heroTag: 'video0', + tooltip: 'Pick Video from gallery', + child: const Icon(Icons.video_library), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + backgroundColor: Colors.red, + onPressed: () { + isVideo = true; + _onImageButtonPressed(ImageSource.camera); + }, + heroTag: 'video1', + tooltip: 'Take a Video', + child: const Icon(Icons.videocam), + ), + ), + ], + ), + ); + } + + Text? _getRetrieveErrorWidget() { + if (_retrieveDataError != null) { + final Text result = Text(_retrieveDataError!); + _retrieveDataError = null; + return result; + } + return null; + } + + Future _displayPickImageDialog( + BuildContext context, OnPickImageCallback onPick) async { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Add optional parameters'), + content: Column( + children: [ + TextField( + controller: maxWidthController, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxWidth if desired'), + ), + TextField( + controller: maxHeightController, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxHeight if desired'), + ), + TextField( + controller: qualityController, + keyboardType: TextInputType.number, + decoration: const InputDecoration( + hintText: 'Enter quality if desired'), + ), + ], + ), + actions: [ + TextButton( + child: const Text('CANCEL'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text('PICK'), + onPressed: () { + final double? width = maxWidthController.text.isNotEmpty + ? double.parse(maxWidthController.text) + : null; + final double? height = maxHeightController.text.isNotEmpty + ? double.parse(maxHeightController.text) + : null; + final int? quality = qualityController.text.isNotEmpty + ? int.parse(qualityController.text) + : null; + onPick(width, height, quality); + Navigator.of(context).pop(); + }), + ], + ); + }); + } +} + +typedef OnPickImageCallback = void Function( + double? maxWidth, double? maxHeight, int? quality); + +class AspectRatioVideo extends StatefulWidget { + const AspectRatioVideo(this.controller); + + final VideoPlayerController? controller; + + @override + AspectRatioVideoState createState() => AspectRatioVideoState(); +} + +class AspectRatioVideoState extends State { + VideoPlayerController? get controller => widget.controller; + bool initialized = false; + + void _onVideoControllerUpdate() { + if (!mounted) { + return; + } + if (initialized != controller!.value.isInitialized) { + initialized = controller!.value.isInitialized; + setState(() {}); + } + } + + @override + void initState() { + super.initState(); + controller!.addListener(_onVideoControllerUpdate); + } + + @override + void dispose() { + controller!.removeListener(_onVideoControllerUpdate); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (initialized) { + return Center( + child: AspectRatio( + aspectRatio: controller!.value.aspectRatio, + child: VideoPlayer(controller!), + ), + ); + } else { + return Container(); + } + } +} diff --git a/packages/image_picker/image_picker_ios/example/pubspec.yaml b/packages/image_picker/image_picker_ios/example/pubspec.yaml new file mode 100755 index 000000000000..a47893d7687f --- /dev/null +++ b/packages/image_picker/image_picker_ios/example/pubspec.yaml @@ -0,0 +1,29 @@ +name: image_picker_example +description: Demonstrates how to use the image_picker plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + image_picker_ios: + # When depending on this package from a real application you should use: + # image_picker_ios: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # 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: ../ + image_picker_platform_interface: ^2.3.0 + video_player: ^2.1.4 + +dev_dependencies: + flutter_driver: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/image_picker/image_picker_ios/example/test_driver/integration_test.dart b/packages/image_picker/image_picker_ios/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/image_picker/image_picker_ios/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/image_picker/image_picker/ios/Assets/.gitkeep b/packages/image_picker/image_picker_ios/ios/Assets/.gitkeep similarity index 100% rename from packages/image_picker/image_picker/ios/Assets/.gitkeep rename to packages/image_picker/image_picker_ios/ios/Assets/.gitkeep diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerImageUtil.h b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerImageUtil.h similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTImagePickerImageUtil.h rename to packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerImageUtil.h diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerImageUtil.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerImageUtil.m similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTImagePickerImageUtil.m rename to packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerImageUtil.m diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerMetaDataUtil.h b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerMetaDataUtil.h similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTImagePickerMetaDataUtil.h rename to packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerMetaDataUtil.h diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerMetaDataUtil.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerMetaDataUtil.m similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTImagePickerMetaDataUtil.m rename to packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerMetaDataUtil.m diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPhotoAssetUtil.h b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPhotoAssetUtil.h similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTImagePickerPhotoAssetUtil.h rename to packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPhotoAssetUtil.h diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPhotoAssetUtil.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPhotoAssetUtil.m similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTImagePickerPhotoAssetUtil.m rename to packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPhotoAssetUtil.m diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.h b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.h similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.h rename to packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.h diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m rename to packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin_Test.h b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h similarity index 95% rename from packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin_Test.h rename to packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h index 5442f7d089c6..039f76de427d 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin_Test.h +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// This header is available in the Test module. Import via "@import image_picker.Test;" +// This header is available in the Test module. Import via "@import image_picker_ios_ios.Test;" -#import +#import /** Methods exposed for unit testing. */ @interface FLTImagePickerPlugin () diff --git a/packages/image_picker/image_picker/ios/Classes/FLTPHPickerSaveImageToPathOperation.h b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.h similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTPHPickerSaveImageToPathOperation.h rename to packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.h diff --git a/packages/image_picker/image_picker/ios/Classes/FLTPHPickerSaveImageToPathOperation.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m similarity index 100% rename from packages/image_picker/image_picker/ios/Classes/FLTPHPickerSaveImageToPathOperation.m rename to packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m diff --git a/packages/image_picker/image_picker/ios/Classes/ImagePickerPlugin.modulemap b/packages/image_picker/image_picker_ios/ios/Classes/ImagePickerPlugin.modulemap similarity index 76% rename from packages/image_picker/image_picker/ios/Classes/ImagePickerPlugin.modulemap rename to packages/image_picker/image_picker_ios/ios/Classes/ImagePickerPlugin.modulemap index dc702ea49fb1..0d60b684a256 100644 --- a/packages/image_picker/image_picker/ios/Classes/ImagePickerPlugin.modulemap +++ b/packages/image_picker/image_picker_ios/ios/Classes/ImagePickerPlugin.modulemap @@ -1,6 +1,6 @@ -framework module image_picker { - umbrella header "image_picker-umbrella.h" - +framework module image_picker_ios { + umbrella header "image_picker_ios-umbrella.h" + export * module * { export * } diff --git a/packages/image_picker/image_picker/ios/Classes/image_picker-umbrella.h b/packages/image_picker/image_picker_ios/ios/Classes/image_picker_ios-umbrella.h similarity index 79% rename from packages/image_picker/image_picker/ios/Classes/image_picker-umbrella.h rename to packages/image_picker/image_picker_ios/ios/Classes/image_picker_ios-umbrella.h index 0d89b2e1f636..0e23d6d9d60a 100644 --- a/packages/image_picker/image_picker/ios/Classes/image_picker-umbrella.h +++ b/packages/image_picker/image_picker_ios/ios/Classes/image_picker_ios-umbrella.h @@ -3,4 +3,4 @@ // found in the LICENSE file. #import -#import +#import diff --git a/packages/image_picker/image_picker/ios/image_picker.podspec b/packages/image_picker/image_picker_ios/ios/image_picker_ios.podspec similarity index 86% rename from packages/image_picker/image_picker/ios/image_picker.podspec rename to packages/image_picker/image_picker_ios/ios/image_picker_ios.podspec index 2a10b1ce01a8..549c5f09e1f8 100644 --- a/packages/image_picker/image_picker/ios/image_picker.podspec +++ b/packages/image_picker/image_picker_ios/ios/image_picker_ios.podspec @@ -2,7 +2,7 @@ # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html # Pod::Spec.new do |s| - s.name = 'image_picker' + s.name = 'image_picker_ios' s.version = '0.0.1' s.summary = 'Flutter plugin that shows an image picker.' s.description = <<-DESC @@ -12,8 +12,8 @@ Downloaded by pub (not CocoaPods). s.homepage = 'https://github.com/flutter/plugins' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/image_picker' } - s.documentation_url = 'https://pub.dev/packages/image_picker' + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/image_picker_ios' } + s.documentation_url = 'https://pub.dev/packages/image_picker_ios' s.source_files = 'Classes/**/*.{h,m}' s.public_header_files = 'Classes/**/*.h' s.module_map = 'Classes/ImagePickerPlugin.modulemap' diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml new file mode 100755 index 000000000000..2587c9a0d15b --- /dev/null +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -0,0 +1,26 @@ +name: image_picker_ios +description: iOS implementation of the video_picker plugin. +repository: https://github.com/flutter/plugins/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.4+11 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +flutter: + plugin: + implements: image_picker + platforms: + ios: + pluginClass: FLTImagePickerPlugin + +dependencies: + flutter: + sdk: flutter + image_picker_platform_interface: ^2.3.0 + +dev_dependencies: + flutter_test: + sdk: flutter + mockito: ^5.0.0 From b9ea3037f1ed95c238d418a74e49b87388bbd421 Mon Sep 17 00:00:00 2001 From: Kate Lovett Date: Tue, 5 Apr 2022 13:11:10 -0500 Subject: [PATCH 083/844] [webview_flutter] Migrate deprecated Scaffold snackbars (#5150) --- .../webview_flutter/CHANGELOG.md | 3 +- .../webview_flutter/example/lib/main.dart | 129 +++++++++--------- .../example/test/main_test.dart | 39 ++++++ .../webview_flutter/pubspec.yaml | 2 +- .../webview_flutter_android/CHANGELOG.md | 4 + .../example/lib/main.dart | 79 +++++------ .../webview_flutter_android/pubspec.yaml | 2 +- .../webview_flutter_wkwebview/CHANGELOG.md | 3 +- .../example/lib/main.dart | 47 +++---- .../webview_flutter_wkwebview/pubspec.yaml | 2 +- 10 files changed, 168 insertions(+), 142 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter/example/test/main_test.dart diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index dde4472eed57..f8a6e2514d7e 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 3.0.2 +* Migrates deprecated `Scaffold.showSnackBar` to `ScaffoldMessenger` in example app. * Adds OS version support information to README. ## 3.0.1 diff --git a/packages/webview_flutter/webview_flutter/example/lib/main.dart b/packages/webview_flutter/webview_flutter/example/lib/main.dart index 88c240a7316c..51080be01df0 100644 --- a/packages/webview_flutter/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter/example/lib/main.dart @@ -13,7 +13,7 @@ import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; import 'package:webview_flutter/webview_flutter.dart'; -void main() => runApp(MaterialApp(home: WebViewExample())); +void main() => runApp(const MaterialApp(home: WebViewExample())); const String kNavigationExamplePage = ''' @@ -71,6 +71,10 @@ const String kTransparentBackgroundPage = ''' '''; class WebViewExample extends StatefulWidget { + const WebViewExample({this.cookieManager}); + + final CookieManager? cookieManager; + @override _WebViewExampleState createState() => _WebViewExampleState(); } @@ -96,42 +100,38 @@ class _WebViewExampleState extends State { // This drop down menu demonstrates that Flutter widgets can be shown over the web view. actions: [ NavigationControls(_controller.future), - SampleMenu(_controller.future), + SampleMenu(_controller.future, widget.cookieManager), ], ), - // We're using a Builder here so we have a context that is below the Scaffold - // to allow calling Scaffold.of(context) so we can show a snackbar. - body: Builder(builder: (BuildContext context) { - return WebView( - initialUrl: 'https://flutter.dev', - javascriptMode: JavascriptMode.unrestricted, - onWebViewCreated: (WebViewController webViewController) { - _controller.complete(webViewController); - }, - onProgress: (int progress) { - print('WebView is loading (progress : $progress%)'); - }, - javascriptChannels: { - _toasterJavascriptChannel(context), - }, - navigationDelegate: (NavigationRequest request) { - if (request.url.startsWith('https://www.youtube.com/')) { - print('blocking navigation to $request}'); - return NavigationDecision.prevent; - } - print('allowing navigation to $request'); - return NavigationDecision.navigate; - }, - onPageStarted: (String url) { - print('Page started loading: $url'); - }, - onPageFinished: (String url) { - print('Page finished loading: $url'); - }, - gestureNavigationEnabled: true, - backgroundColor: const Color(0x00000000), - ); - }), + body: WebView( + initialUrl: 'https://flutter.dev', + javascriptMode: JavascriptMode.unrestricted, + onWebViewCreated: (WebViewController webViewController) { + _controller.complete(webViewController); + }, + onProgress: (int progress) { + print('WebView is loading (progress : $progress%)'); + }, + javascriptChannels: { + _toasterJavascriptChannel(context), + }, + navigationDelegate: (NavigationRequest request) { + if (request.url.startsWith('https://www.youtube.com/')) { + print('blocking navigation to $request}'); + return NavigationDecision.prevent; + } + print('allowing navigation to $request'); + return NavigationDecision.navigate; + }, + onPageStarted: (String url) { + print('Page started loading: $url'); + }, + onPageFinished: (String url) { + print('Page finished loading: $url'); + }, + gestureNavigationEnabled: true, + backgroundColor: const Color(0x00000000), + ), floatingActionButton: favoriteButton(), ); } @@ -140,8 +140,7 @@ class _WebViewExampleState extends State { return JavascriptChannel( name: 'Toaster', onMessageReceived: (JavascriptMessage message) { - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(message.message)), ); }); @@ -152,19 +151,24 @@ class _WebViewExampleState extends State { future: _controller.future, builder: (BuildContext context, AsyncSnapshot controller) { - if (controller.hasData) { - return FloatingActionButton( - onPressed: () async { - final String url = (await controller.data!.currentUrl())!; - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( - SnackBar(content: Text('Favorited $url')), - ); - }, - child: const Icon(Icons.favorite), - ); - } - return Container(); + return FloatingActionButton( + onPressed: () async { + String? url; + if (controller.hasData) { + url = (await controller.data!.currentUrl())!; + } + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + controller.hasData + ? 'Favorited $url' + : 'Unable to favorite', + ), + ), + ); + }, + child: const Icon(Icons.favorite), + ); }); } } @@ -186,10 +190,11 @@ enum MenuOptions { } class SampleMenu extends StatelessWidget { - SampleMenu(this.controller); + SampleMenu(this.controller, CookieManager? cookieManager) + : cookieManager = cookieManager ?? CookieManager(); final Future controller; - final CookieManager cookieManager = CookieManager(); + late final CookieManager cookieManager; @override Widget build(BuildContext context) { @@ -315,8 +320,7 @@ class SampleMenu extends StatelessWidget { WebViewController controller, BuildContext context) async { final String cookies = await controller.runJavascriptReturningResult('document.cookie'); - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar(SnackBar( + ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Column( mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.min, @@ -332,8 +336,7 @@ class SampleMenu extends StatelessWidget { WebViewController controller, BuildContext context) async { await controller.runJavascript( 'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";'); - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar(const SnackBar( + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Added a test entry to cache.'), )); } @@ -348,8 +351,7 @@ class SampleMenu extends StatelessWidget { Future _onClearCache( WebViewController controller, BuildContext context) async { await controller.clearCache(); - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar(const SnackBar( + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Cache cleared.'), )); } @@ -360,8 +362,7 @@ class SampleMenu extends StatelessWidget { if (!hadCookies) { message = 'There are no cookies.'; } - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar(SnackBar( + ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text(message), )); } @@ -375,7 +376,7 @@ class SampleMenu extends StatelessWidget { Future _onSetCookie( WebViewController controller, BuildContext context) async { - await CookieManager().setCookie( + await cookieManager.setCookie( const WebViewCookie( name: 'foo', value: 'bar', domain: 'httpbin.org', path: '/anything'), ); @@ -466,8 +467,7 @@ class NavigationControls extends StatelessWidget { if (await controller!.canGoBack()) { await controller.goBack(); } else { - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('No back history item')), ); return; @@ -482,8 +482,7 @@ class NavigationControls extends StatelessWidget { if (await controller!.canGoForward()) { await controller.goForward(); } else { - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('No forward history item')), ); diff --git a/packages/webview_flutter/webview_flutter/example/test/main_test.dart b/packages/webview_flutter/webview_flutter/example/test/main_test.dart new file mode 100644 index 000000000000..867633366e1a --- /dev/null +++ b/packages/webview_flutter/webview_flutter/example/test/main_test.dart @@ -0,0 +1,39 @@ +// 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:webview_flutter/webview_flutter.dart'; +import 'package:webview_flutter_example/main.dart'; + +void main() { + testWidgets('Test snackbar from ScaffoldMessenger', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: WebViewExample(cookieManager: FakeCookieManager()), + ), + ); + expect(find.byIcon(Icons.favorite), findsOneWidget); + await tester.tap(find.byIcon(Icons.favorite)); + await tester.pump(); + expect(find.byType(SnackBar), findsOneWidget); + }); +} + +class FakeCookieManager implements CookieManager { + factory FakeCookieManager() { + return _instance ??= FakeCookieManager._(); + } + + FakeCookieManager._(); + + static FakeCookieManager? _instance; + + @override + Future clearCookies() => throw UnimplementedError(); + + @override + Future setCookie(WebViewCookie cookie) => throw UnimplementedError(); +} diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 322f4ada3b56..10350984ce9a 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 3.0.1 +version: 3.0.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index ad81b0f6e83d..fb5a2b85ffed 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.5 + +* Migrates deprecated `Scaffold.showSnackBar` to `ScaffoldMessenger` in example app. + ## 2.8.4 * Fixes bug preventing `mockito` code generation for tests. 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 0d0cd59af796..3dd107bd557b 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -110,37 +110,33 @@ class _WebViewExampleState extends State<_WebViewExample> { _SampleMenu(_controller.future), ], ), - // We're using a Builder here so we have a context that is below the Scaffold - // to allow calling Scaffold.of(context) so we can show a snackbar. - body: Builder(builder: (BuildContext context) { - return WebView( - initialUrl: 'https://flutter.dev', - onWebViewCreated: (WebViewController controller) { - _controller.complete(controller); - }, - onProgress: (int progress) { - print('WebView is loading (progress : $progress%)'); - }, - navigationDelegate: (NavigationRequest request) { - if (request.url.startsWith('https://www.youtube.com/')) { - print('blocking navigation to $request}'); - return NavigationDecision.prevent; - } - print('allowing navigation to $request'); - return NavigationDecision.navigate; - }, - onPageStarted: (String url) { - print('Page started loading: $url'); - }, - onPageFinished: (String url) { - print('Page finished loading: $url'); - }, - javascriptChannels: _createJavascriptChannels(context), - javascriptMode: JavascriptMode.unrestricted, - userAgent: 'Custom_User_Agent', - backgroundColor: const Color(0x80000000), - ); - }), + body: WebView( + initialUrl: 'https://flutter.dev', + onWebViewCreated: (WebViewController controller) { + _controller.complete(controller); + }, + onProgress: (int progress) { + print('WebView is loading (progress : $progress%)'); + }, + navigationDelegate: (NavigationRequest request) { + if (request.url.startsWith('https://www.youtube.com/')) { + print('blocking navigation to $request}'); + return NavigationDecision.prevent; + } + print('allowing navigation to $request'); + return NavigationDecision.navigate; + }, + onPageStarted: (String url) { + print('Page started loading: $url'); + }, + onPageFinished: (String url) { + print('Page finished loading: $url'); + }, + javascriptChannels: _createJavascriptChannels(context), + javascriptMode: JavascriptMode.unrestricted, + userAgent: 'Custom_User_Agent', + backgroundColor: const Color(0x80000000), + ), floatingActionButton: favoriteButton(), ); } @@ -154,8 +150,7 @@ class _WebViewExampleState extends State<_WebViewExample> { return FloatingActionButton( onPressed: () async { final String url = (await controller.data!.currentUrl())!; - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Favorited $url')), ); }, @@ -323,8 +318,7 @@ class _SampleMenu extends StatelessWidget { WebViewController controller, BuildContext context) async { final String cookies = await controller.runJavascriptReturningResult('document.cookie'); - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar(SnackBar( + ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Column( mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.min, @@ -340,8 +334,7 @@ class _SampleMenu extends StatelessWidget { WebViewController controller, BuildContext context) async { await controller.runJavascript( 'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";'); - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar(const SnackBar( + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Added a test entry to cache.'), )); } @@ -356,8 +349,7 @@ class _SampleMenu extends StatelessWidget { Future _onClearCache( WebViewController controller, BuildContext context) async { await controller.clearCache(); - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar(const SnackBar( + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Cache cleared.'), )); } @@ -369,8 +361,7 @@ class _SampleMenu extends StatelessWidget { if (!hadCookies) { message = 'There are no cookies.'; } - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar(SnackBar( + ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text(message), )); } @@ -474,8 +465,7 @@ class _NavigationControls extends StatelessWidget { if (await controller!.canGoBack()) { await controller.goBack(); } else { - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('No back history item')), ); return; @@ -490,8 +480,7 @@ class _NavigationControls extends StatelessWidget { if (await controller!.canGoForward()) { await controller.goForward(); } else { - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('No forward history item')), ); diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 5c3c83abc53d..3fae698d1481 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/plugins/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: 2.8.4 +version: 2.8.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index c3dd8726273e..bad900de46b4 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.7.2 * Fixes an integration test race condition. +* Migrates deprecated `Scaffold.showSnackBar` to `ScaffoldMessenger` in example app. ## 2.7.1 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 d4e0ec4aba0c..e126de8491e5 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -105,27 +105,23 @@ class _WebViewExampleState extends State<_WebViewExample> { _SampleMenu(_controller.future), ], ), - // We're using a Builder here so we have a context that is below the Scaffold - // to allow calling Scaffold.of(context) so we can show a snackbar. - body: Builder(builder: (BuildContext context) { - return WebView( - initialUrl: 'https://flutter.dev/', - onWebViewCreated: (WebViewController controller) { - _controller.complete(controller); - }, - javascriptChannels: _createJavascriptChannels(context), - javascriptMode: JavascriptMode.unrestricted, - navigationDelegate: (NavigationRequest request) { - if (request.url.startsWith('https://www.youtube.com/')) { - print('blocking navigation to $request}'); - return NavigationDecision.prevent; - } - print('allowing navigation to $request'); - return NavigationDecision.navigate; - }, - backgroundColor: const Color(0x80000000), - ); - }), + body: WebView( + initialUrl: 'https://flutter.dev/', + onWebViewCreated: (WebViewController controller) { + _controller.complete(controller); + }, + javascriptChannels: _createJavascriptChannels(context), + javascriptMode: JavascriptMode.unrestricted, + navigationDelegate: (NavigationRequest request) { + if (request.url.startsWith('https://www.youtube.com/')) { + print('blocking navigation to $request}'); + return NavigationDecision.prevent; + } + print('allowing navigation to $request'); + return NavigationDecision.navigate; + }, + backgroundColor: const Color(0x80000000), + ), floatingActionButton: favoriteButton(), ); } @@ -139,8 +135,7 @@ class _WebViewExampleState extends State<_WebViewExample> { return FloatingActionButton( onPressed: () async { final String url = (await controller.data!.currentUrl())!; - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Favorited $url')), ); }, @@ -456,8 +451,7 @@ class _NavigationControls extends StatelessWidget { if (await controller!.canGoBack()) { await controller.goBack(); } else { - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('No back history item')), ); return; @@ -472,8 +466,7 @@ class _NavigationControls extends StatelessWidget { if (await controller!.canGoForward()) { await controller.goForward(); } else { - // ignore: deprecated_member_use - Scaffold.of(context).showSnackBar( + ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('No forward history item')), ); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 219d6ce49f4f..75e7ccaaf74f 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/plugins/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: 2.7.1 +version: 2.7.2 environment: sdk: ">=2.14.0 <3.0.0" From d2ddbf470f9ec5a7c8dad220ff854568c0ed1b9b Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 5 Apr 2022 15:56:11 -0400 Subject: [PATCH 084/844] [image_picker] Publish federated version (#5174) --- packages/image_picker/image_picker/CHANGELOG.md | 2 +- packages/image_picker/image_picker/pubspec.yaml | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index d84304f0b5e9..f1bf54c5cd35 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,4 +1,4 @@ -## NEXT +## 0.8.5 * Moves Android and iOS implementations to federated packages. * Adds OS version support information to README. diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 05ac189fb05d..77a50916283e 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,10 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.4+11 -# Temporarily disable publishing to allow moving Android and iOS -# implementations. -publish_to: none +version: 0.8.5 environment: sdk: ">=2.14.0 <3.0.0" @@ -25,12 +22,9 @@ flutter: dependencies: flutter: sdk: flutter - # Temporary path dependencies to allow moving Android and iOS implementations. - image_picker_android: - path: ../image_picker_android + image_picker_android: ^0.8.4+11 image_picker_for_web: ^2.1.0 - image_picker_ios: - path: ../image_picker_ios + image_picker_ios: ^0.8.4+11 image_picker_platform_interface: ^2.3.0 dev_dependencies: From 029c9998b8546eaa5e2c95bcb5a69b9c5ea7716d Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 5 Apr 2022 19:31:08 -0400 Subject: [PATCH 085/844] [espresso] Updates androidx.test.ext dependencies (#5178) --- .ci/flutter_master.version | 2 +- packages/espresso/CHANGELOG.md | 4 +++- packages/espresso/android/build.gradle | 4 ++-- packages/espresso/pubspec.yaml | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index ef23ed91e5bb..ac353e2ed1d7 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -66ed64be4fd8349cf47bf9b86f4d74ae3b4198f8 +5c6918933e15672e216675fc2e0e06bf8307a55a diff --git a/packages/espresso/CHANGELOG.md b/packages/espresso/CHANGELOG.md index b76cf2ab141b..eb1f267ca1d3 100644 --- a/packages/espresso/CHANGELOG.md +++ b/packages/espresso/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.2.0+1 * Adds OS version support information to README. +* Updates `androidx.test.ext:junit` and `androidx.test.ext:truth` for + compatibilty with updated Flutter template. ## 0.2.0 diff --git a/packages/espresso/android/build.gradle b/packages/espresso/android/build.gradle index 097e53f1c90b..62baba0ac31b 100644 --- a/packages/espresso/android/build.gradle +++ b/packages/espresso/android/build.gradle @@ -67,8 +67,8 @@ dependencies { api 'androidx.test:rules:1.1.0' // Assertions - api 'androidx.test.ext:junit:1.0.0' - api 'androidx.test.ext:truth:1.0.0' + api 'androidx.test.ext:junit:1.1.3' + api 'androidx.test.ext:truth:1.4.0' api 'com.google.truth:truth:0.42' // Espresso dependencies diff --git a/packages/espresso/pubspec.yaml b/packages/espresso/pubspec.yaml index 0bbf3c01158e..7737fc46d4b6 100644 --- a/packages/espresso/pubspec.yaml +++ b/packages/espresso/pubspec.yaml @@ -3,7 +3,7 @@ description: Java classes for testing Flutter apps using Espresso. Allows driving Flutter widgets from a native Espresso test. repository: https://github.com/flutter/plugins/tree/main/packages/espresso issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+espresso%22 -version: 0.2.0 +version: 0.2.0+1 environment: sdk: ">=2.12.0 <3.0.0" From 1b9df5c05572cbabe7bc432b8bab6de9bab3dbb4 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 5 Apr 2022 20:56:12 -0400 Subject: [PATCH 086/844] Roll Flutter from 5c6918933e15 to 5788f5ef07c2 (61 revisions) (#5179) --- .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 ac353e2ed1d7..e543fb794641 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -5c6918933e15672e216675fc2e0e06bf8307a55a +5788f5ef07c202c252de7d8050e6ab8d4c7652bd From c489d243f2ce46fe562e87863af06b61ca4be712 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 6 Apr 2022 01:01:07 -0400 Subject: [PATCH 087/844] Roll Flutter from 5788f5ef07c2 to 08c46bcb58cf (1 revision) (#5180) --- .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 e543fb794641..75cc696990b1 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -5788f5ef07c202c252de7d8050e6ab8d4c7652bd +08c46bcb58cf10cd101a351950e05eecaeb2b701 From 828a56a501bc8a72aed476e6e9fd47a9826a43d7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 6 Apr 2022 11:36:11 -0400 Subject: [PATCH 088/844] Roll Flutter from 08c46bcb58cf to 120b3deb182c (1 revision) (#5181) --- .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 75cc696990b1..1998e56b9aff 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -08c46bcb58cf10cd101a351950e05eecaeb2b701 +120b3deb182c04cf09b509aea7952a74eb2d716d From 90fef1b9fefa1374cf0329fa14097e4807193131 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 6 Apr 2022 12:41:17 -0400 Subject: [PATCH 089/844] Roll Flutter from 120b3deb182c to 94fefaa49d86 (1 revision) (#5182) --- .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 1998e56b9aff..9a17191a84be 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -120b3deb182c04cf09b509aea7952a74eb2d716d +94fefaa49d86fde856146c05d6817382a2fe7b3c From 17843017baa9b4967497cdc17c2095b0867fea6b Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 6 Apr 2022 13:46:11 -0400 Subject: [PATCH 090/844] Roll Flutter from 94fefaa49d86 to 676edd4c828f (6 revisions) (#5183) --- .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 9a17191a84be..aec0c3b24f26 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -94fefaa49d86fde856146c05d6817382a2fe7b3c +676edd4c828f2c4d1fe69d610b3a0290be69ebd6 From eaef38109cc6a3c1272f0a861cfb0b7b756feb0a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 6 Apr 2022 14:51:11 -0400 Subject: [PATCH 091/844] Roll Flutter from 676edd4c828f to f8e528a4ef80 (8 revisions) (#5185) --- .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 aec0c3b24f26..259d5dd8ef87 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -676edd4c828f2c4d1fe69d610b3a0290be69ebd6 +f8e528a4ef8069fa964bf41b38c40c2da55b15a3 From af6b227474e79710c9242dafcc799956f1affbb9 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 6 Apr 2022 15:56:11 -0400 Subject: [PATCH 092/844] Roll Flutter from f8e528a4ef80 to e382f1376a9b (2 revisions) (#5186) --- .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 259d5dd8ef87..2158e53edb2f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f8e528a4ef8069fa964bf41b38c40c2da55b15a3 +e382f1376a9b428f12036098c119d570e3f62651 From 00164cb3e5c82b1f0f0f64fd43edf9cb21d718d5 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 6 Apr 2022 17:01:20 -0400 Subject: [PATCH 093/844] Roll Flutter from e382f1376a9b to d08f7ab9c949 (3 revisions) (#5187) --- .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 2158e53edb2f..ac2143591260 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e382f1376a9b428f12036098c119d570e3f62651 +d08f7ab9c949425845074809f42086523c236330 From 8ca3aa40254b007fd9bb7e52eb84ce54763e67cf Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 6 Apr 2022 15:16:40 -0700 Subject: [PATCH 094/844] [webview_flutter_wkwebview] Added support for the rest of the creation params (#5130) --- .../lib/src/foundation/foundation.dart | 2 +- .../lib/src/ui_kit/ui_kit.dart | 26 +- .../lib/src/web_kit/web_kit.dart | 49 +++- .../lib/src/web_kit_webview_widget.dart | 154 ++++++++--- .../test/src/web_kit_webview_widget_test.dart | 246 +++++++++++++++++- .../web_kit_webview_widget_test.mocks.dart | 129 +++++---- 6 files changed, 506 insertions(+), 100 deletions(-) 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 2d80ea20b636..466c03a6c8e9 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 @@ -156,7 +156,7 @@ class NSObject { } /// Informs the observing object when the value at the specified key path has changed. - set observeValue( + Future setObserveValue( void Function( String keyPath, NSObject object, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart index 9b7973e2ace5..08dd7541a9f4 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart @@ -5,12 +5,15 @@ import 'dart:async'; import 'dart:math'; +import 'package:flutter/painting.dart'; + +import '../foundation/foundation.dart'; import '../web_kit/web_kit.dart'; /// A view that allows the scrolling and zooming of its contained views. /// /// Wraps [UIScrollView](https://developer.apple.com/documentation/uikit/uiscrollview?language=objc). -class UIScrollView { +class UIScrollView extends UIView { /// Constructs a [UIScrollView] that is owned by [webView]. // TODO(bparrishMines): Remove ignore once constructor is implemented. // ignore: avoid_unused_constructor_parameters @@ -40,3 +43,24 @@ class UIScrollView { throw UnimplementedError(); } } + +/// Manages the content for a rectangular area on the screen. +/// +/// Wraps [UIView](https://developer.apple.com/documentation/uikit/uiview?language=objc). +class UIView extends NSObject { + /// The view’s background color. + /// + /// The default value is null, which results in a transparent background color. + /// + /// Sets [UIView.backgroundColor](https://developer.apple.com/documentation/uikit/uiview/1622591-backgroundcolor?language=objc). + Future setBackgroundColor(Color? color) { + throw UnimplementedError(); + } + + /// Determines whether the view is opaque. + /// + /// Sets [UIView.opaque](https://developer.apple.com/documentation/uikit/uiview?language=objc). + Future setOpaque(bool opaque) { + throw UnimplementedError(); + } +} 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 fb714f8a05d5..fb315cc847fb 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 @@ -192,6 +192,29 @@ class WKScriptMessage { final Object? body; } +/// Encapsulates the standard behaviors to apply to websites. +/// +/// Wraps [WKPreferences](https://developer.apple.com/documentation/webkit/wkpreferences?language=objc). +class WKPreferences { + /// Constructs a [WKPreferences]. + WKPreferences(); + + // A WKPreferences that is owned by configuration. + WKPreferences._fromWebViewConfiguration( + // TODO(bparrishMines): Remove ignore once constructor is implemented. + // ignore: avoid_unused_constructor_parameters + WKWebViewConfiguration configuration, + ); + + // TODO(bparrishMines): Deprecated for iOS 14.0+. Add support for alternative. + /// Sets whether JavaScript is enabled. + /// + /// The default value is true. + Future setJavaScriptEnabled(bool enabled) { + throw UnimplementedError(); + } +} + /// Manages cookies, disk and memory caches, and other types of data for a web view. /// /// Wraps [WKWebsiteDataStore](https://developer.apple.com/documentation/webkit/wkwebsitedatastore?language=objc). @@ -245,7 +268,7 @@ class WKUserContentController { WKUserContentController(); // A WKUserContentController that is owned by configuration. - WKUserContentController._fromWebViewConfiguretion( + WKUserContentController._fromWebViewConfiguration( // TODO(bparrishMines): Remove ignore once constructor is implemented. // ignore: avoid_unused_constructor_parameters WKWebViewConfiguration configuration, @@ -304,25 +327,31 @@ class WKUserContentController { /// Wraps [WKWebViewConfiguration](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration?language=objc). class WKWebViewConfiguration { /// Constructs a [WKWebViewConfiguration]. - WKWebViewConfiguration({required this.userContentController}); + WKWebViewConfiguration(); // A WKWebViewConfiguration that is owned by webView. // TODO(bparrishMines): Remove ignore once constructor is implemented. // ignore: avoid_unused_constructor_parameters - WKWebViewConfiguration._fromWebView(WKWebView webView) { - userContentController = - WKUserContentController._fromWebViewConfiguretion(this); - } - - /// Coordinates interactions between your app’s code and the webpage’s scripts and other content. - late final WKUserContentController userContentController; + WKWebViewConfiguration._fromWebView(WKWebView webView); late WKWebsiteDataStore _websiteDataStore = WKWebsiteDataStore._fromWebViewConfiguration(this); + late final WKUserContentController _userContentController = + WKUserContentController._fromWebViewConfiguration(this); + + late final WKPreferences _preferences = + WKPreferences._fromWebViewConfiguration(this); + /// Used to get and set the site’s cookies and to track the cached data objects. WKWebsiteDataStore get webSiteDataStore => _websiteDataStore; + /// Coordinates interactions between your app’s code and the webpage’s scripts and other content. + WKUserContentController get userContentController => _userContentController; + + /// Manages the preference-related settings for the web view. + WKPreferences get preferences => _preferences; + /// Used to get and set the site’s cookies and to track the cached data objects. /// /// Sets [WKWebViewConfiguration.webSiteDataStore](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/1395661-websitedatastore?language=objc). @@ -429,7 +458,7 @@ class WKNavigationDelegate { /// Object that displays interactive web content, such as for an in-app browser. /// /// Wraps [WKWebView](https://developer.apple.com/documentation/webkit/wkwebview?language=objc). -class WKWebView extends NSObject { +class WKWebView extends UIView { /// Constructs a [WKWebView]. /// /// [configuration] contains the configuration details for the web view. This diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 67b05c1f053a..d7cbfb4510b2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:math'; import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:path/path.dart' as path; @@ -86,23 +87,12 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { }) : super(callbacksHandler) { _setCreationParams( creationParams, - configuration: configuration ?? - WKWebViewConfiguration( - userContentController: WKUserContentController(), - ), + configuration: configuration ?? WKWebViewConfiguration(), ); - - webView.setUIDelegate(uiDelegate); - uiDelegate.setOnCreateWebView(( - WKWebViewConfiguration configuration, - WKNavigationAction navigationAction, - ) { - if (!navigationAction.targetFrame.isMainFrame) { - webView.loadRequest(navigationAction.request); - } - }); } + bool _zoomEnabled = true; + final Map _scriptMessageHandlers = {}; @@ -162,13 +152,37 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { webView = webViewProxy.createWebView(configuration); + webView.setUIDelegate(uiDelegate); + uiDelegate.setOnCreateWebView(( + WKWebViewConfiguration configuration, + WKNavigationAction navigationAction, + ) { + if (!navigationAction.targetFrame.isMainFrame) { + webView.loadRequest(navigationAction.request); + } + }); + await addJavascriptChannels(params.javascriptChannelNames); webView.setNavigationDelegate(navigationDelegate); + if (params.userAgent != null) { + webView.setCustomUserAgent(params.userAgent!); + } + if (params.webSettings != null) { updateSettings(params.webSettings!); } + + if (params.backgroundColor != null) { + webView.setOpaque(false); + webView.setBackgroundColor(Colors.transparent); + webView.scrollView.setBackgroundColor(params.backgroundColor!); + } + + if (params.initialUrl != null) { + await loadUrl(params.initialUrl!, null); + } } void _setWebViewConfiguration( @@ -343,12 +357,20 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { @override Future updateSettings(WebSettings setting) async { - if (setting.hasNavigationDelegate != null) { - _setHasNavigationDelegate(setting.hasNavigationDelegate!); - } - if (setting.hasProgressTracking != null) { - _setHasProgressTracking(setting.hasProgressTracking!); - } + await Future.wait(>[ + _setUserAgent(setting.userAgent), + if (setting.hasNavigationDelegate != null) + _setHasNavigationDelegate(setting.hasNavigationDelegate!), + if (setting.hasProgressTracking != null) + _setHasProgressTracking(setting.hasProgressTracking!), + if (setting.javascriptMode != null) + _setJavaScriptMode(setting.javascriptMode!), + if (setting.zoomEnabled != null) _setZoomEnabled(setting.zoomEnabled!), + if (setting.gestureNavigationEnabled != null) + webView.setAllowsBackForwardNavigationGestures( + setting.gestureNavigationEnabled!, + ), + ]); } @override @@ -400,24 +422,12 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { return; } - // WKWebView does not support removing a single user script, so this removes - // all user scripts and all message handlers and re-registers channels that - // shouldn't be removed. Note that this workaround could interfere with - // exposing support for custom scripts from applications. - webView.configuration.userContentController.removeAllUserScripts(); - webView.configuration.userContentController - .removeAllScriptMessageHandlers(); - - javascriptChannelNames.forEach(_scriptMessageHandlers.remove); - final Set remainingNames = _scriptMessageHandlers.keys.toSet(); - _scriptMessageHandlers.clear(); - - await addJavascriptChannels(remainingNames); + await _resetUserScripts(removedJavaScriptChannels: javascriptChannelNames); } - void _setHasNavigationDelegate(bool hasNavigationDelegate) { + Future _setHasNavigationDelegate(bool hasNavigationDelegate) { if (hasNavigationDelegate) { - navigationDelegate.setDecidePolicyForNavigationAction( + return navigationDelegate.setDecidePolicyForNavigationAction( (WKWebView webView, WKNavigationAction action) async { final bool allow = await callbacksHandler.onNavigationRequest( url: action.request.url, @@ -429,20 +439,20 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { : WKNavigationActionPolicy.cancel; }); } else { - navigationDelegate.setDecidePolicyForNavigationAction(null); + return navigationDelegate.setDecidePolicyForNavigationAction(null); } } Future _setHasProgressTracking(bool hasProgressTracking) { if (hasProgressTracking) { - webView.observeValue = ( + webView.setObserveValue(( String keyPath, NSObject object, Map change, ) { final double progress = change[NSKeyValueChangeKey.newValue]! as double; callbacksHandler.onProgress((progress * 100).round()); - }; + }); return webView.addObserver( webView, keyPath: 'estimatedProgress', @@ -451,11 +461,77 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { }, ); } else { - webView.observeValue = null; + webView.setObserveValue(null); return webView.removeObserver(webView, keyPath: 'estimatedProgress'); } } + Future _setJavaScriptMode(JavascriptMode mode) { + switch (mode) { + case JavascriptMode.disabled: + return webView.configuration.preferences.setJavaScriptEnabled(false); + case JavascriptMode.unrestricted: + return webView.configuration.preferences.setJavaScriptEnabled(true); + } + } + + Future _setUserAgent(WebSetting userAgent) async { + if (userAgent.isPresent) { + await webView.setCustomUserAgent(userAgent.value); + } + } + + Future _setZoomEnabled(bool zoomEnabled) async { + if (_zoomEnabled == zoomEnabled) { + return; + } + + _zoomEnabled = zoomEnabled; + if (!zoomEnabled) { + return _disableZoom(); + } + + return _resetUserScripts(); + } + + Future _disableZoom() { + const WKUserScript userScript = WKUserScript( + "var meta = document.createElement('meta');" + "meta.name = 'viewport';" + "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0," + "user-scalable=no';" + "var head = document.getElementsByTagName('head')[0];head.appendChild(meta);", + WKUserScriptInjectionTime.atDocumentEnd, + isMainFrameOnly: true, + ); + return webView.configuration.userContentController + .addUserScript(userScript); + } + + // WkWebView does not support removing a single user script, so all user + // scripts and all message handlers are removed instead. And the JavaScript + // channels that shouldn't be removed are re-registered. Note that this + // workaround could interfere with exposing support for custom scripts from + // applications. + Future _resetUserScripts({ + Set removedJavaScriptChannels = const {}, + }) async { + webView.configuration.userContentController.removeAllUserScripts(); + webView.configuration.userContentController + .removeAllScriptMessageHandlers(); + + removedJavaScriptChannels.forEach(_scriptMessageHandlers.remove); + final Set remainingNames = _scriptMessageHandlers.keys.toSet(); + _scriptMessageHandlers.clear(); + + await Future.wait(>[ + addJavascriptChannels(remainingNames), + // Zoom is disabled with a WKUserScript, so this adds it back if it was + // removed above. + if (!_zoomEnabled) _disableZoom(), + ]); + } + static WebResourceError _toWebResourceError(NSError error) { WebResourceErrorType? errorType; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index e8b29dccb4e4..5d36e593fadb 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:math'; import 'dart:typed_data'; +import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -22,6 +23,7 @@ import 'web_kit_webview_widget_test.mocks.dart'; @GenerateMocks([ UIScrollView, WKNavigationDelegate, + WKPreferences, WKScriptMessageHandler, WKWebView, WKWebViewConfiguration, @@ -39,6 +41,7 @@ void main() { late MockWKWebView mockWebView; late MockWebViewWidgetProxy mockWebViewWidgetProxy; late MockWKUserContentController mockUserContentController; + late MockWKPreferences mockPreferences; late MockWKWebViewConfiguration mockWebViewConfiguration; late MockWKUIDelegate mockUIDelegate; late MockUIScrollView mockScrollView; @@ -54,6 +57,7 @@ void main() { mockWebView = MockWKWebView(); mockWebViewConfiguration = MockWKWebViewConfiguration(); mockUserContentController = MockWKUserContentController(); + mockPreferences = MockWKPreferences(); mockUIDelegate = MockWKUIDelegate(); mockScrollView = MockUIScrollView(); mockWebsiteDataStore = MockWKWebsiteDataStore(); @@ -68,6 +72,7 @@ void main() { when(mockWebViewConfiguration.userContentController).thenReturn( mockUserContentController, ); + when(mockWebViewConfiguration.preferences).thenReturn(mockPreferences); when(mockWebView.scrollView).thenReturn(mockScrollView); @@ -131,6 +136,55 @@ void main() { }); group('$CreationParams', () { + testWidgets('initialUrl', (WidgetTester tester) async { + await buildWidget( + tester, + creationParams: CreationParams( + initialUrl: 'https://www.google.com', + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + hasNavigationDelegate: false, + ), + ), + ); + final NSUrlRequest request = verify(mockWebView.loadRequest(captureAny)) + .captured + .single as NSUrlRequest; + expect(request.url, 'https://www.google.com'); + }); + + testWidgets('backgroundColor', (WidgetTester tester) async { + await buildWidget( + tester, + creationParams: CreationParams( + backgroundColor: Colors.red, + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + hasNavigationDelegate: false, + ), + ), + ); + + verify(mockWebView.setOpaque(false)); + verify(mockWebView.setBackgroundColor(Colors.transparent)); + verify(mockScrollView.setBackgroundColor(Colors.red)); + }); + + testWidgets('userAgent', (WidgetTester tester) async { + await buildWidget( + tester, + creationParams: CreationParams( + userAgent: 'MyUserAgent', + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + hasNavigationDelegate: false, + ), + ), + ); + + verify(mockWebView.setCustomUserAgent('MyUserAgent')); + }); + testWidgets('autoMediaPlaybackPolicy true', (WidgetTester tester) async { await buildWidget( tester, @@ -205,6 +259,158 @@ void main() { }); group('$WebSettings', () { + testWidgets('javascriptMode', (WidgetTester tester) async { + await buildWidget( + tester, + creationParams: CreationParams( + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + javascriptMode: JavascriptMode.unrestricted, + hasNavigationDelegate: false, + ), + ), + ); + + verify(mockPreferences.setJavaScriptEnabled(true)); + }); + + testWidgets('hasNavigationDelegate', (WidgetTester tester) async { + await buildWidget( + tester, + creationParams: CreationParams( + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + hasNavigationDelegate: true, + ), + ), + ); + + verify(mockNavigationDelegate + .setDecidePolicyForNavigationAction(argThat(isNotNull))); + }); + + testWidgets('userAgent', (WidgetTester tester) async { + await buildWidget( + tester, + creationParams: CreationParams( + webSettings: WebSettings( + userAgent: const WebSetting.of('myUserAgent'), + hasNavigationDelegate: false, + ), + ), + ); + + verify(mockWebView.setCustomUserAgent('myUserAgent')); + }); + + testWidgets( + 'enabling zoom re-adds JavaScript channels', + (WidgetTester tester) async { + when(mockWebViewWidgetProxy.createScriptMessageHandler()) + .thenReturn( + MockWKScriptMessageHandler(), + ); + + await buildWidget( + tester, + creationParams: CreationParams( + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + zoomEnabled: false, + hasNavigationDelegate: false, + ), + javascriptChannelNames: {'myChannel'}, + ), + ); + + clearInteractions(mockUserContentController); + + await testController.updateSettings(WebSettings( + userAgent: const WebSetting.absent(), + zoomEnabled: true, + )); + + final List javaScriptChannels = verifyInOrder([ + mockUserContentController.removeAllUserScripts(), + mockUserContentController.removeAllScriptMessageHandlers(), + mockUserContentController.addScriptMessageHandler( + captureAny, + captureAny, + ), + ]).captured[2]; + + expect( + javaScriptChannels[0], + isA(), + ); + expect(javaScriptChannels[1], 'myChannel'); + }, + ); + + testWidgets( + 'enabling zoom removes script', + (WidgetTester tester) async { + when(mockWebViewWidgetProxy.createScriptMessageHandler()) + .thenReturn( + MockWKScriptMessageHandler(), + ); + + await buildWidget( + tester, + creationParams: CreationParams( + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + zoomEnabled: false, + hasNavigationDelegate: false, + ), + ), + ); + + clearInteractions(mockUserContentController); + + await testController.updateSettings(WebSettings( + userAgent: const WebSetting.absent(), + zoomEnabled: true, + )); + + verify(mockUserContentController.removeAllUserScripts()); + verify(mockUserContentController.removeAllScriptMessageHandlers()); + verifyNever(mockUserContentController.addScriptMessageHandler( + any, + any, + )); + }, + ); + + testWidgets('zoomEnabled is false', (WidgetTester tester) async { + await buildWidget( + tester, + creationParams: CreationParams( + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + zoomEnabled: false, + hasNavigationDelegate: false, + ), + ), + ); + + final WKUserScript zoomScript = + verify(mockUserContentController.addUserScript(captureAny)) + .captured + .first as WKUserScript; + expect(zoomScript.isMainFrameOnly, isTrue); + expect(zoomScript.injectionTime, + WKUserScriptInjectionTime.atDocumentEnd); + expect( + zoomScript.source, + "var meta = document.createElement('meta');" + "meta.name = 'viewport';" + "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0," + "user-scalable=no';" + "var head = document.getElementsByTagName('head')[0];head.appendChild(meta);", + ); + }); + testWidgets('allowsInlineMediaPlayback', (WidgetTester tester) async { await buildWidget( tester, @@ -653,6 +859,44 @@ void main() { ); expect(userScripts[0].isMainFrameOnly, false); }); + + testWidgets('removeJavascriptChannels with zoom disabled', + (WidgetTester tester) async { + when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn( + MockWKScriptMessageHandler(), + ); + + await buildWidget( + tester, + creationParams: CreationParams( + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + zoomEnabled: false, + hasNavigationDelegate: false, + ), + ), + ); + + await testController.addJavascriptChannels({'c'}); + clearInteractions(mockUserContentController); + await testController.removeJavascriptChannels({'c'}); + + final WKUserScript zoomScript = + verify(mockUserContentController.addUserScript(captureAny)) + .captured + .first as WKUserScript; + expect(zoomScript.isMainFrameOnly, isTrue); + expect( + zoomScript.injectionTime, WKUserScriptInjectionTime.atDocumentEnd); + expect( + zoomScript.source, + "var meta = document.createElement('meta');" + "meta.name = 'viewport';" + "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0," + "user-scalable=no';" + "var head = document.getElementsByTagName('head')[0];head.appendChild(meta);", + ); + }); }); group('$WebViewPlatformCallbacksHandler', () { @@ -803,7 +1047,7 @@ void main() { testWidgets('onProgress', (WidgetTester tester) async { await buildWidget(tester, hasProgressTracking: true); final dynamic observeValue = - verify(mockWebView.observeValue = captureAny).captured.single + verify(mockWebView.setObserveValue(captureAny)).captured.single as void Function( String keyPath, NSObject object, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index 36b8af441e2e..8c8758d5139c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -1,22 +1,24 @@ // Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. +// in webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. // Do not manually edit this file. import 'dart:async' as _i5; import 'dart:math' as _i2; +import 'dart:ui' as _i6; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_platform_interface/src/types/javascript_channel.dart' - as _i8; -import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i9; + as _i9; +import 'package:webview_flutter_platform_interface/src/types/types.dart' + as _i10; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart' - as _i7; + as _i8; import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart' - as _i6; + as _i7; import 'package:webview_flutter_wkwebview/src/ui_kit/ui_kit.dart' as _i4; import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i3; import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart' - as _i10; + as _i11; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -35,20 +37,22 @@ class _FakeWKWebViewConfiguration_1 extends _i1.Fake class _FakeUIScrollView_2 extends _i1.Fake implements _i4.UIScrollView {} -class _FakeWKUserContentController_3 extends _i1.Fake +class _FakeWKWebsiteDataStore_3 extends _i1.Fake + implements _i3.WKWebsiteDataStore {} + +class _FakeWKUserContentController_4 extends _i1.Fake implements _i3.WKUserContentController {} -class _FakeWKWebsiteDataStore_4 extends _i1.Fake - implements _i3.WKWebsiteDataStore {} +class _FakeWKPreferences_5 extends _i1.Fake implements _i3.WKPreferences {} -class _FakeWKWebView_5 extends _i1.Fake implements _i3.WKWebView {} +class _FakeWKWebView_6 extends _i1.Fake implements _i3.WKWebView {} -class _FakeWKScriptMessageHandler_6 extends _i1.Fake +class _FakeWKScriptMessageHandler_7 extends _i1.Fake implements _i3.WKScriptMessageHandler {} -class _FakeWKUIDelegate_7 extends _i1.Fake implements _i3.WKUIDelegate {} +class _FakeWKUIDelegate_8 extends _i1.Fake implements _i3.WKUIDelegate {} -class _FakeWKNavigationDelegate_8 extends _i1.Fake +class _FakeWKNavigationDelegate_9 extends _i1.Fake implements _i3.WKNavigationDelegate {} /// A class which mocks [UIScrollView]. @@ -65,6 +69,11 @@ class MockUIScrollView extends _i1.Mock implements _i4.UIScrollView { returnValue: Future<_i2.Point>.value(_FakePoint_0())) as _i5.Future<_i2.Point>); @override + _i5.Future setBackgroundColor(_i6.Color? color) => + (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override _i5.Future scrollBy(_i2.Point? offset) => (super.noSuchMethod(Invocation.method(#scrollBy, [offset]), returnValue: Future.value(), @@ -113,14 +122,14 @@ class MockWKNavigationDelegate extends _i1.Mock returnValueForMissingStub: Future.value()) as _i5.Future); @override _i5.Future setDidFailNavigation( - void Function(_i3.WKWebView, _i6.NSError)? didFailNavigation) => + void Function(_i3.WKWebView, _i7.NSError)? didFailNavigation) => (super.noSuchMethod( Invocation.method(#setDidFailNavigation, [didFailNavigation]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); @override _i5.Future setDidFailProvisionalNavigation( - void Function(_i3.WKWebView, _i6.NSError)? + void Function(_i3.WKWebView, _i7.NSError)? didFailProvisionalNavigation) => (super.noSuchMethod( Invocation.method( @@ -137,6 +146,21 @@ class MockWKNavigationDelegate extends _i1.Mock returnValueForMissingStub: Future.value()) as _i5.Future); } +/// A class which mocks [WKPreferences]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWKPreferences extends _i1.Mock implements _i3.WKPreferences { + MockWKPreferences() { + _i1.throwOnMissingStub(this); + } + + @override + _i5.Future setJavaScriptEnabled(bool? enabled) => + (super.noSuchMethod(Invocation.method(#setJavaScriptEnabled, [enabled]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); +} + /// A class which mocks [WKScriptMessageHandler]. /// /// See the documentation for Mockito's code generation for more information. @@ -175,13 +199,6 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { (super.noSuchMethod(Invocation.getter(#scrollView), returnValue: _FakeUIScrollView_2()) as _i4.UIScrollView); @override - set observeValue( - void Function( - String, _i6.NSObject, Map<_i6.NSKeyValueChangeKey, Object?>)? - observeValue) => - super.noSuchMethod(Invocation.setter(#observeValue, observeValue), - returnValueForMissingStub: null); - @override _i5.Future setUIDelegate(_i3.WKUIDelegate? delegate) => (super.noSuchMethod(Invocation.method(#setUIDelegate, [delegate]), returnValue: Future.value(), @@ -200,7 +217,7 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { (super.noSuchMethod(Invocation.method(#getEstimatedProgress, []), returnValue: Future.value(0.0)) as _i5.Future); @override - _i5.Future loadRequest(_i6.NSUrlRequest? request) => + _i5.Future loadRequest(_i7.NSUrlRequest? request) => (super.noSuchMethod(Invocation.method(#loadRequest, [request]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); @@ -265,19 +282,37 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { .noSuchMethod(Invocation.method(#evaluateJavaScript, [javaScriptString]), returnValue: Future.value()) as _i5.Future); @override - _i5.Future addObserver(_i6.NSObject? observer, - {String? keyPath, Set<_i6.NSKeyValueObservingOptions>? options}) => + _i5.Future setBackgroundColor(_i6.Color? color) => + (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setOpaque(bool? opaque) => + (super.noSuchMethod(Invocation.method(#setOpaque, [opaque]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => (super.noSuchMethod( Invocation.method( #addObserver, [observer], {#keyPath: keyPath, #options: options}), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i5.Future removeObserver(_i6.NSObject? observer, {String? keyPath}) => + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => (super.noSuchMethod( Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setObserveValue( + void Function( + String, _i7.NSObject, Map<_i7.NSKeyValueChangeKey, Object?>)? + observeValue) => + (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKWebViewConfiguration]. @@ -289,21 +324,19 @@ class MockWKWebViewConfiguration extends _i1.Mock _i1.throwOnMissingStub(this); } + @override + _i3.WKWebsiteDataStore get webSiteDataStore => + (super.noSuchMethod(Invocation.getter(#webSiteDataStore), + returnValue: _FakeWKWebsiteDataStore_3()) as _i3.WKWebsiteDataStore); @override _i3.WKUserContentController get userContentController => (super.noSuchMethod(Invocation.getter(#userContentController), - returnValue: _FakeWKUserContentController_3()) + returnValue: _FakeWKUserContentController_4()) as _i3.WKUserContentController); @override - set userContentController( - _i3.WKUserContentController? _userContentController) => - super.noSuchMethod( - Invocation.setter(#userContentController, _userContentController), - returnValueForMissingStub: null); - @override - _i3.WKWebsiteDataStore get webSiteDataStore => - (super.noSuchMethod(Invocation.getter(#webSiteDataStore), - returnValue: _FakeWKWebsiteDataStore_4()) as _i3.WKWebsiteDataStore); + _i3.WKPreferences get preferences => + (super.noSuchMethod(Invocation.getter(#preferences), + returnValue: _FakeWKPreferences_5()) as _i3.WKPreferences); @override _i5.Future setWebSiteDataStore( _i3.WKWebsiteDataStore? websiteDataStore) => @@ -404,23 +437,23 @@ class MockWKUserContentController extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockJavascriptChannelRegistry extends _i1.Mock - implements _i7.JavascriptChannelRegistry { + implements _i8.JavascriptChannelRegistry { MockJavascriptChannelRegistry() { _i1.throwOnMissingStub(this); } @override - Map get channels => + Map get channels => (super.noSuchMethod(Invocation.getter(#channels), - returnValue: {}) - as Map); + returnValue: {}) + as Map); @override void onJavascriptChannelMessage(String? channel, String? message) => super.noSuchMethod( Invocation.method(#onJavascriptChannelMessage, [channel, message]), returnValueForMissingStub: null); @override - void updateJavascriptChannelsFromSet(Set<_i8.JavascriptChannel>? channels) => + void updateJavascriptChannelsFromSet(Set<_i9.JavascriptChannel>? channels) => super.noSuchMethod( Invocation.method(#updateJavascriptChannelsFromSet, [channels]), returnValueForMissingStub: null); @@ -430,7 +463,7 @@ class MockJavascriptChannelRegistry extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewPlatformCallbacksHandler extends _i1.Mock - implements _i7.WebViewPlatformCallbacksHandler { + implements _i8.WebViewPlatformCallbacksHandler { MockWebViewPlatformCallbacksHandler() { _i1.throwOnMissingStub(this); } @@ -454,7 +487,7 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock super.noSuchMethod(Invocation.method(#onProgress, [progress]), returnValueForMissingStub: null); @override - void onWebResourceError(_i9.WebResourceError? error) => + void onWebResourceError(_i10.WebResourceError? error) => super.noSuchMethod(Invocation.method(#onWebResourceError, [error]), returnValueForMissingStub: null); } @@ -463,7 +496,7 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewWidgetProxy extends _i1.Mock - implements _i10.WebViewWidgetProxy { + implements _i11.WebViewWidgetProxy { MockWebViewWidgetProxy() { _i1.throwOnMissingStub(this); } @@ -471,18 +504,18 @@ class MockWebViewWidgetProxy extends _i1.Mock @override _i3.WKWebView createWebView(_i3.WKWebViewConfiguration? configuration) => (super.noSuchMethod(Invocation.method(#createWebView, [configuration]), - returnValue: _FakeWKWebView_5()) as _i3.WKWebView); + returnValue: _FakeWKWebView_6()) as _i3.WKWebView); @override _i3.WKScriptMessageHandler createScriptMessageHandler() => (super.noSuchMethod(Invocation.method(#createScriptMessageHandler, []), - returnValue: _FakeWKScriptMessageHandler_6()) + returnValue: _FakeWKScriptMessageHandler_7()) as _i3.WKScriptMessageHandler); @override _i3.WKUIDelegate createUIDelgate() => (super.noSuchMethod(Invocation.method(#createUIDelgate, []), - returnValue: _FakeWKUIDelegate_7()) as _i3.WKUIDelegate); + returnValue: _FakeWKUIDelegate_8()) as _i3.WKUIDelegate); @override _i3.WKNavigationDelegate createNavigationDelegate() => (super.noSuchMethod( Invocation.method(#createNavigationDelegate, []), - returnValue: _FakeWKNavigationDelegate_8()) as _i3.WKNavigationDelegate); + returnValue: _FakeWKNavigationDelegate_9()) as _i3.WKNavigationDelegate); } From b5a1d7ecf74d6587caf305658dbda668e5027458 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 7 Apr 2022 08:51:08 -0400 Subject: [PATCH 095/844] Roll Flutter from d08f7ab9c949 to 2999e8531569 (6 revisions) (#5190) --- .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 ac2143591260..7f64f4658e1e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -d08f7ab9c949425845074809f42086523c236330 +2999e8531569bf57c1eb8c771e503adaeeffea8a From 9e874339eab4a985302e783599bed5b53e4fb0c6 Mon Sep 17 00:00:00 2001 From: Viren Khatri <44755140+werainkhatri@users.noreply.github.com> Date: Thu, 7 Apr 2022 19:56:12 +0530 Subject: [PATCH 096/844] migrate from ui.hash* to Object.hash* (#4994) --- packages/camera/camera_web/CHANGELOG.md | 3 ++- .../camera_web/lib/src/types/camera_metadata.dart | 4 +--- .../camera_web/lib/src/types/camera_options.dart | 10 ++++------ .../lib/src/types/zoom_level_capability.dart | 3 +-- packages/camera/camera_web/pubspec.yaml | 2 +- .../CHANGELOG.md | 5 +++++ .../lib/src/types/camera.dart | 4 ++-- .../lib/src/types/location.dart | 6 ++---- .../lib/src/types/maps_object_updates.dart | 6 ++---- .../lib/src/types/marker.dart | 4 ++-- .../lib/src/types/screen_coordinate.dart | 4 +--- .../lib/src/types/tile_overlay.dart | 4 +--- .../lib/src/types/ui.dart | 4 +--- .../pubspec.yaml | 4 ++-- .../test/types/maps_object_updates_test.dart | 11 ++++------- .../test/types/test_maps_object.dart | 5 +---- .../test/types/tile_overlay_test.dart | 4 +--- .../test/types/tile_overlay_updates_test.dart | 10 ++++------ packages/google_sign_in/google_sign_in/CHANGELOG.md | 3 ++- .../google_sign_in/lib/google_sign_in.dart | 3 +-- packages/google_sign_in/google_sign_in/pubspec.yaml | 2 +- .../in_app_purchase_android/CHANGELOG.md | 5 +++++ .../src/billing_client_wrappers/purchase_wrapper.dart | 10 ++++------ .../billing_client_wrappers/sku_details_wrapper.dart | 8 +++----- .../in_app_purchase_android/pubspec.yaml | 4 ++-- .../in_app_purchase_storekit/CHANGELOG.md | 4 ++++ .../store_kit_wrappers/sk_payment_queue_wrapper.dart | 7 +++---- .../sk_payment_transaction_wrappers.dart | 6 ++---- .../src/store_kit_wrappers/sk_product_wrapper.dart | 11 +++++------ .../src/store_kit_wrappers/sk_storefront_wrapper.dart | 4 +--- .../in_app_purchase_storekit/pubspec.yaml | 2 +- packages/ios_platform_images/CHANGELOG.md | 3 ++- .../ios_platform_images/lib/ios_platform_images.dart | 2 +- packages/ios_platform_images/pubspec.yaml | 2 +- .../webview_flutter_platform_interface/CHANGELOG.md | 5 +++++ .../lib/src/types/web_settings.dart | 2 +- .../webview_flutter_platform_interface/pubspec.yaml | 4 ++-- 37 files changed, 83 insertions(+), 97 deletions(-) diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 9e486667c103..852e4a03fd43 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.2.1+4 +* Migrates from `ui.hash*` to `Object.hash*`. * Updates minimum Flutter version for changes in 0.2.1+3. ## 0.2.1+3 diff --git a/packages/camera/camera_web/lib/src/types/camera_metadata.dart b/packages/camera/camera_web/lib/src/types/camera_metadata.dart index c42dd3ad8b75..e5c6b3875b6a 100644 --- a/packages/camera/camera_web/lib/src/types/camera_metadata.dart +++ b/packages/camera/camera_web/lib/src/types/camera_metadata.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter/foundation.dart'; /// Metadata used along the camera description @@ -38,5 +36,5 @@ class CameraMetadata { } @override - int get hashCode => hashValues(deviceId.hashCode, facingMode.hashCode); + int get hashCode => Object.hash(deviceId.hashCode, facingMode.hashCode); } diff --git a/packages/camera/camera_web/lib/src/types/camera_options.dart b/packages/camera/camera_web/lib/src/types/camera_options.dart index 8fa40bdc1bb8..08491b56081b 100644 --- a/packages/camera/camera_web/lib/src/types/camera_options.dart +++ b/packages/camera/camera_web/lib/src/types/camera_options.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter/foundation.dart'; /// Options used to create a camera with the given @@ -50,7 +48,7 @@ class CameraOptions { } @override - int get hashCode => hashValues(audio, video); + int get hashCode => Object.hash(audio, video); } /// Indicates whether the audio track is requested. @@ -140,7 +138,7 @@ class VideoConstraints { } @override - int get hashCode => hashValues(facingMode, width, height, deviceId); + int get hashCode => Object.hash(facingMode, width, height, deviceId); } /// The camera type used in [FacingModeConstraint]. @@ -213,7 +211,7 @@ class FacingModeConstraint { } @override - int get hashCode => hashValues(ideal, exact); + int get hashCode => Object.hash(ideal, exact); } /// The size of the requested video track used in @@ -272,5 +270,5 @@ class VideoSizeConstraint { } @override - int get hashCode => hashValues(minimum, ideal, maximum); + int get hashCode => Object.hash(minimum, ideal, maximum); } diff --git a/packages/camera/camera_web/lib/src/types/zoom_level_capability.dart b/packages/camera/camera_web/lib/src/types/zoom_level_capability.dart index 9a868d2bc0dc..d20bd25108bb 100644 --- a/packages/camera/camera_web/lib/src/types/zoom_level_capability.dart +++ b/packages/camera/camera_web/lib/src/types/zoom_level_capability.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:html' as html; -import 'dart:ui' show hashValues; import 'package:flutter/foundation.dart'; @@ -46,5 +45,5 @@ class ZoomLevelCapability { } @override - int get hashCode => hashValues(minimum, maximum, videoTrack); + int get hashCode => Object.hash(minimum, maximum, videoTrack); } diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index cb6aa19c49f1..2d1a4508eb73 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.2.1+3 +version: 0.2.1+4 environment: sdk: ">=2.12.0 <3.0.0" 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 d1b4ef8c18cf..b955bc033210 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,8 @@ +## 2.1.6 + +* Migrates from `ui.hash*` to `Object.hash*`. +* Updates minimum Flutter version to 2.5.0. + ## 2.1.5 Removes dependency on `meta`. diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/camera.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/camera.dart index 7cb6369e7f59..89006eba6214 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/camera.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:ui' show hashValues, Offset; +import 'dart:ui' show Offset; import 'types.dart'; @@ -99,7 +99,7 @@ class CameraPosition { } @override - int get hashCode => hashValues(bearing, target, tilt, zoom); + int get hashCode => Object.hash(bearing, target, tilt, zoom); @override String toString() => diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/location.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/location.dart index e2ca635be75e..5bc4ca608f1a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/location.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/location.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter/foundation.dart' show visibleForTesting; /// A pair of latitude and longitude coordinates, stored as degrees. @@ -55,7 +53,7 @@ class LatLng { } @override - int get hashCode => hashValues(latitude, longitude); + int get hashCode => Object.hash(latitude, longitude); } /// A latitude/longitude aligned rectangle. @@ -132,5 +130,5 @@ class LatLngBounds { } @override - int get hashCode => hashValues(southwest, northeast); + int get hashCode => Object.hash(southwest, northeast); } diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object_updates.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object_updates.dart index 2e2eefa3d32e..3267cad8d8a2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object_updates.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object_updates.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues, hashList; - import 'package:flutter/foundation.dart' show objectRuntimeType, setEquals; import 'maps_object.dart'; @@ -114,8 +112,8 @@ class MapsObjectUpdates { } @override - int get hashCode => hashValues(hashList(_objectsToAdd), - hashList(_objectIdsToRemove), hashList(_objectsToChange)); + int get hashCode => Object.hash(Object.hashAll(_objectsToAdd), + Object.hashAll(_objectIdsToRemove), Object.hashAll(_objectsToChange)); @override String toString() { 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 5e62f7d9ffba..8057d2962e9e 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 @@ -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:ui' show hashValues, Offset; +import 'dart:ui' show Offset; import 'package:flutter/foundation.dart' show immutable, ValueChanged, VoidCallback; @@ -90,7 +90,7 @@ class InfoWindow { } @override - int get hashCode => hashValues(title.hashCode, snippet, anchor); + int get hashCode => Object.hash(title.hashCode, snippet, anchor); @override String toString() { diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/screen_coordinate.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/screen_coordinate.dart index 46bc6c12e509..b424aa5c00e4 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/screen_coordinate.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/screen_coordinate.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter/foundation.dart' show immutable; /// Represents a point coordinate in the [GoogleMap]'s view. @@ -42,5 +40,5 @@ class ScreenCoordinate { } @override - int get hashCode => hashValues(x, y); + int get hashCode => Object.hash(x, y); } diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile_overlay.dart index db2b53d63512..80b05272e21d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile_overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile_overlay.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter/foundation.dart' show immutable; import 'types.dart'; @@ -146,6 +144,6 @@ class TileOverlay implements MapsObject { } @override - int get hashCode => hashValues(tileOverlayId, fadeIn, tileProvider, + int get hashCode => Object.hash(tileOverlayId, fadeIn, tileProvider, transparency, zIndex, visible, tileSize); } diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/ui.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/ui.dart index 38c34fcfd27f..18f88b910b48 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/ui.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/ui.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'types.dart'; /// Type of map tiles to display. @@ -97,7 +95,7 @@ class MinMaxZoomPreference { } @override - int get hashCode => hashValues(minZoom, maxZoom); + int get hashCode => Object.hash(minZoom, maxZoom); @override String toString() { 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 6125dd43d9f6..8df31fcf626b 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,11 +4,11 @@ repository: https://github.com/flutter/plugins/tree/main/packages/google_maps_fl 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.1.5 +version: 2.1.6 environment: sdk: '>=2.12.0 <3.0.0' - flutter: ">=2.0.0" + flutter: ">=2.5.0" dependencies: collection: ^1.15.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/maps_object_updates_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/maps_object_updates_test.dart index f09f70fd769e..73ed1d9d1a90 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/maps_object_updates_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/maps_object_updates_test.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues, hashList; - -import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter_platform_interface/src/types/maps_object.dart'; import 'package:google_maps_flutter_platform_interface/src/types/maps_object_updates.dart'; @@ -129,10 +126,10 @@ void main() { TestMapsObjectUpdate.from(previous, current); expect( updates.hashCode, - hashValues( - hashList(updates.objectsToAdd), - hashList(updates.objectIdsToRemove), - hashList(updates.objectsToChange))); + Object.hash( + Object.hashAll(updates.objectsToAdd), + Object.hashAll(updates.objectIdsToRemove), + Object.hashAll(updates.objectsToChange))); }); test('toString', () async { diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/test_maps_object.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/test_maps_object.dart index b95ae50a8f08..e28da7ab79ad 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/test_maps_object.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/test_maps_object.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - -import 'package:flutter/rendering.dart'; import 'package:google_maps_flutter_platform_interface/src/types/maps_object.dart'; import 'package:google_maps_flutter_platform_interface/src/types/maps_object_updates.dart'; @@ -37,7 +34,7 @@ class TestMapsObject implements MapsObject { } @override - int get hashCode => hashValues(mapsId, data); + int get hashCode => Object.hash(mapsId, data); } class TestMapsObjectUpdate extends MapsObjectUpdates { diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_test.dart index 3a4c34764ef7..c3ccf695032b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; @@ -130,7 +128,7 @@ void main() { tileSize: 128); expect( tileOverlay.hashCode, - hashValues( + Object.hash( tileOverlay.tileOverlayId, tileOverlay.fadeIn, tileOverlay.tileProvider, diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_updates_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_updates_test.dart index 05be14e1ba0b..fbb345c50563 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_updates_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_updates_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues, hashList; - import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter_platform_interface/src/types/tile_overlay.dart'; import 'package:google_maps_flutter_platform_interface/src/types/tile_overlay_updates.dart'; @@ -98,10 +96,10 @@ void main() { TileOverlayUpdates.from(previous, current); expect( updates.hashCode, - hashValues( - hashList(updates.tileOverlaysToAdd), - hashList(updates.tileOverlayIdsToRemove), - hashList(updates.tileOverlaysToChange))); + Object.hash( + Object.hashAll(updates.tileOverlaysToAdd), + Object.hashAll(updates.tileOverlayIdsToRemove), + Object.hashAll(updates.tileOverlaysToChange))); }); test('toString', () async { diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index c0a377603b34..24b729c4d78a 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 5.2.5 +* Migrates from `ui.hash*` to `Object.hash*`. * Adds OS version support information to README. ## 5.2.4 diff --git a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart index 6afd409807fa..228f34b651c5 100644 --- a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart +++ b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:ui' show hashValues; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart' show PlatformException; @@ -148,7 +147,7 @@ class GoogleSignInAccount implements GoogleIdentity { @override int get hashCode => - hashValues(displayName, email, id, photoUrl, _idToken, serverAuthCode); + Object.hash(displayName, email, id, photoUrl, _idToken, serverAuthCode); @override String toString() { diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index de15d0bb0740..2c5de81f8e98 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/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 and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.2.4 +version: 5.2.5 environment: sdk: ">=2.14.0 <3.0.0" 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 a871cfafb4a2..626dba704ad7 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.2.2+3 + +* Migrates from `ui.hash*` to `Object.hash*`. +* Updates minimum Flutter version to 2.5.0. + ## 0.2.2+2 * Internal code cleanup for stricter analysis options. diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.dart index 653e5147f9b0..efaf07984df7 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter/foundation.dart'; import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -71,7 +69,7 @@ class PurchaseWrapper { } @override - int get hashCode => hashValues( + int get hashCode => Object.hash( orderId, packageName, purchaseTime, @@ -238,7 +236,7 @@ class PurchaseHistoryRecordWrapper { } @override - int get hashCode => hashValues(purchaseTime, purchaseToken, signature, sku, + int get hashCode => Object.hash(purchaseTime, purchaseToken, signature, sku, originalJson, developerPayload); } @@ -278,7 +276,7 @@ class PurchasesResultWrapper { } @override - int get hashCode => hashValues(billingResult, responseCode, purchasesList); + int get hashCode => Object.hash(billingResult, responseCode, purchasesList); /// The detailed description of the status of the operation. final BillingResultWrapper billingResult; @@ -326,7 +324,7 @@ class PurchasesHistoryResult { } @override - int get hashCode => hashValues(billingResult, purchaseHistoryRecordList); + int get hashCode => Object.hash(billingResult, purchaseHistoryRecordList); /// The detailed description of the status of the [BillingClient.queryPurchaseHistory]. final BillingResultWrapper billingResult; diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart index 53595c572901..07f9d8f29abf 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter/foundation.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -161,7 +159,7 @@ class SkuDetailsWrapper { @override int get hashCode { - return hashValues( + return Object.hash( description.hashCode, freeTrialPeriod.hashCode, introductoryPrice.hashCode, @@ -216,7 +214,7 @@ class SkuDetailsResponseWrapper { } @override - int get hashCode => hashValues(billingResult, skuDetailsList); + int get hashCode => Object.hash(billingResult, skuDetailsList); } /// Params containing the response code and the debug message from the Play Billing API response. @@ -261,5 +259,5 @@ class BillingResultWrapper { } @override - int get hashCode => hashValues(responseCode, debugMessage); + int get hashCode => Object.hash(responseCode, debugMessage); } 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 0a667c672945..62888e6dfb73 100644 --- a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml @@ -2,11 +2,11 @@ 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/plugins/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.2.2+2 +version: 0.2.2+3 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.5.0" flutter: plugin: 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 d614baaf2305..4af33f177799 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.0+5 + +* Migrates from `ui.hash*` to `Object.hash*`. + ## 0.3.0+4 * Ensures that `NSError` instances with an unexpected value for the `userInfo` field don't crash the app, but send an explanatory message instead. 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 022848281327..819c291a1441 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 @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:ui' show hashValues; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; @@ -365,7 +364,7 @@ class SKError { } @override - int get hashCode => hashValues( + int get hashCode => Object.hash( code, domain, userInfo, @@ -482,7 +481,7 @@ class SKPaymentWrapper { } @override - int get hashCode => hashValues(productIdentifier, applicationUsername, + int get hashCode => Object.hash(productIdentifier, applicationUsername, quantity, simulatesAskToBuyInSandbox, requestData); @override @@ -585,5 +584,5 @@ class SKPaymentDiscountWrapper { @override int get hashCode => - hashValues(identifier, keyIdentifier, nonce, signature, timestamp); + Object.hash(identifier, keyIdentifier, nonce, signature, timestamp); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart index 4c4c91257d9d..3894721a1f80 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_transaction_wrappers.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter/foundation.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -190,8 +188,8 @@ class SKPaymentTransactionWrapper { } @override - int get hashCode => hashValues(payment, transactionState, originalTransaction, - transactionTimeStamp, transactionIdentifier, error); + int get hashCode => Object.hash(payment, transactionState, + originalTransaction, transactionTimeStamp, transactionIdentifier, error); @override String toString() => _$SKPaymentTransactionWrapperToJson(this).toString(); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart index 105d999d8a69..2354563261fc 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.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 'dart:ui' show hashValues; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -64,7 +63,7 @@ class SkProductResponseWrapper { } @override - int get hashCode => hashValues(products, invalidProductIdentifiers); + int get hashCode => Object.hash(products, invalidProductIdentifiers); } /// Dart wrapper around StoreKit's [SKProductPeriodUnit](https://developer.apple.com/documentation/storekit/skproductperiodunit?language=objc). @@ -142,7 +141,7 @@ class SKProductSubscriptionPeriodWrapper { } @override - int get hashCode => hashValues(numberOfUnits, unit); + int get hashCode => Object.hash(numberOfUnits, unit); } /// Dart wrapper around StoreKit's [SKProductDiscountPaymentMode](https://developer.apple.com/documentation/storekit/skproductdiscountpaymentmode?language=objc). @@ -232,7 +231,7 @@ class SKProductDiscountWrapper { } @override - int get hashCode => hashValues( + int get hashCode => Object.hash( price, priceLocale, numberOfPeriods, paymentMode, subscriptionPeriod); } @@ -342,7 +341,7 @@ class SKProductWrapper { } @override - int get hashCode => hashValues( + int get hashCode => Object.hash( productIdentifier, localizedTitle, localizedDescription, @@ -410,5 +409,5 @@ class SKPriceLocaleWrapper { } @override - int get hashCode => hashValues(currencySymbol, currencyCode); + int get hashCode => Object.hash(currencySymbol, currencyCode); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart index 0bf1103a5abd..ff9e9b7db746 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_storefront_wrapper.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui' show hashValues; - import 'package:flutter/foundation.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -56,7 +54,7 @@ class SKStorefrontWrapper { } @override - int get hashCode => hashValues( + int get hashCode => Object.hash( countryCode, identifier, ); 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 512d2a6ba036..9693c186119c 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 platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/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.0+4 +version: 0.3.0+5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index f2fb43dec1d1..5e7d997cc711 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.2.0+5 +* Migrates from `ui.hash*` to `Object.hash*`. * Adds OS version support information to README. ## 0.2.0+4 diff --git a/packages/ios_platform_images/lib/ios_platform_images.dart b/packages/ios_platform_images/lib/ios_platform_images.dart index 23a437d775ef..9632b3b2c05d 100644 --- a/packages/ios_platform_images/lib/ios_platform_images.dart +++ b/packages/ios_platform_images/lib/ios_platform_images.dart @@ -95,7 +95,7 @@ class _FutureMemoryImage extends ImageProvider<_FutureMemoryImage> { /// See [ImageProvider.hashCode]. @override - int get hashCode => hashValues(_futureBytes.hashCode, _futureScale); + int get hashCode => Object.hash(_futureBytes.hashCode, _futureScale); /// See [ImageProvider.toString]. @override diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index fe7952abbc3b..7f80714e4c1c 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -2,7 +2,7 @@ name: ios_platform_images description: A plugin to share images between Flutter and iOS in add-to-app setups. repository: https://github.com/flutter/plugins/tree/main/packages/ios_platform_images issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+ios_platform_images%22 -version: 0.2.0+4 +version: 0.2.0+5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index 500bee7d2622..f3f612adac35 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.8.2 + +* Migrates from `ui.hash*` to `Object.hash*`. +* Updates minimum Flutter version to 2.5.0. + ## 1.8.1 * Update to use the `verify` method introduced in platform_plugin_interface 2.1.0. diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/web_settings.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/web_settings.dart index 57c0a482089c..102ab10ccea7 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/web_settings.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/web_settings.dart @@ -62,7 +62,7 @@ class WebSetting { } @override - int get hashCode => hashValues(_value, isPresent); + int get hashCode => Object.hash(_value, isPresent); } /// Settings for configuring a WebViewPlatform. diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml index 50b816d51d5d..f9e754931bb8 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml @@ -4,11 +4,11 @@ repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutte issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview_flutter%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.8.1 +version: 1.8.2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.5.0" dependencies: flutter: From 68e600e54aa0da732ca72dc459a109266a2ac4a7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 7 Apr 2022 11:11:06 -0400 Subject: [PATCH 097/844] Roll Flutter from 2999e8531569 to f5f9ad915e55 (7 revisions) (#5193) --- .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 7f64f4658e1e..0001767b4015 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2999e8531569bf57c1eb8c771e503adaeeffea8a +f5f9ad915e554283ccff109fed969d9ef255fb91 From ab07a4ec3ebf6579f9d5422a8bfc166b2eedbe66 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 7 Apr 2022 09:52:51 -0700 Subject: [PATCH 098/844] [webview_flutter_wkwebview] Implement Dart side of the Pigeon HostApis (#5129) --- .../lib/src/common/instance_manager.dart | 52 + .../lib/src/common/web_kit.pigeon.dart | 1683 +++++++++++++++++ .../lib/src/foundation/foundation.dart | 27 +- .../src/foundation/foundation_api_impls.dart | 85 + .../lib/src/ui_kit/ui_kit.dart | 42 +- .../lib/src/ui_kit/ui_kit_api_impls.dart | 107 ++ .../lib/src/web_kit/web_kit.dart | 319 +++- .../lib/src/web_kit/web_kit_api_impls.dart | 549 ++++++ .../lib/src/web_kit_webview_widget.dart | 2 +- .../pigeons/web_kit.dart | 352 ++++ .../webview_flutter_wkwebview/pubspec.yaml | 1 + .../src/common/instance_manager_test.dart | 35 + .../test/src/common/test_web_kit.pigeon.dart | 1308 +++++++++++++ .../test/src/foundation/foundation_test.dart | 100 + .../src/foundation/foundation_test.mocks.dart | 48 + .../test/src/ui_kit/ui_kit_test.dart | 122 ++ .../test/src/ui_kit/ui_kit_test.mocks.dart | 200 ++ .../test/src/web_kit/web_kit_test.dart | 530 ++++++ .../test/src/web_kit/web_kit_test.mocks.dart | 287 +++ .../test/src/web_kit_webview_widget_test.dart | 2 +- .../web_kit_webview_widget_test.mocks.dart | 74 +- 21 files changed, 5806 insertions(+), 119 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/common/instance_manager_test.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.dart new file mode 100644 index 000000000000..830ba2e94935 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.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. + +/// Maintains instances stored to communicate with Objective-C objects. +class InstanceManager { + final Map _instanceIdsToInstances = {}; + final Map _instancesToInstanceIds = {}; + + int _nextInstanceId = 0; + + /// Global instance of [InstanceManager]. + static final InstanceManager instance = InstanceManager(); + + /// Attempt to add a new instance. + /// + /// Returns new if [instance] has already been added. Otherwise, it is added + /// with a new instance id. + int? tryAddInstance(Object instance) { + if (_instancesToInstanceIds.containsKey(instance)) { + return null; + } + + final int instanceId = _nextInstanceId++; + _instancesToInstanceIds[instance] = instanceId; + _instanceIdsToInstances[instanceId] = instance; + return instanceId; + } + + /// Remove the instance from the manager. + /// + /// Returns null if the instance is removed. Otherwise, return the instanceId + /// of the removed instance. + int? removeInstance(T instance) { + final int? instanceId = _instancesToInstanceIds[instance]; + if (instanceId != null) { + _instancesToInstanceIds.remove(instance); + _instanceIdsToInstances.remove(instanceId); + } + return instanceId; + } + + /// Retrieve the Object paired with instanceId. + T? getInstance(int instanceId) { + return _instanceIdsToInstances[instanceId] as T?; + } + + /// Retrieve the instanceId paired with instance. + int? getInstanceId(Object instance) { + return _instancesToInstanceIds[instance]; + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart new file mode 100644 index 000000000000..22013d2ff0cc --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart @@ -0,0 +1,1683 @@ +// 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 (v2.0.3), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; + +enum NSKeyValueObservingOptionsEnum { + newValue, + oldValue, + initialValue, + priorNotification, +} + +enum NSKeyValueChangeEnum { + setting, + insertion, + removal, + replacement, +} + +enum NSKeyValueChangeKeyEnum { + indexes, + kind, + newValue, + notificationIsPrior, + oldValue, +} + +enum WKUserScriptInjectionTimeEnum { + atDocumentStart, + atDocumentEnd, +} + +enum WKAudiovisualMediaTypeEnum { + none, + audio, + video, + all, +} + +enum WKWebsiteDataTypesEnum { + cookies, + memoryCache, + diskCache, + offlineWebApplicationCache, + localStroage, + sessionStorage, + sqlDatabases, + indexedDBDatabases, +} + +enum WKNavigationActionPolicyEnum { + allow, + cancel, +} + +class NSKeyValueObservingOptionsEnumData { + NSKeyValueObservingOptionsEnumData({ + this.value, + }); + + NSKeyValueObservingOptionsEnum? value; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['value'] = value == null ? null : value!.index; + return pigeonMap; + } + + static NSKeyValueObservingOptionsEnumData decode(Object message) { + final Map pigeonMap = message as Map; + return NSKeyValueObservingOptionsEnumData( + value: pigeonMap['value'] != null + ? NSKeyValueObservingOptionsEnum.values[pigeonMap['value']! as int] + : null, + ); + } +} + +class WKUserScriptInjectionTimeEnumData { + WKUserScriptInjectionTimeEnumData({ + this.value, + }); + + WKUserScriptInjectionTimeEnum? value; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['value'] = value == null ? null : value!.index; + return pigeonMap; + } + + static WKUserScriptInjectionTimeEnumData decode(Object message) { + final Map pigeonMap = message as Map; + return WKUserScriptInjectionTimeEnumData( + value: pigeonMap['value'] != null + ? WKUserScriptInjectionTimeEnum.values[pigeonMap['value']! as int] + : null, + ); + } +} + +class WKAudiovisualMediaTypeEnumData { + WKAudiovisualMediaTypeEnumData({ + this.value, + }); + + WKAudiovisualMediaTypeEnum? value; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['value'] = value == null ? null : value!.index; + return pigeonMap; + } + + static WKAudiovisualMediaTypeEnumData decode(Object message) { + final Map pigeonMap = message as Map; + return WKAudiovisualMediaTypeEnumData( + value: pigeonMap['value'] != null + ? WKAudiovisualMediaTypeEnum.values[pigeonMap['value']! as int] + : null, + ); + } +} + +class WKWebsiteDataTypesEnumData { + WKWebsiteDataTypesEnumData({ + this.value, + }); + + WKWebsiteDataTypesEnum? value; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['value'] = value == null ? null : value!.index; + return pigeonMap; + } + + static WKWebsiteDataTypesEnumData decode(Object message) { + final Map pigeonMap = message as Map; + return WKWebsiteDataTypesEnumData( + value: pigeonMap['value'] != null + ? WKWebsiteDataTypesEnum.values[pigeonMap['value']! as int] + : null, + ); + } +} + +class NSUrlRequestData { + NSUrlRequestData({ + required this.url, + this.httpMethod, + this.httpBody, + required this.allHttpHeaderFields, + }); + + String url; + String? httpMethod; + Uint8List? httpBody; + Map allHttpHeaderFields; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['url'] = url; + pigeonMap['httpMethod'] = httpMethod; + pigeonMap['httpBody'] = httpBody; + pigeonMap['allHttpHeaderFields'] = allHttpHeaderFields; + return pigeonMap; + } + + static NSUrlRequestData decode(Object message) { + final Map pigeonMap = message as Map; + return NSUrlRequestData( + url: pigeonMap['url']! as String, + httpMethod: pigeonMap['httpMethod'] as String?, + httpBody: pigeonMap['httpBody'] as Uint8List?, + allHttpHeaderFields: + (pigeonMap['allHttpHeaderFields'] as Map?)! + .cast(), + ); + } +} + +class WKUserScriptData { + WKUserScriptData({ + required this.source, + this.injectionTime, + required this.isMainFrameOnly, + }); + + String source; + WKUserScriptInjectionTimeEnumData? injectionTime; + bool isMainFrameOnly; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['source'] = source; + pigeonMap['injectionTime'] = + injectionTime == null ? null : injectionTime!.encode(); + pigeonMap['isMainFrameOnly'] = isMainFrameOnly; + return pigeonMap; + } + + static WKUserScriptData decode(Object message) { + final Map pigeonMap = message as Map; + return WKUserScriptData( + source: pigeonMap['source']! as String, + injectionTime: pigeonMap['injectionTime'] != null + ? WKUserScriptInjectionTimeEnumData.decode( + pigeonMap['injectionTime']!) + : null, + isMainFrameOnly: pigeonMap['isMainFrameOnly']! as bool, + ); + } +} + +class _WKWebsiteDataStoreHostApiCodec extends StandardMessageCodec { + const _WKWebsiteDataStoreHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is WKWebsiteDataTypesEnumData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return WKWebsiteDataTypesEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +class WKWebsiteDataStoreHostApi { + /// Constructor for [WKWebsiteDataStoreHostApi]. 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. + WKWebsiteDataStoreHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _WKWebsiteDataStoreHostApiCodec(); + + Future createFromWebViewConfiguration( + int arg_instanceId, int arg_configurationInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createFromWebViewConfiguration', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_configurationInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future removeDataOfTypes( + int arg_instanceId, + List arg_dataTypes, + double arg_secondsModifiedSinceEpoch) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel.send([ + arg_instanceId, + arg_dataTypes, + arg_secondsModifiedSinceEpoch + ]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _UIViewHostApiCodec extends StandardMessageCodec { + const _UIViewHostApiCodec(); +} + +class UIViewHostApi { + /// Constructor for [UIViewHostApi]. 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. + UIViewHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _UIViewHostApiCodec(); + + Future> getContentOffset(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIViewHostApi.getContentOffset', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as List?)!.cast(); + } + } + + Future setBackgroundColor(int arg_instanceId, int? arg_value) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIViewHostApi.setBackgroundColor', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_value]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setOpaque(int arg_instanceId, bool arg_opaque) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIViewHostApi.setOpaque', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_opaque]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _UIScrollViewHostApiCodec extends StandardMessageCodec { + const _UIScrollViewHostApiCodec(); +} + +class UIScrollViewHostApi { + /// Constructor for [UIScrollViewHostApi]. 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. + UIScrollViewHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _UIScrollViewHostApiCodec(); + + Future createFromWebView( + int arg_instanceId, int arg_webViewInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIScrollViewHostApi.createFromWebView', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_webViewInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future> getContentOffset(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIScrollViewHostApi.getContentOffset', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as List?)!.cast(); + } + } + + Future scrollBy(int arg_instanceId, double arg_x, double arg_y) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIScrollViewHostApi.scrollBy', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_x, arg_y]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setContentOffset( + int arg_instanceId, double arg_x, double arg_y) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIScrollViewHostApi.setContentOffset', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_x, arg_y]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _WKWebViewConfigurationHostApiCodec extends StandardMessageCodec { + const _WKWebViewConfigurationHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is WKAudiovisualMediaTypeEnumData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +class WKWebViewConfigurationHostApi { + /// Constructor for [WKWebViewConfigurationHostApi]. 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. + WKWebViewConfigurationHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = + _WKWebViewConfigurationHostApiCodec(); + + Future create(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.create', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future createFromWebView( + int arg_instanceId, int arg_webViewInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.createFromWebView', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_webViewInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setAllowsInlineMediaPlayback( + int arg_instanceId, bool arg_allow) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.setAllowsInlineMediaPlayback', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_allow]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setMediaTypesRequiringUserActionForPlayback(int arg_instanceId, + List arg_types) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.setMediaTypesRequiringUserActionForPlayback', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_types]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _WKUserContentControllerHostApiCodec extends StandardMessageCodec { + const _WKUserContentControllerHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is WKUserScriptData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is WKUserScriptInjectionTimeEnumData) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return WKUserScriptData.decode(readValue(buffer)!); + + case 129: + return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +class WKUserContentControllerHostApi { + /// Constructor for [WKUserContentControllerHostApi]. 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. + WKUserContentControllerHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = + _WKUserContentControllerHostApiCodec(); + + Future createFromWebViewConfiguration( + int arg_instanceId, int arg_configurationInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.createFromWebViewConfiguration', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_configurationInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future addScriptMessageHandler( + int arg_instanceId, int arg_handlerInstanceid, String arg_name) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.addScriptMessageHandler', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_handlerInstanceid, arg_name]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future removeScriptMessageHandler( + int arg_instanceId, String arg_name) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.removeScriptMessageHandler', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_name]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future removeAllScriptMessageHandlers(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllScriptMessageHandlers', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future addUserScript( + int arg_instanceId, WKUserScriptData arg_userScript) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.addUserScript', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_userScript]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future removeAllUserScripts(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllUserScripts', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _WKPreferencesHostApiCodec extends StandardMessageCodec { + const _WKPreferencesHostApiCodec(); +} + +class WKPreferencesHostApi { + /// Constructor for [WKPreferencesHostApi]. 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. + WKPreferencesHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _WKPreferencesHostApiCodec(); + + Future createFromWebViewConfiguration( + int arg_instanceId, int arg_configurationInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKPreferencesHostApi.createFromWebViewConfiguration', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_configurationInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setJavaScriptEnabled( + int arg_instanceId, bool arg_enabled) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKPreferencesHostApi.setJavaScriptEnabled', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_enabled]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _WKScriptMessageHandlerHostApiCodec extends StandardMessageCodec { + const _WKScriptMessageHandlerHostApiCodec(); +} + +class WKScriptMessageHandlerHostApi { + /// Constructor for [WKScriptMessageHandlerHostApi]. 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. + WKScriptMessageHandlerHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = + _WKScriptMessageHandlerHostApiCodec(); + + Future create(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKScriptMessageHandlerHostApi.create', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _WKNavigationDelegateHostApiCodec extends StandardMessageCodec { + const _WKNavigationDelegateHostApiCodec(); +} + +class WKNavigationDelegateHostApi { + /// Constructor for [WKNavigationDelegateHostApi]. 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. + WKNavigationDelegateHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = + _WKNavigationDelegateHostApiCodec(); + + Future create(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKNavigationDelegateHostApi.create', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _NSObjectHostApiCodec extends StandardMessageCodec { + const _NSObjectHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is NSKeyValueObservingOptionsEnumData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +class NSObjectHostApi { + /// Constructor for [NSObjectHostApi]. 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. + NSObjectHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _NSObjectHostApiCodec(); + + Future dispose(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NSObjectHostApi.dispose', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future addObserver( + int arg_instanceId, + int arg_observerInstanceId, + String arg_keyPath, + List arg_options) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NSObjectHostApi.addObserver', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel.send([ + arg_instanceId, + arg_observerInstanceId, + arg_keyPath, + arg_options + ]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future removeObserver(int arg_instanceId, int arg_observerInstanceId, + String arg_keyPath) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NSObjectHostApi.removeObserver', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel.send( + [arg_instanceId, arg_observerInstanceId, arg_keyPath]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _WKWebViewHostApiCodec extends StandardMessageCodec { + const _WKWebViewHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is NSUrlRequestData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return NSUrlRequestData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +class WKWebViewHostApi { + /// Constructor for [WKWebViewHostApi]. 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. + WKWebViewHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _WKWebViewHostApiCodec(); + + Future create( + int arg_instanceId, int arg_configurationInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.create', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_configurationInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setUIDelegate( + int arg_instanceId, int? arg_uiDelegateInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.setUIDelegate', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_uiDelegateInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setNavigationDelegate( + int arg_instanceId, int? arg_navigationDelegateInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.setNavigationDelegate', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_navigationDelegateInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future getUrl(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.getUrl', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as String?); + } + } + + Future getEstimatedProgress(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.getEstimatedProgress', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as double?)!; + } + } + + Future loadRequest( + int arg_instanceId, NSUrlRequestData arg_request) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.loadRequest', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_request]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future loadHtmlString( + int arg_instanceId, String arg_string, String? arg_baseUrl) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_string, arg_baseUrl]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future loadFileUrl( + int arg_instanceId, String arg_url, String arg_readAccessUrl) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.loadFileUrl', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_url, arg_readAccessUrl]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future loadFlutterAsset(int arg_instanceId, String arg_key) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.loadFlutterAsset', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_key]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future canGoBack(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.canGoBack', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as bool?)!; + } + } + + Future canGoForward(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.canGoForward', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as bool?)!; + } + } + + Future goBack(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.goBack', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future goForward(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.goForward', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future reload(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.reload', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future getTitle(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.getTitle', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as String?); + } + } + + Future setAllowsBackForwardNavigationGestures( + int arg_instanceId, bool arg_allow) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.setAllowsBackForwardNavigationGestures', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_allow]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setCustomUserAgent( + int arg_instanceId, String? arg_userAgent) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_userAgent]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future evaluateJavaScript( + int arg_instanceId, String arg_javascriptString) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_javascriptString]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as String?)!; + } + } +} + +class _WKUIDelegateHostApiCodec extends StandardMessageCodec { + const _WKUIDelegateHostApiCodec(); +} + +class WKUIDelegateHostApi { + /// Constructor for [WKUIDelegateHostApi]. 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. + WKUIDelegateHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _WKUIDelegateHostApiCodec(); + + Future create(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUIDelegateHostApi.create', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} 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 466c03a6c8e9..8aa315aa10ee 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 @@ -5,6 +5,10 @@ import 'dart:typed_data'; import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import '../common/instance_manager.dart'; +import 'foundation_api_impls.dart'; /// The values that can be returned in a change map. /// @@ -140,6 +144,15 @@ class NSError { /// The root class of most Objective-C class hierarchies. class NSObject { + /// Constructs an [NSObject]. + NSObject({BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) + : _api = NSObjectHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + + final NSObjectHostApiImpl _api; + /// Registers the observer object to receive KVO notifications. Future addObserver( NSObject observer, { @@ -147,12 +160,22 @@ class NSObject { required Set options, }) { assert(options.isNotEmpty); - throw UnimplementedError(); + return _api.addObserverForInstances( + this, + observer, + keyPath, + options, + ); } /// Stops the observer object from receiving change notifications for the property. Future removeObserver(NSObject observer, {required String keyPath}) { - throw UnimplementedError(); + return _api.removeObserverForInstances(this, observer, keyPath); + } + + /// Release the reference to the Objective-C object. + Future dispose() { + return _api.disposeForInstances(this); } /// Informs the observing object when the value at the specified key path has changed. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart new file mode 100644 index 000000000000..ec0d60cc748e --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart @@ -0,0 +1,85 @@ +// 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 '../common/instance_manager.dart'; +import '../common/web_kit.pigeon.dart'; +import 'foundation.dart'; + +Iterable + _toNSKeyValueObservingOptionsEnumData( + Iterable options, +) { + return options.map(( + NSKeyValueObservingOptions option, + ) { + late final NSKeyValueObservingOptionsEnum? value; + switch (option) { + case NSKeyValueObservingOptions.newValue: + value = NSKeyValueObservingOptionsEnum.newValue; + break; + case NSKeyValueObservingOptions.oldValue: + value = NSKeyValueObservingOptionsEnum.oldValue; + break; + case NSKeyValueObservingOptions.initialValue: + value = NSKeyValueObservingOptionsEnum.initialValue; + break; + case NSKeyValueObservingOptions.priorNotification: + value = NSKeyValueObservingOptionsEnum.priorNotification; + break; + } + + return NSKeyValueObservingOptionsEnumData(value: value); + }); +} + +/// Host api implementation for [NSObject]. +class NSObjectHostApiImpl extends NSObjectHostApi { + /// Constructs an [NSObjectHostApiImpl]. + NSObjectHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [addObserver] with the ids of the provided object instances. + Future addObserverForInstances( + NSObject instance, + NSObject observer, + String keyPath, + Set options, + ) { + return addObserver( + instanceManager.getInstanceId(instance)!, + instanceManager.getInstanceId(observer)!, + keyPath, + _toNSKeyValueObservingOptionsEnumData(options).toList(), + ); + } + + /// Calls [removeObserver] with the ids of the provided object instances. + Future removeObserverForInstances( + NSObject instance, + NSObject observer, + String keyPath, + ) { + return removeObserver( + instanceManager.getInstanceId(instance)!, + instanceManager.getInstanceId(observer)!, + keyPath, + ); + } + + /// Calls [dispose] with the ids of the provided object instances. + Future disposeForInstances(NSObject instance) async { + final int? instanceId = instanceManager.removeInstance(instance); + if (instanceId != null) { + await dispose(instanceId); + } + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart index 08dd7541a9f4..7c1bdd01724c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart @@ -2,28 +2,39 @@ // 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/painting.dart'; +import 'package:flutter/painting.dart' show Color; +import 'package:flutter/services.dart'; +import '../common/instance_manager.dart'; import '../foundation/foundation.dart'; import '../web_kit/web_kit.dart'; +import 'ui_kit_api_impls.dart'; /// A view that allows the scrolling and zooming of its contained views. /// /// Wraps [UIScrollView](https://developer.apple.com/documentation/uikit/uiscrollview?language=objc). class UIScrollView extends UIView { /// Constructs a [UIScrollView] that is owned by [webView]. - // TODO(bparrishMines): Remove ignore once constructor is implemented. - // ignore: avoid_unused_constructor_parameters - UIScrollView.fromWebView(WKWebView webView); + UIScrollView.fromWebView( + WKWebView webView, { + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _scrollViewApi = UIScrollViewHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _scrollViewApi.createFromWebViewForInstances(this, webView); + } + + final UIScrollViewHostApiImpl _scrollViewApi; /// Point at which the origin of the content view is offset from the origin of the scroll view. /// /// Represents [WKWebView.contentOffset](https://developer.apple.com/documentation/uikit/uiscrollview/1619404-contentoffset?language=objc). Future> getContentOffset() { - throw UnimplementedError(); + return _scrollViewApi.getContentOffsetForInstances(this); } /// Move the scrolled position of this view. @@ -31,7 +42,7 @@ class UIScrollView extends UIView { /// This method is not a part of UIKit and is only a helper method to make /// scrollBy atomic. Future scrollBy(Point offset) { - throw UnimplementedError(); + return _scrollViewApi.scrollByForInstances(this, offset); } /// Set point at which the origin of the content view is offset from the origin of the scroll view. @@ -39,8 +50,8 @@ class UIScrollView extends UIView { /// The default value is `Point(0.0, 0.0)`. /// /// Sets [WKWebView.contentOffset](https://developer.apple.com/documentation/uikit/uiscrollview/1619404-contentoffset?language=objc). - Future setContentOffset(FutureOr> offset) { - throw UnimplementedError(); + Future setContentOffset(Point offset) { + return _scrollViewApi.setContentOffsetForInstances(this, offset); } } @@ -48,19 +59,28 @@ class UIScrollView extends UIView { /// /// Wraps [UIView](https://developer.apple.com/documentation/uikit/uiview?language=objc). class UIView extends NSObject { + /// Constructs an [NSObject]. + UIView({BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) + : _viewApi = UIViewHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + + final UIViewHostApiImpl _viewApi; + /// The view’s background color. /// /// The default value is null, which results in a transparent background color. /// /// Sets [UIView.backgroundColor](https://developer.apple.com/documentation/uikit/uiview/1622591-backgroundcolor?language=objc). Future setBackgroundColor(Color? color) { - throw UnimplementedError(); + return _viewApi.setBackgroundColorForInstances(this, color); } /// Determines whether the view is opaque. /// /// Sets [UIView.opaque](https://developer.apple.com/documentation/uikit/uiview?language=objc). Future setOpaque(bool opaque) { - throw UnimplementedError(); + return _viewApi.setOpaqueForInstances(this, opaque); } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart new file mode 100644 index 000000000000..b2ca5672f8e2 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.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 'dart:async'; +import 'dart:math'; + +import 'package:flutter/painting.dart' show Color; +import 'package:flutter/services.dart'; + +import '../common/instance_manager.dart'; +import '../common/web_kit.pigeon.dart'; +import '../web_kit/web_kit.dart'; +import 'ui_kit.dart'; + +/// Host api implementation for [UIScrollView]. +class UIScrollViewHostApiImpl extends UIScrollViewHostApi { + /// Constructs a [UIScrollViewHostApiImpl]. + UIScrollViewHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [createFromWebView] with the ids of the provided object instances. + Future createFromWebViewForInstances( + UIScrollView instance, + WKWebView webView, + ) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await createFromWebView( + instanceId, + instanceManager.getInstanceId(webView)!, + ); + } + } + + /// Calls [getContentOffset] with the ids of the provided object instances. + Future> getContentOffsetForInstances( + UIScrollView instance, + ) async { + final List point = await getContentOffset( + instanceManager.getInstanceId(instance)!, + ); + return Point(point[0]!, point[1]!); + } + + /// Calls [scrollBy] with the ids of the provided object instances. + Future scrollByForInstances( + UIScrollView instance, + Point offset, + ) { + return scrollBy( + instanceManager.getInstanceId(instance)!, + offset.x, + offset.y, + ); + } + + /// Calls [setContentOffset] with the ids of the provided object instances. + Future setContentOffsetForInstances( + UIScrollView instance, + Point offset, + ) async { + return setContentOffset( + instanceManager.getInstanceId(instance)!, + offset.x, + offset.y, + ); + } +} + +/// Host api implementation for [UIView]. +class UIViewHostApiImpl extends UIViewHostApi { + /// Constructs a [UIViewHostApiImpl]. + UIViewHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [setBackgroundColor] with the ids of the provided object instances. + Future setBackgroundColorForInstances( + UIView instance, + Color? color, + ) async { + return setBackgroundColor( + instanceManager.getInstanceId(instance)!, + color?.value, + ); + } + + /// Calls [setOpaque] with the ids of the provided object instances. + Future setOpaqueForInstances( + UIView instance, + bool opaque, + ) async { + return setOpaque(instanceManager.getInstanceId(instance)!, opaque); + } +} 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 fb315cc847fb..0703ce1267d9 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 @@ -3,9 +3,12 @@ // found in the LICENSE file. import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import '../common/instance_manager.dart'; import '../foundation/foundation.dart'; import '../ui_kit/ui_kit.dart'; +import 'web_kit_api_impls.dart'; /// Times at which to inject script content into a webpage. /// @@ -196,22 +199,30 @@ class WKScriptMessage { /// /// Wraps [WKPreferences](https://developer.apple.com/documentation/webkit/wkpreferences?language=objc). class WKPreferences { - /// Constructs a [WKPreferences]. - WKPreferences(); - - // A WKPreferences that is owned by configuration. - WKPreferences._fromWebViewConfiguration( - // TODO(bparrishMines): Remove ignore once constructor is implemented. - // ignore: avoid_unused_constructor_parameters - WKWebViewConfiguration configuration, - ); + /// Constructs a [WKPreferences] that is owned by [configuration]. + @visibleForTesting + WKPreferences.fromWebViewConfiguration( + WKWebViewConfiguration configuration, { + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _preferencesApi = WKPreferencesHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _preferencesApi.createFromWebViewConfigurationForInstances( + this, + configuration, + ); + } + + final WKPreferencesHostApiImpl _preferencesApi; // TODO(bparrishMines): Deprecated for iOS 14.0+. Add support for alternative. /// Sets whether JavaScript is enabled. /// /// The default value is true. Future setJavaScriptEnabled(bool enabled) { - throw UnimplementedError(); + return _preferencesApi.setJavaScriptEnabledForInstances(this, enabled); } } @@ -219,18 +230,34 @@ class WKPreferences { /// /// Wraps [WKWebsiteDataStore](https://developer.apple.com/documentation/webkit/wkwebsitedatastore?language=objc). class WKWebsiteDataStore { - WKWebsiteDataStore._fromWebViewConfiguration( - // TODO(bparrishMines): Remove ignore once constructor is implemented. - // ignore: avoid_unused_constructor_parameters - WKWebViewConfiguration configuration, - ); + /// Constructs a [WKWebsiteDataStore] that is owned by [configuration]. + @visibleForTesting + WKWebsiteDataStore.fromWebViewConfiguration( + WKWebViewConfiguration configuration, { + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _websiteDataStoreApi = WKWebsiteDataStoreHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _websiteDataStoreApi.createFromWebViewConfigurationForInstances( + this, + configuration, + ); + } + + final WKWebsiteDataStoreHostApiImpl _websiteDataStoreApi; /// Removes website data that changed after the specified date. Future removeDataOfTypes( Set dataTypes, DateTime since, ) { - throw UnimplementedError(); + return _websiteDataStoreApi.removeDataOfTypesForInstances( + this, + dataTypes, + secondsModifiedSinceEpoch: since.millisecondsSinceEpoch / 1000, + ); } } @@ -238,6 +265,19 @@ class WKWebsiteDataStore { /// /// Wraps [WKScriptMessageHandler](https://developer.apple.com/documentation/webkit/wkscriptmessagehandler?language=objc) class WKScriptMessageHandler { + /// Constructs a [WKScriptMessageHandler]. + WKScriptMessageHandler({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _scriptMessageHandlerApi = WKScriptMessageHandlerHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _scriptMessageHandlerApi.createForInstances(this); + } + + final WKScriptMessageHandlerHostApiImpl _scriptMessageHandlerApi; + /// Tells the handler that a webpage sent a script message. /// /// Use this method to respond to a message sent from the webpage’s @@ -264,15 +304,23 @@ class WKScriptMessageHandler { /// /// Wraps [WKUserContentController](https://developer.apple.com/documentation/webkit/wkusercontentcontroller?language=objc). class WKUserContentController { - /// Constructs a [WKUserContentController]. - WKUserContentController(); - - // A WKUserContentController that is owned by configuration. - WKUserContentController._fromWebViewConfiguration( - // TODO(bparrishMines): Remove ignore once constructor is implemented. - // ignore: avoid_unused_constructor_parameters - WKWebViewConfiguration configuration, - ); + /// Constructs a [WKUserContentController] that is owned by [configuration]. + @visibleForTesting + WKUserContentController.fromWebViewConfiguration( + WKWebViewConfiguration configuration, { + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _userContentControllerApi = WKUserContentControllerHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _userContentControllerApi.createFromWebViewConfigurationForInstances( + this, + configuration, + ); + } + + final WKUserContentControllerHostApiImpl _userContentControllerApi; /// Installs a message handler that you can call from your JavaScript code. /// @@ -290,7 +338,11 @@ class WKUserContentController { String name, ) { assert(name.isNotEmpty); - throw UnimplementedError(); + return _userContentControllerApi.addScriptMessageHandlerForInstances( + this, + handler, + name, + ); } /// Uninstalls the custom message handler with the specified name from your JavaScript code. @@ -303,22 +355,28 @@ class WKUserContentController { /// message handler from the page content world. If you installed the message /// handler in a different content world, this method doesn’t remove it. Future removeScriptMessageHandler(String name) { - throw UnimplementedError(); + return _userContentControllerApi.removeScriptMessageHandlerForInstances( + this, + name, + ); } /// Uninstalls all custom message handlers associated with the user content controller. Future removeAllScriptMessageHandlers() { - throw UnimplementedError(); + return _userContentControllerApi.removeAllScriptMessageHandlersForInstances( + this, + ); } /// Injects the specified script into the webpage’s content. Future addUserScript(WKUserScript userScript) { - throw UnimplementedError(); + return _userContentControllerApi.addUserScriptForInstances( + this, userScript); } /// Removes all user scripts from the web view. Future removeAllUserScripts() { - throw UnimplementedError(); + return _userContentControllerApi.removeAllUserScriptsForInstances(this); } } @@ -327,44 +385,81 @@ class WKUserContentController { /// Wraps [WKWebViewConfiguration](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration?language=objc). class WKWebViewConfiguration { /// Constructs a [WKWebViewConfiguration]. - WKWebViewConfiguration(); - - // A WKWebViewConfiguration that is owned by webView. - // TODO(bparrishMines): Remove ignore once constructor is implemented. - // ignore: avoid_unused_constructor_parameters - WKWebViewConfiguration._fromWebView(WKWebView webView); - - late WKWebsiteDataStore _websiteDataStore = - WKWebsiteDataStore._fromWebViewConfiguration(this); - - late final WKUserContentController _userContentController = - WKUserContentController._fromWebViewConfiguration(this); - - late final WKPreferences _preferences = - WKPreferences._fromWebViewConfiguration(this); - - /// Used to get and set the site’s cookies and to track the cached data objects. - WKWebsiteDataStore get webSiteDataStore => _websiteDataStore; + factory WKWebViewConfiguration({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) { + final WKWebViewConfiguration configuration = WKWebViewConfiguration._( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + configuration._webViewConfigurationApi.createForInstances(configuration); + return configuration; + } + + /// A WKWebViewConfiguration that is owned by webView. + @visibleForTesting + factory WKWebViewConfiguration.fromWebView( + WKWebView webView, { + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) { + final WKWebViewConfiguration configuration = WKWebViewConfiguration._( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + configuration._webViewConfigurationApi.createFromWebViewForInstances( + configuration, + webView, + ); + return configuration; + } + + WKWebViewConfiguration._({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _binaryMessenger = binaryMessenger, + _instanceManager = instanceManager, + _webViewConfigurationApi = WKWebViewConfigurationHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + + final BinaryMessenger? _binaryMessenger; + final InstanceManager? _instanceManager; + + late final WKWebViewConfigurationHostApiImpl _webViewConfigurationApi; /// Coordinates interactions between your app’s code and the webpage’s scripts and other content. - WKUserContentController get userContentController => _userContentController; + late final WKUserContentController userContentController = + WKUserContentController.fromWebViewConfiguration( + this, + binaryMessenger: _binaryMessenger, + instanceManager: _instanceManager, + ); /// Manages the preference-related settings for the web view. - WKPreferences get preferences => _preferences; + late final WKPreferences preferences = + WKPreferences.fromWebViewConfiguration(this); /// Used to get and set the site’s cookies and to track the cached data objects. /// - /// Sets [WKWebViewConfiguration.webSiteDataStore](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/1395661-websitedatastore?language=objc). - Future setWebSiteDataStore(WKWebsiteDataStore websiteDataStore) { - _websiteDataStore = websiteDataStore; - throw UnimplementedError(); - } + /// Represents [WKWebViewConfiguration.webSiteDataStore](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/1395661-websitedatastore?language=objc). + late final WKWebsiteDataStore websiteDataStore = + WKWebsiteDataStore.fromWebViewConfiguration( + this, + binaryMessenger: _binaryMessenger, + instanceManager: _instanceManager, + ); /// Indicates whether HTML5 videos play inline or use the native full-screen controller. /// /// Sets [WKWebViewConfiguration.allowsInlineMediaPlayback](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/1614793-allowsinlinemediaplayback?language=objc). Future setAllowsInlineMediaPlayback(bool allow) { - throw UnimplementedError(); + return _webViewConfigurationApi.setAllowsInlineMediaPlaybackForInstances( + this, + allow, + ); } /// The media types that require a user gesture to begin playing. @@ -377,7 +472,11 @@ class WKWebViewConfiguration { Set types, ) { assert(types.isNotEmpty); - throw UnimplementedError(); + return _webViewConfigurationApi + .setMediaTypesRequiringUserActionForPlaybackForInstances( + this, + types, + ); } } @@ -385,7 +484,20 @@ class WKWebViewConfiguration { /// /// Wraps [WKUIDelegate](https://developer.apple.com/documentation/webkit/wkuidelegate?language=objc). class WKUIDelegate { - /// Indicates a new [WebView] was requested to be created with [configuration]. + /// Constructs a [WKUIDelegate]. + WKUIDelegate({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _uiDelegateApi = WKUIDelegateHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _uiDelegateApi.createForInstances(this); + } + + final WKUIDelegateHostApiImpl _uiDelegateApi; + + /// Indicates a new [WKWebView] was requested to be created with [configuration]. Future setOnCreateWebView( void Function( WKWebViewConfiguration configuration, @@ -404,6 +516,19 @@ class WKUIDelegate { /// /// Wraps [WKNavigationDelegate](https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc). class WKNavigationDelegate { + /// Constructs a [WKNavigationDelegate]. + WKNavigationDelegate({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _navigationDelegateApi.createForInstances(this); + } + + final WKNavigationDelegateHostApiImpl _navigationDelegateApi; + /// Called when navigation from the main frame has started. Future setDidStartProvisionalNavigation( void Function( @@ -468,11 +593,27 @@ class WKWebView extends UIView { /// values, see [WKWebViewConfiguration]. If you didn’t create your web view /// using the `configuration` parameter, this value uses a default /// configuration object. - // TODO(bparrishMines): Remove ignore once constructor is implemented. - // ignore: avoid_unused_constructor_parameters - WKWebView([WKWebViewConfiguration? configuration]) { - throw UnimplementedError(); - } + WKWebView( + WKWebViewConfiguration configuration, { + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _binaryMessenger = binaryMessenger, + _instanceManager = instanceManager, + _webViewApi = WKWebViewHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ), + super( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ) { + _webViewApi.createForInstances(this, configuration); + } + + final BinaryMessenger? _binaryMessenger; + final InstanceManager? _instanceManager; + + final WKWebViewHostApiImpl _webViewApi; /// Contains the configuration details for the web view. /// @@ -484,30 +625,38 @@ class WKWebView extends UIView { /// If you didn’t create your web view with a [WKWebViewConfiguration] this /// property contains a default configuration object. late final WKWebViewConfiguration configuration = - WKWebViewConfiguration._fromWebView(this); + WKWebViewConfiguration.fromWebView( + this, + binaryMessenger: _binaryMessenger, + instanceManager: _instanceManager, + ); /// The scrollable view associated with the web view. - late final UIScrollView scrollView = UIScrollView.fromWebView(this); + late final UIScrollView scrollView = UIScrollView.fromWebView( + this, + binaryMessenger: _binaryMessenger, + instanceManager: _instanceManager, + ); /// Used to integrate custom user interface elements into web view interactions. /// /// Sets [WKWebView.UIDelegate](https://developer.apple.com/documentation/webkit/wkwebview/1415009-uidelegate?language=objc). Future setUIDelegate(WKUIDelegate? delegate) { - throw UnimplementedError(); + return _webViewApi.setUIDelegateForInstances(this, delegate); } /// The object you use to manage navigation behavior for the web view. /// /// Sets [WKWebView.navigationDelegate](https://developer.apple.com/documentation/webkit/wkwebview/1414971-navigationdelegate?language=objc). Future setNavigationDelegate(WKNavigationDelegate? delegate) { - throw UnimplementedError(); + return _webViewApi.setNavigationDelegateForInstances(this, delegate); } /// The URL for the current webpage. /// /// Represents [WKWebView.URL](https://developer.apple.com/documentation/webkit/wkwebview/1415005-url?language=objc). Future getUrl() { - throw UnimplementedError(); + return _webViewApi.getUrlForInstances(this); } /// An estimate of what fraction of the current navigation has been loaded. @@ -516,7 +665,7 @@ class WKWebView extends UIView { /// /// Represents [WKWebView.estimatedProgress](https://developer.apple.com/documentation/webkit/wkwebview/1415007-estimatedprogress?language=objc). Future getEstimatedProgress() { - throw UnimplementedError(); + return _webViewApi.getEstimatedProgressForInstances(this); } /// Loads the web content referenced by the specified URL request object and navigates to it. @@ -524,17 +673,17 @@ class WKWebView extends UIView { /// Use this method to load a page from a local or network-based URL. For /// example, you might use it to navigate to a network-based webpage. Future loadRequest(NSUrlRequest request) { - throw UnimplementedError(); + return _webViewApi.loadRequestForInstances(this, request); } /// Loads the contents of the specified HTML string and navigates to it. Future loadHtmlString(String string, {String? baseUrl}) { - throw UnimplementedError(); + return _webViewApi.loadHtmlStringForInstances(this, string, baseUrl); } /// Loads the web content from the specified file and navigates to it. Future loadFileUrl(String url, {required String readAccessUrl}) { - throw UnimplementedError(); + return _webViewApi.loadFileUrlForInstances(this, url, readAccessUrl); } /// Loads the Flutter asset specified in the pubspec.yaml file. @@ -542,39 +691,39 @@ class WKWebView extends UIView { /// This method is not a part of WebKit and is only a Flutter specific helper /// method. Future loadFlutterAsset(String key) { - throw UnimplementedError(); + return _webViewApi.loadFlutterAssetForInstances(this, key); } /// Indicates whether there is a valid back item in the back-forward list. Future canGoBack() { - throw UnimplementedError(); + return _webViewApi.canGoBackForInstances(this); } /// Indicates whether there is a valid forward item in the back-forward list. Future canGoForward() { - throw UnimplementedError(); + return _webViewApi.canGoForwardForInstances(this); } /// Navigates to the back item in the back-forward list. Future goBack() { - throw UnimplementedError(); + return _webViewApi.goBackForInstances(this); } /// Navigates to the forward item in the back-forward list. Future goForward() { - throw UnimplementedError(); + return _webViewApi.goForwardForInstances(this); } /// Reloads the current webpage. Future reload() { - throw UnimplementedError(); + return _webViewApi.reloadForInstances(this); } /// The page title. /// /// Represents [WKWebView.title](https://developer.apple.com/documentation/webkit/wkwebview/1415015-title?language=objc). Future getTitle() { - throw UnimplementedError(); + return _webViewApi.getTitleForInstances(this); } /// Indicates whether horizontal swipe gestures trigger page navigation. @@ -583,7 +732,10 @@ class WKWebView extends UIView { /// /// Sets [WKWebView.allowsBackForwardNavigationGestures](https://developer.apple.com/documentation/webkit/wkwebview/1414995-allowsbackforwardnavigationgestu?language=objc). Future setAllowsBackForwardNavigationGestures(bool allow) { - throw UnimplementedError(); + return _webViewApi.setAllowsBackForwardNavigationGesturesForInstances( + this, + allow, + ); } /// The custom user agent string. @@ -592,7 +744,7 @@ class WKWebView extends UIView { /// /// Sets [WKWebView.customUserAgent](https://developer.apple.com/documentation/webkit/wkwebview/1414950-customuseragent?language=objc). Future setCustomUserAgent(String? userAgent) { - throw UnimplementedError(); + return _webViewApi.setCustomUserAgentForInstances(this, userAgent); } /// Evaluates the specified JavaScript string. @@ -600,6 +752,9 @@ class WKWebView extends UIView { /// Throws a `PlatformException` if an error occurs or return value is not /// supported. Future evaluateJavaScript(String javaScriptString) { - throw UnimplementedError(); + return _webViewApi.evaluateJavaScriptForInstances( + this, + javaScriptString, + ); } } 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 new file mode 100644 index 000000000000..188f65cbe177 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart @@ -0,0 +1,549 @@ +// 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 '../common/instance_manager.dart'; +import '../common/web_kit.pigeon.dart'; +import '../foundation/foundation.dart'; +import 'web_kit.dart'; + +Iterable _toWKWebsiteDataTypesEnumData( + Iterable types) { + return types.map((WKWebsiteDataTypes type) { + late final WKWebsiteDataTypesEnum value; + switch (type) { + case WKWebsiteDataTypes.cookies: + value = WKWebsiteDataTypesEnum.cookies; + break; + case WKWebsiteDataTypes.memoryCache: + value = WKWebsiteDataTypesEnum.memoryCache; + break; + case WKWebsiteDataTypes.diskCache: + value = WKWebsiteDataTypesEnum.diskCache; + break; + case WKWebsiteDataTypes.offlineWebApplicationCache: + value = WKWebsiteDataTypesEnum.offlineWebApplicationCache; + break; + case WKWebsiteDataTypes.localStroage: + value = WKWebsiteDataTypesEnum.localStroage; + break; + case WKWebsiteDataTypes.sessionStorage: + value = WKWebsiteDataTypesEnum.sessionStorage; + break; + case WKWebsiteDataTypes.sqlDatabases: + value = WKWebsiteDataTypesEnum.sqlDatabases; + break; + case WKWebsiteDataTypes.indexedDBDatabases: + value = WKWebsiteDataTypesEnum.indexedDBDatabases; + break; + } + + return WKWebsiteDataTypesEnumData(value: value); + }); +} + +extension _WKUserScriptInjectionTimeConverter on WKUserScriptInjectionTime { + WKUserScriptInjectionTimeEnumData toWKUserScriptInjectionTimeEnumData() { + late final WKUserScriptInjectionTimeEnum value; + switch (this) { + case WKUserScriptInjectionTime.atDocumentStart: + value = WKUserScriptInjectionTimeEnum.atDocumentStart; + break; + case WKUserScriptInjectionTime.atDocumentEnd: + value = WKUserScriptInjectionTimeEnum.atDocumentEnd; + break; + } + + return WKUserScriptInjectionTimeEnumData(value: value); + } +} + +Iterable _toWKAudiovisualMediaTypeEnumData( + Iterable types, +) { + return types + .map((WKAudiovisualMediaType type) { + late final WKAudiovisualMediaTypeEnum value; + switch (type) { + case WKAudiovisualMediaType.none: + value = WKAudiovisualMediaTypeEnum.none; + break; + case WKAudiovisualMediaType.audio: + value = WKAudiovisualMediaTypeEnum.audio; + break; + case WKAudiovisualMediaType.video: + value = WKAudiovisualMediaTypeEnum.video; + break; + case WKAudiovisualMediaType.all: + value = WKAudiovisualMediaTypeEnum.all; + break; + } + + return WKAudiovisualMediaTypeEnumData(value: value); + }); +} + +extension _WKUserScriptConverter on WKUserScript { + WKUserScriptData toWKUserScriptData() { + return WKUserScriptData( + source: source, + injectionTime: injectionTime.toWKUserScriptInjectionTimeEnumData(), + isMainFrameOnly: isMainFrameOnly, + ); + } +} + +extension _NSUrlRequestConverter on NSUrlRequest { + NSUrlRequestData toNSUrlRequestData() { + return NSUrlRequestData( + url: url, + httpMethod: httpMethod, + httpBody: httpBody, + allHttpHeaderFields: allHttpHeaderFields, + ); + } +} + +/// Host api implementation for [WKWebSiteDataStore]. +class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi { + /// Constructs a [WebsiteDataStoreHostApiImpl]. + WKWebsiteDataStoreHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [createFromWebViewConfiguration] with the ids of the provided object instances. + Future createFromWebViewConfigurationForInstances( + WKWebsiteDataStore instance, + WKWebViewConfiguration configuration, + ) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await createFromWebViewConfiguration( + instanceId, + instanceManager.getInstanceId(configuration)!, + ); + } + } + + /// Calls [removeDataOfTypes] with the ids of the provided object instances. + Future removeDataOfTypesForInstances( + WKWebsiteDataStore instance, + Set dataTypes, { + required double secondsModifiedSinceEpoch, + }) { + return removeDataOfTypes( + instanceManager.getInstanceId(instance)!, + _toWKWebsiteDataTypesEnumData(dataTypes).toList(), + secondsModifiedSinceEpoch, + ); + } +} + +/// Host api implementation for [WKScriptMessageHandler]. +class WKScriptMessageHandlerHostApiImpl extends WKScriptMessageHandlerHostApi { + /// Constructs a [WKScriptMessageHandlerHostApiImpl]. + WKScriptMessageHandlerHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [create] with the ids of the provided object instances. + Future createForInstances(WKScriptMessageHandler instance) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await create(instanceId); + } + } +} + +/// Host api implementation for [WKPreferences]. +class WKPreferencesHostApiImpl extends WKPreferencesHostApi { + /// Constructs a [WKPreferencesHostApiImpl]. + WKPreferencesHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [createFromWebViewConfiguration] with the ids of the provided object instances. + Future createFromWebViewConfigurationForInstances( + WKPreferences instance, + WKWebViewConfiguration configuration, + ) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await createFromWebViewConfiguration( + instanceId, + instanceManager.getInstanceId(configuration)!, + ); + } + } + + /// Calls [setJavaScriptEnabled] with the ids of the provided object instances. + Future setJavaScriptEnabledForInstances( + WKPreferences instance, + bool enabled, + ) { + return setJavaScriptEnabled( + instanceManager.getInstanceId(instance)!, + enabled, + ); + } +} + +/// Host api implementation for [WKUserContentController]. +class WKUserContentControllerHostApiImpl + extends WKUserContentControllerHostApi { + /// Constructs a [WKUserContentControllerHostApiImpl]. + WKUserContentControllerHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [createFromWebViewConfiguration] with the ids of the provided object instances. + Future createFromWebViewConfigurationForInstances( + WKUserContentController instance, + WKWebViewConfiguration configuration, + ) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await createFromWebViewConfiguration( + instanceId, + instanceManager.getInstanceId(configuration)!, + ); + } + } + + /// Calls [addScriptMessageHandler] with the ids of the provided object instances. + Future addScriptMessageHandlerForInstances( + WKUserContentController instance, + WKScriptMessageHandler handler, + String name, + ) { + return addScriptMessageHandler( + instanceManager.getInstanceId(instance)!, + instanceManager.getInstanceId(handler)!, + name, + ); + } + + /// Calls [removeScriptMessageHandler] with the ids of the provided object instances. + Future removeScriptMessageHandlerForInstances( + WKUserContentController instance, + String name, + ) { + return removeScriptMessageHandler( + instanceManager.getInstanceId(instance)!, + name, + ); + } + + /// Calls [removeAllScriptMessageHandlers] with the ids of the provided object instances. + Future removeAllScriptMessageHandlersForInstances( + WKUserContentController instance, + ) { + return removeAllScriptMessageHandlers( + instanceManager.getInstanceId(instance)!, + ); + } + + /// Calls [addUserScript] with the ids of the provided object instances. + Future addUserScriptForInstances( + WKUserContentController instance, + WKUserScript userScript, + ) { + return addUserScript( + instanceManager.getInstanceId(instance)!, + userScript.toWKUserScriptData(), + ); + } + + /// Calls [removeAllUserScripts] with the ids of the provided object instances. + Future removeAllUserScriptsForInstances( + WKUserContentController instance, + ) { + return removeAllUserScripts(instanceManager.getInstanceId(instance)!); + } +} + +/// Host api implementation for [WKWebViewConfiguration]. +class WKWebViewConfigurationHostApiImpl extends WKWebViewConfigurationHostApi { + /// Constructs a [WKWebViewConfigurationHostApiImpl]. + WKWebViewConfigurationHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [create] with the ids of the provided object instances. + Future createForInstances(WKWebViewConfiguration instance) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await create(instanceId); + } + } + + /// Calls [createFromWebView] with the ids of the provided object instances. + Future createFromWebViewForInstances( + WKWebViewConfiguration instance, + WKWebView webView, + ) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await createFromWebView( + instanceId, + instanceManager.getInstanceId(webView)!, + ); + } + } + + /// Calls [setAllowsInlineMediaPlayback] with the ids of the provided object instances. + Future setAllowsInlineMediaPlaybackForInstances( + WKWebViewConfiguration instance, + bool allow, + ) { + return setAllowsInlineMediaPlayback( + instanceManager.getInstanceId(instance)!, + allow, + ); + } + + /// Calls [setMediaTypesRequiringUserActionForPlayback] with the ids of the provided object instances. + Future setMediaTypesRequiringUserActionForPlaybackForInstances( + WKWebViewConfiguration instance, + Set types, + ) { + return setMediaTypesRequiringUserActionForPlayback( + instanceManager.getInstanceId(instance)!, + _toWKAudiovisualMediaTypeEnumData(types).toList(), + ); + } +} + +/// Host api implementation for [WKUIDelegate]. +class WKUIDelegateHostApiImpl extends WKUIDelegateHostApi { + /// Constructs a [WKUIDelegateHostApiImpl]. + WKUIDelegateHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [create] with the ids of the provided object instances. + Future createForInstances(WKUIDelegate instance) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await create(instanceId); + } + } +} + +/// Host api implementation for [WKNavigationDelegate]. +class WKNavigationDelegateHostApiImpl extends WKNavigationDelegateHostApi { + /// Constructs a [WKNavigationDelegateHostApiImpl]. + WKNavigationDelegateHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [create] with the ids of the provided object instances. + Future createForInstances(WKNavigationDelegate instance) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await create(instanceId); + } + } +} + +/// Host api implementation for [WKWebView]. +class WKWebViewHostApiImpl extends WKWebViewHostApi { + /// Constructs a [WKWebViewHostApiImpl]. + WKWebViewHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [create] with the ids of the provided object instances. + Future createForInstances( + WKWebView instance, + WKWebViewConfiguration configuration, + ) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await create( + instanceId, + instanceManager.getInstanceId(configuration)!, + ); + } + } + + /// Calls [loadRequest] with the ids of the provided object instances. + Future loadRequestForInstances( + WKWebView webView, NSUrlRequest request) { + return loadRequest( + instanceManager.getInstanceId(webView)!, + request.toNSUrlRequestData(), + ); + } + + /// Calls [loadHtmlString] with the ids of the provided object instances. + Future loadHtmlStringForInstances( + WKWebView instance, + String string, + String? baseUrl, + ) { + return loadHtmlString( + instanceManager.getInstanceId(instance)!, + string, + baseUrl, + ); + } + + /// Calls [loadFileUrl] with the ids of the provided object instances. + Future loadFileUrlForInstances( + WKWebView instance, + String url, + String readAccessUrl, + ) { + return loadFileUrl( + instanceManager.getInstanceId(instance)!, + url, + readAccessUrl, + ); + } + + /// Calls [loadFlutterAsset] with the ids of the provided object instances. + Future loadFlutterAssetForInstances(WKWebView instance, String key) { + return loadFlutterAsset( + instanceManager.getInstanceId(instance)!, + key, + ); + } + + /// Calls [canGoBack] with the ids of the provided object instances. + Future canGoBackForInstances(WKWebView instance) { + return canGoBack(instanceManager.getInstanceId(instance)!); + } + + /// Calls [canGoForward] with the ids of the provided object instances. + Future canGoForwardForInstances(WKWebView instance) { + return canGoForward(instanceManager.getInstanceId(instance)!); + } + + /// Calls [goBack] with the ids of the provided object instances. + Future goBackForInstances(WKWebView instance) { + return goBack(instanceManager.getInstanceId(instance)!); + } + + /// Calls [goForward] with the ids of the provided object instances. + Future goForwardForInstances(WKWebView instance) { + return goForward(instanceManager.getInstanceId(instance)!); + } + + /// Calls [reload] with the ids of the provided object instances. + Future reloadForInstances(WKWebView instance) { + return reload(instanceManager.getInstanceId(instance)!); + } + + /// Calls [getUrl] with the ids of the provided object instances. + Future getUrlForInstances(WKWebView instance) { + return getUrl(instanceManager.getInstanceId(instance)!); + } + + /// Calls [getTitle] with the ids of the provided object instances. + Future getTitleForInstances(WKWebView instance) { + return getTitle(instanceManager.getInstanceId(instance)!); + } + + /// Calls [getEstimatedProgress] with the ids of the provided object instances. + Future getEstimatedProgressForInstances(WKWebView instance) { + return getEstimatedProgress(instanceManager.getInstanceId(instance)!); + } + + /// Calls [setAllowsBackForwardNavigationGestures] with the ids of the provided object instances. + Future setAllowsBackForwardNavigationGesturesForInstances( + WKWebView instance, + bool allow, + ) { + return setAllowsBackForwardNavigationGestures( + instanceManager.getInstanceId(instance)!, + allow, + ); + } + + /// Calls [setCustomUserAgent] with the ids of the provided object instances. + Future setCustomUserAgentForInstances( + WKWebView instance, + String? userAgent, + ) { + return setCustomUserAgent( + instanceManager.getInstanceId(instance)!, + userAgent, + ); + } + + /// Calls [evaluateJavaScript] with the ids of the provided object instances. + Future evaluateJavaScriptForInstances( + WKWebView instance, + String javaScriptString, + ) { + return evaluateJavaScript( + instanceManager.getInstanceId(instance)!, + javaScriptString, + ); + } + + /// Calls [setNavigationDelegate] with the ids of the provided object instances. + Future setNavigationDelegateForInstances( + WKWebView instance, + WKNavigationDelegate? delegate, + ) { + return setNavigationDelegate( + instanceManager.getInstanceId(instance)!, + delegate != null ? instanceManager.getInstanceId(delegate)! : null, + ); + } + + /// Calls [setUIDelegate] with the ids of the provided object instances. + Future setUIDelegateForInstances( + WKWebView instance, + WKUIDelegate? delegate, + ) { + return setUIDelegate( + instanceManager.getInstanceId(instance)!, + delegate != null ? instanceManager.getInstanceId(delegate)! : null, + ); + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index d7cbfb4510b2..d63cbef71f9f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -226,7 +226,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { @override Future clearCache() { - return webView.configuration.webSiteDataStore.removeDataOfTypes( + return webView.configuration.websiteDataStore.removeDataOfTypes( { WKWebsiteDataTypes.memoryCache, WKWebsiteDataTypes.diskCache, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart new file mode 100644 index 000000000000..0d5328dda8d6 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -0,0 +1,352 @@ +// 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'; + +@ConfigurePigeon( + PigeonOptions( + dartOut: 'lib/src/common/web_kit.pigeon.dart', + dartTestOut: 'test/src/common/test_web_kit.pigeon.dart', + dartOptions: DartOptions(isNullSafe: true, copyrightHeader: [ + '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.', + ]), + ), +) + +/// Mirror of NSKeyValueObservingOptions. +/// +/// See https://developer.apple.com/documentation/foundation/nskeyvalueobservingoptions?language=objc. +enum NSKeyValueObservingOptionsEnum { + newValue, + oldValue, + initialValue, + priorNotification, +} + +class NSKeyValueObservingOptionsEnumData { + // TODO(bparrishMines): Generated code fails when enums are marked as nonnull. + // Change to nonnull once this is fixed: https://github.com/flutter/flutter/issues/100594 + late NSKeyValueObservingOptionsEnum? value; +} + +/// Mirror of NSKeyValueChange. +/// +/// See https://developer.apple.com/documentation/foundation/nskeyvaluechange?language=objc. +enum NSKeyValueChangeEnum { + setting, + insertion, + removal, + replacement, +} + +class NSKeyValueChangeEnumData { + late NSKeyValueChangeEnum? value; +} + +/// Mirror of NSKeyValueChangeKey. +/// +/// See https://developer.apple.com/documentation/foundation/nskeyvaluechangekey?language=objc. +enum NSKeyValueChangeKeyEnum { + indexes, + kind, + newValue, + notificationIsPrior, + oldValue, +} + +class NSKeyValueChangeKeyEnumData { + late NSKeyValueChangeKeyEnum? value; +} + +/// Mirror of WKUserScriptInjectionTime. +/// +/// See https://developer.apple.com/documentation/webkit/wkuserscriptinjectiontime?language=objc. +enum WKUserScriptInjectionTimeEnum { + atDocumentStart, + atDocumentEnd, +} + +class WKUserScriptInjectionTimeEnumData { + late WKUserScriptInjectionTimeEnum? value; +} + +/// Mirror of WKAudiovisualMediaTypes. +/// +/// See [WKAudiovisualMediaTypes](https://developer.apple.com/documentation/webkit/wkaudiovisualmediatypes?language=objc). +enum WKAudiovisualMediaTypeEnum { + none, + audio, + video, + all, +} + +class WKAudiovisualMediaTypeEnumData { + late WKAudiovisualMediaTypeEnum? value; +} + +/// Mirror of WKWebsiteDataTypes. +/// +/// See https://developer.apple.com/documentation/webkit/wkwebsitedatarecord/data_store_record_types?language=objc. +enum WKWebsiteDataTypesEnum { + cookies, + memoryCache, + diskCache, + offlineWebApplicationCache, + localStroage, + sessionStorage, + sqlDatabases, + indexedDBDatabases, +} + +class WKWebsiteDataTypesEnumData { + late WKWebsiteDataTypesEnum? value; +} + +/// Mirror of WKNavigationActionPolicy. +/// +/// See https://developer.apple.com/documentation/webkit/wknavigationactionpolicy?language=objc. +enum WKNavigationActionPolicyEnum { + allow, + cancel, +} + +class WKNavigationActionPolicyEnumData { + late WKNavigationActionPolicyEnum? value; +} + +/// Mirror of NSURLRequest. +/// +/// See https://developer.apple.com/documentation/foundation/nsurlrequest?language=objc. +class NSUrlRequestData { + late String url; + late String? httpMethod; + late Uint8List? httpBody; + late Map allHttpHeaderFields; +} + +/// Mirror of WKUserScript. +/// +/// See https://developer.apple.com/documentation/webkit/wkuserscript?language=objc. +class WKUserScriptData { + late String source; + late WKUserScriptInjectionTimeEnumData? injectionTime; + late bool isMainFrameOnly; +} + +/// Mirror of WKNavigationAction. +/// +/// See https://developer.apple.com/documentation/webkit/wknavigationaction. +class WKNavigationActionData { + late NSUrlRequestData request; + late WKFrameInfoData targetFrame; +} + +/// Mirror of WKFrameInfo. +/// +/// See https://developer.apple.com/documentation/webkit/wkframeinfo?language=objc. +class WKFrameInfoData { + late bool isMainFrame; +} + +/// Mirror of NSError. +/// +/// See https://developer.apple.com/documentation/foundation/nserror?language=objc. +class NSErrorData { + late int code; + late String domain; + late String localiziedDescription; +} + +/// Mirror of WKScriptMessage. +/// +/// See https://developer.apple.com/documentation/webkit/wkscriptmessage?language=objc. +class WKScriptMessageData { + late String name; + late Object? body; +} + +/// Mirror of WKWebsiteDataStore. +/// +/// See https://developer.apple.com/documentation/webkit/wkwebsitedatastore?language=objc. +@HostApi(dartHostTestHandler: 'TestWKWebsiteDataStoreHostApi') +abstract class WKWebsiteDataStoreHostApi { + void createFromWebViewConfiguration( + int instanceId, + int configurationInstanceId, + ); + + @async + void removeDataOfTypes( + int instanceId, + List dataTypes, + double secondsModifiedSinceEpoch, + ); +} + +/// Mirror of UIView. +/// +/// See https://developer.apple.com/documentation/uikit/uiview?language=objc. +@HostApi(dartHostTestHandler: 'TestUIViewHostApi') +abstract class UIViewHostApi { + List getContentOffset(int instanceId); + + void setBackgroundColor(int instanceId, int? value); + + void setOpaque(int instanceId, bool opaque); +} + +/// Mirror of UIScrollView. +/// +/// See https://developer.apple.com/documentation/uikit/uiscrollview?language=objc. +@HostApi(dartHostTestHandler: 'TestUIScrollViewHostApi') +abstract class UIScrollViewHostApi { + void createFromWebView(int instanceId, int webViewInstanceId); + + List getContentOffset(int instanceId); + + void scrollBy(int instanceId, double x, double y); + + void setContentOffset(int instanceId, double x, double y); +} + +/// Mirror of WKWebViewConfiguration. +/// +/// See https://developer.apple.com/documentation/webkit/wkwebviewconfiguration?language=objc. +@HostApi(dartHostTestHandler: 'TestWKWebViewConfigurationHostApi') +abstract class WKWebViewConfigurationHostApi { + void create(int instanceId); + + void createFromWebView(int instanceId, int webViewInstanceId); + + void setAllowsInlineMediaPlayback(int instanceId, bool allow); + + void setMediaTypesRequiringUserActionForPlayback( + int instanceId, + List types, + ); +} + +/// Mirror of WKUserContentController. +/// +/// See https://developer.apple.com/documentation/webkit/wkusercontentcontroller?language=objc. +@HostApi(dartHostTestHandler: 'TestWKUserContentControllerHostApi') +abstract class WKUserContentControllerHostApi { + void createFromWebViewConfiguration( + int instanceId, + int configurationInstanceId, + ); + + void addScriptMessageHandler( + int instanceId, + int handlerInstanceid, + String name, + ); + + void removeScriptMessageHandler(int instanceId, String name); + + void removeAllScriptMessageHandlers(int instanceId); + + void addUserScript(int instanceId, WKUserScriptData userScript); + + void removeAllUserScripts(int instanceId); +} + +/// Mirror of WKUserPreferences. +/// +/// See https://developer.apple.com/documentation/webkit/wkpreferences?language=objc. +@HostApi(dartHostTestHandler: 'TestWKPreferencesHostApi') +abstract class WKPreferencesHostApi { + void createFromWebViewConfiguration( + int instanceId, + int configurationInstanceId, + ); + + void setJavaScriptEnabled(int instanceId, bool enabled); +} + +/// Mirror of WKScriptMessageHandler. +/// +/// See https://developer.apple.com/documentation/webkit/wkscriptmessagehandler?language=objc. +@HostApi(dartHostTestHandler: 'TestWKScriptMessageHandlerHostApi') +abstract class WKScriptMessageHandlerHostApi { + void create(int instanceId); +} + +/// Mirror of WKNavigationDelegate. +/// +/// See https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc. +@HostApi(dartHostTestHandler: 'TestWKNavigationDelegateHostApi') +abstract class WKNavigationDelegateHostApi { + void create(int instanceId); +} + +/// Mirror of NSObject. +/// +/// See https://developer.apple.com/documentation/objectivec/nsobject. +@HostApi(dartHostTestHandler: 'TestNSObjectHostApi') +abstract class NSObjectHostApi { + void dispose(int instanceId); + + void addObserver( + int instanceId, + int observerInstanceId, + String keyPath, + List options, + ); + + void removeObserver(int instanceId, int observerInstanceId, String keyPath); +} + +/// Mirror of WKWebView. +/// +/// See https://developer.apple.com/documentation/webkit/wkwebview?language=objc. +@HostApi(dartHostTestHandler: 'TestWKWebViewHostApi') +abstract class WKWebViewHostApi { + void create(int instanceId, int configurationInstanceId); + + void setUIDelegate(int instanceId, int? uiDelegateInstanceId); + + void setNavigationDelegate(int instanceId, int? navigationDelegateInstanceId); + + String? getUrl(int instanceId); + + double getEstimatedProgress(int instanceId); + + void loadRequest(int instanceId, NSUrlRequestData request); + + void loadHtmlString(int instanceId, String string, String? baseUrl); + + void loadFileUrl(int instanceId, String url, String readAccessUrl); + + void loadFlutterAsset(int instanceId, String key); + + bool canGoBack(int instanceId); + + bool canGoForward(int instanceId); + + void goBack(int instanceId); + + void goForward(int instanceId); + + void reload(int instanceId); + + String? getTitle(int instanceId); + + void setAllowsBackForwardNavigationGestures(int instanceId, bool allow); + + void setCustomUserAgent(int instanceId, String? userAgent); + + @async + String evaluateJavaScript(int instanceId, String javascriptString); +} + +/// Mirror of WKUIDelegate. +/// +/// See https://developer.apple.com/documentation/webkit/wkuidelegate?language=objc. +@HostApi(dartHostTestHandler: 'TestWKUIDelegateHostApi') +abstract class WKUIDelegateHostApi { + void create(int instanceId); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 75e7ccaaf74f..2a4a7a4f674d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -29,3 +29,4 @@ dev_dependencies: sdk: flutter mockito: ^5.1.0 pedantic: ^1.10.0 + pigeon: ^2.0.3 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/instance_manager_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/instance_manager_test.dart new file mode 100644 index 000000000000..10956c0a4aba --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/instance_manager_test.dart @@ -0,0 +1,35 @@ +// 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:webview_flutter_wkwebview/src/common/instance_manager.dart'; + +void main() { + group('InstanceManager', () { + late InstanceManager testInstanceManager; + + setUp(() { + testInstanceManager = InstanceManager(); + }); + + test('tryAddInstance', () { + final Object object = Object(); + + expect(testInstanceManager.tryAddInstance(object), 0); + expect(testInstanceManager.getInstanceId(object), 0); + expect(testInstanceManager.getInstance(0), object); + expect(testInstanceManager.tryAddInstance(object), null); + }); + + test('removeInstance', () { + final Object object = Object(); + testInstanceManager.tryAddInstance(object); + + expect(testInstanceManager.removeInstance(object), 0); + expect(testInstanceManager.getInstanceId(object), null); + expect(testInstanceManager.getInstance(0), null); + expect(testInstanceManager.removeInstance(object), null); + }); + }); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart new file mode 100644 index 000000000000..78e4b9dcfd3d --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart @@ -0,0 +1,1308 @@ +// 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 (v2.0.3), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis +// ignore_for_file: avoid_relative_lib_imports +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart'; + +class _TestWKWebsiteDataStoreHostApiCodec extends StandardMessageCodec { + const _TestWKWebsiteDataStoreHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is WKWebsiteDataTypesEnumData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return WKWebsiteDataTypesEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestWKWebsiteDataStoreHostApi { + static const MessageCodec codec = + _TestWKWebsiteDataStoreHostApiCodec(); + + void createFromWebViewConfiguration( + int instanceId, int configurationInstanceId); + Future removeDataOfTypes( + int instanceId, + List dataTypes, + double secondsModifiedSinceEpoch); + static void setup(TestWKWebsiteDataStoreHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createFromWebViewConfiguration', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createFromWebViewConfiguration 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.WKWebsiteDataStoreHostApi.createFromWebViewConfiguration was null, expected non-null int.'); + final int? arg_configurationInstanceId = (args[1] as int?); + assert(arg_configurationInstanceId != null, + 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createFromWebViewConfiguration was null, expected non-null int.'); + api.createFromWebViewConfiguration( + arg_instanceId!, arg_configurationInstanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes 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.WKWebsiteDataStoreHostApi.removeDataOfTypes was null, expected non-null int.'); + final List? arg_dataTypes = + (args[1] as List?)?.cast(); + assert(arg_dataTypes != null, + 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes was null, expected non-null List.'); + final double? arg_secondsModifiedSinceEpoch = (args[2] as double?); + assert(arg_secondsModifiedSinceEpoch != null, + 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes was null, expected non-null double.'); + await api.removeDataOfTypes( + arg_instanceId!, arg_dataTypes!, arg_secondsModifiedSinceEpoch!); + return {}; + }); + } + } + } +} + +class _TestUIViewHostApiCodec extends StandardMessageCodec { + const _TestUIViewHostApiCodec(); +} + +abstract class TestUIViewHostApi { + static const MessageCodec codec = _TestUIViewHostApiCodec(); + + List getContentOffset(int instanceId); + void setBackgroundColor(int instanceId, int? value); + void setOpaque(int instanceId, bool opaque); + static void setup(TestUIViewHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIViewHostApi.getContentOffset', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UIViewHostApi.getContentOffset 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.UIViewHostApi.getContentOffset was null, expected non-null int.'); + final List output = api.getContentOffset(arg_instanceId!); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIViewHostApi.setBackgroundColor', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UIViewHostApi.setBackgroundColor 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.UIViewHostApi.setBackgroundColor was null, expected non-null int.'); + final int? arg_value = (args[1] as int?); + assert(arg_value != null, + 'Argument for dev.flutter.pigeon.UIViewHostApi.setBackgroundColor was null, expected non-null int.'); + api.setBackgroundColor(arg_instanceId!, arg_value!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIViewHostApi.setOpaque', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UIViewHostApi.setOpaque 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.UIViewHostApi.setOpaque was null, expected non-null int.'); + final bool? arg_opaque = (args[1] as bool?); + assert(arg_opaque != null, + 'Argument for dev.flutter.pigeon.UIViewHostApi.setOpaque was null, expected non-null bool.'); + api.setOpaque(arg_instanceId!, arg_opaque!); + return {}; + }); + } + } + } +} + +class _TestUIScrollViewHostApiCodec extends StandardMessageCodec { + const _TestUIScrollViewHostApiCodec(); +} + +abstract class TestUIScrollViewHostApi { + static const MessageCodec codec = _TestUIScrollViewHostApiCodec(); + + void createFromWebView(int instanceId, int webViewInstanceId); + List getContentOffset(int instanceId); + void scrollBy(int instanceId, double x, double y); + void setContentOffset(int instanceId, double x, double y); + static void setup(TestUIScrollViewHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIScrollViewHostApi.createFromWebView', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.createFromWebView 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.UIScrollViewHostApi.createFromWebView was null, expected non-null int.'); + final int? arg_webViewInstanceId = (args[1] as int?); + assert(arg_webViewInstanceId != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.createFromWebView was null, expected non-null int.'); + api.createFromWebView(arg_instanceId!, arg_webViewInstanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIScrollViewHostApi.getContentOffset', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.getContentOffset 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.UIScrollViewHostApi.getContentOffset was null, expected non-null int.'); + final List output = api.getContentOffset(arg_instanceId!); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIScrollViewHostApi.scrollBy', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.scrollBy 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.UIScrollViewHostApi.scrollBy was null, expected non-null int.'); + final double? arg_x = (args[1] as double?); + assert(arg_x != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.scrollBy was null, expected non-null double.'); + final double? arg_y = (args[2] as double?); + assert(arg_y != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.scrollBy was null, expected non-null double.'); + api.scrollBy(arg_instanceId!, arg_x!, arg_y!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UIScrollViewHostApi.setContentOffset', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.setContentOffset 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.UIScrollViewHostApi.setContentOffset was null, expected non-null int.'); + final double? arg_x = (args[1] as double?); + assert(arg_x != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.setContentOffset was null, expected non-null double.'); + final double? arg_y = (args[2] as double?); + assert(arg_y != null, + 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.setContentOffset was null, expected non-null double.'); + api.setContentOffset(arg_instanceId!, arg_x!, arg_y!); + return {}; + }); + } + } + } +} + +class _TestWKWebViewConfigurationHostApiCodec extends StandardMessageCodec { + const _TestWKWebViewConfigurationHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is WKAudiovisualMediaTypeEnumData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestWKWebViewConfigurationHostApi { + static const MessageCodec codec = + _TestWKWebViewConfigurationHostApiCodec(); + + void create(int instanceId); + void createFromWebView(int instanceId, int webViewInstanceId); + void setAllowsInlineMediaPlayback(int instanceId, bool allow); + void setMediaTypesRequiringUserActionForPlayback( + int instanceId, List types); + static void setup(TestWKWebViewConfigurationHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.create 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.WKWebViewConfigurationHostApi.create was null, expected non-null int.'); + api.create(arg_instanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.createFromWebView', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.createFromWebView 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.WKWebViewConfigurationHostApi.createFromWebView was null, expected non-null int.'); + final int? arg_webViewInstanceId = (args[1] as int?); + assert(arg_webViewInstanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.createFromWebView was null, expected non-null int.'); + api.createFromWebView(arg_instanceId!, arg_webViewInstanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.setAllowsInlineMediaPlayback', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.setAllowsInlineMediaPlayback 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.WKWebViewConfigurationHostApi.setAllowsInlineMediaPlayback was null, expected non-null int.'); + final bool? arg_allow = (args[1] as bool?); + assert(arg_allow != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.setAllowsInlineMediaPlayback was null, expected non-null bool.'); + api.setAllowsInlineMediaPlayback(arg_instanceId!, arg_allow!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.setMediaTypesRequiringUserActionForPlayback', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.setMediaTypesRequiringUserActionForPlayback 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.WKWebViewConfigurationHostApi.setMediaTypesRequiringUserActionForPlayback was null, expected non-null int.'); + final List? arg_types = + (args[1] as List?) + ?.cast(); + assert(arg_types != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.setMediaTypesRequiringUserActionForPlayback was null, expected non-null List.'); + api.setMediaTypesRequiringUserActionForPlayback( + arg_instanceId!, arg_types!); + return {}; + }); + } + } + } +} + +class _TestWKUserContentControllerHostApiCodec extends StandardMessageCodec { + const _TestWKUserContentControllerHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is WKUserScriptData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is WKUserScriptInjectionTimeEnumData) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return WKUserScriptData.decode(readValue(buffer)!); + + case 129: + return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestWKUserContentControllerHostApi { + static const MessageCodec codec = + _TestWKUserContentControllerHostApiCodec(); + + void createFromWebViewConfiguration( + int instanceId, int configurationInstanceId); + void addScriptMessageHandler( + int instanceId, int handlerInstanceid, String name); + void removeScriptMessageHandler(int instanceId, String name); + void removeAllScriptMessageHandlers(int instanceId); + void addUserScript(int instanceId, WKUserScriptData userScript); + void removeAllUserScripts(int instanceId); + static void setup(TestWKUserContentControllerHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.createFromWebViewConfiguration', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.createFromWebViewConfiguration 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.WKUserContentControllerHostApi.createFromWebViewConfiguration was null, expected non-null int.'); + final int? arg_configurationInstanceId = (args[1] as int?); + assert(arg_configurationInstanceId != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.createFromWebViewConfiguration was null, expected non-null int.'); + api.createFromWebViewConfiguration( + arg_instanceId!, arg_configurationInstanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.addScriptMessageHandler', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.addScriptMessageHandler 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.WKUserContentControllerHostApi.addScriptMessageHandler was null, expected non-null int.'); + final int? arg_handlerInstanceid = (args[1] as int?); + assert(arg_handlerInstanceid != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.addScriptMessageHandler was null, expected non-null int.'); + final String? arg_name = (args[2] as String?); + assert(arg_name != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.addScriptMessageHandler was null, expected non-null String.'); + api.addScriptMessageHandler( + arg_instanceId!, arg_handlerInstanceid!, arg_name!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.removeScriptMessageHandler', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.removeScriptMessageHandler 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.WKUserContentControllerHostApi.removeScriptMessageHandler was null, expected non-null int.'); + final String? arg_name = (args[1] as String?); + assert(arg_name != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.removeScriptMessageHandler was null, expected non-null String.'); + api.removeScriptMessageHandler(arg_instanceId!, arg_name!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllScriptMessageHandlers', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllScriptMessageHandlers 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.WKUserContentControllerHostApi.removeAllScriptMessageHandlers was null, expected non-null int.'); + api.removeAllScriptMessageHandlers(arg_instanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.addUserScript', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.addUserScript 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.WKUserContentControllerHostApi.addUserScript was null, expected non-null int.'); + final WKUserScriptData? arg_userScript = + (args[1] as WKUserScriptData?); + assert(arg_userScript != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.addUserScript was null, expected non-null WKUserScriptData.'); + api.addUserScript(arg_instanceId!, arg_userScript!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllUserScripts', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllUserScripts 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.WKUserContentControllerHostApi.removeAllUserScripts was null, expected non-null int.'); + api.removeAllUserScripts(arg_instanceId!); + return {}; + }); + } + } + } +} + +class _TestWKPreferencesHostApiCodec extends StandardMessageCodec { + const _TestWKPreferencesHostApiCodec(); +} + +abstract class TestWKPreferencesHostApi { + static const MessageCodec codec = _TestWKPreferencesHostApiCodec(); + + void createFromWebViewConfiguration( + int instanceId, int configurationInstanceId); + void setJavaScriptEnabled(int instanceId, bool enabled); + static void setup(TestWKPreferencesHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKPreferencesHostApi.createFromWebViewConfiguration', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKPreferencesHostApi.createFromWebViewConfiguration 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.WKPreferencesHostApi.createFromWebViewConfiguration was null, expected non-null int.'); + final int? arg_configurationInstanceId = (args[1] as int?); + assert(arg_configurationInstanceId != null, + 'Argument for dev.flutter.pigeon.WKPreferencesHostApi.createFromWebViewConfiguration was null, expected non-null int.'); + api.createFromWebViewConfiguration( + arg_instanceId!, arg_configurationInstanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKPreferencesHostApi.setJavaScriptEnabled', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKPreferencesHostApi.setJavaScriptEnabled 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.WKPreferencesHostApi.setJavaScriptEnabled was null, expected non-null int.'); + final bool? arg_enabled = (args[1] as bool?); + assert(arg_enabled != null, + 'Argument for dev.flutter.pigeon.WKPreferencesHostApi.setJavaScriptEnabled was null, expected non-null bool.'); + api.setJavaScriptEnabled(arg_instanceId!, arg_enabled!); + return {}; + }); + } + } + } +} + +class _TestWKScriptMessageHandlerHostApiCodec extends StandardMessageCodec { + const _TestWKScriptMessageHandlerHostApiCodec(); +} + +abstract class TestWKScriptMessageHandlerHostApi { + static const MessageCodec codec = + _TestWKScriptMessageHandlerHostApiCodec(); + + void create(int instanceId); + static void setup(TestWKScriptMessageHandlerHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKScriptMessageHandlerHostApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKScriptMessageHandlerHostApi.create 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.WKScriptMessageHandlerHostApi.create was null, expected non-null int.'); + api.create(arg_instanceId!); + return {}; + }); + } + } + } +} + +class _TestWKNavigationDelegateHostApiCodec extends StandardMessageCodec { + const _TestWKNavigationDelegateHostApiCodec(); +} + +abstract class TestWKNavigationDelegateHostApi { + static const MessageCodec codec = + _TestWKNavigationDelegateHostApiCodec(); + + void create(int instanceId); + static void setup(TestWKNavigationDelegateHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKNavigationDelegateHostApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateHostApi.create 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.WKNavigationDelegateHostApi.create was null, expected non-null int.'); + api.create(arg_instanceId!); + return {}; + }); + } + } + } +} + +class _TestNSObjectHostApiCodec extends StandardMessageCodec { + const _TestNSObjectHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is NSKeyValueObservingOptionsEnumData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestNSObjectHostApi { + static const MessageCodec codec = _TestNSObjectHostApiCodec(); + + void dispose(int instanceId); + void addObserver(int instanceId, int observerInstanceId, String keyPath, + List options); + void removeObserver(int instanceId, int observerInstanceId, String keyPath); + static void setup(TestNSObjectHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NSObjectHostApi.dispose', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NSObjectHostApi.dispose 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.NSObjectHostApi.dispose was null, expected non-null int.'); + api.dispose(arg_instanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NSObjectHostApi.addObserver', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NSObjectHostApi.addObserver 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.NSObjectHostApi.addObserver was null, expected non-null int.'); + final int? arg_observerInstanceId = (args[1] as int?); + assert(arg_observerInstanceId != null, + 'Argument for dev.flutter.pigeon.NSObjectHostApi.addObserver was null, expected non-null int.'); + final String? arg_keyPath = (args[2] as String?); + assert(arg_keyPath != null, + 'Argument for dev.flutter.pigeon.NSObjectHostApi.addObserver was null, expected non-null String.'); + final List? arg_options = + (args[3] as List?) + ?.cast(); + assert(arg_options != null, + 'Argument for dev.flutter.pigeon.NSObjectHostApi.addObserver was null, expected non-null List.'); + api.addObserver(arg_instanceId!, arg_observerInstanceId!, + arg_keyPath!, arg_options!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NSObjectHostApi.removeObserver', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NSObjectHostApi.removeObserver 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.NSObjectHostApi.removeObserver was null, expected non-null int.'); + final int? arg_observerInstanceId = (args[1] as int?); + assert(arg_observerInstanceId != null, + 'Argument for dev.flutter.pigeon.NSObjectHostApi.removeObserver was null, expected non-null int.'); + final String? arg_keyPath = (args[2] as String?); + assert(arg_keyPath != null, + 'Argument for dev.flutter.pigeon.NSObjectHostApi.removeObserver was null, expected non-null String.'); + api.removeObserver( + arg_instanceId!, arg_observerInstanceId!, arg_keyPath!); + return {}; + }); + } + } + } +} + +class _TestWKWebViewHostApiCodec extends StandardMessageCodec { + const _TestWKWebViewHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is NSUrlRequestData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return NSUrlRequestData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestWKWebViewHostApi { + static const MessageCodec codec = _TestWKWebViewHostApiCodec(); + + void create(int instanceId, int configurationInstanceId); + void setUIDelegate(int instanceId, int? uiDelegateInstanceId); + void setNavigationDelegate(int instanceId, int? navigationDelegateInstanceId); + String? getUrl(int instanceId); + double getEstimatedProgress(int instanceId); + void loadRequest(int instanceId, NSUrlRequestData request); + void loadHtmlString(int instanceId, String string, String? baseUrl); + void loadFileUrl(int instanceId, String url, String readAccessUrl); + void loadFlutterAsset(int instanceId, String key); + bool canGoBack(int instanceId); + bool canGoForward(int instanceId); + void goBack(int instanceId); + void goForward(int instanceId); + void reload(int instanceId); + String? getTitle(int instanceId); + void setAllowsBackForwardNavigationGestures(int instanceId, bool allow); + void setCustomUserAgent(int instanceId, String? userAgent); + Future evaluateJavaScript(int instanceId, String javascriptString); + static void setup(TestWKWebViewHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.create 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.WKWebViewHostApi.create was null, expected non-null int.'); + final int? arg_configurationInstanceId = (args[1] as int?); + assert(arg_configurationInstanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.create was null, expected non-null int.'); + api.create(arg_instanceId!, arg_configurationInstanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.setUIDelegate', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setUIDelegate 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.WKWebViewHostApi.setUIDelegate was null, expected non-null int.'); + final int? arg_uiDelegateInstanceId = (args[1] as int?); + assert(arg_uiDelegateInstanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setUIDelegate was null, expected non-null int.'); + api.setUIDelegate(arg_instanceId!, arg_uiDelegateInstanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.setNavigationDelegate', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setNavigationDelegate 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.WKWebViewHostApi.setNavigationDelegate was null, expected non-null int.'); + final int? arg_navigationDelegateInstanceId = (args[1] as int?); + assert(arg_navigationDelegateInstanceId != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setNavigationDelegate was null, expected non-null int.'); + api.setNavigationDelegate( + arg_instanceId!, arg_navigationDelegateInstanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.getUrl', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.getUrl 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.WKWebViewHostApi.getUrl was null, expected non-null int.'); + final String? output = api.getUrl(arg_instanceId!); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.getEstimatedProgress', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.getEstimatedProgress 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.WKWebViewHostApi.getEstimatedProgress was null, expected non-null int.'); + final double output = api.getEstimatedProgress(arg_instanceId!); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.loadRequest', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadRequest 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.WKWebViewHostApi.loadRequest was null, expected non-null int.'); + final NSUrlRequestData? arg_request = (args[1] as NSUrlRequestData?); + assert(arg_request != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadRequest was null, expected non-null NSUrlRequestData.'); + api.loadRequest(arg_instanceId!, arg_request!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString 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.WKWebViewHostApi.loadHtmlString was null, expected non-null int.'); + final String? arg_string = (args[1] as String?); + assert(arg_string != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString was null, expected non-null String.'); + final String? arg_baseUrl = (args[2] as String?); + assert(arg_baseUrl != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString was null, expected non-null String.'); + api.loadHtmlString(arg_instanceId!, arg_string!, arg_baseUrl!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.loadFileUrl', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadFileUrl 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.WKWebViewHostApi.loadFileUrl was null, expected non-null int.'); + final String? arg_url = (args[1] as String?); + assert(arg_url != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadFileUrl was null, expected non-null String.'); + final String? arg_readAccessUrl = (args[2] as String?); + assert(arg_readAccessUrl != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadFileUrl was null, expected non-null String.'); + api.loadFileUrl(arg_instanceId!, arg_url!, arg_readAccessUrl!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.loadFlutterAsset', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadFlutterAsset 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.WKWebViewHostApi.loadFlutterAsset was null, expected non-null int.'); + final String? arg_key = (args[1] as String?); + assert(arg_key != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadFlutterAsset was null, expected non-null String.'); + api.loadFlutterAsset(arg_instanceId!, arg_key!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.canGoBack', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.canGoBack 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.WKWebViewHostApi.canGoBack was null, expected non-null int.'); + final bool output = api.canGoBack(arg_instanceId!); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.canGoForward', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.canGoForward 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.WKWebViewHostApi.canGoForward was null, expected non-null int.'); + final bool output = api.canGoForward(arg_instanceId!); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.goBack', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.goBack 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.WKWebViewHostApi.goBack was null, expected non-null int.'); + api.goBack(arg_instanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.goForward', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.goForward 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.WKWebViewHostApi.goForward was null, expected non-null int.'); + api.goForward(arg_instanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.reload', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.reload 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.WKWebViewHostApi.reload was null, expected non-null int.'); + api.reload(arg_instanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.getTitle', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.getTitle 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.WKWebViewHostApi.getTitle was null, expected non-null int.'); + final String? output = api.getTitle(arg_instanceId!); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.setAllowsBackForwardNavigationGestures', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setAllowsBackForwardNavigationGestures 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.WKWebViewHostApi.setAllowsBackForwardNavigationGestures was null, expected non-null int.'); + final bool? arg_allow = (args[1] as bool?); + assert(arg_allow != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setAllowsBackForwardNavigationGestures was null, expected non-null bool.'); + api.setAllowsBackForwardNavigationGestures( + arg_instanceId!, arg_allow!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent 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.WKWebViewHostApi.setCustomUserAgent was null, expected non-null int.'); + final String? arg_userAgent = (args[1] as String?); + assert(arg_userAgent != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent was null, expected non-null String.'); + api.setCustomUserAgent(arg_instanceId!, arg_userAgent!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript 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.WKWebViewHostApi.evaluateJavaScript was null, expected non-null int.'); + final String? arg_javascriptString = (args[1] as String?); + assert(arg_javascriptString != null, + 'Argument for dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript was null, expected non-null String.'); + final String output = await api.evaluateJavaScript( + arg_instanceId!, arg_javascriptString!); + return {'result': output}; + }); + } + } + } +} + +class _TestWKUIDelegateHostApiCodec extends StandardMessageCodec { + const _TestWKUIDelegateHostApiCodec(); +} + +abstract class TestWKUIDelegateHostApi { + static const MessageCodec codec = _TestWKUIDelegateHostApiCodec(); + + void create(int instanceId); + static void setup(TestWKUIDelegateHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUIDelegateHostApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKUIDelegateHostApi.create 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.WKUIDelegateHostApi.create was null, expected non-null int.'); + api.create(arg_instanceId!); + return {}; + }); + } + } + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart new file mode 100644 index 000000000000..a08680940bcc --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart @@ -0,0 +1,100 @@ +// 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:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:webview_flutter_wkwebview/src/common/instance_manager.dart'; +import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart'; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; + +import '../common/test_web_kit.pigeon.dart'; +import 'foundation_test.mocks.dart'; + +@GenerateMocks([ + TestNSObjectHostApi, +]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('Foundation', () { + late InstanceManager instanceManager; + + setUp(() { + instanceManager = InstanceManager(); + }); + + group('$NSObject', () { + late MockTestNSObjectHostApi mockPlatformHostApi; + + late NSObject object; + + setUp(() { + mockPlatformHostApi = MockTestNSObjectHostApi(); + TestNSObjectHostApi.setup(mockPlatformHostApi); + + object = NSObject(instanceManager: instanceManager); + instanceManager.tryAddInstance(object); + }); + + tearDown(() { + TestNSObjectHostApi.setup(null); + }); + + test('addObserver', () async { + final NSObject observer = NSObject(instanceManager: instanceManager); + instanceManager.tryAddInstance(observer); + + await object.addObserver( + observer, + keyPath: 'aKeyPath', + options: { + NSKeyValueObservingOptions.initialValue, + NSKeyValueObservingOptions.priorNotification, + }, + ); + + final List optionsData = + verify(mockPlatformHostApi.addObserver( + instanceManager.getInstanceId(object), + instanceManager.getInstanceId(observer), + 'aKeyPath', + captureAny, + )).captured.single as List; + + expect(optionsData, hasLength(2)); + expect( + optionsData[0]!.value, + NSKeyValueObservingOptionsEnum.initialValue, + ); + expect( + optionsData[1]!.value, + NSKeyValueObservingOptionsEnum.priorNotification, + ); + }); + + test('removeObserver', () async { + final NSObject observer = NSObject(instanceManager: instanceManager); + instanceManager.tryAddInstance(observer); + + await object.removeObserver(observer, keyPath: 'aKeyPath'); + + verify(mockPlatformHostApi.removeObserver( + instanceManager.getInstanceId(object), + instanceManager.getInstanceId(observer), + 'aKeyPath', + )); + }); + + test('dispose', () async { + final int instanceId = instanceManager.getInstanceId(object)!; + + await object.dispose(); + verify( + mockPlatformHostApi.dispose(instanceId), + ); + }); + }); + }); +} 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 new file mode 100644 index 000000000000..7bd208eeac05 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart @@ -0,0 +1,48 @@ +// Mocks generated by Mockito 5.1.0 from annotations +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart. +// Do not manually edit this file. + +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart' + as _i3; + +import '../common/test_web_kit.pigeon.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: 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 + +/// A class which mocks [TestNSObjectHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestNSObjectHostApi extends _i1.Mock + implements _i2.TestNSObjectHostApi { + MockTestNSObjectHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void dispose(int? instanceId) => + super.noSuchMethod(Invocation.method(#dispose, [instanceId]), + returnValueForMissingStub: null); + @override + void addObserver(int? instanceId, int? observerInstanceId, String? keyPath, + List<_i3.NSKeyValueObservingOptionsEnumData?>? options) => + super.noSuchMethod( + Invocation.method( + #addObserver, [instanceId, observerInstanceId, keyPath, options]), + returnValueForMissingStub: null); + @override + void removeObserver( + int? instanceId, int? observerInstanceId, String? keyPath) => + super.noSuchMethod( + Invocation.method( + #removeObserver, [instanceId, observerInstanceId, keyPath]), + returnValueForMissingStub: null); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart new file mode 100644 index 000000000000..b6c50609552f --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart @@ -0,0 +1,122 @@ +// 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:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:webview_flutter_wkwebview/src/common/instance_manager.dart'; +import 'package:webview_flutter_wkwebview/src/ui_kit/ui_kit.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; + +import '../common/test_web_kit.pigeon.dart'; +import 'ui_kit_test.mocks.dart'; + +@GenerateMocks([ + TestWKWebViewConfigurationHostApi, + TestWKWebViewHostApi, + TestUIScrollViewHostApi, + TestUIViewHostApi, +]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('UIKit', () { + late InstanceManager instanceManager; + + setUp(() { + instanceManager = InstanceManager(); + }); + + group('$UIScrollView', () { + late MockTestUIScrollViewHostApi mockPlatformHostApi; + + late UIScrollView scrollView; + late int scrollViewInstanceId; + + setUp(() { + mockPlatformHostApi = MockTestUIScrollViewHostApi(); + TestUIScrollViewHostApi.setup(mockPlatformHostApi); + + TestWKWebViewConfigurationHostApi.setup( + MockTestWKWebViewConfigurationHostApi(), + ); + TestWKWebViewHostApi.setup(MockTestWKWebViewHostApi()); + final WKWebView webView = WKWebView( + WKWebViewConfiguration(instanceManager: instanceManager), + instanceManager: instanceManager, + ); + + scrollView = UIScrollView.fromWebView( + webView, + instanceManager: instanceManager, + ); + scrollViewInstanceId = instanceManager.getInstanceId(scrollView)!; + }); + + tearDown(() { + TestUIScrollViewHostApi.setup(null); + TestWKWebViewConfigurationHostApi.setup(null); + TestWKWebViewHostApi.setup(null); + }); + + test('getContentOffset', () async { + when(mockPlatformHostApi.getContentOffset(scrollViewInstanceId)) + .thenReturn([4.0, 10.0]); + expect( + scrollView.getContentOffset(), + completion(const Point(4.0, 10.0)), + ); + }); + + test('scrollBy', () async { + await scrollView.scrollBy(const Point(4.0, 10.0)); + verify(mockPlatformHostApi.scrollBy(scrollViewInstanceId, 4.0, 10.0)); + }); + + test('setContentOffset', () async { + await scrollView.setContentOffset(const Point(4.0, 10.0)); + verify(mockPlatformHostApi.setContentOffset( + scrollViewInstanceId, + 4.0, + 10.0, + )); + }); + }); + + group('$UIView', () { + late MockTestUIViewHostApi mockPlatformHostApi; + + late UIView view; + late int viewInstanceId; + + setUp(() { + mockPlatformHostApi = MockTestUIViewHostApi(); + TestUIViewHostApi.setup(mockPlatformHostApi); + + view = UIView(instanceManager: instanceManager); + viewInstanceId = instanceManager.tryAddInstance(view)!; + }); + + tearDown(() { + TestUIViewHostApi.setup(null); + }); + + test('setBackgroundColor', () async { + await view.setBackgroundColor(Colors.red); + verify(mockPlatformHostApi.setBackgroundColor( + viewInstanceId, + Colors.red.value, + )); + }); + + test('setOpaque', () async { + await view.setOpaque(false); + verify(mockPlatformHostApi.setOpaque(viewInstanceId, false)); + }); + }); + }); +} 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 new file mode 100644 index 000000000000..e876aa63cd29 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart @@ -0,0 +1,200 @@ +// Mocks generated by Mockito 5.1.0 from annotations +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i4; + +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart' + as _i3; + +import '../common/test_web_kit.pigeon.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: 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 + +/// A class which mocks [TestWKWebViewConfigurationHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKWebViewConfigurationHostApi extends _i1.Mock + implements _i2.TestWKWebViewConfigurationHostApi { + MockTestWKWebViewConfigurationHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create(int? instanceId) => + super.noSuchMethod(Invocation.method(#create, [instanceId]), + returnValueForMissingStub: null); + @override + void createFromWebView(int? instanceId, int? webViewInstanceId) => + super.noSuchMethod( + Invocation.method( + #createFromWebView, [instanceId, webViewInstanceId]), + returnValueForMissingStub: null); + @override + void setAllowsInlineMediaPlayback(int? instanceId, bool? allow) => + super.noSuchMethod( + Invocation.method(#setAllowsInlineMediaPlayback, [instanceId, allow]), + returnValueForMissingStub: null); + @override + void setMediaTypesRequiringUserActionForPlayback( + int? instanceId, List<_i3.WKAudiovisualMediaTypeEnumData?>? types) => + super.noSuchMethod( + Invocation.method(#setMediaTypesRequiringUserActionForPlayback, + [instanceId, types]), + returnValueForMissingStub: null); +} + +/// A class which mocks [TestWKWebViewHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKWebViewHostApi extends _i1.Mock + implements _i2.TestWKWebViewHostApi { + MockTestWKWebViewHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create(int? instanceId, int? configurationInstanceId) => + super.noSuchMethod( + Invocation.method(#create, [instanceId, configurationInstanceId]), + returnValueForMissingStub: null); + @override + void setUIDelegate(int? instanceId, int? uiDelegateInstanceId) => + super.noSuchMethod( + Invocation.method(#setUIDelegate, [instanceId, uiDelegateInstanceId]), + returnValueForMissingStub: null); + @override + void setNavigationDelegate( + int? instanceId, int? navigationDelegateInstanceId) => + super.noSuchMethod( + Invocation.method(#setNavigationDelegate, + [instanceId, navigationDelegateInstanceId]), + returnValueForMissingStub: null); + @override + String? getUrl(int? instanceId) => + (super.noSuchMethod(Invocation.method(#getUrl, [instanceId])) as String?); + @override + double getEstimatedProgress(int? instanceId) => (super.noSuchMethod( + Invocation.method(#getEstimatedProgress, [instanceId]), + returnValue: 0.0) as double); + @override + void loadRequest(int? instanceId, _i3.NSUrlRequestData? request) => + super.noSuchMethod(Invocation.method(#loadRequest, [instanceId, request]), + returnValueForMissingStub: null); + @override + void loadHtmlString(int? instanceId, String? string, String? baseUrl) => + super.noSuchMethod( + Invocation.method(#loadHtmlString, [instanceId, string, baseUrl]), + returnValueForMissingStub: null); + @override + void loadFileUrl(int? instanceId, String? url, String? readAccessUrl) => + super.noSuchMethod( + Invocation.method(#loadFileUrl, [instanceId, url, readAccessUrl]), + returnValueForMissingStub: null); + @override + void loadFlutterAsset(int? instanceId, String? key) => super.noSuchMethod( + Invocation.method(#loadFlutterAsset, [instanceId, key]), + returnValueForMissingStub: null); + @override + bool canGoBack(int? instanceId) => + (super.noSuchMethod(Invocation.method(#canGoBack, [instanceId]), + returnValue: false) as bool); + @override + bool canGoForward(int? instanceId) => + (super.noSuchMethod(Invocation.method(#canGoForward, [instanceId]), + returnValue: false) as bool); + @override + void goBack(int? instanceId) => + super.noSuchMethod(Invocation.method(#goBack, [instanceId]), + returnValueForMissingStub: null); + @override + void goForward(int? instanceId) => + super.noSuchMethod(Invocation.method(#goForward, [instanceId]), + returnValueForMissingStub: null); + @override + void reload(int? instanceId) => + super.noSuchMethod(Invocation.method(#reload, [instanceId]), + returnValueForMissingStub: null); + @override + String? getTitle(int? instanceId) => + (super.noSuchMethod(Invocation.method(#getTitle, [instanceId])) + as String?); + @override + void setAllowsBackForwardNavigationGestures(int? instanceId, bool? allow) => + super.noSuchMethod( + Invocation.method( + #setAllowsBackForwardNavigationGestures, [instanceId, allow]), + returnValueForMissingStub: null); + @override + void setCustomUserAgent(int? instanceId, String? userAgent) => + super.noSuchMethod( + Invocation.method(#setCustomUserAgent, [instanceId, userAgent]), + returnValueForMissingStub: null); + @override + _i4.Future evaluateJavaScript( + int? instanceId, String? javascriptString) => + (super.noSuchMethod( + Invocation.method( + #evaluateJavaScript, [instanceId, javascriptString]), + returnValue: Future.value('')) as _i4.Future); +} + +/// A class which mocks [TestUIScrollViewHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestUIScrollViewHostApi extends _i1.Mock + implements _i2.TestUIScrollViewHostApi { + MockTestUIScrollViewHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void createFromWebView(int? instanceId, int? webViewInstanceId) => + super.noSuchMethod( + Invocation.method( + #createFromWebView, [instanceId, webViewInstanceId]), + returnValueForMissingStub: null); + @override + List getContentOffset(int? instanceId) => + (super.noSuchMethod(Invocation.method(#getContentOffset, [instanceId]), + returnValue: []) as List); + @override + void scrollBy(int? instanceId, double? x, double? y) => + super.noSuchMethod(Invocation.method(#scrollBy, [instanceId, x, y]), + returnValueForMissingStub: null); + @override + void setContentOffset(int? instanceId, double? x, double? y) => super + .noSuchMethod(Invocation.method(#setContentOffset, [instanceId, x, y]), + returnValueForMissingStub: null); +} + +/// A class which mocks [TestUIViewHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestUIViewHostApi extends _i1.Mock implements _i2.TestUIViewHostApi { + MockTestUIViewHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + List getContentOffset(int? instanceId) => + (super.noSuchMethod(Invocation.method(#getContentOffset, [instanceId]), + returnValue: []) as List); + @override + void setBackgroundColor(int? instanceId, int? value) => super.noSuchMethod( + Invocation.method(#setBackgroundColor, [instanceId, value]), + returnValueForMissingStub: null); + @override + void setOpaque(int? instanceId, bool? opaque) => + super.noSuchMethod(Invocation.method(#setOpaque, [instanceId, opaque]), + returnValueForMissingStub: null); +} 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 new file mode 100644 index 000000000000..d51fa9793dd4 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart @@ -0,0 +1,530 @@ +// 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:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:webview_flutter_wkwebview/src/common/instance_manager.dart'; +import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart'; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; + +import '../common/test_web_kit.pigeon.dart'; +import 'web_kit_test.mocks.dart'; + +@GenerateMocks([ + TestWKNavigationDelegateHostApi, + TestWKPreferencesHostApi, + TestWKScriptMessageHandlerHostApi, + TestWKUIDelegateHostApi, + TestWKUserContentControllerHostApi, + TestWKWebViewConfigurationHostApi, + TestWKWebViewHostApi, + TestWKWebsiteDataStoreHostApi, +]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('WebKit', () { + late InstanceManager instanceManager; + + setUp(() { + instanceManager = InstanceManager(); + }); + + group('$WKWebsiteDataStore', () { + late MockTestWKWebsiteDataStoreHostApi mockPlatformHostApi; + + late WKWebsiteDataStore websiteDataStore; + + late WKWebViewConfiguration webViewConfiguration; + + setUp(() { + mockPlatformHostApi = MockTestWKWebsiteDataStoreHostApi(); + TestWKWebsiteDataStoreHostApi.setup(mockPlatformHostApi); + + TestWKWebViewConfigurationHostApi.setup( + MockTestWKWebViewConfigurationHostApi(), + ); + webViewConfiguration = WKWebViewConfiguration( + instanceManager: instanceManager, + ); + + websiteDataStore = WKWebsiteDataStore.fromWebViewConfiguration( + webViewConfiguration, + instanceManager: instanceManager, + ); + }); + + tearDown(() { + TestWKWebsiteDataStoreHostApi.setup(null); + TestWKWebViewConfigurationHostApi.setup(null); + }); + + test('createFromWebViewConfiguration', () { + verify(mockPlatformHostApi.createFromWebViewConfiguration( + instanceManager.getInstanceId(websiteDataStore), + instanceManager.getInstanceId(webViewConfiguration), + )); + }); + + test('removeDataOfTypes', () { + websiteDataStore.removeDataOfTypes( + {WKWebsiteDataTypes.cookies}, + DateTime.fromMillisecondsSinceEpoch(5000), + ); + + final WKWebsiteDataTypesEnumData typeData = + verify(mockPlatformHostApi.removeDataOfTypes( + instanceManager.getInstanceId(websiteDataStore), + captureAny, + 5.0, + )).captured.single.single as WKWebsiteDataTypesEnumData; + + expect(typeData.value, WKWebsiteDataTypesEnum.cookies); + }); + }); + + group('$WKScriptMessageHandler', () { + late MockTestWKScriptMessageHandlerHostApi mockPlatformHostApi; + + late WKScriptMessageHandler scriptMessageHandler; + + setUp(() async { + mockPlatformHostApi = MockTestWKScriptMessageHandlerHostApi(); + TestWKScriptMessageHandlerHostApi.setup(mockPlatformHostApi); + + scriptMessageHandler = WKScriptMessageHandler( + instanceManager: instanceManager, + ); + }); + + tearDown(() { + TestWKScriptMessageHandlerHostApi.setup(null); + }); + + test('create', () async { + verify(mockPlatformHostApi.create( + instanceManager.getInstanceId(scriptMessageHandler), + )); + }); + }); + + group('$WKPreferences', () { + late MockTestWKPreferencesHostApi mockPlatformHostApi; + + late WKPreferences preferences; + + late WKWebViewConfiguration webViewConfiguration; + + setUp(() { + mockPlatformHostApi = MockTestWKPreferencesHostApi(); + TestWKPreferencesHostApi.setup(mockPlatformHostApi); + + TestWKWebViewConfigurationHostApi.setup( + MockTestWKWebViewConfigurationHostApi(), + ); + webViewConfiguration = WKWebViewConfiguration( + instanceManager: instanceManager, + ); + + preferences = WKPreferences.fromWebViewConfiguration( + webViewConfiguration, + instanceManager: instanceManager, + ); + }); + + tearDown(() { + TestWKPreferencesHostApi.setup(null); + TestWKWebViewConfigurationHostApi.setup(null); + }); + + test('createFromWebViewConfiguration', () async { + verify(mockPlatformHostApi.createFromWebViewConfiguration( + instanceManager.getInstanceId(preferences), + instanceManager.getInstanceId(webViewConfiguration), + )); + }); + + test('setJavaScriptEnabled', () async { + await preferences.setJavaScriptEnabled(true); + verify(mockPlatformHostApi.setJavaScriptEnabled( + instanceManager.getInstanceId(preferences), + true, + )); + }); + }); + + group('$WKUserContentController', () { + late MockTestWKUserContentControllerHostApi mockPlatformHostApi; + + late WKUserContentController userContentController; + + late WKWebViewConfiguration webViewConfiguration; + + setUp(() { + mockPlatformHostApi = MockTestWKUserContentControllerHostApi(); + TestWKUserContentControllerHostApi.setup(mockPlatformHostApi); + + TestWKWebViewConfigurationHostApi.setup( + MockTestWKWebViewConfigurationHostApi(), + ); + webViewConfiguration = WKWebViewConfiguration( + instanceManager: instanceManager, + ); + + userContentController = + WKUserContentController.fromWebViewConfiguration( + webViewConfiguration, + instanceManager: instanceManager, + ); + }); + + tearDown(() { + TestWKUserContentControllerHostApi.setup(null); + TestWKWebViewConfigurationHostApi.setup(null); + }); + + test('createFromWebViewConfiguration', () async { + verify(mockPlatformHostApi.createFromWebViewConfiguration( + instanceManager.getInstanceId(userContentController), + instanceManager.getInstanceId(webViewConfiguration), + )); + }); + + test('addScriptMessageHandler', () async { + TestWKScriptMessageHandlerHostApi.setup( + MockTestWKScriptMessageHandlerHostApi(), + ); + final WKScriptMessageHandler handler = WKScriptMessageHandler( + instanceManager: instanceManager, + ); + + userContentController.addScriptMessageHandler(handler, 'handlerName'); + verify(mockPlatformHostApi.addScriptMessageHandler( + instanceManager.getInstanceId(userContentController), + instanceManager.getInstanceId(handler), + 'handlerName', + )); + }); + + test('removeScriptMessageHandler', () async { + userContentController.removeScriptMessageHandler('handlerName'); + verify(mockPlatformHostApi.removeScriptMessageHandler( + instanceManager.getInstanceId(userContentController), + 'handlerName', + )); + }); + + test('removeAllScriptMessageHandlers', () async { + userContentController.removeAllScriptMessageHandlers(); + verify(mockPlatformHostApi.removeAllScriptMessageHandlers( + instanceManager.getInstanceId(userContentController), + )); + }); + + test('addUserScript', () { + userContentController.addUserScript(const WKUserScript( + 'aScript', + WKUserScriptInjectionTime.atDocumentEnd, + isMainFrameOnly: false, + )); + verify(mockPlatformHostApi.addUserScript( + instanceManager.getInstanceId(userContentController), + argThat(isA()), + )); + }); + + test('removeAllUserScripts', () { + userContentController.removeAllUserScripts(); + verify(mockPlatformHostApi.removeAllUserScripts( + instanceManager.getInstanceId(userContentController), + )); + }); + }); + + group('$WKWebViewConfiguration', () { + late MockTestWKWebViewConfigurationHostApi mockPlatformHostApi; + + late WKWebViewConfiguration webViewConfiguration; + + setUp(() async { + mockPlatformHostApi = MockTestWKWebViewConfigurationHostApi(); + TestWKWebViewConfigurationHostApi.setup(mockPlatformHostApi); + + webViewConfiguration = WKWebViewConfiguration( + instanceManager: instanceManager, + ); + }); + + tearDown(() { + TestWKWebViewConfigurationHostApi.setup(null); + }); + + test('create', () async { + verify( + mockPlatformHostApi.create(instanceManager.getInstanceId( + webViewConfiguration, + )), + ); + }); + + test('createFromWebView', () async { + TestWKWebViewHostApi.setup(MockTestWKWebViewHostApi()); + final WKWebView webView = WKWebView( + webViewConfiguration, + instanceManager: instanceManager, + ); + + final WKWebViewConfiguration configurationFromWebView = + WKWebViewConfiguration.fromWebView( + webView, + instanceManager: instanceManager, + ); + verify(mockPlatformHostApi.createFromWebView( + instanceManager.getInstanceId(configurationFromWebView)!, + instanceManager.getInstanceId(webView)!, + )); + }); + + test('allowsInlineMediaPlayback', () { + webViewConfiguration.setAllowsInlineMediaPlayback(true); + verify(mockPlatformHostApi.setAllowsInlineMediaPlayback( + instanceManager.getInstanceId(webViewConfiguration), + true, + )); + }); + + test('mediaTypesRequiringUserActionForPlayback', () { + webViewConfiguration.setMediaTypesRequiringUserActionForPlayback( + { + WKAudiovisualMediaType.audio, + WKAudiovisualMediaType.video, + }, + ); + + final List typeData = verify( + mockPlatformHostApi.setMediaTypesRequiringUserActionForPlayback( + instanceManager.getInstanceId(webViewConfiguration), + captureAny, + )).captured.single as List; + + expect(typeData, hasLength(2)); + expect(typeData[0]!.value, WKAudiovisualMediaTypeEnum.audio); + expect(typeData[1]!.value, WKAudiovisualMediaTypeEnum.video); + }); + }); + + group('$WKNavigationDelegate', () { + late MockTestWKNavigationDelegateHostApi mockPlatformHostApi; + + late WKNavigationDelegate navigationDelegate; + + setUp(() async { + mockPlatformHostApi = MockTestWKNavigationDelegateHostApi(); + TestWKNavigationDelegateHostApi.setup(mockPlatformHostApi); + + navigationDelegate = WKNavigationDelegate( + instanceManager: instanceManager, + ); + }); + + tearDown(() { + TestWKNavigationDelegateHostApi.setup(null); + }); + + test('create', () async { + verify(mockPlatformHostApi.create( + instanceManager.getInstanceId(navigationDelegate), + )); + }); + }); + + group('$WKWebView', () { + late MockTestWKWebViewHostApi mockPlatformHostApi; + + late WKWebViewConfiguration webViewConfiguration; + + late WKWebView webView; + late int webViewInstanceId; + + setUp(() { + mockPlatformHostApi = MockTestWKWebViewHostApi(); + TestWKWebViewHostApi.setup(mockPlatformHostApi); + + TestWKWebViewConfigurationHostApi.setup( + MockTestWKWebViewConfigurationHostApi()); + webViewConfiguration = WKWebViewConfiguration( + instanceManager: instanceManager, + ); + + webView = WKWebView( + webViewConfiguration, + instanceManager: instanceManager, + ); + webViewInstanceId = instanceManager.getInstanceId(webView)!; + }); + + tearDown(() { + TestWKWebViewHostApi.setup(null); + TestWKWebViewConfigurationHostApi.setup(null); + }); + + test('create', () async { + verify(mockPlatformHostApi.create( + instanceManager.getInstanceId(webView), + instanceManager.getInstanceId( + webViewConfiguration, + ), + )); + }); + + test('setUIDelegate', () async { + TestWKUIDelegateHostApi.setup(MockTestWKUIDelegateHostApi()); + final WKUIDelegate uiDelegate = WKUIDelegate( + instanceManager: instanceManager, + ); + + await webView.setUIDelegate(uiDelegate); + verify(mockPlatformHostApi.setUIDelegate( + webViewInstanceId, + instanceManager.getInstanceId(uiDelegate), + )); + + TestWKUIDelegateHostApi.setup(null); + }); + + test('setNavigationDelegate', () async { + TestWKNavigationDelegateHostApi.setup( + MockTestWKNavigationDelegateHostApi(), + ); + final WKNavigationDelegate navigationDelegate = WKNavigationDelegate( + instanceManager: instanceManager, + ); + + await webView.setNavigationDelegate(navigationDelegate); + verify(mockPlatformHostApi.setNavigationDelegate( + webViewInstanceId, + instanceManager.getInstanceId(navigationDelegate), + )); + + TestWKNavigationDelegateHostApi.setup(null); + }); + + test('getUrl', () { + when( + mockPlatformHostApi.getUrl(webViewInstanceId), + ).thenReturn('www.flutter.dev'); + expect(webView.getUrl(), completion('www.flutter.dev')); + }); + + test('getEstimatedProgress', () { + when( + mockPlatformHostApi.getEstimatedProgress(webViewInstanceId), + ).thenReturn(54.5); + expect(webView.getEstimatedProgress(), completion(54.5)); + }); + + test('loadRequest', () { + webView.loadRequest(const NSUrlRequest(url: 'www.flutter.dev')); + verify(mockPlatformHostApi.loadRequest( + webViewInstanceId, + argThat(isA()), + )); + }); + + test('loadHtmlString', () { + webView.loadHtmlString('a', baseUrl: 'b'); + verify(mockPlatformHostApi.loadHtmlString(webViewInstanceId, 'a', 'b')); + }); + + test('loadFileUrl', () { + webView.loadFileUrl('a', readAccessUrl: 'b'); + verify(mockPlatformHostApi.loadFileUrl(webViewInstanceId, 'a', 'b')); + }); + + test('loadFlutterAsset', () { + webView.loadFlutterAsset('a'); + verify(mockPlatformHostApi.loadFlutterAsset(webViewInstanceId, 'a')); + }); + + test('canGoBack', () { + when(mockPlatformHostApi.canGoBack(webViewInstanceId)).thenReturn(true); + expect(webView.canGoBack(), completion(isTrue)); + }); + + test('canGoForward', () { + when(mockPlatformHostApi.canGoForward(webViewInstanceId)) + .thenReturn(false); + expect(webView.canGoForward(), completion(isFalse)); + }); + + test('goBack', () { + webView.goBack(); + verify(mockPlatformHostApi.goBack(webViewInstanceId)); + }); + + test('goForward', () { + webView.goForward(); + verify(mockPlatformHostApi.goForward(webViewInstanceId)); + }); + + test('reload', () { + webView.reload(); + verify(mockPlatformHostApi.reload(webViewInstanceId)); + }); + + test('getTitle', () { + when(mockPlatformHostApi.getTitle(webViewInstanceId)) + .thenReturn('MyTitle'); + expect(webView.getTitle(), completion('MyTitle')); + }); + + test('setAllowsBackForwardNavigationGestures', () { + webView.setAllowsBackForwardNavigationGestures(false); + verify(mockPlatformHostApi.setAllowsBackForwardNavigationGestures( + webViewInstanceId, + false, + )); + }); + + test('customUserAgent', () { + webView.setCustomUserAgent('hello'); + verify(mockPlatformHostApi.setCustomUserAgent( + webViewInstanceId, + 'hello', + )); + }); + + test('evaluateJavaScript', () { + when(mockPlatformHostApi.evaluateJavaScript(webViewInstanceId, 'gogo')) + .thenAnswer((_) => Future.value('stopstop')); + expect(webView.evaluateJavaScript('gogo'), completion('stopstop')); + }); + }); + + group('$WKUIDelegate', () { + late MockTestWKUIDelegateHostApi mockPlatformHostApi; + + late WKUIDelegate uiDelegate; + + setUp(() async { + mockPlatformHostApi = MockTestWKUIDelegateHostApi(); + TestWKUIDelegateHostApi.setup(mockPlatformHostApi); + + uiDelegate = WKUIDelegate(instanceManager: instanceManager); + }); + + tearDown(() { + TestWKUIDelegateHostApi.setup(null); + }); + + test('create', () async { + verify(mockPlatformHostApi.create( + instanceManager.getInstanceId(uiDelegate), + )); + }); + }); + }); +} 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 new file mode 100644 index 000000000000..a2f16317f1fe --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart @@ -0,0 +1,287 @@ +// Mocks generated by Mockito 5.1.0 from annotations +// in webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i4; + +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart' + as _i3; + +import '../common/test_web_kit.pigeon.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: 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 + +/// A class which mocks [TestWKNavigationDelegateHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKNavigationDelegateHostApi extends _i1.Mock + implements _i2.TestWKNavigationDelegateHostApi { + MockTestWKNavigationDelegateHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create(int? instanceId) => + super.noSuchMethod(Invocation.method(#create, [instanceId]), + returnValueForMissingStub: null); +} + +/// A class which mocks [TestWKPreferencesHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKPreferencesHostApi extends _i1.Mock + implements _i2.TestWKPreferencesHostApi { + MockTestWKPreferencesHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void createFromWebViewConfiguration( + int? instanceId, int? configurationInstanceId) => + super.noSuchMethod( + Invocation.method(#createFromWebViewConfiguration, + [instanceId, configurationInstanceId]), + returnValueForMissingStub: null); + @override + void setJavaScriptEnabled(int? instanceId, bool? enabled) => + super.noSuchMethod( + Invocation.method(#setJavaScriptEnabled, [instanceId, enabled]), + returnValueForMissingStub: null); +} + +/// A class which mocks [TestWKScriptMessageHandlerHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKScriptMessageHandlerHostApi extends _i1.Mock + implements _i2.TestWKScriptMessageHandlerHostApi { + MockTestWKScriptMessageHandlerHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create(int? instanceId) => + super.noSuchMethod(Invocation.method(#create, [instanceId]), + returnValueForMissingStub: null); +} + +/// A class which mocks [TestWKUIDelegateHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKUIDelegateHostApi extends _i1.Mock + implements _i2.TestWKUIDelegateHostApi { + MockTestWKUIDelegateHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create(int? instanceId) => + super.noSuchMethod(Invocation.method(#create, [instanceId]), + returnValueForMissingStub: null); +} + +/// A class which mocks [TestWKUserContentControllerHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKUserContentControllerHostApi extends _i1.Mock + implements _i2.TestWKUserContentControllerHostApi { + MockTestWKUserContentControllerHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void createFromWebViewConfiguration( + int? instanceId, int? configurationInstanceId) => + super.noSuchMethod( + Invocation.method(#createFromWebViewConfiguration, + [instanceId, configurationInstanceId]), + returnValueForMissingStub: null); + @override + void addScriptMessageHandler( + int? instanceId, int? handlerInstanceid, String? name) => + super.noSuchMethod( + Invocation.method( + #addScriptMessageHandler, [instanceId, handlerInstanceid, name]), + returnValueForMissingStub: null); + @override + void removeScriptMessageHandler(int? instanceId, String? name) => + super.noSuchMethod( + Invocation.method(#removeScriptMessageHandler, [instanceId, name]), + returnValueForMissingStub: null); + @override + void removeAllScriptMessageHandlers(int? instanceId) => super.noSuchMethod( + Invocation.method(#removeAllScriptMessageHandlers, [instanceId]), + returnValueForMissingStub: null); + @override + void addUserScript(int? instanceId, _i3.WKUserScriptData? userScript) => super + .noSuchMethod(Invocation.method(#addUserScript, [instanceId, userScript]), + returnValueForMissingStub: null); + @override + void removeAllUserScripts(int? instanceId) => + super.noSuchMethod(Invocation.method(#removeAllUserScripts, [instanceId]), + returnValueForMissingStub: null); +} + +/// A class which mocks [TestWKWebViewConfigurationHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKWebViewConfigurationHostApi extends _i1.Mock + implements _i2.TestWKWebViewConfigurationHostApi { + MockTestWKWebViewConfigurationHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create(int? instanceId) => + super.noSuchMethod(Invocation.method(#create, [instanceId]), + returnValueForMissingStub: null); + @override + void createFromWebView(int? instanceId, int? webViewInstanceId) => + super.noSuchMethod( + Invocation.method( + #createFromWebView, [instanceId, webViewInstanceId]), + returnValueForMissingStub: null); + @override + void setAllowsInlineMediaPlayback(int? instanceId, bool? allow) => + super.noSuchMethod( + Invocation.method(#setAllowsInlineMediaPlayback, [instanceId, allow]), + returnValueForMissingStub: null); + @override + void setMediaTypesRequiringUserActionForPlayback( + int? instanceId, List<_i3.WKAudiovisualMediaTypeEnumData?>? types) => + super.noSuchMethod( + Invocation.method(#setMediaTypesRequiringUserActionForPlayback, + [instanceId, types]), + returnValueForMissingStub: null); +} + +/// A class which mocks [TestWKWebViewHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKWebViewHostApi extends _i1.Mock + implements _i2.TestWKWebViewHostApi { + MockTestWKWebViewHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create(int? instanceId, int? configurationInstanceId) => + super.noSuchMethod( + Invocation.method(#create, [instanceId, configurationInstanceId]), + returnValueForMissingStub: null); + @override + void setUIDelegate(int? instanceId, int? uiDelegateInstanceId) => + super.noSuchMethod( + Invocation.method(#setUIDelegate, [instanceId, uiDelegateInstanceId]), + returnValueForMissingStub: null); + @override + void setNavigationDelegate( + int? instanceId, int? navigationDelegateInstanceId) => + super.noSuchMethod( + Invocation.method(#setNavigationDelegate, + [instanceId, navigationDelegateInstanceId]), + returnValueForMissingStub: null); + @override + String? getUrl(int? instanceId) => + (super.noSuchMethod(Invocation.method(#getUrl, [instanceId])) as String?); + @override + double getEstimatedProgress(int? instanceId) => (super.noSuchMethod( + Invocation.method(#getEstimatedProgress, [instanceId]), + returnValue: 0.0) as double); + @override + void loadRequest(int? instanceId, _i3.NSUrlRequestData? request) => + super.noSuchMethod(Invocation.method(#loadRequest, [instanceId, request]), + returnValueForMissingStub: null); + @override + void loadHtmlString(int? instanceId, String? string, String? baseUrl) => + super.noSuchMethod( + Invocation.method(#loadHtmlString, [instanceId, string, baseUrl]), + returnValueForMissingStub: null); + @override + void loadFileUrl(int? instanceId, String? url, String? readAccessUrl) => + super.noSuchMethod( + Invocation.method(#loadFileUrl, [instanceId, url, readAccessUrl]), + returnValueForMissingStub: null); + @override + void loadFlutterAsset(int? instanceId, String? key) => super.noSuchMethod( + Invocation.method(#loadFlutterAsset, [instanceId, key]), + returnValueForMissingStub: null); + @override + bool canGoBack(int? instanceId) => + (super.noSuchMethod(Invocation.method(#canGoBack, [instanceId]), + returnValue: false) as bool); + @override + bool canGoForward(int? instanceId) => + (super.noSuchMethod(Invocation.method(#canGoForward, [instanceId]), + returnValue: false) as bool); + @override + void goBack(int? instanceId) => + super.noSuchMethod(Invocation.method(#goBack, [instanceId]), + returnValueForMissingStub: null); + @override + void goForward(int? instanceId) => + super.noSuchMethod(Invocation.method(#goForward, [instanceId]), + returnValueForMissingStub: null); + @override + void reload(int? instanceId) => + super.noSuchMethod(Invocation.method(#reload, [instanceId]), + returnValueForMissingStub: null); + @override + String? getTitle(int? instanceId) => + (super.noSuchMethod(Invocation.method(#getTitle, [instanceId])) + as String?); + @override + void setAllowsBackForwardNavigationGestures(int? instanceId, bool? allow) => + super.noSuchMethod( + Invocation.method( + #setAllowsBackForwardNavigationGestures, [instanceId, allow]), + returnValueForMissingStub: null); + @override + void setCustomUserAgent(int? instanceId, String? userAgent) => + super.noSuchMethod( + Invocation.method(#setCustomUserAgent, [instanceId, userAgent]), + returnValueForMissingStub: null); + @override + _i4.Future evaluateJavaScript( + int? instanceId, String? javascriptString) => + (super.noSuchMethod( + Invocation.method( + #evaluateJavaScript, [instanceId, javascriptString]), + returnValue: Future.value('')) as _i4.Future); +} + +/// A class which mocks [TestWKWebsiteDataStoreHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKWebsiteDataStoreHostApi extends _i1.Mock + implements _i2.TestWKWebsiteDataStoreHostApi { + MockTestWKWebsiteDataStoreHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void createFromWebViewConfiguration( + int? instanceId, int? configurationInstanceId) => + super.noSuchMethod( + Invocation.method(#createFromWebViewConfiguration, + [instanceId, configurationInstanceId]), + returnValueForMissingStub: null); + @override + _i4.Future removeDataOfTypes( + int? instanceId, + List<_i3.WKWebsiteDataTypesEnumData?>? dataTypes, + double? secondsModifiedSinceEpoch) => + (super.noSuchMethod( + Invocation.method(#removeDataOfTypes, + [instanceId, dataTypes, secondsModifiedSinceEpoch]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 5d36e593fadb..0c36f6b7de5a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -76,7 +76,7 @@ void main() { when(mockWebView.scrollView).thenReturn(mockScrollView); - when(mockWebViewConfiguration.webSiteDataStore).thenReturn( + when(mockWebViewConfiguration.websiteDataStore).thenReturn( mockWebsiteDataStore, ); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index 8c8758d5139c..6c34e0c72004 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. // Do not manually edit this file. import 'dart:async' as _i5; @@ -37,13 +37,13 @@ class _FakeWKWebViewConfiguration_1 extends _i1.Fake class _FakeUIScrollView_2 extends _i1.Fake implements _i4.UIScrollView {} -class _FakeWKWebsiteDataStore_3 extends _i1.Fake - implements _i3.WKWebsiteDataStore {} - -class _FakeWKUserContentController_4 extends _i1.Fake +class _FakeWKUserContentController_3 extends _i1.Fake implements _i3.WKUserContentController {} -class _FakeWKPreferences_5 extends _i1.Fake implements _i3.WKPreferences {} +class _FakeWKPreferences_4 extends _i1.Fake implements _i3.WKPreferences {} + +class _FakeWKWebsiteDataStore_5 extends _i1.Fake + implements _i3.WKWebsiteDataStore {} class _FakeWKWebView_6 extends _i1.Fake implements _i3.WKWebView {} @@ -69,18 +69,50 @@ class MockUIScrollView extends _i1.Mock implements _i4.UIScrollView { returnValue: Future<_i2.Point>.value(_FakePoint_0())) as _i5.Future<_i2.Point>); @override + _i5.Future scrollBy(_i2.Point? offset) => + (super.noSuchMethod(Invocation.method(#scrollBy, [offset]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setContentOffset(_i2.Point? offset) => + (super.noSuchMethod(Invocation.method(#setContentOffset, [offset]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override _i5.Future setBackgroundColor(_i6.Color? color) => (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i5.Future scrollBy(_i2.Point? offset) => - (super.noSuchMethod(Invocation.method(#scrollBy, [offset]), + _i5.Future setOpaque(bool? opaque) => + (super.noSuchMethod(Invocation.method(#setOpaque, [opaque]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i5.Future setContentOffset(_i5.FutureOr<_i2.Point>? offset) => - (super.noSuchMethod(Invocation.method(#setContentOffset, [offset]), + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future dispose() => + (super.noSuchMethod(Invocation.method(#dispose, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setObserveValue( + void Function( + String, _i7.NSObject, Map<_i7.NSKeyValueChangeKey, Object?>)? + observeValue) => + (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); } @@ -306,6 +338,11 @@ class MockWKWebView extends _i1.Mock implements _i3.WKWebView { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); @override + _i5.Future dispose() => + (super.noSuchMethod(Invocation.method(#dispose, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override _i5.Future setObserveValue( void Function( String, _i7.NSObject, Map<_i7.NSKeyValueChangeKey, Object?>)? @@ -324,26 +361,19 @@ class MockWKWebViewConfiguration extends _i1.Mock _i1.throwOnMissingStub(this); } - @override - _i3.WKWebsiteDataStore get webSiteDataStore => - (super.noSuchMethod(Invocation.getter(#webSiteDataStore), - returnValue: _FakeWKWebsiteDataStore_3()) as _i3.WKWebsiteDataStore); @override _i3.WKUserContentController get userContentController => (super.noSuchMethod(Invocation.getter(#userContentController), - returnValue: _FakeWKUserContentController_4()) + returnValue: _FakeWKUserContentController_3()) as _i3.WKUserContentController); @override _i3.WKPreferences get preferences => (super.noSuchMethod(Invocation.getter(#preferences), - returnValue: _FakeWKPreferences_5()) as _i3.WKPreferences); + returnValue: _FakeWKPreferences_4()) as _i3.WKPreferences); @override - _i5.Future setWebSiteDataStore( - _i3.WKWebsiteDataStore? websiteDataStore) => - (super.noSuchMethod( - Invocation.method(#setWebSiteDataStore, [websiteDataStore]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + _i3.WKWebsiteDataStore get websiteDataStore => + (super.noSuchMethod(Invocation.getter(#websiteDataStore), + returnValue: _FakeWKWebsiteDataStore_5()) as _i3.WKWebsiteDataStore); @override _i5.Future setAllowsInlineMediaPlayback(bool? allow) => (super .noSuchMethod(Invocation.method(#setAllowsInlineMediaPlayback, [allow]), From 3e44799597d7cd36cbf4d65435454faf7a2bdd62 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 7 Apr 2022 14:06:10 -0400 Subject: [PATCH 099/844] Roll Flutter from f5f9ad915e55 to c7d2935077fb (1 revision) (#5197) --- .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 0001767b4015..c23046b108f7 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f5f9ad915e554283ccff109fed969d9ef255fb91 +c7d2935077fb46d18fe7986f472c5347044256f7 From 7013dbd45efaee6e194211f429a90a57b1778bb2 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Thu, 7 Apr 2022 21:51:10 +0200 Subject: [PATCH 100/844] [local_auth_platform_interface] Export externally used types from local_auth_platform_interface.dart directly. (#5196) --- .../local_auth/local_auth_platform_interface/CHANGELOG.md | 4 ++++ .../lib/local_auth_platform_interface.dart | 6 +++--- .../local_auth_platform_interface/lib/types/types.dart | 7 +++++++ .../local_auth/local_auth_platform_interface/pubspec.yaml | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 packages/local_auth/local_auth_platform_interface/lib/types/types.dart diff --git a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md index 0d8803f93540..d16a91ee259a 100644 --- a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md +++ b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.1 + +* Export externally used types from local_auth_platform_interface.dart directly. + ## 1.0.0 * Initial release. diff --git a/packages/local_auth/local_auth_platform_interface/lib/local_auth_platform_interface.dart b/packages/local_auth/local_auth_platform_interface/lib/local_auth_platform_interface.dart index b909ee90d12b..de652b20f462 100644 --- a/packages/local_auth/local_auth_platform_interface/lib/local_auth_platform_interface.dart +++ b/packages/local_auth/local_auth_platform_interface/lib/local_auth_platform_interface.dart @@ -3,11 +3,11 @@ // found in the LICENSE file. import 'package:local_auth_platform_interface/default_method_channel_platform.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; -import 'package:local_auth_platform_interface/types/auth_options.dart'; -import 'package:local_auth_platform_interface/types/biometric_type.dart'; +import 'package:local_auth_platform_interface/types/types.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +export 'package:local_auth_platform_interface/types/types.dart'; + /// The interface that implementations of local_auth must implement. /// /// Platform implementations should extend this class rather than implement it as `local_auth` diff --git a/packages/local_auth/local_auth_platform_interface/lib/types/types.dart b/packages/local_auth/local_auth_platform_interface/lib/types/types.dart new file mode 100644 index 000000000000..ea43b942cffd --- /dev/null +++ b/packages/local_auth/local_auth_platform_interface/lib/types/types.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. + +export 'auth_messages.dart'; +export 'auth_options.dart'; +export 'biometric_type.dart'; diff --git a/packages/local_auth/local_auth_platform_interface/pubspec.yaml b/packages/local_auth/local_auth_platform_interface/pubspec.yaml index f04268926ebd..cd12a8a2dcab 100644 --- a/packages/local_auth/local_auth_platform_interface/pubspec.yaml +++ b/packages/local_auth/local_auth_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/l issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%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: 1.0.0 +version: 1.0.1 environment: sdk: ">=2.14.0 <3.0.0" From 6db6c925525b23647268bf4b88b6e0737d8c8ec5 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 7 Apr 2022 16:31:12 -0400 Subject: [PATCH 101/844] Roll Flutter from c7d2935077fb to 64f7bf7e2ffd (1 revision) (#5198) --- .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 c23046b108f7..67e6e123190a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c7d2935077fb46d18fe7986f472c5347044256f7 +64f7bf7e2ffd1f56c3185f344ee8bc07aad0e094 From 1647add69e489831d673f8554f2df5285105129e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 7 Apr 2022 17:36:12 -0400 Subject: [PATCH 102/844] Roll Flutter from 64f7bf7e2ffd to b528310f589b (2 revisions) (#5200) --- .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 67e6e123190a..40f762f991f1 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -64f7bf7e2ffd1f56c3185f344ee8bc07aad0e094 +b528310f589b162ec9760903ea30943ea8f38171 From 21f2e8301f64dd1d9c12bbbd1efb4b9aa9a993e2 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 7 Apr 2022 19:56:09 -0400 Subject: [PATCH 103/844] Roll Flutter from b528310f589b to b0de14ce07c0 (4 revisions) (#5202) --- .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 40f762f991f1..3fa2f83271a4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b528310f589b162ec9760903ea30943ea8f38171 +b0de14ce07c00760c74d20bbe23c69a5d4a09b6b From 07de4d02343678303ba429ada58af7d147df6fc6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 7 Apr 2022 22:06:09 -0400 Subject: [PATCH 104/844] Roll Flutter from b0de14ce07c0 to e3f8b50fd523 (5 revisions) (#5205) --- .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 3fa2f83271a4..4af947ec5f05 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b0de14ce07c00760c74d20bbe23c69a5d4a09b6b +e3f8b50fd52342adb14db30a246211779677e70f From 9434dd4ef3dd246aaa702b6837b3aaf216f566f4 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 8 Apr 2022 08:56:05 -0400 Subject: [PATCH 105/844] Roll Flutter from e3f8b50fd523 to 8e259c0fa32b (1 revision) (#5206) --- .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 4af947ec5f05..527d7ec008f1 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e3f8b50fd52342adb14db30a246211779677e70f +8e259c0fa32b67c681c3ba167978cfcf3054dcd9 From 45327fbf3e920ae2825217fc5ce93d7a753eab1e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 8 Apr 2022 10:01:08 -0400 Subject: [PATCH 106/844] Roll Flutter from 8e259c0fa32b to 221235c7ed1a (5 revisions) (#5208) --- .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 527d7ec008f1..12c984204c82 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8e259c0fa32b67c681c3ba167978cfcf3054dcd9 +221235c7ed1aad465b4c4a6c2d6277c954df5ccc From 4aa77f977cab42514d56a01d52004921c30e3601 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 8 Apr 2022 12:26:11 -0400 Subject: [PATCH 107/844] Roll Flutter from 221235c7ed1a to b63c4a6db091 (1 revision) (#5210) --- .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 12c984204c82..17261e759419 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -221235c7ed1aad465b4c4a6c2d6277c954df5ccc +b63c4a6db091a6bb47d5e0e0b32fa5b5b410b28e From f5083a817fc8357b07c0c86690eac2d2997176dd Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 8 Apr 2022 13:31:09 -0400 Subject: [PATCH 108/844] Roll Flutter from b63c4a6db091 to 2979ff6f1342 (1 revision) (#5211) --- .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 17261e759419..61a5368c1a4f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b63c4a6db091a6bb47d5e0e0b32fa5b5b410b28e +2979ff6f13429a37d3c7b2083029b71b0d413738 From ad146f15b77837271b0aff28a5e283328a3d5999 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 8 Apr 2022 14:36:09 -0400 Subject: [PATCH 109/844] Roll Flutter from 2979ff6f1342 to 3109073fd9f9 (1 revision) (#5212) --- .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 61a5368c1a4f..6da5863d9f74 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2979ff6f13429a37d3c7b2083029b71b0d413738 +3109073fd9f9219d81ba20d5cf77ec12e708b53f From 3376571d03846a76ff503767b016a1c4e27e9153 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 8 Apr 2022 20:12:10 -0400 Subject: [PATCH 110/844] Roll Flutter from 3109073fd9f9 to cae740b9032f (1 revision) (#5213) --- .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 6da5863d9f74..9ae85077f980 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3109073fd9f9219d81ba20d5cf77ec12e708b53f +cae740b9032f528dddc7978671b918210070daae From a733da89f1a82eb3da39d44fcffabf1506918a3a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 8 Apr 2022 21:19:11 -0400 Subject: [PATCH 111/844] Roll Flutter from cae740b9032f to c14ca6d32112 (8 revisions) (#5216) --- .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 9ae85077f980..2b0996dde1ba 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -cae740b9032f528dddc7978671b918210070daae +c14ca6d32112cfd7801347ac25ebc1b6863ff118 From d2cbf8ae82a8155b1b8a1be84d365005cabaad82 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 8 Apr 2022 22:24:12 -0400 Subject: [PATCH 112/844] Roll Flutter from c14ca6d32112 to c1227319ab95 (2 revisions) (#5217) --- .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 2b0996dde1ba..9f6b26b73348 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c14ca6d32112cfd7801347ac25ebc1b6863ff118 +c1227319ab95a45743bc2bc4af9546f3c1d9636c From eab13bb6107ce654ee07b113cc62f77e76f84a69 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 9 Apr 2022 02:34:07 -0400 Subject: [PATCH 113/844] Roll Flutter from c1227319ab95 to 8ff3640c9f04 (3 revisions) (#5220) --- .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 9f6b26b73348..37994874d17d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c1227319ab95a45743bc2bc4af9546f3c1d9636c +8ff3640c9f047ec87a86f3ab4dc7081c788979a4 From 1600a9002417484e495dccf30959ed0d5c8bae24 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 9 Apr 2022 05:49:10 -0400 Subject: [PATCH 114/844] Roll Flutter from 8ff3640c9f04 to 074ee4b2f446 (1 revision) (#5221) --- .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 37994874d17d..baae0a825831 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8ff3640c9f047ec87a86f3ab4dc7081c788979a4 +074ee4b2f44621840d4a3ba9a68b7093246cdf0d From 700fba5b1bc74d98924f2a41e4eaa7dfb44fe96c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 9 Apr 2022 06:54:07 -0400 Subject: [PATCH 115/844] Roll Flutter from 074ee4b2f446 to dd73a32c0ce8 (1 revision) (#5222) --- .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 baae0a825831..0dfcd9c461d1 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -074ee4b2f44621840d4a3ba9a68b7093246cdf0d +dd73a32c0ce81cdbcfa80a53ed9baa0f624645e6 From dd2cd964a136b2191ec7887004ac0ddf0657ee89 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Mon, 11 Apr 2022 19:49:09 +0200 Subject: [PATCH 116/844] [local_auth] Update app facing package to use default method channel implementation in new platform interface package. (#5195) --- packages/local_auth/local_auth/CHANGELOG.md | 1 + .../local_auth/lib/auth_strings.dart | 7 +- .../local_auth/local_auth/lib/local_auth.dart | 95 ++--- packages/local_auth/local_auth/pubspec.yaml | 9 +- .../local_auth/test/local_auth_test.dart | 360 ++++++------------ 5 files changed, 168 insertions(+), 304 deletions(-) diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index a2f93cad6286..1ca9fe146c7b 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,6 +1,7 @@ ## NEXT * Adds OS version support information to README. +* Switches over to default method implementation in new platform interface. ## 1.1.11 diff --git a/packages/local_auth/local_auth/lib/auth_strings.dart b/packages/local_auth/local_auth/lib/auth_strings.dart index 3e34659b8dad..585742ac68c2 100644 --- a/packages/local_auth/local_auth/lib/auth_strings.dart +++ b/packages/local_auth/local_auth/lib/auth_strings.dart @@ -9,11 +9,12 @@ // ignore_for_file: public_member_api_docs import 'package:intl/intl.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; /// Android side authentication messages. /// /// Provides default values for all messages. -class AndroidAuthMessages { +class AndroidAuthMessages extends AuthMessages { const AndroidAuthMessages({ this.biometricHint, this.biometricNotRecognized, @@ -38,6 +39,7 @@ class AndroidAuthMessages { final String? goToSettingsDescription; final String? signInTitle; + @override Map get args { return { 'biometricHint': biometricHint ?? androidBiometricHint, @@ -62,7 +64,7 @@ class AndroidAuthMessages { /// iOS side authentication messages. /// /// Provides default values for all messages. -class IOSAuthMessages { +class IOSAuthMessages extends AuthMessages { const IOSAuthMessages({ this.lockOut, this.goToSettingsButton, @@ -77,6 +79,7 @@ class IOSAuthMessages { final String? cancelButton; final String? localizedFallbackTitle; + @override Map get args { return { 'lockOut': lockOut ?? iOSLockOut, diff --git a/packages/local_auth/local_auth/lib/local_auth.dart b/packages/local_auth/local_auth/lib/local_auth.dart index 3e925c00e5ae..32818b31783d 100644 --- a/packages/local_auth/local_auth/lib/local_auth.dart +++ b/packages/local_auth/local_auth/lib/local_auth.dart @@ -12,14 +12,11 @@ import 'dart:async'; import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; import 'package:platform/platform.dart'; - import 'auth_strings.dart'; -import 'error_codes.dart'; - -enum BiometricType { face, fingerprint, iris } -const MethodChannel _channel = MethodChannel('plugins.flutter.io/local_auth'); +export 'package:local_auth_platform_interface/types/biometric_type.dart'; Platform _platform = const LocalPlatform(); @@ -31,7 +28,7 @@ void setMockPathProviderPlatform(Platform platform) { /// A Flutter plugin for authenticating the user identity locally. class LocalAuthentication { /// The `authenticateWithBiometrics` method has been deprecated. - /// Use `authenticate` with `biometricOnly: true` instead + /// Use `authenticate` with `biometricOnly: true` instead. @Deprecated('Use `authenticate` with `biometricOnly: true` instead') Future authenticateWithBiometrics({ required String localizedReason, @@ -41,21 +38,21 @@ class LocalAuthentication { IOSAuthMessages iOSAuthStrings = const IOSAuthMessages(), bool sensitiveTransaction = true, }) => - authenticate( + LocalAuthPlatform.instance.authenticate( localizedReason: localizedReason, - useErrorDialogs: useErrorDialogs, - stickyAuth: stickyAuth, - androidAuthStrings: androidAuthStrings, - iOSAuthStrings: iOSAuthStrings, - sensitiveTransaction: sensitiveTransaction, - biometricOnly: true, + authMessages: [iOSAuthStrings, androidAuthStrings], + options: AuthenticationOptions( + useErrorDialogs: useErrorDialogs, + stickyAuth: stickyAuth, + sensitiveTransaction: sensitiveTransaction, + biometricOnly: true, + ), ); /// Authenticates the user with biometrics available on the device while also /// allowing the user to use device authentication - pin, pattern, passcode. /// - /// Returns a [Future] holding true, if the user successfully authenticated, - /// false otherwise. + /// Returns true, if the user successfully authenticated, false otherwise. /// /// [localizedReason] is the message to show to user while prompting them /// for authentication. This is typically along the lines of: 'Please scan @@ -99,29 +96,17 @@ class LocalAuthentication { IOSAuthMessages iOSAuthStrings = const IOSAuthMessages(), bool sensitiveTransaction = true, bool biometricOnly = false, - }) async { - assert(localizedReason.isNotEmpty); - - final Map args = { - 'localizedReason': localizedReason, - 'useErrorDialogs': useErrorDialogs, - 'stickyAuth': stickyAuth, - 'sensitiveTransaction': sensitiveTransaction, - 'biometricOnly': biometricOnly, - }; - if (_platform.isIOS) { - args.addAll(iOSAuthStrings.args); - } else if (_platform.isAndroid) { - args.addAll(androidAuthStrings.args); - } else { - throw PlatformException( - code: otherOperatingSystem, - message: 'Local authentication does not support non-Android/iOS ' - 'operating systems.', - details: 'Your operating system is ${_platform.operatingSystem}', - ); - } - return (await _channel.invokeMethod('authenticate', args)) ?? false; + }) { + return LocalAuthPlatform.instance.authenticate( + localizedReason: localizedReason, + authMessages: [iOSAuthStrings, androidAuthStrings], + options: AuthenticationOptions( + useErrorDialogs: useErrorDialogs, + stickyAuth: stickyAuth, + sensitiveTransaction: sensitiveTransaction, + biometricOnly: biometricOnly, + ), + ); } /// Returns true if auth was cancelled successfully. @@ -131,7 +116,7 @@ class LocalAuthentication { /// Returns [Future] bool true or false: Future stopAuthentication() async { if (_platform.isAndroid) { - return await _channel.invokeMethod('stopAuthentication') ?? false; + return LocalAuthPlatform.instance.stopAuthentication(); } return true; } @@ -139,16 +124,15 @@ class LocalAuthentication { /// Returns true if device is capable of checking biometrics /// /// Returns a [Future] bool true or false: - Future get canCheckBiometrics async => - (await _channel.invokeListMethod('getAvailableBiometrics'))! - .isNotEmpty; + Future get canCheckBiometrics => + LocalAuthPlatform.instance.deviceSupportsBiometrics(); /// Returns true if device is capable of checking biometrics or is able to /// fail over to device credentials. /// /// Returns a [Future] bool true or false: Future isDeviceSupported() async => - (await _channel.invokeMethod('isDeviceSupported')) ?? false; + LocalAuthPlatform.instance.isDeviceSupported(); /// Returns a list of enrolled biometrics /// @@ -156,27 +140,6 @@ class LocalAuthentication { /// - BiometricType.face /// - BiometricType.fingerprint /// - BiometricType.iris (not yet implemented) - Future> getAvailableBiometrics() async { - final List result = (await _channel.invokeListMethod( - 'getAvailableBiometrics', - )) ?? - []; - final List biometrics = []; - for (final String value in result) { - switch (value) { - case 'face': - biometrics.add(BiometricType.face); - break; - case 'fingerprint': - biometrics.add(BiometricType.fingerprint); - break; - case 'iris': - biometrics.add(BiometricType.iris); - break; - case 'undefined': - break; - } - } - return biometrics; - } + Future> getAvailableBiometrics() => + LocalAuthPlatform.instance.getEnrolledBiometrics(); } diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index 78c79f4abce4..5716eae9546b 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -5,9 +5,13 @@ repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/loc issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 version: 1.1.11 +# Temporarily disable publishing to allow moving Android and iOS +# implementations. +publish_to: none + environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: @@ -23,6 +27,7 @@ dependencies: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 intl: ^0.17.0 + local_auth_platform_interface: ^1.0.1 platform: ^3.0.0 dev_dependencies: @@ -32,4 +37,4 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 + mockito: ^5.1.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 3de9758f9d0c..b92297d90231 100644 --- a/packages/local_auth/local_auth/test/local_auth_test.dart +++ b/packages/local_auth/local_auth/test/local_auth_test.dart @@ -2,255 +2,147 @@ // 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/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:local_auth/auth_strings.dart'; import 'package:local_auth/local_auth.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; +import 'package:local_auth_platform_interface/types/auth_options.dart'; +import 'package:mockito/mockito.dart'; import 'package:platform/platform.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; void main() { - TestWidgetsFlutterBinding.ensureInitialized(); + WidgetsFlutterBinding.ensureInitialized(); + late LocalAuthentication localAuthentication; + late MockLocalAuthPlatform mockLocalAuthPlatform; + + setUp(() { + localAuthentication = LocalAuthentication(); + mockLocalAuthPlatform = MockLocalAuthPlatform(); + LocalAuthPlatform.instance = mockLocalAuthPlatform; + }); + + test('authenticateWithBiometrics calls platform implementation', () { + when(mockLocalAuthPlatform.authenticate( + localizedReason: anyNamed('localizedReason'), + authMessages: anyNamed('authMessages'), + options: anyNamed('options'), + )).thenAnswer((_) async => true); + localAuthentication.authenticateWithBiometrics( + localizedReason: 'Test Reason'); + verify(mockLocalAuthPlatform.authenticate( + localizedReason: 'Test Reason', + authMessages: [ + const IOSAuthMessages(), + const AndroidAuthMessages(), + ], + options: const AuthenticationOptions(biometricOnly: true), + )).called(1); + }); + + test('authenticate calls platform implementation', () { + when(mockLocalAuthPlatform.authenticate( + localizedReason: anyNamed('localizedReason'), + authMessages: anyNamed('authMessages'), + options: anyNamed('options'), + )).thenAnswer((_) async => true); + localAuthentication.authenticate(localizedReason: 'Test Reason'); + verify(mockLocalAuthPlatform.authenticate( + localizedReason: 'Test Reason', + authMessages: [ + const IOSAuthMessages(), + const AndroidAuthMessages(), + ], + options: const AuthenticationOptions(), + )).called(1); + }); - group('LocalAuth', () { - const MethodChannel channel = MethodChannel( - 'plugins.flutter.io/local_auth', - ); + test('isDeviceSupported calls platform implementation', () { + when(mockLocalAuthPlatform.isDeviceSupported()) + .thenAnswer((_) async => true); + localAuthentication.isDeviceSupported(); + verify(mockLocalAuthPlatform.isDeviceSupported()).called(1); + }); - final List log = []; - late LocalAuthentication localAuthentication; + test('getEnrolledBiometrics calls platform implementation', () { + when(mockLocalAuthPlatform.getEnrolledBiometrics()) + .thenAnswer((_) async => []); + localAuthentication.getAvailableBiometrics(); + verify(mockLocalAuthPlatform.getEnrolledBiometrics()).called(1); + }); - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) { - log.add(methodCall); - return Future.value(true); - }); - localAuthentication = LocalAuthentication(); - log.clear(); - }); + test('stopAuthentication calls platform implementation on Android', () { + when(mockLocalAuthPlatform.stopAuthentication()) + .thenAnswer((_) async => true); + setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android')); + localAuthentication.stopAuthentication(); + verify(mockLocalAuthPlatform.stopAuthentication()).called(1); + }); - group('With device auth fail over', () { - test('authenticate with no args on Android.', () async { - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android')); - await localAuthentication.authenticate( - localizedReason: 'Needs secure', - biometricOnly: true, - ); - expect( - log, - [ - isMethodCall( - 'authenticate', - arguments: { - 'localizedReason': 'Needs secure', - 'useErrorDialogs': true, - 'stickyAuth': false, - 'sensitiveTransaction': true, - 'biometricOnly': true, - 'biometricHint': androidBiometricHint, - 'biometricNotRecognized': androidBiometricNotRecognized, - 'biometricSuccess': androidBiometricSuccess, - 'biometricRequired': androidBiometricRequiredTitle, - 'cancelButton': androidCancelButton, - 'deviceCredentialsRequired': - androidDeviceCredentialsRequiredTitle, - 'deviceCredentialsSetupDescription': - androidDeviceCredentialsSetupDescription, - 'goToSetting': goToSettings, - 'goToSettingDescription': androidGoToSettingsDescription, - 'signInTitle': androidSignInTitle, - }, - ), - ], - ); - }); + test('stopAuthentication does not call platform implementation on iOS', () { + setMockPathProviderPlatform(FakePlatform(operatingSystem: 'ios')); + localAuthentication.stopAuthentication(); + verifyNever(mockLocalAuthPlatform.stopAuthentication()); + }); - test('authenticate with no args on iOS.', () async { - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'ios')); - await localAuthentication.authenticate( - localizedReason: 'Needs secure', - biometricOnly: true, - ); - expect( - log, - [ - isMethodCall('authenticate', arguments: { - 'localizedReason': 'Needs secure', - 'useErrorDialogs': true, - 'stickyAuth': false, - 'sensitiveTransaction': true, - 'biometricOnly': true, - 'lockOut': iOSLockOut, - 'goToSetting': goToSettings, - 'goToSettingDescriptionIOS': iOSGoToSettingsDescription, - 'okButton': iOSOkButton, - }), - ], - ); - }); + test('canCheckBiometrics returns correct result', () async { + when(mockLocalAuthPlatform.deviceSupportsBiometrics()) + .thenAnswer((_) async => false); + bool? result; + result = await localAuthentication.canCheckBiometrics; + expect(result, false); + when(mockLocalAuthPlatform.deviceSupportsBiometrics()) + .thenAnswer((_) async => true); + result = await localAuthentication.canCheckBiometrics; + expect(result, true); + verify(mockLocalAuthPlatform.deviceSupportsBiometrics()).called(2); + }); +} - test('authenticate with `localizedFallbackTitle` on iOS.', () async { - const IOSAuthMessages iosAuthMessages = - IOSAuthMessages(localizedFallbackTitle: 'Enter PIN'); - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'ios')); - await localAuthentication.authenticate( - localizedReason: 'Needs secure', - biometricOnly: true, - iOSAuthStrings: iosAuthMessages, - ); - expect( - log, - [ - isMethodCall('authenticate', arguments: { - 'localizedReason': 'Needs secure', - 'useErrorDialogs': true, - 'stickyAuth': false, - 'sensitiveTransaction': true, - 'biometricOnly': true, - 'lockOut': iOSLockOut, - 'goToSetting': goToSettings, - 'goToSettingDescriptionIOS': iOSGoToSettingsDescription, - 'okButton': iOSOkButton, - 'localizedFallbackTitle': 'Enter PIN', - }), - ], - ); - }); +class MockLocalAuthPlatform extends Mock + with MockPlatformInterfaceMixin + implements LocalAuthPlatform { + MockLocalAuthPlatform() { + throwOnMissingStub(this); + } - test('authenticate with no localizedReason on iOS.', () async { - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'ios')); - await expectLater( - localAuthentication.authenticate( - localizedReason: '', - biometricOnly: true, - ), - throwsAssertionError, - ); - }); + @override + Future authenticate({ + String? localizedReason, + Iterable? authMessages = const [ + IOSAuthMessages(), + AndroidAuthMessages() + ], + AuthenticationOptions? options = const AuthenticationOptions(), + }) => + super.noSuchMethod( + Invocation.method(#authenticate, [], { + #localizedReason: localizedReason, + #authMessages: authMessages, + #options: options, + }), + returnValue: Future.value(false)) as Future; - test('authenticate with no sensitive transaction.', () async { - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android')); - await localAuthentication.authenticate( - localizedReason: 'Insecure', - sensitiveTransaction: false, - useErrorDialogs: false, - biometricOnly: true, - ); - expect( - log, - [ - isMethodCall('authenticate', arguments: { - 'localizedReason': 'Insecure', - 'useErrorDialogs': false, - 'stickyAuth': false, - 'sensitiveTransaction': false, - 'biometricOnly': true, - 'biometricHint': androidBiometricHint, - 'biometricNotRecognized': androidBiometricNotRecognized, - 'biometricSuccess': androidBiometricSuccess, - 'biometricRequired': androidBiometricRequiredTitle, - 'cancelButton': androidCancelButton, - 'deviceCredentialsRequired': - androidDeviceCredentialsRequiredTitle, - 'deviceCredentialsSetupDescription': - androidDeviceCredentialsSetupDescription, - 'goToSetting': goToSettings, - 'goToSettingDescription': androidGoToSettingsDescription, - 'signInTitle': androidSignInTitle, - }), - ], - ); - }); - }); + @override + Future> getEnrolledBiometrics() => + super.noSuchMethod(Invocation.method(#getEnrolledBiometrics, []), + returnValue: Future>.value([])) + as Future>; - group('With biometrics only', () { - test('authenticate with no args on Android.', () async { - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android')); - await localAuthentication.authenticate( - localizedReason: 'Needs secure', - ); - expect( - log, - [ - isMethodCall('authenticate', arguments: { - 'localizedReason': 'Needs secure', - 'useErrorDialogs': true, - 'stickyAuth': false, - 'sensitiveTransaction': true, - 'biometricOnly': false, - 'biometricHint': androidBiometricHint, - 'biometricNotRecognized': androidBiometricNotRecognized, - 'biometricSuccess': androidBiometricSuccess, - 'biometricRequired': androidBiometricRequiredTitle, - 'cancelButton': androidCancelButton, - 'deviceCredentialsRequired': - androidDeviceCredentialsRequiredTitle, - 'deviceCredentialsSetupDescription': - androidDeviceCredentialsSetupDescription, - 'goToSetting': goToSettings, - 'goToSettingDescription': androidGoToSettingsDescription, - 'signInTitle': androidSignInTitle, - }), - ], - ); - }); + @override + Future isDeviceSupported() => + super.noSuchMethod(Invocation.method(#isDeviceSupported, []), + returnValue: Future.value(false)) as Future; - test('authenticate with no args on iOS.', () async { - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'ios')); - await localAuthentication.authenticate( - localizedReason: 'Needs secure', - ); - expect( - log, - [ - isMethodCall('authenticate', arguments: { - 'localizedReason': 'Needs secure', - 'useErrorDialogs': true, - 'stickyAuth': false, - 'sensitiveTransaction': true, - 'biometricOnly': false, - 'lockOut': iOSLockOut, - 'goToSetting': goToSettings, - 'goToSettingDescriptionIOS': iOSGoToSettingsDescription, - 'okButton': iOSOkButton, - }), - ], - ); - }); + @override + Future stopAuthentication() => + super.noSuchMethod(Invocation.method(#stopAuthentication, []), + returnValue: Future.value(false)) as Future; - test('authenticate with no sensitive transaction.', () async { - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android')); - await localAuthentication.authenticate( - localizedReason: 'Insecure', - sensitiveTransaction: false, - useErrorDialogs: false, - ); - expect( - log, - [ - isMethodCall('authenticate', arguments: { - 'localizedReason': 'Insecure', - 'useErrorDialogs': false, - 'stickyAuth': false, - 'sensitiveTransaction': false, - 'biometricOnly': false, - 'biometricHint': androidBiometricHint, - 'biometricNotRecognized': androidBiometricNotRecognized, - 'biometricSuccess': androidBiometricSuccess, - 'biometricRequired': androidBiometricRequiredTitle, - 'cancelButton': androidCancelButton, - 'deviceCredentialsRequired': - androidDeviceCredentialsRequiredTitle, - 'deviceCredentialsSetupDescription': - androidDeviceCredentialsSetupDescription, - 'goToSetting': goToSettings, - 'goToSettingDescription': androidGoToSettingsDescription, - 'signInTitle': androidSignInTitle, - }), - ], - ); - }); - }); - }); + @override + Future deviceSupportsBiometrics() => super.noSuchMethod( + Invocation.method(#deviceSupportsBiometrics, []), + returnValue: Future.value(false)) as Future; } From e187f9d5ec7adab8a1c59cc164b5b801abe8a4cc Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 11 Apr 2022 14:29:11 -0400 Subject: [PATCH 117/844] Roll Flutter from dd73a32c0ce8 to 61a86b5bee38 (2 revisions) (#5224) --- .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 0dfcd9c461d1..da189c4029e3 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -dd73a32c0ce81cdbcfa80a53ed9baa0f624645e6 +61a86b5bee381bb001dd7f3c9baab30a97c50c15 From fdcf76c8278df1e3ccd37e1ba9b9102039bd1de8 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 11 Apr 2022 16:39:12 -0400 Subject: [PATCH 118/844] Roll Flutter from 61a86b5bee38 to b08647376976 (18 revisions) (#5227) --- .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 da189c4029e3..1da60dd836a4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -61a86b5bee381bb001dd7f3c9baab30a97c50c15 +b0864737697694dacfcee7e506f13667c00889d6 From ceed798f297d2f0b7ad9ee742b0968837274a75e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 11 Apr 2022 18:09:10 -0400 Subject: [PATCH 119/844] Roll Flutter from b08647376976 to 6b461de0129e (3 revisions) (#5228) --- .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 1da60dd836a4..1eec7021c4be 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b0864737697694dacfcee7e506f13667c00889d6 +6b461de0129ef7215118ef9d76e77bfa4c5f01df From 5d8a3efa910d002680726f77e69a3f6848bd2e40 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Tue, 12 Apr 2022 00:32:29 +0200 Subject: [PATCH 120/844] [local_auth] Add native iOS implementation of platform interface [2] (#5207) * Switch over app facing package to use default method channel implementation in platform interface. * Remove accidental deprecation * Export externally used types from local_auth_platform_interface.dart directly. * Add missing license header * Update imports of types from platform interface package. * Move changes from local_auth/federation_ios to local_auth/federation_ios_2 * Update dependency on platform interface package * Fix analysis issues --- .../local_auth/local_auth/example/ios/Podfile | 6 - .../ios/Runner.xcodeproj/project.pbxproj | 161 +---- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- packages/local_auth/local_auth/pubspec.yaml | 5 +- packages/local_auth/local_auth_ios/AUTHORS | 67 ++ .../local_auth/local_auth_ios/CHANGELOG.md | 3 + packages/local_auth/local_auth_ios/LICENSE | 25 + packages/local_auth/local_auth_ios/README.md | 11 + .../local_auth_ios/example/README.md | 8 + .../integration_test/local_auth_test.dart | 19 + .../ios/Flutter/AppFrameworkInfo.plist | 30 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + .../local_auth_ios/example/ios/Podfile | 43 ++ .../ios/Runner.xcodeproj/project.pbxproj | 630 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/xcschemes/Runner.xcscheme | 97 +++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/ios/Runner/AppDelegate.h | 10 + .../example/ios/Runner/AppDelegate.m | 17 + .../AppIcon.appiconset/Contents.json | 116 ++++ .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 bytes .../Runner/Base.lproj/LaunchScreen.storyboard | 27 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../example/ios/Runner/Info.plist | 51 ++ .../local_auth_ios/example/ios/Runner/main.m | 13 + .../ios/RunnerTests/FLTLocalAuthPluginTests.m | 2 +- .../example/ios/RunnerTests/Info.plist | 0 .../local_auth_ios/example/lib/main.dart | 240 +++++++ .../local_auth_ios/example/pubspec.yaml | 28 + .../example/test_driver/integration_test.dart | 7 + .../ios/Assets/.gitkeep | 0 .../ios/Classes/FLTLocalAuthPlugin.h | 0 .../ios/Classes/FLTLocalAuthPlugin.m | 3 +- .../ios/local_auth_ios.podspec} | 4 +- .../local_auth_ios/lib/local_auth_ios.dart | 88 +++ .../lib/types/auth_messages_ios.dart | 96 +++ .../local_auth/local_auth_ios/pubspec.yaml | 27 + .../local_auth_ios/test/local_auth_test.dart | 163 +++++ 53 files changed, 1881 insertions(+), 173 deletions(-) create mode 100644 packages/local_auth/local_auth_ios/AUTHORS create mode 100644 packages/local_auth/local_auth_ios/CHANGELOG.md create mode 100644 packages/local_auth/local_auth_ios/LICENSE create mode 100644 packages/local_auth/local_auth_ios/README.md create mode 100644 packages/local_auth/local_auth_ios/example/README.md create mode 100644 packages/local_auth/local_auth_ios/example/integration_test/local_auth_test.dart create mode 100644 packages/local_auth/local_auth_ios/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 packages/local_auth/local_auth_ios/example/ios/Flutter/Debug.xcconfig create mode 100644 packages/local_auth/local_auth_ios/example/ios/Flutter/Release.xcconfig create mode 100644 packages/local_auth/local_auth_ios/example/ios/Podfile create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/AppDelegate.h create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/AppDelegate.m create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/Info.plist create mode 100644 packages/local_auth/local_auth_ios/example/ios/Runner/main.m rename packages/local_auth/{local_auth => local_auth_ios}/example/ios/RunnerTests/FLTLocalAuthPluginTests.m (99%) rename packages/local_auth/{local_auth => local_auth_ios}/example/ios/RunnerTests/Info.plist (100%) create mode 100644 packages/local_auth/local_auth_ios/example/lib/main.dart create mode 100644 packages/local_auth/local_auth_ios/example/pubspec.yaml create mode 100644 packages/local_auth/local_auth_ios/example/test_driver/integration_test.dart rename packages/local_auth/{local_auth => local_auth_ios}/ios/Assets/.gitkeep (100%) rename packages/local_auth/{local_auth => local_auth_ios}/ios/Classes/FLTLocalAuthPlugin.h (100%) rename packages/local_auth/{local_auth => local_auth_ios}/ios/Classes/FLTLocalAuthPlugin.m (99%) rename packages/local_auth/{local_auth/ios/local_auth.podspec => local_auth_ios/ios/local_auth_ios.podspec} (89%) create mode 100644 packages/local_auth/local_auth_ios/lib/local_auth_ios.dart create mode 100644 packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart create mode 100644 packages/local_auth/local_auth_ios/pubspec.yaml create mode 100644 packages/local_auth/local_auth_ios/test/local_auth_test.dart diff --git a/packages/local_auth/local_auth/example/ios/Podfile b/packages/local_auth/local_auth/example/ios/Podfile index ef20d8e3c010..f7d6a5e68c3a 100644 --- a/packages/local_auth/local_auth/example/ios/Podfile +++ b/packages/local_auth/local_auth/example/ios/Podfile @@ -29,12 +29,6 @@ flutter_ios_podfile_setup target 'Runner' do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) - - target 'RunnerTests' do - inherit! :search_paths - - pod 'OCMock', '3.5' - end end post_install do |installer| diff --git a/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/project.pbxproj b/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/project.pbxproj index 3de4b94f9d5c..b40fbca4cf66 100644 --- a/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,25 +9,14 @@ /* Begin PBXBuildFile section */ 0CCCD07A2CE24E13C9C1EEA4 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D274A3F79473B1549B2BBD5 /* libPods-Runner.a */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 3398D2E426164AD8005A052F /* FLTLocalAuthPluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3398D2E326164AD8005A052F /* FLTLocalAuthPluginTests.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 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 */; }; - B726772E092FC537C9618264 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 719FE2C7EAF8D9A045E09C29 /* libPods-RunnerTests.a */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - 3398D2D226163948005A052F /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; -/* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { @@ -45,11 +34,8 @@ /* 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 = ""; }; - 3398D2CD26163948005A052F /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 3398D2D126163948005A052F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3398D2DC261649CD005A052F /* liblocal_auth.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = liblocal_auth.a; sourceTree = BUILT_PRODUCTS_DIR; }; 3398D2DF26164A03005A052F /* liblocal_auth.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = liblocal_auth.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 3398D2E326164AD8005A052F /* FLTLocalAuthPluginTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTLocalAuthPluginTests.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 658CDD04B21E4EA92F8EF229 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 719FE2C7EAF8D9A045E09C29 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -71,14 +57,6 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 3398D2CA26163948005A052F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - B726772E092FC537C9618264 /* libPods-RunnerTests.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -90,15 +68,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 33BF11D226680B2E002967F3 /* RunnerTests */ = { - isa = PBXGroup; - children = ( - 3398D2E326164AD8005A052F /* FLTLocalAuthPluginTests.m */, - 3398D2D126163948005A052F /* Info.plist */, - ); - path = RunnerTests; - sourceTree = ""; - }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -113,7 +82,6 @@ 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( - 33BF11D226680B2E002967F3 /* RunnerTests */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, @@ -126,7 +94,6 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, - 3398D2CD26163948005A052F /* RunnerTests.xctest */, ); name = Products; sourceTree = ""; @@ -180,25 +147,6 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 3398D2CC26163948005A052F /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 3398D2D426163948005A052F /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - B5AF6C7A6759E6F38749E537 /* [CP] Check Pods Manifest.lock */, - 3398D2C926163948005A052F /* Sources */, - 3398D2CA26163948005A052F /* Frameworks */, - 3398D2CB26163948005A052F /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 3398D2D326163948005A052F /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = RunnerTests; - productReference = 3398D2CD26163948005A052F /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; 97C146ED1CF9000F007C117D /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; @@ -226,14 +174,9 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1100; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { - 3398D2CC26163948005A052F = { - CreatedOnToolsVersion = 12.4; - ProvisioningStyle = Automatic; - TestTargetID = 97C146ED1CF9000F007C117D; - }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; }; @@ -253,19 +196,11 @@ projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, - 3398D2CC26163948005A052F /* RunnerTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 3398D2CB26163948005A052F /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EC1CF9000F007C117D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -326,39 +261,9 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - B5AF6C7A6759E6F38749E537 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 3398D2C926163948005A052F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 3398D2E426164AD8005A052F /* FLTLocalAuthPluginTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EA1CF9000F007C117D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -371,14 +276,6 @@ }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 3398D2D326163948005A052F /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = 3398D2D226163948005A052F /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -399,53 +296,6 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - 3398D2D526163948005A052F /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 99302E79EC77497F2F274D12 /* Pods-RunnerTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = RunnerTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Debug; - }; - 3398D2D626163948005A052F /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = FEA527BB0A821430FEAA1566 /* Pods-RunnerTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = RunnerTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Release; - }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -597,15 +447,6 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 3398D2D426163948005A052F /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 3398D2D526163948005A052F /* Debug */, - 3398D2D626163948005A052F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 58a5d07a15c8..b2af55dd6d37 100644 --- a/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/local_auth/local_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ + +Google Inc. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> +Bodhi Mulders diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md new file mode 100644 index 000000000000..7f198f2d66c0 --- /dev/null +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +* Initial release from migration to federated architecture. diff --git a/packages/local_auth/local_auth_ios/LICENSE b/packages/local_auth/local_auth_ios/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/local_auth/local_auth_ios/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/local_auth/local_auth_ios/README.md b/packages/local_auth/local_auth_ios/README.md new file mode 100644 index 000000000000..d9f40436b617 --- /dev/null +++ b/packages/local_auth/local_auth_ios/README.md @@ -0,0 +1,11 @@ +# local\_auth\_ios + +The iOS implementation of [`local_auth`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `local_auth` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/local_auth +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/local_auth/local_auth_ios/example/README.md b/packages/local_auth/local_auth_ios/example/README.md new file mode 100644 index 000000000000..a4a6091c9ba6 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/README.md @@ -0,0 +1,8 @@ +# local_auth_example + +Demonstrates how to use the local_auth plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/local_auth/local_auth_ios/example/integration_test/local_auth_test.dart b/packages/local_auth/local_auth_ios/example/integration_test/local_auth_test.dart new file mode 100644 index 000000000000..d73cfd6aa625 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/integration_test/local_auth_test.dart @@ -0,0 +1,19 @@ +// 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:integration_test/integration_test.dart'; + +import 'package:local_auth_ios/local_auth_ios.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('canCheckBiometrics', (WidgetTester tester) async { + expect( + LocalAuthIOS().getEnrolledBiometrics(), + completion(isList), + ); + }); +} diff --git a/packages/local_auth/local_auth_ios/example/ios/Flutter/AppFrameworkInfo.plist b/packages/local_auth/local_auth_ios/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000000..3a9c234f96d4 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + UIRequiredDeviceCapabilities + + arm64 + + MinimumOSVersion + 9.0 + + diff --git a/packages/local_auth/local_auth_ios/example/ios/Flutter/Debug.xcconfig b/packages/local_auth/local_auth_ios/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000000..e8efba114687 --- /dev/null +++ b/packages/local_auth/local_auth_ios/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/local_auth/local_auth_ios/example/ios/Flutter/Release.xcconfig b/packages/local_auth/local_auth_ios/example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000000..399e9340e6f6 --- /dev/null +++ b/packages/local_auth/local_auth_ios/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/local_auth/local_auth_ios/example/ios/Podfile b/packages/local_auth/local_auth_ios/example/ios/Podfile new file mode 100644 index 000000000000..ee8f1d9ec3ef --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Podfile @@ -0,0 +1,43 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.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 + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + + pod 'OCMock','3.5' + 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/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..f8b4056d135d --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,630 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 0CCCD07A2CE24E13C9C1EEA4 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D274A3F79473B1549B2BBD5 /* libPods-Runner.a */; }; + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3398D2E426164AD8005A052F /* FLTLocalAuthPluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3398D2E326164AD8005A052F /* FLTLocalAuthPluginTests.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 691CB38B382734AF80FBCA4C /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ADBFA21B380E07A3A585383D /* libPods-RunnerTests.a */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 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 */ + 3398D2D226163948005A052F /* 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 = ""; }; + 3398D2CD26163948005A052F /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3398D2D126163948005A052F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 3398D2DC261649CD005A052F /* liblocal_auth.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = liblocal_auth.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 3398D2DF26164A03005A052F /* liblocal_auth.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = liblocal_auth.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 3398D2E326164AD8005A052F /* FLTLocalAuthPluginTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTLocalAuthPluginTests.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 658CDD04B21E4EA92F8EF229 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 8D6545CD14E27D6F8299FFD5 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.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; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 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 = ""; }; + 9D274A3F79473B1549B2BBD5 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + ADBFA21B380E07A3A585383D /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + D9B3BCBC68F8928E2907FB87 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + EB36DF6C3F25E00DF4175422 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 3398D2CA26163948005A052F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 691CB38B382734AF80FBCA4C /* libPods-RunnerTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0CCCD07A2CE24E13C9C1EEA4 /* libPods-Runner.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BF11D226680B2E002967F3 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 3398D2E326164AD8005A052F /* FLTLocalAuthPluginTests.m */, + 3398D2D126163948005A052F /* Info.plist */, + ); + 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 = ( + 33BF11D226680B2E002967F3 /* RunnerTests */, + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + F8CC53B854B121315C7319D2 /* Pods */, + E2D5FA899A019BD3E0DB0917 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 3398D2CD26163948005A052F /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + E2D5FA899A019BD3E0DB0917 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 3398D2DF26164A03005A052F /* liblocal_auth.a */, + 3398D2DC261649CD005A052F /* liblocal_auth.a */, + 9D274A3F79473B1549B2BBD5 /* libPods-Runner.a */, + ADBFA21B380E07A3A585383D /* libPods-RunnerTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + F8CC53B854B121315C7319D2 /* Pods */ = { + isa = PBXGroup; + children = ( + EB36DF6C3F25E00DF4175422 /* Pods-Runner.debug.xcconfig */, + 658CDD04B21E4EA92F8EF229 /* Pods-Runner.release.xcconfig */, + 8D6545CD14E27D6F8299FFD5 /* Pods-RunnerTests.debug.xcconfig */, + D9B3BCBC68F8928E2907FB87 /* Pods-RunnerTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 3398D2CC26163948005A052F /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3398D2D426163948005A052F /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 9C21D3AD392EA849AEB09231 /* [CP] Check Pods Manifest.lock */, + 3398D2C926163948005A052F /* Sources */, + 3398D2CA26163948005A052F /* Frameworks */, + 3398D2CB26163948005A052F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 3398D2D326163948005A052F /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 3398D2CD26163948005A052F /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 98D96A2D1A74AF66E3DD2DBC /* [CP] Check Pods Manifest.lock */, + 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 = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = "The Flutter Authors"; + TargetAttributes = { + 3398D2CC26163948005A052F = { + CreatedOnToolsVersion = 12.4; + ProvisioningStyle = Automatic; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 3398D2CC26163948005A052F /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 3398D2CB26163948005A052F /* 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; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + 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; + 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"; + }; + 98D96A2D1A74AF66E3DD2DBC /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9C21D3AD392EA849AEB09231 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 3398D2C926163948005A052F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3398D2E426164AD8005A052F /* FLTLocalAuthPluginTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 3398D2D326163948005A052F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 3398D2D226163948005A052F /* 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 */ + 3398D2D526163948005A052F /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8D6545CD14E27D6F8299FFD5 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = RunnerTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Debug; + }; + 3398D2D626163948005A052F /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D9B3BCBC68F8928E2907FB87 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = RunnerTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Release; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = 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; + 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 = 9.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; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = 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; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.localAuthExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.localAuthExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 3398D2D426163948005A052F /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3398D2D526163948005A052F /* Debug */, + 3398D2D626163948005A052F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..919434a6254f --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..b2af55dd6d37 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/local_auth/local_auth_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/local_auth/local_auth_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/AppDelegate.h b/packages/local_auth/local_auth_ios/example/ios/Runner/AppDelegate.h new file mode 100644 index 000000000000..0681d288bb70 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner/AppDelegate.h @@ -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 +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/AppDelegate.m b/packages/local_auth/local_auth_ios/example/ios/Runner/AppDelegate.m new file mode 100644 index 000000000000..30b87969f44a --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner/AppDelegate.m @@ -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. + +#include "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + // Override point for customization after application launch. + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..d22f10b2ab63 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "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" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..28c6bf03016f6c994b70f38d1b7346e5831b531f GIT binary patch literal 564 zcmV-40?Yl0P)Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV7ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 zcmds$`#;kQ7{|XelZftyR5~xW7?MLxS4^|Hw3&P7^y)@A9Fj{Xm1~_CIV^XZ%SLBn zA;!r`GqGHg=7>xrB{?psZQs88ZaedDoagm^KF{a*>G|dJWRSe^I$DNW008I^+;Kjt z>9p3GNR^I;v>5_`+91i(*G;u5|L+Bu6M=(afLjtkya#yZ175|z$pU~>2#^Z_pCZ7o z1c6UNcv2B3?; zX%qdxCXQpdKRz=#b*q0P%b&o)5ZrNZt7$fiETSK_VaY=mb4GK`#~0K#~9^ zcY!`#Af+4h?UMR-gMKOmpuYeN5P*RKF!(tb`)oe0j2BH1l?=>y#S5pMqkx6i{*=V9JF%>N8`ewGhRE(|WohnD59R^$_36{4>S zDFlPC5|k?;SPsDo87!B{6*7eqmMdU|QZ84>6)Kd9wNfh90=y=TFQay-0__>=<4pk& zYDjgIhL-jQ9o>z32K)BgAH+HxamL{ZL~ozu)Qqe@a`FpH=oQRA8=L-m-1dam(Ix2V z?du;LdMO+ooBelr^_y4{|44tmgH^2hSzPFd;U^!1p>6d|o)(-01z{i&Kj@)z-yfWQ)V#3Uo!_U}q3u`(fOs`_f^ueFii1xBNUB z6MecwJN$CqV&vhc+)b(p4NzGGEgwWNs z@*lUV6LaduZH)4_g!cE<2G6#+hJrWd5(|p1Z;YJ7ifVHv+n49btR}dq?HHDjl{m$T z!jLZcGkb&XS2OG~u%&R$(X+Z`CWec%QKt>NGYvd5g20)PU(dOn^7%@6kQb}C(%=vr z{?RP(z~C9DPnL{q^@pVw@|Vx~@3v!9dCaBtbh2EdtoNHm4kGxp>i#ct)7p|$QJs+U z-a3qtcPvhihub?wnJqEt>zC@)2suY?%-96cYCm$Q8R%-8$PZYsx3~QOLMDf(piXMm zB=<63yQk1AdOz#-qsEDX>>c)EES%$owHKue;?B3)8aRd}m~_)>SL3h2(9X;|+2#7X z+#2)NpD%qJvCQ0a-uzZLmz*ms+l*N}w)3LRQ*6>|Ub-fyptY(keUxw+)jfwF5K{L9 z|Cl_w=`!l_o><384d&?)$6Nh(GAm=4p_;{qVn#hI8lqewW7~wUlyBM-4Z|)cZr?Rh z=xZ&Ol>4(CU85ea(CZ^aO@2N18K>ftl8>2MqetAR53_JA>Fal`^)1Y--Am~UDa4th zKfCYpcXky$XSFDWBMIl(q=Mxj$iMBX=|j9P)^fDmF(5(5$|?Cx}DKEJa&XZP%OyE`*GvvYQ4PV&!g2|L^Q z?YG}tx;sY@GzMmsY`7r$P+F_YLz)(e}% zyakqFB<6|x9R#TdoP{R$>o7y(-`$$p0NxJ6?2B8tH)4^yF(WhqGZlM3=9Ibs$%U1w zWzcss*_c0=v_+^bfb`kBFsI`d;ElwiU%frgRB%qBjn@!0U2zZehBn|{%uNIKBA7n= zzE`nnwTP85{g;8AkYxA68>#muXa!G>xH22D1I*SiD~7C?7Za+9y7j1SHiuSkKK*^O zsZ==KO(Ua#?YUpXl{ViynyT#Hzk=}5X$e04O@fsMQjb}EMuPWFO0e&8(2N(29$@Vd zn1h8Yd>6z(*p^E{c(L0Lg=wVdupg!z@WG;E0k|4a%s7Up5C0c)55XVK*|x9RQeZ1J@1v9MX;>n34(i>=YE@Iur`0Vah(inE3VUFZNqf~tSz{1fz3Fsn_x4F>o(Yo;kpqvBe-sbwH(*Y zu$JOl0b83zu$JMvy<#oH^Wl>aWL*?aDwnS0iEAwC?DK@aT)GHRLhnz2WCvf3Ba;o=aY7 z2{Asu5MEjGOY4O#Ggz@@J;q*0`kd2n8I3BeNuMmYZf{}pg=jTdTCrIIYuW~luKecn z+E-pHY%ohj@uS0%^ z&(OxwPFPD$+#~`H?fMvi9geVLci(`K?Kj|w{rZ9JgthFHV+=6vMbK~0)Ea<&WY-NC zy-PnZft_k2tfeQ*SuC=nUj4H%SQ&Y$gbH4#2sT0cU0SdFs=*W*4hKGpuR1{)mV;Qf5pw4? zfiQgy0w3fC*w&Bj#{&=7033qFR*<*61B4f9K%CQvxEn&bsWJ{&winp;FP!KBj=(P6 z4Z_n4L7cS;ao2)ax?Tm|I1pH|uLpDSRVghkA_UtFFuZ0b2#>!8;>-_0ELjQSD-DRd z4im;599VHDZYtnWZGAB25W-e(2VrzEh|etsv2YoP#VbIZ{aFkwPrzJ#JvCvA*mXS& z`}Q^v9(W4GiSs}#s7BaN!WA2bniM$0J(#;MR>uIJ^uvgD3GS^%*ikdW6-!VFUU?JV zZc2)4cMsX@j z5HQ^e3BUzOdm}yC-xA%SY``k$rbfk z;CHqifhU*jfGM@DkYCecD9vl*qr58l6x<8URB=&%{!Cu3RO*MrKZ4VO}V6R0a zZw3Eg^0iKWM1dcTYZ0>N899=r6?+adUiBKPciJw}L$=1f4cs^bio&cr9baLF>6#BM z(F}EXe-`F=f_@`A7+Q&|QaZ??Txp_dB#lg!NH=t3$G8&06MFhwR=Iu*Im0s_b2B@| znW>X}sy~m#EW)&6E&!*0%}8UAS)wjt+A(io#wGI@Z2S+Ms1Cxl%YVE800007ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 zcmVjJBgitF5mAp-i>4+KS_oR{|13AP->1TD4=w)g|)JHOx|a2Wk1Va z!k)vP$UcQ#mdj%wNQoaJ!w>jv_6&JPyutpQps?s5dmDQ>`%?Bvj>o<%kYG!YW6H-z zu`g$@mp`;qDR!51QaS}|ZToSuAGcJ7$2HF0z`ln4t!#Yg46>;vGG9N9{V@9z#}6v* zfP?}r6b{*-C*)(S>NECI_E~{QYzN5SXRmVnP<=gzP+_Sp(Aza_hKlZ{C1D&l*(7IKXxQC1Z9#6wx}YrGcn~g%;icdw>T0Rf^w0{ z$_wn1J+C0@!jCV<%Go5LA45e{5gY9PvZp8uM$=1}XDI+9m7!A95L>q>>oe0$nC->i zeexUIvq%Uk<-$>DiDb?!In)lAmtuMWxvWlk`2>4lNuhSsjAf2*2tjT`y;@d}($o)S zn(+W&hJ1p0xy@oxP%AM15->wPLp{H!k)BdBD$toBpJh+crWdsNV)qsHaqLg2_s|Ih z`8E9z{E3sA!}5aKu?T!#enD(wLw?IT?k-yWVHZ8Akz4k5(TZJN^zZgm&zM28sfTD2BYJ|Fde3Xzh;;S` z=GXTnY4Xc)8nYoz6&vF;P7{xRF-{|2Xs5>a5)@BrnQ}I(_x7Cgpx#5&Td^4Q9_FnQ zX5so*;#8-J8#c$OlA&JyPp$LKUhC~-e~Ij!L%uSMu!-VZG7Hx-L{m2DVR2i=GR(_% zCVD!4N`I)&Q5S`?P&fQZ=4#Dgt_v2-DzkT}K(9gF0L(owe-Id$Rc2qZVLqI_M_DyO z9@LC#U28_LU{;wGZ&))}0R2P4MhajKCd^K#D+JJ&JIXZ_p#@+7J9A&P<0kdRujtQ_ zOy>3=C$kgi6$0pW06KaLz!21oOryKM3ZUOWqppndxfH}QpgjEJ`j7Tzn5bk6K&@RA?vl##y z$?V~1E(!wB5rH`>3nc&@)|#<1dN2cMzzm=PGhQ|Yppne(C-Vlt450IXc`J4R0W@I7 zd1e5uW6juvO%ni(WX7BsKx3MLngO7rHO;^R5I~0^nE^9^E_eYLgiR9&KnJ)pBbfno zSVnW$0R+&6jOOsZ82}nJ126+c|%svPo;TeUku<2G7%?$oft zyaO;tVo}(W)VsTUhq^XmFi#2z%-W9a{7mXn{uzivYQ_d6b7VJG{77naW(vHt-uhnY zVN#d!JTqVh(7r-lhtXVU6o})aZbDt_;&wJVGl2FKYFBFpU-#9U)z#(A%=IVnqytR$SY-sO( z($oNE09{D^@OuYPz&w~?9>Fl5`g9u&ecFGhqX=^#fmR=we0CJw+5xna*@oHnkahk+ z9aWeE3v|An+O5%?4fA&$Fgu~H_YmqR!yIU!bFCk4!#pAj%(lI(A5n)n@Id#M)O9Yx zJU9oKy{sRAIV3=5>(s8n{8ryJ!;ho}%pn6hZKTKbqk=&m=f*UnK$zW3YQP*)pw$O* zIfLA^!-bmBl6%d_n$#tP8Zd_(XdA*z*WH|E_yILwjtI~;jK#v-6jMl^?<%Y%`gvpwv&cFb$||^v4D&V=aNy?NGo620jL3VZnA%s zH~I|qPzB~e(;p;b^gJr7Ure#7?8%F0m4vzzPy^^(q4q1OdthF}Fi*RmVZN1OwTsAP zn9CZP`FazX3^kG(KodIZ=Kty8DLTy--UKfa1$6XugS zk%6v$Kmxt6U!YMx0JQ)0qX*{CXwZZk$vEROidEc7=J-1;peNat!vS<3P-FT5po>iE z!l3R+<`#x|+_hw!HjQGV=8!q|76y8L7N8gP3$%0kfush|u0uU^?dKBaeRSBUpOZ0c z62;D&Mdn2}N}xHRFTRI?zRv=>=AjHgH}`2k4WK=#AHB)UFrR-J87GgX*x5fL^W2#d z=(%K8-oZfMO=i{aWRDg=FX}UubM4eotRDcn;OR#{3q=*?3mE3_oJ-~prjhxh%PgQT zyn)Qozaq0@o&|LEgS{Ind4Swsr;b`u185hZPOBLL<`d2%^Yp1?oL)=jnLi;Zo0ZDliTtQ^b5SmfIMe{T==zZkbvn$KTQGlbG8w}s@M3TZnde;1Am46P3juKb zl9GU&3F=q`>j!`?SyH#r@O59%@aMX^rx}Nxe<>NqpUp5=lX1ojGDIR*-D^SDuvCKF z?3$xG(gVUsBERef_YjPFl^rU9EtD{pt z0CXwpN7BN3!8>hajGaTVk-wl=9rxmfWtIhC{mheHgStLi^+Nz12a?4r(fz)?3A%at zMlvQmL<2-R)-@G1wJ0^zQK%mR=r4d{Y3fHp){nWXUL#|CqXl(+v+qDh>FkF9`eWrW zfr^D%LNfOcTNvtx0JXR35J0~Jpi2#P3Q&80w+nqNfc}&G0A~*)lGHKv=^FE+b(37|)zL;KLF>oiGfb(?&1 zV3XRu!Sw>@quKiab%g6jun#oZ%!>V#A%+lNc?q>6+VvyAn=kf_6z^(TZUa4Eelh{{ zqFX-#dY(EV@7l$NE&kv9u9BR8&Ojd#ZGJ6l8_BW}^r?DIS_rU2(XaGOK z225E@kH5Opf+CgD^{y29jD4gHbGf{1MD6ggQ&%>UG4WyPh5q_tb`{@_34B?xfSO*| zZv8!)q;^o-bz`MuxXk*G^}(6)ACb@=Lfs`Hxoh>`Y0NE8QRQ!*p|SH@{r8=%RKd4p z+#Ty^-0kb=-H-O`nAA3_6>2z(D=~Tbs(n8LHxD0`R0_ATFqp-SdY3(bZ3;VUM?J=O zKCNsxsgt@|&nKMC=*+ZqmLHhX1KHbAJs{nGVMs6~TiF%Q)P@>!koa$%oS zjXa=!5>P`vC-a}ln!uH1ooeI&v?=?v7?1n~P(wZ~0>xWxd_Aw;+}9#eULM7M8&E?Y zC-ZLhi3RoM92SXUb-5i-Lmt5_rfjE{6y^+24`y$1lywLyHO!)Boa7438K4#iLe?rh z2O~YGSgFUBH?og*6=r9rme=peP~ah`(8Zt7V)j5!V0KPFf_mebo3z95U8(up$-+EA^9dTRLq>Yl)YMBuch9%=e5B`Vnb>o zt03=kq;k2TgGe4|lGne&zJa~h(UGutjP_zr?a7~#b)@15XNA>Dj(m=gg2Q5V4-$)D|Q9}R#002ovPDHLkV1o7DH3k3x literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/local_auth/local_auth_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 zcmV-m2cP(fP)x~L`~4d)Rspd&<9kFh{hn*KP1LP0~$;u(LfAu zp%fx&qLBcRHx$G|3q(bv@+b;o0*D|jwD-Q9uQR(l*ST}s+uPgQ-MeFwZ#GS?b332? z&Tk$&_miXn3IGq)AmQ)3sisq{raD4(k*bHvpCe-TdWq^NRTEVM)i9xbgQ&ccnUVx* zEY%vS%gDcSg=!tuIK8$Th2_((_h^+7;R|G{n06&O2#6%LK`a}n?h_fL18btz<@lFG za}xS}u?#DBMB> zw^b($1Z)`9G?eP95EKi&$eOy@K%h;ryrR3la%;>|o*>CgB(s>dDcNOXg}CK9SPmD? zmr-s{0wRmxUnbDrYfRvnZ@d z6johZ2sMX{YkGSKWd}m|@V7`Degt-43=2M?+jR%8{(H$&MLLmS;-|JxnX2pnz;el1jsvqQz}pGSF<`mqEXRQ5sC4#BbwnB_4` zc5bFE-Gb#JV3tox9fp-vVEN{(tOCpRse`S+@)?%pz+zVJXSooTrNCUg`R6`hxwb{) zC@{O6MKY8tfZ5@!yy=p5Y|#+myRL=^{tc(6YgAnkg3I(Cd!r5l;|;l-MQ8B`;*SCE z{u)uP^C$lOPM z5d~UhKhRRmvv{LIa^|oavk1$QiEApSrP@~Jjbg`<*dW4TO?4qG%a%sTPUFz(QtW5( zM)lA+5)0TvH~aBaOAs|}?u2FO;yc-CZ1gNM1dAxJ?%m?YsGR`}-xk2*dxC}r5j$d* zE!#Vtbo69h>V4V`BL%_&$} z+oJAo@jQ^Tk`;%xw-4G>hhb&)B?##U+(6Fi7nno`C<|#PVA%$Y{}N-?(Gc$1%tr4Pc}}hm~yY#fTOe!@v9s-ik$dX~|ygArPhByaXn8 zpI^FUjNWMsTFKTP3X7m?UK)3m zp6rI^_zxRYrx6_QmhoWoDR`fp4R7gu6;gdO)!KexaoO2D88F9x#TM1(9Bn7g;|?|o z)~$n&Lh#hCP6_LOPD>a)NmhW})LADx2kq=X7}7wYRj-0?dXr&bHaRWCfSqvzFa=sn z-8^gSyn-RmH=BZ{AJZ~!8n5621GbUJV7Qvs%JNv&$%Q17s_X%s-41vAPfIR>;x0Wlqr5?09S>x#%Qkt>?(&XjFRY}*L6BeQ3 z<6XEBh^S7>AbwGm@XP{RkeEKj6@_o%oV?hDuUpUJ+r#JZO?!IUc;r0R?>mi)*ZpQ) z#((dn=A#i_&EQn|hd)N$#A*fjBFuiHcYvo?@y1 z5|fV=a^a~d!c-%ZbMNqkMKiSzM{Yq=7_c&1H!mXk60Uv32dV;vMg&-kQ)Q{+PFtwc zj|-uQ;b^gts??J*9VxxOro}W~Q9j4Em|zSRv)(WSO9$F$s=Ydu%Q+5DOid~lwk&we zY%W(Z@ofdwPHncEZzZgmqS|!gTj3wQq9rxQy+^eNYKr1mj&?tm@wkO*9@UtnRMG>c aR{jt9+;fr}hV%pg00001^@s67{VYS000c7NklQEG_j zup^)eW&WUIApqy$=APz8jE@awGp)!bsTjDbrJO`$x^ZR^dr;>)LW>{ zs70vpsD38v)19rI=GNk1b(0?Js9~rjsQsu*K;@SD40RB-3^gKU-MYC7G!Bw{fZsqp zih4iIi;Hr_xZ033Iu{sQxLS=}yBXgLMn40d++>aQ0#%8D1EbGZp7+ z5=mK?t31BkVYbGOxE9`i748x`YgCMwL$qMsChbSGSE1`p{nSmadR zcQ#R)(?!~dmtD0+D2!K zR9%!Xp1oOJzm(vbLvT^$IKp@+W2=-}qTzTgVtQ!#Y7Gxz}stUIm<1;oBQ^Sh2X{F4ibaOOx;5ZGSNK z0maF^@(UtV$=p6DXLgRURwF95C=|U8?osGhgOED*b z7woJ_PWXBD>V-NjQAm{~T%sjyJ{5tn2f{G%?J!KRSrrGvQ1(^`YLA5B!~eycY(e5_ z*%aa{at13SxC(=7JT7$IQF~R3sy`Nn%EMv!$-8ZEAryB*yB1k&stni)=)8-ODo41g zkJu~roIgAih94tb=YsL%iH5@^b~kU9M-=aqgXIrbtxMpFy5mekFm#edF9z7RQ6V}R zBIhbXs~pMzt0VWy1Fi$^fh+1xxLDoK09&5&MJl(q#THjPm(0=z2H2Yfm^a&E)V+a5 zbi>08u;bJsDRUKR9(INSc7XyuWv(JsD+BB*0hS)FO&l&7MdViuur@-<-EHw>kHRGY zqoT}3fDv2-m{NhBG8X}+rgOEZ;amh*DqN?jEfQdqxdj08`Sr=C-KmT)qU1 z+9Cl)a1mgXxhQiHVB}l`m;-RpmKy?0*|yl?FXvJkFxuu!fKlcmz$kN(a}i*saM3nr z0!;a~_%Xqy24IxA2rz<+08=B-Q|2PT)O4;EaxP^6qixOv7-cRh?*T?zZU`{nIM-at zTKYWr9rJ=tppQ9I#Z#mLgINVB!pO-^FOcvFw6NhV0gztuO?g ztoA*C-52Q-Z-P#xB4HAY3KQVd%dz1S4PA3vHp0aa=zAO?FCt zC_GaTyVBg2F!bBr3U@Zy2iJgIAt>1sf$JWA9kh{;L+P*HfUBX1Zy{4MgNbDfBV_ly z!y#+753arsZUt@366jIC0klaC@ckuk!qu=pAyf7&QmiBUT^L1&tOHzsK)4n|pmrVT zs2($4=?s~VejTFHbFdDOwG;_58LkIj1Fh@{glkO#F1>a==ymJS$z;gdedT1zPx4Kj ztjS`y_C}%af-RtpehdQDt3a<=W5C4$)9W@QAse;WUry$WYmr51ml9lkeunUrE`-3e zmq1SgSOPNEE-Mf+AGJ$g0M;3@w!$Ej;hMh=v=I+Lpz^n%Pg^MgwyqOkNyu2c^of)C z1~ALor3}}+RiF*K4+4{(1%1j3pif1>sv0r^mTZ?5Jd-It!tfPfiG_p$AY*Vfak%FG z4z#;wLtw&E&?}w+eKG^=#jF7HQzr8rV0mY<1YAJ_uGz~$E13p?F^fPSzXSn$8UcI$ z8er9{5w5iv0qf8%70zV71T1IBB1N}R5Kp%NO0=5wJalZt8;xYp;b{1K) zHY>2wW-`Sl{=NpR%iu3(u6l&)rc%%cSA#aV7WCowfbFR4wcc{LQZv~o1u_`}EJA3>ki`?9CKYTA!rhO)if*zRdd}Kn zEPfYbhoVE~!FI_2YbC5qAj1kq;xP6%J8+?2PAs?`V3}nyFVD#sV3+uP`pi}{$l9U^ zSz}_M9f7RgnnRhaoIJgT8us!1aB&4!*vYF07Hp&}L zCRlop0oK4DL@ISz{2_BPlezc;xj2|I z23RlDNpi9LgTG_#(w%cMaS)%N`e>~1&a3<{Xy}>?WbF>OOLuO+j&hc^YohQ$4F&ze z+hwnro1puQjnKm;vFG~o>`kCeUIlkA-2tI?WBKCFLMBY=J{hpSsQ=PDtU$=duS_hq zHpymHt^uuV1q@uc4bFb{MdG*|VoW@15Osrqt2@8ll0qO=j*uOXn{M0UJX#SUztui9FN4)K3{9!y8PC-AHHvpVTU;x|-7P+taAtyglk#rjlH2 z5Gq8ik}BPaGiM{#Woyg;*&N9R2{J0V+WGB69cEtH7F?U~Kbi6ksi*`CFXsi931q7Y zGO82?whBhN%w1iDetv%~wM*Y;E^)@Vl?VDj-f*RX>{;o_=$fU!&KAXbuadYZ46Zbg z&6jMF=49$uL^73y;;N5jaHYv)BTyfh&`qVLYn?`o6BCA_z-0niZz=qPG!vonK3MW_ zo$V96zM!+kJRs{P-5-rQVse0VBH*n6A58)4uc&gfHMa{gIhV2fGf{st>E8sKyP-$8zp~wJX^A*@DI&-;8>gANXZj zU)R+Y)PB?=)a|Kj>8NXEu^S_h^7R`~Q&7*Kn!xyvzVv&^>?^iu;S~R2e-2fJx-oUb cX)(b1KSk$MOV07*qoM6N<$f&6$jw%VRuvdN2+38CZWny1cRtlsl+0_KtW)EU14Ei(F!UtWuj4IK+3{sK@>rh zs1Z;=(DD&U6+tlyL?UnHVN^&g6QhFi2#HS+*qz;(>63G(`|jRtW|nz$Pv7qTovP!^ zP_jES{mr@O-02w%!^a?^1ZP!_KmQiz0L~jZ=W@Qt`8wzOoclQsAS<5YdH;a(4bGLE zk8s}1If(PSIgVi!XE!5kA?~z*sobvNyohr;=Q_@h2@$6Flyej3J)D-6YfheRGl`HEcPk|~huT_2-U?PfL=4BPV)f1o!%rQ!NMt_MYw-5bUSwQ9Z&zC>u zOrl~UJglJNa%f50Ok}?WB{on`Ci`p^Y!xBA?m@rcJXLxtrE0FhRF3d*ir>yzO|BD$ z3V}HpFcCh6bTzY}Nt_(W%QYd3NG)jJ4<`F<1Od) zfQblTdC&h2lCz`>y?>|9o2CdvC8qZeIZt%jN;B7Hdn2l*k4M4MFEtq`q_#5?}c$b$pf_3y{Y!cRDafZBEj-*OD|gz#PBDeu3QoueOesLzB+O zxjf2wvf6Wwz>@AiOo2mO4=TkAV+g~%_n&R;)l#!cBxjuoD$aS-`IIJv7cdX%2{WT7 zOm%5rs(wqyPE^k5SIpUZ!&Lq4<~%{*>_Hu$2|~Xa;iX*tz8~G6O3uFOS?+)tWtdi| zV2b#;zRN!m@H&jd=!$7YY6_}|=!IU@=SjvGDFtL;aCtw06U;-v^0%k0FOyESt z1Wv$={b_H&8FiRV?MrzoHWd>%v6KTRU;-v^Miiz+@q`(BoT!+<37CKhoKb)|8!+RG z6BQFU^@fRW;s8!mOf2QViKQGk0TVER6EG1`#;Nm39Do^PoT!+<37AD!%oJe86(=et zZ~|sLzU>V-qYiU6V8$0GmU7_K8|Fd0B?+9Un1BhKAz#V~Fk^`mJtlCX#{^8^M8!me z8Yg;8-~>!e<-iG;h*0B1kBKm}hItVGY6WnjVpgnTTAC$rqQ^v)4KvOtpY|sIj@WYg zyw##ZZ5AC2IKNC;^hwg9BPk0wLStlmBr;E|$5GoAo$&Ui_;S9WY62n3)i49|T%C#i017z3J=$RF|KyZWnci*@lW4 z=AKhNN6+m`Q!V3Ye68|8y@%=am>YD0nG99M)NWc20%)gwO!96j7muR}Fr&54SxKP2 zP30S~lt=a*qDlbu3+Av57=9v&vr<6g0&`!8E2fq>I|EJGKs}t|{h7+KT@)LfIV-3K zK)r_fr2?}FFyn*MYoLC>oV-J~eavL2ho4a4^r{E-8m2hi>~hA?_vIG4a*KT;2eyl1 zh_hUvUJpNCFwBvRq5BI*srSle>c6%n`#VNsyC|MGa{(P&08p=C9+WUw9Hl<1o9T4M zdD=_C0F7#o8A_bRR?sFNmU0R6tW`ElnF8p53IdHo#S9(JoZCz}fHwJ6F<&?qrpVqE zte|m%89JQD+XwaPU#%#lVs-@-OL);|MdfINd6!XwP2h(eyafTUsoRkA%&@fe?9m@jw-v(yTTiV2(*fthQH9}SqmsRPVnwwbV$1E(_lkmo&S zF-truCU914_$jpqjr(>Ha4HkM4YMT>m~NosUu&UZ>zirfHo%N6PPs9^_o$WqPA0#5 z%tG>qFCL+b*0s?sZ;Sht0nE7Kl>OVXy=gjWxxK;OJ3yGd7-pZf7JYNcZo2*1SF`u6 zHJyRRxGw9mDlOiXqVMsNe#WX`fC`vrtjSQ%KmLcl(lC>ZOQzG^%iql2w-f_K@r?OE zwCICifM#L-HJyc7Gm>Ern?+Sk3&|Khmu4(~3qa$(m6Ub^U0E5RHq49za|XklN#?kP zl;EstdW?(_4D>kwjWy2f!LM)y?F94kyU3`W!6+AyId-89v}sXJpuic^NLL7GJItl~ zsiuB98AI-(#Mnm|=A-R6&2fwJ0JVSY#Q>&3$zFh|@;#%0qeF=j5Ajq@4i0tIIW z&}sk$&fGwoJpe&u-JeGLi^r?dO`m=y(QO{@h zQqAC7$rvz&5+mo3IqE?h=a~6m>%r5Quapvzq;{y~p zJpyXOBgD9VrW7@#p6l7O?o3feml(DtSL>D^R) zZUY%T2b0-vBAFN7VB;M88!~HuOXi4KcI6aRQ&h|XQ0A?m%j2=l1f0cGP}h(oVfJ`N zz#PpmFC*ieab)zJK<4?^k=g%OjPnkANzbAbmGZHoVRk*mTfm75s_cWVa`l*f$B@xu z5E*?&@seIo#*Y~1rBm!7sF9~~u6Wrj5oICUOuz}CS)jdNIznfzCA(stJ(7$c^e5wN z?lt>eYgbA!kvAR7zYSD&*r1$b|(@;9dcZ^67R0 zXAXJKa|5Sdmj!g578Nwt6d$sXuc&MWezA0Whd`94$h{{?1IwXP4)Tx4obDK%xoFZ_Z zjjHJ_P@R_e5blG@yEjnaJb`l;s%Lb2&=8$&Ct-fV`E^4CUs)=jTk!I}2d&n!f@)bm z@ z_4Dc86+3l2*p|~;o-Sb~oXb_RuLmoifDU^&Te$*FevycC0*nE3Xws8gsWp|Rj2>SM zns)qcYj?^2sd8?N!_w~4v+f-HCF|a$TNZDoNl$I1Uq87euoNgKb6&r26TNrfkUa@o zfdiFA@p{K&mH3b8i!lcoz)V{n8Q@g(vR4ns4r6w;K z>1~ecQR0-<^J|Ndg5fvVUM9g;lbu-){#ghGw(fg>L zh)T5Ljb%lWE;V9L!;Cqk>AV1(rULYF07ZBJbGb9qbSoLAd;in9{)95YqX$J43-dY7YU*k~vrM25 zxh5_IqO0LYZW%oxQ5HOzmk4x{atE*vipUk}sh88$b2tn?!ujEHn`tQLe&vo}nMb&{ zio`xzZ&GG6&ZyN3jnaQy#iVqXE9VT(3tWY$n-)uWDQ|tc{`?fq2F`oQ{;d3aWPg4Hp-(iE{ry>MIPWL> iW8 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Base.lproj/Main.storyboard b/packages/local_auth/local_auth_ios/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/Info.plist b/packages/local_auth/local_auth_ios/example/ios/Runner/Info.plist new file mode 100644 index 000000000000..f8e0356d0a68 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner/Info.plist @@ -0,0 +1,51 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + local_auth_example + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + NSFaceIDUsageDescription + App needs to authenticate using faces. + + diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner/main.m b/packages/local_auth/local_auth_ios/example/ios/Runner/main.m new file mode 100644 index 000000000000..f143297b30d6 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/ios/Runner/main.m @@ -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 +#import +#import "AppDelegate.h" + +int main(int argc, char *argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/packages/local_auth/local_auth/example/ios/RunnerTests/FLTLocalAuthPluginTests.m b/packages/local_auth/local_auth_ios/example/ios/RunnerTests/FLTLocalAuthPluginTests.m similarity index 99% rename from packages/local_auth/local_auth/example/ios/RunnerTests/FLTLocalAuthPluginTests.m rename to packages/local_auth/local_auth_ios/example/ios/RunnerTests/FLTLocalAuthPluginTests.m index 3572524d8991..744d9124f7b1 100644 --- a/packages/local_auth/local_auth/example/ios/RunnerTests/FLTLocalAuthPluginTests.m +++ b/packages/local_auth/local_auth_ios/example/ios/RunnerTests/FLTLocalAuthPluginTests.m @@ -10,7 +10,7 @@ #if __has_include() #import #else -@import local_auth; +@import local_auth_ios; #endif // Private API needed for tests. diff --git a/packages/local_auth/local_auth/example/ios/RunnerTests/Info.plist b/packages/local_auth/local_auth_ios/example/ios/RunnerTests/Info.plist similarity index 100% rename from packages/local_auth/local_auth/example/ios/RunnerTests/Info.plist rename to packages/local_auth/local_auth_ios/example/ios/RunnerTests/Info.plist diff --git a/packages/local_auth/local_auth_ios/example/lib/main.dart b/packages/local_auth/local_auth_ios/example/lib/main.dart new file mode 100644 index 000000000000..9346be10fd78 --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/lib/main.dart @@ -0,0 +1,240 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:local_auth_ios/local_auth_ios.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; +import 'package:local_auth_platform_interface/types/biometric_type.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatefulWidget { + @override + _MyAppState createState() => _MyAppState(); +} + +class _MyAppState extends State { + _SupportState _supportState = _SupportState.unknown; + bool? _canCheckBiometrics; + List? _availableBiometrics; + String _authorized = 'Not Authorized'; + bool _isAuthenticating = false; + + @override + void initState() { + super.initState(); + LocalAuthPlatform.instance.isDeviceSupported().then( + (bool isSupported) => setState(() => _supportState = isSupported + ? _SupportState.supported + : _SupportState.unsupported), + ); + } + + Future _checkBiometrics() async { + late bool canCheckBiometrics; + try { + canCheckBiometrics = + (await LocalAuthPlatform.instance.getEnrolledBiometrics()).isNotEmpty; + } on PlatformException catch (e) { + canCheckBiometrics = false; + print(e); + } + if (!mounted) { + return; + } + + setState(() { + _canCheckBiometrics = canCheckBiometrics; + }); + } + + Future _getEnrolledBiometrics() async { + late List availableBiometrics; + try { + availableBiometrics = + await LocalAuthPlatform.instance.getEnrolledBiometrics(); + } on PlatformException catch (e) { + availableBiometrics = []; + print(e); + } + if (!mounted) { + return; + } + + setState(() { + _availableBiometrics = availableBiometrics; + }); + } + + Future _authenticate() async { + bool authenticated = false; + try { + setState(() { + _isAuthenticating = true; + _authorized = 'Authenticating'; + }); + authenticated = await LocalAuthPlatform.instance.authenticate( + localizedReason: 'Let OS determine authentication method', + authMessages: [const IOSAuthMessages()], + options: const AuthenticationOptions( + useErrorDialogs: true, + stickyAuth: true, + ), + ); + setState(() { + _isAuthenticating = false; + }); + } on PlatformException catch (e) { + print(e); + setState(() { + _isAuthenticating = false; + _authorized = 'Error - ${e.message}'; + }); + return; + } + if (!mounted) { + return; + } + + setState( + () => _authorized = authenticated ? 'Authorized' : 'Not Authorized'); + } + + Future _authenticateWithBiometrics() async { + bool authenticated = false; + try { + setState(() { + _isAuthenticating = true; + _authorized = 'Authenticating'; + }); + authenticated = await LocalAuthPlatform.instance.authenticate( + localizedReason: + 'Scan your fingerprint (or face or whatever) to authenticate', + authMessages: [const IOSAuthMessages()], + options: const AuthenticationOptions( + useErrorDialogs: true, + stickyAuth: true, + biometricOnly: true, + ), + ); + setState(() { + _isAuthenticating = false; + _authorized = 'Authenticating'; + }); + } on PlatformException catch (e) { + print(e); + setState(() { + _isAuthenticating = false; + _authorized = 'Error - ${e.message}'; + }); + return; + } + if (!mounted) { + return; + } + + final String message = authenticated ? 'Authorized' : 'Not Authorized'; + setState(() { + _authorized = message; + }); + } + + Future _cancelAuthentication() async { + await LocalAuthPlatform.instance.stopAuthentication(); + setState(() => _isAuthenticating = false); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: ListView( + padding: const EdgeInsets.only(top: 30), + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (_supportState == _SupportState.unknown) + const CircularProgressIndicator() + else if (_supportState == _SupportState.supported) + const Text('This device is supported') + else + const Text('This device is not supported'), + const Divider(height: 100), + Text('Can check biometrics: $_canCheckBiometrics\n'), + ElevatedButton( + child: const Text('Check biometrics'), + onPressed: _checkBiometrics, + ), + const Divider(height: 100), + Text('Available biometrics: $_availableBiometrics\n'), + ElevatedButton( + child: const Text('Get available biometrics'), + onPressed: _getEnrolledBiometrics, + ), + const Divider(height: 100), + Text('Current State: $_authorized\n'), + if (_isAuthenticating) + ElevatedButton( + onPressed: _cancelAuthentication, + child: Row( + mainAxisSize: MainAxisSize.min, + children: const [ + Text('Cancel Authentication'), + Icon(Icons.cancel), + ], + ), + ) + else + Column( + children: [ + ElevatedButton( + child: Row( + mainAxisSize: MainAxisSize.min, + children: const [ + Text('Authenticate'), + Icon(Icons.perm_device_information), + ], + ), + onPressed: _authenticate, + ), + ElevatedButton( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(_isAuthenticating + ? 'Cancel' + : 'Authenticate: biometrics only'), + const Icon(Icons.fingerprint), + ], + ), + onPressed: _authenticateWithBiometrics, + ), + ], + ), + ], + ), + ], + ), + ), + ); + } +} + +enum _SupportState { + unknown, + supported, + unsupported, +} diff --git a/packages/local_auth/local_auth_ios/example/pubspec.yaml b/packages/local_auth/local_auth_ios/example/pubspec.yaml new file mode 100644 index 000000000000..f83806b9d08e --- /dev/null +++ b/packages/local_auth/local_auth_ios/example/pubspec.yaml @@ -0,0 +1,28 @@ +name: local_auth_ios_example +description: Demonstrates how to use the local_auth_ios plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +dependencies: + flutter: + sdk: flutter + local_auth_ios: + # When depending on this package from a real application you should use: + # local_auth: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # 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: ../ + local_auth_platform_interface: ^1.0.0 + +dev_dependencies: + flutter_driver: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/local_auth/local_auth_ios/example/test_driver/integration_test.dart b/packages/local_auth/local_auth_ios/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/local_auth/local_auth_ios/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/local_auth/local_auth/ios/Assets/.gitkeep b/packages/local_auth/local_auth_ios/ios/Assets/.gitkeep similarity index 100% rename from packages/local_auth/local_auth/ios/Assets/.gitkeep rename to packages/local_auth/local_auth_ios/ios/Assets/.gitkeep diff --git a/packages/local_auth/local_auth/ios/Classes/FLTLocalAuthPlugin.h b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.h similarity index 100% rename from packages/local_auth/local_auth/ios/Classes/FLTLocalAuthPlugin.h rename to packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.h diff --git a/packages/local_auth/local_auth/ios/Classes/FLTLocalAuthPlugin.m b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m similarity index 99% rename from packages/local_auth/local_auth/ios/Classes/FLTLocalAuthPlugin.m rename to packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m index 70113efa00a0..d4d0e0d0f79f 100644 --- a/packages/local_auth/local_auth/ios/Classes/FLTLocalAuthPlugin.m +++ b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m @@ -20,7 +20,7 @@ @implementation FLTLocalAuthPlugin { + (void)registerWithRegistrar:(NSObject *)registrar { FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/local_auth" + [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/local_auth_ios" binaryMessenger:[registrar messenger]]; FLTLocalAuthPlugin *instance = [[FLTLocalAuthPlugin alloc] init]; [registrar addMethodCallDelegate:instance channel:channel]; @@ -180,7 +180,6 @@ - (void)handleAuthReplyWithSuccess:(BOOL)success case LAErrorPasscodeNotSet: case LAErrorTouchIDNotAvailable: case LAErrorTouchIDNotEnrolled: - case LAErrorUserFallback: case LAErrorTouchIDLockout: [self handleErrors:error flutterArguments:arguments withFlutterResult:result]; return; diff --git a/packages/local_auth/local_auth/ios/local_auth.podspec b/packages/local_auth/local_auth_ios/ios/local_auth_ios.podspec similarity index 89% rename from packages/local_auth/local_auth/ios/local_auth.podspec rename to packages/local_auth/local_auth_ios/ios/local_auth_ios.podspec index 4ab779ad50cd..0828c6085ea2 100644 --- a/packages/local_auth/local_auth/ios/local_auth.podspec +++ b/packages/local_auth/local_auth_ios/ios/local_auth_ios.podspec @@ -2,7 +2,7 @@ # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html # Pod::Spec.new do |s| - s.name = 'local_auth' + s.name = 'local_auth_ios' s.version = '0.0.1' s.summary = 'Flutter Local Auth' s.description = <<-DESC @@ -13,7 +13,7 @@ Downloaded by pub (not CocoaPods). s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/local_auth' } - s.documentation_url = 'https://pub.dev/packages/local_auth' + s.documentation_url = 'https://pub.dev/packages/local_auth_ios' s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' diff --git a/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart b/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart new file mode 100644 index 000000000000..7d085ccf14d9 --- /dev/null +++ b/packages/local_auth/local_auth_ios/lib/local_auth_ios.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/services.dart'; +import 'package:local_auth_ios/types/auth_messages_ios.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; +import 'package:local_auth_platform_interface/types/auth_options.dart'; +import 'package:local_auth_platform_interface/types/biometric_type.dart'; + +export 'package:local_auth_ios/types/auth_messages_ios.dart'; +export 'package:local_auth_platform_interface/types/auth_messages.dart'; +export 'package:local_auth_platform_interface/types/auth_options.dart'; +export 'package:local_auth_platform_interface/types/biometric_type.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.io/local_auth_ios'); + +/// The implementation of [LocalAuthPlatform] for iOS. +class LocalAuthIOS extends LocalAuthPlatform { + /// Registers this class as the default instance of [LocalAuthPlatform]. + static void registerWith() { + LocalAuthPlatform.instance = LocalAuthIOS(); + } + + @override + Future authenticate({ + required String localizedReason, + required Iterable authMessages, + AuthenticationOptions options = const AuthenticationOptions(), + }) async { + assert(localizedReason.isNotEmpty); + final Map args = { + 'localizedReason': localizedReason, + 'useErrorDialogs': options.useErrorDialogs, + 'stickyAuth': options.stickyAuth, + 'sensitiveTransaction': options.sensitiveTransaction, + 'biometricOnly': options.biometricOnly, + }; + args.addAll(const IOSAuthMessages().args); + for (final AuthMessages messages in authMessages) { + if (messages is IOSAuthMessages) { + args.addAll(messages.args); + } + } + return (await _channel.invokeMethod('authenticate', args)) ?? false; + } + + @override + Future deviceSupportsBiometrics() async { + return (await getEnrolledBiometrics()).isNotEmpty; + } + + @override + Future> getEnrolledBiometrics() async { + final List result = (await _channel.invokeListMethod( + 'getAvailableBiometrics', + )) ?? + []; + final List biometrics = []; + for (final String value in result) { + switch (value) { + case 'face': + biometrics.add(BiometricType.face); + break; + case 'fingerprint': + biometrics.add(BiometricType.fingerprint); + break; + case 'iris': + biometrics.add(BiometricType.iris); + break; + case 'undefined': + break; + } + } + return biometrics; + } + + @override + Future isDeviceSupported() async => + (await _channel.invokeMethod('isDeviceSupported')) ?? false; + + @override + Future stopAuthentication() async { + throw UnimplementedError('stopAuthentication() is not supported on iOS.'); + } +} diff --git a/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart b/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart new file mode 100644 index 000000000000..8a776243d242 --- /dev/null +++ b/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart @@ -0,0 +1,96 @@ +// 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:intl/intl.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; + +/// Class wrapping all authentication messages needed on iOS. +/// Provides default values for all messages. +@immutable +class IOSAuthMessages extends AuthMessages { + /// Constructs a new instance. + const IOSAuthMessages({ + this.lockOut, + this.goToSettingsButton, + this.goToSettingsDescription, + this.cancelButton, + }); + + /// Message advising the user to re-enable biometrics on their device. + final String? lockOut; + + /// Message shown on a button that the user can click to go to settings pages + /// from the current dialog. + /// Maximum 30 characters. + final String? goToSettingsButton; + + /// Message advising the user to go to the settings and configure Biometrics + /// for their device. + final String? goToSettingsDescription; + + /// Message shown on a button that the user can click to leave the current + /// dialog. + /// Maximum 30 characters. + final String? cancelButton; + + @override + Map get args { + return { + 'lockOut': lockOut ?? iOSLockOut, + 'goToSetting': goToSettingsButton ?? goToSettings, + 'goToSettingDescriptionIOS': + goToSettingsDescription ?? iOSGoToSettingsDescription, + 'okButton': cancelButton ?? iOSOkButton, + }; + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is IOSAuthMessages && + runtimeType == other.runtimeType && + lockOut == other.lockOut && + goToSettingsButton == other.goToSettingsButton && + goToSettingsDescription == other.goToSettingsDescription && + cancelButton == other.cancelButton; + + @override + int get hashCode => + lockOut.hashCode ^ + goToSettingsButton.hashCode ^ + goToSettingsDescription.hashCode ^ + cancelButton.hashCode; +} + +// Default Strings for IOSAuthMessages plugin. Currently supports English. +// Intl.message must be string literals. + +/// Message shown on a button that the user can click to go to settings pages +/// from the current dialog. +String get goToSettings => Intl.message('Go to settings', + desc: 'Message shown on a button that the user can click to go to ' + 'settings pages from the current dialog. Maximum 30 characters.'); + +/// Message advising the user to re-enable biometrics on their device. +/// It shows in a dialog on iOS. +String get iOSLockOut => Intl.message( + 'Biometric authentication is disabled. Please lock and unlock your screen to ' + 'enable it.', + desc: 'Message advising the user to re-enable biometrics on their device.'); + +/// Message advising the user to go to the settings and configure Biometrics +/// for their device. +String get iOSGoToSettingsDescription => Intl.message( + 'Biometric authentication is not set up on your device. Please either enable ' + 'Touch ID or Face ID on your phone.', + desc: + 'Message advising the user to go to the settings and configure Biometrics ' + 'for their device.'); + +/// Message shown on a button that the user can click to leave the current +/// dialog. +String get iOSOkButton => Intl.message('OK', + desc: 'Message showed on a button that the user can click to leave the ' + 'current dialog. Maximum 30 characters.'); diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml new file mode 100644 index 000000000000..a9126fdea676 --- /dev/null +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -0,0 +1,27 @@ +name: local_auth_ios +description: iOS implementation of the local_auth plugin. +repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_ios +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 +version: 1.0.0 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +flutter: + plugin: + implements: local_auth + platforms: + ios: + pluginClass: FLTLocalAuthPlugin + dartPluginClass: LocalAuthIOS + +dependencies: + flutter: + sdk: flutter + intl: ^0.17.0 + local_auth_platform_interface: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter \ No newline at end of file diff --git a/packages/local_auth/local_auth_ios/test/local_auth_test.dart b/packages/local_auth/local_auth_ios/test/local_auth_test.dart new file mode 100644 index 000000000000..b529764adbd6 --- /dev/null +++ b/packages/local_auth/local_auth_ios/test/local_auth_test.dart @@ -0,0 +1,163 @@ +// 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:async'; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:local_auth_ios/local_auth_ios.dart'; +import 'package:local_auth_platform_interface/types/auth_options.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('LocalAuth', () { + const MethodChannel channel = MethodChannel( + 'plugins.flutter.io/local_auth_ios', + ); + + final List log = []; + late LocalAuthIOS localAuthentication; + + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) { + log.add(methodCall); + switch (methodCall.method) { + case 'getAvailableBiometrics': + return Future>.value( + ['face', 'fingerprint', 'iris', 'undefined']); + default: + return Future.value(true); + } + }); + localAuthentication = LocalAuthIOS(); + log.clear(); + }); + + test('deviceSupportsBiometrics calls getEnrolledBiometrics', () async { + final bool result = await localAuthentication.deviceSupportsBiometrics(); + + expect( + log, + [ + isMethodCall('getAvailableBiometrics', arguments: null), + ], + ); + expect(result, true); + }); + + test('getEnrolledBiometrics calls platform', () async { + final List result = + await localAuthentication.getEnrolledBiometrics(); + + expect( + log, + [ + isMethodCall('getAvailableBiometrics', arguments: null), + ], + ); + expect(result, [ + BiometricType.face, + BiometricType.fingerprint, + BiometricType.iris + ]); + }); + + test('isDeviceSupported calls platform', () async { + await localAuthentication.isDeviceSupported(); + + expect( + log, + [ + isMethodCall('isDeviceSupported', arguments: null), + ], + ); + }); + + test('stopAuthentication throws UnimplementedError', () async { + expect(() async => await localAuthentication.stopAuthentication(), + throwsUnimplementedError); + }); + + group('With device auth fail over', () { + test('authenticate with no args.', () async { + await localAuthentication.authenticate( + authMessages: [const IOSAuthMessages()], + localizedReason: 'Needs secure', + options: const AuthenticationOptions(biometricOnly: true), + ); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'Needs secure', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': true, + }..addAll(const IOSAuthMessages().args)), + ], + ); + }); + + test('authenticate with no localizedReason.', () async { + await expectLater( + localAuthentication.authenticate( + authMessages: [const IOSAuthMessages()], + localizedReason: '', + options: const AuthenticationOptions(biometricOnly: true), + ), + throwsAssertionError, + ); + }); + }); + + group('With biometrics only', () { + test('authenticate with no args.', () async { + await localAuthentication.authenticate( + authMessages: [const IOSAuthMessages()], + localizedReason: 'Needs secure', + ); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'Needs secure', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': false, + }..addAll(const IOSAuthMessages().args)), + ], + ); + }); + + test('authenticate with no sensitive transaction.', () async { + await localAuthentication.authenticate( + authMessages: [const IOSAuthMessages()], + localizedReason: 'Insecure', + options: const AuthenticationOptions( + sensitiveTransaction: false, + useErrorDialogs: false, + ), + ); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'Insecure', + 'useErrorDialogs': false, + 'stickyAuth': false, + 'sensitiveTransaction': false, + 'biometricOnly': false, + }..addAll(const IOSAuthMessages().args)), + ], + ); + }); + }); + }); +} From d05ec56c6ddaa6870632e9a7b73071df1ea6495b Mon Sep 17 00:00:00 2001 From: BeMacized Date: Tue, 12 Apr 2022 01:39:10 +0200 Subject: [PATCH 121/844] [local_auth] Add native Android implementation of platform interface (#4700) --- packages/local_auth/local_auth/pubspec.yaml | 7 +- .../local_auth/local_auth_android/AUTHORS | 67 +++++ .../local_auth_android/CHANGELOG.md | 3 + .../local_auth/local_auth_android/LICENSE | 25 ++ .../local_auth/local_auth_android/README.md | 11 + .../android/build.gradle | 0 .../android/lint-baseline.xml | 0 .../android/settings.gradle | 0 .../android/src/main/AndroidManifest.xml | 0 .../localauth/AuthenticationHelper.java | 0 .../plugins/localauth/LocalAuthPlugin.java | 2 +- .../res/drawable/fingerprint_initial_icon.xml | 0 .../res/drawable/fingerprint_success_icon.xml | 0 .../res/drawable/fingerprint_warning_icon.xml | 0 .../main/res/drawable/ic_done_white_24dp.xml | 0 .../drawable/ic_fingerprint_white_24dp.xml | 0 .../drawable/ic_priority_high_white_24dp.xml | 0 .../src/main/res/layout/go_to_setting.xml | 0 .../android/src/main/res/layout/scan_fp.xml | 0 .../android/src/main/res/values/colors.xml | 0 .../android/src/main/res/values/dimens.xml | 0 .../android/src/main/res/values/styles.xml | 0 .../plugins/localauth/LocalAuthTest.java | 0 .../local_auth_android/example/README.md | 8 + .../example/android/app/build.gradle | 58 +++++ .../gradle/wrapper/gradle-wrapper.properties | 5 + .../flutter/plugins/DartIntegrationTest.java | 14 ++ .../FlutterFragmentActivityTest.java | 20 ++ .../android/app/src/main/AndroidManifest.xml | 21 ++ .../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 .../example/android/build.gradle | 29 +++ .../example/android/gradle.properties | 4 + .../gradle/wrapper/gradle-wrapper.properties | 6 + .../example/android/settings.gradle | 15 ++ .../example/android/settings_aar.gradle | 1 + .../integration_test/local_auth_test.dart | 19 ++ .../local_auth_android/example/lib/main.dart | 238 ++++++++++++++++++ .../local_auth_android/example/pubspec.yaml | 28 +++ .../example/test_driver/integration_test.dart | 7 + .../lib/local_auth_android.dart | 87 +++++++ .../lib/types/auth_messages_android.dart | 191 ++++++++++++++ .../local_auth_android/pubspec.yaml | 29 +++ .../test/local_auth_test.dart | 181 +++++++++++++ 47 files changed, 1072 insertions(+), 4 deletions(-) create mode 100644 packages/local_auth/local_auth_android/AUTHORS create mode 100644 packages/local_auth/local_auth_android/CHANGELOG.md create mode 100644 packages/local_auth/local_auth_android/LICENSE create mode 100644 packages/local_auth/local_auth_android/README.md rename packages/local_auth/{local_auth => local_auth_android}/android/build.gradle (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/lint-baseline.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/settings.gradle (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/AndroidManifest.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java (99%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/drawable/fingerprint_initial_icon.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/drawable/fingerprint_success_icon.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/drawable/fingerprint_warning_icon.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/drawable/ic_done_white_24dp.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/drawable/ic_priority_high_white_24dp.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/layout/go_to_setting.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/layout/scan_fp.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/values/colors.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/values/dimens.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/main/res/values/styles.xml (100%) rename packages/local_auth/{local_auth => local_auth_android}/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java (100%) create mode 100644 packages/local_auth/local_auth_android/example/README.md create mode 100644 packages/local_auth/local_auth_android/example/android/app/build.gradle create mode 100644 packages/local_auth/local_auth_android/example/android/app/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java create mode 100644 packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/localauth/FlutterFragmentActivityTest.java create mode 100644 packages/local_auth/local_auth_android/example/android/app/src/main/AndroidManifest.xml create mode 100644 packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 packages/local_auth/local_auth_android/example/android/build.gradle create mode 100644 packages/local_auth/local_auth_android/example/android/gradle.properties create mode 100644 packages/local_auth/local_auth_android/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/local_auth/local_auth_android/example/android/settings.gradle create mode 100644 packages/local_auth/local_auth_android/example/android/settings_aar.gradle create mode 100644 packages/local_auth/local_auth_android/example/integration_test/local_auth_test.dart create mode 100644 packages/local_auth/local_auth_android/example/lib/main.dart create mode 100644 packages/local_auth/local_auth_android/example/pubspec.yaml create mode 100644 packages/local_auth/local_auth_android/example/test_driver/integration_test.dart create mode 100644 packages/local_auth/local_auth_android/lib/local_auth_android.dart create mode 100644 packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart create mode 100644 packages/local_auth/local_auth_android/pubspec.yaml create mode 100644 packages/local_auth/local_auth_android/test/local_auth_test.dart diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index dbffb4cda879..baf5d46b7b00 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -17,8 +17,7 @@ flutter: plugin: platforms: android: - package: io.flutter.plugins.localauth - pluginClass: LocalAuthPlugin + default_package: local_auth_android ios: default_package: local_auth_ios @@ -27,7 +26,9 @@ dependencies: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 intl: ^0.17.0 - # Temporary path dependencies to allow moving Android and iOS implementations. +# Temporary path dependencies to allow moving Android and iOS implementations. + local_auth_android: + path: ../local_auth_android local_auth_ios: path: ../local_auth_ios local_auth_platform_interface: ^1.0.1 diff --git a/packages/local_auth/local_auth_android/AUTHORS b/packages/local_auth/local_auth_android/AUTHORS new file mode 100644 index 000000000000..d5694690c247 --- /dev/null +++ b/packages/local_auth/local_auth_android/AUTHORS @@ -0,0 +1,67 @@ +# 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. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> +Bodhi Mulders diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md new file mode 100644 index 000000000000..7f198f2d66c0 --- /dev/null +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +* Initial release from migration to federated architecture. diff --git a/packages/local_auth/local_auth_android/LICENSE b/packages/local_auth/local_auth_android/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/local_auth/local_auth_android/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/local_auth/local_auth_android/README.md b/packages/local_auth/local_auth_android/README.md new file mode 100644 index 000000000000..07244912f231 --- /dev/null +++ b/packages/local_auth/local_auth_android/README.md @@ -0,0 +1,11 @@ +# local\_auth\_android + +The Android implementation of [`local_auth`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `local_auth` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/local_auth +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin \ No newline at end of file diff --git a/packages/local_auth/local_auth/android/build.gradle b/packages/local_auth/local_auth_android/android/build.gradle similarity index 100% rename from packages/local_auth/local_auth/android/build.gradle rename to packages/local_auth/local_auth_android/android/build.gradle diff --git a/packages/local_auth/local_auth/android/lint-baseline.xml b/packages/local_auth/local_auth_android/android/lint-baseline.xml similarity index 100% rename from packages/local_auth/local_auth/android/lint-baseline.xml rename to packages/local_auth/local_auth_android/android/lint-baseline.xml diff --git a/packages/local_auth/local_auth/android/settings.gradle b/packages/local_auth/local_auth_android/android/settings.gradle similarity index 100% rename from packages/local_auth/local_auth/android/settings.gradle rename to packages/local_auth/local_auth_android/android/settings.gradle diff --git a/packages/local_auth/local_auth/android/src/main/AndroidManifest.xml b/packages/local_auth/local_auth_android/android/src/main/AndroidManifest.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/AndroidManifest.xml rename to packages/local_auth/local_auth_android/android/src/main/AndroidManifest.xml diff --git a/packages/local_auth/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java similarity index 100% rename from packages/local_auth/local_auth/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java rename to packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java diff --git a/packages/local_auth/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java similarity index 99% rename from packages/local_auth/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java rename to packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java index a63e22a512d0..49a6b788fe46 100644 --- a/packages/local_auth/local_auth/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java +++ b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java @@ -40,7 +40,7 @@ */ @SuppressWarnings("deprecation") public class LocalAuthPlugin implements MethodCallHandler, FlutterPlugin, ActivityAware { - private static final String CHANNEL_NAME = "plugins.flutter.io/local_auth"; + private static final String CHANNEL_NAME = "plugins.flutter.io/local_auth_android"; private static final int LOCK_REQUEST_CODE = 221; private Activity activity; private final AtomicBoolean authInProgress = new AtomicBoolean(false); diff --git a/packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_initial_icon.xml b/packages/local_auth/local_auth_android/android/src/main/res/drawable/fingerprint_initial_icon.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_initial_icon.xml rename to packages/local_auth/local_auth_android/android/src/main/res/drawable/fingerprint_initial_icon.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_success_icon.xml b/packages/local_auth/local_auth_android/android/src/main/res/drawable/fingerprint_success_icon.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_success_icon.xml rename to packages/local_auth/local_auth_android/android/src/main/res/drawable/fingerprint_success_icon.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_warning_icon.xml b/packages/local_auth/local_auth_android/android/src/main/res/drawable/fingerprint_warning_icon.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/drawable/fingerprint_warning_icon.xml rename to packages/local_auth/local_auth_android/android/src/main/res/drawable/fingerprint_warning_icon.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/drawable/ic_done_white_24dp.xml b/packages/local_auth/local_auth_android/android/src/main/res/drawable/ic_done_white_24dp.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/drawable/ic_done_white_24dp.xml rename to packages/local_auth/local_auth_android/android/src/main/res/drawable/ic_done_white_24dp.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml b/packages/local_auth/local_auth_android/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml rename to packages/local_auth/local_auth_android/android/src/main/res/drawable/ic_fingerprint_white_24dp.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/drawable/ic_priority_high_white_24dp.xml b/packages/local_auth/local_auth_android/android/src/main/res/drawable/ic_priority_high_white_24dp.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/drawable/ic_priority_high_white_24dp.xml rename to packages/local_auth/local_auth_android/android/src/main/res/drawable/ic_priority_high_white_24dp.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/layout/go_to_setting.xml b/packages/local_auth/local_auth_android/android/src/main/res/layout/go_to_setting.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/layout/go_to_setting.xml rename to packages/local_auth/local_auth_android/android/src/main/res/layout/go_to_setting.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/layout/scan_fp.xml b/packages/local_auth/local_auth_android/android/src/main/res/layout/scan_fp.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/layout/scan_fp.xml rename to packages/local_auth/local_auth_android/android/src/main/res/layout/scan_fp.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/values/colors.xml b/packages/local_auth/local_auth_android/android/src/main/res/values/colors.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/values/colors.xml rename to packages/local_auth/local_auth_android/android/src/main/res/values/colors.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/values/dimens.xml b/packages/local_auth/local_auth_android/android/src/main/res/values/dimens.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/values/dimens.xml rename to packages/local_auth/local_auth_android/android/src/main/res/values/dimens.xml diff --git a/packages/local_auth/local_auth/android/src/main/res/values/styles.xml b/packages/local_auth/local_auth_android/android/src/main/res/values/styles.xml similarity index 100% rename from packages/local_auth/local_auth/android/src/main/res/values/styles.xml rename to packages/local_auth/local_auth_android/android/src/main/res/values/styles.xml diff --git a/packages/local_auth/local_auth/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java b/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java similarity index 100% rename from packages/local_auth/local_auth/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java rename to packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java diff --git a/packages/local_auth/local_auth_android/example/README.md b/packages/local_auth/local_auth_android/example/README.md new file mode 100644 index 000000000000..a4a6091c9ba6 --- /dev/null +++ b/packages/local_auth/local_auth_android/example/README.md @@ -0,0 +1,8 @@ +# local_auth_example + +Demonstrates how to use the local_auth plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/local_auth/local_auth_android/example/android/app/build.gradle b/packages/local_auth/local_auth_android/example/android/app/build.gradle new file mode 100644 index 000000000000..d1cef4bf53a9 --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/app/build.gradle @@ -0,0 +1,58 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 31 + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + applicationId "io.flutter.plugins.localauthexample" + minSdkVersion 16 + targetSdkVersion 28 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' +} diff --git a/packages/local_auth/local_auth_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/local_auth/local_auth_android/example/android/app/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..186b71557c50 --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java new file mode 100644 index 000000000000..0f4298dca155 --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java @@ -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. + +package io.flutter.plugins; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DartIntegrationTest {} diff --git a/packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/localauth/FlutterFragmentActivityTest.java b/packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/localauth/FlutterFragmentActivityTest.java new file mode 100644 index 000000000000..68c22371d7dd --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/app/src/androidTest/java/io/flutter/plugins/localauth/FlutterFragmentActivityTest.java @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.localauth; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.integration_test.FlutterTestRunner; +import io.flutter.embedding.android.FlutterFragmentActivity; +import io.flutter.plugins.DartIntegrationTest; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@DartIntegrationTest +@RunWith(FlutterTestRunner.class) +public class FlutterFragmentActivityTest { + @Rule + public ActivityTestRule rule = + new ActivityTestRule<>(FlutterFragmentActivity.class); +} diff --git a/packages/local_auth/local_auth_android/example/android/app/src/main/AndroidManifest.xml b/packages/local_auth/local_auth_android/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..8c091772107a --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + diff --git a/packages/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/local_auth/local_auth_android/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/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/local_auth/local_auth_android/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/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/local_auth/local_auth_android/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/local_auth/local_auth_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/local_auth/local_auth_android/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/local_auth/local_auth_android/example/android/build.gradle b/packages/local_auth/local_auth_android/example/android/build.gradle new file mode 100644 index 000000000000..54c943621de5 --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.1.1' + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/packages/local_auth/local_auth_android/example/android/gradle.properties b/packages/local_auth/local_auth_android/example/android/gradle.properties new file mode 100644 index 000000000000..7fe61a74cee0 --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx1024m +android.useAndroidX=true +android.enableJetifier=true +android.enableR8=true diff --git a/packages/local_auth/local_auth_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/local_auth/local_auth_android/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..cd9fe1c68282 --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sun Jan 03 14:07:08 CST 2021 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip diff --git a/packages/local_auth/local_auth_android/example/android/settings.gradle b/packages/local_auth/local_auth_android/example/android/settings.gradle new file mode 100644 index 000000000000..115da6cb4f4d --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withInputStream { stream -> plugins.load(stream) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/packages/local_auth/local_auth_android/example/android/settings_aar.gradle b/packages/local_auth/local_auth_android/example/android/settings_aar.gradle new file mode 100644 index 000000000000..e7b4def49cb5 --- /dev/null +++ b/packages/local_auth/local_auth_android/example/android/settings_aar.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/packages/local_auth/local_auth_android/example/integration_test/local_auth_test.dart b/packages/local_auth/local_auth_android/example/integration_test/local_auth_test.dart new file mode 100644 index 000000000000..1dfc0ae7a6d6 --- /dev/null +++ b/packages/local_auth/local_auth_android/example/integration_test/local_auth_test.dart @@ -0,0 +1,19 @@ +// 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:integration_test/integration_test.dart'; + +import 'package:local_auth_android/local_auth_android.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('canCheckBiometrics', (WidgetTester tester) async { + expect( + LocalAuthAndroid().getEnrolledBiometrics(), + completion(isList), + ); + }); +} diff --git a/packages/local_auth/local_auth_android/example/lib/main.dart b/packages/local_auth/local_auth_android/example/lib/main.dart new file mode 100644 index 000000000000..4c045214734d --- /dev/null +++ b/packages/local_auth/local_auth_android/example/lib/main.dart @@ -0,0 +1,238 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:local_auth_android/local_auth_android.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatefulWidget { + @override + _MyAppState createState() => _MyAppState(); +} + +class _MyAppState extends State { + _SupportState _supportState = _SupportState.unknown; + bool? _canCheckBiometrics; + List? _availableBiometrics; + String _authorized = 'Not Authorized'; + bool _isAuthenticating = false; + + @override + void initState() { + super.initState(); + LocalAuthPlatform.instance.isDeviceSupported().then( + (bool isSupported) => setState(() => _supportState = isSupported + ? _SupportState.supported + : _SupportState.unsupported), + ); + } + + Future _checkBiometrics() async { + late bool canCheckBiometrics; + try { + canCheckBiometrics = + (await LocalAuthPlatform.instance.getEnrolledBiometrics()).isNotEmpty; + } on PlatformException catch (e) { + canCheckBiometrics = false; + print(e); + } + if (!mounted) { + return; + } + + setState(() { + _canCheckBiometrics = canCheckBiometrics; + }); + } + + Future _getEnrolledBiometrics() async { + late List availableBiometrics; + try { + availableBiometrics = + await LocalAuthPlatform.instance.getEnrolledBiometrics(); + } on PlatformException catch (e) { + availableBiometrics = []; + print(e); + } + if (!mounted) { + return; + } + + setState(() { + _availableBiometrics = availableBiometrics; + }); + } + + Future _authenticate() async { + bool authenticated = false; + try { + setState(() { + _isAuthenticating = true; + _authorized = 'Authenticating'; + }); + authenticated = await LocalAuthPlatform.instance.authenticate( + localizedReason: 'Let OS determine authentication method', + authMessages: [const AndroidAuthMessages()], + options: const AuthenticationOptions( + useErrorDialogs: true, + stickyAuth: true, + ), + ); + setState(() { + _isAuthenticating = false; + }); + } on PlatformException catch (e) { + print(e); + setState(() { + _isAuthenticating = false; + _authorized = 'Error - ${e.message}'; + }); + return; + } + if (!mounted) { + return; + } + + setState( + () => _authorized = authenticated ? 'Authorized' : 'Not Authorized'); + } + + Future _authenticateWithBiometrics() async { + bool authenticated = false; + try { + setState(() { + _isAuthenticating = true; + _authorized = 'Authenticating'; + }); + authenticated = await LocalAuthPlatform.instance.authenticate( + localizedReason: + 'Scan your fingerprint (or face or whatever) to authenticate', + authMessages: [const AndroidAuthMessages()], + options: const AuthenticationOptions( + useErrorDialogs: true, + stickyAuth: true, + biometricOnly: true, + ), + ); + setState(() { + _isAuthenticating = false; + _authorized = 'Authenticating'; + }); + } on PlatformException catch (e) { + print(e); + setState(() { + _isAuthenticating = false; + _authorized = 'Error - ${e.message}'; + }); + return; + } + if (!mounted) { + return; + } + + final String message = authenticated ? 'Authorized' : 'Not Authorized'; + setState(() { + _authorized = message; + }); + } + + Future _cancelAuthentication() async { + await LocalAuthPlatform.instance.stopAuthentication(); + setState(() => _isAuthenticating = false); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: ListView( + padding: const EdgeInsets.only(top: 30), + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (_supportState == _SupportState.unknown) + const CircularProgressIndicator() + else if (_supportState == _SupportState.supported) + const Text('This device is supported') + else + const Text('This device is not supported'), + const Divider(height: 100), + Text('Can check biometrics: $_canCheckBiometrics\n'), + ElevatedButton( + child: const Text('Check biometrics'), + onPressed: _checkBiometrics, + ), + const Divider(height: 100), + Text('Available biometrics: $_availableBiometrics\n'), + ElevatedButton( + child: const Text('Get available biometrics'), + onPressed: _getEnrolledBiometrics, + ), + const Divider(height: 100), + Text('Current State: $_authorized\n'), + if (_isAuthenticating) + ElevatedButton( + onPressed: _cancelAuthentication, + child: Row( + mainAxisSize: MainAxisSize.min, + children: const [ + Text('Cancel Authentication'), + Icon(Icons.cancel), + ], + ), + ) + else + Column( + children: [ + ElevatedButton( + child: Row( + mainAxisSize: MainAxisSize.min, + children: const [ + Text('Authenticate'), + Icon(Icons.perm_device_information), + ], + ), + onPressed: _authenticate, + ), + ElevatedButton( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(_isAuthenticating + ? 'Cancel' + : 'Authenticate: biometrics only'), + const Icon(Icons.fingerprint), + ], + ), + onPressed: _authenticateWithBiometrics, + ), + ], + ), + ], + ), + ], + ), + ), + ); + } +} + +enum _SupportState { + unknown, + supported, + unsupported, +} diff --git a/packages/local_auth/local_auth_android/example/pubspec.yaml b/packages/local_auth/local_auth_android/example/pubspec.yaml new file mode 100644 index 000000000000..c07a81d2be3b --- /dev/null +++ b/packages/local_auth/local_auth_android/example/pubspec.yaml @@ -0,0 +1,28 @@ +name: local_auth_android_example +description: Demonstrates how to use the local_auth_android plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +dependencies: + flutter: + sdk: flutter + local_auth_android: + # When depending on this package from a real application you should use: + # local_auth_android: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # 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: ../ + local_auth_platform_interface: ^1.0.0 + +dev_dependencies: + flutter_driver: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/local_auth/local_auth_android/example/test_driver/integration_test.dart b/packages/local_auth/local_auth_android/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/local_auth/local_auth_android/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/local_auth/local_auth_android/lib/local_auth_android.dart b/packages/local_auth/local_auth_android/lib/local_auth_android.dart new file mode 100644 index 000000000000..a3f314e3347b --- /dev/null +++ b/packages/local_auth/local_auth_android/lib/local_auth_android.dart @@ -0,0 +1,87 @@ +// 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:local_auth_android/types/auth_messages_android.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; +import 'package:local_auth_platform_interface/types/auth_options.dart'; +import 'package:local_auth_platform_interface/types/biometric_type.dart'; + +export 'package:local_auth_android/types/auth_messages_android.dart'; +export 'package:local_auth_platform_interface/types/auth_messages.dart'; +export 'package:local_auth_platform_interface/types/auth_options.dart'; +export 'package:local_auth_platform_interface/types/biometric_type.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.io/local_auth_android'); + +/// The implementation of [LocalAuthPlatform] for Android. +class LocalAuthAndroid extends LocalAuthPlatform { + /// Registers this class as the default instance of [LocalAuthPlatform]. + static void registerWith() { + LocalAuthPlatform.instance = LocalAuthAndroid(); + } + + @override + Future authenticate({ + required String localizedReason, + required Iterable authMessages, + AuthenticationOptions options = const AuthenticationOptions(), + }) async { + assert(localizedReason.isNotEmpty); + final Map args = { + 'localizedReason': localizedReason, + 'useErrorDialogs': options.useErrorDialogs, + 'stickyAuth': options.stickyAuth, + 'sensitiveTransaction': options.sensitiveTransaction, + 'biometricOnly': options.biometricOnly, + }; + args.addAll(const AndroidAuthMessages().args); + for (final AuthMessages messages in authMessages) { + if (messages is AndroidAuthMessages) { + args.addAll(messages.args); + } + } + return (await _channel.invokeMethod('authenticate', args)) ?? false; + } + + @override + Future deviceSupportsBiometrics() async { + return (await getEnrolledBiometrics()).isNotEmpty; + } + + @override + Future> getEnrolledBiometrics() async { + final List result = (await _channel.invokeListMethod( + 'getAvailableBiometrics', + )) ?? + []; + final List biometrics = []; + for (final String value in result) { + switch (value) { + case 'face': + biometrics.add(BiometricType.face); + break; + case 'fingerprint': + biometrics.add(BiometricType.fingerprint); + break; + case 'iris': + biometrics.add(BiometricType.iris); + break; + case 'undefined': + break; + } + } + return biometrics; + } + + @override + Future isDeviceSupported() async => + (await _channel.invokeMethod('isDeviceSupported')) ?? false; + + @override + Future stopAuthentication() async => + await _channel.invokeMethod('stopAuthentication') ?? false; +} diff --git a/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart b/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart new file mode 100644 index 000000000000..ea61a4b06d4e --- /dev/null +++ b/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart @@ -0,0 +1,191 @@ +// 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:intl/intl.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; + +/// Android side authentication messages. +/// +/// Provides default values for all messages. +@immutable +class AndroidAuthMessages extends AuthMessages { + /// Constructs a new instance. + const AndroidAuthMessages({ + this.biometricHint, + this.biometricNotRecognized, + this.biometricRequiredTitle, + this.biometricSuccess, + this.cancelButton, + this.deviceCredentialsRequiredTitle, + this.deviceCredentialsSetupDescription, + this.goToSettingsButton, + this.goToSettingsDescription, + this.signInTitle, + }); + + /// Hint message advising the user how to authenticate with biometrics. + /// Maximum 60 characters. + final String? biometricHint; + + /// Message to let the user know that authentication was failed. + /// Maximum 60 characters. + final String? biometricNotRecognized; + + /// Message shown as a title in a dialog which indicates the user + /// has not set up biometric authentication on their device. + /// Maximum 60 characters. + final String? biometricRequiredTitle; + + /// Message to let the user know that authentication was successful. + /// Maximum 60 characters + final String? biometricSuccess; + + /// Message shown on a button that the user can click to leave the + /// current dialog. + /// Maximum 30 characters. + final String? cancelButton; + + /// Message shown as a title in a dialog which indicates the user + /// has not set up credentials authentication on their device. + /// Maximum 60 characters. + final String? deviceCredentialsRequiredTitle; + + /// Message advising the user to go to the settings and configure + /// device credentials on their device. + final String? deviceCredentialsSetupDescription; + + /// Message shown on a button that the user can click to go to settings pages + /// from the current dialog. + /// Maximum 30 characters. + final String? goToSettingsButton; + + /// Message advising the user to go to the settings and configure + /// biometric on their device. + final String? goToSettingsDescription; + + /// Message shown as a title in a dialog which indicates the user + /// that they need to scan biometric to continue. + /// Maximum 60 characters. + final String? signInTitle; + + @override + Map get args { + return { + 'biometricHint': biometricHint ?? androidBiometricHint, + 'biometricNotRecognized': + biometricNotRecognized ?? androidBiometricNotRecognized, + 'biometricSuccess': biometricSuccess ?? androidBiometricSuccess, + 'biometricRequired': + biometricRequiredTitle ?? androidBiometricRequiredTitle, + 'cancelButton': cancelButton ?? androidCancelButton, + 'deviceCredentialsRequired': deviceCredentialsRequiredTitle ?? + androidDeviceCredentialsRequiredTitle, + 'deviceCredentialsSetupDescription': deviceCredentialsSetupDescription ?? + androidDeviceCredentialsSetupDescription, + 'goToSetting': goToSettingsButton ?? goToSettings, + 'goToSettingDescription': + goToSettingsDescription ?? androidGoToSettingsDescription, + 'signInTitle': signInTitle ?? androidSignInTitle, + }; + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is AndroidAuthMessages && + runtimeType == other.runtimeType && + biometricHint == other.biometricHint && + biometricNotRecognized == other.biometricNotRecognized && + biometricRequiredTitle == other.biometricRequiredTitle && + biometricSuccess == other.biometricSuccess && + cancelButton == other.cancelButton && + deviceCredentialsRequiredTitle == + other.deviceCredentialsRequiredTitle && + deviceCredentialsSetupDescription == + other.deviceCredentialsSetupDescription && + goToSettingsButton == other.goToSettingsButton && + goToSettingsDescription == other.goToSettingsDescription && + signInTitle == other.signInTitle; + + @override + int get hashCode => + biometricHint.hashCode ^ + biometricNotRecognized.hashCode ^ + biometricRequiredTitle.hashCode ^ + biometricSuccess.hashCode ^ + cancelButton.hashCode ^ + deviceCredentialsRequiredTitle.hashCode ^ + deviceCredentialsSetupDescription.hashCode ^ + goToSettingsButton.hashCode ^ + goToSettingsDescription.hashCode ^ + signInTitle.hashCode; +} + +// Default strings for AndroidAuthMessages. Currently supports English. +// Intl.message must be string literals. + +/// Message shown on a button that the user can click to go to settings pages +/// from the current dialog. +String get goToSettings => Intl.message('Go to settings', + desc: 'Message shown on a button that the user can click to go to ' + 'settings pages from the current dialog. Maximum 30 characters.'); + +/// Hint message advising the user how to authenticate with biometrics. +String get androidBiometricHint => Intl.message('Verify identity', + desc: 'Hint message advising the user how to authenticate with biometrics. ' + 'Maximum 60 characters.'); + +/// Message to let the user know that authentication was failed. +String get androidBiometricNotRecognized => + Intl.message('Not recognized. Try again.', + desc: 'Message to let the user know that authentication was failed. ' + 'Maximum 60 characters.'); + +/// Message to let the user know that authentication was successful. It +String get androidBiometricSuccess => Intl.message('Success', + desc: 'Message to let the user know that authentication was successful. ' + 'Maximum 60 characters.'); + +/// Message shown on a button that the user can click to leave the +/// current dialog. +String get androidCancelButton => Intl.message('Cancel', + desc: 'Message shown on a button that the user can click to leave the ' + 'current dialog. Maximum 30 characters.'); + +/// Message shown as a title in a dialog which indicates the user +/// that they need to scan biometric to continue. +String get androidSignInTitle => Intl.message('Authentication required', + desc: 'Message shown as a title in a dialog which indicates the user ' + 'that they need to scan biometric to continue. Maximum 60 characters.'); + +/// Message shown as a title in a dialog which indicates the user +/// has not set up biometric authentication on their device. +String get androidBiometricRequiredTitle => Intl.message('Biometric required', + desc: 'Message shown as a title in a dialog which indicates the user ' + 'has not set up biometric authentication on their device. ' + 'Maximum 60 characters.'); + +/// Message shown as a title in a dialog which indicates the user +/// has not set up credentials authentication on their device. +String get androidDeviceCredentialsRequiredTitle => + Intl.message('Device credentials required', + desc: 'Message shown as a title in a dialog which indicates the user ' + 'has not set up credentials authentication on their device. ' + 'Maximum 60 characters.'); + +/// Message advising the user to go to the settings and configure +/// device credentials on their device. +String get androidDeviceCredentialsSetupDescription => + Intl.message('Device credentials required', + desc: 'Message advising the user to go to the settings and configure ' + 'device credentials on their device.'); + +/// Message advising the user to go to the settings and configure +/// biometric on their device. +String get androidGoToSettingsDescription => Intl.message( + 'Biometric authentication is not set up on your device. Go to ' + '\'Settings > Security\' to add biometric authentication.', + desc: 'Message advising the user to go to the settings and configure ' + 'biometric on their device.'); diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml new file mode 100644 index 000000000000..ec2991db6a90 --- /dev/null +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -0,0 +1,29 @@ +name: local_auth_android +description: Android implementation of the local_auth plugin. +repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_android +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 +version: 1.0.0 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +flutter: + plugin: + implements: local_auth + platforms: + android: + package: io.flutter.plugins.localauth + pluginClass: LocalAuthPlugin + dartPluginClass: LocalAuthAndroid + +dependencies: + flutter: + sdk: flutter + flutter_plugin_android_lifecycle: ^2.0.1 + intl: ^0.17.0 + local_auth_platform_interface: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter \ No newline at end of file diff --git a/packages/local_auth/local_auth_android/test/local_auth_test.dart b/packages/local_auth/local_auth_android/test/local_auth_test.dart new file mode 100644 index 000000000000..31f5e5796445 --- /dev/null +++ b/packages/local_auth/local_auth_android/test/local_auth_test.dart @@ -0,0 +1,181 @@ +// 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:async'; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:local_auth_android/local_auth_android.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('LocalAuth', () { + const MethodChannel channel = MethodChannel( + 'plugins.flutter.io/local_auth_android', + ); + + final List log = []; + late LocalAuthAndroid localAuthentication; + + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) { + log.add(methodCall); + switch (methodCall.method) { + case 'getAvailableBiometrics': + return Future>.value( + ['face', 'fingerprint', 'iris', 'undefined']); + default: + return Future.value(true); + } + }); + localAuthentication = LocalAuthAndroid(); + log.clear(); + }); + + test('deviceSupportsBiometrics calls getEnrolledBiometrics', () async { + final bool result = await localAuthentication.deviceSupportsBiometrics(); + + expect( + log, + [ + isMethodCall('getAvailableBiometrics', arguments: null), + ], + ); + expect(result, true); + }); + + test('getEnrolledBiometrics calls platform', () async { + final List result = + await localAuthentication.getEnrolledBiometrics(); + + expect( + log, + [ + isMethodCall('getAvailableBiometrics', arguments: null), + ], + ); + expect(result, [ + BiometricType.face, + BiometricType.fingerprint, + BiometricType.iris + ]); + }); + + test('isDeviceSupported calls platform', () async { + await localAuthentication.isDeviceSupported(); + expect( + log, + [ + isMethodCall('isDeviceSupported', arguments: null), + ], + ); + }); + + test('stopAuthentication calls platform', () async { + await localAuthentication.stopAuthentication(); + expect( + log, + [ + isMethodCall('stopAuthentication', arguments: null), + ], + ); + }); + + group('With device auth fail over', () { + test('authenticate with no args.', () async { + await localAuthentication.authenticate( + authMessages: [const AndroidAuthMessages()], + localizedReason: 'Needs secure', + options: const AuthenticationOptions(biometricOnly: true), + ); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'Needs secure', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': true, + }..addAll(const AndroidAuthMessages().args)), + ], + ); + }); + + test('authenticate with no sensitive transaction.', () async { + await localAuthentication.authenticate( + authMessages: [const AndroidAuthMessages()], + localizedReason: 'Insecure', + options: const AuthenticationOptions( + sensitiveTransaction: false, + useErrorDialogs: false, + biometricOnly: true, + ), + ); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'Insecure', + 'useErrorDialogs': false, + 'stickyAuth': false, + 'sensitiveTransaction': false, + 'biometricOnly': true, + }..addAll(const AndroidAuthMessages().args)), + ], + ); + }); + }); + + group('With biometrics only', () { + test('authenticate with no args.', () async { + await localAuthentication.authenticate( + authMessages: [const AndroidAuthMessages()], + localizedReason: 'Needs secure', + ); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'Needs secure', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': false, + }..addAll(const AndroidAuthMessages().args)), + ], + ); + }); + + test('authenticate with no sensitive transaction.', () async { + await localAuthentication.authenticate( + authMessages: [const AndroidAuthMessages()], + localizedReason: 'Insecure', + options: const AuthenticationOptions( + sensitiveTransaction: false, + useErrorDialogs: false, + ), + ); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'Insecure', + 'useErrorDialogs': false, + 'stickyAuth': false, + 'sensitiveTransaction': false, + 'biometricOnly': false, + }..addAll(const AndroidAuthMessages().args)), + ], + ); + }); + }); + }); +} From 4ee959fc7f95cf3f57464c2db8432987c6065a19 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 11 Apr 2022 21:29:16 -0400 Subject: [PATCH 122/844] Roll Flutter from 6b461de0129e to 355fd23a799d (4 revisions) (#5230) --- .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 1eec7021c4be..ab6577828f8d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -6b461de0129ef7215118ef9d76e77bfa4c5f01df +355fd23a799dd437dd8d7067d75a04000d13c889 From 737e6282d971e802e64f4791827dbaaee116a411 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 11 Apr 2022 23:39:12 -0400 Subject: [PATCH 123/844] Roll Flutter from 355fd23a799d to 8a899d4a74ff (4 revisions) (#5232) --- .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 ab6577828f8d..5366d5a15553 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -355fd23a799dd437dd8d7067d75a04000d13c889 +8a899d4a74ff131648e9e9633db5151c275f1f08 From c3307440476bae722735cac63808886cc692da5a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 12 Apr 2022 10:24:14 -0400 Subject: [PATCH 124/844] Roll Flutter from 8a899d4a74ff to 9e0f0fe9d7b8 (2 revisions) (#5233) --- .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 5366d5a15553..25c6ddc070f4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8a899d4a74ff131648e9e9633db5151c275f1f08 +9e0f0fe9d7b8fc42195c84c521bf5809c9dbbfe4 From e39b3effb09434de7071190e70323d55dfd806d7 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 12 Apr 2022 14:19:12 -0400 Subject: [PATCH 125/844] [google_sign_in] Federate mobile implementations (#5214) --- .../google_sign_in/CHANGELOG.md | 4 + .../google_sign_in/android/settings.gradle | 1 - .../google_sign_in/example/ios/Podfile | 5 - .../ios/Runner.xcodeproj/project.pbxproj | 249 ------ .../google_sign_in/pubspec.yaml | 14 +- .../google_sign_in_android/AUTHORS | 66 ++ .../google_sign_in_android/CHANGELOG.md | 3 + .../google_sign_in_android/LICENSE | 25 + .../google_sign_in_android/README.md | 11 + .../android/build.gradle | 0 .../android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 0 .../googlesignin/BackgroundTaskRunner.java | 0 .../plugins/googlesignin/Executors.java | 0 .../googlesignin/GoogleSignInPlugin.java | 0 .../googlesignin/GoogleSignInWrapper.java | 0 .../googlesignin/GoogleSignInTest.java | 0 .../google_sign_in_android/example/README.md | 8 + .../example/android/app/build.gradle | 67 ++ .../example/android/app/google-services.json | 246 ++++++ .../gradle/wrapper/gradle-wrapper.properties | 5 + .../flutter/plugins/DartIntegrationTest.java | 14 + .../FlutterActivityTest.java | 19 + .../googlesigninexample/GoogleSignInTest.java | 0 .../android/app/src/debug/AndroidManifest.xml | 17 + .../android/app/src/main/AndroidManifest.xml | 19 + .../main/java/io/flutter/plugins/.gitignore | 1 + .../GoogleSignInTestActivity.java | 20 + .../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/strings.xml | 4 + .../example/android/build.gradle | 29 + .../example/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../example/android/settings.gradle | 15 + .../integration_test/google_sign_in_test.dart | 16 + .../example/lib/main.dart | 179 +++++ .../example/pubspec.yaml | 30 + .../example/test_driver/integration_test.dart | 7 + .../google_sign_in_android/pubspec.yaml | 35 + .../google_sign_in/google_sign_in_ios/AUTHORS | 66 ++ .../google_sign_in_ios/CHANGELOG.md | 3 + .../google_sign_in/google_sign_in_ios/LICENSE | 25 + .../google_sign_in_ios/README.md | 11 + .../google_sign_in_ios/example/README.md | 8 + .../integration_test/google_sign_in_test.dart | 16 + .../ios/Flutter/AppFrameworkInfo.plist | 30 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + .../ios/GoogleSignInPluginTest}/Info.plist | 0 .../google_sign_in_ios/example/ios/Podfile | 47 ++ .../ios/Runner.xcodeproj/project.pbxproj | 740 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/xcschemes/Runner.xcscheme | 107 +++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/ios/Runner/AppDelegate.h | 10 + .../example/ios/Runner/AppDelegate.m | 17 + .../AppIcon.appiconset/Contents.json | 116 +++ .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 bytes .../Runner/Base.lproj/LaunchScreen.storyboard | 27 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../ios/Runner/GoogleService-Info.plist | 44 ++ .../example/ios/Runner/Info.plist | 64 ++ .../example/ios/Runner/main.m | 13 + .../ios/RunnerTests/GoogleSignInTests.m | 4 +- .../example/ios/RunnerTests}/Info.plist | 0 .../ios/RunnerUITests/GoogleSignInUITests.m | 0 .../example/ios/RunnerUITests/Info.plist | 22 + .../google_sign_in_ios/example/lib/main.dart | 179 +++++ .../google_sign_in_ios/example/pubspec.yaml | 29 + .../example/test_driver/integration_test.dart | 7 + .../ios/Assets/.gitkeep | 0 .../ios/Classes/FLTGoogleSignInPlugin.h | 0 .../ios/Classes/FLTGoogleSignInPlugin.m | 0 .../Classes/FLTGoogleSignInPlugin.modulemap | 4 +- .../ios/Classes/FLTGoogleSignInPlugin_Test.h | 2 +- .../Classes/google_sign_in_ios-umbrella.h} | 2 +- .../ios/google_sign_in_ios.podspec} | 4 +- .../google_sign_in_ios/pubspec.yaml | 35 + 96 files changed, 2539 insertions(+), 266 deletions(-) delete mode 100644 packages/google_sign_in/google_sign_in/android/settings.gradle create mode 100644 packages/google_sign_in/google_sign_in_android/AUTHORS create mode 100644 packages/google_sign_in/google_sign_in_android/CHANGELOG.md create mode 100644 packages/google_sign_in/google_sign_in_android/LICENSE create mode 100644 packages/google_sign_in/google_sign_in_android/README.md rename packages/google_sign_in/{google_sign_in => google_sign_in_android}/android/build.gradle (100%) create mode 100644 packages/google_sign_in/google_sign_in_android/android/settings.gradle rename packages/google_sign_in/{google_sign_in => google_sign_in_android}/android/src/main/AndroidManifest.xml (100%) rename packages/google_sign_in/{google_sign_in => google_sign_in_android}/android/src/main/java/io/flutter/plugins/googlesignin/BackgroundTaskRunner.java (100%) rename packages/google_sign_in/{google_sign_in => google_sign_in_android}/android/src/main/java/io/flutter/plugins/googlesignin/Executors.java (100%) rename packages/google_sign_in/{google_sign_in => google_sign_in_android}/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java (100%) rename packages/google_sign_in/{google_sign_in => google_sign_in_android}/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInWrapper.java (100%) rename packages/google_sign_in/{google_sign_in => google_sign_in_android}/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java (100%) create mode 100644 packages/google_sign_in/google_sign_in_android/example/README.md create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/build.gradle create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/google-services.json create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlesigninexample/FlutterActivityTest.java rename packages/google_sign_in/{google_sign_in => google_sign_in_android}/example/android/app/src/androidTest/java/io/flutter/plugins/googlesigninexample/GoogleSignInTest.java (100%) create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/debug/AndroidManifest.xml create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/main/AndroidManifest.xml create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/main/java/io/flutter/plugins/.gitignore create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/GoogleSignInTestActivity.java create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/values/strings.xml create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/build.gradle create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/gradle.properties create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/google_sign_in/google_sign_in_android/example/android/settings.gradle create mode 100644 packages/google_sign_in/google_sign_in_android/example/integration_test/google_sign_in_test.dart create mode 100644 packages/google_sign_in/google_sign_in_android/example/lib/main.dart create mode 100644 packages/google_sign_in/google_sign_in_android/example/pubspec.yaml create mode 100644 packages/google_sign_in/google_sign_in_android/example/test_driver/integration_test.dart create mode 100644 packages/google_sign_in/google_sign_in_android/pubspec.yaml create mode 100644 packages/google_sign_in/google_sign_in_ios/AUTHORS create mode 100644 packages/google_sign_in/google_sign_in_ios/CHANGELOG.md create mode 100644 packages/google_sign_in/google_sign_in_ios/LICENSE create mode 100644 packages/google_sign_in/google_sign_in_ios/README.md create mode 100644 packages/google_sign_in/google_sign_in_ios/example/README.md create mode 100644 packages/google_sign_in/google_sign_in_ios/example/integration_test/google_sign_in_test.dart create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/Debug.xcconfig create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/Release.xcconfig rename packages/google_sign_in/{google_sign_in/example/ios/RunnerTests => google_sign_in_ios/example/ios/GoogleSignInPluginTest}/Info.plist (100%) create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Podfile create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/AppDelegate.h create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/AppDelegate.m create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/GoogleService-Info.plist create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Info.plist create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/Runner/main.m rename packages/google_sign_in/{google_sign_in => google_sign_in_ios}/example/ios/RunnerTests/GoogleSignInTests.m (99%) rename packages/google_sign_in/{google_sign_in/example/ios/RunnerUITests => google_sign_in_ios/example/ios/RunnerTests}/Info.plist (100%) rename packages/google_sign_in/{google_sign_in => google_sign_in_ios}/example/ios/RunnerUITests/GoogleSignInUITests.m (100%) create mode 100644 packages/google_sign_in/google_sign_in_ios/example/ios/RunnerUITests/Info.plist create mode 100644 packages/google_sign_in/google_sign_in_ios/example/lib/main.dart create mode 100644 packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml create mode 100644 packages/google_sign_in/google_sign_in_ios/example/test_driver/integration_test.dart rename packages/google_sign_in/{google_sign_in => google_sign_in_ios}/ios/Assets/.gitkeep (100%) rename packages/google_sign_in/{google_sign_in => google_sign_in_ios}/ios/Classes/FLTGoogleSignInPlugin.h (100%) rename packages/google_sign_in/{google_sign_in => google_sign_in_ios}/ios/Classes/FLTGoogleSignInPlugin.m (100%) rename packages/google_sign_in/{google_sign_in => google_sign_in_ios}/ios/Classes/FLTGoogleSignInPlugin.modulemap (55%) rename packages/google_sign_in/{google_sign_in => google_sign_in_ios}/ios/Classes/FLTGoogleSignInPlugin_Test.h (89%) rename packages/google_sign_in/{google_sign_in/ios/Classes/google_sign_in-umbrella.h => google_sign_in_ios/ios/Classes/google_sign_in_ios-umbrella.h} (85%) rename packages/google_sign_in/{google_sign_in/ios/google_sign_in.podspec => google_sign_in_ios/ios/google_sign_in_ios.podspec} (90%) create mode 100644 packages/google_sign_in/google_sign_in_ios/pubspec.yaml diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 24b729c4d78a..bee4dafaf16f 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Moves Android and iOS implementations to federated packages. + ## 5.2.5 * Migrates from `ui.hash*` to `Object.hash*`. diff --git a/packages/google_sign_in/google_sign_in/android/settings.gradle b/packages/google_sign_in/google_sign_in/android/settings.gradle deleted file mode 100644 index d943fae5ece0..000000000000 --- a/packages/google_sign_in/google_sign_in/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'googlesignin' diff --git a/packages/google_sign_in/google_sign_in/example/ios/Podfile b/packages/google_sign_in/google_sign_in/example/ios/Podfile index e577a3081fe8..56085c312df7 100644 --- a/packages/google_sign_in/google_sign_in/example/ios/Podfile +++ b/packages/google_sign_in/google_sign_in/example/ios/Podfile @@ -29,11 +29,6 @@ flutter_ios_podfile_setup target 'Runner' do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) - target 'RunnerTests' do - inherit! :search_paths - - pod 'OCMock','3.5' - end end post_install do |installer| diff --git a/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj index 06857ed2bd59..8909bb9b31c6 100644 --- a/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj @@ -16,28 +16,8 @@ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; C2FB9CBA01DB0A2DE5F31E12 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0263E28FA425D1CE928BDE15 /* libPods-Runner.a */; }; - C56D3B06A42F3B35C1F47A43 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18AD6475292B9C45B529DDC9 /* libPods-RunnerTests.a */; }; - F76AC1A52666D0540040C8BC /* GoogleSignInTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F76AC1A42666D0540040C8BC /* GoogleSignInTests.m */; }; - F76AC1B32666D0610040C8BC /* GoogleSignInUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F76AC1B22666D0610040C8BC /* GoogleSignInUITests.m */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - F76AC1A72666D0540040C8BC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; - F76AC1B52666D0610040C8BC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; @@ -73,12 +53,6 @@ 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 = ""; }; F582639B44581540871D9BB0 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - F76AC1A22666D0540040C8BC /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - F76AC1A42666D0540040C8BC /* GoogleSignInTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleSignInTests.m; sourceTree = ""; }; - F76AC1A62666D0540040C8BC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - F76AC1B02666D0610040C8BC /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - F76AC1B22666D0610040C8BC /* GoogleSignInUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleSignInUITests.m; sourceTree = ""; }; - F76AC1B42666D0610040C8BC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -90,21 +64,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - F76AC19F2666D0540040C8BC /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - C56D3B06A42F3B35C1F47A43 /* libPods-RunnerTests.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F76AC1AD2666D0610040C8BC /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -135,8 +94,6 @@ children = ( 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, - F76AC1A32666D0540040C8BC /* RunnerTests */, - F76AC1B12666D0610040C8BC /* RunnerUITests */, 97C146EF1CF9000F007C117D /* Products */, 840012C8B5EDBCF56B0E4AC1 /* Pods */, CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, @@ -147,8 +104,6 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, - F76AC1A22666D0540040C8BC /* RunnerTests.xctest */, - F76AC1B02666D0610040C8BC /* RunnerUITests.xctest */, ); name = Products; sourceTree = ""; @@ -187,24 +142,6 @@ name = Frameworks; sourceTree = ""; }; - F76AC1A32666D0540040C8BC /* RunnerTests */ = { - isa = PBXGroup; - children = ( - F76AC1A42666D0540040C8BC /* GoogleSignInTests.m */, - F76AC1A62666D0540040C8BC /* Info.plist */, - ); - path = RunnerTests; - sourceTree = ""; - }; - F76AC1B12666D0610040C8BC /* RunnerUITests */ = { - isa = PBXGroup; - children = ( - F76AC1B22666D0610040C8BC /* GoogleSignInUITests.m */, - F76AC1B42666D0610040C8BC /* Info.plist */, - ); - path = RunnerUITests; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -230,43 +167,6 @@ productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; }; - F76AC1A12666D0540040C8BC /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = F76AC1AB2666D0540040C8BC /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - 27975964E48117AA65B1D6C7 /* [CP] Check Pods Manifest.lock */, - F76AC19E2666D0540040C8BC /* Sources */, - F76AC19F2666D0540040C8BC /* Frameworks */, - F76AC1A02666D0540040C8BC /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - F76AC1A82666D0540040C8BC /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = RunnerTests; - productReference = F76AC1A22666D0540040C8BC /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - F76AC1AF2666D0610040C8BC /* RunnerUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = F76AC1B72666D0610040C8BC /* Build configuration list for PBXNativeTarget "RunnerUITests" */; - buildPhases = ( - F76AC1AC2666D0610040C8BC /* Sources */, - F76AC1AD2666D0610040C8BC /* Frameworks */, - F76AC1AE2666D0610040C8BC /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - F76AC1B62666D0610040C8BC /* PBXTargetDependency */, - ); - name = RunnerUITests; - productName = RunnerUITests; - productReference = F76AC1B02666D0610040C8BC /* RunnerUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -279,16 +179,6 @@ 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; }; - F76AC1A12666D0540040C8BC = { - CreatedOnToolsVersion = 12.5; - ProvisioningStyle = Automatic; - TestTargetID = 97C146ED1CF9000F007C117D; - }; - F76AC1AF2666D0610040C8BC = { - CreatedOnToolsVersion = 12.5; - ProvisioningStyle = Automatic; - TestTargetID = 97C146ED1CF9000F007C117D; - }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; @@ -305,8 +195,6 @@ projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, - F76AC1A12666D0540040C8BC /* RunnerTests */, - F76AC1AF2666D0610040C8BC /* RunnerUITests */, ); }; /* End PBXProject section */ @@ -324,45 +212,9 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - F76AC1A02666D0540040C8BC /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F76AC1AE2666D0610040C8BC /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 27975964E48117AA65B1D6C7 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -440,37 +292,8 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - F76AC19E2666D0540040C8BC /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F76AC1A52666D0540040C8BC /* GoogleSignInTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F76AC1AC2666D0610040C8BC /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - F76AC1B32666D0610040C8BC /* GoogleSignInUITests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - F76AC1A82666D0540040C8BC /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = F76AC1A72666D0540040C8BC /* PBXContainerItemProxy */; - }; - F76AC1B62666D0610040C8BC /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = F76AC1B52666D0610040C8BC /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -641,60 +464,6 @@ }; name = Release; }; - F76AC1A92666D0540040C8BC /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 37E582FF620A90D0EB2C0851 /* Pods-RunnerTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; - INFOPLIST_FILE = RunnerTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Debug; - }; - F76AC1AA2666D0540040C8BC /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 45D93D4513839BFEA2AA74FE /* Pods-RunnerTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; - INFOPLIST_FILE = RunnerTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Release; - }; - F76AC1B82666D0610040C8BC /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RunnerUITests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_TARGET_NAME = Runner; - }; - name = Debug; - }; - F76AC1B92666D0610040C8BC /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = RunnerUITests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_TARGET_NAME = Runner; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -716,24 +485,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - F76AC1AB2666D0540040C8BC /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F76AC1A92666D0540040C8BC /* Debug */, - F76AC1AA2666D0540040C8BC /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F76AC1B72666D0610040C8BC /* Build configuration list for PBXNativeTarget "RunnerUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F76AC1B82666D0610040C8BC /* Debug */, - F76AC1B92666D0610040C8BC /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ }; rootObject = 97C146E61CF9000F007C117D /* Project object */; diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 2c5de81f8e98..43904af23121 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -4,6 +4,10 @@ description: Flutter plugin for Google Sign-In, a secure authentication system repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 version: 5.2.5 +# Temporarily disable publishing to allow moving Android and iOS +# implementations. +publish_to: none + environment: sdk: ">=2.14.0 <3.0.0" @@ -13,16 +17,20 @@ flutter: plugin: platforms: android: - package: io.flutter.plugins.googlesignin - pluginClass: GoogleSignInPlugin + default_package: google_sign_in_android ios: - pluginClass: FLTGoogleSignInPlugin + default_package: google_sign_in_ios web: default_package: google_sign_in_web dependencies: flutter: sdk: flutter + # Temporary path dependencies to allow moving Android and iOS implementations. + google_sign_in_android: + path: ../google_sign_in_android + google_sign_in_ios: + path: ../google_sign_in_ios google_sign_in_platform_interface: ^2.1.0 google_sign_in_web: ^0.10.0 diff --git a/packages/google_sign_in/google_sign_in_android/AUTHORS b/packages/google_sign_in/google_sign_in_android/AUTHORS new file mode 100644 index 000000000000..493a0b4ef9c2 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/AUTHORS @@ -0,0 +1,66 @@ +# 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. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md new file mode 100644 index 000000000000..e7c6847e7750 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -0,0 +1,3 @@ +## 5.2.5 + +* Splits from `video_player` as a federated implementation. diff --git a/packages/google_sign_in/google_sign_in_android/LICENSE b/packages/google_sign_in/google_sign_in_android/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/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/google_sign_in/google_sign_in_android/README.md b/packages/google_sign_in/google_sign_in_android/README.md new file mode 100644 index 000000000000..5c7c70ede917 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/README.md @@ -0,0 +1,11 @@ +# google\_sign\_in\_android + +The Android implementation of [`google_sign_in`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `google_sign_in` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/google_sign_in +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/google_sign_in/google_sign_in/android/build.gradle b/packages/google_sign_in/google_sign_in_android/android/build.gradle similarity index 100% rename from packages/google_sign_in/google_sign_in/android/build.gradle rename to packages/google_sign_in/google_sign_in_android/android/build.gradle diff --git a/packages/google_sign_in/google_sign_in_android/android/settings.gradle b/packages/google_sign_in/google_sign_in_android/android/settings.gradle new file mode 100644 index 000000000000..35ebd0e2428a --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'google_sign_in_android' diff --git a/packages/google_sign_in/google_sign_in/android/src/main/AndroidManifest.xml b/packages/google_sign_in/google_sign_in_android/android/src/main/AndroidManifest.xml similarity index 100% rename from packages/google_sign_in/google_sign_in/android/src/main/AndroidManifest.xml rename to packages/google_sign_in/google_sign_in_android/android/src/main/AndroidManifest.xml diff --git a/packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/BackgroundTaskRunner.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/BackgroundTaskRunner.java similarity index 100% rename from packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/BackgroundTaskRunner.java rename to packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/BackgroundTaskRunner.java diff --git a/packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/Executors.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/Executors.java similarity index 100% rename from packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/Executors.java rename to packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/Executors.java diff --git a/packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java similarity index 100% rename from packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java rename to packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java diff --git a/packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInWrapper.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInWrapper.java similarity index 100% rename from packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInWrapper.java rename to packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInWrapper.java diff --git a/packages/google_sign_in/google_sign_in/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java b/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java similarity index 100% rename from packages/google_sign_in/google_sign_in/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java rename to packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java diff --git a/packages/google_sign_in/google_sign_in_android/example/README.md b/packages/google_sign_in/google_sign_in_android/example/README.md new file mode 100644 index 000000000000..79d99dc72982 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/README.md @@ -0,0 +1,8 @@ +# google_sign_in_android example + +Exercises the Android implementation of `GoogleSignInPlatform`. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/build.gradle b/packages/google_sign_in/google_sign_in_android/example/android/app/build.gradle new file mode 100644 index 000000000000..8ac99fe56f3a --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/build.gradle @@ -0,0 +1,67 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 31 + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + applicationId "io.flutter.plugins.googlesigninexample" + minSdkVersion 16 + targetSdkVersion 28 + multiDexEnabled true + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } + + testOptions { + unitTests.returnDefaultValues = true + } +} + +flutter { + source '../..' +} + +dependencies { + implementation 'com.google.android.gms:play-services-auth:16.0.1' + testImplementation'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + api 'androidx.test:core:1.2.0' +} diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/google-services.json b/packages/google_sign_in/google_sign_in_android/example/android/app/google-services.json new file mode 100644 index 000000000000..efa524535553 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/google-services.json @@ -0,0 +1,246 @@ +{ + "project_info": { + "project_number": "479882132969", + "firebase_url": "https://my-flutter-proj.firebaseio.com", + "project_id": "my-flutter-proj", + "storage_bucket": "my-flutter-proj.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:479882132969:android:c73fd19ff7e2c0be", + "android_client_info": { + "package_name": "io.flutter.plugins.cameraexample" + } + }, + "oauth_client": [ + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCrZz9T0Pg0rDnpxfNuPBrOxGhXskfebXs" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 1, + "other_platform_oauth_client": [] + }, + "ads_service": { + "status": 2 + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:479882132969:android:632cdf3fc0a17139", + "android_client_info": { + "package_name": "io.flutter.plugins.firebasedynamiclinksexample" + } + }, + "oauth_client": [ + { + "client_id": "479882132969-32qusitiag53931ck80h121ajhlc5a7e.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "io.flutter.plugins.firebasedynamiclinksexample", + "certificate_hash": "e733b7a303250b63e06de6f7c9767c517d69cfa0" + } + }, + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCrZz9T0Pg0rDnpxfNuPBrOxGhXskfebXs" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 2, + "other_platform_oauth_client": [ + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "479882132969-gjp4e63ogu2h6guttj2ie6t3f10ic7i8.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "io.flutter.plugins.firebaseMlVisionExample" + } + } + ] + }, + "ads_service": { + "status": 2 + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:479882132969:android:ae50362b4bc06086", + "android_client_info": { + "package_name": "io.flutter.plugins.firebasemlvisionexample" + } + }, + "oauth_client": [ + { + "client_id": "479882132969-9pp74fkgmtvt47t9rikc1p861v7n85tn.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "io.flutter.plugins.firebasemlvisionexample", + "certificate_hash": "e733b7a303250b63e06de6f7c9767c517d69cfa0" + } + }, + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCrZz9T0Pg0rDnpxfNuPBrOxGhXskfebXs" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 2, + "other_platform_oauth_client": [ + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "479882132969-gjp4e63ogu2h6guttj2ie6t3f10ic7i8.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "io.flutter.plugins.firebaseMlVisionExample" + } + } + ] + }, + "ads_service": { + "status": 2 + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:479882132969:android:215a22700e1b466b", + "android_client_info": { + "package_name": "io.flutter.plugins.firebaseperformanceexample" + } + }, + "oauth_client": [ + { + "client_id": "479882132969-8h4kiv8m7ho4tvn6uuujsfcrf69unuf7.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "io.flutter.plugins.firebaseperformanceexample", + "certificate_hash": "e733b7a303250b63e06de6f7c9767c517d69cfa0" + } + }, + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCrZz9T0Pg0rDnpxfNuPBrOxGhXskfebXs" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 2, + "other_platform_oauth_client": [ + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "479882132969-gjp4e63ogu2h6guttj2ie6t3f10ic7i8.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "io.flutter.plugins.firebaseMlVisionExample" + } + } + ] + }, + "ads_service": { + "status": 2 + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:479882132969:android:5e9f1f89e134dc86", + "android_client_info": { + "package_name": "io.flutter.plugins.googlesigninexample" + } + }, + "oauth_client": [ + { + "client_id": "479882132969-90ml692hkonp587sl0v0rurmnvkekgrg.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "io.flutter.plugins.googlesigninexample", + "certificate_hash": "e733b7a303250b63e06de6f7c9767c517d69cfa0" + } + }, + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCrZz9T0Pg0rDnpxfNuPBrOxGhXskfebXs" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 2, + "other_platform_oauth_client": [ + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "479882132969-gjp4e63ogu2h6guttj2ie6t3f10ic7i8.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "io.flutter.plugins.firebaseMlVisionExample" + } + } + ] + }, + "ads_service": { + "status": 2 + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/google_sign_in/google_sign_in_android/example/android/app/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..9a4163a4f5ee --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java new file mode 100644 index 000000000000..0f4298dca155 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java @@ -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. + +package io.flutter.plugins; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DartIntegrationTest {} diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlesigninexample/FlutterActivityTest.java b/packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlesigninexample/FlutterActivityTest.java new file mode 100644 index 000000000000..edc01de491af --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlesigninexample/FlutterActivityTest.java @@ -0,0 +1,19 @@ +// 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.googlesigninexample; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.integration_test.FlutterTestRunner; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.plugins.DartIntegrationTest; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@DartIntegrationTest +@RunWith(FlutterTestRunner.class) +public class FlutterActivityTest { + @Rule + public ActivityTestRule rule = new ActivityTestRule<>(FlutterActivity.class); +} diff --git a/packages/google_sign_in/google_sign_in/example/android/app/src/androidTest/java/io/flutter/plugins/googlesigninexample/GoogleSignInTest.java b/packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlesigninexample/GoogleSignInTest.java similarity index 100% rename from packages/google_sign_in/google_sign_in/example/android/app/src/androidTest/java/io/flutter/plugins/googlesigninexample/GoogleSignInTest.java rename to packages/google_sign_in/google_sign_in_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlesigninexample/GoogleSignInTest.java diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/src/debug/AndroidManifest.xml b/packages/google_sign_in/google_sign_in_android/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000000..4d764900a530 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/AndroidManifest.xml b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..22a34d7218f7 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/java/io/flutter/plugins/.gitignore b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/java/io/flutter/plugins/.gitignore new file mode 100644 index 000000000000..9eb4563d2ae1 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/java/io/flutter/plugins/.gitignore @@ -0,0 +1 @@ +GeneratedPluginRegistrant.java diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/GoogleSignInTestActivity.java b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/GoogleSignInTestActivity.java new file mode 100644 index 000000000000..09506a2632df --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/GoogleSignInTestActivity.java @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.googlesigninexample; + +import androidx.annotation.NonNull; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.engine.FlutterEngine; + +// Makes the FlutterEngine accessible for testing. +public class GoogleSignInTestActivity extends FlutterActivity { + public FlutterEngine engine; + + @Override + public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { + super.configureFlutterEngine(flutterEngine); + engine = flutterEngine; + } +} diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/google_sign_in/google_sign_in_android/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/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/google_sign_in/google_sign_in_android/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/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/google_sign_in/google_sign_in_android/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/google_sign_in/google_sign_in_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/google_sign_in/google_sign_in_android/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/google_sign_in/google_sign_in_android/example/android/app/src/main/res/values/strings.xml b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/values/strings.xml new file mode 100644 index 000000000000..c7e28ffcedd1 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + YOUR_WEB_CLIENT_ID + diff --git a/packages/google_sign_in/google_sign_in_android/example/android/build.gradle b/packages/google_sign_in/google_sign_in_android/example/android/build.gradle new file mode 100644 index 000000000000..e101ac08df55 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.3.0' + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/packages/google_sign_in/google_sign_in_android/example/android/gradle.properties b/packages/google_sign_in/google_sign_in_android/example/android/gradle.properties new file mode 100644 index 000000000000..d12b9a8297e5 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.enableR8=true +android.useAndroidX=true diff --git a/packages/google_sign_in/google_sign_in_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/google_sign_in/google_sign_in_android/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..019065d1d650 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/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-4.10.2-all.zip diff --git a/packages/google_sign_in/google_sign_in_android/example/android/settings.gradle b/packages/google_sign_in/google_sign_in_android/example/android/settings.gradle new file mode 100644 index 000000000000..115da6cb4f4d --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withInputStream { stream -> plugins.load(stream) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/packages/google_sign_in/google_sign_in_android/example/integration_test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in_android/example/integration_test/google_sign_in_test.dart new file mode 100644 index 000000000000..d4631f6a6fd3 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/integration_test/google_sign_in_test.dart @@ -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. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; +import 'package:integration_test/integration_test.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('Can initialize the plugin', (WidgetTester tester) async { + final GoogleSignInPlatform signIn = GoogleSignInPlatform.instance; + expect(signIn, isNotNull); + }); +} diff --git a/packages/google_sign_in/google_sign_in_android/example/lib/main.dart b/packages/google_sign_in/google_sign_in_android/example/lib/main.dart new file mode 100644 index 000000000000..a750c330001d --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/lib/main.dart @@ -0,0 +1,179 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; +import 'dart:convert' show json; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; +import 'package:http/http.dart' as http; + +void main() { + runApp( + MaterialApp( + title: 'Google Sign In', + home: SignInDemo(), + ), + ); +} + +class SignInDemo extends StatefulWidget { + @override + State createState() => SignInDemoState(); +} + +class SignInDemoState extends State { + GoogleSignInUserData? _currentUser; + String _contactText = ''; + // Future that completes when `init` has completed on the sign in instance. + Future? _initialization; + + @override + void initState() { + super.initState(); + _signIn(); + } + + Future _ensureInitialized() { + return _initialization ??= GoogleSignInPlatform.instance.init( + scopes: [ + 'email', + 'https://www.googleapis.com/auth/contacts.readonly', + ], + )..catchError((dynamic _) { + _initialization = null; + }); + } + + void _setUser(GoogleSignInUserData? user) { + setState(() { + _currentUser = user; + if (user != null) { + _handleGetContact(user); + } + }); + } + + Future _signIn() async { + await _ensureInitialized(); + final GoogleSignInUserData? newUser = + await GoogleSignInPlatform.instance.signInSilently(); + _setUser(newUser); + } + + Future> _getAuthHeaders() async { + final GoogleSignInUserData? user = _currentUser; + if (user == null) { + throw StateError('No user signed in'); + } + + final GoogleSignInTokenData response = + await GoogleSignInPlatform.instance.getTokens( + email: user.email, + shouldRecoverAuth: true, + ); + + return { + 'Authorization': 'Bearer ${response.accessToken}', + // TODO(kevmoo): Use the correct value once it's available. + // See https://github.com/flutter/flutter/issues/80905 + 'X-Goog-AuthUser': '0', + }; + } + + Future _handleGetContact(GoogleSignInUserData user) async { + setState(() { + _contactText = 'Loading contact info...'; + }); + final http.Response response = await http.get( + Uri.parse('https://people.googleapis.com/v1/people/me/connections' + '?requestMask.includeField=person.names'), + headers: await _getAuthHeaders(), + ); + if (response.statusCode != 200) { + setState(() { + _contactText = 'People API gave a ${response.statusCode} ' + 'response. Check logs for details.'; + }); + print('People API ${response.statusCode} response: ${response.body}'); + return; + } + final Map data = + json.decode(response.body) as Map; + final int contactCount = + (data['connections'] as List?)?.length ?? 0; + setState(() { + _contactText = '$contactCount contacts found'; + }); + } + + Future _handleSignIn() async { + try { + await _ensureInitialized(); + _setUser(await GoogleSignInPlatform.instance.signIn()); + } catch (error) { + final bool canceled = + error is PlatformException && error.code == 'sign_in_canceled'; + if (!canceled) { + print(error); + } + } + } + + Future _handleSignOut() async { + await _ensureInitialized(); + await GoogleSignInPlatform.instance.disconnect(); + } + + Widget _buildBody() { + final GoogleSignInUserData? user = _currentUser; + if (user != null) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + ListTile( + title: Text(user.displayName ?? ''), + subtitle: Text(user.email), + ), + const Text('Signed in successfully.'), + Text(_contactText), + ElevatedButton( + child: const Text('SIGN OUT'), + onPressed: _handleSignOut, + ), + ElevatedButton( + child: const Text('REFRESH'), + onPressed: () => _handleGetContact(user), + ), + ], + ); + } else { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + const Text('You are not currently signed in.'), + ElevatedButton( + child: const Text('SIGN IN'), + onPressed: _handleSignIn, + ), + ], + ); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Google Sign In'), + ), + body: ConstrainedBox( + constraints: const BoxConstraints.expand(), + child: _buildBody(), + )); + } +} diff --git a/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml new file mode 100644 index 000000000000..316cdd893a2c --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml @@ -0,0 +1,30 @@ +name: google_sign_in_example +description: Example of Google Sign-In plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + google_sign_in_android: + # When depending on this package from a real application you should use: + # google_sign_in_android: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # 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_sign_in_platform_interface: ^2.1.0 + http: ^0.13.0 + +dev_dependencies: + espresso: ^0.1.0+2 + flutter_driver: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/google_sign_in/google_sign_in_android/example/test_driver/integration_test.dart b/packages/google_sign_in/google_sign_in_android/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/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/google_sign_in/google_sign_in_android/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/pubspec.yaml new file mode 100644 index 000000000000..efd679ca399b --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/pubspec.yaml @@ -0,0 +1,35 @@ +name: google_sign_in_android +description: Android implementation of the google_sign_in plugin. +repository: https://github.com/flutter/plugins/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: 5.2.5 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +flutter: + plugin: + implements: google_sign_in + platforms: + android: + package: io.flutter.plugins.googlesignin + pluginClass: GoogleSignInPlugin + +dependencies: + flutter: + sdk: flutter + google_sign_in_platform_interface: ^2.1.0 + +dev_dependencies: + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + +# The example deliberately includes limited-use secrets. +false_secrets: + - /example/android/app/google-services.json + - /example/lib/main.dart diff --git a/packages/google_sign_in/google_sign_in_ios/AUTHORS b/packages/google_sign_in/google_sign_in_ios/AUTHORS new file mode 100644 index 000000000000..493a0b4ef9c2 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/AUTHORS @@ -0,0 +1,66 @@ +# 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. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md new file mode 100644 index 000000000000..e7c6847e7750 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md @@ -0,0 +1,3 @@ +## 5.2.5 + +* Splits from `video_player` as a federated implementation. diff --git a/packages/google_sign_in/google_sign_in_ios/LICENSE b/packages/google_sign_in/google_sign_in_ios/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/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/google_sign_in/google_sign_in_ios/README.md b/packages/google_sign_in/google_sign_in_ios/README.md new file mode 100644 index 000000000000..25e08fdb4040 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/README.md @@ -0,0 +1,11 @@ +# google\_sign\_in\_ios + +The iOS implementation of [`google_sign_in`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `google_sign_in` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/google_sign_in +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/google_sign_in/google_sign_in_ios/example/README.md b/packages/google_sign_in/google_sign_in_ios/example/README.md new file mode 100644 index 000000000000..ca3dc7023fc9 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/README.md @@ -0,0 +1,8 @@ +# google_sign_in_ios example + +Exercises the iOS implementation of `GoogleSignInPlatform`. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/google_sign_in/google_sign_in_ios/example/integration_test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in_ios/example/integration_test/google_sign_in_test.dart new file mode 100644 index 000000000000..d4631f6a6fd3 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/integration_test/google_sign_in_test.dart @@ -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. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; +import 'package:integration_test/integration_test.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('Can initialize the plugin', (WidgetTester tester) async { + final GoogleSignInPlatform signIn = GoogleSignInPlatform.instance; + expect(signIn, isNotNull); + }); +} diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/AppFrameworkInfo.plist b/packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000000..3a9c234f96d4 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + UIRequiredDeviceCapabilities + + arm64 + + MinimumOSVersion + 9.0 + + diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/Debug.xcconfig b/packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000000..9803018ca79d --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Generated.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/Release.xcconfig b/packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000000..a4a8c604e13d --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Generated.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" diff --git a/packages/google_sign_in/google_sign_in/example/ios/RunnerTests/Info.plist b/packages/google_sign_in/google_sign_in_ios/example/ios/GoogleSignInPluginTest/Info.plist similarity index 100% rename from packages/google_sign_in/google_sign_in/example/ios/RunnerTests/Info.plist rename to packages/google_sign_in/google_sign_in_ios/example/ios/GoogleSignInPluginTest/Info.plist diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile b/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile new file mode 100644 index 000000000000..e577a3081fe8 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile @@ -0,0 +1,47 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.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 + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + + pod 'OCMock','3.5' + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + target.build_configurations.each do |build_configuration| + # GoogleSignIn does not support arm64 simulators. + build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386' + end + end +end diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..f2bf4ebc514e --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,740 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 5C6F5A6E1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C6F5A6D1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m */; }; + 7A303C2E1E89D76400B1F19E /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7A303C2D1E89D76400B1F19E /* GoogleService-Info.plist */; }; + 7ACDFB0E1E8944C400BE2D00 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7ACDFB0D1E8944C400BE2D00 /* AppFrameworkInfo.plist */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 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 */; }; + C2FB9CBA01DB0A2DE5F31E12 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0263E28FA425D1CE928BDE15 /* libPods-Runner.a */; }; + C56D3B06A42F3B35C1F47A43 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18AD6475292B9C45B529DDC9 /* libPods-RunnerTests.a */; }; + F76AC1A52666D0540040C8BC /* GoogleSignInTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F76AC1A42666D0540040C8BC /* GoogleSignInTests.m */; }; + F76AC1B32666D0610040C8BC /* GoogleSignInUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F76AC1B22666D0610040C8BC /* GoogleSignInUITests.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + F76AC1A72666D0540040C8BC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; + F76AC1B52666D0610040C8BC /* 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 */ + 0263E28FA425D1CE928BDE15 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 18AD6475292B9C45B529DDC9 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 37E582FF620A90D0EB2C0851 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 45D93D4513839BFEA2AA74FE /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 5A76713E622F06379AEDEBFA /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 5C6F5A6C1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 5C6F5A6D1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 7A303C2D1E89D76400B1F19E /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 7ACDFB0D1E8944C400BE2D00 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; 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; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 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 = ""; }; + F582639B44581540871D9BB0 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + F76AC1A22666D0540040C8BC /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + F76AC1A42666D0540040C8BC /* GoogleSignInTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleSignInTests.m; sourceTree = ""; }; + F76AC1A62666D0540040C8BC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F76AC1B02666D0610040C8BC /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + F76AC1B22666D0610040C8BC /* GoogleSignInUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleSignInUITests.m; sourceTree = ""; }; + F76AC1B42666D0610040C8BC /* 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 = ( + C2FB9CBA01DB0A2DE5F31E12 /* libPods-Runner.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F76AC19F2666D0540040C8BC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C56D3B06A42F3B35C1F47A43 /* libPods-RunnerTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F76AC1AD2666D0610040C8BC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 840012C8B5EDBCF56B0E4AC1 /* Pods */ = { + isa = PBXGroup; + children = ( + 5A76713E622F06379AEDEBFA /* Pods-Runner.debug.xcconfig */, + F582639B44581540871D9BB0 /* Pods-Runner.release.xcconfig */, + 37E582FF620A90D0EB2C0851 /* Pods-RunnerTests.debug.xcconfig */, + 45D93D4513839BFEA2AA74FE /* Pods-RunnerTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 7ACDFB0D1E8944C400BE2D00 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + F76AC1A32666D0540040C8BC /* RunnerTests */, + F76AC1B12666D0610040C8BC /* RunnerUITests */, + 97C146EF1CF9000F007C117D /* Products */, + 840012C8B5EDBCF56B0E4AC1 /* Pods */, + CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + F76AC1A22666D0540040C8BC /* RunnerTests.xctest */, + F76AC1B02666D0610040C8BC /* RunnerUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 5C6F5A6C1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.h */, + 5C6F5A6D1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m */, + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 7A303C2D1E89D76400B1F19E /* GoogleService-Info.plist */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + CF3B75C9A7D2FA2A4C99F110 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 0263E28FA425D1CE928BDE15 /* libPods-Runner.a */, + 18AD6475292B9C45B529DDC9 /* libPods-RunnerTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + F76AC1A32666D0540040C8BC /* RunnerTests */ = { + isa = PBXGroup; + children = ( + F76AC1A42666D0540040C8BC /* GoogleSignInTests.m */, + F76AC1A62666D0540040C8BC /* Info.plist */, + ); + path = RunnerTests; + sourceTree = ""; + }; + F76AC1B12666D0610040C8BC /* RunnerUITests */ = { + isa = PBXGroup; + children = ( + F76AC1B22666D0610040C8BC /* GoogleSignInUITests.m */, + F76AC1B42666D0610040C8BC /* Info.plist */, + ); + path = RunnerUITests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + AB1344B0443C71CD721E1BB7 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 532EA9D341340B1DCD08293D /* [CP] Copy Pods Resources */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; + F76AC1A12666D0540040C8BC /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F76AC1AB2666D0540040C8BC /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 27975964E48117AA65B1D6C7 /* [CP] Check Pods Manifest.lock */, + F76AC19E2666D0540040C8BC /* Sources */, + F76AC19F2666D0540040C8BC /* Frameworks */, + F76AC1A02666D0540040C8BC /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + F76AC1A82666D0540040C8BC /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = F76AC1A22666D0540040C8BC /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + F76AC1AF2666D0610040C8BC /* RunnerUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F76AC1B72666D0610040C8BC /* Build configuration list for PBXNativeTarget "RunnerUITests" */; + buildPhases = ( + F76AC1AC2666D0610040C8BC /* Sources */, + F76AC1AD2666D0610040C8BC /* Frameworks */, + F76AC1AE2666D0610040C8BC /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + F76AC1B62666D0610040C8BC /* PBXTargetDependency */, + ); + name = RunnerUITests; + productName = RunnerUITests; + productReference = F76AC1B02666D0610040C8BC /* RunnerUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = "The Flutter Authors"; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + }; + F76AC1A12666D0540040C8BC = { + CreatedOnToolsVersion = 12.5; + ProvisioningStyle = Automatic; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + F76AC1AF2666D0610040C8BC = { + CreatedOnToolsVersion = 12.5; + ProvisioningStyle = Automatic; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + F76AC1A12666D0540040C8BC /* RunnerTests */, + F76AC1AF2666D0610040C8BC /* RunnerUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7A303C2E1E89D76400B1F19E /* GoogleService-Info.plist in Resources */, + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 7ACDFB0E1E8944C400BE2D00 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F76AC1A02666D0540040C8BC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F76AC1AE2666D0610040C8BC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 27975964E48117AA65B1D6C7 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed\n/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin\n"; + }; + 532EA9D341340B1DCD08293D /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", + "${PODS_ROOT}/GoogleSignIn/Resources/GoogleSignIn.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleSignIn.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + 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"; + }; + AB1344B0443C71CD721E1BB7 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 5C6F5A6E1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F76AC19E2666D0540040C8BC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F76AC1A52666D0540040C8BC /* GoogleSignInTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F76AC1AC2666D0610040C8BC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F76AC1B32666D0610040C8BC /* GoogleSignInUITests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + F76AC1A82666D0540040C8BC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = F76AC1A72666D0540040C8BC /* PBXContainerItemProxy */; + }; + F76AC1B62666D0610040C8BC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = F76AC1B52666D0610040C8BC /* 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 */ + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = 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; + 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 = 9.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; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = 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; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.googleSignInExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.googleSignInExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + F76AC1A92666D0540040C8BC /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 37E582FF620A90D0EB2C0851 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Debug; + }; + F76AC1AA2666D0540040C8BC /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 45D93D4513839BFEA2AA74FE /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Release; + }; + F76AC1B82666D0610040C8BC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = RunnerUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = Runner; + }; + name = Debug; + }; + F76AC1B92666D0610040C8BC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = RunnerUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = Runner; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F76AC1AB2666D0540040C8BC /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F76AC1A92666D0540040C8BC /* Debug */, + F76AC1AA2666D0540040C8BC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F76AC1B72666D0610040C8BC /* Build configuration list for PBXNativeTarget "RunnerUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F76AC1B82666D0610040C8BC /* Debug */, + F76AC1B92666D0610040C8BC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..919434a6254f --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..f4569c48ce10 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/AppDelegate.h b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/AppDelegate.h new file mode 100644 index 000000000000..0681d288bb70 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/AppDelegate.h @@ -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 +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/AppDelegate.m b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/AppDelegate.m new file mode 100644 index 000000000000..30b87969f44a --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/AppDelegate.m @@ -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. + +#include "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + // Override point for customization after application launch. + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..d22f10b2ab63 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "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" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..28c6bf03016f6c994b70f38d1b7346e5831b531f GIT binary patch literal 564 zcmV-40?Yl0P)Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV7ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 zcmds$`#;kQ7{|XelZftyR5~xW7?MLxS4^|Hw3&P7^y)@A9Fj{Xm1~_CIV^XZ%SLBn zA;!r`GqGHg=7>xrB{?psZQs88ZaedDoagm^KF{a*>G|dJWRSe^I$DNW008I^+;Kjt z>9p3GNR^I;v>5_`+91i(*G;u5|L+Bu6M=(afLjtkya#yZ175|z$pU~>2#^Z_pCZ7o z1c6UNcv2B3?; zX%qdxCXQpdKRz=#b*q0P%b&o)5ZrNZt7$fiETSK_VaY=mb4GK`#~0K#~9^ zcY!`#Af+4h?UMR-gMKOmpuYeN5P*RKF!(tb`)oe0j2BH1l?=>y#S5pMqkx6i{*=V9JF%>N8`ewGhRE(|WohnD59R^$_36{4>S zDFlPC5|k?;SPsDo87!B{6*7eqmMdU|QZ84>6)Kd9wNfh90=y=TFQay-0__>=<4pk& zYDjgIhL-jQ9o>z32K)BgAH+HxamL{ZL~ozu)Qqe@a`FpH=oQRA8=L-m-1dam(Ix2V z?du;LdMO+ooBelr^_y4{|44tmgH^2hSzPFd;U^!1p>6d|o)(-01z{i&Kj@)z-yfWQ)V#3Uo!_U}q3u`(fOs`_f^ueFii1xBNUB z6MecwJN$CqV&vhc+)b(p4NzGGEgwWNs z@*lUV6LaduZH)4_g!cE<2G6#+hJrWd5(|p1Z;YJ7ifVHv+n49btR}dq?HHDjl{m$T z!jLZcGkb&XS2OG~u%&R$(X+Z`CWec%QKt>NGYvd5g20)PU(dOn^7%@6kQb}C(%=vr z{?RP(z~C9DPnL{q^@pVw@|Vx~@3v!9dCaBtbh2EdtoNHm4kGxp>i#ct)7p|$QJs+U z-a3qtcPvhihub?wnJqEt>zC@)2suY?%-96cYCm$Q8R%-8$PZYsx3~QOLMDf(piXMm zB=<63yQk1AdOz#-qsEDX>>c)EES%$owHKue;?B3)8aRd}m~_)>SL3h2(9X;|+2#7X z+#2)NpD%qJvCQ0a-uzZLmz*ms+l*N}w)3LRQ*6>|Ub-fyptY(keUxw+)jfwF5K{L9 z|Cl_w=`!l_o><384d&?)$6Nh(GAm=4p_;{qVn#hI8lqewW7~wUlyBM-4Z|)cZr?Rh z=xZ&Ol>4(CU85ea(CZ^aO@2N18K>ftl8>2MqetAR53_JA>Fal`^)1Y--Am~UDa4th zKfCYpcXky$XSFDWBMIl(q=Mxj$iMBX=|j9P)^fDmF(5(5$|?Cx}DKEJa&XZP%OyE`*GvvYQ4PV&!g2|L^Q z?YG}tx;sY@GzMmsY`7r$P+F_YLz)(e}% zyakqFB<6|x9R#TdoP{R$>o7y(-`$$p0NxJ6?2B8tH)4^yF(WhqGZlM3=9Ibs$%U1w zWzcss*_c0=v_+^bfb`kBFsI`d;ElwiU%frgRB%qBjn@!0U2zZehBn|{%uNIKBA7n= zzE`nnwTP85{g;8AkYxA68>#muXa!G>xH22D1I*SiD~7C?7Za+9y7j1SHiuSkKK*^O zsZ==KO(Ua#?YUpXl{ViynyT#Hzk=}5X$e04O@fsMQjb}EMuPWFO0e&8(2N(29$@Vd zn1h8Yd>6z(*p^E{c(L0Lg=wVdupg!z@WG;E0k|4a%s7Up5C0c)55XVK*|x9RQeZ1J@1v9MX;>n34(i>=YE@Iur`0Vah(inE3VUFZNqf~tSz{1fz3Fsn_x4F>o(Yo;kpqvBe-sbwH(*Y zu$JOl0b83zu$JMvy<#oH^Wl>aWL*?aDwnS0iEAwC?DK@aT)GHRLhnz2WCvf3Ba;o=aY7 z2{Asu5MEjGOY4O#Ggz@@J;q*0`kd2n8I3BeNuMmYZf{}pg=jTdTCrIIYuW~luKecn z+E-pHY%ohj@uS0%^ z&(OxwPFPD$+#~`H?fMvi9geVLci(`K?Kj|w{rZ9JgthFHV+=6vMbK~0)Ea<&WY-NC zy-PnZft_k2tfeQ*SuC=nUj4H%SQ&Y$gbH4#2sT0cU0SdFs=*W*4hKGpuR1{)mV;Qf5pw4? zfiQgy0w3fC*w&Bj#{&=7033qFR*<*61B4f9K%CQvxEn&bsWJ{&winp;FP!KBj=(P6 z4Z_n4L7cS;ao2)ax?Tm|I1pH|uLpDSRVghkA_UtFFuZ0b2#>!8;>-_0ELjQSD-DRd z4im;599VHDZYtnWZGAB25W-e(2VrzEh|etsv2YoP#VbIZ{aFkwPrzJ#JvCvA*mXS& z`}Q^v9(W4GiSs}#s7BaN!WA2bniM$0J(#;MR>uIJ^uvgD3GS^%*ikdW6-!VFUU?JV zZc2)4cMsX@j z5HQ^e3BUzOdm}yC-xA%SY``k$rbfk z;CHqifhU*jfGM@DkYCecD9vl*qr58l6x<8URB=&%{!Cu3RO*MrKZ4VO}V6R0a zZw3Eg^0iKWM1dcTYZ0>N899=r6?+adUiBKPciJw}L$=1f4cs^bio&cr9baLF>6#BM z(F}EXe-`F=f_@`A7+Q&|QaZ??Txp_dB#lg!NH=t3$G8&06MFhwR=Iu*Im0s_b2B@| znW>X}sy~m#EW)&6E&!*0%}8UAS)wjt+A(io#wGI@Z2S+Ms1Cxl%YVE800007ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 zcmVjJBgitF5mAp-i>4+KS_oR{|13AP->1TD4=w)g|)JHOx|a2Wk1Va z!k)vP$UcQ#mdj%wNQoaJ!w>jv_6&JPyutpQps?s5dmDQ>`%?Bvj>o<%kYG!YW6H-z zu`g$@mp`;qDR!51QaS}|ZToSuAGcJ7$2HF0z`ln4t!#Yg46>;vGG9N9{V@9z#}6v* zfP?}r6b{*-C*)(S>NECI_E~{QYzN5SXRmVnP<=gzP+_Sp(Aza_hKlZ{C1D&l*(7IKXxQC1Z9#6wx}YrGcn~g%;icdw>T0Rf^w0{ z$_wn1J+C0@!jCV<%Go5LA45e{5gY9PvZp8uM$=1}XDI+9m7!A95L>q>>oe0$nC->i zeexUIvq%Uk<-$>DiDb?!In)lAmtuMWxvWlk`2>4lNuhSsjAf2*2tjT`y;@d}($o)S zn(+W&hJ1p0xy@oxP%AM15->wPLp{H!k)BdBD$toBpJh+crWdsNV)qsHaqLg2_s|Ih z`8E9z{E3sA!}5aKu?T!#enD(wLw?IT?k-yWVHZ8Akz4k5(TZJN^zZgm&zM28sfTD2BYJ|Fde3Xzh;;S` z=GXTnY4Xc)8nYoz6&vF;P7{xRF-{|2Xs5>a5)@BrnQ}I(_x7Cgpx#5&Td^4Q9_FnQ zX5so*;#8-J8#c$OlA&JyPp$LKUhC~-e~Ij!L%uSMu!-VZG7Hx-L{m2DVR2i=GR(_% zCVD!4N`I)&Q5S`?P&fQZ=4#Dgt_v2-DzkT}K(9gF0L(owe-Id$Rc2qZVLqI_M_DyO z9@LC#U28_LU{;wGZ&))}0R2P4MhajKCd^K#D+JJ&JIXZ_p#@+7J9A&P<0kdRujtQ_ zOy>3=C$kgi6$0pW06KaLz!21oOryKM3ZUOWqppndxfH}QpgjEJ`j7Tzn5bk6K&@RA?vl##y z$?V~1E(!wB5rH`>3nc&@)|#<1dN2cMzzm=PGhQ|Yppne(C-Vlt450IXc`J4R0W@I7 zd1e5uW6juvO%ni(WX7BsKx3MLngO7rHO;^R5I~0^nE^9^E_eYLgiR9&KnJ)pBbfno zSVnW$0R+&6jOOsZ82}nJ126+c|%svPo;TeUku<2G7%?$oft zyaO;tVo}(W)VsTUhq^XmFi#2z%-W9a{7mXn{uzivYQ_d6b7VJG{77naW(vHt-uhnY zVN#d!JTqVh(7r-lhtXVU6o})aZbDt_;&wJVGl2FKYFBFpU-#9U)z#(A%=IVnqytR$SY-sO( z($oNE09{D^@OuYPz&w~?9>Fl5`g9u&ecFGhqX=^#fmR=we0CJw+5xna*@oHnkahk+ z9aWeE3v|An+O5%?4fA&$Fgu~H_YmqR!yIU!bFCk4!#pAj%(lI(A5n)n@Id#M)O9Yx zJU9oKy{sRAIV3=5>(s8n{8ryJ!;ho}%pn6hZKTKbqk=&m=f*UnK$zW3YQP*)pw$O* zIfLA^!-bmBl6%d_n$#tP8Zd_(XdA*z*WH|E_yILwjtI~;jK#v-6jMl^?<%Y%`gvpwv&cFb$||^v4D&V=aNy?NGo620jL3VZnA%s zH~I|qPzB~e(;p;b^gJr7Ure#7?8%F0m4vzzPy^^(q4q1OdthF}Fi*RmVZN1OwTsAP zn9CZP`FazX3^kG(KodIZ=Kty8DLTy--UKfa1$6XugS zk%6v$Kmxt6U!YMx0JQ)0qX*{CXwZZk$vEROidEc7=J-1;peNat!vS<3P-FT5po>iE z!l3R+<`#x|+_hw!HjQGV=8!q|76y8L7N8gP3$%0kfush|u0uU^?dKBaeRSBUpOZ0c z62;D&Mdn2}N}xHRFTRI?zRv=>=AjHgH}`2k4WK=#AHB)UFrR-J87GgX*x5fL^W2#d z=(%K8-oZfMO=i{aWRDg=FX}UubM4eotRDcn;OR#{3q=*?3mE3_oJ-~prjhxh%PgQT zyn)Qozaq0@o&|LEgS{Ind4Swsr;b`u185hZPOBLL<`d2%^Yp1?oL)=jnLi;Zo0ZDliTtQ^b5SmfIMe{T==zZkbvn$KTQGlbG8w}s@M3TZnde;1Am46P3juKb zl9GU&3F=q`>j!`?SyH#r@O59%@aMX^rx}Nxe<>NqpUp5=lX1ojGDIR*-D^SDuvCKF z?3$xG(gVUsBERef_YjPFl^rU9EtD{pt z0CXwpN7BN3!8>hajGaTVk-wl=9rxmfWtIhC{mheHgStLi^+Nz12a?4r(fz)?3A%at zMlvQmL<2-R)-@G1wJ0^zQK%mR=r4d{Y3fHp){nWXUL#|CqXl(+v+qDh>FkF9`eWrW zfr^D%LNfOcTNvtx0JXR35J0~Jpi2#P3Q&80w+nqNfc}&G0A~*)lGHKv=^FE+b(37|)zL;KLF>oiGfb(?&1 zV3XRu!Sw>@quKiab%g6jun#oZ%!>V#A%+lNc?q>6+VvyAn=kf_6z^(TZUa4Eelh{{ zqFX-#dY(EV@7l$NE&kv9u9BR8&Ojd#ZGJ6l8_BW}^r?DIS_rU2(XaGOK z225E@kH5Opf+CgD^{y29jD4gHbGf{1MD6ggQ&%>UG4WyPh5q_tb`{@_34B?xfSO*| zZv8!)q;^o-bz`MuxXk*G^}(6)ACb@=Lfs`Hxoh>`Y0NE8QRQ!*p|SH@{r8=%RKd4p z+#Ty^-0kb=-H-O`nAA3_6>2z(D=~Tbs(n8LHxD0`R0_ATFqp-SdY3(bZ3;VUM?J=O zKCNsxsgt@|&nKMC=*+ZqmLHhX1KHbAJs{nGVMs6~TiF%Q)P@>!koa$%oS zjXa=!5>P`vC-a}ln!uH1ooeI&v?=?v7?1n~P(wZ~0>xWxd_Aw;+}9#eULM7M8&E?Y zC-ZLhi3RoM92SXUb-5i-Lmt5_rfjE{6y^+24`y$1lywLyHO!)Boa7438K4#iLe?rh z2O~YGSgFUBH?og*6=r9rme=peP~ah`(8Zt7V)j5!V0KPFf_mebo3z95U8(up$-+EA^9dTRLq>Yl)YMBuch9%=e5B`Vnb>o zt03=kq;k2TgGe4|lGne&zJa~h(UGutjP_zr?a7~#b)@15XNA>Dj(m=gg2Q5V4-$)D|Q9}R#002ovPDHLkV1o7DH3k3x literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 zcmV-m2cP(fP)x~L`~4d)Rspd&<9kFh{hn*KP1LP0~$;u(LfAu zp%fx&qLBcRHx$G|3q(bv@+b;o0*D|jwD-Q9uQR(l*ST}s+uPgQ-MeFwZ#GS?b332? z&Tk$&_miXn3IGq)AmQ)3sisq{raD4(k*bHvpCe-TdWq^NRTEVM)i9xbgQ&ccnUVx* zEY%vS%gDcSg=!tuIK8$Th2_((_h^+7;R|G{n06&O2#6%LK`a}n?h_fL18btz<@lFG za}xS}u?#DBMB> zw^b($1Z)`9G?eP95EKi&$eOy@K%h;ryrR3la%;>|o*>CgB(s>dDcNOXg}CK9SPmD? zmr-s{0wRmxUnbDrYfRvnZ@d z6johZ2sMX{YkGSKWd}m|@V7`Degt-43=2M?+jR%8{(H$&MLLmS;-|JxnX2pnz;el1jsvqQz}pGSF<`mqEXRQ5sC4#BbwnB_4` zc5bFE-Gb#JV3tox9fp-vVEN{(tOCpRse`S+@)?%pz+zVJXSooTrNCUg`R6`hxwb{) zC@{O6MKY8tfZ5@!yy=p5Y|#+myRL=^{tc(6YgAnkg3I(Cd!r5l;|;l-MQ8B`;*SCE z{u)uP^C$lOPM z5d~UhKhRRmvv{LIa^|oavk1$QiEApSrP@~Jjbg`<*dW4TO?4qG%a%sTPUFz(QtW5( zM)lA+5)0TvH~aBaOAs|}?u2FO;yc-CZ1gNM1dAxJ?%m?YsGR`}-xk2*dxC}r5j$d* zE!#Vtbo69h>V4V`BL%_&$} z+oJAo@jQ^Tk`;%xw-4G>hhb&)B?##U+(6Fi7nno`C<|#PVA%$Y{}N-?(Gc$1%tr4Pc}}hm~yY#fTOe!@v9s-ik$dX~|ygArPhByaXn8 zpI^FUjNWMsTFKTP3X7m?UK)3m zp6rI^_zxRYrx6_QmhoWoDR`fp4R7gu6;gdO)!KexaoO2D88F9x#TM1(9Bn7g;|?|o z)~$n&Lh#hCP6_LOPD>a)NmhW})LADx2kq=X7}7wYRj-0?dXr&bHaRWCfSqvzFa=sn z-8^gSyn-RmH=BZ{AJZ~!8n5621GbUJV7Qvs%JNv&$%Q17s_X%s-41vAPfIR>;x0Wlqr5?09S>x#%Qkt>?(&XjFRY}*L6BeQ3 z<6XEBh^S7>AbwGm@XP{RkeEKj6@_o%oV?hDuUpUJ+r#JZO?!IUc;r0R?>mi)*ZpQ) z#((dn=A#i_&EQn|hd)N$#A*fjBFuiHcYvo?@y1 z5|fV=a^a~d!c-%ZbMNqkMKiSzM{Yq=7_c&1H!mXk60Uv32dV;vMg&-kQ)Q{+PFtwc zj|-uQ;b^gts??J*9VxxOro}W~Q9j4Em|zSRv)(WSO9$F$s=Ydu%Q+5DOid~lwk&we zY%W(Z@ofdwPHncEZzZgmqS|!gTj3wQq9rxQy+^eNYKr1mj&?tm@wkO*9@UtnRMG>c aR{jt9+;fr}hV%pg00001^@s67{VYS000c7NklQEG_j zup^)eW&WUIApqy$=APz8jE@awGp)!bsTjDbrJO`$x^ZR^dr;>)LW>{ zs70vpsD38v)19rI=GNk1b(0?Js9~rjsQsu*K;@SD40RB-3^gKU-MYC7G!Bw{fZsqp zih4iIi;Hr_xZ033Iu{sQxLS=}yBXgLMn40d++>aQ0#%8D1EbGZp7+ z5=mK?t31BkVYbGOxE9`i748x`YgCMwL$qMsChbSGSE1`p{nSmadR zcQ#R)(?!~dmtD0+D2!K zR9%!Xp1oOJzm(vbLvT^$IKp@+W2=-}qTzTgVtQ!#Y7Gxz}stUIm<1;oBQ^Sh2X{F4ibaOOx;5ZGSNK z0maF^@(UtV$=p6DXLgRURwF95C=|U8?osGhgOED*b z7woJ_PWXBD>V-NjQAm{~T%sjyJ{5tn2f{G%?J!KRSrrGvQ1(^`YLA5B!~eycY(e5_ z*%aa{at13SxC(=7JT7$IQF~R3sy`Nn%EMv!$-8ZEAryB*yB1k&stni)=)8-ODo41g zkJu~roIgAih94tb=YsL%iH5@^b~kU9M-=aqgXIrbtxMpFy5mekFm#edF9z7RQ6V}R zBIhbXs~pMzt0VWy1Fi$^fh+1xxLDoK09&5&MJl(q#THjPm(0=z2H2Yfm^a&E)V+a5 zbi>08u;bJsDRUKR9(INSc7XyuWv(JsD+BB*0hS)FO&l&7MdViuur@-<-EHw>kHRGY zqoT}3fDv2-m{NhBG8X}+rgOEZ;amh*DqN?jEfQdqxdj08`Sr=C-KmT)qU1 z+9Cl)a1mgXxhQiHVB}l`m;-RpmKy?0*|yl?FXvJkFxuu!fKlcmz$kN(a}i*saM3nr z0!;a~_%Xqy24IxA2rz<+08=B-Q|2PT)O4;EaxP^6qixOv7-cRh?*T?zZU`{nIM-at zTKYWr9rJ=tppQ9I#Z#mLgINVB!pO-^FOcvFw6NhV0gztuO?g ztoA*C-52Q-Z-P#xB4HAY3KQVd%dz1S4PA3vHp0aa=zAO?FCt zC_GaTyVBg2F!bBr3U@Zy2iJgIAt>1sf$JWA9kh{;L+P*HfUBX1Zy{4MgNbDfBV_ly z!y#+753arsZUt@366jIC0klaC@ckuk!qu=pAyf7&QmiBUT^L1&tOHzsK)4n|pmrVT zs2($4=?s~VejTFHbFdDOwG;_58LkIj1Fh@{glkO#F1>a==ymJS$z;gdedT1zPx4Kj ztjS`y_C}%af-RtpehdQDt3a<=W5C4$)9W@QAse;WUry$WYmr51ml9lkeunUrE`-3e zmq1SgSOPNEE-Mf+AGJ$g0M;3@w!$Ej;hMh=v=I+Lpz^n%Pg^MgwyqOkNyu2c^of)C z1~ALor3}}+RiF*K4+4{(1%1j3pif1>sv0r^mTZ?5Jd-It!tfPfiG_p$AY*Vfak%FG z4z#;wLtw&E&?}w+eKG^=#jF7HQzr8rV0mY<1YAJ_uGz~$E13p?F^fPSzXSn$8UcI$ z8er9{5w5iv0qf8%70zV71T1IBB1N}R5Kp%NO0=5wJalZt8;xYp;b{1K) zHY>2wW-`Sl{=NpR%iu3(u6l&)rc%%cSA#aV7WCowfbFR4wcc{LQZv~o1u_`}EJA3>ki`?9CKYTA!rhO)if*zRdd}Kn zEPfYbhoVE~!FI_2YbC5qAj1kq;xP6%J8+?2PAs?`V3}nyFVD#sV3+uP`pi}{$l9U^ zSz}_M9f7RgnnRhaoIJgT8us!1aB&4!*vYF07Hp&}L zCRlop0oK4DL@ISz{2_BPlezc;xj2|I z23RlDNpi9LgTG_#(w%cMaS)%N`e>~1&a3<{Xy}>?WbF>OOLuO+j&hc^YohQ$4F&ze z+hwnro1puQjnKm;vFG~o>`kCeUIlkA-2tI?WBKCFLMBY=J{hpSsQ=PDtU$=duS_hq zHpymHt^uuV1q@uc4bFb{MdG*|VoW@15Osrqt2@8ll0qO=j*uOXn{M0UJX#SUztui9FN4)K3{9!y8PC-AHHvpVTU;x|-7P+taAtyglk#rjlH2 z5Gq8ik}BPaGiM{#Woyg;*&N9R2{J0V+WGB69cEtH7F?U~Kbi6ksi*`CFXsi931q7Y zGO82?whBhN%w1iDetv%~wM*Y;E^)@Vl?VDj-f*RX>{;o_=$fU!&KAXbuadYZ46Zbg z&6jMF=49$uL^73y;;N5jaHYv)BTyfh&`qVLYn?`o6BCA_z-0niZz=qPG!vonK3MW_ zo$V96zM!+kJRs{P-5-rQVse0VBH*n6A58)4uc&gfHMa{gIhV2fGf{st>E8sKyP-$8zp~wJX^A*@DI&-;8>gANXZj zU)R+Y)PB?=)a|Kj>8NXEu^S_h^7R`~Q&7*Kn!xyvzVv&^>?^iu;S~R2e-2fJx-oUb cX)(b1KSk$MOV07*qoM6N<$f&6$jw%VRuvdN2+38CZWny1cRtlsl+0_KtW)EU14Ei(F!UtWuj4IK+3{sK@>rh zs1Z;=(DD&U6+tlyL?UnHVN^&g6QhFi2#HS+*qz;(>63G(`|jRtW|nz$Pv7qTovP!^ zP_jES{mr@O-02w%!^a?^1ZP!_KmQiz0L~jZ=W@Qt`8wzOoclQsAS<5YdH;a(4bGLE zk8s}1If(PSIgVi!XE!5kA?~z*sobvNyohr;=Q_@h2@$6Flyej3J)D-6YfheRGl`HEcPk|~huT_2-U?PfL=4BPV)f1o!%rQ!NMt_MYw-5bUSwQ9Z&zC>u zOrl~UJglJNa%f50Ok}?WB{on`Ci`p^Y!xBA?m@rcJXLxtrE0FhRF3d*ir>yzO|BD$ z3V}HpFcCh6bTzY}Nt_(W%QYd3NG)jJ4<`F<1Od) zfQblTdC&h2lCz`>y?>|9o2CdvC8qZeIZt%jN;B7Hdn2l*k4M4MFEtq`q_#5?}c$b$pf_3y{Y!cRDafZBEj-*OD|gz#PBDeu3QoueOesLzB+O zxjf2wvf6Wwz>@AiOo2mO4=TkAV+g~%_n&R;)l#!cBxjuoD$aS-`IIJv7cdX%2{WT7 zOm%5rs(wqyPE^k5SIpUZ!&Lq4<~%{*>_Hu$2|~Xa;iX*tz8~G6O3uFOS?+)tWtdi| zV2b#;zRN!m@H&jd=!$7YY6_}|=!IU@=SjvGDFtL;aCtw06U;-v^0%k0FOyESt z1Wv$={b_H&8FiRV?MrzoHWd>%v6KTRU;-v^Miiz+@q`(BoT!+<37CKhoKb)|8!+RG z6BQFU^@fRW;s8!mOf2QViKQGk0TVER6EG1`#;Nm39Do^PoT!+<37AD!%oJe86(=et zZ~|sLzU>V-qYiU6V8$0GmU7_K8|Fd0B?+9Un1BhKAz#V~Fk^`mJtlCX#{^8^M8!me z8Yg;8-~>!e<-iG;h*0B1kBKm}hItVGY6WnjVpgnTTAC$rqQ^v)4KvOtpY|sIj@WYg zyw##ZZ5AC2IKNC;^hwg9BPk0wLStlmBr;E|$5GoAo$&Ui_;S9WY62n3)i49|T%C#i017z3J=$RF|KyZWnci*@lW4 z=AKhNN6+m`Q!V3Ye68|8y@%=am>YD0nG99M)NWc20%)gwO!96j7muR}Fr&54SxKP2 zP30S~lt=a*qDlbu3+Av57=9v&vr<6g0&`!8E2fq>I|EJGKs}t|{h7+KT@)LfIV-3K zK)r_fr2?}FFyn*MYoLC>oV-J~eavL2ho4a4^r{E-8m2hi>~hA?_vIG4a*KT;2eyl1 zh_hUvUJpNCFwBvRq5BI*srSle>c6%n`#VNsyC|MGa{(P&08p=C9+WUw9Hl<1o9T4M zdD=_C0F7#o8A_bRR?sFNmU0R6tW`ElnF8p53IdHo#S9(JoZCz}fHwJ6F<&?qrpVqE zte|m%89JQD+XwaPU#%#lVs-@-OL);|MdfINd6!XwP2h(eyafTUsoRkA%&@fe?9m@jw-v(yTTiV2(*fthQH9}SqmsRPVnwwbV$1E(_lkmo&S zF-truCU914_$jpqjr(>Ha4HkM4YMT>m~NosUu&UZ>zirfHo%N6PPs9^_o$WqPA0#5 z%tG>qFCL+b*0s?sZ;Sht0nE7Kl>OVXy=gjWxxK;OJ3yGd7-pZf7JYNcZo2*1SF`u6 zHJyRRxGw9mDlOiXqVMsNe#WX`fC`vrtjSQ%KmLcl(lC>ZOQzG^%iql2w-f_K@r?OE zwCICifM#L-HJyc7Gm>Ern?+Sk3&|Khmu4(~3qa$(m6Ub^U0E5RHq49za|XklN#?kP zl;EstdW?(_4D>kwjWy2f!LM)y?F94kyU3`W!6+AyId-89v}sXJpuic^NLL7GJItl~ zsiuB98AI-(#Mnm|=A-R6&2fwJ0JVSY#Q>&3$zFh|@;#%0qeF=j5Ajq@4i0tIIW z&}sk$&fGwoJpe&u-JeGLi^r?dO`m=y(QO{@h zQqAC7$rvz&5+mo3IqE?h=a~6m>%r5Quapvzq;{y~p zJpyXOBgD9VrW7@#p6l7O?o3feml(DtSL>D^R) zZUY%T2b0-vBAFN7VB;M88!~HuOXi4KcI6aRQ&h|XQ0A?m%j2=l1f0cGP}h(oVfJ`N zz#PpmFC*ieab)zJK<4?^k=g%OjPnkANzbAbmGZHoVRk*mTfm75s_cWVa`l*f$B@xu z5E*?&@seIo#*Y~1rBm!7sF9~~u6Wrj5oICUOuz}CS)jdNIznfzCA(stJ(7$c^e5wN z?lt>eYgbA!kvAR7zYSD&*r1$b|(@;9dcZ^67R0 zXAXJKa|5Sdmj!g578Nwt6d$sXuc&MWezA0Whd`94$h{{?1IwXP4)Tx4obDK%xoFZ_Z zjjHJ_P@R_e5blG@yEjnaJb`l;s%Lb2&=8$&Ct-fV`E^4CUs)=jTk!I}2d&n!f@)bm z@ z_4Dc86+3l2*p|~;o-Sb~oXb_RuLmoifDU^&Te$*FevycC0*nE3Xws8gsWp|Rj2>SM zns)qcYj?^2sd8?N!_w~4v+f-HCF|a$TNZDoNl$I1Uq87euoNgKb6&r26TNrfkUa@o zfdiFA@p{K&mH3b8i!lcoz)V{n8Q@g(vR4ns4r6w;K z>1~ecQR0-<^J|Ndg5fvVUM9g;lbu-){#ghGw(fg>L zh)T5Ljb%lWE;V9L!;Cqk>AV1(rULYF07ZBJbGb9qbSoLAd;in9{)95YqX$J43-dY7YU*k~vrM25 zxh5_IqO0LYZW%oxQ5HOzmk4x{atE*vipUk}sh88$b2tn?!ujEHn`tQLe&vo}nMb&{ zio`xzZ&GG6&ZyN3jnaQy#iVqXE9VT(3tWY$n-)uWDQ|tc{`?fq2F`oQ{;d3aWPg4Hp-(iE{ry>MIPWL> iW8 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Base.lproj/Main.storyboard b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/GoogleService-Info.plist b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/GoogleService-Info.plist new file mode 100644 index 000000000000..6042aab908af --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/GoogleService-Info.plist @@ -0,0 +1,44 @@ + + + + + AD_UNIT_ID_FOR_BANNER_TEST + ca-app-pub-3940256099942544/2934735716 + AD_UNIT_ID_FOR_INTERSTITIAL_TEST + ca-app-pub-3940256099942544/4411468910 + CLIENT_ID + 479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u + ANDROID_CLIENT_ID + 479882132969-jie8r1me6dsra60pal6ejaj8dgme3tg0.apps.googleusercontent.com + API_KEY + AIzaSyBECOwLTAN6PU4Aet1b2QLGIb3kRK8Xjew + GCM_SENDER_ID + 479882132969 + PLIST_VERSION + 1 + BUNDLE_ID + io.flutter.plugins.googleSignInExample + PROJECT_ID + my-flutter-proj + STORAGE_BUCKET + my-flutter-proj.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:479882132969:ios:2643f950e0a0da08 + DATABASE_URL + https://my-flutter-proj.firebaseio.com + SERVER_CLIENT_ID + YOUR_SERVER_CLIENT_ID + + \ No newline at end of file diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Info.plist b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Info.plist new file mode 100644 index 000000000000..187584d1cfd9 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/Info.plist @@ -0,0 +1,64 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Google Sign-In Example + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + GoogleSignInExample + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + com.googleusercontent.apps.479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + CADisableMinimumFrameDurationOnPhone + + + diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/main.m b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/main.m new file mode 100644 index 000000000000..f143297b30d6 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner/main.m @@ -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 +#import +#import "AppDelegate.h" + +int main(int argc, char *argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/packages/google_sign_in/google_sign_in/example/ios/RunnerTests/GoogleSignInTests.m b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m similarity index 99% rename from packages/google_sign_in/google_sign_in/example/ios/RunnerTests/GoogleSignInTests.m rename to packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m index 6f8b821a5299..dbbd65397ac5 100644 --- a/packages/google_sign_in/google_sign_in/example/ios/RunnerTests/GoogleSignInTests.m +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m @@ -5,8 +5,8 @@ @import Flutter; @import XCTest; -@import google_sign_in; -@import google_sign_in.Test; +@import google_sign_in_ios; +@import google_sign_in_ios.Test; @import GoogleSignIn; // OCMock library doesn't generate a valid modulemap. diff --git a/packages/google_sign_in/google_sign_in/example/ios/RunnerUITests/Info.plist b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/Info.plist similarity index 100% rename from packages/google_sign_in/google_sign_in/example/ios/RunnerUITests/Info.plist rename to packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/Info.plist diff --git a/packages/google_sign_in/google_sign_in/example/ios/RunnerUITests/GoogleSignInUITests.m b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerUITests/GoogleSignInUITests.m similarity index 100% rename from packages/google_sign_in/google_sign_in/example/ios/RunnerUITests/GoogleSignInUITests.m rename to packages/google_sign_in/google_sign_in_ios/example/ios/RunnerUITests/GoogleSignInUITests.m diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerUITests/Info.plist b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerUITests/Info.plist new file mode 100644 index 000000000000..64d65ca49577 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart b/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart new file mode 100644 index 000000000000..a750c330001d --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart @@ -0,0 +1,179 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; +import 'dart:convert' show json; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; +import 'package:http/http.dart' as http; + +void main() { + runApp( + MaterialApp( + title: 'Google Sign In', + home: SignInDemo(), + ), + ); +} + +class SignInDemo extends StatefulWidget { + @override + State createState() => SignInDemoState(); +} + +class SignInDemoState extends State { + GoogleSignInUserData? _currentUser; + String _contactText = ''; + // Future that completes when `init` has completed on the sign in instance. + Future? _initialization; + + @override + void initState() { + super.initState(); + _signIn(); + } + + Future _ensureInitialized() { + return _initialization ??= GoogleSignInPlatform.instance.init( + scopes: [ + 'email', + 'https://www.googleapis.com/auth/contacts.readonly', + ], + )..catchError((dynamic _) { + _initialization = null; + }); + } + + void _setUser(GoogleSignInUserData? user) { + setState(() { + _currentUser = user; + if (user != null) { + _handleGetContact(user); + } + }); + } + + Future _signIn() async { + await _ensureInitialized(); + final GoogleSignInUserData? newUser = + await GoogleSignInPlatform.instance.signInSilently(); + _setUser(newUser); + } + + Future> _getAuthHeaders() async { + final GoogleSignInUserData? user = _currentUser; + if (user == null) { + throw StateError('No user signed in'); + } + + final GoogleSignInTokenData response = + await GoogleSignInPlatform.instance.getTokens( + email: user.email, + shouldRecoverAuth: true, + ); + + return { + 'Authorization': 'Bearer ${response.accessToken}', + // TODO(kevmoo): Use the correct value once it's available. + // See https://github.com/flutter/flutter/issues/80905 + 'X-Goog-AuthUser': '0', + }; + } + + Future _handleGetContact(GoogleSignInUserData user) async { + setState(() { + _contactText = 'Loading contact info...'; + }); + final http.Response response = await http.get( + Uri.parse('https://people.googleapis.com/v1/people/me/connections' + '?requestMask.includeField=person.names'), + headers: await _getAuthHeaders(), + ); + if (response.statusCode != 200) { + setState(() { + _contactText = 'People API gave a ${response.statusCode} ' + 'response. Check logs for details.'; + }); + print('People API ${response.statusCode} response: ${response.body}'); + return; + } + final Map data = + json.decode(response.body) as Map; + final int contactCount = + (data['connections'] as List?)?.length ?? 0; + setState(() { + _contactText = '$contactCount contacts found'; + }); + } + + Future _handleSignIn() async { + try { + await _ensureInitialized(); + _setUser(await GoogleSignInPlatform.instance.signIn()); + } catch (error) { + final bool canceled = + error is PlatformException && error.code == 'sign_in_canceled'; + if (!canceled) { + print(error); + } + } + } + + Future _handleSignOut() async { + await _ensureInitialized(); + await GoogleSignInPlatform.instance.disconnect(); + } + + Widget _buildBody() { + final GoogleSignInUserData? user = _currentUser; + if (user != null) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + ListTile( + title: Text(user.displayName ?? ''), + subtitle: Text(user.email), + ), + const Text('Signed in successfully.'), + Text(_contactText), + ElevatedButton( + child: const Text('SIGN OUT'), + onPressed: _handleSignOut, + ), + ElevatedButton( + child: const Text('REFRESH'), + onPressed: () => _handleGetContact(user), + ), + ], + ); + } else { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + const Text('You are not currently signed in.'), + ElevatedButton( + child: const Text('SIGN IN'), + onPressed: _handleSignIn, + ), + ], + ); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Google Sign In'), + ), + body: ConstrainedBox( + constraints: const BoxConstraints.expand(), + child: _buildBody(), + )); + } +} diff --git a/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml new file mode 100644 index 000000000000..f2d32c521c1a --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml @@ -0,0 +1,29 @@ +name: google_sign_in_example +description: Example of Google Sign-In plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + google_sign_in_ios: + # When depending on this package from a real application you should use: + # google_sign_in_ios: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # 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_sign_in_platform_interface: ^2.1.0 + http: ^0.13.0 + +dev_dependencies: + flutter_driver: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/google_sign_in/google_sign_in_ios/example/test_driver/integration_test.dart b/packages/google_sign_in/google_sign_in_ios/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/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/google_sign_in/google_sign_in/ios/Assets/.gitkeep b/packages/google_sign_in/google_sign_in_ios/ios/Assets/.gitkeep similarity index 100% rename from packages/google_sign_in/google_sign_in/ios/Assets/.gitkeep rename to packages/google_sign_in/google_sign_in_ios/ios/Assets/.gitkeep diff --git a/packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin.h b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h similarity index 100% rename from packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin.h rename to packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.h diff --git a/packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m similarity index 100% rename from packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin.m rename to packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m diff --git a/packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin.modulemap b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.modulemap similarity index 55% rename from packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin.modulemap rename to packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.modulemap index 271f509e7fd7..31e30d93c582 100644 --- a/packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin.modulemap +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.modulemap @@ -1,5 +1,5 @@ -framework module google_sign_in { - umbrella header "google_sign_in-umbrella.h" +framework module google_sign_in_ios { + umbrella header "google_sign_in_ios-umbrella.h" export * module * { export * } diff --git a/packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin_Test.h b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin_Test.h similarity index 89% rename from packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin_Test.h rename to packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin_Test.h index 8fa6cf348018..f8d5be6f8522 100644 --- a/packages/google_sign_in/google_sign_in/ios/Classes/FLTGoogleSignInPlugin_Test.h +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin_Test.h @@ -4,7 +4,7 @@ // This header is available in the Test module. Import via "@import google_sign_in.Test;" -#import +#import @class GIDSignIn; diff --git a/packages/google_sign_in/google_sign_in/ios/Classes/google_sign_in-umbrella.h b/packages/google_sign_in/google_sign_in_ios/ios/Classes/google_sign_in_ios-umbrella.h similarity index 85% rename from packages/google_sign_in/google_sign_in/ios/Classes/google_sign_in-umbrella.h rename to packages/google_sign_in/google_sign_in_ios/ios/Classes/google_sign_in_ios-umbrella.h index 343c390f1782..23b7e992a5cd 100644 --- a/packages/google_sign_in/google_sign_in/ios/Classes/google_sign_in-umbrella.h +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/google_sign_in_ios-umbrella.h @@ -3,7 +3,7 @@ // found in the LICENSE file. #import -#import +#import FOUNDATION_EXPORT double google_sign_inVersionNumber; FOUNDATION_EXPORT const unsigned char google_sign_inVersionString[]; diff --git a/packages/google_sign_in/google_sign_in/ios/google_sign_in.podspec b/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec similarity index 90% rename from packages/google_sign_in/google_sign_in/ios/google_sign_in.podspec rename to packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec index 19ea753520a7..f583f6cffbf0 100644 --- a/packages/google_sign_in/google_sign_in/ios/google_sign_in.podspec +++ b/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec @@ -2,7 +2,7 @@ # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html # Pod::Spec.new do |s| - s.name = 'google_sign_in' + s.name = 'google_sign_in_ios' s.version = '0.0.1' s.summary = 'Google Sign-In plugin for Flutter' s.description = <<-DESC @@ -11,7 +11,7 @@ Enables Google Sign-In in Flutter apps. s.homepage = 'https://github.com/flutter/plugins/tree/main/packages/google_sign_in' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/google_sign_in' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/google_sign_in/google_sign_in_ios' } s.source_files = 'Classes/**/*.{h,m}' s.public_header_files = 'Classes/**/*.h' s.module_map = 'Classes/FLTGoogleSignInPlugin.modulemap' diff --git a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml new file mode 100644 index 000000000000..52c4f77feb4b --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml @@ -0,0 +1,35 @@ +name: google_sign_in_ios +description: Android implementation of the google_sign_in plugin. +repository: https://github.com/flutter/plugins/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.2.5 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.5.0" + +flutter: + plugin: + implements: google_sign_in + platforms: + ios: + pluginClass: FLTGoogleSignInPlugin + +dependencies: + flutter: + sdk: flutter + google_sign_in_platform_interface: ^2.1.0 + +dev_dependencies: + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + +# The example deliberately includes limited-use secrets. +false_secrets: + - /example/ios/Runner/GoogleService-Info.plist + - /example/ios/RunnerTests/GoogleSignInTests.m + - /example/lib/main.dart From f6229363f4e6e67eb5329bdb4a918d14fa8d5883 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 12 Apr 2022 15:04:12 -0400 Subject: [PATCH 126/844] Roll Flutter from 9e0f0fe9d7b8 to 0f2b1a3baf20 (4 revisions) (#5235) --- .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 25c6ddc070f4..b3b760fb2b46 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -9e0f0fe9d7b8fc42195c84c521bf5809c9dbbfe4 +0f2b1a3baf203586ad0580e9c74920cc2db4e2f7 From 52bd662fd4e8a59a3d2015b6ef928f22330ba09f Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 12 Apr 2022 12:51:56 -0700 Subject: [PATCH 127/844] [webview_flutter_wkwebview] Add support for cookie manager (#5203) --- .../lib/src/common/web_kit.pigeon.dart | 9 +- .../lib/src/foundation/foundation.dart | 89 ++++++++++++++++++- .../lib/src/web_kit/web_kit.dart | 74 +++++++++++++-- .../lib/src/web_kit/web_kit_api_impls.dart | 2 +- .../lib/src/web_kit_cookie_manager.dart | 54 +++++++++++ .../pigeons/web_kit.dart | 2 +- .../test/src/common/test_web_kit.pigeon.dart | 6 +- .../src/foundation/foundation_test.mocks.dart | 2 +- .../test/src/web_kit/web_kit_test.dart | 22 +++-- .../test/src/web_kit/web_kit_test.mocks.dart | 7 +- .../test/src/web_kit_cookie_manager_test.dart | 86 ++++++++++++++++++ .../web_kit_cookie_manager_test.mocks.dart | 59 ++++++++++++ .../test/src/web_kit_webview_widget_test.dart | 22 ++--- .../web_kit_webview_widget_test.mocks.dart | 30 ++++--- 14 files changed, 414 insertions(+), 50 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart index 22013d2ff0cc..5bf79a675be0 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart @@ -283,7 +283,7 @@ class WKWebsiteDataStoreHostApi { } } - Future removeDataOfTypes( + Future removeDataOfTypes( int arg_instanceId, List arg_dataTypes, double arg_secondsModifiedSinceEpoch) async { @@ -308,8 +308,13 @@ class WKWebsiteDataStoreHostApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { - return; + return (replyMap['result'] as bool?)!; } } } 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 8aa315aa10ee..1fef81d9dbaa 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 @@ -86,10 +86,85 @@ enum NSKeyValueChangeKey { /// Indicates the value of this key is the value before the attribute was changed. /// - /// https://developer.apple.com/documentation/foundation/nskeyvaluechangeoldkey?language=objc. + /// See https://developer.apple.com/documentation/foundation/nskeyvaluechangeoldkey?language=objc. oldValue, } +/// The supported keys in a cookie attributes dictionary. +/// +/// Wraps [NSHTTPCookiePropertyKey](https://developer.apple.com/documentation/foundation/nshttpcookiepropertykey). +enum NSHttpCookiePropertyKey { + /// A String object containing the comment for the cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookiecomment. + comment, + + /// A String object containing the comment URL for the cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookiecommenturl. + commentUrl, + + /// A String object stating whether the cookie should be discarded at the end of the session. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookiediscard. + discard, + + /// A String object specifying the expiration date for the cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookiedomain. + domain, + + /// A String object specifying the expiration date for the cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookieexpires. + expires, + + /// A String object containing an integer value stating how long in seconds the cookie should be kept, at most. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookiemaximumage. + maximumAge, + + /// A String object containing the name of the cookie (required). + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookiename. + name, + + /// A String object containing the URL that set this cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookieoriginurl. + originUrl, + + /// A String object containing the path for the cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookiepath. + path, + + /// A String object containing comma-separated integer values specifying the ports for the cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookieport. + port, + + /// A String indicating the same-site policy for the cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookiesamesitepolicy. + sameSitePolicy, + + /// A String object indicating that the cookie should be transmitted only over secure channels. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookiesecure. + secure, + + /// A String object containing the value of the cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookievalue. + value, + + /// A String object that specifies the version of the cookie. + /// + /// See https://developer.apple.com/documentation/foundation/nshttpcookieversion. + version, +} + /// A URL load request that is independent of protocol or URL scheme. /// /// Wraps [NSUrlRequest](https://developer.apple.com/documentation/foundation/nsurlrequest?language=objc). @@ -142,6 +217,18 @@ class NSError { final String localizedDescription; } +/// A representation of an HTTP cookie. +/// +/// Wraps [NSHTTPCookie](https://developer.apple.com/documentation/foundation/nshttpcookie). +@immutable +class NSHttpCookie { + /// Initializes an HTTP cookie object using the provided properties. + const NSHttpCookie.withProperties(this.properties); + + /// Properties of the new cookie object. + final Map properties; +} + /// The root class of most Objective-C class hierarchies. class NSObject { /// Constructs an [NSObject]. 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 0703ce1267d9..416e2626fc4a 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 @@ -126,6 +126,18 @@ class WKErrorCode { static const int javaScriptResultTypeIsUnsupported = 5; } +/// A record of the data that a particular website stores persistently. +/// +/// Wraps [WKWebsiteDataRecord](https://developer.apple.com/documentation/webkit/wkwebsitedatarecord?language=objc). +@immutable +class WKWebsiteDataRecord { + /// Constructs a [WKWebsiteDataRecord]. + const WKWebsiteDataRecord({required this.displayName}); + + /// Identifying information that you display to users. + final String displayName; +} + /// An object that contains information about an action that causes navigation to occur. /// /// Wraps [WKNavigationAction](https://developer.apple.com/documentation/webkit/wknavigationaction?language=objc). @@ -230,26 +242,51 @@ class WKPreferences { /// /// Wraps [WKWebsiteDataStore](https://developer.apple.com/documentation/webkit/wkwebsitedatastore?language=objc). class WKWebsiteDataStore { - /// Constructs a [WKWebsiteDataStore] that is owned by [configuration]. - @visibleForTesting - WKWebsiteDataStore.fromWebViewConfiguration( - WKWebViewConfiguration configuration, { + WKWebsiteDataStore._({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, }) : _websiteDataStoreApi = WKWebsiteDataStoreHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, - ) { - _websiteDataStoreApi.createFromWebViewConfigurationForInstances( - this, + ); + + factory WKWebsiteDataStore._defaultDataStore() { + throw UnimplementedError(); + } + + /// Constructs a [WKWebsiteDataStore] that is owned by [configuration]. + @visibleForTesting + factory WKWebsiteDataStore.fromWebViewConfiguration( + WKWebViewConfiguration configuration, { + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) { + final WKWebsiteDataStore websiteDataStore = WKWebsiteDataStore._( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + websiteDataStore._websiteDataStoreApi + .createFromWebViewConfigurationForInstances( + websiteDataStore, configuration, ); + return websiteDataStore; } + /// Default data store that stores data persistently to disk. + static final WKWebsiteDataStore defaultDataStore = + WKWebsiteDataStore._defaultDataStore(); + final WKWebsiteDataStoreHostApiImpl _websiteDataStoreApi; + /// Manages the HTTP cookies associated with a particular web view. + late final WKHttpCookieStore httpCookieStore = + WKHttpCookieStore.fromWebsiteDataStore(this); + /// Removes website data that changed after the specified date. - Future removeDataOfTypes( + /// + /// Returns whether any data was removed. + Future removeDataOfTypes( Set dataTypes, DateTime since, ) { @@ -261,6 +298,26 @@ class WKWebsiteDataStore { } } +/// An object that manages the HTTP cookies associated with a particular web view. +/// +/// Wraps [WKHTTPCookieStore](https://developer.apple.com/documentation/webkit/wkhttpcookiestore?language=objc). +class WKHttpCookieStore { + /// Constructs a [WKHttpCookieStore] that is owned by [dataStore]. + @visibleForTesting + WKHttpCookieStore.fromWebsiteDataStore( + // TODO(bparrishMines): Remove ignore on implementation. + // ignore: avoid_unused_constructor_parameters + WKWebsiteDataStore dataStore, + ) { + throw UnimplementedError(); + } + + /// Adds a cookie to the cookie store. + Future setCookie(NSHttpCookie cookie) { + throw UnimplementedError(); + } +} + /// An interface for receiving messages from JavaScript code running in a webpage. /// /// Wraps [WKScriptMessageHandler](https://developer.apple.com/documentation/webkit/wkscriptmessagehandler?language=objc) @@ -471,7 +528,6 @@ class WKWebViewConfiguration { Future setMediaTypesRequiringUserActionForPlayback( Set types, ) { - assert(types.isNotEmpty); return _webViewConfigurationApi .setMediaTypesRequiringUserActionForPlaybackForInstances( this, 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 188f65cbe177..1cda9d4b94c2 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 @@ -133,7 +133,7 @@ class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi { } /// Calls [removeDataOfTypes] with the ids of the provided object instances. - Future removeDataOfTypesForInstances( + Future removeDataOfTypesForInstances( WKWebsiteDataStore instance, Set dataTypes, { required double secondsModifiedSinceEpoch, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart new file mode 100644 index 000000000000..c73111d73fd0 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart @@ -0,0 +1,54 @@ +// 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:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; + +/// Handles all cookie operations for the WebView platform. +class WebKitCookieManager extends WebViewCookieManagerPlatform { + /// Constructs a [WebKitCookieManager]. + WebKitCookieManager({WKWebsiteDataStore? websiteDataStore}) + : websiteDataStore = + websiteDataStore ?? WKWebsiteDataStore.defaultDataStore; + + /// Manages stored data for [WKWebView]s. + final WKWebsiteDataStore websiteDataStore; + + @override + Future clearCookies() async { + return websiteDataStore.removeDataOfTypes( + {WKWebsiteDataTypes.cookies}, + DateTime.fromMillisecondsSinceEpoch(0), + ); + } + + @override + Future setCookie(WebViewCookie cookie) { + if (!_isValidPath(cookie.path)) { + throw ArgumentError( + 'The path property for the provided cookie was not given a legal value.'); + } + + return websiteDataStore.httpCookieStore.setCookie( + NSHttpCookie.withProperties( + { + NSHttpCookiePropertyKey.name: cookie.name, + NSHttpCookiePropertyKey.value: cookie.value, + NSHttpCookiePropertyKey.domain: cookie.domain, + NSHttpCookiePropertyKey.path: cookie.path, + }, + ), + ); + } + + bool _isValidPath(String path) { + // Permitted ranges based on RFC6265bis: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1 + return !path.codeUnits.any( + (int char) { + return (char < 0x20 || char > 0x3A) && (char < 0x3C || char > 0x7E); + }, + ); + } +} 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 0d5328dda8d6..370149e638ff 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -179,7 +179,7 @@ abstract class WKWebsiteDataStoreHostApi { ); @async - void removeDataOfTypes( + bool removeDataOfTypes( int instanceId, List dataTypes, double secondsModifiedSinceEpoch, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart index 78e4b9dcfd3d..0e369a351c75 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart @@ -44,7 +44,7 @@ abstract class TestWKWebsiteDataStoreHostApi { void createFromWebViewConfiguration( int instanceId, int configurationInstanceId); - Future removeDataOfTypes( + Future removeDataOfTypes( int instanceId, List dataTypes, double secondsModifiedSinceEpoch); @@ -96,9 +96,9 @@ abstract class TestWKWebsiteDataStoreHostApi { final double? arg_secondsModifiedSinceEpoch = (args[2] as double?); assert(arg_secondsModifiedSinceEpoch != null, 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes was null, expected non-null double.'); - await api.removeDataOfTypes( + final bool output = await api.removeDataOfTypes( arg_instanceId!, arg_dataTypes!, arg_secondsModifiedSinceEpoch!); - return {}; + return {'result': output}; }); } } 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 7bd208eeac05..b8552c5d8157 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 @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart. +// in webview_flutter_wkwebview/test/src/foundation/foundation_test.dart. // Do not manually edit this file. import 'package:mockito/mockito.dart' as _i1; 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 d51fa9793dd4..aa77157efcf7 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 @@ -70,19 +70,29 @@ void main() { }); test('removeDataOfTypes', () { - websiteDataStore.removeDataOfTypes( - {WKWebsiteDataTypes.cookies}, - DateTime.fromMillisecondsSinceEpoch(5000), + when(mockPlatformHostApi.removeDataOfTypes( + any, + any, + any, + )).thenAnswer((_) => Future.value(true)); + + expect( + websiteDataStore.removeDataOfTypes( + {WKWebsiteDataTypes.cookies}, + DateTime.fromMillisecondsSinceEpoch(5000), + ), + completion(true), ); - final WKWebsiteDataTypesEnumData typeData = + final List typeData = verify(mockPlatformHostApi.removeDataOfTypes( instanceManager.getInstanceId(websiteDataStore), captureAny, 5.0, - )).captured.single.single as WKWebsiteDataTypesEnumData; + )).captured.single.cast() + as List; - expect(typeData.value, WKWebsiteDataTypesEnum.cookies); + expect(typeData.single.value, WKWebsiteDataTypesEnum.cookies); }); }); 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 a2f16317f1fe..9629e2e62636 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 @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. // Do not manually edit this file. import 'dart:async' as _i4; @@ -275,13 +275,12 @@ class MockTestWKWebsiteDataStoreHostApi extends _i1.Mock [instanceId, configurationInstanceId]), returnValueForMissingStub: null); @override - _i4.Future removeDataOfTypes( + _i4.Future removeDataOfTypes( int? instanceId, List<_i3.WKWebsiteDataTypesEnumData?>? dataTypes, double? secondsModifiedSinceEpoch) => (super.noSuchMethod( Invocation.method(#removeDataOfTypes, [instanceId, dataTypes, secondsModifiedSinceEpoch]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValue: Future.value(false)) as _i4.Future); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart new file mode 100644 index 000000000000..4529316d3159 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart @@ -0,0 +1,86 @@ +// 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:async'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit_cookie_manager.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart'; + +import 'web_kit_cookie_manager_test.mocks.dart'; + +@GenerateMocks([ + WKHttpCookieStore, + WKWebsiteDataStore, +]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$WebKitWebViewWidget', () { + late MockWKWebsiteDataStore mockWebsiteDataStore; + late MockWKHttpCookieStore mockWKHttpCookieStore; + + late WebKitCookieManager cookieManager; + + setUp(() { + mockWebsiteDataStore = MockWKWebsiteDataStore(); + mockWKHttpCookieStore = MockWKHttpCookieStore(); + when(mockWebsiteDataStore.httpCookieStore) + .thenReturn(mockWKHttpCookieStore); + + cookieManager = + WebKitCookieManager(websiteDataStore: mockWebsiteDataStore); + }); + + test('clearCookies', () async { + when(mockWebsiteDataStore.removeDataOfTypes( + {WKWebsiteDataTypes.cookies}, any)) + .thenAnswer((_) => Future.value(true)); + expect(cookieManager.clearCookies(), completion(true)); + + when(mockWebsiteDataStore.removeDataOfTypes( + {WKWebsiteDataTypes.cookies}, any)) + .thenAnswer((_) => Future.value(false)); + expect(cookieManager.clearCookies(), completion(false)); + }); + + test('setCookie', () async { + await cookieManager.setCookie( + const WebViewCookie(name: 'a', value: 'b', domain: 'c', path: 'd'), + ); + + final NSHttpCookie cookie = + verify(mockWKHttpCookieStore.setCookie(captureAny)).captured.single + as NSHttpCookie; + expect( + cookie.properties, + { + NSHttpCookiePropertyKey.name: 'a', + NSHttpCookiePropertyKey.value: 'b', + NSHttpCookiePropertyKey.domain: 'c', + NSHttpCookiePropertyKey.path: 'd', + }, + ); + }); + + test('setCookie throws argument error with invalid path', () async { + expect( + () => cookieManager.setCookie( + WebViewCookie( + name: 'a', + value: 'b', + domain: 'c', + path: String.fromCharCode(0x1F), + ), + ), + throwsArgumentError, + ); + }); + }); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart new file mode 100644 index 000000000000..88025d3d9465 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart @@ -0,0 +1,59 @@ +// Mocks generated by Mockito 5.1.0 from annotations +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i3; + +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart' + as _i4; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.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: 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 + +class _FakeWKHttpCookieStore_0 extends _i1.Fake + implements _i2.WKHttpCookieStore {} + +/// A class which mocks [WKHttpCookieStore]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWKHttpCookieStore extends _i1.Mock implements _i2.WKHttpCookieStore { + MockWKHttpCookieStore() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future setCookie(_i4.NSHttpCookie? cookie) => + (super.noSuchMethod(Invocation.method(#setCookie, [cookie]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); +} + +/// A class which mocks [WKWebsiteDataStore]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWKWebsiteDataStore extends _i1.Mock + implements _i2.WKWebsiteDataStore { + MockWKWebsiteDataStore() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.WKHttpCookieStore get httpCookieStore => + (super.noSuchMethod(Invocation.getter(#httpCookieStore), + returnValue: _FakeWKHttpCookieStore_0()) as _i2.WKHttpCookieStore); + @override + _i3.Future removeDataOfTypes( + Set<_i2.WKWebsiteDataTypes>? dataTypes, DateTime? since) => + (super.noSuchMethod( + Invocation.method(#removeDataOfTypes, [dataTypes, since]), + returnValue: Future.value(false)) as _i3.Future); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 0c36f6b7de5a..86ee1a99b949 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -769,17 +769,19 @@ void main() { testWidgets('clearCache', (WidgetTester tester) async { await buildWidget(tester); + when( + mockWebsiteDataStore.removeDataOfTypes( + { + WKWebsiteDataTypes.memoryCache, + WKWebsiteDataTypes.diskCache, + WKWebsiteDataTypes.offlineWebApplicationCache, + WKWebsiteDataTypes.localStroage, + }, + DateTime.fromMillisecondsSinceEpoch(0), + ), + ).thenAnswer((_) => Future.value(false)); - await testController.clearCache(); - verify(mockWebsiteDataStore.removeDataOfTypes( - { - WKWebsiteDataTypes.memoryCache, - WKWebsiteDataTypes.diskCache, - WKWebsiteDataTypes.offlineWebApplicationCache, - WKWebsiteDataTypes.localStroage, - }, - DateTime.fromMillisecondsSinceEpoch(0), - )); + expect(testController.clearCache(), completes); }); testWidgets('addJavascriptChannels', (WidgetTester tester) async { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index 6c34e0c72004..bc79f656b6e3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. +// in webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. // Do not manually edit this file. import 'dart:async' as _i5; @@ -45,14 +45,17 @@ class _FakeWKPreferences_4 extends _i1.Fake implements _i3.WKPreferences {} class _FakeWKWebsiteDataStore_5 extends _i1.Fake implements _i3.WKWebsiteDataStore {} -class _FakeWKWebView_6 extends _i1.Fake implements _i3.WKWebView {} +class _FakeWKHttpCookieStore_6 extends _i1.Fake + implements _i3.WKHttpCookieStore {} -class _FakeWKScriptMessageHandler_7 extends _i1.Fake +class _FakeWKWebView_7 extends _i1.Fake implements _i3.WKWebView {} + +class _FakeWKScriptMessageHandler_8 extends _i1.Fake implements _i3.WKScriptMessageHandler {} -class _FakeWKUIDelegate_8 extends _i1.Fake implements _i3.WKUIDelegate {} +class _FakeWKUIDelegate_9 extends _i1.Fake implements _i3.WKUIDelegate {} -class _FakeWKNavigationDelegate_9 extends _i1.Fake +class _FakeWKNavigationDelegate_10 extends _i1.Fake implements _i3.WKNavigationDelegate {} /// A class which mocks [UIScrollView]. @@ -399,12 +402,15 @@ class MockWKWebsiteDataStore extends _i1.Mock } @override - _i5.Future removeDataOfTypes( + _i3.WKHttpCookieStore get httpCookieStore => + (super.noSuchMethod(Invocation.getter(#httpCookieStore), + returnValue: _FakeWKHttpCookieStore_6()) as _i3.WKHttpCookieStore); + @override + _i5.Future removeDataOfTypes( Set<_i3.WKWebsiteDataTypes>? dataTypes, DateTime? since) => (super.noSuchMethod( Invocation.method(#removeDataOfTypes, [dataTypes, since]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValue: Future.value(false)) as _i5.Future); } /// A class which mocks [WKUIDelegate]. @@ -534,18 +540,18 @@ class MockWebViewWidgetProxy extends _i1.Mock @override _i3.WKWebView createWebView(_i3.WKWebViewConfiguration? configuration) => (super.noSuchMethod(Invocation.method(#createWebView, [configuration]), - returnValue: _FakeWKWebView_6()) as _i3.WKWebView); + returnValue: _FakeWKWebView_7()) as _i3.WKWebView); @override _i3.WKScriptMessageHandler createScriptMessageHandler() => (super.noSuchMethod(Invocation.method(#createScriptMessageHandler, []), - returnValue: _FakeWKScriptMessageHandler_7()) + returnValue: _FakeWKScriptMessageHandler_8()) as _i3.WKScriptMessageHandler); @override _i3.WKUIDelegate createUIDelgate() => (super.noSuchMethod(Invocation.method(#createUIDelgate, []), - returnValue: _FakeWKUIDelegate_8()) as _i3.WKUIDelegate); + returnValue: _FakeWKUIDelegate_9()) as _i3.WKUIDelegate); @override _i3.WKNavigationDelegate createNavigationDelegate() => (super.noSuchMethod( Invocation.method(#createNavigationDelegate, []), - returnValue: _FakeWKNavigationDelegate_9()) as _i3.WKNavigationDelegate); + returnValue: _FakeWKNavigationDelegate_10()) as _i3.WKNavigationDelegate); } From 463bf985b12f150f67a5fe93dffee145c3279adb Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 12 Apr 2022 16:39:11 -0400 Subject: [PATCH 128/844] Roll Flutter from 0f2b1a3baf20 to 33e540a92e39 (6 revisions) (#5237) --- .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 b3b760fb2b46..77cdca8dff59 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -0f2b1a3baf203586ad0580e9c74920cc2db4e2f7 +33e540a92e3916fa8c44f095a032f44bb6110664 From 818fed95281f9ca3a93920b34e17aaf23a0f24c8 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 12 Apr 2022 17:44:14 -0400 Subject: [PATCH 129/844] Roll Flutter from 33e540a92e39 to 17be6d73e6cb (2 revisions) (#5239) --- .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 77cdca8dff59..bdf93dcf4489 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -33e540a92e3916fa8c44f095a032f44bb6110664 +17be6d73e6cb5f21f601569f11fea60597330d84 From 0b7e457131c15d1a2fed1025f1fb4cd8f9e37f36 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 12 Apr 2022 20:24:13 -0400 Subject: [PATCH 130/844] [google_sign_in] Publish federation (#5238) --- packages/google_sign_in/google_sign_in/CHANGELOG.md | 2 +- packages/google_sign_in/google_sign_in/pubspec.yaml | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index bee4dafaf16f..caab46de7c5b 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,4 +1,4 @@ -## NEXT +## 5.3.0 * Moves Android and iOS implementations to federated packages. diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 43904af23121..760706f2e7bc 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -3,10 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system for signing in with a Google account on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.2.5 -# Temporarily disable publishing to allow moving Android and iOS -# implementations. -publish_to: none +version: 5.3.0 environment: @@ -26,11 +23,8 @@ flutter: dependencies: flutter: sdk: flutter - # Temporary path dependencies to allow moving Android and iOS implementations. - google_sign_in_android: - path: ../google_sign_in_android - google_sign_in_ios: - path: ../google_sign_in_ios + google_sign_in_android: ^5.2.5 + google_sign_in_ios: ^5.2.5 google_sign_in_platform_interface: ^2.1.0 google_sign_in_web: ^0.10.0 From a7e3bfc5c4256c2a2e71403fed384949fa5922fb Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 12 Apr 2022 21:04:13 -0400 Subject: [PATCH 131/844] Roll Flutter from 17be6d73e6cb to 888208c1f4e0 (4 revisions) (#5240) --- .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 bdf93dcf4489..0e23be9dad2b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -17be6d73e6cb5f21f601569f11fea60597330d84 +888208c1f4e0d779e8863997a12725e5ef32e0c0 From 07bf81700b9aeef29f9007bc1f232e345d3094db Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 13 Apr 2022 00:19:06 -0400 Subject: [PATCH 132/844] Roll Flutter from 888208c1f4e0 to fd73f2730c78 (10 revisions) (#5246) --- .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 0e23be9dad2b..83cc089a8050 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -888208c1f4e0d779e8863997a12725e5ef32e0c0 +fd73f2730c7876a2d52d71d88669cc48256da709 From d9450806d2f887830f18bea5f72c2dc3cee06d17 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 13 Apr 2022 01:24:11 -0400 Subject: [PATCH 133/844] Roll Flutter from fd73f2730c78 to 009688a99eb9 (1 revision) (#5247) --- .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 83cc089a8050..b0e5d8c17c1b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fd73f2730c7876a2d52d71d88669cc48256da709 +009688a99eb9f2c0a2db06ceb90022461705f572 From b6a9d6ccf0d13be817d29b36ece660079d606896 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 13 Apr 2022 10:59:09 -0400 Subject: [PATCH 134/844] Roll Flutter from 009688a99eb9 to 2b0255f0d9f3 (2 revisions) (#5248) --- .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 b0e5d8c17c1b..146bc5e5f6b2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -009688a99eb9f2c0a2db06ceb90022461705f572 +2b0255f0d9f38f5671eaffe2438240ed048670cc From ace57091e0c7901f28a6d36f056169c4e254a6bd Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 13 Apr 2022 12:04:10 -0400 Subject: [PATCH 135/844] Roll Flutter from 2b0255f0d9f3 to 5fb74db1a9cb (1 revision) (#5251) --- .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 146bc5e5f6b2..1efe14550c21 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2b0255f0d9f38f5671eaffe2438240ed048670cc +5fb74db1a9cb8c57dbf58c1611159e4c31d2d43d From 980cf40ea600198f3b89a9f20ac3aee81313d8ca Mon Sep 17 00:00:00 2001 From: BeMacized Date: Wed, 13 Apr 2022 18:49:10 +0200 Subject: [PATCH 136/844] [local_auth] Change stopAuthentication to return false instead of throwing on iOS. (#5249) --- packages/local_auth/local_auth_ios/CHANGELOG.md | 4 ++++ packages/local_auth/local_auth_ios/lib/local_auth_ios.dart | 3 ++- packages/local_auth/local_auth_ios/pubspec.yaml | 2 +- .../local_auth/local_auth_ios/test/local_auth_test.dart | 6 +++--- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index 7f198f2d66c0..d826cc8032e5 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.1 + +* BREAKING CHANGE: Changes `stopAuthentication` to always return false instead of throwing an error. + ## 1.0.0 * Initial release from migration to federated architecture. diff --git a/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart b/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart index 7d085ccf14d9..d92d076d79fb 100644 --- a/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart +++ b/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart @@ -81,8 +81,9 @@ class LocalAuthIOS extends LocalAuthPlatform { Future isDeviceSupported() async => (await _channel.invokeMethod('isDeviceSupported')) ?? false; + /// Always returns false as this method is not supported on iOS. @override Future stopAuthentication() async { - throw UnimplementedError('stopAuthentication() is not supported on iOS.'); + return false; } } diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index a9126fdea676..537b1a64d003 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.0 +version: 1.0.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_ios/test/local_auth_test.dart b/packages/local_auth/local_auth_ios/test/local_auth_test.dart index b529764adbd6..180383bc258c 100644 --- a/packages/local_auth/local_auth_ios/test/local_auth_test.dart +++ b/packages/local_auth/local_auth_ios/test/local_auth_test.dart @@ -75,9 +75,9 @@ void main() { ); }); - test('stopAuthentication throws UnimplementedError', () async { - expect(() async => await localAuthentication.stopAuthentication(), - throwsUnimplementedError); + test('stopAuthentication returns false', () async { + final bool result = await localAuthentication.stopAuthentication(); + expect(result, false); }); group('With device auth fail over', () { From f487761bf965273e2d9a6bd1c09a7eb145efbd13 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 13 Apr 2022 13:29:12 -0400 Subject: [PATCH 137/844] Roll Flutter from 5fb74db1a9cb to 86d43e428aaa (2 revisions) (#5252) --- .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 1efe14550c21..ac8097884e27 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -5fb74db1a9cb8c57dbf58c1611159e4c31d2d43d +86d43e428aaa049c7d83242c6b8f0b386cf39934 From 787060564a5143fa06e98e473740b3c4ab1786ef Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 13 Apr 2022 14:34:12 -0400 Subject: [PATCH 138/844] Roll Flutter from 86d43e428aaa to a20cd33d6734 (3 revisions) (#5253) --- .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 ac8097884e27..3bad460e6f61 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -86d43e428aaa049c7d83242c6b8f0b386cf39934 +a20cd33d6734769b0d186ef91d653326972ed784 From af665aee67d86a6202bbd50afa67dec7a465a162 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Wed, 13 Apr 2022 12:34:09 -0700 Subject: [PATCH 139/844] [google_sign_in_web] Add `plugin_name` to `init` call. (#5242) --- .../google_sign_in_web/CHANGELOG.md | 4 ++- .../example/integration_test/auth2_test.dart | 21 +++++++++++++++ .../gapi_mocks/src/auth2_init.dart | 2 ++ .../lib/google_sign_in_web.dart | 1 + .../lib/src/generated/gapiauth2.dart | 26 ++++++++++++------- .../google_sign_in_web/pubspec.yaml | 2 +- 6 files changed, 45 insertions(+), 11 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 e35caf75ea1f..aab4acae0376 100644 --- a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.10.1 * Updates minimum Flutter version to 2.8. +* Passes `plugin_name` to Google Sign-In's `init` method so new applications can + continue using this plugin after April 30th 2022. Issue [#88084](https://github.com/flutter/flutter/issues/88084). ## 0.10.0+5 diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart index 2af9476dbb33..9db024361580 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_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:html' as html; + import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; @@ -126,6 +128,25 @@ void main() { throwsAssertionError); }); + // See: https://github.com/flutter/flutter/issues/88084 + testWidgets('Init passes plugin_name parameter with the expected value', + (WidgetTester tester) async { + await plugin.init( + hostedDomain: 'foo', + scopes: ['some', 'scope'], + clientId: '1234', + ); + + final Object? initParameters = + js_util.getProperty(html.window, 'gapi2.init.parameters'); + expect(initParameters, isNotNull); + + final Object? pluginNameParameter = + js_util.getProperty(initParameters!, 'plugin_name'); + expect(pluginNameParameter, isA()); + expect(pluginNameParameter, 'dart-google_sign_in_web'); + }); + group('Successful .init, then', () { setUp(() async { await plugin.init( diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_mocks/src/auth2_init.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_mocks/src/auth2_init.dart index cc49b2759e7f..84f4e6ee8ba8 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_mocks/src/auth2_init.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_mocks/src/auth2_init.dart @@ -12,6 +12,8 @@ var mockUser = ${googleUser(userData)}; function GapiAuth2() {} GapiAuth2.prototype.init = function (initOptions) { + /*Leak the initOptions so we can look at them later.*/ + window['gapi2.init.parameters'] = initOptions; return { then: (onSuccess, onError) => { window.setTimeout(() => { diff --git a/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart b/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart index 731ced5ddbbe..533c353df310 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart @@ -92,6 +92,7 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { // The js lib wants a space-separated list of values scope: scopes.join(' '), client_id: appClientId!, + plugin_name: 'dart-google_sign_in_web', )); final Completer isAuthInitialized = Completer(); diff --git a/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart b/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart index 88d196bad007..8e23713c90e9 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart @@ -233,15 +233,23 @@ abstract class ClientConfig { /// The default redirect_uri is the current URL stripped of query parameters and hash fragment. external String? get redirect_uri; external set redirect_uri(String? v); - external factory ClientConfig( - {String client_id, - String cookie_policy, - String scope, - bool fetch_basic_profile, - String? hosted_domain, - String openid_realm, - String /*'popup'|'redirect'*/ ux_mode, - String redirect_uri}); + + /// Allows newly created Client IDs to use the Google Platform Library from now until the March 30th, 2023 deprecation date. + /// See: https://github.com/flutter/flutter/issues/88084 + external String? get plugin_name; + external set plugin_name(String? v); + + external factory ClientConfig({ + String client_id, + String cookie_policy, + String scope, + bool fetch_basic_profile, + String? hosted_domain, + String openid_realm, + String /*'popup'|'redirect'*/ ux_mode, + String redirect_uri, + String plugin_name, + }); } @JS('gapi.auth2.SigninOptionsBuilder') 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 d97a7c4da6f1..5a09453b8e86 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/plugins/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.10.0+5 +version: 0.10.1 environment: sdk: ">=2.12.0 <3.0.0" From 4fa853d5c5feeb05f7e98a4fe236dc0373d186a7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 13 Apr 2022 16:44:07 -0400 Subject: [PATCH 140/844] Roll Flutter from a20cd33d6734 to 9526c84b2e48 (7 revisions) (#5255) --- .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 3bad460e6f61..5c830b3b1a37 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a20cd33d6734769b0d186ef91d653326972ed784 +9526c84b2e48331260f119687498f32424635730 From c622c1d1b0fa719b47d9bebb5836f16e51df6fd2 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 13 Apr 2022 17:49:06 -0400 Subject: [PATCH 141/844] Roll Flutter from 9526c84b2e48 to 52d17b667250 (2 revisions) (#5257) --- .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 5c830b3b1a37..6c29c6d510c9 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -9526c84b2e48331260f119687498f32424635730 +52d17b667250c6f126f0a03cec04b6a8c72dee04 From 97fbd1fb42025b748bf1499399a2ba813943c94f Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 00:14:09 -0400 Subject: [PATCH 142/844] Roll Flutter from 52d17b667250 to adda8e93881d (3 revisions) (#5258) --- .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 6c29c6d510c9..9ed8fdcadfa8 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -52d17b667250c6f126f0a03cec04b6a8c72dee04 +adda8e93881d681680de2f7f33bfc021dac20c9d From c188b1854ac6e30597ffb3e3f935d5ec7218aae6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 02:34:09 -0400 Subject: [PATCH 143/844] Roll Flutter from adda8e93881d to 61be9229b7f3 (1 revision) (#5259) --- .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 9ed8fdcadfa8..fbce84b0d935 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -adda8e93881d681680de2f7f33bfc021dac20c9d +61be9229b7f35557fceed203b572dc96384ade42 From 6f57118e69cc5c3a9bb6c19968142ebb6561ac99 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 03:39:08 -0400 Subject: [PATCH 144/844] Roll Flutter from 61be9229b7f3 to c2dc92ca881d (3 revisions) (#5260) --- .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 fbce84b0d935..449ed38ee12e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -61be9229b7f35557fceed203b572dc96384ade42 +c2dc92ca881d3f71e8db872798362dcaa4eecf4b From d14ba3689576cd712150f719fb2ff75c7f294ff1 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 04:44:06 -0400 Subject: [PATCH 145/844] Roll Flutter from c2dc92ca881d to 3aba137efd31 (1 revision) (#5261) --- .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 449ed38ee12e..636ff4aecae0 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c2dc92ca881d3f71e8db872798362dcaa4eecf4b +3aba137efd3109c0a4735f814cac5ac864ceb592 From 84cec3dda5fd2376e057426104769a0f0045f4d3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 05:49:08 -0400 Subject: [PATCH 146/844] Roll Flutter from 3aba137efd31 to cb968c5f3291 (1 revision) (#5262) --- .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 636ff4aecae0..7da40e375dd3 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3aba137efd3109c0a4735f814cac5ac864ceb592 +cb968c5f3291b53e6f380b757babfca8b9ac2b86 From b76f3f51bdc685fd514daf927121413dbfb8d92f Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 10:24:09 -0400 Subject: [PATCH 147/844] Roll Flutter from cb968c5f3291 to 05759325bad1 (1 revision) (#5263) --- .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 7da40e375dd3..6817202325c6 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -cb968c5f3291b53e6f380b757babfca8b9ac2b86 +05759325bad1493757c02ad31ad7cc9e332e06ad From b2f5465c087730e32e657ac865c13de501033045 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Thu, 14 Apr 2022 17:59:08 +0200 Subject: [PATCH 148/844] [local_auth] Refactor package to make use of new platform interface and native implementations (#4701) --- packages/local_auth/local_auth/AUTHORS | 1 + packages/local_auth/local_auth/CHANGELOG.md | 39 ++++- .../local_auth/example/lib/main.dart | 16 +- .../local_auth/lib/auth_strings.dart | 164 ------------------ .../local_auth/local_auth/lib/local_auth.dart | 146 +--------------- .../local_auth/lib/src/local_auth.dart | 77 ++++++++ .../lib/{ => src/types}/error_codes.dart | 2 +- packages/local_auth/local_auth/pubspec.yaml | 16 +- .../local_auth/test/local_auth_test.dart | 39 +---- 9 files changed, 142 insertions(+), 358 deletions(-) delete mode 100644 packages/local_auth/local_auth/lib/auth_strings.dart create mode 100644 packages/local_auth/local_auth/lib/src/local_auth.dart rename packages/local_auth/local_auth/lib/{ => src/types}/error_codes.dart (93%) diff --git a/packages/local_auth/local_auth/AUTHORS b/packages/local_auth/local_auth/AUTHORS index 493a0b4ef9c2..d5694690c247 100644 --- a/packages/local_auth/local_auth/AUTHORS +++ b/packages/local_auth/local_auth/AUTHORS @@ -64,3 +64,4 @@ Aleksandr Yurkovskiy Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> +Bodhi Mulders diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 1ca9fe146c7b..deac871d935f 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,7 +1,42 @@ -## NEXT +## 2.0.0 +* Migrates plugin to federated architecture. * Adds OS version support information to README. -* Switches over to default method implementation in new platform interface. +* BREAKING CHANGE: Deprecated method `authenticateWithBiometrics` has been removed. + Use `authenticate` instead. +* BREAKING CHANGE: Enum `BiometricType` has been expanded with options for `strong` and `weak`, + and applications should be updated to handle these accordingly. +* BREAKING CHANGE: Parameters of `authenticate` have been changed. + + Example: + ```dart + // Old way of calling `authenticate`. + Future authenticate( + localizedReason: 'localized reason', + useErrorDialogs: true, + stickyAuth: false, + androidAuthStrings: const AndroidAuthMessages(), + iOSAuthStrings: const IOSAuthMessages(), + sensitiveTransaction: true, + biometricOnly: false, + ); + // New way of calling `authenticate`. + Future authenticate( + localizedReason: 'localized reason', + authMessages: const [ + IOSAuthMessages(), + AndroidAuthMessages() + ], + options: const AuthenticationOptions( + useErrorDialogs: true, + stickyAuth: false, + sensitiveTransaction: true, + biometricOnly: false, + ), + ); + ``` + + ## 1.1.11 diff --git a/packages/local_auth/local_auth/example/lib/main.dart b/packages/local_auth/local_auth/example/lib/main.dart index a9604b3411b7..92ad7cf4fb3f 100644 --- a/packages/local_auth/local_auth/example/lib/main.dart +++ b/packages/local_auth/local_auth/example/lib/main.dart @@ -79,9 +79,12 @@ class _MyAppState extends State { _authorized = 'Authenticating'; }); authenticated = await auth.authenticate( - localizedReason: 'Let OS determine authentication method', + localizedReason: 'Let OS determine authentication method', + options: const AuthenticationOptions( useErrorDialogs: true, - stickyAuth: true); + stickyAuth: true, + ), + ); setState(() { _isAuthenticating = false; }); @@ -109,11 +112,14 @@ class _MyAppState extends State { _authorized = 'Authenticating'; }); authenticated = await auth.authenticate( - localizedReason: - 'Scan your fingerprint (or face or whatever) to authenticate', + localizedReason: + 'Scan your fingerprint (or face or whatever) to authenticate', + options: const AuthenticationOptions( useErrorDialogs: true, stickyAuth: true, - biometricOnly: true); + biometricOnly: true, + ), + ); setState(() { _isAuthenticating = false; _authorized = 'Authenticating'; diff --git a/packages/local_auth/local_auth/lib/auth_strings.dart b/packages/local_auth/local_auth/lib/auth_strings.dart deleted file mode 100644 index 585742ac68c2..000000000000 --- a/packages/local_auth/local_auth/lib/auth_strings.dart +++ /dev/null @@ -1,164 +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. - -// This is a temporary ignore to allow us to land a new set of linter rules in a -// series of manageable patches instead of one gigantic PR. It disables some of -// the new lints that are already failing on this plugin, for this plugin. It -// should be deleted and the failing lints addressed as soon as possible. -// ignore_for_file: public_member_api_docs - -import 'package:intl/intl.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; - -/// Android side authentication messages. -/// -/// Provides default values for all messages. -class AndroidAuthMessages extends AuthMessages { - const AndroidAuthMessages({ - this.biometricHint, - this.biometricNotRecognized, - this.biometricRequiredTitle, - this.biometricSuccess, - this.cancelButton, - this.deviceCredentialsRequiredTitle, - this.deviceCredentialsSetupDescription, - this.goToSettingsButton, - this.goToSettingsDescription, - this.signInTitle, - }); - - final String? biometricHint; - final String? biometricNotRecognized; - final String? biometricRequiredTitle; - final String? biometricSuccess; - final String? cancelButton; - final String? deviceCredentialsRequiredTitle; - final String? deviceCredentialsSetupDescription; - final String? goToSettingsButton; - final String? goToSettingsDescription; - final String? signInTitle; - - @override - Map get args { - return { - 'biometricHint': biometricHint ?? androidBiometricHint, - 'biometricNotRecognized': - biometricNotRecognized ?? androidBiometricNotRecognized, - 'biometricSuccess': biometricSuccess ?? androidBiometricSuccess, - 'biometricRequired': - biometricRequiredTitle ?? androidBiometricRequiredTitle, - 'cancelButton': cancelButton ?? androidCancelButton, - 'deviceCredentialsRequired': deviceCredentialsRequiredTitle ?? - androidDeviceCredentialsRequiredTitle, - 'deviceCredentialsSetupDescription': deviceCredentialsSetupDescription ?? - androidDeviceCredentialsSetupDescription, - 'goToSetting': goToSettingsButton ?? goToSettings, - 'goToSettingDescription': - goToSettingsDescription ?? androidGoToSettingsDescription, - 'signInTitle': signInTitle ?? androidSignInTitle, - }; - } -} - -/// iOS side authentication messages. -/// -/// Provides default values for all messages. -class IOSAuthMessages extends AuthMessages { - const IOSAuthMessages({ - this.lockOut, - this.goToSettingsButton, - this.goToSettingsDescription, - this.cancelButton, - this.localizedFallbackTitle, - }); - - final String? lockOut; - final String? goToSettingsButton; - final String? goToSettingsDescription; - final String? cancelButton; - final String? localizedFallbackTitle; - - @override - Map get args { - return { - 'lockOut': lockOut ?? iOSLockOut, - 'goToSetting': goToSettingsButton ?? goToSettings, - 'goToSettingDescriptionIOS': - goToSettingsDescription ?? iOSGoToSettingsDescription, - 'okButton': cancelButton ?? iOSOkButton, - if (localizedFallbackTitle != null) - 'localizedFallbackTitle': localizedFallbackTitle!, - }; - } -} - -// Strings for local_authentication plugin. Currently supports English. -// Intl.message must be string literals. -String get androidBiometricHint => Intl.message('Verify identity', - desc: - 'Hint message advising the user how to authenticate with biometrics. It is ' - 'used on Android side. Maximum 60 characters.'); - -String get androidBiometricNotRecognized => - Intl.message('Not recognized. Try again.', - desc: 'Message to let the user know that authentication was failed. It ' - 'is used on Android side. Maximum 60 characters.'); - -String get androidBiometricSuccess => Intl.message('Success', - desc: 'Message to let the user know that authentication was successful. It ' - 'is used on Android side. Maximum 60 characters.'); - -String get androidCancelButton => Intl.message('Cancel', - desc: 'Message showed on a button that the user can click to leave the ' - 'current dialog. It is used on Android side. Maximum 30 characters.'); - -String get androidSignInTitle => Intl.message('Authentication required', - desc: 'Message showed as a title in a dialog which indicates the user ' - 'that they need to scan biometric to continue. It is used on ' - 'Android side. Maximum 60 characters.'); - -String get androidBiometricRequiredTitle => Intl.message('Biometric required', - desc: 'Message showed as a title in a dialog which indicates the user ' - 'has not set up biometric authentication on their device. It is used on Android' - ' side. Maximum 60 characters.'); - -String get androidDeviceCredentialsRequiredTitle => Intl.message( - 'Device credentials required', - desc: 'Message showed as a title in a dialog which indicates the user ' - 'has not set up credentials authentication on their device. It is used on Android' - ' side. Maximum 60 characters.'); - -String get androidDeviceCredentialsSetupDescription => Intl.message( - 'Device credentials required', - desc: 'Message advising the user to go to the settings and configure ' - 'device credentials on their device. It shows in a dialog on Android side.'); - -String get goToSettings => Intl.message('Go to settings', - desc: 'Message showed on a button that the user can click to go to ' - 'settings pages from the current dialog. It is used on both Android ' - 'and iOS side. Maximum 30 characters.'); - -String get androidGoToSettingsDescription => Intl.message( - 'Biometric authentication is not set up on your device. Go to ' - '\'Settings > Security\' to add biometric authentication.', - desc: 'Message advising the user to go to the settings and configure ' - 'biometric on their device. It shows in a dialog on Android side.'); - -String get iOSLockOut => Intl.message( - 'Biometric authentication is disabled. Please lock and unlock your screen to ' - 'enable it.', - desc: - 'Message advising the user to re-enable biometrics on their device. It ' - 'shows in a dialog on iOS side.'); - -String get iOSGoToSettingsDescription => Intl.message( - 'Biometric authentication is not set up on your device. Please either enable ' - 'Touch ID or Face ID on your phone.', - desc: - 'Message advising the user to go to the settings and configure Biometrics ' - 'for their device. It shows in a dialog on iOS side.'); - -String get iOSOkButton => Intl.message('OK', - desc: 'Message showed on a button that the user can click to leave the ' - 'current dialog. It is used on iOS side. Maximum 30 characters.'); diff --git a/packages/local_auth/local_auth/lib/local_auth.dart b/packages/local_auth/local_auth/lib/local_auth.dart index 32818b31783d..7c42fedc7755 100644 --- a/packages/local_auth/local_auth/lib/local_auth.dart +++ b/packages/local_auth/local_auth/lib/local_auth.dart @@ -2,144 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// This is a temporary ignore to allow us to land a new set of linter rules in a -// series of manageable patches instead of one gigantic PR. It disables some of -// the new lints that are already failing on this plugin, for this plugin. It -// should be deleted and the failing lints addressed as soon as possible. -// ignore_for_file: public_member_api_docs - -import 'dart:async'; - -import 'package:flutter/foundation.dart' show visibleForTesting; -import 'package:flutter/services.dart'; -import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; -import 'package:platform/platform.dart'; -import 'auth_strings.dart'; - -export 'package:local_auth_platform_interface/types/biometric_type.dart'; - -Platform _platform = const LocalPlatform(); - -@visibleForTesting -void setMockPathProviderPlatform(Platform platform) { - _platform = platform; -} - -/// A Flutter plugin for authenticating the user identity locally. -class LocalAuthentication { - /// The `authenticateWithBiometrics` method has been deprecated. - /// Use `authenticate` with `biometricOnly: true` instead. - @Deprecated('Use `authenticate` with `biometricOnly: true` instead') - Future authenticateWithBiometrics({ - required String localizedReason, - bool useErrorDialogs = true, - bool stickyAuth = false, - AndroidAuthMessages androidAuthStrings = const AndroidAuthMessages(), - IOSAuthMessages iOSAuthStrings = const IOSAuthMessages(), - bool sensitiveTransaction = true, - }) => - LocalAuthPlatform.instance.authenticate( - localizedReason: localizedReason, - authMessages: [iOSAuthStrings, androidAuthStrings], - options: AuthenticationOptions( - useErrorDialogs: useErrorDialogs, - stickyAuth: stickyAuth, - sensitiveTransaction: sensitiveTransaction, - biometricOnly: true, - ), - ); - - /// Authenticates the user with biometrics available on the device while also - /// allowing the user to use device authentication - pin, pattern, passcode. - /// - /// Returns true, if the user successfully authenticated, false otherwise. - /// - /// [localizedReason] is the message to show to user while prompting them - /// for authentication. This is typically along the lines of: 'Please scan - /// your finger to access MyApp.'. This must not be empty. - /// - /// [useErrorDialogs] = true means the system will attempt to handle user - /// fixable issues encountered while authenticating. For instance, if - /// fingerprint reader exists on the phone but there's no fingerprint - /// registered, the plugin will attempt to take the user to settings to add - /// one. Anything that is not user fixable, such as no biometric sensor on - /// device, will be returned as a [PlatformException]. - /// - /// [stickyAuth] is used when the application goes into background for any - /// reason while the authentication is in progress. Due to security reasons, - /// the authentication has to be stopped at that time. If stickyAuth is set - /// to true, authentication resumes when the app is resumed. If it is set to - /// false (default), then as soon as app is paused a failure message is sent - /// back to Dart and it is up to the client app to restart authentication or - /// do something else. - /// - /// Construct [AndroidAuthStrings] and [IOSAuthStrings] if you want to - /// customize messages in the dialogs. - /// - /// Setting [sensitiveTransaction] to true enables platform specific - /// precautions. For instance, on face unlock, Android opens a confirmation - /// dialog after the face is recognized to make sure the user meant to unlock - /// their phone. - /// - /// Setting [biometricOnly] to true prevents authenticates from using non-biometric - /// local authentication such as pin, passcode, and passcode. - /// - /// Throws an [PlatformException] if there were technical problems with local - /// authentication (e.g. lack of relevant hardware). This might throw - /// [PlatformException] with error code [otherOperatingSystem] on the iOS - /// simulator. - Future authenticate({ - required String localizedReason, - bool useErrorDialogs = true, - bool stickyAuth = false, - AndroidAuthMessages androidAuthStrings = const AndroidAuthMessages(), - IOSAuthMessages iOSAuthStrings = const IOSAuthMessages(), - bool sensitiveTransaction = true, - bool biometricOnly = false, - }) { - return LocalAuthPlatform.instance.authenticate( - localizedReason: localizedReason, - authMessages: [iOSAuthStrings, androidAuthStrings], - options: AuthenticationOptions( - useErrorDialogs: useErrorDialogs, - stickyAuth: stickyAuth, - sensitiveTransaction: sensitiveTransaction, - biometricOnly: biometricOnly, - ), - ); - } - - /// Returns true if auth was cancelled successfully. - /// This api only works for Android. - /// Returns false if there was some error or no auth in progress. - /// - /// Returns [Future] bool true or false: - Future stopAuthentication() async { - if (_platform.isAndroid) { - return LocalAuthPlatform.instance.stopAuthentication(); - } - return true; - } - - /// Returns true if device is capable of checking biometrics - /// - /// Returns a [Future] bool true or false: - Future get canCheckBiometrics => - LocalAuthPlatform.instance.deviceSupportsBiometrics(); - - /// Returns true if device is capable of checking biometrics or is able to - /// fail over to device credentials. - /// - /// Returns a [Future] bool true or false: - Future isDeviceSupported() async => - LocalAuthPlatform.instance.isDeviceSupported(); - - /// Returns a list of enrolled biometrics - /// - /// Returns a [Future] List with the following possibilities: - /// - BiometricType.face - /// - BiometricType.fingerprint - /// - BiometricType.iris (not yet implemented) - Future> getAvailableBiometrics() => - LocalAuthPlatform.instance.getEnrolledBiometrics(); -} +export 'package:local_auth/src/local_auth.dart' show LocalAuthentication; +export 'package:local_auth_platform_interface/types/auth_options.dart' + show AuthenticationOptions; +export 'package:local_auth_platform_interface/types/biometric_type.dart' + show BiometricType; diff --git a/packages/local_auth/local_auth/lib/src/local_auth.dart b/packages/local_auth/local_auth/lib/src/local_auth.dart new file mode 100644 index 000000000000..508e2b14e129 --- /dev/null +++ b/packages/local_auth/local_auth/lib/src/local_auth.dart @@ -0,0 +1,77 @@ +// 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 is a temporary ignore to allow us to land a new set of linter rules in a +// series of manageable patches instead of one gigantic PR. It disables some of +// the new lints that are already failing on this plugin, for this plugin. It +// should be deleted and the failing lints addressed as soon as possible. +// ignore_for_file: public_member_api_docs + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:local_auth/src/types/error_codes.dart'; +import 'package:local_auth_android/local_auth_android.dart'; +import 'package:local_auth_ios/local_auth_ios.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_platform_interface/types/auth_messages.dart'; +import 'package:local_auth_platform_interface/types/biometric_type.dart'; + +/// A Flutter plugin for authenticating the user identity locally. +class LocalAuthentication { + /// Authenticates the user with biometrics available on the device while also + /// allowing the user to use device authentication - pin, pattern, passcode. + /// + /// Returns true if the user successfully authenticated, false otherwise. + /// + /// [localizedReason] is the message to show to user while prompting them + /// for authentication. This is typically along the lines of: 'Authenticate + /// to access MyApp.'. This must not be empty. + /// + /// Provide [authMessages] if you want to + /// customize messages in the dialogs. + /// + /// Provide [options] for configuring further authentication related options. + /// + /// Throws a [PlatformException] if there were technical problems with local + /// authentication (e.g. lack of relevant hardware). This might throw + /// [PlatformException] with error code [otherOperatingSystem] on the iOS + /// simulator. + Future authenticate( + {required String localizedReason, + Iterable authMessages = const [ + IOSAuthMessages(), + AndroidAuthMessages() + ], + AuthenticationOptions options = const AuthenticationOptions()}) { + return LocalAuthPlatform.instance.authenticate( + localizedReason: localizedReason, + authMessages: authMessages, + options: options, + ); + } + + /// Cancels any in-progress authentication, returning true if auth was + /// cancelled successfully. + /// + /// This API is not supported by all platforms. + /// Returns false if there was some error, no authentication in progress, + /// or the current platform lacks support. + Future stopAuthentication() async { + return LocalAuthPlatform.instance.stopAuthentication(); + } + + /// Returns true if device is capable of checking biometrics. + Future get canCheckBiometrics => + LocalAuthPlatform.instance.deviceSupportsBiometrics(); + + /// Returns true if device is capable of checking biometrics or is able to + /// fail over to device credentials. + Future isDeviceSupported() async => + LocalAuthPlatform.instance.isDeviceSupported(); + + /// Returns a list of enrolled biometrics. + Future> getAvailableBiometrics() => + LocalAuthPlatform.instance.getEnrolledBiometrics(); +} diff --git a/packages/local_auth/local_auth/lib/error_codes.dart b/packages/local_auth/local_auth/lib/src/types/error_codes.dart similarity index 93% rename from packages/local_auth/local_auth/lib/error_codes.dart rename to packages/local_auth/local_auth/lib/src/types/error_codes.dart index bcf15b7b2154..3426099bacbd 100644 --- a/packages/local_auth/local_auth/lib/error_codes.dart +++ b/packages/local_auth/local_auth/lib/src/types/error_codes.dart @@ -15,7 +15,7 @@ const String notEnrolled = 'NotEnrolled'; /// Indicates the device does not have a Touch ID/fingerprint scanner. const String notAvailable = 'NotAvailable'; -/// Indicates the device operating system is not iOS or Android. +/// Indicates the device operating system is unsupported. const String otherOperatingSystem = 'OtherOperatingSystem'; /// Indicates the API lock out due to too many attempts. diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index baf5d46b7b00..fa055fab17f8 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -3,11 +3,7 @@ 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/plugins/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: 1.1.11 - -# Temporarily disable publishing to allow moving Android and iOS -# implementations. -publish_to: none +version: 2.0.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -24,15 +20,10 @@ flutter: dependencies: flutter: sdk: flutter - flutter_plugin_android_lifecycle: ^2.0.1 intl: ^0.17.0 -# Temporary path dependencies to allow moving Android and iOS implementations. - local_auth_android: - path: ../local_auth_android - local_auth_ios: - path: ../local_auth_ios + local_auth_android: ^1.0.0 + local_auth_ios: ^1.0.1 local_auth_platform_interface: ^1.0.1 - platform: ^3.0.0 dev_dependencies: flutter_driver: @@ -42,3 +33,4 @@ dev_dependencies: integration_test: sdk: flutter mockito: ^5.1.0 + plugin_platform_interface: ^2.1.2 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 b92297d90231..d3f92dfe95db 100644 --- a/packages/local_auth/local_auth/test/local_auth_test.dart +++ b/packages/local_auth/local_auth/test/local_auth_test.dart @@ -4,13 +4,14 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:local_auth/auth_strings.dart'; import 'package:local_auth/local_auth.dart'; +import 'package:local_auth/src/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_platform_interface/local_auth_platform_interface.dart'; import 'package:local_auth_platform_interface/types/auth_messages.dart'; import 'package:local_auth_platform_interface/types/auth_options.dart'; import 'package:mockito/mockito.dart'; -import 'package:platform/platform.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; void main() { @@ -24,24 +25,6 @@ void main() { LocalAuthPlatform.instance = mockLocalAuthPlatform; }); - test('authenticateWithBiometrics calls platform implementation', () { - when(mockLocalAuthPlatform.authenticate( - localizedReason: anyNamed('localizedReason'), - authMessages: anyNamed('authMessages'), - options: anyNamed('options'), - )).thenAnswer((_) async => true); - localAuthentication.authenticateWithBiometrics( - localizedReason: 'Test Reason'); - verify(mockLocalAuthPlatform.authenticate( - localizedReason: 'Test Reason', - authMessages: [ - const IOSAuthMessages(), - const AndroidAuthMessages(), - ], - options: const AuthenticationOptions(biometricOnly: true), - )).called(1); - }); - test('authenticate calls platform implementation', () { when(mockLocalAuthPlatform.authenticate( localizedReason: anyNamed('localizedReason'), @@ -73,20 +56,13 @@ void main() { verify(mockLocalAuthPlatform.getEnrolledBiometrics()).called(1); }); - test('stopAuthentication calls platform implementation on Android', () { + test('stopAuthentication calls platform implementation', () { when(mockLocalAuthPlatform.stopAuthentication()) .thenAnswer((_) async => true); - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'android')); localAuthentication.stopAuthentication(); verify(mockLocalAuthPlatform.stopAuthentication()).called(1); }); - test('stopAuthentication does not call platform implementation on iOS', () { - setMockPathProviderPlatform(FakePlatform(operatingSystem: 'ios')); - localAuthentication.stopAuthentication(); - verifyNever(mockLocalAuthPlatform.stopAuthentication()); - }); - test('canCheckBiometrics returns correct result', () async { when(mockLocalAuthPlatform.deviceSupportsBiometrics()) .thenAnswer((_) async => false); @@ -110,11 +86,8 @@ class MockLocalAuthPlatform extends Mock @override Future authenticate({ - String? localizedReason, - Iterable? authMessages = const [ - IOSAuthMessages(), - AndroidAuthMessages() - ], + required String? localizedReason, + required Iterable? authMessages, AuthenticationOptions? options = const AuthenticationOptions(), }) => super.noSuchMethod( From ce66b3b386de03cc1bdd226ba6010b60e0257a70 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 12:54:10 -0400 Subject: [PATCH 149/844] Roll Flutter from 05759325bad1 to ff9d6e5e339f (1 revision) (#5265) --- .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 6817202325c6..be9c1d0f99a9 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -05759325bad1493757c02ad31ad7cc9e332e06ad +ff9d6e5e339fa3f8af64199f95bbfe5ecb2ddd4e From 08e4e02ef2a104e591fcaa92f8ff1f57775f75af Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 13:59:12 -0400 Subject: [PATCH 150/844] Roll Flutter from ff9d6e5e339f to e2d12060a2bc (2 revisions) (#5266) --- .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 be9c1d0f99a9..b71dfa7411fb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ff9d6e5e339fa3f8af64199f95bbfe5ecb2ddd4e +e2d12060a2bc574a3e1209e206d5cf031cdbc335 From 98b79b6f016bca66ad7022bb2aba76983b15b099 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 15:04:10 -0400 Subject: [PATCH 151/844] Roll Flutter from e2d12060a2bc to 08e467dde7a1 (1 revision) (#5268) --- .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 b71dfa7411fb..c9a269c2cc44 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e2d12060a2bc574a3e1209e206d5cf031cdbc335 +08e467dde7a1c3906ce6f4231d9f1d1b36b64022 From 2614d857174199312d6cf756f07e7df737e607b6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Apr 2022 17:14:09 -0400 Subject: [PATCH 152/844] Roll Flutter from 08e467dde7a1 to 2b8333240d38 (5 revisions) (#5270) --- .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 c9a269c2cc44..103b05611de2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -08e467dde7a1c3906ce6f4231d9f1d1b36b64022 +2b8333240d38cf72b8e13580e24d01f5a188e26c From e3a184f30f1758642d7679369b2942a4ea44bd54 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 14 Apr 2022 18:25:46 -0700 Subject: [PATCH 153/844] [webview_flutter_wkwebview] Implement `WKNavigationDelegate.didFinishNavigation` as a proof of concept for callback methods (#5199) --- .../common/function_flutter_api_impls.dart | 25 +++++ .../lib/src/common/web_kit.pigeon.dart | 101 ++++++++++++++++++ .../lib/src/foundation/foundation.dart | 6 +- .../src/foundation/foundation_api_impls.dart | 50 +++++++++ .../lib/src/web_kit/web_kit.dart | 19 ++-- .../lib/src/web_kit/web_kit_api_impls.dart | 87 +++++++++++++++ .../pigeons/web_kit.dart | 20 ++++ .../src/common/function_flutter_api_test.dart | 33 ++++++ .../test/src/common/test_web_kit.pigeon.dart | 24 +++++ .../test/src/web_kit/web_kit_test.dart | 44 ++++++++ .../test/src/web_kit/web_kit_test.mocks.dart | 6 ++ 11 files changed, 407 insertions(+), 8 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/function_flutter_api_impls.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/function_flutter_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/function_flutter_api_impls.dart new file mode 100644 index 000000000000..c6eb711513d2 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/function_flutter_api_impls.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 'instance_manager.dart'; +import 'web_kit.pigeon.dart'; + +/// Flutter api to dispose functions. +class FunctionFlutterApiImpl extends FunctionFlutterApi { + /// Constructs a [FunctionFlutterApiImpl]. + FunctionFlutterApiImpl({InstanceManager? instanceManager}) { + this.instanceManager = instanceManager ?? InstanceManager.instance; + } + + /// Maintains instances stored to communicate with native language objects. + late final InstanceManager instanceManager; + + @override + void dispose(int instanceId) { + final Function? function = instanceManager.getInstance(instanceId); + if (function != null) { + instanceManager.removeInstance(function); + } + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart index 5bf79a675be0..39f5663c1838 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart @@ -1022,6 +1022,75 @@ class WKNavigationDelegateHostApi { return; } } + + Future setDidFinishNavigation( + int arg_instanceId, int? arg_functionInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_functionInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _WKNavigationDelegateFlutterApiCodec extends StandardMessageCodec { + const _WKNavigationDelegateFlutterApiCodec(); +} + +abstract class WKNavigationDelegateFlutterApi { + static const MessageCodec codec = + _WKNavigationDelegateFlutterApiCodec(); + + void didFinishNavigation( + int functionInstanceId, int webViewInstanceId, String? url); + static void setup(WKNavigationDelegateFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null.'); + final List args = (message as List?)!; + final int? arg_functionInstanceId = (args[0] as int?); + assert(arg_functionInstanceId != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null int.'); + final int? arg_webViewInstanceId = (args[1] as int?); + assert(arg_webViewInstanceId != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null int.'); + final String? arg_url = (args[2] as String?); + assert(arg_url != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null String.'); + api.didFinishNavigation( + arg_functionInstanceId!, arg_webViewInstanceId!, arg_url!); + return; + }); + } + } + } } class _NSObjectHostApiCodec extends StandardMessageCodec { @@ -1142,6 +1211,38 @@ class NSObjectHostApi { } } +class _FunctionFlutterApiCodec extends StandardMessageCodec { + const _FunctionFlutterApiCodec(); +} + +abstract class FunctionFlutterApi { + static const MessageCodec codec = _FunctionFlutterApiCodec(); + + void dispose(int instanceId); + static void setup(FunctionFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FunctionFlutterApi.dispose', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.FunctionFlutterApi.dispose 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.FunctionFlutterApi.dispose was null, expected non-null int.'); + api.dispose(arg_instanceId!); + return; + }); + } + } + } +} + class _WKWebViewHostApiCodec extends StandardMessageCodec { const _WKWebViewHostApiCodec(); @override 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 1fef81d9dbaa..ec7bb5377de0 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 @@ -236,7 +236,11 @@ class NSObject { : _api = NSObjectHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, - ); + ) { + // Ensures FlutterApis for the Foundation library and FunctionFlutterApi are + // set up. + FoundationFlutterApis.instance.ensureSetUp(); + } final NSObjectHostApiImpl _api; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart index ec0d60cc748e..b007d1a8312c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart @@ -2,8 +2,10 @@ // 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/services.dart'; +import '../common/function_flutter_api_impls.dart'; import '../common/instance_manager.dart'; import '../common/web_kit.pigeon.dart'; import 'foundation.dart'; @@ -35,6 +37,54 @@ Iterable }); } +/// Handles initialization of Flutter APIs for the Foundation library. +class FoundationFlutterApis { + /// Constructs a [FoundationFlutterApis]. + @visibleForTesting + FoundationFlutterApis({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _binaryMessenger = binaryMessenger { + functionFlutterApi = + FunctionFlutterApiImpl(instanceManager: instanceManager); + } + + static FoundationFlutterApis _instance = FoundationFlutterApis(); + + /// Sets the global instance containing the Flutter Apis for the Foundation library. + @visibleForTesting + static set instance(FoundationFlutterApis instance) { + _instance = instance; + } + + /// Global instance containing the Flutter Apis for the Foundation library. + static FoundationFlutterApis get instance { + return _instance; + } + + final BinaryMessenger? _binaryMessenger; + bool _hasBeenSetUp = false; + + /// Flutter Api for disposing functions. + /// + /// This FlutterApi is placed here because [FoundationFlutterApis.ensureSetUp] + /// is called inside [NSObject] and [NSObject] is the parent class of all + /// objects. + @visibleForTesting + late final FunctionFlutterApiImpl functionFlutterApi; + + /// Ensures all the Flutter APIs have been set up to receive calls from native code. + void ensureSetUp() { + if (!_hasBeenSetUp) { + FunctionFlutterApi.setup( + functionFlutterApi, + binaryMessenger: _binaryMessenger, + ); + _hasBeenSetUp = true; + } + } +} + /// Host api implementation for [NSObject]. class NSObjectHostApiImpl extends NSObjectHostApi { /// Constructs an [NSObjectHostApiImpl]. 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 416e2626fc4a..64a45ada265f 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 @@ -571,15 +571,20 @@ class WKUIDelegate { /// coordinate changes in your web view’s main frame. /// /// Wraps [WKNavigationDelegate](https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc). -class WKNavigationDelegate { +class WKNavigationDelegate extends NSObject { /// Constructs a [WKNavigationDelegate]. WKNavigationDelegate({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl( + }) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ), + super( binaryMessenger: binaryMessenger, instanceManager: instanceManager, ) { + WebKitFlutterApis.instance.ensureSetUp(); _navigationDelegateApi.createForInstances(this); } @@ -587,10 +592,7 @@ class WKNavigationDelegate { /// Called when navigation from the main frame has started. Future setDidStartProvisionalNavigation( - void Function( - WKWebView webView, - String? url, - )? + void Function(WKWebView webView, String? url)? didStartProvisionalNavigation, ) { throw UnimplementedError(); @@ -600,7 +602,10 @@ class WKNavigationDelegate { Future setDidFinishNavigation( void Function(WKWebView webView, String? url)? didFinishNavigation, ) { - throw UnimplementedError(); + return _navigationDelegateApi.setDidFinishNavigationFromInstance( + this, + didFinishNavigation, + ); } /// Called when permission is needed to navigate to new content. 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 1cda9d4b94c2..d33407905375 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 @@ -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/services.dart'; import '../common/instance_manager.dart'; @@ -106,6 +107,51 @@ extension _NSUrlRequestConverter on NSUrlRequest { } } +/// Handles initialization of Flutter APIs for WebKit. +class WebKitFlutterApis { + /// Constructs a [WebKitFlutterApis]. + @visibleForTesting + WebKitFlutterApis({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _binaryMessenger = binaryMessenger { + navigationDelegateFlutterApi = WKNavigationDelegateFlutterApiImpl( + instanceManager: instanceManager, + ); + } + + static WebKitFlutterApis _instance = WebKitFlutterApis(); + + /// Sets the global instance containing the Flutter Apis for the WebKit library. + @visibleForTesting + static set instance(WebKitFlutterApis instance) { + _instance = instance; + } + + /// Global instance containing the Flutter Apis for the WebKit library. + static WebKitFlutterApis get instance { + return _instance; + } + + final BinaryMessenger? _binaryMessenger; + bool _hasBeenSetUp = false; + + /// Flutter Api for [WKNavigationDelegate]. + @visibleForTesting + late final WKNavigationDelegateFlutterApiImpl navigationDelegateFlutterApi; + + /// Ensures all the Flutter APIs have been set up to receive calls from native code. + void ensureSetUp() { + if (!_hasBeenSetUp) { + WKNavigationDelegateFlutterApi.setup( + navigationDelegateFlutterApi, + binaryMessenger: _binaryMessenger, + ); + _hasBeenSetUp = true; + } + } +} + /// Host api implementation for [WKWebSiteDataStore]. class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi { /// Constructs a [WebsiteDataStoreHostApiImpl]. @@ -381,6 +427,47 @@ class WKNavigationDelegateHostApiImpl extends WKNavigationDelegateHostApi { await create(instanceId); } } + + /// Calls [setDidFinishNavigation] with the ids of the provided object instances. + Future setDidFinishNavigationFromInstance( + WKNavigationDelegate instance, + void Function(WKWebView, String?)? didFinishNavigation, + ) { + int? functionInstanceId; + if (didFinishNavigation != null) { + functionInstanceId = instanceManager.getInstanceId(didFinishNavigation) ?? + instanceManager.tryAddInstance(didFinishNavigation)!; + } + return setDidFinishNavigation( + instanceManager.getInstanceId(instance)!, + functionInstanceId, + ); + } +} + +/// Flutter api implementation for [WKNavigationDelegate]. +class WKNavigationDelegateFlutterApiImpl + extends WKNavigationDelegateFlutterApi { + /// Constructs a [WKNavigationDelegateFlutterApiImpl]. + WKNavigationDelegateFlutterApiImpl({InstanceManager? instanceManager}) { + this.instanceManager = instanceManager ?? InstanceManager.instance; + } + + /// Maintains instances stored to communicate with native language objects. + late final InstanceManager instanceManager; + + @override + void didFinishNavigation( + int functionInstanceId, + int webViewInstanceId, + String? url, + ) { + final void Function( + WKWebView webView, + String? url, + ) function = instanceManager.getInstance(functionInstanceId)!; + function(instanceManager.getInstance(webViewInstanceId)!, url); + } } /// Host api implementation for [WKWebView]. 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 370149e638ff..b0a239fcd146 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -281,6 +281,20 @@ abstract class WKScriptMessageHandlerHostApi { @HostApi(dartHostTestHandler: 'TestWKNavigationDelegateHostApi') abstract class WKNavigationDelegateHostApi { void create(int instanceId); + + void setDidFinishNavigation(int instanceId, int? functionInstanceId); +} + +/// Mirror of WKNavigationDelegate. +/// +/// See https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc. +@FlutterApi() +abstract class WKNavigationDelegateFlutterApi { + void didFinishNavigation( + int functionInstanceId, + int webViewInstanceId, + String? url, + ); } /// Mirror of NSObject. @@ -300,6 +314,12 @@ abstract class NSObjectHostApi { void removeObserver(int instanceId, int observerInstanceId, String keyPath); } +/// Disposes references to functions. +@FlutterApi() +abstract class FunctionFlutterApi { + void dispose(int instanceId); +} + /// Mirror of WKWebView. /// /// See https://developer.apple.com/documentation/webkit/wkwebview?language=objc. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart new file mode 100644 index 000000000000..63e59386ceaf --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart @@ -0,0 +1,33 @@ +// 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:webview_flutter_wkwebview/src/common/instance_manager.dart'; +import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart'; +import 'package:webview_flutter_wkwebview/src/foundation/foundation_api_impls.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$FunctionFlutterApi', () { + late InstanceManager instanceManager; + + setUp(() { + instanceManager = InstanceManager(); + }); + + test('dispose', () { + final Function function = () {}; + final int functionInstanceId = instanceManager.tryAddInstance(function)!; + + FoundationFlutterApis.instance = FoundationFlutterApis( + instanceManager: instanceManager, + )..ensureSetUp(); + + FoundationFlutterApis.instance.functionFlutterApi + .dispose(functionInstanceId); + expect(instanceManager.getInstanceId(function), isNull); + }); + }); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart index 0e369a351c75..06a7b7a0a4c0 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart @@ -707,6 +707,7 @@ abstract class TestWKNavigationDelegateHostApi { _TestWKNavigationDelegateHostApiCodec(); void create(int instanceId); + void setDidFinishNavigation(int instanceId, int? functionInstanceId); static void setup(TestWKNavigationDelegateHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -728,6 +729,29 @@ abstract class TestWKNavigationDelegateHostApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation 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.WKNavigationDelegateHostApi.setDidFinishNavigation was null, expected non-null int.'); + final int? arg_functionInstanceId = (args[1] as int?); + assert(arg_functionInstanceId != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation was null, expected non-null int.'); + api.setDidFinishNavigation(arg_instanceId!, arg_functionInstanceId!); + return {}; + }); + } + } } } 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 aa77157efcf7..00a3a31a6957 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 @@ -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_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; @@ -9,6 +11,7 @@ import 'package:webview_flutter_wkwebview/src/common/instance_manager.dart'; import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart'; import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit_api_impls.dart'; import '../common/test_web_kit.pigeon.dart'; import 'web_kit_test.mocks.dart'; @@ -28,9 +31,12 @@ void main() { group('WebKit', () { late InstanceManager instanceManager; + late WebKitFlutterApis flutterApis; setUp(() { instanceManager = InstanceManager(); + flutterApis = WebKitFlutterApis(instanceManager: instanceManager); + WebKitFlutterApis.instance = flutterApis; }); group('$WKWebsiteDataStore', () { @@ -329,12 +335,23 @@ void main() { group('$WKNavigationDelegate', () { late MockTestWKNavigationDelegateHostApi mockPlatformHostApi; + late WKWebView webView; + late WKNavigationDelegate navigationDelegate; setUp(() async { mockPlatformHostApi = MockTestWKNavigationDelegateHostApi(); TestWKNavigationDelegateHostApi.setup(mockPlatformHostApi); + TestWKWebViewConfigurationHostApi.setup( + MockTestWKWebViewConfigurationHostApi(), + ); + TestWKWebViewHostApi.setup(MockTestWKWebViewHostApi()); + webView = WKWebView( + WKWebViewConfiguration(instanceManager: instanceManager), + instanceManager: instanceManager, + ); + navigationDelegate = WKNavigationDelegate( instanceManager: instanceManager, ); @@ -342,6 +359,8 @@ void main() { tearDown(() { TestWKNavigationDelegateHostApi.setup(null); + TestWKWebViewConfigurationHostApi.setup(null); + TestWKWebViewHostApi.setup(null); }); test('create', () async { @@ -349,6 +368,31 @@ void main() { instanceManager.getInstanceId(navigationDelegate), )); }); + + test('setDidFinishNavigation', () async { + final Completer> argsCompleter = + Completer>(); + + navigationDelegate.setDidFinishNavigation( + (WKWebView webView, String? url) { + argsCompleter.complete([webView, url]); + }, + ); + + final int functionInstanceId = + verify(mockPlatformHostApi.setDidFinishNavigation( + instanceManager.getInstanceId(navigationDelegate), + captureAny, + )).captured.single as int; + + flutterApis.navigationDelegateFlutterApi.didFinishNavigation( + functionInstanceId, + instanceManager.getInstanceId(webView)!, + 'url', + ); + + expect(argsCompleter.future, completion([webView, 'url'])); + }); }); group('$WKWebView', () { 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 9629e2e62636..3758c9e573a4 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 @@ -33,6 +33,12 @@ class MockTestWKNavigationDelegateHostApi extends _i1.Mock void create(int? instanceId) => super.noSuchMethod(Invocation.method(#create, [instanceId]), returnValueForMissingStub: null); + @override + void setDidFinishNavigation(int? instanceId, int? functionInstanceId) => + super.noSuchMethod( + Invocation.method( + #setDidFinishNavigation, [instanceId, functionInstanceId]), + returnValueForMissingStub: null); } /// A class which mocks [TestWKPreferencesHostApi]. From d07eaffb0d76ec128fa3e9746b1b624eda3483e2 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 14 Apr 2022 20:07:17 -0700 Subject: [PATCH 154/844] [webview_flutter_wkwebview] Implements the `HostApis` and methods for the `CookieManager`. (#5244) --- .../lib/src/common/web_kit.pigeon.dart | 181 ++++++++++++++++++ .../lib/src/web_kit/web_kit.dart | 40 +++- .../lib/src/web_kit/web_kit_api_impls.dart | 115 +++++++++++ .../pigeons/web_kit.dart | 46 +++++ .../test/src/common/test_web_kit.pigeon.dart | 109 +++++++++++ .../test/src/foundation/foundation_test.dart | 2 +- .../src/foundation/foundation_test.mocks.dart | 2 +- .../test/src/ui_kit/ui_kit_test.dart | 4 +- .../test/src/web_kit/web_kit_test.dart | 90 ++++++++- .../test/src/web_kit/web_kit_test.mocks.dart | 28 ++- .../test/src/web_kit_cookie_manager_test.dart | 3 +- .../web_kit_cookie_manager_test.mocks.dart | 27 +++ .../test/src/web_kit_webview_widget_test.dart | 12 +- 13 files changed, 629 insertions(+), 30 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart index 39f5663c1838..6395f4171f45 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart @@ -61,6 +61,23 @@ enum WKNavigationActionPolicyEnum { cancel, } +enum NSHttpCookiePropertyKeyEnum { + comment, + commentUrl, + discard, + domain, + expires, + maximumAge, + name, + originUrl, + path, + port, + sameSitePolicy, + secure, + value, + version, +} + class NSKeyValueObservingOptionsEnumData { NSKeyValueObservingOptionsEnumData({ this.value, @@ -153,6 +170,29 @@ class WKWebsiteDataTypesEnumData { } } +class NSHttpCookiePropertyKeyEnumData { + NSHttpCookiePropertyKeyEnumData({ + this.value, + }); + + NSHttpCookiePropertyKeyEnum? value; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['value'] = value == null ? null : value!.index; + return pigeonMap; + } + + static NSHttpCookiePropertyKeyEnumData decode(Object message) { + final Map pigeonMap = message as Map; + return NSHttpCookiePropertyKeyEnumData( + value: pigeonMap['value'] != null + ? NSHttpCookiePropertyKeyEnum.values[pigeonMap['value']! as int] + : null, + ); + } +} + class NSUrlRequestData { NSUrlRequestData({ required this.url, @@ -221,6 +261,28 @@ class WKUserScriptData { } } +class NSHttpCookieData { + NSHttpCookieData({ + required this.properties, + }); + + Map properties; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['properties'] = properties; + return pigeonMap; + } + + static NSHttpCookieData decode(Object message) { + final Map pigeonMap = message as Map; + return NSHttpCookieData( + properties: (pigeonMap['properties'] as Map?)! + .cast(), + ); + } +} + class _WKWebsiteDataStoreHostApiCodec extends StandardMessageCodec { const _WKWebsiteDataStoreHostApiCodec(); @override @@ -283,6 +345,31 @@ class WKWebsiteDataStoreHostApi { } } + Future createDefaultDataStore(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createDefaultDataStore', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + Future removeDataOfTypes( int arg_instanceId, List arg_dataTypes, @@ -1787,3 +1874,97 @@ class WKUIDelegateHostApi { } } } + +class _WKHttpCookieStoreHostApiCodec extends StandardMessageCodec { + const _WKHttpCookieStoreHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is NSHttpCookieData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is NSHttpCookiePropertyKeyEnumData) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return NSHttpCookieData.decode(readValue(buffer)!); + + case 129: + return NSHttpCookiePropertyKeyEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +class WKHttpCookieStoreHostApi { + /// Constructor for [WKHttpCookieStoreHostApi]. 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. + WKHttpCookieStoreHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _WKHttpCookieStoreHostApiCodec(); + + Future createFromWebsiteDataStore( + int arg_instanceId, int arg_websiteDataStoreInstanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKHttpCookieStoreHostApi.createFromWebsiteDataStore', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_websiteDataStoreInstanceId]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future setCookie( + int arg_instanceId, NSHttpCookieData arg_cookie) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKHttpCookieStoreHostApi.setCookie', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_instanceId, arg_cookie]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} 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 64a45ada265f..ab63db67bba1 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 @@ -251,7 +251,11 @@ class WKWebsiteDataStore { ); factory WKWebsiteDataStore._defaultDataStore() { - throw UnimplementedError(); + final WKWebsiteDataStore websiteDataStore = WKWebsiteDataStore._(); + websiteDataStore._websiteDataStoreApi.createDefaultDataStoreForInstances( + websiteDataStore, + ); + return websiteDataStore; } /// Constructs a [WKWebsiteDataStore] that is owned by [configuration]. @@ -301,20 +305,38 @@ class WKWebsiteDataStore { /// An object that manages the HTTP cookies associated with a particular web view. /// /// Wraps [WKHTTPCookieStore](https://developer.apple.com/documentation/webkit/wkhttpcookiestore?language=objc). -class WKHttpCookieStore { +class WKHttpCookieStore extends NSObject { + WKHttpCookieStore._({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _httpCookieStoreApi = WKHttpCookieStoreHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + /// Constructs a [WKHttpCookieStore] that is owned by [dataStore]. @visibleForTesting - WKHttpCookieStore.fromWebsiteDataStore( - // TODO(bparrishMines): Remove ignore on implementation. - // ignore: avoid_unused_constructor_parameters - WKWebsiteDataStore dataStore, - ) { - throw UnimplementedError(); + factory WKHttpCookieStore.fromWebsiteDataStore( + WKWebsiteDataStore dataStore, { + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) { + final WKHttpCookieStore cookieStore = WKHttpCookieStore._( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + cookieStore._httpCookieStoreApi.createFromWebsiteDataStoreForInstances( + cookieStore, + dataStore, + ); + return cookieStore; } + final WKHttpCookieStoreHostApiImpl _httpCookieStoreApi; + /// Adds a cookie to the cookie store. Future setCookie(NSHttpCookie cookie) { - throw UnimplementedError(); + return _httpCookieStoreApi.setCookieForInsances(this, cookie); } } 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 d33407905375..b970ab26bcc7 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 @@ -45,6 +45,73 @@ Iterable _toWKWebsiteDataTypesEnumData( }); } +extension _NSHttpCookieConverter on NSHttpCookie { + NSHttpCookieData toNSHttpCookieData() { + return NSHttpCookieData( + properties: properties.map( + (NSHttpCookiePropertyKey key, Object value) { + return MapEntry( + key.toNSHttpCookiePropertyKeyEnumData(), + value.toString(), + ); + }, + ), + ); + } +} + +extension _NSHttpCookiePropertyKeyConverter on NSHttpCookiePropertyKey { + NSHttpCookiePropertyKeyEnumData toNSHttpCookiePropertyKeyEnumData() { + late final NSHttpCookiePropertyKeyEnum value; + switch (this) { + case NSHttpCookiePropertyKey.comment: + value = NSHttpCookiePropertyKeyEnum.comment; + break; + case NSHttpCookiePropertyKey.commentUrl: + value = NSHttpCookiePropertyKeyEnum.commentUrl; + break; + case NSHttpCookiePropertyKey.discard: + value = NSHttpCookiePropertyKeyEnum.discard; + break; + case NSHttpCookiePropertyKey.domain: + value = NSHttpCookiePropertyKeyEnum.domain; + break; + case NSHttpCookiePropertyKey.expires: + value = NSHttpCookiePropertyKeyEnum.expires; + break; + case NSHttpCookiePropertyKey.maximumAge: + value = NSHttpCookiePropertyKeyEnum.maximumAge; + break; + case NSHttpCookiePropertyKey.name: + value = NSHttpCookiePropertyKeyEnum.name; + break; + case NSHttpCookiePropertyKey.originUrl: + value = NSHttpCookiePropertyKeyEnum.originUrl; + break; + case NSHttpCookiePropertyKey.path: + value = NSHttpCookiePropertyKeyEnum.path; + break; + case NSHttpCookiePropertyKey.port: + value = NSHttpCookiePropertyKeyEnum.port; + break; + case NSHttpCookiePropertyKey.sameSitePolicy: + value = NSHttpCookiePropertyKeyEnum.sameSitePolicy; + break; + case NSHttpCookiePropertyKey.secure: + value = NSHttpCookiePropertyKeyEnum.secure; + break; + case NSHttpCookiePropertyKey.value: + value = NSHttpCookiePropertyKeyEnum.value; + break; + case NSHttpCookiePropertyKey.version: + value = NSHttpCookiePropertyKeyEnum.version; + break; + } + + return NSHttpCookiePropertyKeyEnumData(value: value); + } +} + extension _WKUserScriptInjectionTimeConverter on WKUserScriptInjectionTime { WKUserScriptInjectionTimeEnumData toWKUserScriptInjectionTimeEnumData() { late final WKUserScriptInjectionTimeEnum value; @@ -178,6 +245,16 @@ class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi { } } + /// Calls [createDefaultDataStore] with the ids of the provided object instances. + Future createDefaultDataStoreForInstances( + WKWebsiteDataStore instance, + ) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await createDefaultDataStore(instanceId); + } + } + /// Calls [removeDataOfTypes] with the ids of the provided object instances. Future removeDataOfTypesForInstances( WKWebsiteDataStore instance, @@ -251,6 +328,44 @@ class WKPreferencesHostApiImpl extends WKPreferencesHostApi { } } +/// Host api implementation for [WKHttpCookieStore]. +class WKHttpCookieStoreHostApiImpl extends WKHttpCookieStoreHostApi { + /// Constructs a [WKHttpCookieStoreHostApiImpl]. + WKHttpCookieStoreHostApiImpl({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? InstanceManager.instance, + super(binaryMessenger: binaryMessenger); + + /// Maintains instances stored to communicate with Objective-C objects. + final InstanceManager instanceManager; + + /// Calls [createFromWebsiteDataStore] with the ids of the provided object instances. + Future createFromWebsiteDataStoreForInstances( + WKHttpCookieStore instance, + WKWebsiteDataStore dataStore, + ) async { + final int? instanceId = instanceManager.tryAddInstance(instance); + if (instanceId != null) { + await createFromWebsiteDataStore( + instanceId, + instanceManager.getInstanceId(dataStore)!, + ); + } + } + + /// Calls [setCookie] with the ids of the provided object instances. + Future setCookieForInsances( + WKHttpCookieStore instance, + NSHttpCookie cookie, + ) { + return setCookie( + instanceManager.getInstanceId(instance)!, + cookie.toNSHttpCookieData(), + ); + } +} + /// Host api implementation for [WKUserContentController]. class WKUserContentControllerHostApiImpl extends WKUserContentControllerHostApi { 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 b0a239fcd146..23d8712b0486 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -117,6 +117,30 @@ class WKNavigationActionPolicyEnumData { late WKNavigationActionPolicyEnum? value; } +/// Mirror of NSHTTPCookiePropertyKey. +/// +/// See https://developer.apple.com/documentation/foundation/nshttpcookiepropertykey. +enum NSHttpCookiePropertyKeyEnum { + comment, + commentUrl, + discard, + domain, + expires, + maximumAge, + name, + originUrl, + path, + port, + sameSitePolicy, + secure, + value, + version, +} + +class NSHttpCookiePropertyKeyEnumData { + late NSHttpCookiePropertyKeyEnum? value; +} + /// Mirror of NSURLRequest. /// /// See https://developer.apple.com/documentation/foundation/nsurlrequest?language=objc. @@ -168,6 +192,13 @@ class WKScriptMessageData { late Object? body; } +/// Mirror of NSHttpCookieData. +/// +/// See https://developer.apple.com/documentation/foundation/nshttpcookie?language=objc. +class NSHttpCookieData { + late Map properties; +} + /// Mirror of WKWebsiteDataStore. /// /// See https://developer.apple.com/documentation/webkit/wkwebsitedatastore?language=objc. @@ -178,6 +209,8 @@ abstract class WKWebsiteDataStoreHostApi { int configurationInstanceId, ); + void createDefaultDataStore(int instanceId); + @async bool removeDataOfTypes( int instanceId, @@ -370,3 +403,16 @@ abstract class WKWebViewHostApi { abstract class WKUIDelegateHostApi { void create(int instanceId); } + +/// Mirror of WKHttpCookieStore. +/// +/// See https://developer.apple.com/documentation/webkit/wkhttpcookiestore?language=objc. +@HostApi(dartHostTestHandler: 'TestWKHttpCookieStoreHostApi') +abstract class WKHttpCookieStoreHostApi { + void createFromWebsiteDataStore( + int instanceId, + int websiteDataStoreInstanceId, + ); + + void setCookie(int instanceId, NSHttpCookieData cookie); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart index 06a7b7a0a4c0..67ae8882cbd3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart @@ -44,6 +44,7 @@ abstract class TestWKWebsiteDataStoreHostApi { void createFromWebViewConfiguration( int instanceId, int configurationInstanceId); + void createDefaultDataStore(int instanceId); Future removeDataOfTypes( int instanceId, List dataTypes, @@ -74,6 +75,26 @@ abstract class TestWKWebsiteDataStoreHostApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createDefaultDataStore', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createDefaultDataStore 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.WKWebsiteDataStoreHostApi.createDefaultDataStore was null, expected non-null int.'); + api.createDefaultDataStore(arg_instanceId!); + return {}; + }); + } + } { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes', @@ -1330,3 +1351,91 @@ abstract class TestWKUIDelegateHostApi { } } } + +class _TestWKHttpCookieStoreHostApiCodec extends StandardMessageCodec { + const _TestWKHttpCookieStoreHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is NSHttpCookieData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is NSHttpCookiePropertyKeyEnumData) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return NSHttpCookieData.decode(readValue(buffer)!); + + case 129: + return NSHttpCookiePropertyKeyEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestWKHttpCookieStoreHostApi { + static const MessageCodec codec = + _TestWKHttpCookieStoreHostApiCodec(); + + void createFromWebsiteDataStore( + int instanceId, int websiteDataStoreInstanceId); + void setCookie(int instanceId, NSHttpCookieData cookie); + static void setup(TestWKHttpCookieStoreHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKHttpCookieStoreHostApi.createFromWebsiteDataStore', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKHttpCookieStoreHostApi.createFromWebsiteDataStore 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.WKHttpCookieStoreHostApi.createFromWebsiteDataStore was null, expected non-null int.'); + final int? arg_websiteDataStoreInstanceId = (args[1] as int?); + assert(arg_websiteDataStoreInstanceId != null, + 'Argument for dev.flutter.pigeon.WKHttpCookieStoreHostApi.createFromWebsiteDataStore was null, expected non-null int.'); + api.createFromWebsiteDataStore( + arg_instanceId!, arg_websiteDataStoreInstanceId!); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKHttpCookieStoreHostApi.setCookie', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKHttpCookieStoreHostApi.setCookie 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.WKHttpCookieStoreHostApi.setCookie was null, expected non-null int.'); + final NSHttpCookieData? arg_cookie = (args[1] as NSHttpCookieData?); + assert(arg_cookie != null, + 'Argument for dev.flutter.pigeon.WKHttpCookieStoreHostApi.setCookie was null, expected non-null NSHttpCookieData.'); + api.setCookie(arg_instanceId!, arg_cookie!); + return {}; + }); + } + } + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart index a08680940bcc..007c2bc32252 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart @@ -25,7 +25,7 @@ void main() { instanceManager = InstanceManager(); }); - group('$NSObject', () { + group('NSObject', () { late MockTestNSObjectHostApi mockPlatformHostApi; late NSObject object; 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 b8552c5d8157..7bd208eeac05 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 @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/test/src/foundation/foundation_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart. // Do not manually edit this file. import 'package:mockito/mockito.dart' as _i1; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart index b6c50609552f..7db190f8192c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart @@ -31,7 +31,7 @@ void main() { instanceManager = InstanceManager(); }); - group('$UIScrollView', () { + group('UIScrollView', () { late MockTestUIScrollViewHostApi mockPlatformHostApi; late UIScrollView scrollView; @@ -87,7 +87,7 @@ void main() { }); }); - group('$UIView', () { + group('UIView', () { late MockTestUIViewHostApi mockPlatformHostApi; late UIView view; 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 00a3a31a6957..8564bf889c2f 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 @@ -17,6 +17,7 @@ import '../common/test_web_kit.pigeon.dart'; import 'web_kit_test.mocks.dart'; @GenerateMocks([ + TestWKHttpCookieStoreHostApi, TestWKNavigationDelegateHostApi, TestWKPreferencesHostApi, TestWKScriptMessageHandlerHostApi, @@ -39,7 +40,7 @@ void main() { WebKitFlutterApis.instance = flutterApis; }); - group('$WKWebsiteDataStore', () { + group('WKWebsiteDataStore', () { late MockTestWKWebsiteDataStoreHostApi mockPlatformHostApi; late WKWebsiteDataStore websiteDataStore; @@ -75,6 +76,16 @@ void main() { )); }); + test('createDefaultDataStore', () { + final WKWebsiteDataStore defaultDataStore = + WKWebsiteDataStore.defaultDataStore; + verify( + mockPlatformHostApi.createDefaultDataStore( + InstanceManager.instance.getInstanceId(defaultDataStore), + ), + ); + }); + test('removeDataOfTypes', () { when(mockPlatformHostApi.removeDataOfTypes( any, @@ -102,7 +113,70 @@ void main() { }); }); - group('$WKScriptMessageHandler', () { + group('WKHttpCookieStore', () { + late MockTestWKHttpCookieStoreHostApi mockPlatformHostApi; + + late WKHttpCookieStore httpCookieStore; + + late WKWebsiteDataStore websiteDataStore; + + setUp(() { + mockPlatformHostApi = MockTestWKHttpCookieStoreHostApi(); + TestWKHttpCookieStoreHostApi.setup(mockPlatformHostApi); + + TestWKWebViewConfigurationHostApi.setup( + MockTestWKWebViewConfigurationHostApi(), + ); + TestWKWebsiteDataStoreHostApi.setup( + MockTestWKWebsiteDataStoreHostApi(), + ); + + websiteDataStore = WKWebsiteDataStore.fromWebViewConfiguration( + WKWebViewConfiguration(instanceManager: instanceManager), + instanceManager: instanceManager, + ); + + httpCookieStore = WKHttpCookieStore.fromWebsiteDataStore( + websiteDataStore, + instanceManager: instanceManager, + ); + }); + + tearDown(() { + TestWKHttpCookieStoreHostApi.setup(null); + TestWKWebsiteDataStoreHostApi.setup(null); + TestWKWebViewConfigurationHostApi.setup(null); + }); + + test('createFromWebsiteDataStore', () { + verify(mockPlatformHostApi.createFromWebsiteDataStore( + instanceManager.getInstanceId(httpCookieStore), + instanceManager.getInstanceId(websiteDataStore), + )); + }); + + test('setCookie', () async { + await httpCookieStore.setCookie( + const NSHttpCookie.withProperties({ + NSHttpCookiePropertyKey.comment: 'aComment', + })); + + final NSHttpCookieData cookie = verify( + mockPlatformHostApi.setCookie( + instanceManager.getInstanceId(httpCookieStore)!, + captureAny, + ), + ).captured.single as NSHttpCookieData; + + expect( + cookie.properties.entries.single.key!.value, + NSHttpCookiePropertyKeyEnum.comment, + ); + expect(cookie.properties.entries.single.value, 'aComment'); + }); + }); + + group('WKScriptMessageHandler', () { late MockTestWKScriptMessageHandlerHostApi mockPlatformHostApi; late WKScriptMessageHandler scriptMessageHandler; @@ -127,7 +201,7 @@ void main() { }); }); - group('$WKPreferences', () { + group('WKPreferences', () { late MockTestWKPreferencesHostApi mockPlatformHostApi; late WKPreferences preferences; @@ -172,7 +246,7 @@ void main() { }); }); - group('$WKUserContentController', () { + group('WKUserContentController', () { late MockTestWKUserContentControllerHostApi mockPlatformHostApi; late WKUserContentController userContentController; @@ -260,7 +334,7 @@ void main() { }); }); - group('$WKWebViewConfiguration', () { + group('WKWebViewConfiguration', () { late MockTestWKWebViewConfigurationHostApi mockPlatformHostApi; late WKWebViewConfiguration webViewConfiguration; @@ -332,7 +406,7 @@ void main() { }); }); - group('$WKNavigationDelegate', () { + group('WKNavigationDelegate', () { late MockTestWKNavigationDelegateHostApi mockPlatformHostApi; late WKWebView webView; @@ -395,7 +469,7 @@ void main() { }); }); - group('$WKWebView', () { + group('WKWebView', () { late MockTestWKWebViewHostApi mockPlatformHostApi; late WKWebViewConfiguration webViewConfiguration; @@ -558,7 +632,7 @@ void main() { }); }); - group('$WKUIDelegate', () { + group('WKUIDelegate', () { late MockTestWKUIDelegateHostApi mockPlatformHostApi; late WKUIDelegate uiDelegate; 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 3758c9e573a4..67c06286185c 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 @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. +// in webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. // Do not manually edit this file. import 'dart:async' as _i4; @@ -20,6 +20,28 @@ import '../common/test_web_kit.pigeon.dart' as _i2; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types +/// A class which mocks [TestWKHttpCookieStoreHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestWKHttpCookieStoreHostApi extends _i1.Mock + implements _i2.TestWKHttpCookieStoreHostApi { + MockTestWKHttpCookieStoreHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void createFromWebsiteDataStore( + int? instanceId, int? websiteDataStoreInstanceId) => + super.noSuchMethod( + Invocation.method(#createFromWebsiteDataStore, + [instanceId, websiteDataStoreInstanceId]), + returnValueForMissingStub: null); + @override + void setCookie(int? instanceId, _i3.NSHttpCookieData? cookie) => + super.noSuchMethod(Invocation.method(#setCookie, [instanceId, cookie]), + returnValueForMissingStub: null); +} + /// A class which mocks [TestWKNavigationDelegateHostApi]. /// /// See the documentation for Mockito's code generation for more information. @@ -281,6 +303,10 @@ class MockTestWKWebsiteDataStoreHostApi extends _i1.Mock [instanceId, configurationInstanceId]), returnValueForMissingStub: null); @override + void createDefaultDataStore(int? instanceId) => super.noSuchMethod( + Invocation.method(#createDefaultDataStore, [instanceId]), + returnValueForMissingStub: null); + @override _i4.Future removeDataOfTypes( int? instanceId, List<_i3.WKWebsiteDataTypesEnumData?>? dataTypes, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart index 4529316d3159..5238c0bb2c56 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart @@ -11,7 +11,6 @@ import 'package:webview_flutter_platform_interface/webview_flutter_platform_inte import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; import 'package:webview_flutter_wkwebview/src/web_kit_cookie_manager.dart'; -import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart'; import 'web_kit_cookie_manager_test.mocks.dart'; @@ -22,7 +21,7 @@ import 'web_kit_cookie_manager_test.mocks.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - group('$WebKitWebViewWidget', () { + group('WebKitWebViewWidget', () { late MockWKWebsiteDataStore mockWebsiteDataStore; late MockWKHttpCookieStore mockWKHttpCookieStore; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart index 88025d3d9465..a85c57f7bdb3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart @@ -35,6 +35,33 @@ class MockWKHttpCookieStore extends _i1.Mock implements _i2.WKHttpCookieStore { (super.noSuchMethod(Invocation.method(#setCookie, [cookie]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i3.Future); + @override + _i3.Future addObserver(_i4.NSObject? observer, + {String? keyPath, Set<_i4.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); + @override + _i3.Future removeObserver(_i4.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); + @override + _i3.Future dispose() => + (super.noSuchMethod(Invocation.method(#dispose, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); + @override + _i3.Future setObserveValue( + void Function( + String, _i4.NSObject, Map<_i4.NSKeyValueChangeKey, Object?>)? + observeValue) => + (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); } /// A class which mocks [WKWebsiteDataStore]. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 86ee1a99b949..8c104bdb3ee2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -37,7 +37,7 @@ import 'web_kit_webview_widget_test.mocks.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - group('$WebKitWebViewWidget', () { + group('WebKitWebViewWidget', () { late MockWKWebView mockWebView; late MockWebViewWidgetProxy mockWebViewWidgetProxy; late MockWKUserContentController mockUserContentController; @@ -135,7 +135,7 @@ void main() { verify(mockWebView.loadRequest(request)); }); - group('$CreationParams', () { + group('CreationParams', () { testWidgets('initialUrl', (WidgetTester tester) async { await buildWidget( tester, @@ -258,7 +258,7 @@ void main() { expect(javaScriptChannels[3], 'b'); }); - group('$WebSettings', () { + group('WebSettings', () { testWidgets('javascriptMode', (WidgetTester tester) async { await buildWidget( tester, @@ -427,7 +427,7 @@ void main() { }); }); - group('$WebKitWebViewPlatformController', () { + group('WebKitWebViewPlatformController', () { testWidgets('loadFile', (WidgetTester tester) async { await buildWidget(tester); @@ -901,7 +901,7 @@ void main() { }); }); - group('$WebViewPlatformCallbacksHandler', () { + group('WebViewPlatformCallbacksHandler', () { testWidgets('onPageStarted', (WidgetTester tester) async { await buildWidget(tester); @@ -1074,7 +1074,7 @@ void main() { }); }); - group('$JavascriptChannelRegistry', () { + group('JavascriptChannelRegistry', () { testWidgets('onJavascriptChannelMessage', (WidgetTester tester) async { when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn( MockWKScriptMessageHandler(), From c0bc7ccaf7142db4b181782f1cea3a58f0f8eea4 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 15 Apr 2022 14:01:46 -0400 Subject: [PATCH 155/844] [flutter_plugin_tools] Preserve Dart SDK version in all-plugins-app (#5281) Fixes `all-plugins-app` to preserve the original application's Dart SDK version to avoid changing language feature opt-ins that the template may rely on. --- .ci/flutter_master.version | 2 +- script/tool/CHANGELOG.md | 3 ++ .../src/create_all_plugins_app_command.dart | 27 +++++++++++++---- .../create_all_plugins_app_command_test.dart | 30 ++++++++++++++++--- 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 103b05611de2..d6feb755f3e2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2b8333240d38cf72b8e13580e24d01f5a188e26c +329ceaef666ff8cebd4dc36325ab6bfebdc7c8ef diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index df76e4819e63..6e632c67684d 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -2,6 +2,9 @@ - Adds a new `readme-check` command. - Updates `publish-plugin` command documentation. +- Fixes `all-plugins-app` to preserve the original application's Dart SDK + version to avoid changing language feature opt-ins that the template may + rely on. ## 0.8.2 diff --git a/script/tool/lib/src/create_all_plugins_app_command.dart b/script/tool/lib/src/create_all_plugins_app_command.dart index 82f29bd501f3..6b44ab788786 100644 --- a/script/tool/lib/src/create_all_plugins_app_command.dart +++ b/script/tool/lib/src/create_all_plugins_app_command.dart @@ -147,6 +147,19 @@ class CreateAllPluginsAppCommand extends PluginCommand { } Future _genPubspecWithAllPlugins() async { + final RepositoryPackage buildAllApp = RepositoryPackage(appDirectory); + // Read the old pubspec file's Dart SDK version, in order to preserve it + // in the new file. The template sometimes relies on having opted in to + // specific language features via SDK version, so using a different one + // can cause compilation failures. + final Pubspec originalPubspec = buildAllApp.parsePubspec(); + const String dartSdkKey = 'sdk'; + final VersionConstraint dartSdkConstraint = + originalPubspec.environment?[dartSdkKey] ?? + VersionConstraint.compatibleWith( + Version.parse('2.12.0'), + ); + final Map pluginDeps = await _getValidPathDependencies(); final Pubspec pubspec = Pubspec( @@ -154,9 +167,7 @@ class CreateAllPluginsAppCommand extends PluginCommand { description: 'Flutter app containing all 1st party plugins.', version: Version.parse('1.0.0+1'), environment: { - 'sdk': VersionConstraint.compatibleWith( - Version.parse('2.12.0'), - ), + dartSdkKey: dartSdkConstraint, }, dependencies: { 'flutter': SdkDependency('flutter'), @@ -166,8 +177,7 @@ class CreateAllPluginsAppCommand extends PluginCommand { }, dependencyOverrides: pluginDeps, ); - final File pubspecFile = appDirectory.childFile('pubspec.yaml'); - pubspecFile.writeAsStringSync(_pubspecToString(pubspec)); + buildAllApp.pubspecFile.writeAsStringSync(_pubspecToString(pubspec)); } Future> _getValidPathDependencies() async { @@ -212,7 +222,12 @@ dev_dependencies:${_pubspecMapString(pubspec.devDependencies)} for (final MapEntry entry in values.entries) { buffer.writeln(); if (entry.value is VersionConstraint) { - buffer.write(' ${entry.key}: ${entry.value}'); + String value = entry.value.toString(); + // Range constraints require quoting. + if (value.startsWith('>') || value.startsWith('<')) { + value = "'$value'"; + } + buffer.write(' ${entry.key}: $value'); } else if (entry.value is SdkDependency) { final SdkDependency dep = entry.value as SdkDependency; buffer.write(' ${entry.key}: \n sdk: ${dep.sdk}'); diff --git a/script/tool/test/create_all_plugins_app_command_test.dart b/script/tool/test/create_all_plugins_app_command_test.dart index 0066cc53f61a..917adca020d4 100644 --- a/script/tool/test/create_all_plugins_app_command_test.dart +++ b/script/tool/test/create_all_plugins_app_command_test.dart @@ -2,10 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io' as io; + import 'package:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:file/local.dart'; +import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:flutter_plugin_tools/src/create_all_plugins_app_command.dart'; +import 'package:platform/platform.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:test/test.dart'; import 'util.dart'; @@ -76,14 +81,31 @@ void main() { ])); }); - test('pubspec is compatible with null-safe app code', () async { + test('pubspec preserves existing Dart SDK version', () async { + const String baselineProjectName = 'baseline'; + final Directory baselineProjectDirectory = + testRoot.childDirectory(baselineProjectName); + io.Process.runSync( + getFlutterCommand(const LocalPlatform()), + [ + 'create', + '--template=app', + '--project-name=$baselineProjectName', + baselineProjectDirectory.path, + ], + ); + final Pubspec baselinePubspec = + RepositoryPackage(baselineProjectDirectory).parsePubspec(); + createFakePlugin('plugina', packagesDir); await runCapturingPrint(runner, ['all-plugins-app']); - final String pubspec = - command.appDirectory.childFile('pubspec.yaml').readAsStringSync(); + final Pubspec generatedPubspec = + RepositoryPackage(command.appDirectory).parsePubspec(); - expect(pubspec, contains(RegExp('sdk:\\s*(?:["\']>=|[^])2\\.12\\.'))); + const String dartSdkKey = 'sdk'; + expect(generatedPubspec.environment?[dartSdkKey], + baselinePubspec.environment?[dartSdkKey]); }); test('handles --output-dir', () async { From 02ac515c174b72de810d2c38b603c6b95117ceb7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 15 Apr 2022 16:19:09 -0400 Subject: [PATCH 156/844] Roll Flutter from 329ceaef666f to ec8289c3f90c (26 revisions) (#5283) --- .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 d6feb755f3e2..1234b94e1803 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -329ceaef666ff8cebd4dc36325ab6bfebdc7c8ef +ec8289c3f90c283a8d4ae6a6e2f817baded1776c From 78bf77f604f26967c8851f840ea41bea5ecd7e06 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 16 Apr 2022 13:09:10 -0400 Subject: [PATCH 157/844] Roll Flutter from ec8289c3f90c to 44be0b84ba54 (2 revisions) (#5284) --- .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 1234b94e1803..05c73f193285 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ec8289c3f90c283a8d4ae6a6e2f817baded1776c +44be0b84ba54a1a0a99304a4793bb30b340b7c15 From 8ffa1bf606c151d853797ca952e69032d934efa8 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 16 Apr 2022 14:14:06 -0400 Subject: [PATCH 158/844] Roll Flutter from 44be0b84ba54 to aa5d7b6972ee (12 revisions) (#5286) --- .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 05c73f193285..4dbcb70f91c4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -44be0b84ba54a1a0a99304a4793bb30b340b7c15 +aa5d7b6972ee5be596c15b83d39f7d3dde6ad939 From c9addb447354ce45c977b28acb758a9df6897a63 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 17 Apr 2022 16:24:09 -0400 Subject: [PATCH 159/844] Roll Flutter from aa5d7b6972ee to f4875ae865e9 (1 revision) (#5289) --- .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 4dbcb70f91c4..048f3562ae90 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -aa5d7b6972ee5be596c15b83d39f7d3dde6ad939 +f4875ae865e9d42644a93bbab2164fc7baac4ff7 From c581be43333c4d5f649ad7ab928437e88e269c85 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Mon, 18 Apr 2022 12:24:08 -0700 Subject: [PATCH 160/844] [video_player_web] Stop buffering when browser canPlayThrough. (#5068) --- .../video_player_web/CHANGELOG.md | 8 + .../example/integration_test/utils.dart | 16 ++ .../integration_test/video_player_test.dart | 195 ++++++++++++++ .../video_player_web_test.dart | 59 +++- .../lib/src/video_player.dart | 254 ++++++++++++++++++ .../lib/video_player_web.dart | 247 +++-------------- .../video_player_web/pubspec.yaml | 2 +- 7 files changed, 557 insertions(+), 224 deletions(-) create mode 100644 packages/video_player/video_player_web/example/integration_test/utils.dart create mode 100644 packages/video_player/video_player_web/example/integration_test/video_player_test.dart create mode 100644 packages/video_player/video_player_web/lib/src/video_player.dart diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 1cd428c4deea..3310660137a2 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,3 +1,11 @@ +## 2.0.8 + +* Ensures `buffering` state is only removed when the browser reports enough data + has been buffered so that the video can likely play through without stopping + (`onCanPlayThrough`). Issue [#94630](https://github.com/flutter/flutter/issues/94630). +* Improves testability of the `_VideoPlayer` private class. +* Ensures that tests that listen to a Stream fail "fast" (1 second max timeout). + ## 2.0.7 * Internal code cleanup for stricter analysis options. 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 new file mode 100644 index 000000000000..b0118514053a --- /dev/null +++ b/packages/video_player/video_player_web/example/integration_test/utils.dart @@ -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. + +// Returns the URL to load an asset from this example app as a network source. +// +// TODO(stuartmorgan): Convert this to a local `HttpServer` that vends the +// assets directly, https://github.com/flutter/flutter/issues/95420 +String getUrlForAssetAsNetworkSource(String assetKey) { + return 'https://github.com/flutter/plugins/blob/' + // This hash can be rolled forward to pick up newly-added assets. + 'cb381ced070d356799dddf24aca38ce0579d3d7b' + '/packages/video_player/video_player/example/' + '$assetKey' + '?raw=true'; +} 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 new file mode 100644 index 000000000000..41aba9792e23 --- /dev/null +++ b/packages/video_player/video_player_web/example/integration_test/video_player_test.dart @@ -0,0 +1,195 @@ +// 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: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/video_player.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('VideoPlayer', () { + late html.VideoElement video; + + setUp(() { + // Never set "src" on the video, so this test doesn't hit the network! + video = html.VideoElement() + ..controls = true + ..setAttribute('playsinline', 'false'); + }); + + testWidgets('fixes critical video element config', (WidgetTester _) async { + VideoPlayer(videoElement: video).initialize(); + + expect(video.controls, isFalse, + reason: 'Video is controlled through code'); + expect(video.getAttribute('autoplay'), 'false', + reason: 'Cannot autoplay on the web'); + expect(video.getAttribute('playsinline'), 'true', + reason: 'Needed by safari iOS'); + }); + + testWidgets('setVolume', (WidgetTester tester) async { + final VideoPlayer player = VideoPlayer(videoElement: video)..initialize(); + + player.setVolume(0); + + expect(video.volume, isZero, reason: 'Volume should be zero'); + expect(video.muted, isTrue, reason: 'muted attribute should be true'); + + expect(() { + player.setVolume(-0.0001); + }, throwsAssertionError, reason: 'Volume cannot be < 0'); + + expect(() { + player.setVolume(1.0001); + }, throwsAssertionError, reason: 'Volume cannot be > 1'); + }); + + testWidgets('setPlaybackSpeed', (WidgetTester tester) async { + final VideoPlayer player = VideoPlayer(videoElement: video)..initialize(); + + expect(() { + player.setPlaybackSpeed(-1); + }, throwsAssertionError, reason: 'Playback speed cannot be < 0'); + + expect(() { + player.setPlaybackSpeed(0); + }, throwsAssertionError, reason: 'Playback speed cannot be == 0'); + }); + + testWidgets('seekTo', (WidgetTester tester) async { + final VideoPlayer player = VideoPlayer(videoElement: video)..initialize(); + + expect(() { + player.seekTo(const Duration(seconds: -1)); + }, throwsAssertionError, reason: 'Cannot seek into negative numbers'); + }); + + // The events tested in this group do *not* represent the actual sequence + // of events from a real "video" element. They're crafted to test the + // behavior of the VideoPlayer in different states with different events. + group('events', () { + late StreamController streamController; + late VideoPlayer player; + late Stream timedStream; + + final Set bufferingEvents = { + VideoEventType.bufferingStart, + VideoEventType.bufferingEnd, + }; + + setUp(() { + streamController = StreamController(); + player = + VideoPlayer(videoElement: video, eventController: streamController) + ..initialize(); + + // This stream will automatically close after 100 ms without seeing any events + timedStream = streamController.stream.timeout( + const Duration(milliseconds: 100), + onTimeout: (EventSink sink) { + sink.close(); + }, + ); + }); + + testWidgets('buffering dispatches only when it changes', + (WidgetTester tester) async { + // Take all the "buffering" events that we see during the next few seconds + final Future> stream = timedStream + .where( + (VideoEvent event) => bufferingEvents.contains(event.eventType)) + .map((VideoEvent event) => + event.eventType == VideoEventType.bufferingStart) + .toList(); + + // Simulate some events coming from the player... + player.setBuffering(true); + player.setBuffering(true); + player.setBuffering(true); + player.setBuffering(false); + player.setBuffering(false); + player.setBuffering(true); + player.setBuffering(false); + player.setBuffering(true); + player.setBuffering(false); + + final List events = await stream; + + expect(events, hasLength(6)); + expect(events, [true, false, true, false, true, false]); + }); + + testWidgets('canplay event does not change buffering state', + (WidgetTester tester) async { + // Take all the "buffering" events that we see during the next few seconds + final Future> stream = timedStream + .where( + (VideoEvent event) => bufferingEvents.contains(event.eventType)) + .map((VideoEvent event) => + event.eventType == VideoEventType.bufferingStart) + .toList(); + + player.setBuffering(true); + + // Simulate "canplay" event... + video.dispatchEvent(html.Event('canplay')); + + final List events = await stream; + + expect(events, hasLength(1)); + expect(events, [true]); + }); + + testWidgets('canplaythrough event does change buffering state', + (WidgetTester tester) async { + // Take all the "buffering" events that we see during the next few seconds + final Future> stream = timedStream + .where( + (VideoEvent event) => bufferingEvents.contains(event.eventType)) + .map((VideoEvent event) => + event.eventType == VideoEventType.bufferingStart) + .toList(); + + player.setBuffering(true); + + // Simulate "canplaythrough" event... + video.dispatchEvent(html.Event('canplaythrough')); + + final List events = await stream; + + expect(events, hasLength(2)); + expect(events, [true, false]); + }); + + 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')); + + // Take all the "initialized" events that we see during the next few seconds + final Future> stream = timedStream + .where((VideoEvent event) => + event.eventType == VideoEventType.initialized) + .toList(); + + video.dispatchEvent(html.Event('canplay')); + video.dispatchEvent(html.Event('canplay')); + video.dispatchEvent(html.Event('canplay')); + + final List events = await stream; + + expect(events, hasLength(1)); + expect(events[0].eventType, VideoEventType.initialized); + }); + }); + }); +} diff --git a/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart b/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart index 97b03642cd07..5053ea6e5b04 100644 --- a/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart +++ b/packages/video_player/video_player_web/example/integration_test/video_player_web_test.dart @@ -11,10 +11,15 @@ import 'package:integration_test/integration_test.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; import 'package:video_player_web/video_player_web.dart'; +import 'utils.dart'; + +// Use WebM to allow CI to run tests in Chromium. +const String _videoAssetKey = 'assets/Butterfly-209.webm'; + void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - group('VideoPlayer for Web', () { + group('VideoPlayerWeb plugin (hits network)', () { late Future textureId; setUp(() { @@ -23,8 +28,7 @@ void main() { .create( DataSource( sourceType: DataSourceType.network, - uri: - 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4', + uri: getUrlForAssetAsNetworkSource(_videoAssetKey), ), ) .then((int? textureId) => textureId!); @@ -38,9 +42,9 @@ void main() { expect( VideoPlayerPlatform.instance.create( DataSource( - sourceType: DataSourceType.network, - uri: - 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'), + sourceType: DataSourceType.network, + uri: getUrlForAssetAsNetworkSource(_videoAssetKey), + ), ), completion(isNonZero)); }); @@ -100,9 +104,9 @@ void main() { (WidgetTester tester) async { final int videoPlayerId = (await VideoPlayerPlatform.instance.create( DataSource( - sourceType: DataSourceType.network, - uri: - 'https://flutter.github.io/assets-for-api-docs/assets/videos/_non_existent_video.mp4'), + sourceType: DataSourceType.network, + uri: getUrlForAssetAsNetworkSource('assets/__non_existent.webm'), + ), ))!; final Stream eventStream = @@ -113,7 +117,7 @@ void main() { await VideoPlayerPlatform.instance.play(videoPlayerId); expect(() async { - await eventStream.last; + await eventStream.timeout(const Duration(seconds: 5)).last; }, throwsA(isA())); }); @@ -164,5 +168,40 @@ void main() { expect(VideoPlayerPlatform.instance.setMixWithOthers(true), completes); expect(VideoPlayerPlatform.instance.setMixWithOthers(false), completes); }); + + testWidgets('video playback lifecycle', (WidgetTester tester) async { + final int videoPlayerId = await textureId; + final Stream eventStream = + VideoPlayerPlatform.instance.videoEventsFor(videoPlayerId); + + final Future> stream = eventStream.timeout( + const Duration(seconds: 1), + onTimeout: (EventSink sink) { + sink.close(); + }, + ).toList(); + + await VideoPlayerPlatform.instance.setVolume(videoPlayerId, 0); + await VideoPlayerPlatform.instance.play(videoPlayerId); + + // Let the video play, until we stop seeing events for a second + final List events = await stream; + + await VideoPlayerPlatform.instance.pause(videoPlayerId); + + // The expected list of event types should look like this: + // 1. bufferingStart, + // 2. bufferingUpdate (videoElement.onWaiting), + // 3. initialized (videoElement.onCanPlay), + // 4. bufferingEnd (videoElement.onCanPlayThrough), + expect( + events.map((VideoEvent e) => e.eventType), + equals([ + VideoEventType.bufferingStart, + VideoEventType.bufferingUpdate, + VideoEventType.initialized, + VideoEventType.bufferingEnd + ])); + }); }); } 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 new file mode 100644 index 000000000000..eda188cb1b9f --- /dev/null +++ b/packages/video_player/video_player_web/lib/src/video_player.dart @@ -0,0 +1,254 @@ +// 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:async'; +import 'dart:html' as html; + +import 'package:flutter/foundation.dart' show visibleForTesting; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:video_player_platform_interface/video_player_platform_interface.dart'; + +// An error code value to error name Map. +// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/code +const Map _kErrorValueToErrorName = { + 1: 'MEDIA_ERR_ABORTED', + 2: 'MEDIA_ERR_NETWORK', + 3: 'MEDIA_ERR_DECODE', + 4: 'MEDIA_ERR_SRC_NOT_SUPPORTED', +}; + +// An error code value to description Map. +// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/code +const Map _kErrorValueToErrorDescription = { + 1: 'The user canceled the fetching of the video.', + 2: 'A network error occurred while fetching the video, despite having previously been available.', + 3: 'An error occurred while trying to decode the video, despite having previously been determined to be usable.', + 4: 'The video has been found to be unsuitable (missing or in a format not supported by your browser).', +}; + +// The default error message, when the error is an empty string +// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/message +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. +class VideoPlayer { + /// Create a [VideoPlayer] from a [html.VideoElement] instance. + VideoPlayer({ + required html.VideoElement videoElement, + @visibleForTesting StreamController? eventController, + }) : _videoElement = videoElement, + _eventController = eventController ?? StreamController(); + + final StreamController _eventController; + final html.VideoElement _videoElement; + + bool _isInitialized = false; + bool _isBuffering = false; + + /// Returns the [Stream] of [VideoEvent]s from the inner [html.VideoElement]. + Stream get events => _eventController.stream; + + /// Initializes the wrapped [html.VideoElement]. + /// + /// This method sets the required DOM attributes so videos can [play] programmatically, + /// and attaches listeners to the internal events from the [html.VideoElement] + /// to react to them / expose them through the [VideoPlayer.events] stream. + void initialize() { + _videoElement + ..autoplay = false + ..controls = false; + + // Allows Safari iOS to play the video inline + _videoElement.setAttribute('playsinline', 'true'); + + // Set autoplay to false since most browsers won't autoplay a video unless it is muted + _videoElement.setAttribute('autoplay', 'false'); + + _videoElement.onCanPlay.listen((dynamic _) { + if (!_isInitialized) { + _isInitialized = true; + _sendInitialized(); + } + }); + + _videoElement.onCanPlayThrough.listen((dynamic _) { + setBuffering(false); + }); + + _videoElement.onPlaying.listen((dynamic _) { + setBuffering(false); + }); + + _videoElement.onWaiting.listen((dynamic _) { + setBuffering(true); + _sendBufferingRangesUpdate(); + }); + + // The error event fires when some form of error occurs while attempting to load or perform the media. + _videoElement.onError.listen((html.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!; + _eventController.addError(PlatformException( + code: _kErrorValueToErrorName[error.code]!, + message: error.message != '' ? error.message : _kDefaultErrorMessage, + details: _kErrorValueToErrorDescription[error.code], + )); + }); + + _videoElement.onEnded.listen((dynamic _) { + setBuffering(false); + _eventController.add(VideoEvent(eventType: VideoEventType.completed)); + }); + } + + /// Attempts to play the video. + /// + /// If this method is called programmatically (without user interaction), it + /// might fail unless the video is completely muted (or it has no Audio tracks). + /// + /// When called from some user interaction (a tap on a button), the above + /// limitation should disappear. + Future play() { + return _videoElement.play().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. + // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play + final html.DomException exception = e as html.DomException; + _eventController.addError(PlatformException( + code: exception.name, + message: exception.message, + )); + }, test: (Object e) => e is html.DomException); + } + + /// Pauses the video in the current position. + void pause() { + _videoElement.pause(); + } + + /// Controls whether the video should start again after it finishes. + void setLooping(bool value) { + _videoElement.loop = value; + } + + /// Sets the volume at which the media will be played. + /// + /// 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. + void setVolume(double volume) { + assert(volume >= 0 && volume <= 1); + + // TODO(ditman): Do we need to expose a "muted" API? + // https://github.com/flutter/flutter/issues/60721 + _videoElement.muted = !(volume > 0.0); + _videoElement.volume = volume; + } + + /// Sets the playback `speed`. + /// + /// A `speed` of 1.0 is "normal speed," values lower than 1.0 make the media + /// play slower than normal, higher values make it play faster. + /// + /// `speed` cannot be negative. + /// + /// The audio is muted when the fast forward or slow motion is outside a useful + /// range (for example, Gecko mutes the sound outside the range 0.25 to 4.0). + /// + /// The pitch of the audio is corrected by default. + void setPlaybackSpeed(double speed) { + assert(speed > 0); + + _videoElement.playbackRate = speed; + } + + /// Moves the playback head to a new `position`. + /// + /// `position` cannot be negative. + void seekTo(Duration position) { + assert(!position.isNegative); + + _videoElement.currentTime = position.inMilliseconds.toDouble() / 1000; + } + + /// Returns the current playback head position as a [Duration]. + Duration getPosition() { + _sendBufferingRangesUpdate(); + return Duration(milliseconds: (_videoElement.currentTime * 1000).round()); + } + + /// Disposes of the current [html.VideoElement]. + void dispose() { + _videoElement.removeAttribute('src'); + _videoElement.load(); + } + + // Sends an [VideoEventType.initialized] [VideoEvent] with info about the wrapped video. + void _sendInitialized() { + final Duration? duration = !_videoElement.duration.isNaN + ? Duration( + milliseconds: (_videoElement.duration * 1000).round(), + ) + : null; + + final Size? size = !_videoElement.videoHeight.isNaN + ? Size( + _videoElement.videoWidth.toDouble(), + _videoElement.videoHeight.toDouble(), + ) + : null; + + _eventController.add( + VideoEvent( + eventType: VideoEventType.initialized, + duration: duration, + size: size, + ), + ); + } + + /// Caches the current "buffering" state of the video. + /// + /// If the current buffering state is different from the previous one + /// ([_isBuffering]), this dispatches a [VideoEvent]. + @visibleForTesting + void setBuffering(bool buffering) { + if (_isBuffering != buffering) { + _isBuffering = buffering; + _eventController.add(VideoEvent( + eventType: _isBuffering + ? VideoEventType.bufferingStart + : VideoEventType.bufferingEnd, + )); + } + } + + // Broadcasts the [html.VideoElement.buffered] status through the [events] stream. + void _sendBufferingRangesUpdate() { + _eventController.add(VideoEvent( + buffered: _toDurationRange(_videoElement.buffered), + eventType: VideoEventType.bufferingUpdate, + )); + } + + // Converts from [html.TimeRanges] to our own List. + List _toDurationRange(html.TimeRanges buffered) { + final List durationRange = []; + for (int i = 0; i < buffered.length; i++) { + durationRange.add(DurationRange( + Duration(milliseconds: (buffered.start(i) * 1000).round()), + Duration(milliseconds: (buffered.end(i) * 1000).round()), + )); + } + return 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 a676850f3488..e52fd83de79e 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 @@ -6,34 +6,11 @@ import 'dart:async'; import 'dart:html'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; import 'src/shims/dart_ui.dart' as ui; - -// An error code value to error name Map. -// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/code -const Map _kErrorValueToErrorName = { - 1: 'MEDIA_ERR_ABORTED', - 2: 'MEDIA_ERR_NETWORK', - 3: 'MEDIA_ERR_DECODE', - 4: 'MEDIA_ERR_SRC_NOT_SUPPORTED', -}; - -// An error code value to description Map. -// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/code -const Map _kErrorValueToErrorDescription = { - 1: 'The user canceled the fetching of the video.', - 2: 'A network error occurred while fetching the video, despite having previously been available.', - 3: 'An error occurred while trying to decode the video, despite having previously been determined to be usable.', - 4: 'The video has been found to be unsuitable (missing or in a format not supported by your browser).', -}; - -// The default error message, when the error is an empty string -// See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/message -const String _kDefaultErrorMessage = - 'No further diagnostic information can be determined or provided.'; +import 'src/video_player.dart'; /// The web implementation of [VideoPlayerPlatform]. /// @@ -44,8 +21,10 @@ class VideoPlayerPlugin extends VideoPlayerPlatform { VideoPlayerPlatform.instance = VideoPlayerPlugin(); } - final Map _videoPlayers = {}; + // Map of textureId -> VideoPlayer instances + final Map _videoPlayers = {}; + // Simulate the native "textureId". int _textureCounter = 1; @override @@ -55,13 +34,13 @@ class VideoPlayerPlugin extends VideoPlayerPlatform { @override Future dispose(int textureId) async { - _videoPlayers[textureId]!.dispose(); + _player(textureId).dispose(); _videoPlayers.remove(textureId); return; } void _disposeAllPlayers() { - for (final _VideoPlayer videoPlayer in _videoPlayers.values) { + for (final VideoPlayer videoPlayer in _videoPlayers.values) { videoPlayer.dispose(); } _videoPlayers.clear(); @@ -69,8 +48,7 @@ class VideoPlayerPlugin extends VideoPlayerPlatform { @override Future create(DataSource dataSource) async { - final int textureId = _textureCounter; - _textureCounter++; + final int textureId = _textureCounter++; late String uri; switch (dataSource.sourceType) { @@ -95,58 +73,69 @@ class VideoPlayerPlugin extends VideoPlayerPlatform { 'web implementation of video_player cannot play content uri')); } - final _VideoPlayer player = _VideoPlayer( - uri: uri, - textureId: textureId, - ); + final VideoElement videoElement = VideoElement() + ..id = 'videoElement-$textureId' + ..src = uri + ..style.border = 'none' + ..style.height = '100%' + ..style.width = '100%'; - player.initialize(); + // TODO(hterkelsen): Use initialization parameters once they are available + ui.platformViewRegistry.registerViewFactory( + 'videoPlayer-$textureId', (int viewId) => videoElement); + + final VideoPlayer player = VideoPlayer(videoElement: videoElement) + ..initialize(); _videoPlayers[textureId] = player; + return textureId; } @override Future setLooping(int textureId, bool looping) async { - return _videoPlayers[textureId]!.setLooping(looping); + return _player(textureId).setLooping(looping); } @override Future play(int textureId) async { - return _videoPlayers[textureId]!.play(); + return _player(textureId).play(); } @override Future pause(int textureId) async { - return _videoPlayers[textureId]!.pause(); + return _player(textureId).pause(); } @override Future setVolume(int textureId, double volume) async { - return _videoPlayers[textureId]!.setVolume(volume); + return _player(textureId).setVolume(volume); } @override Future setPlaybackSpeed(int textureId, double speed) async { - assert(speed > 0); - - return _videoPlayers[textureId]!.setPlaybackSpeed(speed); + return _player(textureId).setPlaybackSpeed(speed); } @override Future seekTo(int textureId, Duration position) async { - return _videoPlayers[textureId]!.seekTo(position); + return _player(textureId).seekTo(position); } @override Future getPosition(int textureId) async { - _videoPlayers[textureId]!.sendBufferingUpdate(); - return _videoPlayers[textureId]!.getPosition(); + return _player(textureId).getPosition(); } @override Stream videoEventsFor(int textureId) { - return _videoPlayers[textureId]!.eventController.stream; + return _player(textureId).events; + } + + // Retrieves a [VideoPlayer] by its internal `id`. + // It must have been created earlier from the [create] method. + VideoPlayer _player(int id) { + return _videoPlayers[id]!; } @override @@ -158,171 +147,3 @@ class VideoPlayerPlugin extends VideoPlayerPlatform { @override Future setMixWithOthers(bool mixWithOthers) => Future.value(); } - -class _VideoPlayer { - _VideoPlayer({required this.uri, required this.textureId}); - - final StreamController eventController = - StreamController(); - - final String uri; - final int textureId; - late VideoElement videoElement; - bool isInitialized = false; - bool isBuffering = false; - - void setBuffering(bool buffering) { - if (isBuffering != buffering) { - isBuffering = buffering; - eventController.add(VideoEvent( - eventType: isBuffering - ? VideoEventType.bufferingStart - : VideoEventType.bufferingEnd)); - } - } - - void initialize() { - videoElement = VideoElement() - ..src = uri - ..autoplay = false - ..controls = false - ..style.border = 'none' - ..style.height = '100%' - ..style.width = '100%'; - - // Allows Safari iOS to play the video inline - videoElement.setAttribute('playsinline', 'true'); - - // Set autoplay to false since most browsers won't autoplay a video unless it is muted - videoElement.setAttribute('autoplay', 'false'); - - // TODO(hterkelsen): Use initialization parameters once they are available - ui.platformViewRegistry.registerViewFactory( - 'videoPlayer-$textureId', (int viewId) => videoElement); - - videoElement.onCanPlay.listen((dynamic _) { - if (!isInitialized) { - isInitialized = true; - sendInitialized(); - } - setBuffering(false); - }); - - videoElement.onCanPlayThrough.listen((dynamic _) { - setBuffering(false); - }); - - videoElement.onPlaying.listen((dynamic _) { - setBuffering(false); - }); - - videoElement.onWaiting.listen((dynamic _) { - setBuffering(true); - sendBufferingUpdate(); - }); - - // The error event fires when some form of error occurs while attempting to load or perform the media. - videoElement.onError.listen((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 MediaError error = videoElement.error!; - eventController.addError(PlatformException( - code: _kErrorValueToErrorName[error.code]!, - message: error.message != '' ? error.message : _kDefaultErrorMessage, - details: _kErrorValueToErrorDescription[error.code], - )); - }); - - videoElement.onEnded.listen((dynamic _) { - setBuffering(false); - eventController.add(VideoEvent(eventType: VideoEventType.completed)); - }); - } - - void sendBufferingUpdate() { - eventController.add(VideoEvent( - buffered: _toDurationRange(videoElement.buffered), - eventType: VideoEventType.bufferingUpdate, - )); - } - - Future play() { - return videoElement.play().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. - // See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play - final DomException exception = e as DomException; - eventController.addError(PlatformException( - code: exception.name, - message: exception.message, - )); - }, test: (Object e) => e is DomException); - } - - void pause() { - videoElement.pause(); - } - - void setLooping(bool value) { - videoElement.loop = value; - } - - void setVolume(double value) { - // TODO(ditman): Do we need to expose a "muted" API? https://github.com/flutter/flutter/issues/60721 - if (value > 0.0) { - videoElement.muted = false; - } else { - videoElement.muted = true; - } - videoElement.volume = value; - } - - void setPlaybackSpeed(double speed) { - assert(speed > 0); - - videoElement.playbackRate = speed; - } - - void seekTo(Duration position) { - videoElement.currentTime = position.inMilliseconds.toDouble() / 1000; - } - - Duration getPosition() { - return Duration(milliseconds: (videoElement.currentTime * 1000).round()); - } - - void sendInitialized() { - eventController.add( - VideoEvent( - eventType: VideoEventType.initialized, - duration: Duration( - milliseconds: (videoElement.duration * 1000).round(), - ), - size: Size( - videoElement.videoWidth.toDouble(), - videoElement.videoHeight.toDouble(), - ), - ), - ); - } - - void dispose() { - videoElement.removeAttribute('src'); - videoElement.load(); - } - - List _toDurationRange(TimeRanges buffered) { - final List durationRange = []; - for (int i = 0; i < buffered.length; i++) { - durationRange.add(DurationRange( - Duration(milliseconds: (buffered.start(i) * 1000).round()), - Duration(milliseconds: (buffered.end(i) * 1000).round()), - )); - } - return durationRange; - } -} diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 69a2df4e99e4..064517e1f264 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/plugins/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.0.7 +version: 2.0.8 environment: sdk: ">=2.12.0 <3.0.0" From 788d7e299193af8418d250806dc8c337f01706ab Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 18 Apr 2022 16:09:09 -0400 Subject: [PATCH 161/844] Roll Flutter from f4875ae865e9 to 3c4d7a1aed65 (2 revisions) (#5292) --- .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 048f3562ae90..c91967b89b46 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f4875ae865e9d42644a93bbab2164fc7baac4ff7 +3c4d7a1aed650f75ea0900a466bc2e10e362fb32 From 26e1d673de594bfcce4a4f54aae36bca920526fe Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Mon, 18 Apr 2022 16:29:09 -0700 Subject: [PATCH 162/844] Add owners for Android implementations (#5293) --- CODEOWNERS | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 88ba1f575a4c..0ce86cc085c4 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -9,4 +9,18 @@ packages/webview_flutter/** @bparrishMines # Sub-package-level rules. These should stay last, since the last matching # entry takes precedence. -packages/**/*_web/** @ditman + +# - Web +packages/**/*_web/** @ditman + +# - Android +packages/camera/android/** @camsim99 +packages/google_maps_flutter/android/** @GaryQian +packages/google_sign_in/google_sign_in_android/** @camsim99 +packages/image_picker/image_picker_android/** @GaryQian +packages/in_app_purchase/in_app_purchase_android/** @blasten +packages/local_auth/local_auth_android/** @blasten +packages/path_provider/path_provider_android/** @camsim99 +packages/quick_actions/quick_actions_android/** @camsim99 +packages/url_launcher/url_launcher_android/** @GaryQian +packages/video_player/video_player_android/** @blasten From 357d05ad154bc4d4e240cced1bfbda123d3ac436 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 18 Apr 2022 21:44:10 -0400 Subject: [PATCH 163/844] Roll Flutter from 3c4d7a1aed65 to 3752fb7bd15e (11 revisions) (#5294) --- .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 c91967b89b46..d475c20f7e3a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3c4d7a1aed650f75ea0900a466bc2e10e362fb32 +3752fb7bd15e72215cb9dc40086816e6ff7d5e7d From 4a59768fc17d697ea9fc2060d9383da367564c86 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 19 Apr 2022 00:29:08 -0400 Subject: [PATCH 164/844] Roll Flutter from 3752fb7bd15e to fd360c4a1d24 (2 revisions) (#5295) --- .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 d475c20f7e3a..27faac63c443 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3752fb7bd15e72215cb9dc40086816e6ff7d5e7d +fd360c4a1d2418ad2d1ad83f7b5cc125696837f9 From 06257ddbf67e902fdc4a42155ce10100c9e9f676 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Tue, 19 Apr 2022 13:09:05 +0800 Subject: [PATCH 165/844] [video_player_avfoundation] Applies the standardized transform for videos with different orientations (#5069) --- .../video_player_avfoundation/CHANGELOG.md | 4 + .../ios/RunnerTests/VideoPlayerTests.m | 93 +++++++++++++++++++ .../ios/Classes/AVAssetTrackUtils.h | 14 +++ .../ios/Classes/AVAssetTrackUtils.m | 46 +++++++++ .../ios/Classes/FLTVideoPlayerPlugin.m | 28 +----- .../video_player_avfoundation/pubspec.yaml | 2 +- 6 files changed, 162 insertions(+), 25 deletions(-) create mode 100644 packages/video_player/video_player_avfoundation/ios/Classes/AVAssetTrackUtils.h create mode 100644 packages/video_player/video_player_avfoundation/ios/Classes/AVAssetTrackUtils.m diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index c36c9878d1a1..a1f23eb670fc 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.2 + +* Applies the standardized transform for videos with different orientations. + ## 2.3.1 * Renames internal method channels to avoid potential confusion with the diff --git a/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m index cbf2866aa071..7decd04bd168 100644 --- a/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m @@ -7,6 +7,7 @@ @import XCTest; #import +#import @interface FLTVideoPlayer : NSObject @property(readonly, nonatomic) AVPlayer *player; @@ -17,6 +18,44 @@ @interface FLTVideoPlayerPlugin (Test) NSMutableDictionary *playersByTextureId; @end +@interface FakeAVAssetTrack : AVAssetTrack +@property(readonly, nonatomic) CGAffineTransform preferredTransform; +@property(readonly, nonatomic) CGSize naturalSize; +@property(readonly, nonatomic) UIImageOrientation orientation; +- (instancetype)initWithOrientation:(UIImageOrientation)orientation; +@end + +@implementation FakeAVAssetTrack + +- (instancetype)initWithOrientation:(UIImageOrientation)orientation { + _orientation = orientation; + _naturalSize = CGSizeMake(800, 600); + return self; +} + +- (CGAffineTransform)preferredTransform { + switch (_orientation) { + case UIImageOrientationUp: + return CGAffineTransformMake(1, 0, 0, 1, 0, 0); + case UIImageOrientationDown: + return CGAffineTransformMake(-1, 0, 0, -1, 0, 0); + case UIImageOrientationLeft: + return CGAffineTransformMake(0, -1, 1, 0, 0, 0); + case UIImageOrientationRight: + return CGAffineTransformMake(0, 1, -1, 0, 0, 0); + case UIImageOrientationUpMirrored: + return CGAffineTransformMake(-1, 0, 0, 1, 0, 0); + case UIImageOrientationDownMirrored: + return CGAffineTransformMake(1, 0, 0, -1, 0, 0); + case UIImageOrientationLeftMirrored: + return CGAffineTransformMake(0, -1, -1, 0, 0, 0); + case UIImageOrientationRightMirrored: + return CGAffineTransformMake(0, 1, 1, 0, 0, 0); + } +} + +@end + @interface VideoPlayerTests : XCTestCase @end @@ -121,6 +160,17 @@ - (void)testHLSControls { XCTAssertEqualWithAccuracy([videoInitialization[@"duration"] intValue], 4000, 200); } +- (void)testTransformFix { + [self validateTransformFixForOrientation:UIImageOrientationUp]; + [self validateTransformFixForOrientation:UIImageOrientationDown]; + [self validateTransformFixForOrientation:UIImageOrientationLeft]; + [self validateTransformFixForOrientation:UIImageOrientationRight]; + [self validateTransformFixForOrientation:UIImageOrientationUpMirrored]; + [self validateTransformFixForOrientation:UIImageOrientationDownMirrored]; + [self validateTransformFixForOrientation:UIImageOrientationLeftMirrored]; + [self validateTransformFixForOrientation:UIImageOrientationRightMirrored]; +} + - (NSDictionary *)testPlugin:(FLTVideoPlayerPlugin *)videoPlayerPlugin uri:(NSString *)uri { FlutterError *error; @@ -175,4 +225,47 @@ - (void)testHLSControls { return initializationEvent; } +- (void)validateTransformFixForOrientation:(UIImageOrientation)orientation { + AVAssetTrack *track = [[FakeAVAssetTrack alloc] initWithOrientation:orientation]; + CGAffineTransform t = FLTGetStandardizedTransformForTrack(track); + CGSize size = track.naturalSize; + CGFloat expectX, expectY; + switch (orientation) { + case UIImageOrientationUp: + expectX = 0; + expectY = 0; + break; + case UIImageOrientationDown: + expectX = size.width; + expectY = size.height; + break; + case UIImageOrientationLeft: + expectX = 0; + expectY = size.width; + break; + case UIImageOrientationRight: + expectX = size.height; + expectY = 0; + break; + case UIImageOrientationUpMirrored: + expectX = size.width; + expectY = 0; + break; + case UIImageOrientationDownMirrored: + expectX = 0; + expectY = size.height; + break; + case UIImageOrientationLeftMirrored: + expectX = size.height; + expectY = size.width; + break; + case UIImageOrientationRightMirrored: + expectX = 0; + expectY = 0; + break; + } + XCTAssertEqual(t.tx, expectX); + XCTAssertEqual(t.ty, expectY); +} + @end diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/AVAssetTrackUtils.h b/packages/video_player/video_player_avfoundation/ios/Classes/AVAssetTrackUtils.h new file mode 100644 index 000000000000..9d736bc21afe --- /dev/null +++ b/packages/video_player/video_player_avfoundation/ios/Classes/AVAssetTrackUtils.h @@ -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 + +/** + * 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 FLTGetStandardizedTransformForTrack(AVAssetTrack* track); diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/AVAssetTrackUtils.m b/packages/video_player/video_player_avfoundation/ios/Classes/AVAssetTrackUtils.m new file mode 100644 index 000000000000..de75859a94a4 --- /dev/null +++ b/packages/video_player/video_player_avfoundation/ios/Classes/AVAssetTrackUtils.m @@ -0,0 +1,46 @@ +// 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 + +CGAffineTransform FLTGetStandardizedTransformForTrack(AVAssetTrack *track) { + CGAffineTransform t = track.preferredTransform; + CGSize size = track.naturalSize; + // Each case of control flows corresponds to a specific + // `UIImageOrientation`, with 8 cases in total. + if (t.a == 1 && t.b == 0 && t.c == 0 && t.d == 1) { + // UIImageOrientationUp + t.tx = 0; + t.ty = 0; + } else if (t.a == -1 && t.b == 0 && t.c == 0 && t.d == -1) { + // UIImageOrientationDown + t.tx = size.width; + t.ty = size.height; + } else if (t.a == 0 && t.b == -1 && t.c == 1 && t.d == 0) { + // UIImageOrientationLeft + t.tx = 0; + t.ty = size.width; + } else if (t.a == 0 && t.b == 1 && t.c == -1 && t.d == 0) { + // UIImageOrientationRight + t.tx = size.height; + t.ty = 0; + } else if (t.a == -1 && t.b == 0 && t.c == 0 && t.d == 1) { + // UIImageOrientationUpMirrored + t.tx = size.width; + t.ty = 0; + } else if (t.a == 1 && t.b == 0 && t.c == 0 && t.d == -1) { + // UIImageOrientationDownMirrored + t.tx = 0; + t.ty = size.height; + } else if (t.a == 0 && t.b == -1 && t.c == -1 && t.d == 0) { + // UIImageOrientationLeftMirrored + t.tx = size.height; + t.ty = size.width; + } else if (t.a == 0 && t.b == 1 && t.c == 1 && t.d == 0) { + // UIImageOrientationRightMirrored + t.tx = 0; + t.ty = 0; + } + return t; +} diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m index 2fb9f7f55600..a95779b1cbab 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m @@ -3,8 +3,11 @@ // found in the LICENSE file. #import "FLTVideoPlayerPlugin.h" + #import #import + +#import "AVAssetTrackUtils.h" #import "messages.g.h" #if !__has_feature(objc_arc) @@ -187,29 +190,6 @@ - (instancetype)initWithURL:(NSURL *)url return [self initWithPlayerItem:item frameUpdater:frameUpdater]; } -- (CGAffineTransform)fixTransform:(AVAssetTrack *)videoTrack { - CGAffineTransform transform = videoTrack.preferredTransform; - // TODO(@recastrodiaz): why do we need to do this? Why is the preferredTransform incorrect? - // At least 2 user videos show a black screen when in portrait mode if we directly use the - // videoTrack.preferredTransform Setting tx to the height of the video instead of 0, properly - // displays the video https://github.com/flutter/flutter/issues/17606#issuecomment-413473181 - if (transform.tx == 0 && transform.ty == 0) { - NSInteger rotationDegrees = (NSInteger)round(radiansToDegrees(atan2(transform.b, transform.a))); - NSLog(@"TX and TY are 0. Rotation: %ld. Natural width,height: %f, %f", (long)rotationDegrees, - videoTrack.naturalSize.width, videoTrack.naturalSize.height); - if (rotationDegrees == 90) { - NSLog(@"Setting transform tx"); - transform.tx = videoTrack.naturalSize.height; - transform.ty = 0; - } else if (rotationDegrees == 270) { - NSLog(@"Setting transform ty"); - transform.tx = 0; - transform.ty = videoTrack.naturalSize.width; - } - } - return transform; -} - - (instancetype)initWithPlayerItem:(AVPlayerItem *)item frameUpdater:(FLTFrameUpdater *)frameUpdater { self = [super init]; @@ -226,7 +206,7 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem *)item if ([videoTrack statusOfValueForKey:@"preferredTransform" error:nil] == AVKeyValueStatusLoaded) { // Rotate the video by using a videoComposition and the preferredTransform - self->_preferredTransform = [self fixTransform:videoTrack]; + self->_preferredTransform = FLTGetStandardizedTransformForTrack(videoTrack); // Note: // https://developer.apple.com/documentation/avfoundation/avplayeritem/1388818-videocomposition // Video composition can only be used with file-based media and is not supported for diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index 0e42c26de829..5874b52cba6f 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.1 +version: 2.3.2 environment: sdk: ">=2.14.0 <3.0.0" From 8084ac1f363aa910ed0af64b42610df909d83ee3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 19 Apr 2022 08:24:09 -0400 Subject: [PATCH 166/844] Roll Flutter from fd360c4a1d24 to ef5a6da35a72 (1 revision) (#5298) --- .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 27faac63c443..668e05f55a1d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fd360c4a1d2418ad2d1ad83f7b5cc125696837f9 +ef5a6da35a72dcd89cabb06f4e075801ea260224 From 3e1f7acf2c0b29cdc630b3702457ae169e157128 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 19 Apr 2022 11:44:12 -0400 Subject: [PATCH 167/844] Roll Flutter from ef5a6da35a72 to eb54cefb97fa (1 revision) (#5299) --- .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 668e05f55a1d..35799ab67c50 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ef5a6da35a72dcd89cabb06f4e075801ea260224 +eb54cefb97fa5f712f80e3a2bdeb0baa6542c4eb From a9d2381908cf7adf899d0fcec727f9968fbac01e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 19 Apr 2022 12:49:11 -0400 Subject: [PATCH 168/844] Roll Flutter from eb54cefb97fa to c2ec2426ac39 (1 revision) (#5300) --- .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 35799ab67c50..c714466df2d2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -eb54cefb97fa5f712f80e3a2bdeb0baa6542c4eb +c2ec2426ac39f89e0d09f7f2b549033557eb398f From 659ee36c8ff8894f33b3c9c8368fddf994df1f23 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 19 Apr 2022 13:54:18 -0400 Subject: [PATCH 169/844] Roll Flutter from c2ec2426ac39 to 220ffc77f375 (1 revision) (#5302) --- .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 c714466df2d2..61e76583b2f6 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c2ec2426ac39f89e0d09f7f2b549033557eb398f +220ffc77f3752ed9d59e66babddbe33527422e1b From 65d1f52cef095764967ee93daf22eadfe3dac9cd Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 19 Apr 2022 16:04:10 -0400 Subject: [PATCH 170/844] Roll Flutter from 220ffc77f375 to ce6188bef15c (5 revisions) (#5304) --- .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 61e76583b2f6..8d4ed0152920 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -220ffc77f3752ed9d59e66babddbe33527422e1b +ce6188bef15c468cfd87e183b0ee14eed1a21c2d From fb531ede7f05ba53391e049f3b812caa8eb67d5a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 19 Apr 2022 17:14:11 -0400 Subject: [PATCH 171/844] Roll Flutter from ce6188bef15c to fecc8904ff8c (1 revision) (#5305) --- .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 8d4ed0152920..550dc4c4a068 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ce6188bef15c468cfd87e183b0ee14eed1a21c2d +fecc8904ff8cbe6d59a91eb85a9957884ea89b96 From 6e18c7195678f5f2ff12e79a5fe738280a66fe26 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 19 Apr 2022 18:19:11 -0400 Subject: [PATCH 172/844] Roll Flutter from fecc8904ff8c to 9e4f2650dbf9 (2 revisions) (#5307) --- .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 550dc4c4a068..ee5ede6ecf5b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fecc8904ff8cbe6d59a91eb85a9957884ea89b96 +9e4f2650dbf93295ee3a6cc8409375fc3ebb8bcc From 1d9f3d35a533ded6ebf05a3f36ab43c76d535326 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Wed, 20 Apr 2022 08:39:10 -0700 Subject: [PATCH 173/844] Add iOS path CODEOWNERS (#5306) --- CODEOWNERS | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 0ce86cc085c4..fdf0e30550f7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -14,8 +14,8 @@ packages/webview_flutter/** @bparrishMines packages/**/*_web/** @ditman # - Android -packages/camera/android/** @camsim99 -packages/google_maps_flutter/android/** @GaryQian +packages/camera/camera/android/** @camsim99 +packages/google_maps_flutter/google_maps_flutter/android/** @GaryQian packages/google_sign_in/google_sign_in_android/** @camsim99 packages/image_picker/image_picker_android/** @GaryQian packages/in_app_purchase/in_app_purchase_android/** @blasten @@ -24,3 +24,18 @@ packages/path_provider/path_provider_android/** @camsim99 packages/quick_actions/quick_actions_android/** @camsim99 packages/url_launcher/url_launcher_android/** @GaryQian packages/video_player/video_player_android/** @blasten + +# - iOS +packages/camera/camera/ios/** @hellohuanlin +packages/google_maps_flutter/google_maps_flutter/ios/** @cyanglaz +packages/google_sign_in/google_sign_in_ios/** @jmagman +packages/image_picker/image_picker_ios/** @cyanglaz +packages/in_app_purchase/in_app_purchase_storekit/** @cyanglaz +packages/ios_platform_images/ios/** @jmagman +packages/local_auth/local_auth_ios/** @hellohuanlin +packages/path_provider/path_provider_ios/** @jmagman +packages/quick_actions/quick_actions_ios/** @hellohuanlin +packages/shared_preferences/shared_preferences_ios/** @cyanglaz +packages/url_launcher/url_launcher_ios/** @jmagman +packages/video_player/video_player_avfoundation/** @hellohuanlin +packages/webview_flutter/webview_flutter_wkwebview/** @cyanglaz From 0829ecf67722f2724a6cf950ea06a7fbf9f77650 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 20 Apr 2022 12:29:11 -0400 Subject: [PATCH 174/844] Roll Flutter from 9e4f2650dbf9 to b9f1a907b1aa (2 revisions) (#5308) --- .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 ee5ede6ecf5b..bd294fffd991 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -9e4f2650dbf93295ee3a6cc8409375fc3ebb8bcc +b9f1a907b1aa5f13d0443cecb7e9f851b153c11f From 102cf1bb6308e7560a04aebf854a5ceb546639ea Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 20 Apr 2022 15:29:09 -0400 Subject: [PATCH 175/844] Roll Flutter from b9f1a907b1aa to e526f859ef80 (10 revisions) (#5311) --- .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 bd294fffd991..ade3095c4732 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b9f1a907b1aa5f13d0443cecb7e9f851b153c11f +e526f859ef8061690828762cb779ea29d6cd4981 From f29c36c952312123c4d52af5b1e955f096b11c57 Mon Sep 17 00:00:00 2001 From: Alex Furaiev <33546696+furaiev@users.noreply.github.com> Date: Wed, 20 Apr 2022 23:14:09 +0300 Subject: [PATCH 176/844] [local_auth] support localizedFallbackTitle in IOSAuthMessages (#5297) --- .../local_auth/local_auth_ios/CHANGELOG.md | 4 ++++ .../ios/Classes/FLTLocalAuthPlugin.m | 1 + .../lib/types/auth_messages_ios.dart | 13 +++++++++-- .../local_auth/local_auth_ios/pubspec.yaml | 2 +- .../local_auth_ios/test/local_auth_test.dart | 23 +++++++++++++++++++ 5 files changed, 40 insertions(+), 3 deletions(-) diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index d826cc8032e5..ccacae986d51 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.2 + +* Adds support `localizedFallbackTitle` in authenticateWithBiometrics on iOS. + ## 1.0.1 * BREAKING CHANGE: Changes `stopAuthentication` to always return false instead of throwing an error. diff --git a/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m index d4d0e0d0f79f..ffd1dd2a869d 100644 --- a/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m +++ b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m @@ -181,6 +181,7 @@ - (void)handleAuthReplyWithSuccess:(BOOL)success case LAErrorTouchIDNotAvailable: case LAErrorTouchIDNotEnrolled: case LAErrorTouchIDLockout: + case LAErrorUserFallback: [self handleErrors:error flutterArguments:arguments withFlutterResult:result]; return; case LAErrorSystemCancel: diff --git a/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart b/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart index 8a776243d242..b3236a7ff66e 100644 --- a/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart +++ b/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart @@ -16,6 +16,7 @@ class IOSAuthMessages extends AuthMessages { this.goToSettingsButton, this.goToSettingsDescription, this.cancelButton, + this.localizedFallbackTitle, }); /// Message advising the user to re-enable biometrics on their device. @@ -35,6 +36,10 @@ class IOSAuthMessages extends AuthMessages { /// Maximum 30 characters. final String? cancelButton; + /// The localized title for the fallback button in the dialog presented to + /// the user during authentication. + final String? localizedFallbackTitle; + @override Map get args { return { @@ -43,6 +48,8 @@ class IOSAuthMessages extends AuthMessages { 'goToSettingDescriptionIOS': goToSettingsDescription ?? iOSGoToSettingsDescription, 'okButton': cancelButton ?? iOSOkButton, + if (localizedFallbackTitle != null) + 'localizedFallbackTitle': localizedFallbackTitle!, }; } @@ -54,14 +61,16 @@ class IOSAuthMessages extends AuthMessages { lockOut == other.lockOut && goToSettingsButton == other.goToSettingsButton && goToSettingsDescription == other.goToSettingsDescription && - cancelButton == other.cancelButton; + cancelButton == other.cancelButton && + localizedFallbackTitle == other.localizedFallbackTitle; @override int get hashCode => lockOut.hashCode ^ goToSettingsButton.hashCode ^ goToSettingsDescription.hashCode ^ - cancelButton.hashCode; + cancelButton.hashCode ^ + localizedFallbackTitle.hashCode; } // Default Strings for IOSAuthMessages plugin. Currently supports English. diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index 537b1a64d003..9e69dc7a6617 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.1 +version: 1.0.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_ios/test/local_auth_test.dart b/packages/local_auth/local_auth_ios/test/local_auth_test.dart index 180383bc258c..aa94b8aa48b8 100644 --- a/packages/local_auth/local_auth_ios/test/local_auth_test.dart +++ b/packages/local_auth/local_auth_ios/test/local_auth_test.dart @@ -135,6 +135,29 @@ void main() { ); }); + test('authenticate with `localizedFallbackTitle`', () async { + await localAuthentication.authenticate( + authMessages: [ + const IOSAuthMessages(localizedFallbackTitle: 'Enter PIN'), + ], + localizedReason: 'Needs secure', + ); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'Needs secure', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': false, + 'localizedFallbackTitle': 'Enter PIN', + }..addAll(const IOSAuthMessages().args)), + ], + ); + }); + test('authenticate with no sensitive transaction.', () async { await localAuthentication.authenticate( authMessages: [const IOSAuthMessages()], From d4615a85a7454d158577cf2a2e64c8ee165d7bcf Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 21 Apr 2022 00:44:15 -0400 Subject: [PATCH 177/844] Roll Flutter from e526f859ef80 to fce3cee7763c (4 revisions) (#5313) --- .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 ade3095c4732..c6d41cf2d8dd 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e526f859ef8061690828762cb779ea29d6cd4981 +fce3cee7763c601648e1d98027d0a4aa85f22ebd From e67d151b37b880472daac99ee0ceb7621c858125 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 21 Apr 2022 02:54:09 -0400 Subject: [PATCH 178/844] Roll Flutter from fce3cee7763c to 3fefb2d5eb5f (8 revisions) (#5317) --- .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 c6d41cf2d8dd..8e38ba79e6c4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fce3cee7763c601648e1d98027d0a4aa85f22ebd +3fefb2d5eb5f807ad2046267054f1d3a4d6bea74 From 5c3c1b8aac065aa1988f114d0c66ecec758e9cbb Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 21 Apr 2022 03:59:12 -0400 Subject: [PATCH 179/844] Roll Flutter from 3fefb2d5eb5f to 0dccb58a73dd (1 revision) (#5318) --- .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 8e38ba79e6c4..627a0ce88e85 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3fefb2d5eb5f807ad2046267054f1d3a4d6bea74 +0dccb58a73dd0448b3cc4b24396b61ddcce4f861 From 8ff316f7d49c8a6a512c98a6c95f480767e3218e Mon Sep 17 00:00:00 2001 From: Navaron Bracke Date: Thu, 21 Apr 2022 19:49:13 +0200 Subject: [PATCH 180/844] [camera] Fix the orientation of videos recorded in landscape on Android (#4946) --- packages/camera/camera/CHANGELOG.md | 4 +++ .../DeviceOrientationManager.java | 22 ++++++++++------ .../DeviceOrientationManagerTest.java | 26 ++++++++++++++----- packages/camera/camera/pubspec.yaml | 2 +- 4 files changed, 38 insertions(+), 16 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index cf502221efa0..b86443249dad 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+20 + +* Fixes an issue with the orientation of videos recorded in landscape on Android. + ## 0.9.4+19 * Migrate deprecated Scaffold SnackBar methods to ScaffoldMessenger. diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManager.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManager.java index dd1e489e6225..ec6fa13dbd1d 100644 --- a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManager.java +++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManager.java @@ -142,26 +142,32 @@ public int getPhotoOrientation(PlatformChannel.DeviceOrientation orientation) { } /** - * Returns the device's video orientation in degrees based on the sensor orientation and the last - * known UI orientation. + * Returns the device's video orientation in clockwise degrees based on the sensor orientation and + * the last known UI orientation. * *

Returns one of 0, 90, 180 or 270. * - * @return The device's video orientation in degrees. + * @return The device's video orientation in clockwise degrees. */ public int getVideoOrientation() { return this.getVideoOrientation(this.lastOrientation); } /** - * Returns the device's video orientation in degrees based on the sensor orientation and the - * supplied {@link PlatformChannel.DeviceOrientation} value. + * Returns the device's video orientation in clockwise degrees based on the sensor orientation and + * the supplied {@link PlatformChannel.DeviceOrientation} value. * *

Returns one of 0, 90, 180 or 270. * + *

More details can be found in the official Android documentation: + * https://developer.android.com/reference/android/media/MediaRecorder#setOrientationHint(int) + * + *

See also: + * https://developer.android.com/training/camera2/camera-preview-large-screens#orientation_calculation + * * @param orientation The {@link PlatformChannel.DeviceOrientation} value that is to be converted * into degrees. - * @return The device's video orientation in degrees. + * @return The device's video orientation in clockwise degrees. */ public int getVideoOrientation(PlatformChannel.DeviceOrientation orientation) { int angle = 0; @@ -179,10 +185,10 @@ public int getVideoOrientation(PlatformChannel.DeviceOrientation orientation) { angle = 180; break; case LANDSCAPE_LEFT: - angle = 90; + angle = 270; break; case LANDSCAPE_RIGHT: - angle = 270; + angle = 90; break; } diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java index 82449a10188a..3762006f46d4 100644 --- a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java +++ b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java @@ -62,9 +62,9 @@ public void getVideoOrientation_whenNaturalScreenOrientationEqualsPortraitUp() { deviceOrientationManager.getVideoOrientation(DeviceOrientation.LANDSCAPE_RIGHT); assertEquals(0, degreesPortraitUp); - assertEquals(90, degreesLandscapeLeft); + assertEquals(270, degreesLandscapeLeft); assertEquals(180, degreesPortraitDown); - assertEquals(270, degreesLandscapeRight); + assertEquals(90, degreesLandscapeRight); } @Test @@ -81,18 +81,30 @@ public void getVideoOrientation_whenNaturalScreenOrientationEqualsLandscapeLeft( orientationManager.getVideoOrientation(DeviceOrientation.LANDSCAPE_RIGHT); assertEquals(90, degreesPortraitUp); - assertEquals(180, degreesLandscapeLeft); + assertEquals(0, degreesLandscapeLeft); assertEquals(270, degreesPortraitDown); - assertEquals(0, degreesLandscapeRight); + assertEquals(180, degreesLandscapeRight); } @Test - public void getVideoOrientation_shouldFallbackToSensorOrientationWhenOrientationIsNull() { - setUpUIOrientationMocks(Configuration.ORIENTATION_LANDSCAPE, Surface.ROTATION_0); + public void getVideoOrientation_fallbackToPortraitSensorOrientationWhenOrientationIsNull() { + setUpUIOrientationMocks(Configuration.ORIENTATION_PORTRAIT, Surface.ROTATION_0); int degrees = deviceOrientationManager.getVideoOrientation(null); - assertEquals(90, degrees); + assertEquals(0, degrees); + } + + @Test + public void getVideoOrientation_fallbackToLandscapeSensorOrientationWhenOrientationIsNull() { + setUpUIOrientationMocks(Configuration.ORIENTATION_LANDSCAPE, Surface.ROTATION_0); + + DeviceOrientationManager orientationManager = + DeviceOrientationManager.create(mockActivity, mockDartMessenger, false, 90); + + int degrees = orientationManager.getVideoOrientation(null); + + assertEquals(0, degrees); } @Test diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 2533583d9a6e..feb83f95cd8d 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+19 +version: 0.9.4+20 environment: sdk: ">=2.14.0 <3.0.0" From 92a1ffeda02e6a3cfbe1f588f43007706daf330c Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Thu, 21 Apr 2022 11:39:10 -0700 Subject: [PATCH 181/844] [image_picker] Added image_picker_for_windows. (#4863) --- .../image_picker/image_picker_windows/AUTHORS | 7 + .../image_picker_windows/CHANGELOG.md | 3 + .../image_picker/image_picker_windows/LICENSE | 25 ++ .../image_picker_windows/README.md | 16 + .../image_picker_windows/example/README.md | 8 + .../example/lib/main.dart | 414 ++++++++++++++++++ .../image_picker_windows/example/pubspec.yaml | 27 ++ .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 95 ++++ .../example/windows/flutter/CMakeLists.txt | 103 +++++ .../windows/flutter/generated_plugins.cmake | 16 + .../example/windows/runner/CMakeLists.txt | 17 + .../example/windows/runner/Runner.rc | 121 +++++ .../example/windows/runner/flutter_window.cpp | 65 +++ .../example/windows/runner/flutter_window.h | 37 ++ .../example/windows/runner/main.cpp | 46 ++ .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 67 +++ .../example/windows/runner/utils.h | 23 + .../example/windows/runner/win32_window.cpp | 241 ++++++++++ .../example/windows/runner/win32_window.h | 99 +++++ .../lib/image_picker_windows.dart | 167 +++++++ .../image_picker_windows/pubspec.yaml | 29 ++ .../test/image_picker_windows_test.dart | 128 ++++++ .../test/image_picker_windows_test.mocks.dart | 78 ++++ script/configs/exclude_integration_win32.yaml | 1 + 28 files changed, 1886 insertions(+) create mode 100644 packages/image_picker/image_picker_windows/AUTHORS create mode 100644 packages/image_picker/image_picker_windows/CHANGELOG.md create mode 100644 packages/image_picker/image_picker_windows/LICENSE create mode 100644 packages/image_picker/image_picker_windows/README.md create mode 100644 packages/image_picker/image_picker_windows/example/README.md create mode 100644 packages/image_picker/image_picker_windows/example/lib/main.dart create mode 100644 packages/image_picker/image_picker_windows/example/pubspec.yaml create mode 100644 packages/image_picker/image_picker_windows/example/windows/.gitignore create mode 100644 packages/image_picker/image_picker_windows/example/windows/CMakeLists.txt create mode 100644 packages/image_picker/image_picker_windows/example/windows/flutter/CMakeLists.txt create mode 100644 packages/image_picker/image_picker_windows/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/CMakeLists.txt create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/Runner.rc create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/flutter_window.cpp create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/flutter_window.h create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/main.cpp create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/resource.h create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/resources/app_icon.ico create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/runner.exe.manifest create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/utils.cpp create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/utils.h create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/win32_window.cpp create mode 100644 packages/image_picker/image_picker_windows/example/windows/runner/win32_window.h create mode 100644 packages/image_picker/image_picker_windows/lib/image_picker_windows.dart create mode 100644 packages/image_picker/image_picker_windows/pubspec.yaml create mode 100644 packages/image_picker/image_picker_windows/test/image_picker_windows_test.dart create mode 100644 packages/image_picker/image_picker_windows/test/image_picker_windows_test.mocks.dart diff --git a/packages/image_picker/image_picker_windows/AUTHORS b/packages/image_picker/image_picker_windows/AUTHORS new file mode 100644 index 000000000000..5db3d584e6bc --- /dev/null +++ b/packages/image_picker/image_picker_windows/AUTHORS @@ -0,0 +1,7 @@ +# 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. +Alexandre Zollinger Chohfi \ No newline at end of file diff --git a/packages/image_picker/image_picker_windows/CHANGELOG.md b/packages/image_picker/image_picker_windows/CHANGELOG.md new file mode 100644 index 000000000000..c4a71a0176b5 --- /dev/null +++ b/packages/image_picker/image_picker_windows/CHANGELOG.md @@ -0,0 +1,3 @@ +# 0.1.0 + +* Initial Windows support. diff --git a/packages/image_picker/image_picker_windows/LICENSE b/packages/image_picker/image_picker_windows/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/image_picker/image_picker_windows/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/image_picker/image_picker_windows/README.md b/packages/image_picker/image_picker_windows/README.md new file mode 100644 index 000000000000..0b256411b2fc --- /dev/null +++ b/packages/image_picker/image_picker_windows/README.md @@ -0,0 +1,16 @@ +# image\_picker\_windows + +A Windows implementation of [`image_picker`][1]. + +### pickImage() +The arguments `source`, `maxWidth`, `maxHeight`, `imageQuality`, and `preferredCameraDevice` are not supported on Windows. + +### pickVideo() +The arguments `source`, `preferredCameraDevice`, and `maxDuration` are not supported on Windows. + +## Usage + +### Import the package + +This package is not yet [endorsed](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin), which means you need to add +not only the `image_picker`, as well as the `image_picker_windows`. \ No newline at end of file diff --git a/packages/image_picker/image_picker_windows/example/README.md b/packages/image_picker/image_picker_windows/example/README.md new file mode 100644 index 000000000000..ae730a5ec846 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/README.md @@ -0,0 +1,8 @@ +# image_picker_windows_example + +Demonstrates how to use the image_picker_windows plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.dev/). diff --git a/packages/image_picker/image_picker_windows/example/lib/main.dart b/packages/image_picker/image_picker_windows/example/lib/main.dart new file mode 100644 index 000000000000..af97115676c0 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/lib/main.dart @@ -0,0 +1,414 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'package:video_player/video_player.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return const MaterialApp( + title: 'Image Picker Demo', + home: MyHomePage(title: 'Image Picker Example'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({Key? key, this.title}) : super(key: key); + + final String? title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + List? _imageFileList; + + // This must be called from within a setState() callback + set _imageFile(PickedFile? value) { + _imageFileList = value == null ? null : [value]; + } + + dynamic _pickImageError; + bool _isVideo = false; + + VideoPlayerController? _controller; + VideoPlayerController? _toBeDisposed; + String? _retrieveDataError; + + final ImagePickerPlatform _picker = ImagePickerPlatform.instance; + final TextEditingController maxWidthController = TextEditingController(); + final TextEditingController maxHeightController = TextEditingController(); + final TextEditingController qualityController = TextEditingController(); + + Future _playVideo(PickedFile? file) async { + if (file != null && mounted) { + await _disposeVideoController(); + final VideoPlayerController controller = + VideoPlayerController.file(File(file.path)); + _controller = controller; + await controller.setVolume(1.0); + await controller.initialize(); + await controller.setLooping(true); + await controller.play(); + setState(() {}); + } + } + + Future _handleMultiImagePicked(BuildContext? context) async { + await _displayPickImageDialog(context!, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final List? pickedFileList = await _picker.pickMultiImage( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ); + setState(() { + _imageFileList = pickedFileList; + }); + } catch (e) { + setState(() { + _pickImageError = e; + }); + } + }); + } + + Future _handleSingleImagePicked( + BuildContext? context, ImageSource source) async { + await _displayPickImageDialog(context!, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final PickedFile? pickedFile = await _picker.pickImage( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ); + setState(() { + _imageFile = pickedFile; + }); + } catch (e) { + setState(() { + _pickImageError = e; + }); + } + }); + } + + Future _onImageButtonPressed(ImageSource source, + {BuildContext? context, bool isMultiImage = false}) async { + if (_controller != null) { + await _controller!.setVolume(0.0); + } + if (_isVideo) { + final PickedFile? file = await _picker.pickVideo( + source: source, maxDuration: const Duration(seconds: 10)); + await _playVideo(file); + } else if (isMultiImage) { + await _handleMultiImagePicked(context); + } else { + await _handleSingleImagePicked(context, source); + } + } + + @override + void deactivate() { + if (_controller != null) { + _controller!.setVolume(0.0); + _controller!.pause(); + } + super.deactivate(); + } + + @override + void dispose() { + _disposeVideoController(); + maxWidthController.dispose(); + maxHeightController.dispose(); + qualityController.dispose(); + super.dispose(); + } + + Future _disposeVideoController() async { + if (_toBeDisposed != null) { + await _toBeDisposed!.dispose(); + } + _toBeDisposed = _controller; + _controller = null; + } + + Widget _previewVideo() { + final Text? retrieveError = _getRetrieveErrorWidget(); + if (retrieveError != null) { + return retrieveError; + } + if (_controller == null) { + return const Text( + 'You have not yet picked a video', + textAlign: TextAlign.center, + ); + } + return Padding( + padding: const EdgeInsets.all(10.0), + child: AspectRatioVideo(_controller), + ); + } + + Widget _previewImages() { + final Text? retrieveError = _getRetrieveErrorWidget(); + if (retrieveError != null) { + return retrieveError; + } + if (_imageFileList != null) { + return Semantics( + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + return Semantics( + label: 'image_picker_example_picked_image', + child: Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + label: 'image_picker_example_picked_images'); + } else if (_pickImageError != null) { + return Text( + 'Pick image error: $_pickImageError', + textAlign: TextAlign.center, + ); + } else { + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + } + } + + Widget _handlePreview() { + if (_isVideo) { + return _previewVideo(); + } else { + return _previewImages(); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title!), + ), + body: Center( + child: _handlePreview(), + ), + floatingActionButton: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Semantics( + label: 'image_picker_example_from_gallery', + child: FloatingActionButton( + onPressed: () { + _isVideo = false; + _onImageButtonPressed(ImageSource.gallery, context: context); + }, + heroTag: 'image0', + tooltip: 'Pick Image from gallery', + child: const Icon(Icons.photo), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + _isVideo = false; + _onImageButtonPressed( + ImageSource.gallery, + context: context, + isMultiImage: true, + ); + }, + heroTag: 'image1', + tooltip: 'Pick Multiple Image from gallery', + child: const Icon(Icons.photo_library), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + _isVideo = false; + _onImageButtonPressed(ImageSource.camera, context: context); + }, + heroTag: 'image2', + tooltip: 'Take a Photo', + child: const Icon(Icons.camera_alt), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + backgroundColor: Colors.red, + onPressed: () { + _isVideo = true; + _onImageButtonPressed(ImageSource.gallery); + }, + heroTag: 'video0', + tooltip: 'Pick Video from gallery', + child: const Icon(Icons.video_library), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + backgroundColor: Colors.red, + onPressed: () { + _isVideo = true; + _onImageButtonPressed(ImageSource.camera); + }, + heroTag: 'video1', + tooltip: 'Take a Video', + child: const Icon(Icons.videocam), + ), + ), + ], + ), + ); + } + + Text? _getRetrieveErrorWidget() { + if (_retrieveDataError != null) { + final Text result = Text(_retrieveDataError!); + _retrieveDataError = null; + return result; + } + return null; + } + + Future _displayPickImageDialog( + BuildContext context, OnPickImageCallback onPick) async { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Add optional parameters'), + content: Column( + children: [ + TextField( + controller: maxWidthController, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxWidth if desired'), + ), + TextField( + controller: maxHeightController, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxHeight if desired'), + ), + TextField( + controller: qualityController, + keyboardType: TextInputType.number, + decoration: const InputDecoration( + hintText: 'Enter quality if desired'), + ), + ], + ), + actions: [ + TextButton( + child: const Text('CANCEL'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text('PICK'), + onPressed: () { + final double? width = maxWidthController.text.isNotEmpty + ? double.parse(maxWidthController.text) + : null; + final double? height = maxHeightController.text.isNotEmpty + ? double.parse(maxHeightController.text) + : null; + final int? quality = qualityController.text.isNotEmpty + ? int.parse(qualityController.text) + : null; + onPick(width, height, quality); + Navigator.of(context).pop(); + }), + ], + ); + }); + } +} + +typedef OnPickImageCallback = void Function( + double? maxWidth, double? maxHeight, int? quality); + +class AspectRatioVideo extends StatefulWidget { + const AspectRatioVideo(this.controller); + + final VideoPlayerController? controller; + + @override + AspectRatioVideoState createState() => AspectRatioVideoState(); +} + +class AspectRatioVideoState extends State { + VideoPlayerController? get controller => widget.controller; + bool initialized = false; + + void _onVideoControllerUpdate() { + if (!mounted) { + return; + } + if (initialized != controller!.value.isInitialized) { + initialized = controller!.value.isInitialized; + setState(() {}); + } + } + + @override + void initState() { + super.initState(); + controller!.addListener(_onVideoControllerUpdate); + } + + @override + void dispose() { + controller!.removeListener(_onVideoControllerUpdate); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (initialized) { + return Center( + child: AspectRatio( + aspectRatio: controller!.value.aspectRatio, + child: VideoPlayer(controller!), + ), + ); + } else { + return Container(); + } + } +} diff --git a/packages/image_picker/image_picker_windows/example/pubspec.yaml b/packages/image_picker/image_picker_windows/example/pubspec.yaml new file mode 100644 index 000000000000..68c9395c6097 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/pubspec.yaml @@ -0,0 +1,27 @@ +name: example +description: Example for image_picker_windows implementation. +publish_to: 'none' +version: 1.0.0 + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + image_picker_windows: + # When depending on this package from a real application you should use: + # image_picker_windows: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # 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: .. + video_player: ^2.1.4 + +dev_dependencies: + flutter_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/image_picker/image_picker_windows/example/windows/.gitignore b/packages/image_picker/image_picker_windows/example/windows/.gitignore new file mode 100644 index 000000000000..d492d0d98c8f --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/packages/image_picker/image_picker_windows/example/windows/CMakeLists.txt b/packages/image_picker/image_picker_windows/example/windows/CMakeLists.txt new file mode 100644 index 000000000000..1633297a0c7c --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.14) +project(example LANGUAGES CXX) + +set(BINARY_NAME "example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) 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 new file mode 100644 index 000000000000..b2e4bd8d658b --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/packages/image_picker/image_picker_windows/example/windows/flutter/generated_plugins.cmake b/packages/image_picker/image_picker_windows/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000000..63eda9b7b59f --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,16 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + file_selector_windows +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/CMakeLists.txt b/packages/image_picker/image_picker_windows/example/windows/runner/CMakeLists.txt new file mode 100644 index 000000000000..de2d8916b72b --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) 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 new file mode 100644 index 000000000000..5fdea291cf19 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/flutter_window.cpp b/packages/image_picker/image_picker_windows/example/windows/runner/flutter_window.cpp new file mode 100644 index 000000000000..8254bd9ff3c1 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/flutter_window.cpp @@ -0,0 +1,65 @@ +// 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. + +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/flutter_window.h b/packages/image_picker/image_picker_windows/example/windows/runner/flutter_window.h new file mode 100644 index 000000000000..f1fc669093d0 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/flutter_window.h @@ -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. + +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/main.cpp b/packages/image_picker/image_picker_windows/example/windows/runner/main.cpp new file mode 100644 index 000000000000..df379fa0be93 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/main.cpp @@ -0,0 +1,46 @@ +// 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. + +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t* command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/resource.h b/packages/image_picker/image_picker_windows/example/windows/runner/resource.h new file mode 100644 index 000000000000..d5d958dc4257 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/resources/app_icon.ico b/packages/image_picker/image_picker_windows/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 zcmeHQc|26z|35SKE&G-*mXah&B~fFkXr)DEO&hIfqby^T&>|8^_Ub8Vp#`BLl3lbZ zvPO!8k!2X>cg~Elr=IVxo~J*a`+9wR=A83c-k-DFd(XM&UI1VKCqM@V;DDtJ09WB} zRaHKiW(GT00brH|0EeTeKVbpbGZg?nK6-j827q-+NFM34gXjqWxJ*a#{b_apGN<-L_m3#8Z26atkEn& ze87Bvv^6vVmM+p+cQ~{u%=NJF>#(d;8{7Q{^rWKWNtf14H}>#&y7$lqmY6xmZryI& z($uy?c5-+cPnt2%)R&(KIWEXww>Cnz{OUpT>W$CbO$h1= z#4BPMkFG1Y)x}Ui+WXr?Z!w!t_hjRq8qTaWpu}FH{MsHlU{>;08goVLm{V<&`itk~ zE_Ys=D(hjiy+5=?=$HGii=Y5)jMe9|wWoD_K07(}edAxh`~LBorOJ!Cf@f{_gNCC| z%{*04ViE!#>@hc1t5bb+NO>ncf@@Dv01K!NxH$3Eg1%)|wLyMDF8^d44lV!_Sr}iEWefOaL z8f?ud3Q%Sen39u|%00W<#!E=-RpGa+H8}{ulxVl4mwpjaU+%2pzmi{3HM)%8vb*~-M9rPUAfGCSos8GUXp02|o~0BTV2l#`>>aFV&_P$ejS;nGwSVP8 zMbOaG7<7eKD>c12VdGH;?2@q7535sa7MN*L@&!m?L`ASG%boY7(&L5imY#EQ$KrBB z4@_tfP5m50(T--qv1BJcD&aiH#b-QC>8#7Fx@3yXlonJI#aEIi=8&ChiVpc#N=5le zM*?rDIdcpawoc5kizv$GEjnveyrp3sY>+5_R5;>`>erS%JolimF=A^EIsAK zsPoVyyUHCgf0aYr&alx`<)eb6Be$m&`JYSuBu=p8j%QlNNp$-5C{b4#RubPb|CAIS zGE=9OFLP7?Hgc{?k45)84biT0k&-C6C%Q}aI~q<(7BL`C#<6HyxaR%!dFx7*o^laG z=!GBF^cwK$IA(sn9y6>60Rw{mYRYkp%$jH z*xQM~+bp)G$_RhtFPYx2HTsWk80+p(uqv9@I9)y{b$7NK53rYL$ezbmRjdXS?V}fj zWxX_feWoLFNm3MG7pMUuFPs$qrQWO9!l2B(SIuy2}S|lHNbHzoE+M2|Zxhjq9+Ws8c{*}x^VAib7SbxJ*Q3EnY5lgI9 z=U^f3IW6T=TWaVj+2N%K3<%Un;CF(wUp`TC&Y|ZjyFu6co^uqDDB#EP?DV5v_dw~E zIRK*BoY9y-G_ToU2V_XCX4nJ32~`czdjT!zwme zGgJ0nOk3U4@IE5JwtM}pwimLjk{ln^*4HMU%Fl4~n(cnsLB}Ja-jUM>xIB%aY;Nq8 z)Fp8dv1tkqKanv<68o@cN|%thj$+f;zGSO7H#b+eMAV8xH$hLggtt?O?;oYEgbq@= zV(u9bbd12^%;?nyk6&$GPI%|+<_mEpJGNfl*`!KV;VfmZWw{n{rnZ51?}FDh8we_L z8OI9nE31skDqJ5Oa_ybn7|5@ui>aC`s34p4ZEu6-s!%{uU45$Zd1=p$^^dZBh zu<*pDDPLW+c>iWO$&Z_*{VSQKg7=YEpS3PssPn1U!lSm6eZIho*{@&20e4Y_lRklKDTUCKI%o4Pc<|G^Xgu$J^Q|B87U;`c1zGwf^-zH*VQ^x+i^OUWE0yd z;{FJq)2w!%`x7yg@>uGFFf-XJl4H`YtUG%0slGKOlXV`q?RP>AEWg#x!b{0RicxGhS!3$p7 zij;{gm!_u@D4$Ox%>>bPtLJ> zwKtYz?T_DR1jN>DkkfGU^<#6sGz|~p*I{y`aZ>^Di#TC|Z!7j_O1=Wo8thuit?WxR zh9_S>kw^{V^|g}HRUF=dcq>?q(pHxw!8rx4dC6vbQVmIhmICF#zU!HkHpQ>9S%Uo( zMw{eC+`&pb=GZRou|3;Po1}m46H6NGd$t<2mQh}kaK-WFfmj_66_17BX0|j-E2fe3Jat}ijpc53 zJV$$;PC<5aW`{*^Z6e5##^`Ed#a0nwJDT#Qq~^e8^JTA=z^Kl>La|(UQ!bI@#ge{Dzz@61p-I)kc2?ZxFt^QQ}f%ldLjO*GPj(5)V9IyuUakJX=~GnTgZ4$5!3E=V#t`yOG4U z(gphZB6u2zsj=qNFLYShhg$}lNpO`P9xOSnO*$@@UdMYES*{jJVj|9z-}F^riksLK zbsU+4-{281P9e2UjY6tse^&a)WM1MFw;p#_dHhWI7p&U*9TR0zKdVuQed%6{otTsq z$f~S!;wg#Bd9kez=Br{m|66Wv z#g1xMup<0)H;c2ZO6su_ii&m8j&+jJz4iKnGZ&wxoQX|5a>v&_e#6WA!MB_4asTxLRGQCC5cI(em z%$ZfeqP>!*q5kU>a+BO&ln=4Jm>Ef(QE8o&RgLkk%2}4Tf}U%IFP&uS7}&|Q-)`5< z+e>;s#4cJ-z%&-^&!xsYx777Wt(wZY9(3(avmr|gRe4cD+a8&!LY`1^T?7x{E<=kdY9NYw>A;FtTvQ=Y&1M%lyZPl$ss1oY^Sl8we}n}Aob#6 zl4jERwnt9BlSoWb@3HxYgga(752Vu6Y)k4yk9u~Kw>cA5&LHcrvn1Y-HoIuFWg~}4 zEw4bR`mXZQIyOAzo)FYqg?$5W<;^+XX%Uz61{-L6@eP|lLH%|w?g=rFc;OvEW;^qh z&iYXGhVt(G-q<+_j}CTbPS_=K>RKN0&;dubh0NxJyDOHFF;<1k!{k#7b{|Qok9hac z;gHz}6>H6C6RnB`Tt#oaSrX0p-j-oRJ;_WvS-qS--P*8}V943RT6kou-G=A+7QPGQ z!ze^UGxtW3FC0$|(lY9^L!Lx^?Q8cny(rR`es5U;-xBhphF%_WNu|aO<+e9%6LuZq zt(0PoagJG<%hyuf;te}n+qIl_Ej;czWdc{LX^pS>77s9t*2b4s5dvP_!L^3cwlc)E!(!kGrg~FescVT zZCLeua3f4;d;Tk4iXzt}g}O@nlK3?_o91_~@UMIl?@77Qc$IAlLE95#Z=TES>2E%z zxUKpK{_HvGF;5%Q7n&vA?`{%8ohlYT_?(3A$cZSi)MvIJygXD}TS-3UwyUxGLGiJP znblO~G|*uA^|ac8E-w#}uBtg|s_~s&t>-g0X%zIZ@;o_wNMr_;{KDg^O=rg`fhDZu zFp(VKd1Edj%F zWHPl+)FGj%J1BO3bOHVfH^3d1F{)*PL&sRX`~(-Zy3&9UQX)Z;c51tvaI2E*E7!)q zcz|{vpK7bjxix(k&6=OEIBJC!9lTkUbgg?4-yE{9+pFS)$Ar@vrIf`D0Bnsed(Cf? zObt2CJ>BKOl>q8PyFO6w)+6Iz`LW%T5^R`U_NIW0r1dWv6OY=TVF?N=EfA(k(~7VBW(S;Tu5m4Lg8emDG-(mOSSs=M9Q&N8jc^Y4&9RqIsk(yO_P(mcCr}rCs%1MW1VBrn=0-oQN(Xj!k%iKV zb%ricBF3G4S1;+8lzg5PbZ|$Se$)I=PwiK=cDpHYdov2QO1_a-*dL4KUi|g&oh>(* zq$<`dQ^fat`+VW?m)?_KLn&mp^-@d=&7yGDt<=XwZZC=1scwxO2^RRI7n@g-1o8ps z)&+et_~)vr8aIF1VY1Qrq~Xe``KJrQSnAZ{CSq3yP;V*JC;mmCT6oRLSs7=GA?@6g zUooM}@tKtx(^|aKK8vbaHlUQqwE0}>j&~YlN3H#vKGm@u)xxS?n9XrOWUfCRa< z`20Fld2f&;gg7zpo{Adh+mqNntMc-D$N^yWZAZRI+u1T1zWHPxk{+?vcS1D>08>@6 zLhE@`gt1Y9mAK6Z4p|u(5I%EkfU7rKFSM=E4?VG9tI;a*@?6!ey{lzN5=Y-!$WFSe z&2dtO>^0@V4WRc#L&P%R(?@KfSblMS+N+?xUN$u3K4Ys%OmEh+tq}fnU}i>6YHM?< zlnL2gl~sF!j!Y4E;j3eIU-lfa`RsOL*Tt<%EFC0gPzoHfNWAfKFIKZN8}w~(Yi~=q z>=VNLO2|CjkxP}RkutxjV#4fWYR1KNrPYq5ha9Wl+u>ipsk*I(HS@iLnmGH9MFlTU zaFZ*KSR0px>o+pL7BbhB2EC1%PJ{67_ z#kY&#O4@P=OV#-79y_W>Gv2dxL*@G7%LksNSqgId9v;2xJ zrh8uR!F-eU$NMx@S*+sk=C~Dxr9Qn7TfWnTupuHKuQ$;gGiBcU>GF5sWx(~4IP3`f zWE;YFO*?jGwYh%C3X<>RKHC-DZ!*r;cIr}GLOno^3U4tFSSoJp%oHPiSa%nh=Zgn% z14+8v@ygy0>UgEN1bczD6wK45%M>psM)y^)IfG*>3ItX|TzV*0i%@>L(VN!zdKb8S?Qf7BhjNpziA zR}?={-eu>9JDcl*R=OP9B8N$IcCETXah9SUDhr{yrld{G;PnCWRsPD7!eOOFBTWUQ=LrA_~)mFf&!zJX!Oc-_=kT<}m|K52 z)M=G#;p;Rdb@~h5D{q^K;^fX-m5V}L%!wVC2iZ1uu401Ll}#rocTeK|7FAeBRhNdQ zCc2d^aQnQp=MpOmak60N$OgS}a;p(l9CL`o4r(e-nN}mQ?M&isv-P&d$!8|1D1I(3-z!wi zTgoo)*Mv`gC?~bm?S|@}I|m-E2yqPEvYybiD5azInexpK8?9q*$9Yy9-t%5jU8~ym zgZDx>!@ujQ=|HJnwp^wv-FdD{RtzO9SnyfB{mH_(c!jHL*$>0o-(h(eqe*ZwF6Lvu z{7rkk%PEqaA>o+f{H02tzZ@TWy&su?VNw43! z-X+rN`6llvpUms3ZiSt)JMeztB~>9{J8SPmYs&qohxdYFi!ra8KR$35Zp9oR)eFC4 zE;P31#3V)n`w$fZ|4X-|%MX`xZDM~gJyl2W;O$H25*=+1S#%|53>|LyH za@yh+;325%Gq3;J&a)?%7X%t@WXcWL*BaaR*7UEZad4I8iDt7^R_Fd`XeUo256;sAo2F!HcIQKk;h})QxEsPE5BcKc7WyerTchgKmrfRX z!x#H_%cL#B9TWAqkA4I$R^8{%do3Y*&(;WFmJ zU7Dih{t1<{($VtJRl9|&EB?|cJ)xse!;}>6mSO$o5XIx@V|AA8ZcoD88ZM?C*;{|f zZVmf94_l1OmaICt`2sTyG!$^UeTHx9YuUP!omj(r|7zpm5475|yXI=rR>>fteLI+| z)MoiGho0oEt=*J(;?VY0QzwCqw@cVm?d7Y!z0A@u#H?sCJ*ecvyhj& z-F77lO;SH^dmf?L>3i>?Z*U}Em4ZYV_CjgfvzYsRZ+1B!Uo6H6mbS<-FFL`ytqvb& zE7+)2ahv-~dz(Hs+f})z{*4|{)b=2!RZK;PWwOnO=hG7xG`JU5>bAvUbdYd_CjvtHBHgtGdlO+s^9ca^Bv3`t@VRX2_AD$Ckg36OcQRF zXD6QtGfHdw*hx~V(MV-;;ZZF#dJ-piEF+s27z4X1qi5$!o~xBnvf=uopcn7ftfsZc zy@(PuOk`4GL_n(H9(E2)VUjqRCk9kR?w)v@xO6Jm_Mx})&WGEl=GS0#)0FAq^J*o! zAClhvoTsNP*-b~rN{8Yym3g{01}Ep^^Omf=SKqvN?{Q*C4HNNAcrowIa^mf+3PRy! z*_G-|3i8a;+q;iP@~Of_$(vtFkB8yOyWt2*K)vAn9El>=D;A$CEx6b*XF@4y_6M+2 zpeW`RHoI_p(B{%(&jTHI->hmNmZjHUj<@;7w0mx3&koy!2$@cfX{sN19Y}euYJFn& z1?)+?HCkD0MRI$~uB2UWri})0bru_B;klFdwsLc!ne4YUE;t41JqfG# zZJq6%vbsdx!wYeE<~?>o4V`A3?lN%MnKQ`z=uUivQN^vzJ|C;sdQ37Qn?;lpzg})y z)_2~rUdH}zNwX;Tp0tJ78+&I=IwOQ-fl30R79O8@?Ub8IIA(6I`yHn%lARVL`%b8+ z4$8D-|MZZWxc_)vu6@VZN!HsI$*2NOV&uMxBNzIbRgy%ob_ zhwEH{J9r$!dEix9XM7n&c{S(h>nGm?el;gaX0@|QnzFD@bne`el^CO$yXC?BDJ|Qg z+y$GRoR`?ST1z^e*>;!IS@5Ovb7*RlN>BV_UC!7E_F;N#ky%1J{+iixp(dUJj93aK zzHNN>R-oN7>kykHClPnoPTIj7zc6KM(Pnlb(|s??)SMb)4!sMHU^-ntJwY5Big7xv zb1Ew`Xj;|D2kzGja*C$eS44(d&RMU~c_Y14V9_TLTz0J#uHlsx`S6{nhsA0dWZ#cG zJ?`fO50E>*X4TQLv#nl%3GOk*UkAgt=IY+u0LNXqeln3Z zv$~&Li`ZJOKkFuS)dJRA>)b_Da%Q~axwA_8zNK{BH{#}#m}zGcuckz}riDE-z_Ms> zR8-EqAMcfyGJCtvTpaUVQtajhUS%c@Yj}&6Zz;-M7MZzqv3kA7{SuW$oW#=0az2wQ zg-WG@Vb4|D`pl~Il54N7Hmsauc_ne-a!o5#j3WaBBh@Wuefb!QJIOn5;d)%A#s+5% zuD$H=VNux9bE-}1&bcYGZ+>1Fo;3Z@e&zX^n!?JK*adSbONm$XW9z;Q^L>9U!}Toj2WdafJ%oL#h|yWWwyAGxzfrAWdDTtaKl zK4`5tDpPg5>z$MNv=X0LZ0d6l%D{(D8oT@+w0?ce$DZ6pv>{1&Ok67Ix1 zH}3=IEhPJEhItCC8E=`T`N5(k?G=B4+xzZ?<4!~ ze~z6Wk9!CHTI(0rLJ4{JU?E-puc;xusR?>G?;4vt;q~iI9=kDL=z0Rr%O$vU`30X$ zDZRFyZ`(omOy@u|i6h;wtJlP;+}$|Ak|k2dea7n?U1*$T!sXqqOjq^NxLPMmk~&qI zYg0W?yK8T(6+Ea+$YyspKK?kP$+B`~t3^Pib_`!6xCs32!i@pqXfFV6PmBIR<-QW= zN8L{pt0Vap0x`Gzn#E@zh@H)0FfVfA_Iu4fjYZ+umO1LXIbVc$pY+E234u)ttcrl$ z>s92z4vT%n6cMb>=XT6;l0+9e(|CZG)$@C7t7Z7Ez@a)h)!hyuV&B5K%%)P5?Lk|C zZZSVzdXp{@OXSP0hoU-gF8s8Um(#xzjP2Vem zec#-^JqTa&Y#QJ>-FBxd7tf`XB6e^JPUgagB8iBSEps;92KG`!#mvVcPQ5yNC-GEG zTiHEDYfH+0O15}r^+ z#jxj=@x8iNHWALe!P3R67TwmhItn**0JwnzSV2O&KE8KcT+0hWH^OPD1pwiuyx=b@ zNf5Jh0{9X)8;~Es)$t@%(3!OnbY+`@?i{mGX7Yy}8T_*0a6g;kaFPq;*=px5EhO{Cp%1kI<0?*|h8v!6WnO3cCJRF2-CRrU3JiLJnj@6;L)!0kWYAc_}F{2P))3HmCrz zQ&N&gE70;`!6*eJ4^1IR{f6j4(-l&X!tjHxkbHA^Zhrnhr9g{exN|xrS`5Pq=#Xf& zG%P=#ra-TyVFfgW%cZo5OSIwFL9WtXAlFOa+ubmI5t*3=g#Y zF%;70p5;{ZeFL}&}yOY1N1*Q;*<(kTB!7vM$QokF)yr2FlIU@$Ph58$Bz z0J?xQG=MlS4L6jA22eS42g|9*9pX@$#*sUeM(z+t?hr@r5J&D1rx}2pW&m*_`VDCW zUYY@v-;bAO0HqoAgbbiGGC<=ryf96}3pouhy3XJrX+!!u*O_>Si38V{uJmQ&USptX zKp#l(?>%^7;2%h(q@YWS#9;a!JhKlkR#Vd)ERILlgu!Hr@jA@V;sk4BJ-H#p*4EqC zDGjC*tl=@3Oi6)Bn^QwFpul18fpkbpg0+peH$xyPBqb%`$OUhPKyWb32o7clB*9Z< zN=i~NLjavrLtwgJ01bufP+>p-jR2I95|TpmKpQL2!oV>g(4RvS2pK4*ou%m(h6r3A zX#s&`9LU1ZG&;{CkOK!4fLDTnBys`M!vuz>Q&9OZ0hGQl!~!jSDg|~s*w52opC{sB ze|Cf2luD(*G13LcOAGA!s2FjSK8&IE5#W%J25w!vM0^VyQM!t)inj&RTiJ!wXzFgz z3^IqzB7I0L$llljsGq})thBy9UOyjtFO_*hYM_sgcMk>44jeH0V1FDyELc{S1F-;A zS;T^k^~4biG&V*Irq}O;e}j$$+E_#G?HKIn05iP3j|87TkGK~SqG!-KBg5+mN(aLm z8ybhIM`%C19UX$H$KY6JgXbY$0AT%rEpHC;u`rQ$Y=rxUdsc5*Kvc8jaYaO$^)cI6){P6K0r)I6DY4Wr4&B zLQUBraey#0HV|&c4v7PVo3n$zHj99(TZO^3?Ly%C4nYvJTL9eLBLHsM3WKKD>5!B` zQ=BsR3aR6PD(Fa>327E2HAu5TM~Wusc!)>~(gM)+3~m;92Jd;FnSib=M5d6;;5{%R zb4V7DEJ0V!CP-F*oU?gkc>ksUtAYP&V4ND5J>J2^jt*vcFflQWCrB&fLdT%O59PVJ zhid#toR=FNgD!q3&r8#wEBr`!wzvQu5zX?Q>nlSJ4i@WC*CN*-xU66F^V5crWevQ9gsq$I@z1o(a=k7LL~ z7m_~`o;_Ozha1$8Q}{WBehvAlO4EL60y5}8GDrZ< zXh&F}71JbW2A~8KfEWj&UWV#4+Z4p`b{uAj4&WC zha`}X@3~+Iz^WRlOHU&KngK>#j}+_o@LdBC1H-`gT+krWX3-;!)6?{FBp~%20a}FL zFP9%Emqcwa#(`=G>BBZ0qZDQhmZKJg_g8<=bBFKWr!dyg(YkpE+|R*SGpDVU!+VlU zFC54^DLv}`qa%49T>nNiA9Q7Ips#!Xx90tCU2gvK`(F+GPcL=J^>No{)~we#o@&mUb6c$ zCc*<|NJBk-#+{j9xkQ&ujB zI~`#kN~7W!f*-}wkG~Ld!JqZ@tK}eeSnsS5J1fMFXm|`LJx&}5`@dK3W^7#Wnm+_P zBZkp&j1fa2Y=eIjJ0}gh85jt43kaIXXv?xmo@eHrka!Z|vQv12HN#+!I5E z`(fbuW>gFiJL|uXJ!vKt#z3e3HlVdboH7;e#i3(2<)Fg-I@BR!qY#eof3MFZ&*Y@l zI|KJf&ge@p2Dq09Vu$$Qxb7!}{m-iRk@!)%KL)txi3;~Z4Pb}u@GsW;ELiWeG9V51 znX#}B&4Y2E7-H=OpNE@q{%hFLxwIpBF2t{vPREa8_{linXT;#1vMRWjOzLOP$-hf( z>=?$0;~~PnkqY;~K{EM6Vo-T(0K{A0}VUGmu*hR z{tw3hvBN%N3G3Yw`X5Te+F{J`(3w1s3-+1EbnFQKcrgrX1Jqvs@ADGe%M0s$EbK$$ zK)=y=upBc6SjGYAACCcI=Y*6Fi8_jgwZlLxD26fnQfJmb8^gHRN5(TemhX@0e=vr> zg`W}6U>x6VhoA3DqsGGD9uL1DhB3!OXO=k}59TqD@(0Nb{)Ut_luTioK_>7wjc!5C zIr@w}b`Fez3)0wQfKl&bae7;PcTA7%?f2xucM0G)wt_KO!Ewx>F~;=BI0j=Fb4>pp zv}0R^xM4eti~+^+gE$6b81p(kwzuDti(-K9bc|?+pJEl@H+jSYuxZQV8rl8 zjp@M{#%qItIUFN~KcO9Hed*`$5A-2~pAo~K&<-Q+`9`$CK>rzqAI4w~$F%vs9s{~x zg4BP%Gy*@m?;D6=SRX?888Q6peF@_4Z->8wAH~Cn!R$|Hhq2cIzFYqT_+cDourHbY z0qroxJnrZ4Gh+Ay+F`_c%+KRT>y3qw{)89?=hJ@=KO=@ep)aBJ$c!JHfBMJpsP*3G za7|)VJJ8B;4?n{~ldJF7%jmb`-ftIvNd~ekoufG(`K(3=LNc;HBY& z(lp#q8XAD#cIf}k49zX_i`*fO+#!zKA&%T3j@%)R+#yag067CU%yUEe47>wzGU8^` z1EXFT^@I!{J!F8!X?S6ph8J=gUi5tl93*W>7}_uR<2N2~e}FaG?}KPyugQ=-OGEZs z!GBoyYY+H*ANn4?Z)X4l+7H%`17i5~zRlRIX?t)6_eu=g2Q`3WBhxSUeea+M-S?RL zX9oBGKn%a!H+*hx4d2(I!gsi+@SQK%<{X22M~2tMulJoa)0*+z9=-YO+;DFEm5eE1U9b^B(Z}2^9!Qk`!A$wUE z7$Ar5?NRg2&G!AZqnmE64eh^Anss3i!{}%6@Et+4rr!=}!SBF8eZ2*J3ujCWbl;3; z48H~goPSv(8X61fKKdpP!Z7$88NL^Z?j`!^*I?-P4X^pMxyWz~@$(UeAcTSDd(`vO z{~rc;9|GfMJcApU3k}22a!&)k4{CU!e_ny^Y3cO;tOvOMKEyWz!vG(Kp*;hB?d|R3`2X~=5a6#^o5@qn?J-bI8Ppip{-yG z!k|VcGsq!jF~}7DMr49Wap-s&>o=U^T0!Lcy}!(bhtYsPQy z4|EJe{12QL#=c(suQ89Mhw9<`bui%nx7Nep`C&*M3~vMEACmcRYYRGtANq$F%zh&V zc)cEVeHz*Z1N)L7k-(k3np#{GcDh2Q@ya0YHl*n7fl*ZPAsbU-a94MYYtA#&!c`xGIaV;yzsmrjfieTEtqB_WgZp2*NplHx=$O{M~2#i_vJ{ps-NgK zQsxKK_CBM2PP_je+Xft`(vYfXXgIUr{=PA=7a8`2EHk)Ym2QKIforz# tySWtj{oF3N9@_;i*Fv5S)9x^z=nlWP>jpp-9)52ZmLVA=i*%6g{{fxOO~wEK literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/runner.exe.manifest b/packages/image_picker/image_picker_windows/example/windows/runner/runner.exe.manifest new file mode 100644 index 000000000000..c977c4a42589 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/utils.cpp b/packages/image_picker/image_picker_windows/example/windows/runner/utils.cpp new file mode 100644 index 000000000000..fb7e945a63b7 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/utils.cpp @@ -0,0 +1,67 @@ +// 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. + +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE* unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = + ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, + nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/utils.h b/packages/image_picker/image_picker_windows/example/windows/runner/utils.h new file mode 100644 index 000000000000..bd81e1e02338 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/utils.h @@ -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. + +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/win32_window.cpp b/packages/image_picker/image_picker_windows/example/windows/runner/win32_window.cpp new file mode 100644 index 000000000000..85aa3614e8ad --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/win32_window.cpp @@ -0,0 +1,241 @@ +// 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. + +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { ++g_active_window_count; } + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { return window_handle_; } + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/packages/image_picker/image_picker_windows/example/windows/runner/win32_window.h b/packages/image_picker/image_picker_windows/example/windows/runner/win32_window.h new file mode 100644 index 000000000000..d2a730052223 --- /dev/null +++ b/packages/image_picker/image_picker_windows/example/windows/runner/win32_window.h @@ -0,0 +1,99 @@ +// 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. + +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart b/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart new file mode 100644 index 000000000000..9bd26c471b4e --- /dev/null +++ b/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart @@ -0,0 +1,167 @@ +// 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:async'; + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:file_selector_windows/file_selector_windows.dart'; +import 'package:flutter/foundation.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; + +/// The Windows implementation of [ImagePickerPlatform]. +/// +/// This class implements the `package:image_picker` functionality for +/// Windows. +class ImagePickerWindows extends ImagePickerPlatform { + /// Constructs a ImagePickerWindows. + ImagePickerWindows(); + + /// List of image extensions used when picking images + @visibleForTesting + static const List imageFormats = [ + 'jpg', + 'jpeg', + 'png', + 'bmp', + 'webp', + 'gif', + 'tif', + 'tiff', + 'apng' + ]; + + /// List of video extensions used when picking videos + @visibleForTesting + static const List videoFormats = [ + 'mov', + 'wmv', + 'mkv', + 'mp4', + 'webm', + 'avi', + 'mpeg', + 'mpg' + ]; + + /// The file selector used to prompt the user to select images or videos. + @visibleForTesting + static late FileSelectorPlatform fileSelector = FileSelectorWindows(); + + /// Registers this class as the default instance of [ImagePickerPlatform]. + static void registerWith() { + ImagePickerPlatform.instance = ImagePickerWindows(); + } + + // `maxWidth`, `maxHeight`, `imageQuality` and `preferredCameraDevice` + // arguments are not supported on Windows. If any of these arguments + // is supplied, it'll be silently ignored by the Windows version of + // the plugin. `source` is not implemented for `ImageSource.camera` + // and will throw an exception. + @override + Future pickImage({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) async { + final XFile? file = await getImage( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice); + if (file != null) { + return PickedFile(file.path); + } + return null; + } + + // `preferredCameraDevice` and `maxDuration` arguments are not + // supported on Windows. If any of these arguments is supplied, + // it'll be silently ignored by the Windows version of the plugin. + // `source` is not implemented for `ImageSource.camera` and will + // throw an exception. + @override + Future pickVideo({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) async { + final XFile? file = await getVideo( + source: source, + preferredCameraDevice: preferredCameraDevice, + maxDuration: maxDuration); + if (file != null) { + return PickedFile(file.path); + } + return null; + } + + // `maxWidth`, `maxHeight`, `imageQuality`, and `preferredCameraDevice` + // arguments are not supported on Windows. If any of these arguments + // is supplied, it'll be silently ignored by the Windows version + // of the plugin. `source` is not implemented for `ImageSource.camera` + // and will throw an exception. + @override + Future getImage({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) async { + if (source != ImageSource.gallery) { + // TODO(azchohfi): Support ImageSource.camera. + // See https://github.com/flutter/flutter/issues/102115 + throw UnimplementedError( + 'ImageSource.gallery is currently the only supported source on Windows'); + } + final XTypeGroup typeGroup = + XTypeGroup(label: 'images', extensions: imageFormats); + final XFile? file = await fileSelector + .openFile(acceptedTypeGroups: [typeGroup]); + return file; + } + + // `preferredCameraDevice` and `maxDuration` arguments are not + // supported on Windows. If any of these arguments is supplied, + // it'll be silently ignored by the Windows version of the plugin. + // `source` is not implemented for `ImageSource.camera` and will + // throw an exception. + @override + Future getVideo({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) async { + if (source != ImageSource.gallery) { + // TODO(azchohfi): Support ImageSource.camera. + // See https://github.com/flutter/flutter/issues/102115 + throw UnimplementedError( + 'ImageSource.gallery is currently the only supported source on Windows'); + } + final XTypeGroup typeGroup = + XTypeGroup(label: 'videos', extensions: videoFormats); + final XFile? file = await fileSelector + .openFile(acceptedTypeGroups: [typeGroup]); + return file; + } + + // `maxWidth`, `maxHeight`, and `imageQuality` arguments are not + // supported on Windows. If any of these arguments is supplied, + // it'll be silently ignored by the Windows version of the plugin. + @override + Future> getMultiImage({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + }) async { + final XTypeGroup typeGroup = + XTypeGroup(label: 'images', extensions: imageFormats); + final List files = await fileSelector + .openFiles(acceptedTypeGroups: [typeGroup]); + return files; + } +} diff --git a/packages/image_picker/image_picker_windows/pubspec.yaml b/packages/image_picker/image_picker_windows/pubspec.yaml new file mode 100644 index 000000000000..eec41f7bfa0d --- /dev/null +++ b/packages/image_picker/image_picker_windows/pubspec.yaml @@ -0,0 +1,29 @@ +name: image_picker_windows +description: Windows platform implementation of image_picker +repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_windows +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 +version: 0.1.0 + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=2.5.0" + +flutter: + plugin: + implements: image_picker + platforms: + windows: + dartPluginClass: ImagePickerWindows + +dependencies: + file_selector_platform_interface: ^2.0.4 + file_selector_windows: ^0.8.2 + flutter: + sdk: flutter + image_picker_platform_interface: ^2.4.3 + +dev_dependencies: + build_runner: ^2.1.5 + flutter_test: + sdk: flutter + mockito: ^5.0.16 diff --git a/packages/image_picker/image_picker_windows/test/image_picker_windows_test.dart b/packages/image_picker/image_picker_windows/test/image_picker_windows_test.dart new file mode 100644 index 000000000000..c3df2d80679f --- /dev/null +++ b/packages/image_picker/image_picker_windows/test/image_picker_windows_test.dart @@ -0,0 +1,128 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'package:image_picker_windows/image_picker_windows.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'image_picker_windows_test.mocks.dart'; + +@GenerateMocks([FileSelectorPlatform]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$ImagePickerWindows()', () { + final ImagePickerWindows plugin = ImagePickerWindows(); + late MockFileSelectorPlatform mockFileSelectorPlatform; + + setUp(() { + mockFileSelectorPlatform = MockFileSelectorPlatform(); + + when(mockFileSelectorPlatform.openFile( + acceptedTypeGroups: anyNamed('acceptedTypeGroups'))) + .thenAnswer((_) async => null); + + when(mockFileSelectorPlatform.openFiles( + acceptedTypeGroups: anyNamed('acceptedTypeGroups'))) + .thenAnswer((_) async => List.empty()); + + ImagePickerWindows.fileSelector = mockFileSelectorPlatform; + }); + + test('registered instance', () { + ImagePickerWindows.registerWith(); + expect(ImagePickerPlatform.instance, isA()); + }); + + group('images', () { + test('pickImage passes the accepted type groups correctly', () async { + await plugin.pickImage(source: ImageSource.gallery); + + expect( + verify(mockFileSelectorPlatform.openFile( + acceptedTypeGroups: captureAnyNamed('acceptedTypeGroups'))) + .captured + .single[0] + .extensions, + ImagePickerWindows.imageFormats); + }); + + test('pickImage throws UnimplementedError when source is camera', + () async { + expect(() async => await plugin.pickImage(source: ImageSource.camera), + throwsA(isA())); + }); + + test('getImage passes the accepted type groups correctly', () async { + await plugin.getImage(source: ImageSource.gallery); + + expect( + verify(mockFileSelectorPlatform.openFile( + acceptedTypeGroups: captureAnyNamed('acceptedTypeGroups'))) + .captured + .single[0] + .extensions, + ImagePickerWindows.imageFormats); + }); + + test('getImage throws UnimplementedError when source is camera', + () async { + expect(() async => await plugin.getImage(source: ImageSource.camera), + throwsA(isA())); + }); + + test('getMultiImage passes the accepted type groups correctly', () async { + await plugin.getMultiImage(); + + expect( + verify(mockFileSelectorPlatform.openFiles( + acceptedTypeGroups: captureAnyNamed('acceptedTypeGroups'))) + .captured + .single[0] + .extensions, + ImagePickerWindows.imageFormats); + }); + }); + group('videos', () { + test('pickVideo passes the accepted type groups correctly', () async { + await plugin.pickVideo(source: ImageSource.gallery); + + expect( + verify(mockFileSelectorPlatform.openFile( + acceptedTypeGroups: captureAnyNamed('acceptedTypeGroups'))) + .captured + .single[0] + .extensions, + ImagePickerWindows.videoFormats); + }); + + test('pickVideo throws UnimplementedError when source is camera', + () async { + expect(() async => await plugin.pickVideo(source: ImageSource.camera), + throwsA(isA())); + }); + + test('getVideo passes the accepted type groups correctly', () async { + await plugin.getVideo(source: ImageSource.gallery); + + expect( + verify(mockFileSelectorPlatform.openFile( + acceptedTypeGroups: captureAnyNamed('acceptedTypeGroups'))) + .captured + .single[0] + .extensions, + ImagePickerWindows.videoFormats); + }); + + test('getVideo throws UnimplementedError when source is camera', + () async { + expect(() async => await plugin.getVideo(source: ImageSource.camera), + throwsA(isA())); + }); + }); + }); +} diff --git a/packages/image_picker/image_picker_windows/test/image_picker_windows_test.mocks.dart b/packages/image_picker/image_picker_windows/test/image_picker_windows_test.mocks.dart new file mode 100644 index 000000000000..be2dd2ac5768 --- /dev/null +++ b/packages/image_picker/image_picker_windows/test/image_picker_windows_test.mocks.dart @@ -0,0 +1,78 @@ +// Mocks generated by Mockito 5.1.0 from annotations +// in image_picker_windows/example/windows/flutter/ephemeral/.plugin_symlinks/image_picker_windows/test/image_picker_windows_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i3; + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart' + as _i2; +import 'package:mockito/mockito.dart' as _i1; + +// 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: 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 + +/// A class which mocks [FileSelectorPlatform]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockFileSelectorPlatform extends _i1.Mock + implements _i2.FileSelectorPlatform { + MockFileSelectorPlatform() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future<_i2.XFile?> openFile( + {List<_i2.XTypeGroup>? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText}) => + (super.noSuchMethod( + Invocation.method(#openFile, [], { + #acceptedTypeGroups: acceptedTypeGroups, + #initialDirectory: initialDirectory, + #confirmButtonText: confirmButtonText + }), + returnValue: Future<_i2.XFile?>.value()) as _i3.Future<_i2.XFile?>); + @override + _i3.Future> openFiles( + {List<_i2.XTypeGroup>? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText}) => + (super.noSuchMethod( + Invocation.method(#openFiles, [], { + #acceptedTypeGroups: acceptedTypeGroups, + #initialDirectory: initialDirectory, + #confirmButtonText: confirmButtonText + }), + returnValue: Future>.value(<_i2.XFile>[])) + as _i3.Future>); + @override + _i3.Future getSavePath( + {List<_i2.XTypeGroup>? acceptedTypeGroups, + String? initialDirectory, + String? suggestedName, + String? confirmButtonText}) => + (super.noSuchMethod( + Invocation.method(#getSavePath, [], { + #acceptedTypeGroups: acceptedTypeGroups, + #initialDirectory: initialDirectory, + #suggestedName: suggestedName, + #confirmButtonText: confirmButtonText + }), + returnValue: Future.value()) as _i3.Future); + @override + _i3.Future getDirectoryPath( + {String? initialDirectory, String? confirmButtonText}) => + (super.noSuchMethod( + Invocation.method(#getDirectoryPath, [], { + #initialDirectory: initialDirectory, + #confirmButtonText: confirmButtonText + }), + returnValue: Future.value()) as _i3.Future); +} diff --git a/script/configs/exclude_integration_win32.yaml b/script/configs/exclude_integration_win32.yaml index 4626fbd79ce7..09306691e5ed 100644 --- a/script/configs/exclude_integration_win32.yaml +++ b/script/configs/exclude_integration_win32.yaml @@ -1,3 +1,4 @@ # Can't use Flutter integration tests due to native modal UI. - file_selector - file_selector_windows +- image_picker_windows \ No newline at end of file From 755fb82f0d3ca882c6fc14bc4ddc779771f9949a Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 21 Apr 2022 16:08:21 -0400 Subject: [PATCH 182/844] [flutter_plugin_tools] Run pub get for custom-test (#5322) When running a Dart test script for `custom-test`, `pub get` needs to be run first in order for it to work in a clean environment such as CI. This will unblock enabling Pigeon's Dart tests in flutter/packages. --- script/tool/CHANGELOG.md | 3 +- script/tool/lib/src/custom_test_command.dart | 13 ++++- script/tool/pubspec.yaml | 2 +- .../tool/test/custom_test_command_test.dart | 47 ++++++++++++++++++- 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 6e632c67684d..367f258fbeea 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,10 +1,11 @@ -## NEXT +## 0.8.2+1 - Adds a new `readme-check` command. - Updates `publish-plugin` command documentation. - Fixes `all-plugins-app` to preserve the original application's Dart SDK version to avoid changing language feature opt-ins that the template may rely on. +- Fixes `custom-test` to run `pub get` before running Dart test scripts. ## 0.8.2 diff --git a/script/tool/lib/src/custom_test_command.dart b/script/tool/lib/src/custom_test_command.dart index 1c1dfc068a11..cd9ac32606a6 100644 --- a/script/tool/lib/src/custom_test_command.dart +++ b/script/tool/lib/src/custom_test_command.dart @@ -43,10 +43,19 @@ class CustomTestCommand extends PackageLoopingCommand { // Run the custom Dart script if presest. if (script.existsSync()) { - final int exitCode = await processRunner.runAndStream( + // Ensure that dependencies are available. + final int pubGetExitCode = await processRunner.runAndStream( + 'dart', ['pub', 'get'], + workingDir: package.directory); + if (pubGetExitCode != 0) { + return PackageResult.fail( + ['Unable to get script dependencies']); + } + + final int testExitCode = await processRunner.runAndStream( 'dart', ['run', 'tool/$_scriptName'], workingDir: package.directory); - if (exitCode != 0) { + if (testExitCode != 0) { return PackageResult.fail(); } ranTests = true; diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index f005c565be17..14b22a16e3fb 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.8.2 +version: 0.8.2+1 dependencies: args: ^2.1.0 diff --git a/script/tool/test/custom_test_command_test.dart b/script/tool/test/custom_test_command_test.dart index 6a34c2689f6b..bc30d9a1d2e3 100644 --- a/script/tool/test/custom_test_command_test.dart +++ b/script/tool/test/custom_test_command_test.dart @@ -85,6 +85,21 @@ void main() { ])); }); + test('runs pub get before running Dart test script', () async { + final Directory package = createFakePlugin('a_package', packagesDir, + extraFiles: ['tool/run_tests.dart']); + + await runCapturingPrint(runner, ['custom-test']); + + expect( + processRunner.recordedCalls, + containsAll([ + ProcessCall('dart', const ['pub', 'get'], package.path), + ProcessCall('dart', const ['run', 'tool/run_tests.dart'], + package.path), + ])); + }); + test('runs when only legacy is present', () async { final Directory package = createFakePlugin('a_package', packagesDir, extraFiles: ['run_tests.sh']); @@ -128,7 +143,8 @@ void main() { ]); processRunner.mockProcessesForExecutable['dart'] = [ - MockProcess(exitCode: 1), + MockProcess(exitCode: 0), // pub get + MockProcess(exitCode: 1), // test script ]; Error? commandError; @@ -146,6 +162,32 @@ void main() { ])); }); + test('fails if pub get fails', () async { + createFakePlugin('a_package', packagesDir, extraFiles: [ + 'tool/run_tests.dart', + 'run_tests.sh', + ]); + + processRunner.mockProcessesForExecutable['dart'] = [ + MockProcess(exitCode: 1), + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['custom-test'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The following packages had errors:'), + contains('a_package:\n' + ' Unable to get script dependencies') + ])); + }); + test('fails if legacy fails', () async { final Directory package = createFakePlugin('a_package', packagesDir, extraFiles: [ @@ -260,7 +302,8 @@ void main() { ]); processRunner.mockProcessesForExecutable['dart'] = [ - MockProcess(exitCode: 1), + MockProcess(exitCode: 0), // pub get + MockProcess(exitCode: 1), // test script ]; Error? commandError; From f2dd3d98a3ef316656c328d086a720b5aa180187 Mon Sep 17 00:00:00 2001 From: Ibrahim Cetin Date: Thu, 21 Apr 2022 23:49:08 +0300 Subject: [PATCH 183/844] [video_player] Add setClosedCaptionFile method to VideoPlayerController (#5105) --- .../video_player/video_player/CHANGELOG.md | 3 +- .../video_player/lib/video_player.dart | 55 +++++++++++++------ .../video_player/video_player/pubspec.yaml | 2 +- .../video_player/test/video_player_test.dart | 36 ++++++++++++ 4 files changed, 77 insertions(+), 19 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index d9d1983fe0ad..363546c211e3 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,7 +1,8 @@ -## NEXT +## 2.4.0 * Updates minimum Flutter version to 2.10. * Adds OS version support information to README. +* Adds `setClosedCaptionFile` method to `VideoPlayerController`. ## 2.3.0 diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index b77c530b5831..39cd415dbb79 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -207,8 +207,11 @@ class VideoPlayerController extends ValueNotifier { /// null. The [package] argument must be non-null when the asset comes from a /// package and null otherwise. VideoPlayerController.asset(this.dataSource, - {this.package, this.closedCaptionFile, this.videoPlayerOptions}) - : dataSourceType = DataSourceType.asset, + {this.package, + Future? closedCaptionFile, + this.videoPlayerOptions}) + : _closedCaptionFileFuture = closedCaptionFile, + dataSourceType = DataSourceType.asset, formatHint = null, httpHeaders = const {}, super(VideoPlayerValue(duration: Duration.zero)); @@ -225,10 +228,11 @@ class VideoPlayerController extends ValueNotifier { VideoPlayerController.network( this.dataSource, { this.formatHint, - this.closedCaptionFile, + Future? closedCaptionFile, this.videoPlayerOptions, this.httpHeaders = const {}, - }) : dataSourceType = DataSourceType.network, + }) : _closedCaptionFileFuture = closedCaptionFile, + dataSourceType = DataSourceType.network, package = null, super(VideoPlayerValue(duration: Duration.zero)); @@ -237,8 +241,9 @@ class VideoPlayerController extends ValueNotifier { /// This will load the file from the file-URI given by: /// `'file://${file.path}'`. VideoPlayerController.file(File file, - {this.closedCaptionFile, this.videoPlayerOptions}) - : dataSource = 'file://${file.path}', + {Future? closedCaptionFile, this.videoPlayerOptions}) + : _closedCaptionFileFuture = closedCaptionFile, + dataSource = 'file://${file.path}', dataSourceType = DataSourceType.file, package = null, formatHint = null, @@ -250,9 +255,10 @@ class VideoPlayerController extends ValueNotifier { /// This will load the video from the input content-URI. /// This is supported on Android only. VideoPlayerController.contentUri(Uri contentUri, - {this.closedCaptionFile, this.videoPlayerOptions}) + {Future? closedCaptionFile, this.videoPlayerOptions}) : assert(defaultTargetPlatform == TargetPlatform.android, 'VideoPlayerController.contentUri is only supported on Android.'), + _closedCaptionFileFuture = closedCaptionFile, dataSource = contentUri.toString(), dataSourceType = DataSourceType.contentUri, package = null, @@ -283,13 +289,7 @@ class VideoPlayerController extends ValueNotifier { /// Only set for [asset] videos. The package that the asset was loaded from. final String? package; - /// Optional field to specify a file containing the closed - /// captioning. - /// - /// This future will be awaited and the file will be loaded when - /// [initialize()] is called. - final Future? closedCaptionFile; - + Future? _closedCaptionFileFuture; ClosedCaptionFile? _closedCaptionFile; Timer? _timer; bool _isDisposed = false; @@ -397,9 +397,8 @@ class VideoPlayerController extends ValueNotifier { } } - if (closedCaptionFile != null) { - _closedCaptionFile ??= await closedCaptionFile; - value = value.copyWith(caption: _getCaptionAt(value.position)); + if (_closedCaptionFileFuture != null) { + await _updateClosedCaptionWithFuture(_closedCaptionFileFuture); } void errorListener(Object obj) { @@ -634,6 +633,28 @@ class VideoPlayerController extends ValueNotifier { return Caption.none; } + /// Returns the file containing closed captions for the video, if any. + Future? get closedCaptionFile { + return _closedCaptionFileFuture; + } + + /// Sets a closed caption file. + /// + /// If [closedCaptionFile] is null, closed captions will be removed. + Future setClosedCaptionFile( + Future? closedCaptionFile, + ) async { + await _updateClosedCaptionWithFuture(closedCaptionFile); + _closedCaptionFileFuture = closedCaptionFile; + } + + Future _updateClosedCaptionWithFuture( + Future? closedCaptionFile, + ) async { + _closedCaptionFile = await closedCaptionFile; + value = value.copyWith(caption: _getCaptionAt(value.position)); + } + void _updatePosition(Duration position) { value = value.copyWith( position: position, diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 88e45c5be057..0d654a4330a7 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/plugins/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.3.0 +version: 2.4.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index 5e31e22c7010..64113337a860 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -72,6 +72,11 @@ class FakeController extends ValueNotifier @override void setCaptionOffset(Duration delay) {} + + @override + Future setClosedCaptionFile( + Future? closedCaptionFile, + ) async {} } Future _loadClosedCaption() async => @@ -672,6 +677,37 @@ void main() { await controller.seekTo(const Duration(milliseconds: 300)); expect(controller.value.caption.text, 'one'); }); + + test('setClosedCapitonFile loads caption file', () async { + final VideoPlayerController controller = VideoPlayerController.network( + 'https://127.0.0.1', + ); + + await controller.initialize(); + expect(controller.closedCaptionFile, null); + + await controller.setClosedCaptionFile(_loadClosedCaption()); + expect( + (await controller.closedCaptionFile)!.captions, + (await _loadClosedCaption()).captions, + ); + }); + + test('setClosedCapitonFile removes/changes caption file', () async { + final VideoPlayerController controller = VideoPlayerController.network( + 'https://127.0.0.1', + closedCaptionFile: _loadClosedCaption(), + ); + + await controller.initialize(); + expect( + (await controller.closedCaptionFile)!.captions, + (await _loadClosedCaption()).captions, + ); + + await controller.setClosedCaptionFile(null); + expect(controller.closedCaptionFile, null); + }); }); group('Platform callbacks', () { From 85000d3cacebd52eae13b56065eb1bf6075425d3 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 21 Apr 2022 17:29:09 -0400 Subject: [PATCH 184/844] [various] Replaces manual hashing with Object.hash (#5314) --- .../camera_platform_interface/CHANGELOG.md | 5 ++++ .../lib/src/events/camera_event.dart | 25 +++++++++---------- .../lib/src/types/camera_description.dart | 2 +- .../camera_platform_interface/pubspec.yaml | 5 ++-- .../test/events/camera_event_test.dart | 22 ++++++++-------- .../test/types/camera_description_test.dart | 8 +++--- .../local_auth_android/CHANGELOG.md | 6 ++++- .../lib/types/auth_messages_android.dart | 23 +++++++++-------- .../local_auth_android/pubspec.yaml | 4 +-- .../local_auth/local_auth_ios/CHANGELOG.md | 6 ++++- .../lib/types/auth_messages_ios.dart | 14 ++++++----- .../local_auth/local_auth_ios/pubspec.yaml | 4 +-- .../CHANGELOG.md | 4 +++ .../lib/types/auth_options.dart | 11 ++++---- .../pubspec.yaml | 4 +-- .../CHANGELOG.md | 5 ++++ .../lib/video_player_platform_interface.dart | 15 +++++------ .../pubspec.yaml | 5 ++-- script/tool/test/util.dart | 3 +-- 19 files changed, 97 insertions(+), 74 deletions(-) diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index 6ed662b2afa0..ef251eab9b93 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.6 + +* Adopts `Object.hash`. +* Removes obsolete dependency on `pedantic`. + ## 2.1.5 * Fixes asynchronous exceptions handling of the `initializeCamera` method. diff --git a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart b/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart index a91e538e4be5..755006cab8ab 100644 --- a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart +++ b/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart @@ -117,14 +117,15 @@ class CameraInitializedEvent extends CameraEvent { focusPointSupported == other.focusPointSupported; @override - int get hashCode => - super.hashCode ^ - previewWidth.hashCode ^ - previewHeight.hashCode ^ - exposureMode.hashCode ^ - exposurePointSupported.hashCode ^ - focusMode.hashCode ^ - focusPointSupported.hashCode; + int get hashCode => Object.hash( + super.hashCode, + previewWidth, + previewHeight, + exposureMode, + exposurePointSupported, + focusMode, + focusPointSupported, + ); } /// An event fired when the resolution preset of the camera has changed. @@ -171,8 +172,7 @@ class CameraResolutionChangedEvent extends CameraEvent { captureHeight == other.captureHeight; @override - int get hashCode => - super.hashCode ^ captureWidth.hashCode ^ captureHeight.hashCode; + int get hashCode => Object.hash(super.hashCode, captureWidth, captureHeight); } /// An event fired when the camera is going to close. @@ -239,7 +239,7 @@ class CameraErrorEvent extends CameraEvent { description == other.description; @override - int get hashCode => super.hashCode ^ description.hashCode; + int get hashCode => Object.hash(super.hashCode, description); } /// An event fired when a video has finished recording. @@ -284,6 +284,5 @@ class VideoRecordedEvent extends CameraEvent { maxVideoDuration == other.maxVideoDuration; @override - int get hashCode => - super.hashCode ^ file.hashCode ^ maxVideoDuration.hashCode; + int get hashCode => Object.hash(super.hashCode, file, maxVideoDuration); } diff --git a/packages/camera/camera_platform_interface/lib/src/types/camera_description.dart b/packages/camera/camera_platform_interface/lib/src/types/camera_description.dart index df7263631252..0167cf9e17a1 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/camera_description.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/camera_description.dart @@ -50,7 +50,7 @@ class CameraDescription { lensDirection == other.lensDirection; @override - int get hashCode => name.hashCode ^ lensDirection.hashCode; + int get hashCode => Object.hash(name, lensDirection); @override String toString() { diff --git a/packages/camera/camera_platform_interface/pubspec.yaml b/packages/camera/camera_platform_interface/pubspec.yaml index b28008d8d58e..ab163b4e9f3f 100644 --- a/packages/camera/camera_platform_interface/pubspec.yaml +++ b/packages/camera/camera_platform_interface/pubspec.yaml @@ -4,11 +4,11 @@ repository: https://github.com/flutter/plugins/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.1.5 +version: 2.1.6 environment: sdk: '>=2.12.0 <3.0.0' - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: cross_file: ^0.3.1 @@ -21,4 +21,3 @@ dev_dependencies: async: ^2.5.0 flutter_test: sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart index a46486ed252c..8a428e2fd43a 100644 --- a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart +++ b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart @@ -137,13 +137,14 @@ void main() { test('hashCode should match hashCode of all properties', () { const CameraInitializedEvent event = CameraInitializedEvent( 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); - final int expectedHashCode = event.cameraId.hashCode ^ - event.previewWidth.hashCode ^ - event.previewHeight.hashCode ^ - event.exposureMode.hashCode ^ - event.exposurePointSupported.hashCode ^ - event.focusMode.hashCode ^ - event.focusPointSupported.hashCode; + final int expectedHashCode = Object.hash( + event.cameraId, + event.previewWidth, + event.previewHeight, + event.exposureMode, + event.exposurePointSupported, + event.focusMode, + event.focusPointSupported); expect(event.hashCode, expectedHashCode); }); @@ -223,9 +224,8 @@ void main() { test('hashCode should match hashCode of all properties', () { const CameraResolutionChangedEvent event = CameraResolutionChangedEvent(1, 1024, 640); - final int expectedHashCode = event.cameraId.hashCode ^ - event.captureWidth.hashCode ^ - event.captureHeight.hashCode; + final int expectedHashCode = + Object.hash(event.cameraId, event.captureWidth, event.captureHeight); expect(event.hashCode, expectedHashCode); }); @@ -328,7 +328,7 @@ void main() { test('hashCode should match hashCode of all properties', () { const CameraErrorEvent event = CameraErrorEvent(1, 'Error'); final int expectedHashCode = - event.cameraId.hashCode ^ event.description.hashCode; + Object.hash(event.cameraId, event.description); expect(event.hashCode, expectedHashCode); }); diff --git a/packages/camera/camera_platform_interface/test/types/camera_description_test.dart b/packages/camera/camera_platform_interface/test/types/camera_description_test.dart index 3d3aaaeb4086..a86df031ac3a 100644 --- a/packages/camera/camera_platform_interface/test/types/camera_description_test.dart +++ b/packages/camera/camera_platform_interface/test/types/camera_description_test.dart @@ -97,15 +97,15 @@ void main() { expect(firstDescription == secondDescription, true); }); - test('hashCode should match hashCode of all properties', () { + test('hashCode should match hashCode of all equality-tested properties', + () { const CameraDescription description = CameraDescription( name: 'Test', lensDirection: CameraLensDirection.front, sensorOrientation: 0, ); - final int expectedHashCode = description.name.hashCode ^ - description.lensDirection.hashCode ^ - description.sensorOrientation.hashCode; + final int expectedHashCode = + Object.hash(description.name, description.lensDirection); expect(description.hashCode, expectedHashCode); }); diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 7f198f2d66c0..121daf945fd5 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.1 + +* Adopts `Object.hash`. + ## 1.0.0 -* Initial release from migration to federated architecture. +* Initial release from migration to federated architecture. diff --git a/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart b/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart index ea61a4b06d4e..ad901248e63c 100644 --- a/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart +++ b/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart @@ -110,17 +110,18 @@ class AndroidAuthMessages extends AuthMessages { signInTitle == other.signInTitle; @override - int get hashCode => - biometricHint.hashCode ^ - biometricNotRecognized.hashCode ^ - biometricRequiredTitle.hashCode ^ - biometricSuccess.hashCode ^ - cancelButton.hashCode ^ - deviceCredentialsRequiredTitle.hashCode ^ - deviceCredentialsSetupDescription.hashCode ^ - goToSettingsButton.hashCode ^ - goToSettingsDescription.hashCode ^ - signInTitle.hashCode; + int get hashCode => Object.hash( + super.hashCode, + biometricHint, + biometricNotRecognized, + biometricRequiredTitle, + biometricSuccess, + cancelButton, + deviceCredentialsRequiredTitle, + deviceCredentialsSetupDescription, + goToSettingsButton, + goToSettingsDescription, + signInTitle); } // Default strings for AndroidAuthMessages. Currently supports English. diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index ec2991db6a90..5734843d6584 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.0 +version: 1.0.1 environment: sdk: ">=2.14.0 <3.0.0" @@ -26,4 +26,4 @@ dependencies: dev_dependencies: flutter_test: - sdk: flutter \ No newline at end of file + sdk: flutter diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index ccacae986d51..466aeb50ed6c 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.3 + +* Adopts `Object.hash`. + ## 1.0.2 * Adds support `localizedFallbackTitle` in authenticateWithBiometrics on iOS. @@ -8,4 +12,4 @@ ## 1.0.0 -* Initial release from migration to federated architecture. +* Initial release from migration to federated architecture. diff --git a/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart b/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart index b3236a7ff66e..e5173fc4ab4f 100644 --- a/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart +++ b/packages/local_auth/local_auth_ios/lib/types/auth_messages_ios.dart @@ -65,12 +65,14 @@ class IOSAuthMessages extends AuthMessages { localizedFallbackTitle == other.localizedFallbackTitle; @override - int get hashCode => - lockOut.hashCode ^ - goToSettingsButton.hashCode ^ - goToSettingsDescription.hashCode ^ - cancelButton.hashCode ^ - localizedFallbackTitle.hashCode; + int get hashCode => Object.hash( + super.hashCode, + lockOut, + goToSettingsButton, + goToSettingsDescription, + cancelButton, + localizedFallbackTitle, + ); } // Default Strings for IOSAuthMessages plugin. Currently supports English. diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index 9e69dc7a6617..06d972a01522 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.2 +version: 1.0.3 environment: sdk: ">=2.14.0 <3.0.0" @@ -24,4 +24,4 @@ dependencies: dev_dependencies: flutter_test: - sdk: flutter \ No newline at end of file + sdk: flutter diff --git a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md index d16a91ee259a..0ff4d16ed163 100644 --- a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md +++ b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.2 + +* Adopts `Object.hash`. + ## 1.0.1 * Export externally used types from local_auth_platform_interface.dart directly. diff --git a/packages/local_auth/local_auth_platform_interface/lib/types/auth_options.dart b/packages/local_auth/local_auth_platform_interface/lib/types/auth_options.dart index c4b646c0b97a..a5af8e73a640 100644 --- a/packages/local_auth/local_auth_platform_interface/lib/types/auth_options.dart +++ b/packages/local_auth/local_auth_platform_interface/lib/types/auth_options.dart @@ -52,9 +52,10 @@ class AuthenticationOptions { biometricOnly == other.biometricOnly; @override - int get hashCode => - useErrorDialogs.hashCode ^ - stickyAuth.hashCode ^ - sensitiveTransaction.hashCode ^ - biometricOnly.hashCode; + int get hashCode => Object.hash( + useErrorDialogs, + stickyAuth, + sensitiveTransaction, + biometricOnly, + ); } diff --git a/packages/local_auth/local_auth_platform_interface/pubspec.yaml b/packages/local_auth/local_auth_platform_interface/pubspec.yaml index cd12a8a2dcab..b5d476d85cbd 100644 --- a/packages/local_auth/local_auth_platform_interface/pubspec.yaml +++ b/packages/local_auth/local_auth_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/l issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%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: 1.0.1 +version: 1.0.2 environment: sdk: ">=2.14.0 <3.0.0" @@ -19,4 +19,4 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - mockito: ^5.0.0 \ No newline at end of file + mockito: ^5.0.0 diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index 6595a5ce55db..9843fd81e82d 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,3 +1,8 @@ +## 5.1.2 + +* Adopts `Object.hash`. +* Removes obsolete dependency on `pedantic`. + ## 5.1.1 * Adds `rotationCorrection` (for Android playing videos recorded in landscapeRight [#60327](https://github.com/flutter/flutter/issues/60327)). diff --git a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart index 706ec61abf7c..78173f1fb63c 100644 --- a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart +++ b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart @@ -249,12 +249,13 @@ class VideoEvent { } @override - int get hashCode => - eventType.hashCode ^ - duration.hashCode ^ - size.hashCode ^ - rotationCorrection.hashCode ^ - buffered.hashCode; + int get hashCode => Object.hash( + eventType, + duration, + size, + rotationCorrection, + buffered, + ); } /// Type of the event. @@ -343,7 +344,7 @@ class DurationRange { end == other.end; @override - int get hashCode => start.hashCode ^ end.hashCode; + int get hashCode => Object.hash(start, end); } /// [VideoPlayerOptions] can be optionally used to set additional player settings diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml index ea3b17be1ea5..7a18568f22b5 100644 --- a/packages/video_player/video_player_platform_interface/pubspec.yaml +++ b/packages/video_player/video_player_platform_interface/pubspec.yaml @@ -4,11 +4,11 @@ repository: https://github.com/flutter/plugins/tree/main/packages/video_player/v issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%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: 5.1.1 +version: 5.1.2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: flutter: @@ -18,5 +18,4 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 pigeon: 0.1.21 diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index 6f7d86e054e9..fab4f39cc10f 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -434,8 +434,7 @@ class ProcessCall { } @override - int get hashCode => - (executable.hashCode) ^ (args.hashCode) ^ (workingDir?.hashCode ?? 0); + int get hashCode => Object.hash(executable, args, workingDir); @override String toString() { From a6c1c95be43e293f92c21da633a8953ed1e846fc Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 21 Apr 2022 20:04:08 -0700 Subject: [PATCH 185/844] [webview_flutter_wkwebview] Instance Manager (#5241) --- .../ios/Runner.xcodeproj/project.pbxproj | 6 +- .../ios/RunnerTests/FWFInstanceManagerTests.m | 40 +++++++++++ .../ios/Classes/FWFInstanceManager.h | 70 ++++++++++++++++++ .../ios/Classes/FWFInstanceManager.m | 72 +++++++++++++++++++ .../ios/Classes/webview-umbrella.h | 1 + 5 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj index b681c4704ddd..a444be330949 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -11,6 +11,7 @@ 334734012669319100DCC49E /* FLTWebViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68BDCAF523C3F97800D9C032 /* FLTWebViewTests.m */; }; 334734022669319400DCC49E /* FLTWKNavigationDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 8FA6A87928062CD000A4B183 /* FWFInstanceManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -67,6 +68,7 @@ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFInstanceManagerTests.m; 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; }; @@ -126,6 +128,7 @@ 68BDCAF523C3F97800D9C032 /* FLTWebViewTests.m */, 68BDCAED23C3F7CB00D9C032 /* Info.plist */, E43693B427512C0F00382F85 /* FLTCookieManagerTests.m */, + 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */, ); path = RunnerTests; sourceTree = ""; @@ -423,6 +426,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8FA6A87928062CD000A4B183 /* FWFInstanceManagerTests.m in Sources */, 334734012669319100DCC49E /* FLTWebViewTests.m in Sources */, 334734022669319400DCC49E /* FLTWKNavigationDelegateTests.m in Sources */, E43693B527512C0F00382F85 /* FLTCookieManagerTests.m in Sources */, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m new file mode 100644 index 000000000000..7b40da131d23 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m @@ -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 +@import webview_flutter_wkwebview; + +@interface FWFInstanceManagerTests : XCTestCase +@end + +@implementation FWFInstanceManagerTests +- (void)testAddInstance { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + NSObject *object = [[NSObject alloc] init]; + + [instanceManager addInstance:object withIdentifier:5]; + XCTAssertEqualObjects([instanceManager instanceForIdentifier:5], object); + XCTAssertEqual([instanceManager identifierForInstance:object], 5); +} + +- (void)testRemoveInstance { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + NSObject *object = [[NSObject alloc] init]; + [instanceManager addInstance:object withIdentifier:5]; + + [instanceManager removeInstance:object]; + XCTAssertNil([instanceManager instanceForIdentifier:5]); + XCTAssertEqual([instanceManager identifierForInstance:object], NSNotFound); +} + +- (void)testRemoveInstanceWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + NSObject *object = [[NSObject alloc] init]; + [instanceManager addInstance:object withIdentifier:5]; + + [instanceManager removeInstanceWithIdentifier:5]; + XCTAssertNil([instanceManager instanceForIdentifier:5]); + XCTAssertEqual([instanceManager identifierForInstance:object], NSNotFound); +} +@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 new file mode 100644 index 000000000000..79506bf72adc --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.h @@ -0,0 +1,70 @@ +// 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 + +NS_ASSUME_NONNULL_BEGIN + +/** + * Maintains instances to intercommunicate with Dart objects. + * + * When an instance is added with an identifier, either can be used to retrieve the other. + */ +@interface FWFInstanceManager : NSObject +// 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 +// instead. +/** + * Adds a new instance to the manager. + * + * 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)addInstance:(NSObject *)instance withIdentifier:(long)instanceIdentifier; + +/** + * Removes the instance paired with a given identifier 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; + +/** + * Removes the instance from the manager. + * + * @param instance The instance to be removed from the manager. + * + * @return The identifier of the removed instance if the manager contains the given instance, + * otherwise NSNotFound. + */ +- (long)removeInstance:(NSObject *)instance; + +/** + * Retrieves the instance paired with identifier. + * + * @param instanceIdentifier The identifier paired to an instance. + * + * @return The paired instance if the manager contains the given instanceIdentifier, + * otherwise nil. + */ +- (nullable NSObject *)instanceForIdentifier:(long)instanceIdentifier; + +/** + * Retrieves the identifier paired with an instance. + * + * @param instance An instance that may be stored in the manager. + * + * @return The paired identifer if the manager contains the given instance, otherwise NSNotFound. + */ +- (long)identifierForInstance:(NSObject *)instance; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m new file mode 100644 index 000000000000..0d88d88ba282 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m @@ -0,0 +1,72 @@ +// 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 "FWFInstanceManager.h" + +@interface FWFInstanceManager () +@property dispatch_queue_t lockQueue; +@property NSMapTable *identifiersToInstances; +@property NSMapTable *instancesToIdentifiers; +@end + +@implementation FWFInstanceManager +- (instancetype)init { + if (self) { + self.lockQueue = dispatch_queue_create("FWFInstanceManager", DISPATCH_QUEUE_SERIAL); + self.identifiersToInstances = [NSMapTable strongToStrongObjectsMapTable]; + self.instancesToIdentifiers = [NSMapTable strongToStrongObjectsMapTable]; + } + return self; +} + +- (void)addInstance:(nonnull NSObject *)instance withIdentifier:(long)instanceIdentifier { + NSAssert(instance && instanceIdentifier >= 0, + @"Instance must be nonnull and identifier must be >= 0."); + dispatch_async(_lockQueue, ^{ + [self.instancesToIdentifiers setObject:@(instanceIdentifier) forKey:instance]; + [self.identifiersToInstances setObject:instance forKey:@(instanceIdentifier)]; + }); +} + +- (nullable NSObject *)removeInstanceWithIdentifier:(long)instanceIdentifier { + NSObject *__block instance = nil; + dispatch_sync(_lockQueue, ^{ + instance = [self.identifiersToInstances objectForKey:@(instanceIdentifier)]; + if (instance) { + [self.identifiersToInstances removeObjectForKey:@(instanceIdentifier)]; + [self.instancesToIdentifiers removeObjectForKey:instance]; + } + }); + return instance; +} + +- (long)removeInstance:(NSObject *)instance { + NSAssert(instance, @"Instance must be nonnull."); + NSNumber *__block identifierNumber = nil; + dispatch_sync(_lockQueue, ^{ + identifierNumber = [self.instancesToIdentifiers objectForKey:instance]; + if (identifierNumber) { + [self.identifiersToInstances removeObjectForKey:identifierNumber]; + [self.instancesToIdentifiers removeObjectForKey:instance]; + } + }); + return identifierNumber ? identifierNumber.longValue : NSNotFound; +} + +- (nullable NSObject *)instanceForIdentifier:(long)instanceIdentifier { + NSObject *__block instance = nil; + dispatch_sync(_lockQueue, ^{ + instance = [self.identifiersToInstances objectForKey:@(instanceIdentifier)]; + }); + return instance; +} + +- (long)identifierForInstance:(nonnull NSObject *)instance { + NSNumber *__block identifierNumber = nil; + dispatch_sync(_lockQueue, ^{ + identifierNumber = [self.instancesToIdentifiers objectForKey:instance]; + }); + return identifierNumber ? identifierNumber.longValue : NSNotFound; +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h index c37282e886bc..3743f13f4ac7 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h @@ -7,5 +7,6 @@ #import #import #import +#import #import #import From 1925b91b4f1beb9b712dcb23ab6b5b32a10c9a32 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 21 Apr 2022 23:44:12 -0400 Subject: [PATCH 186/844] Roll Flutter from 0dccb58a73dd to 1b58a593deac (1 revision) (#5319) --- .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 627a0ce88e85..23335129b903 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -0dccb58a73dd0448b3cc4b24396b61ddcce4f861 +1b58a593deac48f1c3a6f579d53a0401372dc59f From 83269f39a7a42d65c3c44dc84c5ff0b7fb6a6bb4 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 22 Apr 2022 00:24:05 -0400 Subject: [PATCH 187/844] [local_auth] Fix default deviceSupportsBiometrics (#5321) --- .../CHANGELOG.md | 6 ++++ .../lib/default_method_channel_platform.dart | 11 +++++- .../pubspec.yaml | 2 +- .../default_method_channel_platform_test.dart | 34 ++++++++++++++++--- 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md index 0ff4d16ed163..9a2ae7aea8e8 100644 --- a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md +++ b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.0.3 + +* Fixes regression in the default method channel implementation of + `deviceSupportsBiometrics` from federation that would cause it to return true + only if something is enrolled. + ## 1.0.2 * Adopts `Object.hash`. diff --git a/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart b/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart index c68a3bfb8371..3e695fa41f17 100644 --- a/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart +++ b/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart @@ -57,6 +57,8 @@ class DefaultLocalAuthPlatform extends LocalAuthPlatform { biometrics.add(BiometricType.iris); break; case 'undefined': + // Sentinel value for the case when nothing is enrolled, but hardware + // support for biometrics is available. break; } } @@ -65,7 +67,14 @@ class DefaultLocalAuthPlatform extends LocalAuthPlatform { @override Future deviceSupportsBiometrics() async { - return (await getEnrolledBiometrics()).isNotEmpty; + final List availableBiometrics = + (await _channel.invokeListMethod( + 'getAvailableBiometrics', + )) ?? + []; + // If anything, including the 'undefined' sentinel, is returned, then there + // is device support for biometrics. + return availableBiometrics.isNotEmpty; } @override diff --git a/packages/local_auth/local_auth_platform_interface/pubspec.yaml b/packages/local_auth/local_auth_platform_interface/pubspec.yaml index b5d476d85cbd..ee6c1e9d6577 100644 --- a/packages/local_auth/local_auth_platform_interface/pubspec.yaml +++ b/packages/local_auth/local_auth_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/l issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%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: 1.0.2 +version: 1.0.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart b/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart index 3853fd84c6fc..96e78e17cfa8 100644 --- a/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart +++ b/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart @@ -10,7 +10,6 @@ import 'package:local_auth_platform_interface/default_method_channel_platform.da import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; import 'package:local_auth_platform_interface/types/auth_messages.dart'; import 'package:local_auth_platform_interface/types/auth_options.dart'; -import 'package:local_auth_platform_interface/types/biometric_type.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -19,9 +18,13 @@ void main() { 'plugins.flutter.io/local_auth', ); - final List log = []; + late List log; late LocalAuthPlatform localAuthentication; + setUp(() async { + log = []; + }); + test( 'DefaultLocalAuthPlatform is registered as the default platform implementation', () async { @@ -32,10 +35,9 @@ void main() { test('getAvailableBiometrics', () async { channel.setMockMethodCallHandler((MethodCall methodCall) { log.add(methodCall); - return Future.value([]); + return Future.value([]); }); localAuthentication = DefaultLocalAuthPlatform(); - log.clear(); await localAuthentication.getEnrolledBiometrics(); expect( log, @@ -45,6 +47,29 @@ void main() { ); }); + test('deviceSupportsBiometrics handles special sentinal value', () async { + // The pre-federation implementation of the platform channels, which the + // default implementation retains compatibility with for the benefit of any + // existing unendorsed implementations, used 'undefined' as a special + // return value from `getAvailableBiometrics` to indicate that nothing was + // enrolled, but that the hardware does support biometrics. + channel.setMockMethodCallHandler((MethodCall methodCall) { + log.add(methodCall); + return Future.value(['undefined']); + }); + + localAuthentication = DefaultLocalAuthPlatform(); + final bool supportsBiometrics = + await localAuthentication.deviceSupportsBiometrics(); + expect(supportsBiometrics, true); + expect( + log, + [ + isMethodCall('getAvailableBiometrics', arguments: null), + ], + ); + }); + group('Boolean returning methods', () { setUp(() { channel.setMockMethodCallHandler((MethodCall methodCall) { @@ -52,7 +77,6 @@ void main() { return Future.value(true); }); localAuthentication = DefaultLocalAuthPlatform(); - log.clear(); }); test('isDeviceSupported', () async { From bde713a230d3931f17105f2b4f8439cf448d031a Mon Sep 17 00:00:00 2001 From: BeMacized Date: Fri, 22 Apr 2022 10:04:09 +0200 Subject: [PATCH 188/844] [local_auth] Fix getEnrolledBiometrics returning non-enrolled biometrics on Android. (#5309) --- .../local_auth_android/CHANGELOG.md | 7 + .../plugins/localauth/LocalAuthPlugin.java | 44 ++--- .../plugins/localauth/LocalAuthTest.java | 166 ++++++++++++++++++ .../local_auth_android/example/lib/main.dart | 23 +-- .../lib/local_auth_android.dart | 18 +- .../local_auth_android/pubspec.yaml | 2 +- .../test/local_auth_test.dart | 16 +- 7 files changed, 224 insertions(+), 52 deletions(-) diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 121daf945fd5..cbf686a2abcb 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.0.2 + +* Fixes `getEnrolledBiometrics` to match documented behaviour: + Present biometrics that are not enrolled are no longer returned. +* `getEnrolledBiometrics` now only returns `weak` and `strong` biometric types. +* `deviceSupportsBiometrics` now returns the correct value regardless of enrollment state. + ## 1.0.1 * Adopts `Object.hash`. diff --git a/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java index 49a6b788fe46..3c5ecad16329 100644 --- a/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java +++ b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java @@ -11,7 +11,6 @@ import android.app.KeyguardManager; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; import android.hardware.fingerprint.FingerprintManager; import android.os.Build; import androidx.annotation.NonNull; @@ -101,8 +100,8 @@ public void onMethodCall(MethodCall call, @NonNull final Result result) { case "authenticate": authenticate(call, result); break; - case "getAvailableBiometrics": - getAvailableBiometrics(result); + case "getEnrolledBiometrics": + getEnrolledBiometrics(result); break; case "isDeviceSupported": isDeviceSupported(result); @@ -110,6 +109,9 @@ public void onMethodCall(MethodCall call, @NonNull final Result result) { case "stopAuthentication": stopAuthentication(result); break; + case "deviceSupportsBiometrics": + deviceSupportsBiometrics(result); + break; default: result.notImplemented(); break; @@ -248,42 +250,39 @@ private void stopAuthentication(Result result) { } } + private void deviceSupportsBiometrics(final Result result) { + result.success(hasBiometricHardware()); + } + /* - * Returns biometric types available on device + * Returns enrolled biometric types available on device. */ - private void getAvailableBiometrics(final Result result) { + private void getEnrolledBiometrics(final Result result) { try { if (activity == null || activity.isFinishing()) { result.error("no_activity", "local_auth plugin requires a foreground activity", null); return; } - ArrayList biometrics = getAvailableBiometrics(); + ArrayList biometrics = getEnrolledBiometrics(); result.success(biometrics); } catch (Exception e) { result.error("no_biometrics_available", e.getMessage(), null); } } - private ArrayList getAvailableBiometrics() { + private ArrayList getEnrolledBiometrics() { ArrayList biometrics = new ArrayList<>(); if (activity == null || activity.isFinishing()) { return biometrics; } - PackageManager packageManager = activity.getPackageManager(); - if (Build.VERSION.SDK_INT >= 23) { - if (packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { - biometrics.add("fingerprint"); - } + if (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) + == BiometricManager.BIOMETRIC_SUCCESS) { + biometrics.add("weak"); } - if (Build.VERSION.SDK_INT >= 29) { - if (packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) { - biometrics.add("face"); - } - if (packageManager.hasSystemFeature(PackageManager.FEATURE_IRIS)) { - biometrics.add("iris"); - } + if (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG) + == BiometricManager.BIOMETRIC_SUCCESS) { + biometrics.add("strong"); } - return biometrics; } @@ -359,4 +358,9 @@ public void onDetachedFromActivity() { final Activity getActivity() { return activity; } + + @VisibleForTesting + void setBiometricManager(BiometricManager biometricManager) { + this.biometricManager = biometricManager; + } } diff --git a/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java b/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java index 41868e603ad8..5fbda46b984f 100644 --- a/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java +++ b/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java @@ -6,12 +6,14 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Activity; import android.content.Context; +import androidx.biometric.BiometricManager; import androidx.lifecycle.Lifecycle; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.embedding.engine.dart.DartExecutor; @@ -20,6 +22,8 @@ import io.flutter.embedding.engine.plugins.lifecycle.HiddenLifecycleReference; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; +import java.util.ArrayList; +import java.util.Collections; import org.junit.Test; public class LocalAuthTest { @@ -31,6 +35,50 @@ public void isDeviceSupportedReturnsFalse() { verify(mockResult).success(false); } + @Test + public void deviceSupportsBiometrics_returnsTrueForPresentNonEnrolledBiometrics() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + final BiometricManager mockBiometricManager = mock(BiometricManager.class); + when(mockBiometricManager.canAuthenticate()) + .thenReturn(BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED); + plugin.setBiometricManager(mockBiometricManager); + plugin.onMethodCall(new MethodCall("deviceSupportsBiometrics", null), mockResult); + verify(mockResult).success(true); + } + + @Test + public void deviceSupportsBiometrics_returnsTrueForPresentEnrolledBiometrics() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + final BiometricManager mockBiometricManager = mock(BiometricManager.class); + when(mockBiometricManager.canAuthenticate()).thenReturn(BiometricManager.BIOMETRIC_SUCCESS); + plugin.setBiometricManager(mockBiometricManager); + plugin.onMethodCall(new MethodCall("deviceSupportsBiometrics", null), mockResult); + verify(mockResult).success(true); + } + + @Test + public void deviceSupportsBiometrics_returnsFalseForNoBiometricHardware() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + final BiometricManager mockBiometricManager = mock(BiometricManager.class); + when(mockBiometricManager.canAuthenticate()) + .thenReturn(BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE); + plugin.setBiometricManager(mockBiometricManager); + plugin.onMethodCall(new MethodCall("deviceSupportsBiometrics", null), mockResult); + verify(mockResult).success(false); + } + + @Test + public void deviceSupportsBiometrics_returnsFalseForNullBiometricManager() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + plugin.setBiometricManager(null); + plugin.onMethodCall(new MethodCall("deviceSupportsBiometrics", null), mockResult); + verify(mockResult).success(false); + } + @Test public void onDetachedFromActivity_ShouldReleaseActivity() { final Activity mockActivity = mock(Activity.class); @@ -61,4 +109,122 @@ public void onDetachedFromActivity_ShouldReleaseActivity() { plugin.onDetachedFromActivity(); assertNull(plugin.getActivity()); } + + @Test + public void getEnrolledBiometrics_shouldReturnError_whenNoActivity() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + + plugin.onMethodCall(new MethodCall("getEnrolledBiometrics", null), mockResult); + verify(mockResult) + .error("no_activity", "local_auth plugin requires a foreground activity", null); + } + + @Test + public void getEnrolledBiometrics_shouldReturnError_whenFinishingActivity() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + final Activity mockActivity = buildMockActivity(); + when(mockActivity.isFinishing()).thenReturn(true); + setPluginActivity(plugin, mockActivity); + + plugin.onMethodCall(new MethodCall("getEnrolledBiometrics", null), mockResult); + verify(mockResult) + .error("no_activity", "local_auth plugin requires a foreground activity", null); + } + + @Test + public void getEnrolledBiometrics_shouldReturnEmptyList_withoutHardwarePresent() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + setPluginActivity(plugin, buildMockActivity()); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + final BiometricManager mockBiometricManager = mock(BiometricManager.class); + when(mockBiometricManager.canAuthenticate(anyInt())) + .thenReturn(BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE); + plugin.setBiometricManager(mockBiometricManager); + + plugin.onMethodCall(new MethodCall("getEnrolledBiometrics", null), mockResult); + verify(mockResult).success(Collections.emptyList()); + } + + @Test + public void getEnrolledBiometrics_shouldReturnEmptyList_withNoMethodsEnrolled() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + setPluginActivity(plugin, buildMockActivity()); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + final BiometricManager mockBiometricManager = mock(BiometricManager.class); + when(mockBiometricManager.canAuthenticate(anyInt())) + .thenReturn(BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED); + plugin.setBiometricManager(mockBiometricManager); + + plugin.onMethodCall(new MethodCall("getEnrolledBiometrics", null), mockResult); + verify(mockResult).success(Collections.emptyList()); + } + + @Test + public void getEnrolledBiometrics_shouldOnlyAddEnrolledBiometrics() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + setPluginActivity(plugin, buildMockActivity()); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + final BiometricManager mockBiometricManager = mock(BiometricManager.class); + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) + .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG)) + .thenReturn(BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED); + plugin.setBiometricManager(mockBiometricManager); + + plugin.onMethodCall(new MethodCall("getEnrolledBiometrics", null), mockResult); + verify(mockResult) + .success( + new ArrayList() { + { + add("weak"); + } + }); + } + + @Test + public void getEnrolledBiometrics_shouldAddStrongBiometrics() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + setPluginActivity(plugin, buildMockActivity()); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + final BiometricManager mockBiometricManager = mock(BiometricManager.class); + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) + .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG)) + .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); + plugin.setBiometricManager(mockBiometricManager); + + plugin.onMethodCall(new MethodCall("getEnrolledBiometrics", null), mockResult); + verify(mockResult) + .success( + new ArrayList() { + { + add("weak"); + add("strong"); + } + }); + } + + private Activity buildMockActivity() { + final Activity mockActivity = mock(Activity.class); + final Context mockContext = mock(Context.class); + when(mockActivity.getBaseContext()).thenReturn(mockContext); + when(mockActivity.getApplicationContext()).thenReturn(mockContext); + return mockActivity; + } + + private void setPluginActivity(LocalAuthPlugin plugin, Activity activity) { + final HiddenLifecycleReference mockLifecycleReference = mock(HiddenLifecycleReference.class); + final FlutterPluginBinding mockPluginBinding = mock(FlutterPluginBinding.class); + final ActivityPluginBinding mockActivityBinding = mock(ActivityPluginBinding.class); + final FlutterEngine mockFlutterEngine = mock(FlutterEngine.class); + final DartExecutor mockDartExecutor = mock(DartExecutor.class); + when(mockPluginBinding.getFlutterEngine()).thenReturn(mockFlutterEngine); + when(mockFlutterEngine.getDartExecutor()).thenReturn(mockDartExecutor); + when(mockActivityBinding.getActivity()).thenReturn(activity); + when(mockActivityBinding.getLifecycle()).thenReturn(mockLifecycleReference); + plugin.onAttachedToEngine(mockPluginBinding); + plugin.onAttachedToActivity(mockActivityBinding); + } } diff --git a/packages/local_auth/local_auth_android/example/lib/main.dart b/packages/local_auth/local_auth_android/example/lib/main.dart index 4c045214734d..29b1d66440eb 100644 --- a/packages/local_auth/local_auth_android/example/lib/main.dart +++ b/packages/local_auth/local_auth_android/example/lib/main.dart @@ -22,8 +22,8 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { _SupportState _supportState = _SupportState.unknown; - bool? _canCheckBiometrics; - List? _availableBiometrics; + bool? _deviceSupportsBiometrics; + List? _enrolledBiometrics; String _authorized = 'Not Authorized'; bool _isAuthenticating = false; @@ -38,12 +38,12 @@ class _MyAppState extends State { } Future _checkBiometrics() async { - late bool canCheckBiometrics; + late bool deviceSupportsBiometrics; try { - canCheckBiometrics = - (await LocalAuthPlatform.instance.getEnrolledBiometrics()).isNotEmpty; + deviceSupportsBiometrics = + await LocalAuthPlatform.instance.deviceSupportsBiometrics(); } on PlatformException catch (e) { - canCheckBiometrics = false; + deviceSupportsBiometrics = false; print(e); } if (!mounted) { @@ -51,7 +51,7 @@ class _MyAppState extends State { } setState(() { - _canCheckBiometrics = canCheckBiometrics; + _deviceSupportsBiometrics = deviceSupportsBiometrics; }); } @@ -69,7 +69,7 @@ class _MyAppState extends State { } setState(() { - _availableBiometrics = availableBiometrics; + _enrolledBiometrics = availableBiometrics; }); } @@ -171,15 +171,16 @@ class _MyAppState extends State { else const Text('This device is not supported'), const Divider(height: 100), - Text('Can check biometrics: $_canCheckBiometrics\n'), + Text( + 'Device supports biometrics: $_deviceSupportsBiometrics\n'), ElevatedButton( child: const Text('Check biometrics'), onPressed: _checkBiometrics, ), const Divider(height: 100), - Text('Available biometrics: $_availableBiometrics\n'), + Text('Enrolled biometrics: $_enrolledBiometrics\n'), ElevatedButton( - child: const Text('Get available biometrics'), + child: const Text('Get enrolled biometrics'), onPressed: _getEnrolledBiometrics, ), const Divider(height: 100), diff --git a/packages/local_auth/local_auth_android/lib/local_auth_android.dart b/packages/local_auth/local_auth_android/lib/local_auth_android.dart index a3f314e3347b..2ce0f88477c7 100644 --- a/packages/local_auth/local_auth_android/lib/local_auth_android.dart +++ b/packages/local_auth/local_auth_android/lib/local_auth_android.dart @@ -49,28 +49,24 @@ class LocalAuthAndroid extends LocalAuthPlatform { @override Future deviceSupportsBiometrics() async { - return (await getEnrolledBiometrics()).isNotEmpty; + return (await _channel.invokeMethod('deviceSupportsBiometrics')) ?? + false; } @override Future> getEnrolledBiometrics() async { final List result = (await _channel.invokeListMethod( - 'getAvailableBiometrics', + 'getEnrolledBiometrics', )) ?? []; final List biometrics = []; for (final String value in result) { switch (value) { - case 'face': - biometrics.add(BiometricType.face); + case 'weak': + biometrics.add(BiometricType.weak); break; - case 'fingerprint': - biometrics.add(BiometricType.fingerprint); - break; - case 'iris': - biometrics.add(BiometricType.iris); - break; - case 'undefined': + case 'strong': + biometrics.add(BiometricType.strong); break; } } diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index 5734843d6584..aad0b9b92e70 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.1 +version: 1.0.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_android/test/local_auth_test.dart b/packages/local_auth/local_auth_android/test/local_auth_test.dart index 31f5e5796445..55ac92626aea 100644 --- a/packages/local_auth/local_auth_android/test/local_auth_test.dart +++ b/packages/local_auth/local_auth_android/test/local_auth_test.dart @@ -24,9 +24,8 @@ void main() { channel.setMockMethodCallHandler((MethodCall methodCall) { log.add(methodCall); switch (methodCall.method) { - case 'getAvailableBiometrics': - return Future>.value( - ['face', 'fingerprint', 'iris', 'undefined']); + case 'getEnrolledBiometrics': + return Future>.value(['weak', 'strong']); default: return Future.value(true); } @@ -35,13 +34,13 @@ void main() { log.clear(); }); - test('deviceSupportsBiometrics calls getEnrolledBiometrics', () async { + test('deviceSupportsBiometrics calls platform', () async { final bool result = await localAuthentication.deviceSupportsBiometrics(); expect( log, [ - isMethodCall('getAvailableBiometrics', arguments: null), + isMethodCall('deviceSupportsBiometrics', arguments: null), ], ); expect(result, true); @@ -54,13 +53,12 @@ void main() { expect( log, [ - isMethodCall('getAvailableBiometrics', arguments: null), + isMethodCall('getEnrolledBiometrics', arguments: null), ], ); expect(result, [ - BiometricType.face, - BiometricType.fingerprint, - BiometricType.iris + BiometricType.weak, + BiometricType.strong, ]); }); From ca63d964d9fdfff9be36533ea4248d0d0ca28fd0 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 22 Apr 2022 08:04:07 -0400 Subject: [PATCH 189/844] [url_launcher] Replace primary APIs with cleaner versions (#5310) --- .../url_launcher/url_launcher/CHANGELOG.md | 17 +- packages/url_launcher/url_launcher/README.md | 106 ++++--- .../integration_test/url_launcher_test.dart | 17 +- .../url_launcher/example/lib/main.dart | 74 +++-- .../url_launcher/lib/src/legacy_api.dart | 154 ++++++++++ .../url_launcher/lib/src/link.dart | 25 +- .../url_launcher/lib/src/types.dart | 54 ++++ .../lib/src/url_launcher_string.dart | 65 ++++ .../lib/src/url_launcher_uri.dart | 90 ++++++ .../url_launcher/lib/url_launcher.dart | 150 +--------- .../url_launcher/lib/url_launcher_string.dart | 13 + .../url_launcher/url_launcher/pubspec.yaml | 2 +- .../url_launcher/test/link_test.dart | 10 +- .../mock_url_launcher_platform.dart | 0 .../legacy_api_test.dart} | 4 +- .../test/src/url_launcher_string_test.dart | 279 ++++++++++++++++++ .../test/src/url_launcher_uri_test.dart | 262 ++++++++++++++++ 17 files changed, 1058 insertions(+), 264 deletions(-) create mode 100644 packages/url_launcher/url_launcher/lib/src/legacy_api.dart create mode 100644 packages/url_launcher/url_launcher/lib/src/types.dart create mode 100644 packages/url_launcher/url_launcher/lib/src/url_launcher_string.dart create mode 100644 packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart create mode 100644 packages/url_launcher/url_launcher/lib/url_launcher_string.dart rename packages/url_launcher/url_launcher/test/{ => mocks}/mock_url_launcher_platform.dart (100%) rename packages/url_launcher/url_launcher/test/{url_launcher_test.dart => src/legacy_api_test.dart} (99%) create mode 100644 packages/url_launcher/url_launcher/test/src/url_launcher_string_test.dart create mode 100644 packages/url_launcher/url_launcher/test/src/url_launcher_uri_test.dart diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 0c38f4847d9b..b1ebc4b35a8e 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,6 +1,19 @@ -## NEXT - +## 6.1.0 + +* Introduces new `launchUrl` and `canLaunchUrl` APIs; `launch` and `canLaunch` + are now deprecated. These new APIs: + * replace the `String` URL argument with a `Uri`, to prevent common issues + with providing invalid URL strings. + * replace `forceSafariVC` and `forceWebView` with `LaunchMode`, which makes + the API platform-neutral, and standardizes the default behavior between + Android and iOS. + * move web view configuration options into a new `WebViewConfiguration` + object. The default behavior for JavaScript and DOM storage is now enabled + rather than disabled. +* Also deprecates `closeWebView` in favor of `closeInAppWebView` to clarify + that it is specific to the in-app web view launch option. * Adds OS version support information to README. +* Reorganizes and clarifies README. ## 6.0.20 diff --git a/packages/url_launcher/url_launcher/README.md b/packages/url_launcher/url_launcher/README.md index 7f8699eceba7..0cdbe1b9859e 100644 --- a/packages/url_launcher/url_launcher/README.md +++ b/packages/url_launcher/url_launcher/README.md @@ -18,14 +18,14 @@ To use this plugin, add `url_launcher` as a [dependency in your pubspec.yaml fil import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; -const String _url = 'https://flutter.dev'; +final Uri _url = Uri.parse('https://flutter.dev'); void main() => runApp( const MaterialApp( home: Material( child: Center( child: RaisedButton( - onPressed: _launchURL, + onPressed: _launchUrl, child: Text('Show Flutter homepage'), ), ), @@ -33,8 +33,8 @@ void main() => runApp( ), ); -void _launchURL() async { - if (!await launch(_url)) throw 'Could not launch $_url'; +void _launchUrl() async { + if (!await launchUrl(_url)) throw 'Could not launch $_url'; } ``` @@ -43,7 +43,7 @@ See the example app for more complex examples. ## Configuration ### iOS -Add any URL schemes passed to `canLaunch` as `LSApplicationQueriesSchemes` entries in your Info.plist file. +Add any URL schemes passed to `canLaunchUrl` as `LSApplicationQueriesSchemes` entries in your Info.plist file. Example: ``` @@ -59,7 +59,7 @@ See [`-[UIApplication canOpenURL:]`](https://developer.apple.com/documentation/u ### Android Starting from API 30 Android requires package visibility configuration in your -`AndroidManifest.xml` otherwise `canLaunch` will return `false`. A `` +`AndroidManifest.xml` otherwise `canLaunchUrl` will return `false`. A `` element must be added to your manifest as a child of the root element. The snippet below shows an example for an application that uses `https`, `tel`, @@ -94,34 +94,53 @@ for examples of other queries. ## Supported URL schemes -The [`launch`](https://pub.dev/documentation/url_launcher/latest/url_launcher/launch.html) method -takes a string argument containing a URL. This URL -can be formatted using a number of different URL schemes. The supported -URL schemes depend on the underlying platform and installed apps. +The provided URL is passed directly to the host platform for handling. The +supported URL schemes therefore depend on the platform and installed apps. Commonly used schemes include: | Scheme | Example | Action | |:---|:---|:---| -| `https:` | `https://flutter.dev` | Open URL in the default browser | -| `mailto:?subject=&body=` | `mailto:smith@example.org?subject=News&body=New%20plugin` | Create email to in the default email app | -| `tel:` | `tel:+1-555-010-999` | Make a phone call to using the default phone app | -| `sms:` | `sms:5550101234` | Send an SMS message to using the default messaging app | +| `https:` | `https://flutter.dev` | Open `` in the default browser | +| `mailto:?subject=&body=` | `mailto:smith@example.org?subject=News&body=New%20plugin` | Create email to `` in the default email app | +| `tel:` | `tel:+1-555-010-999` | Make a phone call to `` using the default phone app | +| `sms:` | `sms:5550101234` | Send an SMS message to `` using the default messaging app | | `file:` | `file:/home` | Open file or folder using default app association, supported on desktop platforms | More details can be found here for [iOS](https://developer.apple.com/library/content/featuredarticles/iPhoneURLScheme_Reference/Introduction/Introduction.html) and [Android](https://developer.android.com/guide/components/intents-common.html) -**Note**: URL schemes are only supported if there are apps installed on the device that can +URL schemes are only supported if there are apps installed on the device that can support them. For example, iOS simulators don't have a default email or phone apps installed, so can't open `tel:` or `mailto:` links. +### Checking supported schemes + +If you need to know at runtime whether a scheme is guaranteed to work before +using it (for instance, to adjust your UI based on what is available), you can +check with [`canLaunchUrl`](https://pub.dev/documentation/url_launcher/latest/url_launcher/canLaunchUrl.html). + +However, `canLaunchUrl` can return false even if `launchUrl` would work in +some circumstances (in web applications, on mobile without the necessary +configuration as described above, etc.), so in cases where you can provide +fallback behavior it is better to use `launchUrl` directly and handle failure. +For example, a UI button that would have sent feedback email using a `mailto` URL +might instead open a web-based feedback form using an `https` URL on failure, +rather than disabling the button if `canLaunchUrl` returns false for `mailto`. + ### Encoding URLs URLs must be properly encoded, especially when including spaces or other special -characters. This can be done using the +characters. In general this is handled automatically by the [`Uri` class](https://api.dart.dev/dart-core/Uri-class.html). -For example: + +**However**, for any scheme other than `http` or `https`, you should use the +`query` parameter and the `encodeQueryParameters` function shown below rather +than `Uri`'s `queryParameters` constructor argument for any query parameters, +due to [a bug](https://github.com/dart-lang/sdk/issues/43838) in the way `Uri` +encodes query parameters. Using `queryParameters` will result in spaces being +converted to `+` in many cases. + ```dart String? encodeQueryParameters(Map params) { return params.entries @@ -137,43 +156,24 @@ final Uri emailLaunchUri = Uri( }), ); -launch(emailLaunchUri.toString()); +launchUrl(emailLaunchUri); ``` -**Warning**: For any scheme other than `http` or `https`, you should use the -`query` parameter and the `encodeQueryParameters` function shown above rather -than `Uri`'s `queryParameters` constructor argument, due to -[a bug](https://github.com/dart-lang/sdk/issues/43838) in the way `Uri` -encodes query parameters. Using `queryParameters` will result in spaces being -converted to `+` in many cases. - -### Handling missing URL receivers +### URLs not handled by `Uri` -A particular mobile device may not be able to receive all supported URL schemes. -For example, a tablet may not have a cellular radio and thus no support for -launching a URL using the `sms` scheme, or a device may not have an email app -and thus no support for launching a URL using the `mailto` scheme. +In rare cases, you may need to launch a URL that the host system considers +valid, but cannot be expressed by `Uri`. For those cases, alternate APIs using +strings are available by importing `url_launcher_string.dart`. -We recommend checking which URL schemes are supported using the -[`canLaunch`](https://pub.dev/documentation/url_launcher/latest/url_launcher/canLaunch.html) -in most cases. If the `canLaunch` method returns false, as a -best practice we suggest adjusting the application UI so that the unsupported -URL is never triggered; for example, if the `mailto` scheme is not supported, a -UI button that would have sent feedback email could be changed to instead open -a web-based feedback form using an `https` URL. +Using these APIs in any other cases is **strongly discouraged**, as providing +invalid URL strings was a very common source of errors with this plugin's +original APIs. -## Browser vs In-app Handling -By default, Android opens up a browser when handling URLs. You can pass -`forceWebView: true` parameter to tell the plugin to open a WebView instead. -If you do this for a URL of a page containing JavaScript, make sure to pass in -`enableJavaScript: true`, or else the launch method will not work properly. On -iOS, the default behavior is to open all web URLs within the app. Everything -else is redirected to the app handler. +### File scheme handling -## File scheme handling -`file:` scheme can be used on desktop platforms: `macOS`, `Linux` and `Windows`. +`file:` scheme can be used on desktop platforms: Windows, macOS, and Linux. -We recommend checking first whether the directory or file exists before calling `launch`. +We recommend checking first whether the directory or file exists before calling `launchUrl`. Example: ```dart @@ -181,13 +181,21 @@ var filePath = '/path/to/file'; final Uri uri = Uri.file(filePath); if (await File(uri.toFilePath()).exists()) { - if (!await launch(uri.toString())) { + if (!await launchUrl(uri)) { throw 'Could not launch $uri'; } } ``` -### macOS file access configuration +#### macOS file access configuration If you need to access files outside of your application's sandbox, you will need to have the necessary [entitlements](https://docs.flutter.dev/desktop#entitlements-and-the-app-sandbox). + +## Browser vs in-app Handling + +On some platforms, web URLs can be launched either in an in-app web view, or +in the default browser. The default behavior depends on the platform (see +[`launchUrl`](https://pub.dev/documentation/url_launcher/latest/url_launcher/launchUrl.html) +for details), but a specific mode can be used on supported platforms by +passing a `LaunchMode`. diff --git a/packages/url_launcher/url_launcher/example/integration_test/url_launcher_test.dart b/packages/url_launcher/url_launcher/example/integration_test/url_launcher_test.dart index b527c22390dc..51c2ec892400 100644 --- a/packages/url_launcher/url_launcher/example/integration_test/url_launcher_test.dart +++ b/packages/url_launcher/url_launcher/example/integration_test/url_launcher_test.dart @@ -13,18 +13,23 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('canLaunch', (WidgetTester _) async { - expect(await canLaunch('randomstring'), false); + expect( + await canLaunchUrl(Uri(scheme: 'randomscheme', path: 'a_path')), false); // Generally all devices should have some default browser. - expect(await canLaunch('http://flutter.dev'), true); - expect(await canLaunch('https://www.google.com/404'), true); + expect(await canLaunchUrl(Uri(scheme: 'http', host: 'flutter.dev')), true); + expect(await canLaunchUrl(Uri(scheme: 'https', host: 'flutter.dev')), true); // SMS handling is available by default on most platforms. if (kIsWeb || !(Platform.isLinux || Platform.isWindows)) { - expect(await canLaunch('sms:5555555555'), true); + expect(await canLaunchUrl(Uri(scheme: 'sms', path: '5555555555')), true); } - // tel: and mailto: links may not be openable on every device. iOS - // simulators notably can't open these link types. + // Sanity-check legacy API. + // ignore: deprecated_member_use + expect(await canLaunch('randomstring'), false); + // Generally all devices should have some default browser. + // ignore: deprecated_member_use + expect(await canLaunch('https://flutter.dev'), true); }); } diff --git a/packages/url_launcher/url_launcher/example/lib/main.dart b/packages/url_launcher/url_launcher/example/lib/main.dart index a5e38ceecc84..898e80661296 100644 --- a/packages/url_launcher/url_launcher/example/lib/main.dart +++ b/packages/url_launcher/url_launcher/example/lib/main.dart @@ -44,67 +44,62 @@ class _MyHomePageState extends State { void initState() { super.initState(); // Check for phone call support. - canLaunch('tel:123').then((bool result) { + canLaunchUrl(Uri(scheme: 'tel', path: '123')).then((bool result) { setState(() { _hasCallSupport = result; }); }); } - Future _launchInBrowser(String url) async { - if (!await launch( + Future _launchInBrowser(Uri url) async { + if (!await launchUrl( url, - forceSafariVC: false, - forceWebView: false, - headers: {'my_header_key': 'my_header_value'}, + mode: LaunchMode.externalApplication, )) { throw 'Could not launch $url'; } } - Future _launchInWebViewOrVC(String url) async { - if (!await launch( + Future _launchInWebViewOrVC(Uri url) async { + if (!await launchUrl( url, - forceSafariVC: true, - forceWebView: true, - headers: {'my_header_key': 'my_header_value'}, + mode: LaunchMode.inAppWebView, + webViewConfiguration: const WebViewConfiguration( + headers: {'my_header_key': 'my_header_value'}), )) { throw 'Could not launch $url'; } } - Future _launchInWebViewWithJavaScript(String url) async { - if (!await launch( + Future _launchInWebViewWithoutJavaScript(Uri url) async { + if (!await launchUrl( url, - forceSafariVC: true, - forceWebView: true, - enableJavaScript: true, + mode: LaunchMode.inAppWebView, + webViewConfiguration: const WebViewConfiguration(enableJavaScript: false), )) { throw 'Could not launch $url'; } } - Future _launchInWebViewWithDomStorage(String url) async { - if (!await launch( + Future _launchInWebViewWithoutDomStorage(Uri url) async { + if (!await launchUrl( url, - forceSafariVC: true, - forceWebView: true, - enableDomStorage: true, + mode: LaunchMode.inAppWebView, + webViewConfiguration: const WebViewConfiguration(enableDomStorage: false), )) { throw 'Could not launch $url'; } } - Future _launchUniversalLinkIos(String url) async { - final bool nativeAppLaunchSucceeded = await launch( + Future _launchUniversalLinkIos(Uri url) async { + final bool nativeAppLaunchSucceeded = await launchUrl( url, - forceSafariVC: false, - universalLinksOnly: true, + mode: LaunchMode.externalNonBrowserApplication, ); if (!nativeAppLaunchSucceeded) { - await launch( + await launchUrl( url, - forceSafariVC: true, + mode: LaunchMode.inAppWebView, ); } } @@ -118,22 +113,19 @@ class _MyHomePageState extends State { } Future _makePhoneCall(String phoneNumber) async { - // Use `Uri` to ensure that `phoneNumber` is properly URL-encoded. - // Just using 'tel:$phoneNumber' would create invalid URLs in some cases, - // such as spaces in the input, which would cause `launch` to fail on some - // platforms. final Uri launchUri = Uri( scheme: 'tel', path: phoneNumber, ); - await launch(launchUri.toString()); + await launchUrl(launchUri); } @override Widget build(BuildContext context) { // onPressed calls using this URL are not gated on a 'canLaunch' check // because the assumption is that every device can launch a web URL. - const String toLaunch = 'https://www.cylog.org/headers/'; + final Uri toLaunch = + Uri(scheme: 'https', host: 'www.cylog.org', path: 'headers/'); return Scaffold( appBar: AppBar( title: Text(widget.title), @@ -160,9 +152,9 @@ class _MyHomePageState extends State { ? const Text('Make phone call') : const Text('Calling not supported'), ), - const Padding( - padding: EdgeInsets.all(16.0), - child: Text(toLaunch), + Padding( + padding: const EdgeInsets.all(16.0), + child: Text(toLaunch.toString()), ), ElevatedButton( onPressed: () => setState(() { @@ -179,15 +171,15 @@ class _MyHomePageState extends State { ), ElevatedButton( onPressed: () => setState(() { - _launched = _launchInWebViewWithJavaScript(toLaunch); + _launched = _launchInWebViewWithoutJavaScript(toLaunch); }), - child: const Text('Launch in app(JavaScript ON)'), + child: const Text('Launch in app (JavaScript OFF)'), ), ElevatedButton( onPressed: () => setState(() { - _launched = _launchInWebViewWithDomStorage(toLaunch); + _launched = _launchInWebViewWithoutDomStorage(toLaunch); }), - child: const Text('Launch in app(DOM storage ON)'), + child: const Text('Launch in app (DOM storage OFF)'), ), const Padding(padding: EdgeInsets.all(16.0)), ElevatedButton( @@ -203,7 +195,7 @@ class _MyHomePageState extends State { _launched = _launchInWebViewOrVC(toLaunch); Timer(const Duration(seconds: 5), () { print('Closing WebView after 5 seconds...'); - closeWebView(); + closeInAppWebView(); }); }), child: const Text('Launch in app + close after 5 seconds'), diff --git a/packages/url_launcher/url_launcher/lib/src/legacy_api.dart b/packages/url_launcher/url_launcher/lib/src/legacy_api.dart new file mode 100644 index 000000000000..a61b200003a0 --- /dev/null +++ b/packages/url_launcher/url_launcher/lib/src/legacy_api.dart @@ -0,0 +1,154 @@ +// 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:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +/// Parses the specified URL string and delegates handling of it to the +/// underlying platform. +/// +/// The returned future completes with a [PlatformException] on invalid URLs and +/// schemes which cannot be handled, that is when [canLaunch] would complete +/// with false. +/// +/// By default when [forceSafariVC] is unset, the launcher +/// opens web URLs in the Safari View Controller, anything else is opened +/// using the default handler on the platform. If set to true, it opens the +/// URL in the Safari View Controller. If false, the URL is opened in the +/// default browser of the phone. Note that to work with universal links on iOS, +/// this must be set to false to let the platform's system handle the URL. +/// Set this to false if you want to use the cookies/context of the main browser +/// of the app (such as SSO flows). This setting will nullify [universalLinksOnly] +/// and will always launch a web content in the built-in Safari View Controller regardless +/// if the url is a universal link or not. +/// +/// [universalLinksOnly] is only used in iOS with iOS version >= 10.0. This setting is only validated +/// when [forceSafariVC] is set to false. The default value of this setting is false. +/// By default (when unset), the launcher will either launch the url in a browser (when the +/// url is not a universal link), or launch the respective native app content (when +/// the url is a universal link). When set to true, the launcher will only launch +/// the content if the url is a universal link and the respective app for the universal +/// link is installed on the user's device; otherwise throw a [PlatformException]. +/// +/// [forceWebView] is an Android only setting. If null or false, the URL is +/// always launched with the default browser on device. If set to true, the URL +/// is launched in a WebView. Unlike iOS, browser context is shared across +/// WebViews. +/// [enableJavaScript] is an Android only setting. If true, WebView enable +/// javascript. +/// [enableDomStorage] is an Android only setting. If true, WebView enable +/// DOM storage. +/// [headers] is an Android only setting that adds headers to the WebView. +/// When not using a WebView, the header information is passed to the browser, +/// some Android browsers do not support the [Browser.EXTRA_HEADERS](https://developer.android.com/reference/android/provider/Browser#EXTRA_HEADERS) +/// intent extra and the header information will be lost. +/// [webOnlyWindowName] is an Web only setting . _blank opens the new url in new tab , +/// _self opens the new url in current tab. +/// Default behaviour is to open the url in new tab. +/// +/// Note that if any of the above are set to true but the URL is not a web URL, +/// this will throw a [PlatformException]. +/// +/// [statusBarBrightness] Sets the status bar brightness of the application +/// after opening a link on iOS. Does nothing if no value is passed. This does +/// not handle resetting the previous status bar style. +/// +/// Returns true if launch url is successful; false is only returned when [universalLinksOnly] +/// is set to true and the universal link failed to launch. +@Deprecated('Use launchUrl instead') +Future launch( + String urlString, { + bool? forceSafariVC, + bool forceWebView = false, + bool enableJavaScript = false, + bool enableDomStorage = false, + bool universalLinksOnly = false, + Map headers = const {}, + Brightness? statusBarBrightness, + String? webOnlyWindowName, +}) async { + final Uri? url = Uri.tryParse(urlString.trimLeft()); + final bool isWebURL = + url != null && (url.scheme == 'http' || url.scheme == 'https'); + + if ((forceSafariVC == true || forceWebView == true) && !isWebURL) { + throw PlatformException( + code: 'NOT_A_WEB_SCHEME', + message: 'To use webview or safariVC, you need to pass' + 'in a web URL. This $urlString is not a web URL.'); + } + + /// [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; + SystemChrome.setSystemUIOverlayStyle(statusBarBrightness == Brightness.light + ? SystemUiOverlayStyle.dark + : SystemUiOverlayStyle.light); + } + + final bool result = await UrlLauncherPlatform.instance.launch( + urlString, + useSafariVC: forceSafariVC ?? isWebURL, + useWebView: forceWebView, + enableJavaScript: enableJavaScript, + enableDomStorage: enableDomStorage, + universalLinksOnly: universalLinksOnly, + headers: headers, + webOnlyWindowName: webOnlyWindowName, + ); + + if (statusBarBrightness != null && + _ambiguate(WidgetsBinding.instance) != null) { + _ambiguate(WidgetsBinding.instance)! + .renderView + .automaticSystemUiAdjustment = previousAutomaticSystemUiAdjustment; + } + + return result; +} + +/// Checks whether the specified URL can be handled by some app installed on the +/// device. +/// +/// On some systems, such as recent versions of Android and iOS, this will +/// always return false unless the application has been configuration to allow +/// querying the system for launch support. See +/// [the README](https://pub.dev/packages/url_launcher#configuration) for +/// details. +@Deprecated('Use canLaunchUrl instead') +Future canLaunch(String urlString) async { + return await UrlLauncherPlatform.instance.canLaunch(urlString); +} + +/// Closes the current WebView, if one was previously opened via a call to [launch]. +/// +/// If [launch] was never called, then this call will not have any effect. +/// +/// On Android systems, if [launch] was called without `forceWebView` being set to `true` +/// Or on IOS systems, if [launch] was called without `forceSafariVC` being set to `true`, +/// this call will not do anything either, simply because there is no +/// WebView/SafariViewController available to be closed. +@Deprecated('Use closeInAppWebView instead') +Future closeWebView() async { + return await UrlLauncherPlatform.instance.closeWebView(); +} + +/// This allows a value of type T or T? to be treated as a value of type T?. +/// +/// We use this so that APIs that have become non-nullable can still be used +/// with `!` and `?` on the stable branch. +// TODO(ianh): Remove this once we roll stable in late 2021. +T? _ambiguate(T? value) => value; diff --git a/packages/url_launcher/url_launcher/lib/src/link.dart b/packages/url_launcher/url_launcher/lib/src/link.dart index 016f97daacbf..76cb97748003 100644 --- a/packages/url_launcher/url_launcher/lib/src/link.dart +++ b/packages/url_launcher/url_launcher/lib/src/link.dart @@ -6,10 +6,12 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; -import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher_platform_interface/link.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; +import 'types.dart'; +import 'url_launcher_uri.dart'; + /// The function used to push routes to the Flutter framework. @visibleForTesting Future Function(Object?, String) pushRouteToFrameworkFunction = @@ -107,7 +109,8 @@ class DefaultLinkDelegate extends StatelessWidget { } Future _followLink(BuildContext context) async { - if (!link.uri!.hasScheme) { + final Uri url = link.uri!; + if (!url.hasScheme) { // A uri that doesn't have a scheme is an internal route name. In this // case, we push it via Flutter's navigation system instead of letting the // browser handle it. @@ -116,18 +119,18 @@ class DefaultLinkDelegate extends StatelessWidget { return; } - // At this point, we know that the link is external. So we use the `launch` - // API to open the link. - final String urlString = link.uri.toString(); - if (await canLaunch(urlString)) { - await launch( - urlString, - forceSafariVC: _useWebView, - forceWebView: _useWebView, + // At this point, we know that the link is external. So we use the + // `launchUrl` API to open the link. + if (await canLaunchUrl(url)) { + await launchUrl( + url, + mode: _useWebView + ? LaunchMode.inAppWebView + : LaunchMode.externalApplication, ); } else { FlutterError.reportError(FlutterErrorDetails( - exception: 'Could not launch link $urlString', + exception: 'Could not launch link ${url.toString()}', stack: StackTrace.current, library: 'url_launcher', context: ErrorDescription('during launching a link'), diff --git a/packages/url_launcher/url_launcher/lib/src/types.dart b/packages/url_launcher/url_launcher/lib/src/types.dart new file mode 100644 index 000000000000..bcfcb7887b17 --- /dev/null +++ b/packages/url_launcher/url_launcher/lib/src/types.dart @@ -0,0 +1,54 @@ +// 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 desired mode to launch a URL. +/// +/// Support for these modes varies by platform. Platforms that do not support +/// the requested mode may substitute another mode. See [launchUrl] for more +/// details. +enum LaunchMode { + /// Leaves the decision of how to launch the URL to the platform + /// implementation. + platformDefault, + + /// Loads the URL in an in-app web view (e.g., Safari View Controller). + inAppWebView, + + /// Passes the URL to the OS to be handled by another application. + externalApplication, + + /// Passes the URL to the OS to be handled by another non-browser application. + externalNonBrowserApplication, +} + +/// Additional configuration options for [LaunchMode.inAppWebView]. +@immutable +class WebViewConfiguration { + /// Creates a new WebViewConfiguration with the given settings. + const WebViewConfiguration({ + this.enableJavaScript = true, + this.enableDomStorage = true, + this.headers = const {}, + }); + + /// Whether or not JavaScript is enabled for the web content. + /// + /// Disabling this may not be supported on all platforms. + final bool enableJavaScript; + + /// Whether or not DOM storage is enabled for the web content. + /// + /// Disabling this may not be supported on all platforms. + final bool enableDomStorage; + + /// Additional headers to pass in the load request. + /// + /// On Android, this may work even when not loading in an in-app web view. + /// When loading in an external browsers, this sets + /// [Browser.EXTRA_HEADERS](https://developer.android.com/reference/android/provider/Browser#EXTRA_HEADERS) + /// Not all browsers support this, so it is not guaranteed to be honored. + final Map headers; +} diff --git a/packages/url_launcher/url_launcher/lib/src/url_launcher_string.dart b/packages/url_launcher/url_launcher/lib/src/url_launcher_string.dart new file mode 100644 index 000000000000..bee2a80a59c0 --- /dev/null +++ b/packages/url_launcher/url_launcher/lib/src/url_launcher_string.dart @@ -0,0 +1,65 @@ +// 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:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +import 'types.dart'; + +/// String version of [launchUrl]. +/// +/// This should be used only in the very rare case of needing to launch a URL +/// that is considered valid by the host platform, but not by Dart's [Uri] +/// class. In all other cases, use [launchUrl] instead, as that will ensure +/// that you are providing a valid URL. +/// +/// The behavior of this method when passing an invalid URL is entirely +/// platform-specific; no effort is made by the plugin to make the URL valid. +/// Some platforms may provide best-effort interpretation of an invalid URL, +/// others will immediately fail if the URL can't be parsed according to the +/// official standards that define URL formats. +Future launchUrlString( + String urlString, { + LaunchMode mode = LaunchMode.platformDefault, + WebViewConfiguration webViewConfiguration = const WebViewConfiguration(), + String? webOnlyWindowName, +}) async { + final bool isWebURL = + urlString.startsWith('http:') || urlString.startsWith('https:'); + if (mode == LaunchMode.inAppWebView && !isWebURL) { + throw ArgumentError.value(urlString, 'urlString', + 'To use an in-app web view, you must provide an http(s) URL.'); + } + final bool useWebView = mode == LaunchMode.inAppWebView || + (isWebURL && mode == LaunchMode.platformDefault); + + // TODO(stuartmorgan): Create a replacement platform interface method that + // uses something more like the new argument structure, and switch to using + // that, to support launch mode on more platforms. + return await UrlLauncherPlatform.instance.launch( + urlString, + useSafariVC: useWebView, + useWebView: useWebView, + enableJavaScript: webViewConfiguration.enableJavaScript, + enableDomStorage: webViewConfiguration.enableDomStorage, + universalLinksOnly: mode == LaunchMode.externalNonBrowserApplication, + headers: webViewConfiguration.headers, + webOnlyWindowName: webOnlyWindowName, + ); +} + +/// String version of [canLaunchUrl]. +/// +/// This should be used only in the very rare case of needing to check a URL +/// that is considered valid by the host platform, but not by Dart's [Uri] +/// class. In all other cases, use [canLaunchUrl] instead, as that will ensure +/// that you are providing a valid URL. +/// +/// The behavior of this method when passing an invalid URL is entirely +/// platform-specific; no effort is made by the plugin to make the URL valid. +/// Some platforms may provide best-effort interpretation of an invalid URL, +/// others will immediately fail if the URL can't be parsed according to the +/// official standards that define URL formats. +Future canLaunchUrlString(String urlString) async { + return await UrlLauncherPlatform.instance.canLaunch(urlString); +} diff --git a/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart b/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart new file mode 100644 index 000000000000..1ca787f44180 --- /dev/null +++ b/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.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:async'; + +import 'package:url_launcher/url_launcher_string.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +import 'types.dart'; + +/// Passes [url] to the underlying platform for handling. +/// +/// [mode] support varies significantly by platform: +/// - [LaunchMode.platformDefault] is supported on all platforms: +/// - On iOS and Android, this treats web URLs as +/// [LaunchMode.inAppWebView], and all other URLs as +/// [LaunchMode.externalApplication]. +/// - On Windows, macOS, and Linux this behaves like +/// [LaunchMode.externalApplication]. +/// - On web, this uses `webOnlyWindowName` for web URLs, and behaves like +/// [LaunchMode.externalApplication] for any other content. +/// - [LaunchMode.inAppWebView] is currently only supported on iOS and +/// Android. If a non-web URL is passed with this mode, an [ArgumentError] +/// will be thrown. +/// - [LaunchMode.externalApplication] is supported on all platforms. +/// On iOS, this should be used in cases where sharing the cookies of the +/// user's browser is important, such as SSO flows, since Safari View +/// Controller does not share the browser's context. +/// - [LaunchMode.externalNonBrowserApplication] is supported on iOS 10+. +/// This setting is used to require universal links to open in a non-browser +/// application. +/// +/// For web, [webOnlyWindowName] specifies a target for the launch. This +/// supports the standard special link target names. For example: +/// - "_blank" opens the new URL in a new tab. +/// - "_self" opens the new URL in the current tab. +/// Default behaviour when unset is to open the url in a new tab. +/// +/// Returns true if the URL was launched successful, otherwise either returns +/// false or throws a [PlatformException] depending on the failure. +Future launchUrl( + Uri url, { + LaunchMode mode = LaunchMode.platformDefault, + WebViewConfiguration webViewConfiguration = const WebViewConfiguration(), + String? webOnlyWindowName, +}) async { + final bool isWebURL = url.scheme == 'http' || url.scheme == 'https'; + if (mode == LaunchMode.inAppWebView && !isWebURL) { + throw ArgumentError.value(url, 'url', + 'To use an in-app web view, you must provide an http(s) URL.'); + } + // TODO(stuartmorgan): Use UrlLauncherPlatform directly once a new API + // that better matches these parameters has been added. For now, delegate to + // launchUrlString so that there's only one copy of the parameter translation + // logic. + return await launchUrlString( + url.toString(), + mode: mode, + webViewConfiguration: webViewConfiguration, + webOnlyWindowName: webOnlyWindowName, + ); +} + +/// Checks whether the specified URL can be handled by some app installed on the +/// device. +/// +/// Returns true if it is possible to verify that there is a handler available. +/// A false return value can indicate either that there is no handler available, +/// or that the application does not have permission to check. For example: +/// - On recent versions of Android and iOS, this will always return false +/// unless the application has been configuration to allow +/// querying the system for launch support. See +/// [the README](https://pub.dev/packages/url_launcher#configuration) for +/// details. +/// - On web, this will always return false except for a few specific schemes +/// that are always assumed to be supported (such as http(s)), as web pages +/// are never allowed to query installed applications. +Future canLaunchUrl(Uri url) async { + return await UrlLauncherPlatform.instance.canLaunch(url.toString()); +} + +/// Closes the current in-app web view, if one was previously opened by +/// [launchUrl]. +/// +/// If [launchUrl] was never called with [LaunchMode.inAppWebView], then this +/// call will have no effect. +Future closeInAppWebView() async { + return await UrlLauncherPlatform.instance.closeWebView(); +} diff --git a/packages/url_launcher/url_launcher/lib/url_launcher.dart b/packages/url_launcher/url_launcher/lib/url_launcher.dart index f28c460cce4f..36c7b60fdacd 100644 --- a/packages/url_launcher/url_launcher/lib/url_launcher.dart +++ b/packages/url_launcher/url_launcher/lib/url_launcher.dart @@ -2,150 +2,6 @@ // 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/foundation.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; -import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; - -/// Parses the specified URL string and delegates handling of it to the -/// underlying platform. -/// -/// The returned future completes with a [PlatformException] on invalid URLs and -/// schemes which cannot be handled, that is when [canLaunch] would complete -/// with false. -/// -/// By default when [forceSafariVC] is unset, the launcher -/// opens web URLs in the Safari View Controller, anything else is opened -/// using the default handler on the platform. If set to true, it opens the -/// URL in the Safari View Controller. If false, the URL is opened in the -/// default browser of the phone. Note that to work with universal links on iOS, -/// this must be set to false to let the platform's system handle the URL. -/// Set this to false if you want to use the cookies/context of the main browser -/// of the app (such as SSO flows). This setting will nullify [universalLinksOnly] -/// and will always launch a web content in the built-in Safari View Controller regardless -/// if the url is a universal link or not. -/// -/// [universalLinksOnly] is only used in iOS with iOS version >= 10.0. This setting is only validated -/// when [forceSafariVC] is set to false. The default value of this setting is false. -/// By default (when unset), the launcher will either launch the url in a browser (when the -/// url is not a universal link), or launch the respective native app content (when -/// the url is a universal link). When set to true, the launcher will only launch -/// the content if the url is a universal link and the respective app for the universal -/// link is installed on the user's device; otherwise throw a [PlatformException]. -/// -/// [forceWebView] is an Android only setting. If null or false, the URL is -/// always launched with the default browser on device. If set to true, the URL -/// is launched in a WebView. Unlike iOS, browser context is shared across -/// WebViews. -/// [enableJavaScript] is an Android only setting. If true, WebView enable -/// javascript. -/// [enableDomStorage] is an Android only setting. If true, WebView enable -/// DOM storage. -/// [headers] is an Android only setting that adds headers to the WebView. -/// When not using a WebView, the header information is passed to the browser, -/// some Android browsers do not support the [Browser.EXTRA_HEADERS](https://developer.android.com/reference/android/provider/Browser#EXTRA_HEADERS) -/// intent extra and the header information will be lost. -/// [webOnlyWindowName] is an Web only setting . _blank opens the new url in new tab , -/// _self opens the new url in current tab. -/// Default behaviour is to open the url in new tab. -/// -/// Note that if any of the above are set to true but the URL is not a web URL, -/// this will throw a [PlatformException]. -/// -/// [statusBarBrightness] Sets the status bar brightness of the application -/// after opening a link on iOS. Does nothing if no value is passed. This does -/// not handle resetting the previous status bar style. -/// -/// Returns true if launch url is successful; false is only returned when [universalLinksOnly] -/// is set to true and the universal link failed to launch. -Future launch( - String urlString, { - bool? forceSafariVC, - bool forceWebView = false, - bool enableJavaScript = false, - bool enableDomStorage = false, - bool universalLinksOnly = false, - Map headers = const {}, - Brightness? statusBarBrightness, - String? webOnlyWindowName, -}) async { - final Uri? url = Uri.tryParse(urlString.trimLeft()); - final bool isWebURL = - url != null && (url.scheme == 'http' || url.scheme == 'https'); - - if ((forceSafariVC == true || forceWebView == true) && !isWebURL) { - throw PlatformException( - code: 'NOT_A_WEB_SCHEME', - message: 'To use webview or safariVC, you need to pass' - 'in a web URL. This $urlString is not a web URL.'); - } - - /// [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; - SystemChrome.setSystemUIOverlayStyle(statusBarBrightness == Brightness.light - ? SystemUiOverlayStyle.dark - : SystemUiOverlayStyle.light); - } - - final bool result = await UrlLauncherPlatform.instance.launch( - urlString, - useSafariVC: forceSafariVC ?? isWebURL, - useWebView: forceWebView, - enableJavaScript: enableJavaScript, - enableDomStorage: enableDomStorage, - universalLinksOnly: universalLinksOnly, - headers: headers, - webOnlyWindowName: webOnlyWindowName, - ); - - if (statusBarBrightness != null && - _ambiguate(WidgetsBinding.instance) != null) { - _ambiguate(WidgetsBinding.instance)! - .renderView - .automaticSystemUiAdjustment = previousAutomaticSystemUiAdjustment; - } - - return result; -} - -/// Checks whether the specified URL can be handled by some app installed on the -/// device. -/// -/// On some systems, such as recent versions of Android and iOS, this will -/// always return false unless the application has been configuration to allow -/// querying the system for launch support. See -/// [the README](https://pub.dev/packages/url_launcher#configuration) for -/// details. -Future canLaunch(String urlString) async { - return await UrlLauncherPlatform.instance.canLaunch(urlString); -} - -/// Closes the current WebView, if one was previously opened via a call to [launch]. -/// -/// If [launch] was never called, then this call will not have any effect. -/// -/// On Android systems, if [launch] was called without `forceWebView` being set to `true` -/// Or on IOS systems, if [launch] was called without `forceSafariVC` being set to `true`, -/// this call will not do anything either, simply because there is no -/// WebView/SafariViewController available to be closed. -Future closeWebView() async { - return await UrlLauncherPlatform.instance.closeWebView(); -} - -/// This allows a value of type T or T? to be treated as a value of type T?. -/// -/// We use this so that APIs that have become non-nullable can still be used -/// with `!` and `?` on the stable branch. -// TODO(ianh): Remove this once we roll stable in late 2021. -T? _ambiguate(T? value) => value; +export 'src/legacy_api.dart'; +export 'src/types.dart'; +export 'src/url_launcher_uri.dart'; diff --git a/packages/url_launcher/url_launcher/lib/url_launcher_string.dart b/packages/url_launcher/url_launcher/lib/url_launcher_string.dart new file mode 100644 index 000000000000..b5a12b1e39ca --- /dev/null +++ b/packages/url_launcher/url_launcher/lib/url_launcher_string.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. + +// Provides a String-based alterantive to the Uri-based primary API. +// +// This is provided as a separate import because it's much easier to use +// incorrectly, so should require explicit opt-in (to avoid issues such as +// IDE auto-complete to the more error-prone APIs just by importing the +// main API). + +export 'src/types.dart'; +export 'src/url_launcher_string.dart'; diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index feb0a2c9ad95..6803d71032cb 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/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.0.20 +version: 6.1.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher/test/link_test.dart b/packages/url_launcher/url_launcher/test/link_test.dart index f7a98a0bf2f0..6242397c5ed7 100644 --- a/packages/url_launcher/url_launcher/test/link_test.dart +++ b/packages/url_launcher/url_launcher/test/link_test.dart @@ -9,7 +9,7 @@ import 'package:url_launcher/link.dart'; import 'package:url_launcher/src/link.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; -import 'mock_url_launcher_platform.dart'; +import 'mocks/mock_url_launcher_platform.dart'; void main() { late MockUrlLauncher mock; @@ -58,8 +58,8 @@ void main() { useSafariVC: false, useWebView: false, universalLinksOnly: false, - enableJavaScript: false, - enableDomStorage: false, + enableJavaScript: true, + enableDomStorage: true, headers: {}, webOnlyWindowName: null, ) @@ -88,8 +88,8 @@ void main() { useSafariVC: true, useWebView: true, universalLinksOnly: false, - enableJavaScript: false, - enableDomStorage: false, + enableJavaScript: true, + enableDomStorage: true, headers: {}, webOnlyWindowName: null, ) diff --git a/packages/url_launcher/url_launcher/test/mock_url_launcher_platform.dart b/packages/url_launcher/url_launcher/test/mocks/mock_url_launcher_platform.dart similarity index 100% rename from packages/url_launcher/url_launcher/test/mock_url_launcher_platform.dart rename to packages/url_launcher/url_launcher/test/mocks/mock_url_launcher_platform.dart diff --git a/packages/url_launcher/url_launcher/test/url_launcher_test.dart b/packages/url_launcher/url_launcher/test/src/legacy_api_test.dart similarity index 99% rename from packages/url_launcher/url_launcher/test/url_launcher_test.dart rename to packages/url_launcher/url_launcher/test/src/legacy_api_test.dart index 4e980cb37253..4594ab21bffc 100644 --- a/packages/url_launcher/url_launcher/test/url_launcher_test.dart +++ b/packages/url_launcher/url_launcher/test/src/legacy_api_test.dart @@ -8,10 +8,10 @@ import 'dart:ui'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart' show PlatformException; import 'package:flutter_test/flutter_test.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:url_launcher/src/legacy_api.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; -import 'mock_url_launcher_platform.dart'; +import '../mocks/mock_url_launcher_platform.dart'; void main() { final MockUrlLauncher mock = MockUrlLauncher(); diff --git a/packages/url_launcher/url_launcher/test/src/url_launcher_string_test.dart b/packages/url_launcher/url_launcher/test/src/url_launcher_string_test.dart new file mode 100644 index 000000000000..95b2f5c27bf3 --- /dev/null +++ b/packages/url_launcher/url_launcher/test/src/url_launcher_string_test.dart @@ -0,0 +1,279 @@ +// 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:url_launcher/src/types.dart'; +import 'package:url_launcher/src/url_launcher_string.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +import '../mocks/mock_url_launcher_platform.dart'; + +void main() { + final MockUrlLauncher mock = MockUrlLauncher(); + UrlLauncherPlatform.instance = mock; + + group('canLaunchUrlString', () { + test('handles returning true', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setCanLaunchExpectations(urlString) + ..setResponse(true); + + final bool result = await canLaunchUrlString(urlString); + + expect(result, isTrue); + }); + + test('handles returning false', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setCanLaunchExpectations(urlString) + ..setResponse(false); + + final bool result = await canLaunchUrlString(urlString); + + expect(result, isFalse); + }); + }); + + group('launchUrlString', () { + test('default behavior with web URL', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrlString(urlString), isTrue); + }); + + test('default behavior with non-web URL', () async { + const String urlString = 'customscheme:foo'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrlString(urlString), isTrue); + }); + + test('explicit default launch mode with web URL', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrlString(urlString, mode: LaunchMode.platformDefault), + isTrue); + }); + + test('explicit default launch mode with non-web URL', () async { + const String urlString = 'customscheme:foo'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrlString(urlString, mode: LaunchMode.platformDefault), + isTrue); + }); + + test('in-app webview', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrlString(urlString, mode: LaunchMode.inAppWebView), + isTrue); + }); + + test('external browser', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrlString(urlString, + mode: LaunchMode.externalApplication), + isTrue); + }); + + test('external non-browser only', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: true, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrlString(urlString, + mode: LaunchMode.externalNonBrowserApplication), + isTrue); + }); + + test('in-app webview without javascript', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: true, + useWebView: true, + enableJavaScript: false, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrlString(urlString, + mode: LaunchMode.inAppWebView, + webViewConfiguration: + const WebViewConfiguration(enableJavaScript: false)), + isTrue); + }); + + test('in-app webview without DOM storage', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: false, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrlString(urlString, + mode: LaunchMode.inAppWebView, + webViewConfiguration: + const WebViewConfiguration(enableDomStorage: false)), + isTrue); + }); + + test('in-app webview with headers', () async { + const String urlString = 'https://flutter.dev'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {'key': 'value'}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrlString(urlString, + mode: LaunchMode.inAppWebView, + webViewConfiguration: const WebViewConfiguration( + headers: {'key': 'value'})), + isTrue); + }); + + test('cannot launch a non-web URL in a webview', () async { + expect( + () async => await launchUrlString('tel:555-555-5555', + mode: LaunchMode.inAppWebView), + throwsA(isA())); + }); + + test('non-web URL with default options', () async { + const String emailLaunchUrlString = + 'mailto:smith@example.com?subject=Hello'; + mock + ..setLaunchExpectations( + url: emailLaunchUrlString, + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrlString(emailLaunchUrlString), isTrue); + }); + + test('allows non-parseable url', () async { + // Not a valid Dart [Uri], but a valid URL on at least some platforms. + const String urlString = + 'rdp://full%20address=s:mypc:3389&audiomode=i:2&disable%20themes=i:1'; + mock + ..setLaunchExpectations( + url: urlString, + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrlString(urlString), isTrue); + }); + }); +} diff --git a/packages/url_launcher/url_launcher/test/src/url_launcher_uri_test.dart b/packages/url_launcher/url_launcher/test/src/url_launcher_uri_test.dart new file mode 100644 index 000000000000..8286e0c43d20 --- /dev/null +++ b/packages/url_launcher/url_launcher/test/src/url_launcher_uri_test.dart @@ -0,0 +1,262 @@ +// 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:url_launcher/src/types.dart'; +import 'package:url_launcher/src/url_launcher_uri.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +import '../mocks/mock_url_launcher_platform.dart'; + +void main() { + final MockUrlLauncher mock = MockUrlLauncher(); + UrlLauncherPlatform.instance = mock; + + test('closeInAppWebView', () async { + await closeInAppWebView(); + expect(mock.closeWebViewCalled, isTrue); + }); + + group('canLaunchUrl', () { + test('handles returning true', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setCanLaunchExpectations(url.toString()) + ..setResponse(true); + + final bool result = await canLaunchUrl(url); + + expect(result, isTrue); + }); + + test('handles returning false', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setCanLaunchExpectations(url.toString()) + ..setResponse(false); + + final bool result = await canLaunchUrl(url); + + expect(result, isFalse); + }); + }); + + group('launchUrl', () { + test('default behavior with web URL', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrl(url), isTrue); + }); + + test('default behavior with non-web URL', () async { + final Uri url = Uri.parse('customscheme:foo'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrl(url), isTrue); + }); + + test('explicit default launch mode with web URL', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrl(url, mode: LaunchMode.platformDefault), isTrue); + }); + + test('explicit default launch mode with non-web URL', () async { + final Uri url = Uri.parse('customscheme:foo'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrl(url, mode: LaunchMode.platformDefault), isTrue); + }); + + test('in-app webview', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrl(url, mode: LaunchMode.inAppWebView), isTrue); + }); + + test('external browser', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrl(url, mode: LaunchMode.externalApplication), isTrue); + }); + + test('external non-browser only', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: true, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrl(url, mode: LaunchMode.externalNonBrowserApplication), + isTrue); + }); + + test('in-app webview without javascript', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: true, + useWebView: true, + enableJavaScript: false, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrl(url, + mode: LaunchMode.inAppWebView, + webViewConfiguration: + const WebViewConfiguration(enableJavaScript: false)), + isTrue); + }); + + test('in-app webview without DOM storage', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: false, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrl(url, + mode: LaunchMode.inAppWebView, + webViewConfiguration: + const WebViewConfiguration(enableDomStorage: false)), + isTrue); + }); + + test('in-app webview with headers', () async { + final Uri url = Uri.parse('https://flutter.dev'); + mock + ..setLaunchExpectations( + url: url.toString(), + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {'key': 'value'}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect( + await launchUrl(url, + mode: LaunchMode.inAppWebView, + webViewConfiguration: const WebViewConfiguration( + headers: {'key': 'value'})), + isTrue); + }); + + test('cannot launch a non-web URL in a webview', () async { + expect( + () async => await launchUrl(Uri(scheme: 'tel', path: '555-555-5555'), + mode: LaunchMode.inAppWebView), + throwsA(isA())); + }); + + test('non-web URL with default options', () async { + final Uri emailLaunchUrl = Uri( + scheme: 'mailto', + path: 'smith@example.com', + queryParameters: {'subject': 'Hello'}, + ); + mock + ..setLaunchExpectations( + url: emailLaunchUrl.toString(), + useSafariVC: false, + useWebView: false, + enableJavaScript: true, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + webOnlyWindowName: null, + ) + ..setResponse(true); + expect(await launchUrl(emailLaunchUrl), isTrue); + }); + }); +} From 4035b518b348be34ad317e8d741e7e49b43e5167 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Fri, 22 Apr 2022 12:04:05 -0700 Subject: [PATCH 190/844] Added gaaclarke as codeowner (#5323) --- CODEOWNERS | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index fdf0e30550f7..bc3e392d1766 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -5,7 +5,10 @@ # reviewed by someone else. # Plugin-level rules. -packages/webview_flutter/** @bparrishMines +packages/path_provider/** @gaaclarke +packages/shared_preferences/** @gaaclarke +packages/video_player/** @gaaclarke +packages/webview_flutter/** @bparrishMines # Sub-package-level rules. These should stay last, since the last matching # entry takes precedence. From 10c8f9e3fba3ff98dd9344a09b1b6bb98efe3959 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 22 Apr 2022 16:14:09 -0400 Subject: [PATCH 191/844] [image_picker] Migrate iOS to Pigeon and improve state handling (#5285) --- .../image_picker_ios/CHANGELOG.md | 6 + .../ios/RunnerTests/ImagePickerPluginTests.m | 140 ++- .../ios/Classes/FLTImagePickerPlugin.m | 404 ++++---- .../ios/Classes/FLTImagePickerPlugin_Test.h | 67 +- .../image_picker_ios/ios/Classes/messages.g.h | 61 ++ .../image_picker_ios/ios/Classes/messages.g.m | 216 ++++ .../lib/image_picker_ios.dart | 209 ++++ .../image_picker_ios/lib/src/messages.g.dart | 194 ++++ .../image_picker_ios/pigeons/copyright.txt | 3 + .../image_picker_ios/pigeons/messages.dart | 47 + .../image_picker_ios/pubspec.yaml | 6 +- .../test/image_picker_ios_test.dart | 937 ++++++++++++++++++ .../image_picker_ios/test/test_api.dart | 127 +++ 13 files changed, 2129 insertions(+), 288 deletions(-) create mode 100644 packages/image_picker/image_picker_ios/ios/Classes/messages.g.h create mode 100644 packages/image_picker/image_picker_ios/ios/Classes/messages.g.m create mode 100644 packages/image_picker/image_picker_ios/lib/image_picker_ios.dart create mode 100644 packages/image_picker/image_picker_ios/lib/src/messages.g.dart create mode 100644 packages/image_picker/image_picker_ios/pigeons/copyright.txt create mode 100644 packages/image_picker/image_picker_ios/pigeons/messages.dart create mode 100644 packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart create mode 100644 packages/image_picker/image_picker_ios/test/test_api.dart diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index 3472ade28d5b..3380d14418c2 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.8.5 + +* Switches to an in-package method channel based on Pigeon. +* Fixes invalid casts when selecting multiple images on versions of iOS before + 14.0. + ## 0.8.4+11 * Splits from `image_picker` as a federated implementation. diff --git a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m index 8df5299e54d9..04d491131d5b 100644 --- a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m @@ -47,14 +47,15 @@ - (void)testPluginPickImageDeviceBack { // Run test FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"pickImage" - arguments:@{@"source" : @(0), @"cameraDevice" : @(0)}]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; - [plugin handleMethodCall:call - result:^(id _Nullable r){ - }]; + + [plugin pickImageWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeCamera + camera:FLTSourceCameraRear] + maxSize:[[FLTMaxSize alloc] init] + quality:nil + completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ + }]; XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceRear); } @@ -78,14 +79,15 @@ - (void)testPluginPickImageDeviceFront { // Run test FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"pickImage" - arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; - [plugin handleMethodCall:call - result:^(id _Nullable r){ - }]; + + [plugin pickImageWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeCamera + camera:FLTSourceCameraFront] + maxSize:[[FLTMaxSize alloc] init] + quality:nil + completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ + }]; XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceFront); } @@ -109,14 +111,14 @@ - (void)testPluginPickVideoDeviceBack { // Run test FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"pickVideo" - arguments:@{@"source" : @(0), @"cameraDevice" : @(0)}]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; - [plugin handleMethodCall:call - result:^(id _Nullable r){ - }]; + + [plugin pickVideoWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeCamera + camera:FLTSourceCameraRear] + maxDuration:nil + completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ + }]; XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceRear); } @@ -141,14 +143,14 @@ - (void)testPluginPickVideoDeviceFront { // Run test FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"pickVideo" - arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; - [plugin handleMethodCall:call - result:^(id _Nullable r){ - }]; + + [plugin pickVideoWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeCamera + camera:FLTSourceCameraFront] + maxDuration:nil + completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ + }]; XCTAssertEqual(controller.cameraDevice, UIImagePickerControllerCameraDeviceFront); } @@ -165,17 +167,12 @@ - (void)testPickMultiImageShouldUseUIImagePickerControllerOnPreiOS14 { FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; [plugin setImagePickerControllerOverrides:@[ mockUIImagePicker ]]; - FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickMultiImage" - arguments:@{ - @"maxWidth" : @(100), - @"maxHeight" : @(200), - @"imageQuality" : @(50), - }]; - - [plugin handleMethodCall:call - result:^(id _Nullable r){ - }]; + [plugin pickMultiImageWithMaxSize:[FLTMaxSize makeWithWidth:@(100) height:@(200)] + quality:@(50) + completion:^(NSArray *_Nullable result, + FlutterError *_Nullable error){ + }]; OCMVerify(times(1), [mockUIImagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]); } @@ -187,17 +184,15 @@ - (void)testPluginPickImageDeviceCancelClickMultipleTimes { return; } FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"pickImage" - arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; plugin.imagePickerControllerOverrides = @[ controller ]; - [plugin handleMethodCall:call - result:^(id _Nullable r){ - }]; - plugin.result = ^(id result) { - }; + [plugin pickImageWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeCamera + camera:FLTSourceCameraRear] + maxSize:[[FLTMaxSize alloc] init] + quality:nil + completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ + }]; // To ensure the flow does not crash by multiple cancel call [plugin imagePickerControllerDidCancel:controller]; @@ -208,14 +203,15 @@ - (void)testPluginPickImageDeviceCancelClickMultipleTimes { - (void)testPickingVideoWithDuration { FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; - FlutterMethodCall *call = [FlutterMethodCall - methodCallWithMethodName:@"pickVideo" - arguments:@{@"source" : @(0), @"cameraDevice" : @(0), @"maxDuration" : @95}]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; - [plugin handleMethodCall:call - result:^(id _Nullable r){ - }]; + + [plugin pickVideoWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeCamera + camera:FLTSourceCameraRear] + maxDuration:@(95) + completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ + }]; + XCTAssertEqual(controller.videoMaximumDuration, 95); } @@ -231,37 +227,17 @@ - (void)testViewController { XCTAssertEqual([plugin viewControllerWithWindow:window], vc2); } -- (void)testPluginMultiImagePathIsNil { - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; - - dispatch_semaphore_t resultSemaphore = dispatch_semaphore_create(0); - __block FlutterError *pickImageResult = nil; - - plugin.result = ^(id _Nullable r) { - pickImageResult = r; - dispatch_semaphore_signal(resultSemaphore); - }; - [plugin handleSavedPathList:nil]; - - dispatch_semaphore_wait(resultSemaphore, DISPATCH_TIME_FOREVER); - - XCTAssertEqualObjects(pickImageResult.code, @"create_error"); -} - - (void)testPluginMultiImagePathHasNullItem { FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; - NSMutableArray *pathList = [NSMutableArray new]; - - [pathList addObject:[NSNull null]]; dispatch_semaphore_t resultSemaphore = dispatch_semaphore_create(0); __block FlutterError *pickImageResult = nil; - - plugin.result = ^(id _Nullable r) { - pickImageResult = r; - dispatch_semaphore_signal(resultSemaphore); - }; - [plugin handleSavedPathList:pathList]; + plugin.callContext = [[FLTImagePickerMethodCallContext alloc] + initWithResult:^(NSArray *_Nullable result, FlutterError *_Nullable error) { + pickImageResult = error; + dispatch_semaphore_signal(resultSemaphore); + }]; + [plugin sendCallResultWithSavedPathList:@[ [NSNull null] ]]; dispatch_semaphore_wait(resultSemaphore, DISPATCH_TIME_FOREVER); @@ -270,19 +246,17 @@ - (void)testPluginMultiImagePathHasNullItem { - (void)testPluginMultiImagePathHasItem { FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; - NSString *savedPath = @"test"; - NSMutableArray *pathList = [NSMutableArray new]; - - [pathList addObject:savedPath]; + NSArray *pathList = @[ @"test" ]; dispatch_semaphore_t resultSemaphore = dispatch_semaphore_create(0); __block id pickImageResult = nil; - plugin.result = ^(id _Nullable r) { - pickImageResult = r; - dispatch_semaphore_signal(resultSemaphore); - }; - [plugin handleSavedPathList:pathList]; + plugin.callContext = [[FLTImagePickerMethodCallContext alloc] + initWithResult:^(NSArray *_Nullable result, FlutterError *_Nullable error) { + pickImageResult = result; + dispatch_semaphore_signal(resultSemaphore); + }]; + [plugin sendCallResultWithSavedPathList:pathList]; dispatch_semaphore_wait(resultSemaphore, DISPATCH_TIME_FOREVER); 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 cc841d6db447..76ed9623a57c 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m @@ -16,31 +16,24 @@ #import "FLTImagePickerMetaDataUtil.h" #import "FLTImagePickerPhotoAssetUtil.h" #import "FLTPHPickerSaveImageToPathOperation.h" +#import "messages.g.h" -/** - * Returns the value for the given key in 'dict', or nil if the value is - * NSNull. - */ -id GetNullableValueForKey(NSDictionary *dict, NSString *key) { - id value = dict[key]; - return value == [NSNull null] ? nil : value; +@implementation FLTImagePickerMethodCallContext +- (instancetype)initWithResult:(nonnull FlutterResultAdapter)result { + if (self = [super init]) { + _result = [result copy]; + } + return self; } +@end + +#pragma mark - @interface FLTImagePickerPlugin () -/** - * The maximum amount of images that are allowed to be picked. - */ -@property(assign, nonatomic) int maxImagesAllowed; - -/** - * The arguments that are passed in from the Flutter method call. - */ -@property(copy, nonatomic) NSDictionary *arguments; - /** * The PHPickerViewController instance used to pick multiple * images. @@ -58,19 +51,13 @@ @interface FLTImagePickerPlugin () *)registrar { - FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/image_picker" - binaryMessenger:[registrar messenger]]; FLTImagePickerPlugin *instance = [FLTImagePickerPlugin new]; - [registrar addMethodCallDelegate:instance channel:channel]; + FLTImagePickerApiSetup(registrar.messenger, instance); } - (UIImagePickerController *)createImagePickerController { @@ -107,130 +94,180 @@ - (UIViewController *)viewControllerWithWindow:(UIWindow *)window { } /** - * Returns the UIImagePickerControllerCameraDevice to use given [arguments]. - * - * If the cameraDevice value that is fetched from arguments is 1 then returns - * UIImagePickerControllerCameraDeviceFront. If the cameraDevice value that is fetched - * from arguments is 0 then returns UIImagePickerControllerCameraDeviceRear. + * Returns the UIImagePickerControllerCameraDevice to use given [source]. * - * @param arguments that should be used to get cameraDevice value. + * @param source The source specification from Dart. */ -- (UIImagePickerControllerCameraDevice)getCameraDeviceFromArguments:(NSDictionary *)arguments { - NSInteger cameraDevice = [arguments[@"cameraDevice"] intValue]; - return (cameraDevice == 1) ? UIImagePickerControllerCameraDeviceFront - : UIImagePickerControllerCameraDeviceRear; +- (UIImagePickerControllerCameraDevice)cameraDeviceForSource:(FLTSourceSpecification *)source { + switch (source.camera) { + case FLTSourceCameraFront: + return UIImagePickerControllerCameraDeviceFront; + case FLTSourceCameraRear: + return UIImagePickerControllerCameraDeviceRear; + } } -- (void)pickImageWithPHPicker:(int)maxImagesAllowed API_AVAILABLE(ios(14)) { +- (void)launchPHPickerWithContext:(nonnull FLTImagePickerMethodCallContext *)context + API_AVAILABLE(ios(14)) { PHPickerConfiguration *config = [[PHPickerConfiguration alloc] initWithPhotoLibrary:PHPhotoLibrary.sharedPhotoLibrary]; - config.selectionLimit = maxImagesAllowed; // Setting to zero allow us to pick unlimited photos + config.selectionLimit = context.maxImageCount; config.filter = [PHPickerFilter imagesFilter]; _pickerViewController = [[PHPickerViewController alloc] initWithConfiguration:config]; _pickerViewController.delegate = self; _pickerViewController.presentationController.delegate = self; - - self.maxImagesAllowed = maxImagesAllowed; + self.callContext = context; [self checkPhotoAuthorizationForAccessLevel]; } -- (void)launchUIImagePickerWithSource:(int)imageSource { +- (void)launchUIImagePickerWithSource:(nonnull FLTSourceSpecification *)source + context:(nonnull FLTImagePickerMethodCallContext *)context { UIImagePickerController *imagePickerController = [self createImagePickerController]; imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext; imagePickerController.delegate = self; imagePickerController.mediaTypes = @[ (NSString *)kUTTypeImage ]; + self.callContext = context; - self.maxImagesAllowed = 1; - - switch (imageSource) { - case SOURCE_CAMERA: - [self checkCameraAuthorizationWithImagePicker:imagePickerController]; + switch (source.type) { + case FLTSourceTypeCamera: + [self checkCameraAuthorizationWithImagePicker:imagePickerController + camera:[self cameraDeviceForSource:source]]; break; - case SOURCE_GALLERY: + case FLTSourceTypeGallery: [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; break; default: - self.result([FlutterError errorWithCode:@"invalid_source" - message:@"Invalid image source." - details:nil]); + [self sendCallResultWithError:[FlutterError errorWithCode:@"invalid_source" + message:@"Invalid image source." + details:nil]]; break; } } -- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { - if (self.result) { - self.result([FlutterError errorWithCode:@"multiple_request" - message:@"Cancelled by a second request" - details:nil]); - self.result = nil; - } - - self.result = result; - _arguments = call.arguments; - - if ([@"pickImage" isEqualToString:call.method]) { - int imageSource = [call.arguments[@"source"] intValue]; +#pragma mark - FLTImagePickerApi + +- (void)pickImageWithSource:(nonnull FLTSourceSpecification *)source + maxSize:(nonnull FLTMaxSize *)maxSize + quality:(nullable NSNumber *)imageQuality + completion: + (nonnull void (^)(NSString *_Nullable, FlutterError *_Nullable))completion { + [self cancelInProgressCall]; + FLTImagePickerMethodCallContext *context = [[FLTImagePickerMethodCallContext alloc] + initWithResult:^void(NSArray *paths, FlutterError *error) { + if (paths && paths.count != 1) { + completion(nil, [FlutterError errorWithCode:@"invalid_result" + message:@"Incorrect number of return paths provided" + details:nil]); + } + completion(paths.firstObject, error); + }]; + context.maxSize = maxSize; + context.imageQuality = imageQuality; + context.maxImageCount = 1; - if (imageSource == SOURCE_GALLERY) { // Capture is not possible with PHPicker - if (@available(iOS 14, *)) { - // PHPicker is used - [self pickImageWithPHPicker:1]; - } else { - // UIImagePicker is used - [self launchUIImagePickerWithSource:imageSource]; - } - } else { - [self launchUIImagePickerWithSource:imageSource]; - } - } else if ([@"pickMultiImage" isEqualToString:call.method]) { + if (source.type == FLTSourceTypeGallery) { // Capture is not possible with PHPicker if (@available(iOS 14, *)) { - [self pickImageWithPHPicker:0]; + [self launchPHPickerWithContext:context]; } else { - [self launchUIImagePickerWithSource:SOURCE_GALLERY]; - } - } else if ([@"pickVideo" isEqualToString:call.method]) { - UIImagePickerController *imagePickerController = [self createImagePickerController]; - imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext; - imagePickerController.delegate = self; - imagePickerController.mediaTypes = @[ - (NSString *)kUTTypeMovie, (NSString *)kUTTypeAVIMovie, (NSString *)kUTTypeVideo, - (NSString *)kUTTypeMPEG4 - ]; - imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh; - - int imageSource = [call.arguments[@"source"] intValue]; - if ([call.arguments[@"maxDuration"] isKindOfClass:[NSNumber class]]) { - NSTimeInterval max = [call.arguments[@"maxDuration"] doubleValue]; - imagePickerController.videoMaximumDuration = max; + [self launchUIImagePickerWithSource:source context:context]; } + } else { + [self launchUIImagePickerWithSource:source context:context]; + } +} - switch (imageSource) { - case SOURCE_CAMERA: - [self checkCameraAuthorizationWithImagePicker:imagePickerController]; - break; - case SOURCE_GALLERY: - [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; - break; - default: - result([FlutterError errorWithCode:@"invalid_source" - message:@"Invalid video source." - details:nil]); - break; - } +- (void)pickMultiImageWithMaxSize:(nonnull FLTMaxSize *)maxSize + quality:(nullable NSNumber *)imageQuality + completion:(nonnull void (^)(NSArray *_Nullable, + FlutterError *_Nullable))completion { + FLTImagePickerMethodCallContext *context = + [[FLTImagePickerMethodCallContext alloc] initWithResult:completion]; + context.maxSize = maxSize; + context.imageQuality = imageQuality; + + if (@available(iOS 14, *)) { + [self launchPHPickerWithContext:context]; } else { - result(FlutterMethodNotImplemented); + // Camera is ignored for gallery mode, so the value here is arbitrary. + [self launchUIImagePickerWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeGallery + camera:FLTSourceCameraRear] + context:context]; } } -- (void)showCameraWithImagePicker:(UIImagePickerController *)imagePickerController { +- (void)pickVideoWithSource:(nonnull FLTSourceSpecification *)source + maxDuration:(nullable NSNumber *)maxDurationSeconds + completion: + (nonnull void (^)(NSString *_Nullable, FlutterError *_Nullable))completion { + FLTImagePickerMethodCallContext *context = [[FLTImagePickerMethodCallContext alloc] + initWithResult:^void(NSArray *paths, FlutterError *error) { + if (paths && paths.count != 1) { + completion(nil, [FlutterError errorWithCode:@"invalid_result" + message:@"Incorrect number of return paths provided" + details:nil]); + } + completion(paths.firstObject, error); + }]; + context.maxImageCount = 1; + + UIImagePickerController *imagePickerController = [self createImagePickerController]; + imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext; + imagePickerController.delegate = self; + imagePickerController.mediaTypes = @[ + (NSString *)kUTTypeMovie, (NSString *)kUTTypeAVIMovie, (NSString *)kUTTypeVideo, + (NSString *)kUTTypeMPEG4 + ]; + imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh; + + if (maxDurationSeconds) { + NSTimeInterval max = [maxDurationSeconds doubleValue]; + imagePickerController.videoMaximumDuration = max; + } + + self.callContext = context; + + switch (source.type) { + case FLTSourceTypeCamera: + [self checkCameraAuthorizationWithImagePicker:imagePickerController + camera:[self cameraDeviceForSource:source]]; + break; + case FLTSourceTypeGallery: + [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; + break; + default: + [self sendCallResultWithError:[FlutterError errorWithCode:@"invalid_source" + message:@"Invalid video source." + details:nil]]; + break; + } +} + +#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). + */ +- (void)cancelInProgressCall { + if (self.callContext) { + [self sendCallResultWithError:[FlutterError errorWithCode:@"multiple_request" + message:@"Cancelled by a second request" + details:nil]]; + self.callContext = nil; + } +} + +- (void)showCamera:(UIImagePickerControllerCameraDevice)device + withImagePicker:(UIImagePickerController *)imagePickerController { @synchronized(self) { if (imagePickerController.beingPresented) { return; } } - UIImagePickerControllerCameraDevice device = [self getCameraDeviceFromArguments:_arguments]; // Camera is not available on simulators if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] && [UIImagePickerController isCameraDeviceAvailable:device]) { @@ -254,25 +291,24 @@ - (void)showCameraWithImagePicker:(UIImagePickerController *)imagePickerControll [[self viewControllerWithWindow:nil] presentViewController:cameraErrorAlert animated:YES completion:nil]; - self.result(nil); - self.result = nil; - _arguments = nil; + [self sendCallResultWithSavedPathList:nil]; } } -- (void)checkCameraAuthorizationWithImagePicker:(UIImagePickerController *)imagePickerController { +- (void)checkCameraAuthorizationWithImagePicker:(UIImagePickerController *)imagePickerController + camera:(UIImagePickerControllerCameraDevice)device { AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; switch (status) { case AVAuthorizationStatusAuthorized: - [self showCameraWithImagePicker:imagePickerController]; + [self showCamera:device withImagePicker:imagePickerController]; break; case AVAuthorizationStatusNotDetermined: { [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) { dispatch_async(dispatch_get_main_queue(), ^{ if (granted) { - [self showCameraWithImagePicker:imagePickerController]; + [self showCamera:device withImagePicker:imagePickerController]; } else { [self errorNoCameraAccess:AVAuthorizationStatusDenied]; } @@ -352,15 +388,17 @@ - (void)checkPhotoAuthorizationForAccessLevel API_AVAILABLE(ios(14)) { - (void)errorNoCameraAccess:(AVAuthorizationStatus)status { switch (status) { case AVAuthorizationStatusRestricted: - self.result([FlutterError errorWithCode:@"camera_access_restricted" - message:@"The user is not allowed to use the camera." - details:nil]); + [self sendCallResultWithError:[FlutterError + errorWithCode:@"camera_access_restricted" + message:@"The user is not allowed to use the camera." + details:nil]]; break; case AVAuthorizationStatusDenied: default: - self.result([FlutterError errorWithCode:@"camera_access_denied" - message:@"The user did not allow camera access." - details:nil]); + [self sendCallResultWithError:[FlutterError + errorWithCode:@"camera_access_denied" + message:@"The user did not allow camera access." + details:nil]]; break; } } @@ -368,15 +406,17 @@ - (void)errorNoCameraAccess:(AVAuthorizationStatus)status { - (void)errorNoPhotoAccess:(PHAuthorizationStatus)status { switch (status) { case PHAuthorizationStatusRestricted: - self.result([FlutterError errorWithCode:@"photo_access_restricted" - message:@"The user is not allowed to use the photo." - details:nil]); + [self sendCallResultWithError:[FlutterError + errorWithCode:@"photo_access_restricted" + message:@"The user is not allowed to use the photo." + details:nil]]; break; case PHAuthorizationStatusDenied: default: - self.result([FlutterError errorWithCode:@"photo_access_denied" - message:@"The user did not allow photo access." - details:nil]); + [self sendCallResultWithError:[FlutterError + errorWithCode:@"photo_access_denied" + message:@"The user did not allow photo access." + details:nil]]; break; } } @@ -406,31 +446,27 @@ - (NSNumber *)getDesiredImageQuality:(NSNumber *)imageQuality { return imageQuality; } +#pragma mark - UIAdaptivePresentationControllerDelegate + - (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController { - if (self.result != nil) { - self.result(nil); - self.result = nil; - self->_arguments = nil; - } + [self sendCallResultWithSavedPathList:nil]; } +#pragma mark - PHPickerViewControllerDelegate + - (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray *)results API_AVAILABLE(ios(14)) { [picker dismissViewControllerAnimated:YES completion:nil]; if (results.count == 0) { - if (self.result != nil) { - self.result(nil); - self.result = nil; - self->_arguments = nil; - } + [self sendCallResultWithSavedPathList:nil]; return; } dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); dispatch_async(backgroundQueue, ^{ - NSNumber *maxWidth = GetNullableValueForKey(self->_arguments, @"maxWidth"); - NSNumber *maxHeight = GetNullableValueForKey(self->_arguments, @"maxHeight"); - NSNumber *imageQuality = GetNullableValueForKey(self->_arguments, @"imageQuality"); + NSNumber *maxWidth = self.callContext.maxSize.width; + NSNumber *maxHeight = self.callContext.maxSize.height; + NSNumber *imageQuality = self.callContext.imageQuality; NSNumber *desiredImageQuality = [self getDesiredImageQuality:imageQuality]; NSOperationQueue *operationQueue = [NSOperationQueue new]; NSMutableArray *pathList = [self createNSMutableArrayWithSize:results.count]; @@ -449,11 +485,13 @@ - (void)picker:(PHPickerViewController *)picker } [operationQueue waitUntilAllOperationsAreFinished]; dispatch_async(dispatch_get_main_queue(), ^{ - [self handleSavedPathList:pathList]; + [self sendCallResultWithSavedPathList:pathList]; }); }); } +#pragma mark - + /** * Creates an NSMutableArray of a certain size filled with NSNull objects. * @@ -470,6 +508,8 @@ - (NSMutableArray *)createNSMutableArrayWithSize:(NSUInteger)size { return mutableArray; } +#pragma mark - UIImagePickerControllerDelegate + - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { NSURL *videoURL = info[UIImagePickerControllerMediaURL]; @@ -478,7 +518,7 @@ - (void)imagePickerController:(UIImagePickerController *)picker // further didFinishPickingMediaWithInfo invocations. A nil check is necessary // to prevent below code to be unwantly executed multiple times and cause a // crash. - if (!self.result) { + if (!self.callContext) { return; } if (videoURL != nil) { @@ -493,27 +533,25 @@ - (void)imagePickerController:(UIImagePickerController *)picker [[NSFileManager defaultManager] copyItemAtURL:videoURL toURL:destination error:&error]; if (error) { - self.result([FlutterError errorWithCode:@"flutter_image_picker_copy_video_error" - message:@"Could not cache the video file." - details:nil]); - self.result = nil; + [self sendCallResultWithError:[FlutterError + errorWithCode:@"flutter_image_picker_copy_video_error" + message:@"Could not cache the video file." + details:nil]]; return; } } videoURL = destination; } } - self.result(videoURL.path); - self.result = nil; - _arguments = nil; + [self sendCallResultWithSavedPathList:@[ videoURL.path ]]; } else { UIImage *image = info[UIImagePickerControllerEditedImage]; if (image == nil) { image = info[UIImagePickerControllerOriginalImage]; } - NSNumber *maxWidth = GetNullableValueForKey(_arguments, @"maxWidth"); - NSNumber *maxHeight = GetNullableValueForKey(_arguments, @"maxHeight"); - NSNumber *imageQuality = GetNullableValueForKey(_arguments, @"imageQuality"); + NSNumber *maxWidth = self.callContext.maxSize.width; + NSNumber *maxHeight = self.callContext.maxSize.height; + NSNumber *imageQuality = self.callContext.imageQuality; NSNumber *desiredImageQuality = [self getDesiredImageQuality:imageQuality]; PHAsset *originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromImagePickerInfo:info]; @@ -547,14 +585,11 @@ - (void)imagePickerController:(UIImagePickerController *)picker - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { [picker dismissViewControllerAnimated:YES completion:nil]; - if (!self.result) { - return; - } - self.result(nil); - self.result = nil; - _arguments = nil; + [self sendCallResultWithSavedPathList:nil]; } +#pragma mark - + - (void)saveImageWithOriginalImageData:(NSData *)originalImageData image:(UIImage *)image maxWidth:(NSNumber *)maxWidth @@ -566,7 +601,7 @@ - (void)saveImageWithOriginalImageData:(NSData *)originalImageData maxWidth:maxWidth maxHeight:maxHeight imageQuality:imageQuality]; - [self handleSavedPathList:@[ savedPath ]]; + [self sendCallResultWithSavedPathList:@[ savedPath ]]; } - (void)saveImageWithPickerInfo:(NSDictionary *)info @@ -575,47 +610,36 @@ - (void)saveImageWithPickerInfo:(NSDictionary *)info NSString *savedPath = [FLTImagePickerPhotoAssetUtil saveImageWithPickerInfo:info image:image imageQuality:imageQuality]; - [self handleSavedPathList:@[ savedPath ]]; + [self sendCallResultWithSavedPathList:@[ savedPath ]]; } -/** - * Applies NSMutableArray on the FLutterResult. - * - * NSString must be returned by FlutterResult if the single image - * mode is active. It is checked by maxImagesAllowed and - * returns the first object of the pathlist. - * - * NSMutableArray must be returned by FlutterResult if the multi-image - * mode is active. After the pathlist count is checked then it returns - * the pathlist. - * - * @param pathList that should be applied to FlutterResult. - */ -- (void)handleSavedPathList:(NSArray *)pathList { - if (!self.result) { +- (void)sendCallResultWithSavedPathList:(nullable NSArray *)pathList { + if (!self.callContext) { return; } - if (pathList) { - if (![pathList containsObject:[NSNull null]]) { - if ((self.maxImagesAllowed == 1)) { - self.result(pathList.firstObject); - } else { - self.result(pathList); - } - } else { - self.result([FlutterError errorWithCode:@"create_error" - message:@"pathList's items should not be null" - details:nil]); - } + if ([pathList containsObject:[NSNull null]]) { + self.callContext.result(nil, [FlutterError errorWithCode:@"create_error" + message:@"pathList's items should not be null" + details:nil]); } else { - // This should never happen. - self.result([FlutterError errorWithCode:@"create_error" - message:@"pathList should not be nil" - details:nil]); + self.callContext.result(pathList, nil); + } + 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. + */ +- (void)sendCallResultWithError:(FlutterError *)error { + if (!self.callContext) { + return; } - self.result = nil; - _arguments = nil; + self.callContext.result(nil, error); + self.callContext = nil; } @end 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 039f76de427d..2c4167746c8e 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 @@ -6,26 +6,65 @@ #import -/** Methods exposed for unit testing. */ -@interface FLTImagePickerPlugin () +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * The return hander 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. + */ +@interface FLTImagePickerMethodCallContext : NSObject + +/** + * Initializes a new context that calls |result| on completion of the operation. + */ +- (instancetype)initWithResult:(nonnull FlutterResultAdapter)result; -/** The Flutter result callback use to report results back to Flutter App. */ -@property(copy, nonatomic) FlutterResult result; +/** The callback to provide results to the Dart caller. */ +@property(nonatomic, copy, nonnull) FlutterResultAdapter result; /** - * Applies NSMutableArray on the FLutterResult. + * The maximum size to enforce on the results. * - * NSString must be returned by FlutterResult if the single image - * mode is active. It is checked by maxImagesAllowed and - * returns the first object of the pathlist. + * If nil, no resizing is done. + */ +@property(nonatomic, strong, nullable) FLTMaxSize *maxSize; + +/** + * The image quality to resample the results to. * - * NSMutableArray must be returned by FlutterResult if the multi-image - * mode is active. After the pathlist count is checked then it returns - * the pathlist. + * If nil, no resampling is done. + */ +@property(nonatomic, strong, nullable) NSNumber *imageQuality; + +/** Maximum number of images to select. 0 indicates no maximum. */ +@property(nonatomic, assign) int maxImageCount; + +@end + +#pragma mark - + +/** Methods exposed for unit testing. */ +@interface FLTImagePickerPlugin () + +/** + * The context of the Flutter method call that is currently being handled, if any. + */ +@property(strong, nonatomic, nullable) FLTImagePickerMethodCallContext *callContext; + +/** + * 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 that should be applied to FlutterResult. + * @param pathList The paths to return. nil indicates a cancelled operation. */ -- (void)handleSavedPathList:(NSArray *)pathList; +- (void)sendCallResultWithSavedPathList:(nullable NSArray *)pathList; /** * Tells the delegate that the user cancelled the pick operation. @@ -52,3 +91,5 @@ (NSArray *)imagePickerControllers; @end + +NS_ASSUME_NONNULL_END diff --git a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h new file mode 100644 index 000000000000..310165f72f4f --- /dev/null +++ b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h @@ -0,0 +1,61 @@ +// 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 (v3.0.2), do not edit directly. +// See also: https://pub.dev/packages/pigeon +#import +@protocol FlutterBinaryMessenger; +@protocol FlutterMessageCodec; +@class FlutterError; +@class FlutterStandardTypedData; + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, FLTSourceCamera) { + FLTSourceCameraRear = 0, + FLTSourceCameraFront = 1, +}; + +typedef NS_ENUM(NSUInteger, FLTSourceType) { + FLTSourceTypeCamera = 0, + FLTSourceTypeGallery = 1, +}; + +@class FLTMaxSize; +@class FLTSourceSpecification; + +@interface FLTMaxSize : NSObject ++ (instancetype)makeWithWidth:(nullable NSNumber *)width height:(nullable NSNumber *)height; +@property(nonatomic, strong, nullable) NSNumber *width; +@property(nonatomic, strong, nullable) NSNumber *height; +@end + +@interface FLTSourceSpecification : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithType:(FLTSourceType)type camera:(FLTSourceCamera)camera; +@property(nonatomic, assign) FLTSourceType type; +@property(nonatomic, assign) FLTSourceCamera camera; +@end + +/// The codec used by FLTImagePickerApi. +NSObject *FLTImagePickerApiGetCodec(void); + +@protocol FLTImagePickerApi +- (void)pickImageWithSource:(FLTSourceSpecification *)source + maxSize:(FLTMaxSize *)maxSize + quality:(nullable NSNumber *)imageQuality + completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)pickMultiImageWithMaxSize:(FLTMaxSize *)maxSize + quality:(nullable NSNumber *)imageQuality + completion:(void (^)(NSArray *_Nullable, + FlutterError *_Nullable))completion; +- (void)pickVideoWithSource:(FLTSourceSpecification *)source + maxDuration:(nullable NSNumber *)maxDurationSeconds + completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +@end + +extern void FLTImagePickerApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +NS_ASSUME_NONNULL_END diff --git a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m new file mode 100644 index 000000000000..6c91c0ab264f --- /dev/null +++ b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m @@ -0,0 +1,216 @@ +// 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 (v3.0.2), do not edit directly. +// See also: https://pub.dev/packages/pigeon +#import "messages.g.h" +#import + +#if !__has_feature(objc_arc) +#error File requires ARC to be enabled. +#endif + +static NSDictionary *wrapResult(id result, FlutterError *error) { + NSDictionary *errorDict = (NSDictionary *)[NSNull null]; + if (error) { + errorDict = @{ + @"code" : (error.code ? error.code : [NSNull null]), + @"message" : (error.message ? error.message : [NSNull null]), + @"details" : (error.details ? error.details : [NSNull null]), + }; + } + return @{ + @"result" : (result ? result : [NSNull null]), + @"error" : errorDict, + }; +} +static id GetNullableObject(NSDictionary *dict, id key) { + id result = dict[key]; + return (result == [NSNull null]) ? nil : result; +} +static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { + id result = array[key]; + return (result == [NSNull null]) ? nil : result; +} + +@interface FLTMaxSize () ++ (FLTMaxSize *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FLTSourceSpecification () ++ (FLTSourceSpecification *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end + +@implementation FLTMaxSize ++ (instancetype)makeWithWidth:(nullable NSNumber *)width height:(nullable NSNumber *)height { + FLTMaxSize *pigeonResult = [[FLTMaxSize alloc] init]; + pigeonResult.width = width; + pigeonResult.height = height; + return pigeonResult; +} ++ (FLTMaxSize *)fromMap:(NSDictionary *)dict { + FLTMaxSize *pigeonResult = [[FLTMaxSize alloc] init]; + pigeonResult.width = GetNullableObject(dict, @"width"); + pigeonResult.height = GetNullableObject(dict, @"height"); + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary + dictionaryWithObjectsAndKeys:(self.width ? self.width : [NSNull null]), @"width", + (self.height ? self.height : [NSNull null]), @"height", nil]; +} +@end + +@implementation FLTSourceSpecification ++ (instancetype)makeWithType:(FLTSourceType)type camera:(FLTSourceCamera)camera { + FLTSourceSpecification *pigeonResult = [[FLTSourceSpecification alloc] init]; + pigeonResult.type = type; + pigeonResult.camera = camera; + return pigeonResult; +} ++ (FLTSourceSpecification *)fromMap:(NSDictionary *)dict { + FLTSourceSpecification *pigeonResult = [[FLTSourceSpecification alloc] init]; + pigeonResult.type = [GetNullableObject(dict, @"type") integerValue]; + pigeonResult.camera = [GetNullableObject(dict, @"camera") integerValue]; + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary + dictionaryWithObjectsAndKeys:@(self.type), @"type", @(self.camera), @"camera", nil]; +} +@end + +@interface FLTImagePickerApiCodecReader : FlutterStandardReader +@end +@implementation FLTImagePickerApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FLTMaxSize fromMap:[self readValue]]; + + case 129: + return [FLTSourceSpecification fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} +@end + +@interface FLTImagePickerApiCodecWriter : FlutterStandardWriter +@end +@implementation FLTImagePickerApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FLTMaxSize class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FLTSourceSpecification class]]) { + [self writeByte:129]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface FLTImagePickerApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FLTImagePickerApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FLTImagePickerApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FLTImagePickerApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FLTImagePickerApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FLTImagePickerApiCodecReaderWriter *readerWriter = + [[FLTImagePickerApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FLTImagePickerApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickImage" + binaryMessenger:binaryMessenger + codec:FLTImagePickerApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(pickImageWithSource:maxSize:quality:completion:)], + @"FLTImagePickerApi api (%@) doesn't respond to " + @"@selector(pickImageWithSource:maxSize:quality:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FLTSourceSpecification *arg_source = GetNullableObjectAtIndex(args, 0); + FLTMaxSize *arg_maxSize = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_imageQuality = GetNullableObjectAtIndex(args, 2); + [api pickImageWithSource:arg_source + maxSize:arg_maxSize + quality:arg_imageQuality + completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickMultiImage" + binaryMessenger:binaryMessenger + codec:FLTImagePickerApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(pickMultiImageWithMaxSize:quality:completion:)], + @"FLTImagePickerApi api (%@) doesn't respond to " + @"@selector(pickMultiImageWithMaxSize:quality:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FLTMaxSize *arg_maxSize = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_imageQuality = GetNullableObjectAtIndex(args, 1); + [api pickMultiImageWithMaxSize:arg_maxSize + quality:arg_imageQuality + completion:^(NSArray *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.ImagePickerApi.pickVideo" + binaryMessenger:binaryMessenger + codec:FLTImagePickerApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(pickVideoWithSource:maxDuration:completion:)], + @"FLTImagePickerApi api (%@) doesn't respond to " + @"@selector(pickVideoWithSource:maxDuration:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FLTSourceSpecification *arg_source = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_maxDurationSeconds = GetNullableObjectAtIndex(args, 1); + [api pickVideoWithSource:arg_source + maxDuration:arg_maxDurationSeconds + completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} diff --git a/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart b/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart new file mode 100644 index 000000000000..3d1413cf0cce --- /dev/null +++ b/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart @@ -0,0 +1,209 @@ +// 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:async'; + +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; + +import 'src/messages.g.dart'; + +// Converts an [ImageSource] to the corresponding Pigeon API enum value. +SourceType _convertSource(ImageSource source) { + switch (source) { + case ImageSource.camera: + return SourceType.camera; + case ImageSource.gallery: + return SourceType.gallery; + default: + throw UnimplementedError('Unknown source: $source'); + } +} + +// Converts a [CameraDevice] to the corresponding Pigeon API enum value. +SourceCamera _convertCamera(CameraDevice camera) { + switch (camera) { + case CameraDevice.front: + return SourceCamera.front; + case CameraDevice.rear: + return SourceCamera.rear; + default: + throw UnimplementedError('Unknown camera: $camera'); + } +} + +/// An implementation of [ImagePickerPlatform] for iOS. +class ImagePickerIOS extends ImagePickerPlatform { + final ImagePickerApi _hostApi = ImagePickerApi(); + + /// Registers this class as the default platform implementation. + static void registerWith() { + ImagePickerPlatform.instance = ImagePickerIOS(); + } + + @override + Future pickImage({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) async { + final String? path = await _pickImageAsPath( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? PickedFile(path) : null; + } + + @override + Future?> pickMultiImage({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + }) async { + final List? paths = await _pickMultiImageAsPath( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + ); + if (paths == null) { + return null; + } + + return paths.map((dynamic path) => PickedFile(path as String)).toList(); + } + + Future?> _pickMultiImageAsPath({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + }) async { + if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { + throw ArgumentError.value( + imageQuality, 'imageQuality', 'must be between 0 and 100'); + } + + if (maxWidth != null && maxWidth < 0) { + throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative'); + } + + if (maxHeight != null && maxHeight < 0) { + throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative'); + } + + // TODO(stuartmorgan): Remove the cast once Pigeon supports non-nullable + // generics, https://github.com/flutter/flutter/issues/97848 + return (await _hostApi.pickMultiImage( + MaxSize(width: maxWidth, height: maxHeight), imageQuality)) + ?.cast(); + } + + Future _pickImageAsPath({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) { + if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { + throw ArgumentError.value( + imageQuality, 'imageQuality', 'must be between 0 and 100'); + } + + if (maxWidth != null && maxWidth < 0) { + throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative'); + } + + if (maxHeight != null && maxHeight < 0) { + throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative'); + } + + return _hostApi.pickImage( + SourceSpecification( + type: _convertSource(source), + camera: _convertCamera(preferredCameraDevice)), + MaxSize(width: maxWidth, height: maxHeight), + imageQuality, + ); + } + + @override + Future pickVideo({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) async { + final String? path = await _pickVideoAsPath( + source: source, + maxDuration: maxDuration, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? PickedFile(path) : null; + } + + Future _pickVideoAsPath({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) { + return _hostApi.pickVideo( + SourceSpecification( + type: _convertSource(source), + camera: _convertCamera(preferredCameraDevice)), + maxDuration?.inSeconds); + } + + @override + Future getImage({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) async { + final String? path = await _pickImageAsPath( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? XFile(path) : null; + } + + @override + Future?> getMultiImage({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + }) async { + final List? paths = await _pickMultiImageAsPath( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + ); + if (paths == null) { + return null; + } + + return paths.map((String path) => XFile(path)).toList(); + } + + @override + Future getVideo({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) async { + final String? path = await _pickVideoAsPath( + source: source, + maxDuration: maxDuration, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? XFile(path) : null; + } +} diff --git a/packages/image_picker/image_picker_ios/lib/src/messages.g.dart b/packages/image_picker/image_picker_ios/lib/src/messages.g.dart new file mode 100644 index 000000000000..0c5859e80ac9 --- /dev/null +++ b/packages/image_picker/image_picker_ios/lib/src/messages.g.dart @@ -0,0 +1,194 @@ +// 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 (v3.0.2), 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 +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; + +enum SourceCamera { + rear, + front, +} + +enum SourceType { + camera, + gallery, +} + +class MaxSize { + MaxSize({ + this.width, + this.height, + }); + + double? width; + double? height; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['width'] = width; + pigeonMap['height'] = height; + return pigeonMap; + } + + static MaxSize decode(Object message) { + final Map pigeonMap = message as Map; + return MaxSize( + width: pigeonMap['width'] as double?, + height: pigeonMap['height'] as double?, + ); + } +} + +class SourceSpecification { + SourceSpecification({ + required this.type, + this.camera, + }); + + SourceType type; + SourceCamera? camera; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['type'] = type.index; + pigeonMap['camera'] = camera?.index; + return pigeonMap; + } + + static SourceSpecification decode(Object message) { + final Map pigeonMap = message as Map; + return SourceSpecification( + type: SourceType.values[pigeonMap['type']! as int], + camera: pigeonMap['camera'] != null + ? SourceCamera.values[pigeonMap['camera']! as int] + : null, + ); + } +} + +class _ImagePickerApiCodec extends StandardMessageCodec { + const _ImagePickerApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is MaxSize) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is SourceSpecification) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return MaxSize.decode(readValue(buffer)!); + + case 129: + return SourceSpecification.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +class ImagePickerApi { + /// Constructor for [ImagePickerApi]. 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. + ImagePickerApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _ImagePickerApiCodec(); + + Future pickImage(SourceSpecification arg_source, MaxSize arg_maxSize, + int? arg_imageQuality) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.pickImage', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_source, arg_maxSize, arg_imageQuality]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as String?); + } + } + + Future?> pickMultiImage( + MaxSize arg_maxSize, int? arg_imageQuality) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.pickMultiImage', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_maxSize, arg_imageQuality]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as List?)?.cast(); + } + } + + Future pickVideo( + SourceSpecification arg_source, int? arg_maxDurationSeconds) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.pickVideo', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_source, arg_maxDurationSeconds]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as String?); + } + } +} diff --git a/packages/image_picker/image_picker_ios/pigeons/copyright.txt b/packages/image_picker/image_picker_ios/pigeons/copyright.txt new file mode 100644 index 000000000000..1236b63caf3a --- /dev/null +++ b/packages/image_picker/image_picker_ios/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. diff --git a/packages/image_picker/image_picker_ios/pigeons/messages.dart b/packages/image_picker/image_picker_ios/pigeons/messages.dart new file mode 100644 index 000000000000..94ac034606e9 --- /dev/null +++ b/packages/image_picker/image_picker_ios/pigeons/messages.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:pigeon/pigeon.dart'; + +@ConfigurePigeon(PigeonOptions( + dartOut: 'lib/src/messages.g.dart', + dartTestOut: 'test/test_api.dart', + objcHeaderOut: 'ios/Classes/messages.g.h', + objcSourceOut: 'ios/Classes/messages.g.m', + objcOptions: ObjcOptions( + prefix: 'FLT', + ), + copyrightHeader: 'pigeons/copyright.txt', +)) +class MaxSize { + MaxSize(this.width, this.height); + double? width; + double? height; +} + +// Corresponds to `CameraDevice` from the platform interface package. +enum SourceCamera { rear, front } + +// Corresponds to `ImageSource` from the platform interface package. +enum SourceType { camera, gallery } + +class SourceSpecification { + SourceSpecification(this.type, this.camera); + SourceType type; + SourceCamera? camera; +} + +@HostApi(dartHostTestHandler: 'TestHostImagePickerApi') +abstract class ImagePickerApi { + @async + @ObjCSelector('pickImageWithSource:maxSize:quality:') + String? pickImage( + SourceSpecification source, MaxSize maxSize, int? imageQuality); + @async + @ObjCSelector('pickMultiImageWithMaxSize:quality:') + List? pickMultiImage(MaxSize maxSize, int? imageQuality); + @async + @ObjCSelector('pickVideoWithSource:maxDuration:') + String? pickVideo(SourceSpecification source, int? maxDurationSeconds); +} diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index 2587c9a0d15b..a9cd052be56a 100755 --- a/packages/image_picker/image_picker_ios/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -2,17 +2,18 @@ name: image_picker_ios description: iOS implementation of the video_picker plugin. repository: https://github.com/flutter/plugins/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.4+11 +version: 0.8.5 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: implements: image_picker platforms: ios: + dartPluginClass: ImagePickerIOS pluginClass: FLTImagePickerPlugin dependencies: @@ -24,3 +25,4 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.0.0 + pigeon: ^3.0.2 diff --git a/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart b/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart new file mode 100644 index 000000000000..09517f1ef96b --- /dev/null +++ b/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart @@ -0,0 +1,937 @@ +// 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_test/flutter_test.dart'; +import 'package:image_picker_ios/image_picker_ios.dart'; +import 'package:image_picker_ios/src/messages.g.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; + +import 'test_api.dart'; + +@immutable +class _LoggedMethodCall { + const _LoggedMethodCall(this.name, {required this.arguments}); + final String name; + final Map arguments; + + @override + bool operator ==(Object other) { + return other is _LoggedMethodCall && + name == other.name && + mapEquals(arguments, other.arguments); + } + + @override + int get hashCode => Object.hash(name, arguments); + + @override + String toString() { + return 'MethodCall: $name $arguments'; + } +} + +class _ApiLogger implements TestHostImagePickerApi { + // The value to return from future calls. + dynamic returnValue = ''; + final List<_LoggedMethodCall> calls = <_LoggedMethodCall>[]; + + @override + Future pickImage( + SourceSpecification source, MaxSize maxSize, int? imageQuality) async { + // Flatten arguments for easy comparison. + calls.add(_LoggedMethodCall('pickImage', arguments: { + 'source': source.type, + 'cameraDevice': source.camera, + 'maxWidth': maxSize.width, + 'maxHeight': maxSize.height, + 'imageQuality': imageQuality, + })); + return returnValue as String?; + } + + @override + Future?> pickMultiImage( + MaxSize maxSize, int? imageQuality) async { + calls.add(_LoggedMethodCall('pickMultiImage', arguments: { + 'maxWidth': maxSize.width, + 'maxHeight': maxSize.height, + 'imageQuality': imageQuality, + })); + return returnValue as List?; + } + + @override + Future pickVideo( + SourceSpecification source, int? maxDurationSeconds) async { + calls.add(_LoggedMethodCall('pickVideo', arguments: { + 'source': source.type, + 'cameraDevice': source.camera, + 'maxDuration': maxDurationSeconds, + })); + return returnValue as String?; + } +} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + final ImagePickerIOS picker = ImagePickerIOS(); + late _ApiLogger log; + + setUp(() { + log = _ApiLogger(); + TestHostImagePickerApi.setup(log); + }); + + test('registration', () async { + ImagePickerIOS.registerWith(); + expect(ImagePickerPlatform.instance, isA()); + }); + + group('#pickImage', () { + test('passes the image source argument correctly', () async { + await picker.pickImage(source: ImageSource.camera); + await picker.pickImage(source: ImageSource.gallery); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.gallery, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + await picker.pickImage(source: ImageSource.camera); + await picker.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + ); + await picker.pickImage( + source: ImageSource.camera, + maxHeight: 10.0, + ); + await picker.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + ); + await picker.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + imageQuality: 70, + ); + await picker.pickImage( + source: ImageSource.camera, + maxHeight: 10.0, + imageQuality: 70, + ); + await picker.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + 'cameraDevice': SourceCamera.rear + }), + ], + ); + }); + + test('does not accept a invalid imageQuality argument', () { + expect( + () => picker.pickImage(imageQuality: -1, source: ImageSource.gallery), + throwsArgumentError, + ); + + expect( + () => picker.pickImage(imageQuality: 101, source: ImageSource.gallery), + throwsArgumentError, + ); + + expect( + () => picker.pickImage(imageQuality: -1, source: ImageSource.camera), + throwsArgumentError, + ); + + expect( + () => picker.pickImage(imageQuality: 101, source: ImageSource.camera), + throwsArgumentError, + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.pickImage(source: ImageSource.camera, maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + () => picker.pickImage(source: ImageSource.camera, maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + log.returnValue = null; + + expect(await picker.pickImage(source: ImageSource.gallery), isNull); + expect(await picker.pickImage(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.pickImage(source: ImageSource.camera); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.pickImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.front, + }), + ], + ); + }); + }); + + group('#pickMultiImage', () { + test('calls the method correctly', () async { + log.returnValue = ['0', '1']; + await picker.pickMultiImage(); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + log.returnValue = ['0', '1']; + await picker.pickMultiImage(); + await picker.pickMultiImage( + maxWidth: 10.0, + ); + await picker.pickMultiImage( + maxHeight: 10.0, + ); + await picker.pickMultiImage( + maxWidth: 10.0, + maxHeight: 20.0, + ); + await picker.pickMultiImage( + maxWidth: 10.0, + imageQuality: 70, + ); + await picker.pickMultiImage( + maxHeight: 10.0, + imageQuality: 70, + ); + await picker.pickMultiImage( + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + }), + ], + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.pickMultiImage(maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + () => picker.pickMultiImage(maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('does not accept a invalid imageQuality argument', () { + expect( + () => picker.pickMultiImage(imageQuality: -1), + throwsArgumentError, + ); + + expect( + () => picker.pickMultiImage(imageQuality: 101), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + log.returnValue = null; + + expect(await picker.pickMultiImage(), isNull); + }); + }); + + group('#pickVideo', () { + test('passes the image source argument correctly', () async { + await picker.pickVideo(source: ImageSource.camera); + await picker.pickVideo(source: ImageSource.gallery); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'cameraDevice': SourceCamera.rear, + 'maxDuration': null, + }), + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.gallery, + 'cameraDevice': SourceCamera.rear, + 'maxDuration': null, + }), + ], + ); + }); + + test('passes the duration argument correctly', () async { + await picker.pickVideo(source: ImageSource.camera); + await picker.pickVideo( + source: ImageSource.camera, + maxDuration: const Duration(seconds: 10), + ); + await picker.pickVideo( + source: ImageSource.camera, + maxDuration: const Duration(minutes: 1), + ); + await picker.pickVideo( + source: ImageSource.camera, + maxDuration: const Duration(hours: 1), + ); + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': null, + 'cameraDevice': SourceCamera.rear, + }), + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': 10, + 'cameraDevice': SourceCamera.rear, + }), + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': 60, + 'cameraDevice': SourceCamera.rear, + }), + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': 3600, + 'cameraDevice': SourceCamera.rear, + }), + ], + ); + }); + + test('handles a null video path response gracefully', () async { + log.returnValue = null; + + expect(await picker.pickVideo(source: ImageSource.gallery), isNull); + expect(await picker.pickVideo(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.pickVideo(source: ImageSource.camera); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'cameraDevice': SourceCamera.rear, + 'maxDuration': null, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.pickVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front, + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': null, + 'cameraDevice': SourceCamera.front, + }), + ], + ); + }); + }); + + group('#getImage', () { + test('passes the image source argument correctly', () async { + await picker.getImage(source: ImageSource.camera); + await picker.getImage(source: ImageSource.gallery); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.gallery, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + await picker.getImage(source: ImageSource.camera); + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + ); + await picker.getImage( + source: ImageSource.camera, + maxHeight: 10.0, + ); + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + ); + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + imageQuality: 70, + ); + await picker.getImage( + source: ImageSource.camera, + maxHeight: 10.0, + imageQuality: 70, + ); + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + 'cameraDevice': SourceCamera.rear + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + 'cameraDevice': SourceCamera.rear + }), + ], + ); + }); + + test('does not accept a invalid imageQuality argument', () { + expect( + () => picker.getImage(imageQuality: -1, source: ImageSource.gallery), + throwsArgumentError, + ); + + expect( + () => picker.getImage(imageQuality: 101, source: ImageSource.gallery), + throwsArgumentError, + ); + + expect( + () => picker.getImage(imageQuality: -1, source: ImageSource.camera), + throwsArgumentError, + ); + + expect( + () => picker.getImage(imageQuality: 101, source: ImageSource.camera), + throwsArgumentError, + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.getImage(source: ImageSource.camera, maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + () => picker.getImage(source: ImageSource.camera, maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + log.returnValue = null; + + expect(await picker.getImage(source: ImageSource.gallery), isNull); + expect(await picker.getImage(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.getImage(source: ImageSource.camera); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.getImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.front, + }), + ], + ); + }); + }); + + group('#getMultiImage', () { + test('calls the method correctly', () async { + log.returnValue = ['0', '1']; + await picker.getMultiImage(); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + log.returnValue = ['0', '1']; + await picker.getMultiImage(); + await picker.getMultiImage( + maxWidth: 10.0, + ); + await picker.getMultiImage( + maxHeight: 10.0, + ); + await picker.getMultiImage( + maxWidth: 10.0, + maxHeight: 20.0, + ); + await picker.getMultiImage( + maxWidth: 10.0, + imageQuality: 70, + ); + await picker.getMultiImage( + maxHeight: 10.0, + imageQuality: 70, + ); + await picker.getMultiImage( + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + }), + ], + ); + }); + + test('does not accept a negative width or height argument', () { + log.returnValue = ['0', '1']; + expect( + () => picker.getMultiImage(maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + () => picker.getMultiImage(maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('does not accept a invalid imageQuality argument', () { + log.returnValue = ['0', '1']; + expect( + () => picker.getMultiImage(imageQuality: -1), + throwsArgumentError, + ); + + expect( + () => picker.getMultiImage(imageQuality: 101), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + log.returnValue = null; + + expect(await picker.getMultiImage(), isNull); + expect(await picker.getMultiImage(), isNull); + }); + }); + + group('#getVideo', () { + test('passes the image source argument correctly', () async { + await picker.getVideo(source: ImageSource.camera); + await picker.getVideo(source: ImageSource.gallery); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'cameraDevice': SourceCamera.rear, + 'maxDuration': null, + }), + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.gallery, + 'cameraDevice': SourceCamera.rear, + 'maxDuration': null, + }), + ], + ); + }); + + test('passes the duration argument correctly', () async { + await picker.getVideo(source: ImageSource.camera); + await picker.getVideo( + source: ImageSource.camera, + maxDuration: const Duration(seconds: 10), + ); + await picker.getVideo( + source: ImageSource.camera, + maxDuration: const Duration(minutes: 1), + ); + await picker.getVideo( + source: ImageSource.camera, + maxDuration: const Duration(hours: 1), + ); + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': null, + 'cameraDevice': SourceCamera.rear, + }), + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': 10, + 'cameraDevice': SourceCamera.rear, + }), + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': 60, + 'cameraDevice': SourceCamera.rear, + }), + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': 3600, + 'cameraDevice': SourceCamera.rear, + }), + ], + ); + }); + + test('handles a null video path response gracefully', () async { + log.returnValue = null; + + expect(await picker.getVideo(source: ImageSource.gallery), isNull); + expect(await picker.getVideo(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.getVideo(source: ImageSource.camera); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'cameraDevice': SourceCamera.rear, + 'maxDuration': null, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.getVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front, + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickVideo', arguments: { + 'source': SourceType.camera, + 'maxDuration': null, + 'cameraDevice': SourceCamera.front, + }), + ], + ); + }); + }); +} diff --git a/packages/image_picker/image_picker_ios/test/test_api.dart b/packages/image_picker/image_picker_ios/test/test_api.dart new file mode 100644 index 000000000000..1f76e871521d --- /dev/null +++ b/packages/image_picker/image_picker_ios/test/test_api.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. +// Autogenerated from Pigeon (v3.0.2), 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 +// ignore_for_file: avoid_relative_lib_imports +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +// Manually changed due to https://github.com/flutter/flutter/issues/97744 +import 'package:image_picker_ios/src/messages.g.dart'; + +class _TestHostImagePickerApiCodec extends StandardMessageCodec { + const _TestHostImagePickerApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is MaxSize) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is SourceSpecification) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return MaxSize.decode(readValue(buffer)!); + + case 129: + return SourceSpecification.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestHostImagePickerApi { + static const MessageCodec codec = _TestHostImagePickerApiCodec(); + + Future pickImage( + SourceSpecification source, MaxSize maxSize, int? imageQuality); + Future?> pickMultiImage(MaxSize maxSize, int? imageQuality); + Future pickVideo( + SourceSpecification source, int? maxDurationSeconds); + static void setup(TestHostImagePickerApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.pickImage', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null.'); + final List args = (message as List?)!; + final SourceSpecification? arg_source = + (args[0] as SourceSpecification?); + assert(arg_source != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null SourceSpecification.'); + final MaxSize? arg_maxSize = (args[1] as MaxSize?); + assert(arg_maxSize != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null MaxSize.'); + final int? arg_imageQuality = (args[2] as int?); + final String? output = + await api.pickImage(arg_source!, arg_maxSize!, arg_imageQuality); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.pickMultiImage', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMultiImage was null.'); + final List args = (message as List?)!; + final MaxSize? arg_maxSize = (args[0] as MaxSize?); + assert(arg_maxSize != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMultiImage was null, expected non-null MaxSize.'); + final int? arg_imageQuality = (args[1] as int?); + final List? output = + await api.pickMultiImage(arg_maxSize!, arg_imageQuality); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ImagePickerApi.pickVideo', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickVideo was null.'); + final List args = (message as List?)!; + final SourceSpecification? arg_source = + (args[0] as SourceSpecification?); + assert(arg_source != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickVideo was null, expected non-null SourceSpecification.'); + final int? arg_maxDurationSeconds = (args[1] as int?); + final String? output = + await api.pickVideo(arg_source!, arg_maxDurationSeconds); + return {'result': output}; + }); + } + } + } +} From 4ff0157340ea1874ca753bf921807e62c1943099 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Mon, 25 Apr 2022 14:00:24 +0200 Subject: [PATCH 192/844] [local_auth] Fix deviceSupportsBiometrics returning false on iOS when present biometric hardware is not enrolled. (#5329) * Fix deviceSupportsBiometrics returning false when hardware is available but not enrolled. * Fix label * Format * Add unit tests * Fix changelog * Revert additions to project.pbxproj * Implement PR feedback --- .../local_auth/local_auth_ios/CHANGELOG.md | 5 + .../ios/Runner.xcodeproj/project.pbxproj | 2 +- .../ios/RunnerTests/FLTLocalAuthPluginTests.m | 205 ++++++++++++++++++ .../local_auth_ios/example/lib/main.dart | 26 +-- .../ios/Classes/FLTLocalAuthPlugin.m | 39 +++- .../local_auth_ios/lib/local_auth_ios.dart | 7 +- .../local_auth/local_auth_ios/pubspec.yaml | 2 +- .../local_auth_ios/test/local_auth_test.dart | 8 +- 8 files changed, 265 insertions(+), 29 deletions(-) diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index 466aeb50ed6c..0fc716e19f25 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.0.4 + +* Fixes `deviceSupportsBiometrics` to return true when biometric hardware + is available but not enrolled. + ## 1.0.3 * Adopts `Object.hash`. diff --git a/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.pbxproj index f8b4056d135d..cbf16eef4060 100644 --- a/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/local_auth/local_auth_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ diff --git a/packages/local_auth/local_auth_ios/example/ios/RunnerTests/FLTLocalAuthPluginTests.m b/packages/local_auth/local_auth_ios/example/ios/RunnerTests/FLTLocalAuthPluginTests.m index 744d9124f7b1..50dbb1a6907b 100644 --- a/packages/local_auth/local_auth_ios/example/ios/RunnerTests/FLTLocalAuthPluginTests.m +++ b/packages/local_auth/local_auth_ios/example/ios/RunnerTests/FLTLocalAuthPluginTests.m @@ -269,4 +269,209 @@ - (void)testSkippedLocalizedFallbackTitle { [self waitForExpectationsWithTimeout:kTimeout handler:nil]; } +- (void)testDeviceSupportsBiometrics_withEnrolledHardware { + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; + id mockAuthContext = OCMClassMock([LAContext class]); + plugin.authContextOverrides = @[ mockAuthContext ]; + + const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; + OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES); + + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"deviceSupportsBiometrics" + arguments:@{}]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; + [plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertTrue([result isKindOfClass:[NSNumber class]]); + XCTAssertTrue([result boolValue]); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeout handler:nil]; +} + +- (void)testDeviceSupportsBiometrics_withNonEnrolledHardware_iOS11 { + if (@available(iOS 11, *)) { + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; + id mockAuthContext = OCMClassMock([LAContext class]); + plugin.authContextOverrides = @[ mockAuthContext ]; + + const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; + void (^canEvaluatePolicyHandler)(NSInvocation *) = ^(NSInvocation *invocation) { + // Write error + NSError *__autoreleasing *authError; + [invocation getArgument:&authError atIndex:3]; + *authError = [NSError errorWithDomain:@"error" code:LAErrorBiometryNotEnrolled userInfo:nil]; + // Write return value + BOOL returnValue = NO; + NSValue *nsReturnValue = [NSValue valueWithBytes:&returnValue objCType:@encode(BOOL)]; + [invocation setReturnValue:&nsReturnValue]; + }; + OCMStub([mockAuthContext canEvaluatePolicy:policy + error:(NSError * __autoreleasing *)[OCMArg anyPointer]]) + .andDo(canEvaluatePolicyHandler); + + FlutterMethodCall *call = + [FlutterMethodCall methodCallWithMethodName:@"deviceSupportsBiometrics" arguments:@{}]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; + [plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertTrue([result isKindOfClass:[NSNumber class]]); + XCTAssertTrue([result boolValue]); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeout handler:nil]; + } +} + +- (void)testDeviceSupportsBiometrics_withNoBiometricHardware { + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; + id mockAuthContext = OCMClassMock([LAContext class]); + plugin.authContextOverrides = @[ mockAuthContext ]; + + const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; + void (^canEvaluatePolicyHandler)(NSInvocation *) = ^(NSInvocation *invocation) { + // Write error + NSError *__autoreleasing *authError; + [invocation getArgument:&authError atIndex:3]; + *authError = [NSError errorWithDomain:@"error" code:0 userInfo:nil]; + // Write return value + BOOL returnValue = NO; + NSValue *nsReturnValue = [NSValue valueWithBytes:&returnValue objCType:@encode(BOOL)]; + [invocation setReturnValue:&nsReturnValue]; + }; + OCMStub([mockAuthContext canEvaluatePolicy:policy + error:(NSError * __autoreleasing *)[OCMArg anyPointer]]) + .andDo(canEvaluatePolicyHandler); + + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"deviceSupportsBiometrics" + arguments:@{}]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; + [plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertTrue([result isKindOfClass:[NSNumber class]]); + XCTAssertFalse([result boolValue]); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeout handler:nil]; +} + +- (void)testGetEnrolledBiometrics_withFaceID_iOS11 { + if (@available(iOS 11, *)) { + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; + id mockAuthContext = OCMClassMock([LAContext class]); + plugin.authContextOverrides = @[ mockAuthContext ]; + + const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; + OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES); + OCMStub([mockAuthContext biometryType]).andReturn(LABiometryTypeFaceID); + + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"getEnrolledBiometrics" + arguments:@{}]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; + [plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertTrue([result isKindOfClass:[NSArray class]]); + XCTAssertEqual([result count], 1); + XCTAssertEqualObjects(result[0], @"face"); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeout handler:nil]; + } +} + +- (void)testGetEnrolledBiometrics_withTouchID_iOS11 { + if (@available(iOS 11, *)) { + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; + id mockAuthContext = OCMClassMock([LAContext class]); + plugin.authContextOverrides = @[ mockAuthContext ]; + + const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; + OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES); + OCMStub([mockAuthContext biometryType]).andReturn(LABiometryTypeTouchID); + + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"getEnrolledBiometrics" + arguments:@{}]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; + [plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertTrue([result isKindOfClass:[NSArray class]]); + XCTAssertEqual([result count], 1); + XCTAssertEqualObjects(result[0], @"fingerprint"); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeout handler:nil]; + } +} + +- (void)testGetEnrolledBiometrics_withTouchID_preIOS11 { + if (@available(iOS 11, *)) { + return; + } + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; + id mockAuthContext = OCMClassMock([LAContext class]); + plugin.authContextOverrides = @[ mockAuthContext ]; + + const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; + OCMStub([mockAuthContext canEvaluatePolicy:policy error:[OCMArg setTo:nil]]).andReturn(YES); + + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"getEnrolledBiometrics" + arguments:@{}]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; + [plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertTrue([result isKindOfClass:[NSArray class]]); + XCTAssertEqual([result count], 1); + XCTAssertEqualObjects(result[0], @"fingerprint"); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeout handler:nil]; +} + +- (void)testGetEnrolledBiometrics_withoutEnrolledHardware_iOS11 { + if (@available(iOS 11, *)) { + FLTLocalAuthPlugin *plugin = [[FLTLocalAuthPlugin alloc] init]; + id mockAuthContext = OCMClassMock([LAContext class]); + plugin.authContextOverrides = @[ mockAuthContext ]; + + const LAPolicy policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; + void (^canEvaluatePolicyHandler)(NSInvocation *) = ^(NSInvocation *invocation) { + // Write error + NSError *__autoreleasing *authError; + [invocation getArgument:&authError atIndex:3]; + *authError = [NSError errorWithDomain:@"error" code:LAErrorBiometryNotEnrolled userInfo:nil]; + // Write return value + BOOL returnValue = NO; + NSValue *nsReturnValue = [NSValue valueWithBytes:&returnValue objCType:@encode(BOOL)]; + [invocation setReturnValue:&nsReturnValue]; + }; + OCMStub([mockAuthContext canEvaluatePolicy:policy + error:(NSError * __autoreleasing *)[OCMArg anyPointer]]) + .andDo(canEvaluatePolicyHandler); + + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"getEnrolledBiometrics" + arguments:@{}]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result is called"]; + [plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertTrue([result isKindOfClass:[NSArray class]]); + XCTAssertEqual([result count], 0); + [expectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:kTimeout handler:nil]; + } +} @end diff --git a/packages/local_auth/local_auth_ios/example/lib/main.dart b/packages/local_auth/local_auth_ios/example/lib/main.dart index 9346be10fd78..c966d67ec9c6 100644 --- a/packages/local_auth/local_auth_ios/example/lib/main.dart +++ b/packages/local_auth/local_auth_ios/example/lib/main.dart @@ -25,7 +25,7 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { _SupportState _supportState = _SupportState.unknown; bool? _canCheckBiometrics; - List? _availableBiometrics; + List? _enrolledBiometrics; String _authorized = 'Not Authorized'; bool _isAuthenticating = false; @@ -40,12 +40,12 @@ class _MyAppState extends State { } Future _checkBiometrics() async { - late bool canCheckBiometrics; + late bool deviceSupportsBiometrics; try { - canCheckBiometrics = - (await LocalAuthPlatform.instance.getEnrolledBiometrics()).isNotEmpty; + deviceSupportsBiometrics = + await LocalAuthPlatform.instance.deviceSupportsBiometrics(); } on PlatformException catch (e) { - canCheckBiometrics = false; + deviceSupportsBiometrics = false; print(e); } if (!mounted) { @@ -53,17 +53,17 @@ class _MyAppState extends State { } setState(() { - _canCheckBiometrics = canCheckBiometrics; + _canCheckBiometrics = deviceSupportsBiometrics; }); } Future _getEnrolledBiometrics() async { - late List availableBiometrics; + late List enrolledBiometrics; try { - availableBiometrics = + enrolledBiometrics = await LocalAuthPlatform.instance.getEnrolledBiometrics(); } on PlatformException catch (e) { - availableBiometrics = []; + enrolledBiometrics = []; print(e); } if (!mounted) { @@ -71,7 +71,7 @@ class _MyAppState extends State { } setState(() { - _availableBiometrics = availableBiometrics; + _enrolledBiometrics = enrolledBiometrics; }); } @@ -173,15 +173,15 @@ class _MyAppState extends State { else const Text('This device is not supported'), const Divider(height: 100), - Text('Can check biometrics: $_canCheckBiometrics\n'), + Text('Device supports biometrics: $_canCheckBiometrics\n'), ElevatedButton( child: const Text('Check biometrics'), onPressed: _checkBiometrics, ), const Divider(height: 100), - Text('Available biometrics: $_availableBiometrics\n'), + Text('Enrolled biometrics: $_enrolledBiometrics\n'), ElevatedButton( - child: const Text('Get available biometrics'), + child: const Text('Get enrolled biometrics'), onPressed: _getEnrolledBiometrics, ), const Divider(height: 100), diff --git a/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m index ffd1dd2a869d..eb7f637f7850 100644 --- a/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m +++ b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m @@ -35,8 +35,10 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result } else { [self authenticate:call.arguments withFlutterResult:result]; } - } else if ([@"getAvailableBiometrics" isEqualToString:call.method]) { - [self getAvailableBiometrics:result]; + } else if ([@"getEnrolledBiometrics" isEqualToString:call.method]) { + [self getEnrolledBiometrics:result]; + } else if ([@"deviceSupportsBiometrics" isEqualToString:call.method]) { + [self deviceSupportsBiometrics:result]; } else if ([@"isDeviceSupported" isEqualToString:call.method]) { result(@YES); } else { @@ -93,14 +95,41 @@ - (void)alertMessage:(NSString *)message completion:nil]; } -- (void)getAvailableBiometrics:(FlutterResult)result { +- (void)deviceSupportsBiometrics:(FlutterResult)result { + LAContext *context = self.createAuthContext; + NSError *authError = nil; + // Check if authentication with biometrics is possible. + if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics + error:&authError]) { + if (authError == nil) { + result(@YES); + return; + } + } + // If not, check if it is because no biometrics are enrolled (but still present). + if (authError != nil) { + if (@available(iOS 11, *)) { + if (authError.code == LAErrorBiometryNotEnrolled) { + result(@YES); + return; + } + } else if (authError.code == LAErrorTouchIDNotEnrolled) { + result(@YES); + return; + } + } + + result(@NO); +} + +- (void)getEnrolledBiometrics:(FlutterResult)result { LAContext *context = self.createAuthContext; NSError *authError = nil; NSMutableArray *biometrics = [[NSMutableArray alloc] init]; if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) { if (authError == nil) { - if (@available(iOS 11.0.1, *)) { + if (@available(iOS 11, *)) { if (context.biometryType == LABiometryTypeFaceID) { [biometrics addObject:@"face"]; } else if (context.biometryType == LABiometryTypeTouchID) { @@ -110,8 +139,6 @@ - (void)getAvailableBiometrics:(FlutterResult)result { [biometrics addObject:@"fingerprint"]; } } - } else if (authError.code == LAErrorTouchIDNotEnrolled) { - [biometrics addObject:@"undefined"]; } result(biometrics); } diff --git a/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart b/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart index d92d076d79fb..ae25bd50ef5e 100644 --- a/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart +++ b/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart @@ -49,13 +49,14 @@ class LocalAuthIOS extends LocalAuthPlatform { @override Future deviceSupportsBiometrics() async { - return (await getEnrolledBiometrics()).isNotEmpty; + return (await _channel.invokeMethod('deviceSupportsBiometrics')) ?? + false; } @override Future> getEnrolledBiometrics() async { final List result = (await _channel.invokeListMethod( - 'getAvailableBiometrics', + 'getEnrolledBiometrics', )) ?? []; final List biometrics = []; @@ -70,8 +71,6 @@ class LocalAuthIOS extends LocalAuthPlatform { case 'iris': biometrics.add(BiometricType.iris); break; - case 'undefined': - break; } } return biometrics; diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index 06d972a01522..77ab74d383c8 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.3 +version: 1.0.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_ios/test/local_auth_test.dart b/packages/local_auth/local_auth_ios/test/local_auth_test.dart index aa94b8aa48b8..192d69ca8ec4 100644 --- a/packages/local_auth/local_auth_ios/test/local_auth_test.dart +++ b/packages/local_auth/local_auth_ios/test/local_auth_test.dart @@ -24,7 +24,7 @@ void main() { channel.setMockMethodCallHandler((MethodCall methodCall) { log.add(methodCall); switch (methodCall.method) { - case 'getAvailableBiometrics': + case 'getEnrolledBiometrics': return Future>.value( ['face', 'fingerprint', 'iris', 'undefined']); default: @@ -35,13 +35,13 @@ void main() { log.clear(); }); - test('deviceSupportsBiometrics calls getEnrolledBiometrics', () async { + test('deviceSupportsBiometrics calls platform', () async { final bool result = await localAuthentication.deviceSupportsBiometrics(); expect( log, [ - isMethodCall('getAvailableBiometrics', arguments: null), + isMethodCall('deviceSupportsBiometrics', arguments: null), ], ); expect(result, true); @@ -54,7 +54,7 @@ void main() { expect( log, [ - isMethodCall('getAvailableBiometrics', arguments: null), + isMethodCall('getEnrolledBiometrics', arguments: null), ], ); expect(result, [ From e55659835427fdc2b3c838548ce7e49267f100fd Mon Sep 17 00:00:00 2001 From: Jesse Seales <103135467+sealesj@users.noreply.github.com> Date: Mon, 25 Apr 2022 22:04:07 -0400 Subject: [PATCH 193/844] Pin dockerfile version (#5377) --- .ci/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/Dockerfile b/.ci/Dockerfile index 73efa3103922..59d4064f0a91 100644 --- a/.ci/Dockerfile +++ b/.ci/Dockerfile @@ -1,7 +1,7 @@ # The Flutter version is not important here, since the CI scripts update Flutter # before running. What matters is that the base image is pinned to minimize # unintended changes when modifying this file. -FROM cirrusci/flutter:2.8.0 +FROM cirrusci/flutter@sha256:505fe8bce2896c75b4df9ccf500b1604155bf932af7465ffcc66fcae8612f82f RUN apt-get update -y From 19414fe0ff65ab34262029c1b41cd7cf106e709d Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 26 Apr 2022 16:39:09 -0400 Subject: [PATCH 194/844] [flutter_plugin_tool] Allow re-pathifying dependencies (#5376) --- script/tool/CHANGELOG.md | 5 ++ .../lib/src/make_deps_path_based_command.dart | 45 ++++++++++++------ .../make_deps_path_based_command_test.dart | 46 +++++++++++++++++++ 3 files changed, 81 insertions(+), 15 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 367f258fbeea..bfc0cafa88c3 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,8 @@ +## NEXT + +- Allows `make-deps-path-based` to skip packages it has alredy rewritten, so + that running multiple times won't fail after the first time. + ## 0.8.2+1 - Adds a new `readme-check` command. diff --git a/script/tool/lib/src/make_deps_path_based_command.dart b/script/tool/lib/src/make_deps_path_based_command.dart index c09060310e97..45a4427d3217 100644 --- a/script/tool/lib/src/make_deps_path_based_command.dart +++ b/script/tool/lib/src/make_deps_path_based_command.dart @@ -16,6 +16,8 @@ import 'common/plugin_command.dart'; const int _exitPackageNotFound = 3; const int _exitCannotUpdatePubspec = 4; +enum _RewriteOutcome { changed, noChangesNeeded, alreadyChanged } + /// Converts all dependencies on target packages to path-based dependencies. /// /// This is to allow for pre-publish testing of changes that could affect other @@ -48,6 +50,10 @@ class MakeDepsPathBasedCommand extends PluginCommand { static const String _targetDependenciesWithNonBreakingUpdatesArg = 'target-dependencies-with-non-breaking-updates'; + // The comment to add to temporary dependency overrides. + static const String _dependencyOverrideWarningComment = + '# FOR TESTING ONLY. DO NOT MERGE.'; + @override final String name = 'make-deps-path-based'; @@ -73,12 +79,19 @@ class MakeDepsPathBasedCommand extends PluginCommand { final String repoRootPath = (await gitDir).path; for (final File pubspec in await _getAllPubspecs()) { - if (await _addDependencyOverridesIfNecessary( - pubspec, localDependencyPackages)) { - // Print the relative path of the changed pubspec. - final String displayPath = p.posix.joinAll(path - .split(path.relative(pubspec.absolute.path, from: repoRootPath))); - print(' Modified $displayPath'); + final String displayPath = p.posix.joinAll( + path.split(path.relative(pubspec.absolute.path, from: repoRootPath))); + final _RewriteOutcome outcome = await _addDependencyOverridesIfNecessary( + pubspec, localDependencyPackages); + switch (outcome) { + case _RewriteOutcome.changed: + print(' Modified $displayPath'); + break; + case _RewriteOutcome.alreadyChanged: + print(' Skipped $displayPath - Already rewritten'); + break; + case _RewriteOutcome.noChangesNeeded: + break; } } } @@ -125,16 +138,18 @@ class MakeDepsPathBasedCommand extends PluginCommand { /// If [pubspecFile] has any dependencies on packages in [localDependencies], /// adds dependency_overrides entries to redirect them to the local version /// using path-based dependencies. - /// - /// Returns true if any changes were made. - Future _addDependencyOverridesIfNecessary(File pubspecFile, + Future<_RewriteOutcome> _addDependencyOverridesIfNecessary(File pubspecFile, Map localDependencies) async { final String pubspecContents = pubspecFile.readAsStringSync(); final Pubspec pubspec = Pubspec.parse(pubspecContents); - // Fail if there are any dependency overrides already. If support for that - // is needed at some point, it can be added, but currently it's not and - // relying on that makes the logic here much simpler. + // Fail if there are any dependency overrides already, other than ones + // created by this script. If support for that is needed at some point, it + // can be added, but currently it's not and relying on that makes the logic + // here much simpler. if (pubspec.dependencyOverrides.isNotEmpty) { + if (pubspecContents.contains(_dependencyOverrideWarningComment)) { + return _RewriteOutcome.alreadyChanged; + } printError( 'Plugins with dependency overrides are not currently supported.'); throw ToolExit(_exitCannotUpdatePubspec); @@ -158,7 +173,7 @@ class MakeDepsPathBasedCommand extends PluginCommand { String newPubspecContents = pubspecContents + ''' -# FOR TESTING ONLY. DO NOT MERGE. +$_dependencyOverrideWarningComment dependency_overrides: '''; for (final String packageName in packagesToOverride) { @@ -175,9 +190,9 @@ dependency_overrides: '''; } pubspecFile.writeAsStringSync(newPubspecContents); - return true; + return _RewriteOutcome.changed; } - return false; + return _RewriteOutcome.noChangesNeeded; } /// Returns all pubspecs anywhere under the packages directory. diff --git a/script/tool/test/make_deps_path_based_command_test.dart b/script/tool/test/make_deps_path_based_command_test.dart index 2021f24079e3..da241c3d83f7 100644 --- a/script/tool/test/make_deps_path_based_command_test.dart +++ b/script/tool/test/make_deps_path_based_command_test.dart @@ -144,6 +144,52 @@ void main() { ])); }); + // This test case ensures that running CI using this command on an interim + // PR that itself used this command won't fail on the rewrite step. + test('running a second time no-ops without failing', () async { + final RepositoryPackage simplePackage = RepositoryPackage( + createFakePackage('foo', packagesDir, isFlutter: true)); + final Directory pluginGroup = packagesDir.childDirectory('bar'); + + RepositoryPackage(createFakePackage('bar_platform_interface', pluginGroup, + isFlutter: true)); + final RepositoryPackage pluginImplementation = + RepositoryPackage(createFakePlugin('bar_android', pluginGroup)); + final RepositoryPackage pluginAppFacing = + RepositoryPackage(createFakePlugin('bar', pluginGroup)); + + _addDependencies(simplePackage, [ + 'bar', + 'bar_android', + 'bar_platform_interface', + ]); + _addDependencies(pluginAppFacing, [ + 'bar_platform_interface', + 'bar_android', + ]); + _addDependencies(pluginImplementation, [ + 'bar_platform_interface', + ]); + + await runCapturingPrint(runner, [ + 'make-deps-path-based', + '--target-dependencies=bar,bar_platform_interface' + ]); + final List output = await runCapturingPrint(runner, [ + 'make-deps-path-based', + '--target-dependencies=bar,bar_platform_interface' + ]); + + expect( + output, + containsAll([ + 'Rewriting references to: bar, bar_platform_interface...', + ' Skipped packages/bar/bar/pubspec.yaml - Already rewritten', + ' Skipped packages/bar/bar_android/pubspec.yaml - Already rewritten', + ' Skipped packages/foo/pubspec.yaml - Already rewritten', + ])); + }); + group('target-dependencies-with-non-breaking-updates', () { test('no-ops for no published changes', () async { final Directory package = createFakePackage('foo', packagesDir); From a59777fc77974724bac6a9c44d5677b2c56ef059 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 26 Apr 2022 17:24:11 -0400 Subject: [PATCH 195/844] [url_launcher] Add canLaunch fallback for web on Android (#5399) --- .../url_launcher_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 156 ++++++++++-------- .../lib/url_launcher_android.dart | 31 +++- .../url_launcher_android/pubspec.yaml | 2 +- .../test/url_launcher_android_test.dart | 100 ++++++++--- 5 files changed, 196 insertions(+), 98 deletions(-) diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md index 9ec1f65911c6..69b96156d849 100644 --- a/packages/url_launcher/url_launcher_android/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.0.16 + +* Adds fallback querying for `canLaunch` with web URLs, to avoid false negatives + when there is a custom scheme handler. + ## 6.0.15 * Switches to an in-package method channel implementation. diff --git a/packages/url_launcher/url_launcher_android/example/lib/main.dart b/packages/url_launcher/url_launcher_android/example/lib/main.dart index 8721c587075e..7abc73430e8b 100644 --- a/packages/url_launcher/url_launcher_android/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_android/example/lib/main.dart @@ -35,73 +35,74 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + bool _hasCallSupport = false; Future? _launched; String _phone = ''; + @override + void initState() { + super.initState(); + // Check for phone call support. + launcher.canLaunch('tel:123').then((bool result) { + setState(() { + _hasCallSupport = result; + }); + }); + } + Future _launchInBrowser(String url) async { - final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; - if (await launcher.canLaunch(url)) { - await launcher.launch( - url, - useSafariVC: false, - useWebView: false, - enableJavaScript: false, - enableDomStorage: false, - universalLinksOnly: false, - headers: {'my_header_key': 'my_header_value'}, - ); - } else { + if (!await launcher.launch( + url, + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: {}, + )) { throw 'Could not launch $url'; } } - Future _launchInWebViewOrVC(String url) async { - final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; - if (await launcher.canLaunch(url)) { - await launcher.launch( - url, - useSafariVC: true, - useWebView: true, - enableJavaScript: false, - enableDomStorage: false, - universalLinksOnly: false, - headers: {'my_header_key': 'my_header_value'}, - ); - } else { + Future _launchInWebView(String url) async { + if (!await launcher.launch( + url, + useSafariVC: true, + useWebView: true, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: {'my_header_key': 'my_header_value'}, + )) { throw 'Could not launch $url'; } } Future _launchInWebViewWithJavaScript(String url) async { - final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; - if (await launcher.canLaunch(url)) { - await launcher.launch( - url, - useSafariVC: true, - useWebView: true, - enableJavaScript: true, - enableDomStorage: false, - universalLinksOnly: false, - headers: {}, - ); - } else { + if (!await launcher.launch( + url, + useSafariVC: true, + useWebView: true, + enableJavaScript: true, + enableDomStorage: false, + universalLinksOnly: false, + headers: {}, + )) { throw 'Could not launch $url'; } } Future _launchInWebViewWithDomStorage(String url) async { - final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; - if (await launcher.canLaunch(url)) { - await launcher.launch( - url, - useSafariVC: true, - useWebView: true, - enableJavaScript: false, - enableDomStorage: true, - universalLinksOnly: false, - headers: {}, - ); - } else { + if (!await launcher.launch( + url, + useSafariVC: true, + useWebView: true, + enableJavaScript: false, + enableDomStorage: true, + universalLinksOnly: false, + headers: {}, + )) { throw 'Could not launch $url'; } } @@ -114,25 +115,30 @@ class _MyHomePageState extends State { } } - Future _makePhoneCall(String url) async { - final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; - if (await launcher.canLaunch(url)) { - await launcher.launch( - url, - useSafariVC: false, - useWebView: false, - enableJavaScript: false, - enableDomStorage: false, - universalLinksOnly: true, - headers: {}, - ); - } else { - throw 'Could not launch $url'; - } + Future _makePhoneCall(String phoneNumber) async { + // Use `Uri` to ensure that `phoneNumber` is properly URL-encoded. + // Just using 'tel:$phoneNumber' would create invalid URLs in some cases, + // such as spaces in the input, which would cause `launch` to fail on some + // platforms. + final Uri launchUri = Uri( + scheme: 'tel', + path: phoneNumber, + ); + await launcher.launch( + launchUri.toString(), + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: true, + headers: {}, + ); } @override Widget build(BuildContext context) { + // onPressed calls using this URL are not gated on a 'canLaunch' check + // because the assumption is that every device can launch a web URL. const String toLaunch = 'https://www.cylog.org/headers/'; return Scaffold( appBar: AppBar( @@ -151,10 +157,14 @@ class _MyHomePageState extends State { hintText: 'Input the phone number to launch')), ), ElevatedButton( - onPressed: () => setState(() { - _launched = _makePhoneCall('tel:$_phone'); - }), - child: const Text('Make phone call'), + onPressed: _hasCallSupport + ? () => setState(() { + _launched = _makePhoneCall(_phone); + }) + : null, + child: _hasCallSupport + ? const Text('Make phone call') + : const Text('Calling not supported'), ), const Padding( padding: EdgeInsets.all(16.0), @@ -169,7 +179,7 @@ class _MyHomePageState extends State { const Padding(padding: EdgeInsets.all(16.0)), ElevatedButton( onPressed: () => setState(() { - _launched = _launchInWebViewOrVC(toLaunch); + _launched = _launchInWebView(toLaunch); }), child: const Text('Launch in app'), ), @@ -177,21 +187,21 @@ class _MyHomePageState extends State { onPressed: () => setState(() { _launched = _launchInWebViewWithJavaScript(toLaunch); }), - child: const Text('Launch in app(JavaScript ON)'), + child: const Text('Launch in app (JavaScript ON)'), ), ElevatedButton( onPressed: () => setState(() { _launched = _launchInWebViewWithDomStorage(toLaunch); }), - child: const Text('Launch in app(DOM storage ON)'), + child: const Text('Launch in app (DOM storage ON)'), ), const Padding(padding: EdgeInsets.all(16.0)), ElevatedButton( onPressed: () => setState(() { - _launched = _launchInWebViewOrVC(toLaunch); + _launched = _launchInWebView(toLaunch); Timer(const Duration(seconds: 5), () { print('Closing WebView after 5 seconds...'); - UrlLauncherPlatform.instance.closeWebView(); + launcher.closeWebView(); }); }), child: const Text('Launch in app + close after 5 seconds'), diff --git a/packages/url_launcher/url_launcher_android/lib/url_launcher_android.dart b/packages/url_launcher/url_launcher_android/lib/url_launcher_android.dart index 52c46356489d..1aa093a36451 100644 --- a/packages/url_launcher/url_launcher_android/lib/url_launcher_android.dart +++ b/packages/url_launcher/url_launcher_android/lib/url_launcher_android.dart @@ -22,7 +22,24 @@ class UrlLauncherAndroid extends UrlLauncherPlatform { final LinkDelegate? linkDelegate = null; @override - Future canLaunch(String url) { + Future canLaunch(String url) async { + final bool canLaunchSpecificUrl = await _canLaunchUrl(url); + if (!canLaunchSpecificUrl) { + final String scheme = _getUrlScheme(url); + // canLaunch can return false when a custom application is registered to + // handle a web URL, but the caller doesn't have permission to see what + // that handler is. If that happens, try a web URL (with the same scheme + // variant, to be safe) that should not have a custom handler. If that + // returns true, then there is a browser, which means that there is + // at least one handler for the original URL. + if (scheme == 'http' || scheme == 'https') { + return await _canLaunchUrl('$scheme://flutter.dev'); + } + } + return canLaunchSpecificUrl; + } + + Future _canLaunchUrl(String url) { return _channel.invokeMethod( 'canLaunch', {'url': url}, @@ -57,4 +74,16 @@ class UrlLauncherAndroid extends UrlLauncherPlatform { }, ).then((bool? value) => value ?? false); } + + // Returns the part of [url] up to the first ':', or an empty string if there + // is no ':'. This deliberately does not use [Uri] to extract the scheme + // so that it works on strings that aren't actually valid URLs, since Android + // is very lenient about what it accepts for launching. + String _getUrlScheme(String url) { + final int schemeEnd = url.indexOf(':'); + if (schemeEnd == -1) { + return ''; + } + return url.substring(0, schemeEnd); + } } diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index b8706aeb13f2..3230dfeffd2e 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_android description: Android implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.15 +version: 6.0.16 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_android/test/url_launcher_android_test.dart b/packages/url_launcher/url_launcher_android/test/url_launcher_android_test.dart index 909d2c100ecf..eebd8cd4c059 100644 --- a/packages/url_launcher/url_launcher_android/test/url_launcher_android_test.dart +++ b/packages/url_launcher/url_launcher_android/test/url_launcher_android_test.dart @@ -10,10 +10,12 @@ import 'package:url_launcher_platform_interface/url_launcher_platform_interface. void main() { TestWidgetsFlutterBinding.ensureInitialized(); - group('$UrlLauncherAndroid', () { - const MethodChannel channel = - MethodChannel('plugins.flutter.io/url_launcher_android'); - final List log = []; + const MethodChannel channel = + MethodChannel('plugins.flutter.io/url_launcher_android'); + late List log; + + setUp(() { + log = []; channel.setMockMethodCallHandler((MethodCall methodCall) async { log.add(methodCall); @@ -21,19 +23,21 @@ void main() { // returned by the method channel if no return statement is specified. return null; }); + }); - tearDown(() { - log.clear(); - }); - - test('registers instance', () { - UrlLauncherAndroid.registerWith(); - expect(UrlLauncherPlatform.instance, isA()); - }); + test('registers instance', () { + UrlLauncherAndroid.registerWith(); + expect(UrlLauncherPlatform.instance, isA()); + }); - test('canLaunch', () async { + group('canLaunch', () { + test('calls through', () async { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return true; + }); final UrlLauncherAndroid launcher = UrlLauncherAndroid(); - await launcher.canLaunch('http://example.com/'); + final bool canLaunch = await launcher.canLaunch('http://example.com/'); expect( log, [ @@ -42,16 +46,64 @@ void main() { }) ], ); + expect(canLaunch, true); }); - test('canLaunch should return false if platform returns null', () async { + test('returns false if platform returns null', () async { final UrlLauncherAndroid launcher = UrlLauncherAndroid(); final bool canLaunch = await launcher.canLaunch('http://example.com/'); expect(canLaunch, false); }); - test('launch', () async { + test('checks a generic URL if an http URL returns false', () async { + const String specificUrl = 'http://example.com/'; + const String genericUrl = 'http://flutter.dev'; + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return methodCall.arguments['url'] != specificUrl; + }); + + final UrlLauncherAndroid launcher = UrlLauncherAndroid(); + final bool canLaunch = await launcher.canLaunch(specificUrl); + + expect(canLaunch, true); + expect(log.length, 2); + expect(log[1].arguments['url'], genericUrl); + }); + + test('checks a generic URL if an https URL returns false', () async { + const String specificUrl = 'https://example.com/'; + const String genericUrl = 'https://flutter.dev'; + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return methodCall.arguments['url'] != specificUrl; + }); + + final UrlLauncherAndroid launcher = UrlLauncherAndroid(); + final bool canLaunch = await launcher.canLaunch(specificUrl); + + expect(canLaunch, true); + expect(log.length, 2); + expect(log[1].arguments['url'], genericUrl); + }); + + test('does not a generic URL if a non-web URL returns false', () async { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return false; + }); + + final UrlLauncherAndroid launcher = UrlLauncherAndroid(); + final bool canLaunch = await launcher.canLaunch('sms:12345'); + + expect(canLaunch, false); + expect(log.length, 1); + }); + }); + + group('launch', () { + test('calls through', () async { final UrlLauncherAndroid launcher = UrlLauncherAndroid(); await launcher.launch( 'http://example.com/', @@ -77,7 +129,7 @@ void main() { ); }); - test('launch with headers', () async { + test('passes headers', () async { final UrlLauncherAndroid launcher = UrlLauncherAndroid(); await launcher.launch( 'http://example.com/', @@ -103,7 +155,7 @@ void main() { ); }); - test('launch universal links only', () async { + test('handles universal links only', () async { final UrlLauncherAndroid launcher = UrlLauncherAndroid(); await launcher.launch( 'http://example.com/', @@ -129,7 +181,7 @@ void main() { ); }); - test('launch force WebView', () async { + test('handles force WebView', () async { final UrlLauncherAndroid launcher = UrlLauncherAndroid(); await launcher.launch( 'http://example.com/', @@ -155,7 +207,7 @@ void main() { ); }); - test('launch force WebView enable javascript', () async { + test('handles force WebView with javascript', () async { final UrlLauncherAndroid launcher = UrlLauncherAndroid(); await launcher.launch( 'http://example.com/', @@ -181,7 +233,7 @@ void main() { ); }); - test('launch force WebView enable DOM storage', () async { + test('handles force WebView with DOM storage', () async { final UrlLauncherAndroid launcher = UrlLauncherAndroid(); await launcher.launch( 'http://example.com/', @@ -207,7 +259,7 @@ void main() { ); }); - test('launch should return false if platform returns null', () async { + test('returns false if platform returns null', () async { final UrlLauncherAndroid launcher = UrlLauncherAndroid(); final bool launched = await launcher.launch( 'http://example.com/', @@ -221,8 +273,10 @@ void main() { expect(launched, false); }); + }); - test('closeWebView default behavior', () async { + group('closeWebView', () { + test('calls through', () async { final UrlLauncherAndroid launcher = UrlLauncherAndroid(); await launcher.closeWebView(); expect( From 3e43f590d4d0143972e702eb14f14658cdc2decb Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Tue, 26 Apr 2022 15:29:06 -0700 Subject: [PATCH 196/844] [path_provider] Fix Unchecked/Unsafe Operation Warning (#5267) --- .../path_provider_android/CHANGELOG.md | 4 ++++ .../pathprovider/PathProviderPlugin.java | 24 ++++++------------- .../path_provider_android/pubspec.yaml | 4 ++-- 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index 7b04e90d5e0f..31f8c81f8a65 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.13 + +* Fixes typing build warning. + ## 2.0.12 * Returns to using a different platform channel name, undoing the revert in diff --git a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java index 278ff58b59dc..6dcf9595ac86 100644 --- a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java +++ b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java @@ -17,16 +17,14 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.BinaryMessenger.TaskQueue; 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; -import io.flutter.plugin.common.MethodCodec; import io.flutter.plugin.common.StandardMethodCodec; import io.flutter.util.PathUtils; import java.io.File; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; @@ -154,25 +152,17 @@ public PathProviderPlugin() {} private void setup(BinaryMessenger messenger, Context context) { String channelName = "plugins.flutter.io/path_provider_android"; - // TODO(gaaclarke): Remove reflection guard when https://github.com/flutter/engine/pull/29147 - // becomes available on the stable branch. + TaskQueue taskQueue = messenger.makeBackgroundTaskQueue(); + try { - Class methodChannelClass = Class.forName("io.flutter.plugin.common.MethodChannel"); - Class taskQueueClass = Class.forName("io.flutter.plugin.common.BinaryMessenger$TaskQueue"); - Method makeBackgroundTaskQueue = messenger.getClass().getMethod("makeBackgroundTaskQueue"); - Object taskQueue = makeBackgroundTaskQueue.invoke(messenger); - Constructor constructor = - methodChannelClass.getConstructor( - BinaryMessenger.class, String.class, MethodCodec.class, taskQueueClass); channel = - constructor.newInstance(messenger, channelName, StandardMethodCodec.INSTANCE, taskQueue); + (MethodChannel) + new MethodChannel(messenger, channelName, StandardMethodCodec.INSTANCE, taskQueue); impl = new PathProviderBackgroundThread(); - Log.d(TAG, "Use TaskQueues."); } catch (Exception ex) { - channel = new MethodChannel(messenger, channelName); - impl = new PathProviderPlatformThread(); - Log.d(TAG, "Don't use TaskQueues."); + Log.e(TAG, "Received exception while setting up PathProviderPlugin", ex); } + this.context = context; channel.setMethodCallHandler(this); } diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index 63b9330a89f9..93ed9848f75b 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,11 +2,11 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.12 +version: 2.0.13 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.8.1" flutter: plugin: From a9d5a93fd23c63238fa56958a138b36e6079f69d Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 27 Apr 2022 14:59:06 -0400 Subject: [PATCH 197/844] [google_sign_in] Switch to internal method channels (#5396) --- .../google_sign_in_android/CHANGELOG.md | 4 + .../googlesignin/GoogleSignInPlugin.java | 2 +- .../integration_test/google_sign_in_test.dart | 8 + .../lib/google_sign_in_android.dart | 95 +++++++++++ .../google_sign_in_android/lib/src/utils.dart | 28 ++++ .../google_sign_in_android/pubspec.yaml | 5 +- .../test/google_sign_in_android_test.dart | 143 +++++++++++++++++ .../google_sign_in_ios/CHANGELOG.md | 4 + .../integration_test/google_sign_in_test.dart | 8 + .../ios/RunnerTests/GoogleSignInTests.m | 27 ---- .../ios/Classes/FLTGoogleSignInPlugin.m | 56 +++---- .../lib/google_sign_in_ios.dart | 97 +++++++++++ .../google_sign_in_ios/lib/src/utils.dart | 28 ++++ .../google_sign_in_ios/pubspec.yaml | 5 +- .../test/google_sign_in_ios_test.dart | 151 ++++++++++++++++++ 15 files changed, 595 insertions(+), 66 deletions(-) create mode 100644 packages/google_sign_in/google_sign_in_android/lib/google_sign_in_android.dart create mode 100644 packages/google_sign_in/google_sign_in_android/lib/src/utils.dart create mode 100644 packages/google_sign_in/google_sign_in_android/test/google_sign_in_android_test.dart create mode 100644 packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart create mode 100644 packages/google_sign_in/google_sign_in_ios/lib/src/utils.dart create mode 100644 packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart 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 e7c6847e7750..3ffa6b5b7d6b 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.2.6 + +* Switches to an internal method channel, rather than the default. + ## 5.2.5 * Splits from `video_player` as a federated implementation. diff --git a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java index 1be023c678bb..a1237f0013a1 100644 --- a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java +++ b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java @@ -44,7 +44,7 @@ /** Google sign-in plugin for Flutter. */ public class GoogleSignInPlugin implements MethodCallHandler, FlutterPlugin, ActivityAware { - private static final String CHANNEL_NAME = "plugins.flutter.io/google_sign_in"; + private static final String CHANNEL_NAME = "plugins.flutter.io/google_sign_in_android"; private static final String METHOD_INIT = "init"; private static final String METHOD_SIGN_IN_SILENTLY = "signInSilently"; diff --git a/packages/google_sign_in/google_sign_in_android/example/integration_test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in_android/example/integration_test/google_sign_in_test.dart index d4631f6a6fd3..f1388ce86d67 100644 --- a/packages/google_sign_in/google_sign_in_android/example/integration_test/google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in_android/example/integration_test/google_sign_in_test.dart @@ -13,4 +13,12 @@ void main() { final GoogleSignInPlatform signIn = GoogleSignInPlatform.instance; expect(signIn, isNotNull); }); + + testWidgets('Method channel handler is present', (WidgetTester tester) async { + // isSignedIn can be called without initialization, so use it to validate + // that the native method handler is present (e.g., that the channel name + // is correct). + final GoogleSignInPlatform signIn = GoogleSignInPlatform.instance; + await expectLater(signIn.isSignedIn(), completes); + }); } diff --git a/packages/google_sign_in/google_sign_in_android/lib/google_sign_in_android.dart b/packages/google_sign_in/google_sign_in_android/lib/google_sign_in_android.dart new file mode 100644 index 000000000000..d96328de695a --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/lib/google_sign_in_android.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 'dart:async'; + +import 'package:flutter/foundation.dart' show visibleForTesting; +import 'package:flutter/services.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; + +import 'src/utils.dart'; + +/// Android implementation of [GoogleSignInPlatform]. +class GoogleSignInAndroid extends GoogleSignInPlatform { + /// This is only exposed for test purposes. It shouldn't be used by clients of + /// the plugin as it may break or change at any time. + @visibleForTesting + MethodChannel channel = + const MethodChannel('plugins.flutter.io/google_sign_in_android'); + + /// Registers this class as the default instance of [GoogleSignInPlatform]. + static void registerWith() { + GoogleSignInPlatform.instance = GoogleSignInAndroid(); + } + + @override + Future init({ + List scopes = const [], + SignInOption signInOption = SignInOption.standard, + String? hostedDomain, + String? clientId, + }) { + return channel.invokeMethod('init', { + 'signInOption': signInOption.toString(), + 'scopes': scopes, + 'hostedDomain': hostedDomain, + 'clientId': clientId, + }); + } + + @override + Future signInSilently() { + return channel + .invokeMapMethod('signInSilently') + .then(getUserDataFromMap); + } + + @override + Future signIn() { + return channel + .invokeMapMethod('signIn') + .then(getUserDataFromMap); + } + + @override + Future getTokens( + {required String email, bool? shouldRecoverAuth = true}) { + return channel + .invokeMapMethod('getTokens', { + 'email': email, + 'shouldRecoverAuth': shouldRecoverAuth, + }).then((Map? result) => getTokenDataFromMap(result!)); + } + + @override + Future signOut() { + return channel.invokeMapMethod('signOut'); + } + + @override + Future disconnect() { + return channel.invokeMapMethod('disconnect'); + } + + @override + Future isSignedIn() async { + return (await channel.invokeMethod('isSignedIn'))!; + } + + @override + Future clearAuthCache({String? token}) { + return channel.invokeMethod( + 'clearAuthCache', + {'token': token}, + ); + } + + @override + Future requestScopes(List scopes) async { + return (await channel.invokeMethod( + 'requestScopes', + >{'scopes': scopes}, + ))!; + } +} diff --git a/packages/google_sign_in/google_sign_in_android/lib/src/utils.dart b/packages/google_sign_in/google_sign_in_android/lib/src/utils.dart new file mode 100644 index 000000000000..5cd7c20b829a --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/lib/src/utils.dart @@ -0,0 +1,28 @@ +// 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:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; + +/// Converts user data coming from native code into the proper platform interface type. +GoogleSignInUserData? getUserDataFromMap(Map? data) { + if (data == null) { + return null; + } + return GoogleSignInUserData( + email: data['email']! as String, + id: data['id']! as String, + displayName: data['displayName'] as String?, + photoUrl: data['photoUrl'] as String?, + idToken: data['idToken'] as String?, + serverAuthCode: data['serverAuthCode'] as String?); +} + +/// Converts token data coming from native code into the proper platform interface type. +GoogleSignInTokenData getTokenDataFromMap(Map data) { + return GoogleSignInTokenData( + idToken: data['idToken'] as String?, + accessToken: data['accessToken'] as String?, + serverAuthCode: data['serverAuthCode'] as String?, + ); +} 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 efd679ca399b..fa3dc1489b26 100644 --- a/packages/google_sign_in/google_sign_in_android/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_android/pubspec.yaml @@ -2,17 +2,18 @@ name: google_sign_in_android description: Android implementation of the google_sign_in plugin. repository: https://github.com/flutter/plugins/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: 5.2.5 +version: 5.2.6 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: implements: google_sign_in platforms: android: + dartPluginClass: GoogleSignInAndroid package: io.flutter.plugins.googlesignin pluginClass: GoogleSignInPlugin diff --git a/packages/google_sign_in/google_sign_in_android/test/google_sign_in_android_test.dart b/packages/google_sign_in/google_sign_in_android/test/google_sign_in_android_test.dart new file mode 100644 index 000000000000..7d39ae5f0232 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_android/test/google_sign_in_android_test.dart @@ -0,0 +1,143 @@ +// 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:flutter_test/flutter_test.dart'; +import 'package:google_sign_in_android/google_sign_in_android.dart'; +import 'package:google_sign_in_android/src/utils.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; + +const Map kUserData = { + 'email': 'john.doe@gmail.com', + 'id': '8162538176523816253123', + 'photoUrl': 'https://lh5.googleusercontent.com/photo.jpg', + 'displayName': 'John Doe', + 'idToken': '123', + 'serverAuthCode': '789', +}; + +const Map kTokenData = { + 'idToken': '123', + 'accessToken': '456', + 'serverAuthCode': '789', +}; + +const Map kDefaultResponses = { + 'init': null, + 'signInSilently': kUserData, + 'signIn': kUserData, + 'signOut': null, + 'disconnect': null, + 'isSignedIn': true, + 'getTokens': kTokenData, + 'requestScopes': true, +}; + +final GoogleSignInUserData? kUser = getUserDataFromMap(kUserData); +final GoogleSignInTokenData kToken = + getTokenDataFromMap(kTokenData as Map); + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + final GoogleSignInAndroid googleSignIn = GoogleSignInAndroid(); + final MethodChannel channel = googleSignIn.channel; + + final List log = []; + late Map + responses; // Some tests mutate some kDefaultResponses + + setUp(() { + responses = Map.from(kDefaultResponses); + channel.setMockMethodCallHandler((MethodCall methodCall) { + log.add(methodCall); + final dynamic response = responses[methodCall.method]; + if (response != null && response is Exception) { + return Future.error('$response'); + } + return Future.value(response); + }); + log.clear(); + }); + + test('registered instance', () { + GoogleSignInAndroid.registerWith(); + expect(GoogleSignInPlatform.instance, isA()); + }); + + test('signInSilently transforms platform data to GoogleSignInUserData', + () async { + final dynamic response = await googleSignIn.signInSilently(); + expect(response, kUser); + }); + test('signInSilently Exceptions -> throws', () async { + responses['signInSilently'] = Exception('Not a user'); + expect(googleSignIn.signInSilently(), + throwsA(isInstanceOf())); + }); + + test('signIn transforms platform data to GoogleSignInUserData', () async { + final dynamic response = await googleSignIn.signIn(); + expect(response, kUser); + }); + test('signIn Exceptions -> throws', () async { + responses['signIn'] = Exception('Not a user'); + expect(googleSignIn.signIn(), throwsA(isInstanceOf())); + }); + + test('getTokens transforms platform data to GoogleSignInTokenData', () async { + final dynamic response = await googleSignIn.getTokens( + email: 'example@example.com', shouldRecoverAuth: false); + expect(response, kToken); + expect( + log[0], + isMethodCall('getTokens', arguments: { + 'email': 'example@example.com', + 'shouldRecoverAuth': false, + })); + }); + + test('Other functions pass through arguments to the channel', () async { + final Map tests = { + () { + googleSignIn.init( + hostedDomain: 'example.com', + scopes: ['two', 'scopes'], + signInOption: SignInOption.games, + clientId: 'fakeClientId'); + }: isMethodCall('init', arguments: { + 'hostedDomain': 'example.com', + 'scopes': ['two', 'scopes'], + 'signInOption': 'SignInOption.games', + 'clientId': 'fakeClientId', + }), + () { + googleSignIn.getTokens( + email: 'example@example.com', shouldRecoverAuth: false); + }: isMethodCall('getTokens', arguments: { + 'email': 'example@example.com', + 'shouldRecoverAuth': false, + }), + () { + googleSignIn.clearAuthCache(token: 'abc'); + }: isMethodCall('clearAuthCache', arguments: { + 'token': 'abc', + }), + () { + googleSignIn.requestScopes(['newScope', 'anotherScope']); + }: isMethodCall('requestScopes', arguments: { + 'scopes': ['newScope', 'anotherScope'], + }), + googleSignIn.signOut: isMethodCall('signOut', arguments: null), + googleSignIn.disconnect: isMethodCall('disconnect', arguments: null), + googleSignIn.isSignedIn: isMethodCall('isSignedIn', arguments: null), + }; + + for (final Function f in tests.keys) { + f(); + } + + expect(log, tests.values); + }); +} 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 e7c6847e7750..3ffa6b5b7d6b 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.2.6 + +* Switches to an internal method channel, rather than the default. + ## 5.2.5 * Splits from `video_player` as a federated implementation. diff --git a/packages/google_sign_in/google_sign_in_ios/example/integration_test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in_ios/example/integration_test/google_sign_in_test.dart index d4631f6a6fd3..f1388ce86d67 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/integration_test/google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in_ios/example/integration_test/google_sign_in_test.dart @@ -13,4 +13,12 @@ void main() { final GoogleSignInPlatform signIn = GoogleSignInPlatform.instance; expect(signIn, isNotNull); }); + + testWidgets('Method channel handler is present', (WidgetTester tester) async { + // isSignedIn can be called without initialization, so use it to validate + // that the native method handler is present (e.g., that the channel name + // is correct). + final GoogleSignInPlatform signIn = GoogleSignInPlatform.instance; + await expectLater(signIn.isSignedIn(), completes); + }); } diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m index dbbd65397ac5..3bc08d18604a 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m @@ -73,35 +73,8 @@ - (void)testDisconnect { OCMVerify([self.mockSignIn disconnect]); } -- (void)testClearAuthCache { - FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"clearAuthCache" - arguments:nil]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; - [self.plugin handleMethodCall:methodCall - result:^(id result) { - XCTAssertNil(result); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:5.0 handler:nil]; -} - #pragma mark - Init -- (void)testInitGamesSignInUnsupported { - FlutterMethodCall *methodCall = - [FlutterMethodCall methodCallWithMethodName:@"init" - arguments:@{@"signInOption" : @"SignInOption.games"}]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; - [self.plugin handleMethodCall:methodCall - result:^(FlutterError *result) { - XCTAssertEqualObjects(result.code, @"unsupported-options"); - [expectation fulfill]; - }]; - [self waitForExpectationsWithTimeout:5.0 handler:nil]; -} - - (void)testInitGoogleServiceInfoPlist { FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"init" diff --git a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m index d13d64d2ba04..5ad69e2ad052 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m @@ -50,7 +50,7 @@ @implementation FLTGoogleSignInPlugin { + (void)registerWithRegistrar:(NSObject *)registrar { FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/google_sign_in" + [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/google_sign_in_ios" binaryMessenger:[registrar messenger]]; FLTGoogleSignInPlugin *instance = [[FLTGoogleSignInPlugin alloc] init]; [registrar addApplicationDelegate:instance]; @@ -78,38 +78,30 @@ - (instancetype)initWithSignIn:(GIDSignIn *)signIn { - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { if ([call.method isEqualToString:@"init"]) { - NSString *signInOption = call.arguments[@"signInOption"]; - if ([signInOption isEqualToString:@"SignInOption.games"]) { - result([FlutterError errorWithCode:@"unsupported-options" - message:@"Games sign in is not supported on iOS" - details:nil]); - } else { - NSString *path = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" - ofType:@"plist"]; - if (path) { - NSMutableDictionary *plist = - [[NSMutableDictionary alloc] initWithContentsOfFile:path]; - BOOL hasDynamicClientId = [call.arguments[@"clientId"] isKindOfClass:[NSString class]]; - - if (hasDynamicClientId) { - self.signIn.clientID = call.arguments[@"clientId"]; - } else { - self.signIn.clientID = plist[kClientIdKey]; - } + NSString *path = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"]; + if (path) { + NSMutableDictionary *plist = + [[NSMutableDictionary alloc] initWithContentsOfFile:path]; + BOOL hasDynamicClientId = [call.arguments[@"clientId"] isKindOfClass:[NSString class]]; + + if (hasDynamicClientId) { + self.signIn.clientID = call.arguments[@"clientId"]; + } else { + self.signIn.clientID = plist[kClientIdKey]; + } - self.signIn.serverClientID = plist[kServerClientIdKey]; - self.signIn.scopes = call.arguments[@"scopes"]; - if (call.arguments[@"hostedDomain"] == [NSNull null]) { - self.signIn.hostedDomain = nil; - } else { - self.signIn.hostedDomain = call.arguments[@"hostedDomain"]; - } - result(nil); + self.signIn.serverClientID = plist[kServerClientIdKey]; + self.signIn.scopes = call.arguments[@"scopes"]; + if (call.arguments[@"hostedDomain"] == [NSNull null]) { + self.signIn.hostedDomain = nil; } else { - result([FlutterError errorWithCode:@"missing-config" - message:@"GoogleService-Info.plist file not found" - details:nil]); + self.signIn.hostedDomain = call.arguments[@"hostedDomain"]; } + result(nil); + } else { + result([FlutterError errorWithCode:@"missing-config" + message:@"GoogleService-Info.plist file not found" + details:nil]); } } else if ([call.method isEqualToString:@"signInSilently"]) { if ([self setAccountRequest:result]) { @@ -144,10 +136,6 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result if ([self setAccountRequest:result]) { [self.signIn disconnect]; } - } else if ([call.method isEqualToString:@"clearAuthCache"]) { - // There's nothing to be done here on iOS since the expired/invalid - // tokens are refreshed automatically by getTokensWithHandler. - result(nil); } else if ([call.method isEqualToString:@"requestScopes"]) { GIDGoogleUser *user = self.signIn.currentUser; if (user == nil) { diff --git a/packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart b/packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart new file mode 100644 index 000000000000..ce8865664507 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart @@ -0,0 +1,97 @@ +// 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:async'; + +import 'package:flutter/foundation.dart' show visibleForTesting; +import 'package:flutter/services.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; + +import 'src/utils.dart'; + +/// iOS implementation of [GoogleSignInPlatform]. +class GoogleSignInIOS extends GoogleSignInPlatform { + /// This is only exposed for test purposes. It shouldn't be used by clients of + /// the plugin as it may break or change at any time. + @visibleForTesting + MethodChannel channel = + const MethodChannel('plugins.flutter.io/google_sign_in_ios'); + + /// Registers this class as the default instance of [GoogleSignInPlatform]. + static void registerWith() { + GoogleSignInPlatform.instance = GoogleSignInIOS(); + } + + @override + Future init({ + List scopes = const [], + SignInOption signInOption = SignInOption.standard, + String? hostedDomain, + String? clientId, + }) { + if (signInOption == SignInOption.games) { + throw PlatformException( + code: 'unsupported-options', + message: 'Games sign in is not supported on iOS'); + } + return channel.invokeMethod('init', { + 'scopes': scopes, + 'hostedDomain': hostedDomain, + 'clientId': clientId, + }); + } + + @override + Future signInSilently() { + return channel + .invokeMapMethod('signInSilently') + .then(getUserDataFromMap); + } + + @override + Future signIn() { + return channel + .invokeMapMethod('signIn') + .then(getUserDataFromMap); + } + + @override + Future getTokens( + {required String email, bool? shouldRecoverAuth = true}) { + return channel + .invokeMapMethod('getTokens', { + 'email': email, + 'shouldRecoverAuth': shouldRecoverAuth, + }).then((Map? result) => getTokenDataFromMap(result!)); + } + + @override + Future signOut() { + return channel.invokeMapMethod('signOut'); + } + + @override + Future disconnect() { + return channel.invokeMapMethod('disconnect'); + } + + @override + Future isSignedIn() async { + return (await channel.invokeMethod('isSignedIn'))!; + } + + @override + Future clearAuthCache({String? token}) async { + // There's nothing to be done here on iOS since the expired/invalid + // tokens are refreshed automatically by getTokens. + } + + @override + Future requestScopes(List scopes) async { + return (await channel.invokeMethod( + 'requestScopes', + >{'scopes': scopes}, + ))!; + } +} diff --git a/packages/google_sign_in/google_sign_in_ios/lib/src/utils.dart b/packages/google_sign_in/google_sign_in_ios/lib/src/utils.dart new file mode 100644 index 000000000000..5cd7c20b829a --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/lib/src/utils.dart @@ -0,0 +1,28 @@ +// 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:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; + +/// Converts user data coming from native code into the proper platform interface type. +GoogleSignInUserData? getUserDataFromMap(Map? data) { + if (data == null) { + return null; + } + return GoogleSignInUserData( + email: data['email']! as String, + id: data['id']! as String, + displayName: data['displayName'] as String?, + photoUrl: data['photoUrl'] as String?, + idToken: data['idToken'] as String?, + serverAuthCode: data['serverAuthCode'] as String?); +} + +/// Converts token data coming from native code into the proper platform interface type. +GoogleSignInTokenData getTokenDataFromMap(Map data) { + return GoogleSignInTokenData( + idToken: data['idToken'] as String?, + accessToken: data['accessToken'] as String?, + serverAuthCode: data['serverAuthCode'] as String?, + ); +} 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 52c4f77feb4b..e5ef3832ff0e 100644 --- a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml @@ -2,17 +2,18 @@ name: google_sign_in_ios description: Android implementation of the google_sign_in plugin. repository: https://github.com/flutter/plugins/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.2.5 +version: 5.2.6 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: implements: google_sign_in platforms: ios: + dartPluginClass: GoogleSignInIOS pluginClass: FLTGoogleSignInPlugin dependencies: diff --git a/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart b/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart new file mode 100644 index 000000000000..92637e938fd9 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart @@ -0,0 +1,151 @@ +// 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:flutter_test/flutter_test.dart'; +import 'package:google_sign_in_ios/google_sign_in_ios.dart'; +import 'package:google_sign_in_ios/src/utils.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; + +const Map kUserData = { + 'email': 'john.doe@gmail.com', + 'id': '8162538176523816253123', + 'photoUrl': 'https://lh5.googleusercontent.com/photo.jpg', + 'displayName': 'John Doe', + 'idToken': '123', + 'serverAuthCode': '789', +}; + +const Map kTokenData = { + 'idToken': '123', + 'accessToken': '456', + 'serverAuthCode': '789', +}; + +const Map kDefaultResponses = { + 'init': null, + 'signInSilently': kUserData, + 'signIn': kUserData, + 'signOut': null, + 'disconnect': null, + 'isSignedIn': true, + 'getTokens': kTokenData, + 'requestScopes': true, +}; + +final GoogleSignInUserData? kUser = getUserDataFromMap(kUserData); +final GoogleSignInTokenData kToken = + getTokenDataFromMap(kTokenData as Map); + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + final GoogleSignInIOS googleSignIn = GoogleSignInIOS(); + final MethodChannel channel = googleSignIn.channel; + + late List log; + late Map + responses; // Some tests mutate some kDefaultResponses + + setUp(() { + responses = Map.from(kDefaultResponses); + log = []; + channel.setMockMethodCallHandler((MethodCall methodCall) { + log.add(methodCall); + final dynamic response = responses[methodCall.method]; + if (response != null && response is Exception) { + return Future.error('$response'); + } + return Future.value(response); + }); + }); + + test('registered instance', () { + GoogleSignInIOS.registerWith(); + expect(GoogleSignInPlatform.instance, isA()); + }); + + test('init throws for SignInOptions.games', () async { + expect( + () => googleSignIn.init( + hostedDomain: 'example.com', + signInOption: SignInOption.games, + clientId: 'fakeClientId'), + throwsA(isInstanceOf().having( + (PlatformException e) => e.code, 'code', 'unsupported-options'))); + }); + + test('signInSilently transforms platform data to GoogleSignInUserData', + () async { + final dynamic response = await googleSignIn.signInSilently(); + expect(response, kUser); + }); + test('signInSilently Exceptions -> throws', () async { + responses['signInSilently'] = Exception('Not a user'); + expect(googleSignIn.signInSilently(), + throwsA(isInstanceOf())); + }); + + test('signIn transforms platform data to GoogleSignInUserData', () async { + final dynamic response = await googleSignIn.signIn(); + expect(response, kUser); + }); + test('signIn Exceptions -> throws', () async { + responses['signIn'] = Exception('Not a user'); + expect(googleSignIn.signIn(), throwsA(isInstanceOf())); + }); + + test('getTokens transforms platform data to GoogleSignInTokenData', () async { + final dynamic response = await googleSignIn.getTokens( + email: 'example@example.com', shouldRecoverAuth: false); + expect(response, kToken); + expect( + log[0], + isMethodCall('getTokens', arguments: { + 'email': 'example@example.com', + 'shouldRecoverAuth': false, + })); + }); + + test('clearAuthCache is a no-op', () async { + await googleSignIn.clearAuthCache(token: 'abc'); + expect(log.isEmpty, true); + }); + + test('Other functions pass through arguments to the channel', () async { + final Map tests = { + () { + googleSignIn.init( + hostedDomain: 'example.com', + scopes: ['two', 'scopes'], + clientId: 'fakeClientId'); + }: isMethodCall('init', arguments: { + 'hostedDomain': 'example.com', + 'scopes': ['two', 'scopes'], + 'clientId': 'fakeClientId', + }), + () { + googleSignIn.getTokens( + email: 'example@example.com', shouldRecoverAuth: false); + }: isMethodCall('getTokens', arguments: { + 'email': 'example@example.com', + 'shouldRecoverAuth': false, + }), + () { + googleSignIn.requestScopes(['newScope', 'anotherScope']); + }: isMethodCall('requestScopes', arguments: { + 'scopes': ['newScope', 'anotherScope'], + }), + googleSignIn.signOut: isMethodCall('signOut', arguments: null), + googleSignIn.disconnect: isMethodCall('disconnect', arguments: null), + googleSignIn.isSignedIn: isMethodCall('isSignedIn', arguments: null), + }; + + for (final Function f in tests.keys) { + f(); + } + + expect(log, tests.values); + }); +} From d78cc8eaaf693848867e9b76933a94a78b3fb799 Mon Sep 17 00:00:00 2001 From: Mahesh Jamdade <31410839+maheshmnj@users.noreply.github.com> Date: Thu, 28 Apr 2022 01:29:07 +0530 Subject: [PATCH 198/844] [google_maps_flutter] update maps sdk for Android (#4984) --- packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md | 3 ++- .../google_maps_flutter/android/build.gradle | 4 ++-- .../io/flutter/plugins/googlemaps/CircleControllerTest.java | 4 ++-- .../io/flutter/plugins/googlemaps/PolygonControllerTest.java | 4 ++-- .../io/flutter/plugins/googlemaps/PolylineControllerTest.java | 4 ++-- .../google_maps_flutter/example/android/build.gradle | 2 +- packages/google_maps_flutter/google_maps_flutter/pubspec.yaml | 2 +- 7 files changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 3ecea7c465e7..61b151427520 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.1.4 +* Updates Android Google maps sdk version to `18.0.2`. * Adds OS version support information to README. ## 2.1.3 diff --git a/packages/google_maps_flutter/google_maps_flutter/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter/android/build.gradle index bf283bea9ef9..356e3c5e22f7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/android/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:3.5.4' } } @@ -35,7 +35,7 @@ android { dependencies { implementation "androidx.annotation:annotation:1.1.0" - implementation 'com.google.android.gms:play-services-maps:17.0.0' + implementation 'com.google.android.gms:play-services-maps:18.0.2' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:rules:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java b/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java index 72a8cab626b5..064c8c3591eb 100644 --- a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java +++ b/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java @@ -7,7 +7,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import com.google.android.gms.internal.maps.zzh; +import com.google.android.gms.internal.maps.zzl; import com.google.android.gms.maps.model.Circle; import org.junit.Test; import org.mockito.Mockito; @@ -16,7 +16,7 @@ public class CircleControllerTest { @Test public void controller_SetsStrokeDensity() { - final zzh z = mock(zzh.class); + final zzl z = mock(zzl.class); final Circle circle = spy(new Circle(z)); final float density = 5; diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java b/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java index 29234b6adb42..5c73a3f3e449 100644 --- a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java +++ b/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java @@ -7,7 +7,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import com.google.android.gms.internal.maps.zzw; +import com.google.android.gms.internal.maps.zzaa; import com.google.android.gms.maps.model.Polygon; import org.junit.Test; import org.mockito.Mockito; @@ -16,7 +16,7 @@ public class PolygonControllerTest { @Test public void controller_SetsStrokeDensity() { - final zzw z = mock(zzw.class); + final zzaa z = mock(zzaa.class); final Polygon polygon = spy(new Polygon(z)); final float density = 5; diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java b/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java index bb7653aa2293..db570174e215 100644 --- a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java +++ b/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java @@ -7,7 +7,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import com.google.android.gms.internal.maps.zzz; +import com.google.android.gms.internal.maps.zzad; import com.google.android.gms.maps.model.Polyline; import org.junit.Test; import org.mockito.Mockito; @@ -16,7 +16,7 @@ public class PolylineControllerTest { @Test public void controller_SetsStrokeDensity() { - final zzz z = mock(zzz.class); + final zzad z = mock(zzad.class); final Polyline polyline = spy(new Polyline(z)); final float density = 5; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter/example/android/build.gradle index 456d020f6e2c..4d8d45d13a0b 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/android/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:3.5.4' } } diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index 741fe6910037..c10f9c679a17 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/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.1.3 +version: 2.1.4 environment: sdk: ">=2.14.0 <3.0.0" From d8f6ddbaad39e1001fe7fb3d7acd811ee66ea721 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Wed, 27 Apr 2022 17:09:11 -0700 Subject: [PATCH 199/844] ci: remove uwp test (#5427) --- .ci.yaml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/.ci.yaml b/.ci.yaml index c00fcf0b7865..14772f475af6 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -85,19 +85,6 @@ targets: {"dependency": "vs_build"} ] - - name: Windows uwp-platform_tests master - recipe: plugins/plugins - timeout: 30 - properties: - add_recipes_cq: "true" - target_file: uwp_build_and_platform_tests.yaml - channel: master - version_file: flutter_master.version - dependencies: > - [ - {"dependency": "vs_build"} - ] - - name: Windows plugin_tools_tests recipe: plugins/plugins timeout: 30 From ac3167f83402c056e97dea7599a6f3949d3aa8cb Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 28 Apr 2022 12:34:11 -0400 Subject: [PATCH 200/844] [flutter_plugin_tools] Adds update-excerpts command (#5339) --- .cirrus.yml | 4 + .gitmodules | 3 + packages/camera/camera/CHANGELOG.md | 4 + packages/camera/camera/README.md | 41 +-- .../camera/camera/example/build.excerpt.yaml | 15 + packages/camera/camera/example/lib/main.dart | 2 + .../example/lib/readme_full_example.dart | 56 ++++ packages/camera/camera/example/pubspec.yaml | 1 + packages/camera/camera/pubspec.yaml | 2 +- script/tool/CHANGELOG.md | 5 +- script/tool/README.md | 11 + .../tool/lib/src/license_check_command.dart | 33 +- script/tool/lib/src/main.dart | 2 + .../tool/lib/src/update_excerpts_command.dart | 225 ++++++++++++++ script/tool/pubspec.yaml | 3 +- script/tool/test/format_command_test.dart | 2 +- .../tool/test/license_check_command_test.dart | 45 ++- .../test/update_excerpts_command_test.dart | 284 ++++++++++++++++++ site-shared | 1 + 19 files changed, 713 insertions(+), 26 deletions(-) create mode 100644 .gitmodules create mode 100644 packages/camera/camera/example/build.excerpt.yaml create mode 100644 packages/camera/camera/example/lib/readme_full_example.dart create mode 100644 script/tool/lib/src/update_excerpts_command.dart create mode 100644 script/tool/test/update_excerpts_command_test.dart create mode 160000 site-shared diff --git a/.cirrus.yml b/.cirrus.yml index 66e8336301d4..c256ab19426e 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -155,6 +155,10 @@ task: analyze_script: - ./script/tool_runner.sh analyze --skip-if-not-supporting-flutter-version="$CHANNEL" --custom-analysis=script/configs/custom_analysis.yaml - echo "If this test fails, the minumum Flutter version should be updated" + - name: readme_excerpts + env: + CIRRUS_CLONE_SUBMODULES: true + script: ./script/tool_runner.sh update-excerpts --fail-on-change ### Web tasks ### - name: web-build_all_plugins env: diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000000..1d3bb5da1bfb --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "site-shared"] + path = site-shared + url = https://github.com/dart-lang/site-shared diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index b86443249dad..a9986697b048 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+21 + +* Fixes README code samples. + ## 0.9.4+20 * Fixes an issue with the orientation of videos recorded in landscape on Android. diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index a313c3a1655f..a1c60a08950d 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -1,5 +1,7 @@ # Camera Plugin + + [![pub package](https://img.shields.io/pub/v/camera.svg)](https://pub.dev/packages/camera) A Flutter plugin for iOS, Android and Web allowing access to the device cameras. @@ -59,33 +61,35 @@ For web integration details, see the As of version [0.5.0](https://github.com/flutter/plugins/blob/master/packages/camera/CHANGELOG.md#050) of the camera plugin, lifecycle changes are no longer handled by the plugin. This means developers are now responsible to control camera resources when the lifecycle state is updated. Failure to do so might lead to unexpected behavior (for example as described in issue [#39109](https://github.com/flutter/flutter/issues/39109)). Handling lifecycle changes can be done by overriding the `didChangeAppLifecycleState` method like so: + ```dart - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - // App state changed before we got the chance to initialize. - if (controller == null || !controller.value.isInitialized) { - return; - } - if (state == AppLifecycleState.inactive) { - controller?.dispose(); - } else if (state == AppLifecycleState.resumed) { - if (controller != null) { - onNewCameraSelected(controller.description); - } - } +@override +void didChangeAppLifecycleState(AppLifecycleState state) { + final CameraController? cameraController = controller; + + // App state changed before we got the chance to initialize. + if (cameraController == null || !cameraController.value.isInitialized) { + return; } + + if (state == AppLifecycleState.inactive) { + cameraController.dispose(); + } else if (state == AppLifecycleState.resumed) { + onNewCameraSelected(cameraController.description); + } +} ``` ### Example Here is a small example flutter app displaying a full screen camera preview. + ```dart -import 'dart:async'; -import 'package:flutter/material.dart'; import 'package:camera/camera.dart'; +import 'package:flutter/material.dart'; -List cameras; +late List cameras; Future main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -100,7 +104,7 @@ class CameraApp extends StatefulWidget { } class _CameraAppState extends State { - CameraController controller; + late CameraController controller; @override void initState() { @@ -116,7 +120,7 @@ class _CameraAppState extends State { @override void dispose() { - controller?.dispose(); + controller.dispose(); super.dispose(); } @@ -130,7 +134,6 @@ class _CameraAppState extends State { ); } } - ``` For a more elaborate usage example see [here](https://github.com/flutter/plugins/tree/main/packages/camera/camera/example). diff --git a/packages/camera/camera/example/build.excerpt.yaml b/packages/camera/camera/example/build.excerpt.yaml new file mode 100644 index 000000000000..e317efa11cb3 --- /dev/null +++ b/packages/camera/camera/example/build.excerpt.yaml @@ -0,0 +1,15 @@ +targets: + $default: + sources: + include: + - lib/** + # Some default includes that aren't really used here but will prevent + # false-negative warnings: + - $package$ + - lib/$lib$ + exclude: + - '**/.*/**' + - '**/build/**' + builders: + code_excerpter|code_excerpter: + enabled: true diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index f9f1378176a3..aabbe249313d 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -106,6 +106,7 @@ class _CameraExampleHomeState extends State super.dispose(); } + // #docregion AppLifecycle @override void didChangeAppLifecycleState(AppLifecycleState state) { final CameraController? cameraController = controller; @@ -121,6 +122,7 @@ class _CameraExampleHomeState extends State onNewCameraSelected(cameraController.description); } } + // #enddocregion AppLifecycle @override Widget build(BuildContext context) { diff --git a/packages/camera/camera/example/lib/readme_full_example.dart b/packages/camera/camera/example/lib/readme_full_example.dart new file mode 100644 index 000000000000..b25e637a0c95 --- /dev/null +++ b/packages/camera/camera/example/lib/readme_full_example.dart @@ -0,0 +1,56 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +// #docregion FullAppExample +import 'package:camera/camera.dart'; +import 'package:flutter/material.dart'; + +late List cameras; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + + cameras = await availableCameras(); + runApp(CameraApp()); +} + +class CameraApp extends StatefulWidget { + @override + _CameraAppState createState() => _CameraAppState(); +} + +class _CameraAppState extends State { + late CameraController controller; + + @override + void initState() { + super.initState(); + controller = CameraController(cameras[0], ResolutionPreset.max); + controller.initialize().then((_) { + if (!mounted) { + return; + } + setState(() {}); + }); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (!controller.value.isInitialized) { + return Container(); + } + return MaterialApp( + home: CameraPreview(controller), + ); + } +} +// #enddocregion FullAppExample diff --git a/packages/camera/camera/example/pubspec.yaml b/packages/camera/camera/example/pubspec.yaml index 1700074f1f88..af4d078ff836 100644 --- a/packages/camera/camera/example/pubspec.yaml +++ b/packages/camera/camera/example/pubspec.yaml @@ -20,6 +20,7 @@ dependencies: video_player: ^2.1.4 dev_dependencies: + build_runner: ^2.1.10 flutter_driver: sdk: flutter flutter_test: diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index feb83f95cd8d..f62777044617 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+20 +version: 0.9.4+21 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index bfc0cafa88c3..82a31154e132 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,5 +1,8 @@ -## NEXT +## 0.8.3 +- Adds a new `update-excerpts` command to maintain README files using the + `code-excerpter` package from flutter/site-shared. +- `license-check` now ignores submodules. - Allows `make-deps-path-based` to skip packages it has alredy rewritten, so that running multiple times won't fail after the first time. diff --git a/script/tool/README.md b/script/tool/README.md index 05be917014f2..d52ee08fdc34 100644 --- a/script/tool/README.md +++ b/script/tool/README.md @@ -107,6 +107,17 @@ dart run ./script/tool/bin/flutter_plugin_tools.dart native-test --ios --android dart run ./script/tool/bin/flutter_plugin_tools.dart native-test --macos --packages plugin_name ``` +### Update README.md from Example Sources + +`update-excerpts` requires sources that are in a submodule. If you didn't clone +with submodules, you will need to `git submodule update --init --recursive` +before running this command. + +```sh +cd +dart run ./script/tool/bin/flutter_plugin_tools.dart update-excerpts --packages plugin_name +``` + ### Publish a Release **Releases are automated for `flutter/plugins` and `flutter/packages`.** diff --git a/script/tool/lib/src/license_check_command.dart b/script/tool/lib/src/license_check_command.dart index d2c129ff7b48..87e4c8b14861 100644 --- a/script/tool/lib/src/license_check_command.dart +++ b/script/tool/lib/src/license_check_command.dart @@ -3,7 +3,9 @@ // found in the LICENSE file. import 'package:file/file.dart'; +import 'package:git/git.dart'; import 'package:path/path.dart' as p; +import 'package:platform/platform.dart'; import 'common/core.dart'; import 'common/plugin_command.dart'; @@ -105,7 +107,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /// Validates that code files have copyright and license blocks. class LicenseCheckCommand extends PluginCommand { /// Creates a new license check command for [packagesDir]. - LicenseCheckCommand(Directory packagesDir) : super(packagesDir); + LicenseCheckCommand(Directory packagesDir, + {Platform platform = const LocalPlatform(), GitDir? gitDir}) + : super(packagesDir, platform: platform, gitDir: gitDir); @override final String name = 'license-check'; @@ -116,7 +120,14 @@ class LicenseCheckCommand extends PluginCommand { @override Future run() async { - final Iterable allFiles = await _getAllFiles(); + // Create a set of absolute paths to submodule directories, with trailing + // separator, to do prefix matching with to test directory inclusion. + final Iterable submodulePaths = (await _getSubmoduleDirectories()) + .map( + (Directory dir) => '${dir.absolute.path}${platform.pathSeparator}'); + + final Iterable allFiles = (await _getAllFiles()).where( + (File file) => !submodulePaths.any(file.absolute.path.startsWith)); final Iterable codeFiles = allFiles.where((File file) => _codeFileExtensions.contains(p.extension(file.path)) && @@ -275,6 +286,24 @@ class LicenseCheckCommand extends PluginCommand { .where((FileSystemEntity entity) => entity is File) .map((FileSystemEntity file) => file as File) .toList(); + + // Returns the directories containing mapped submodules, if any. + Future> _getSubmoduleDirectories() async { + final List submodulePaths = []; + final Directory repoRoot = + packagesDir.fileSystem.directory((await gitDir).path); + final File submoduleSpec = repoRoot.childFile('.gitmodules'); + if (submoduleSpec.existsSync()) { + final RegExp pathLine = RegExp(r'path\s*=\s*(.*)'); + for (final String line in submoduleSpec.readAsLinesSync()) { + final RegExpMatch? match = pathLine.firstMatch(line); + if (match != null) { + submodulePaths.add(repoRoot.childDirectory(match.group(1)!.trim())); + } + } + } + return submodulePaths; + } } enum _LicenseFailureType { incorrectFirstParty, unknownThirdParty } diff --git a/script/tool/lib/src/main.dart b/script/tool/lib/src/main.dart index aa1cf300bd2b..9c572ee270e0 100644 --- a/script/tool/lib/src/main.dart +++ b/script/tool/lib/src/main.dart @@ -28,6 +28,7 @@ import 'publish_plugin_command.dart'; import 'pubspec_check_command.dart'; import 'readme_check_command.dart'; import 'test_command.dart'; +import 'update_excerpts_command.dart'; import 'version_check_command.dart'; import 'xcode_analyze_command.dart'; @@ -68,6 +69,7 @@ void main(List args) { ..addCommand(PubspecCheckCommand(packagesDir)) ..addCommand(ReadmeCheckCommand(packagesDir)) ..addCommand(TestCommand(packagesDir)) + ..addCommand(UpdateExcerptsCommand(packagesDir)) ..addCommand(VersionCheckCommand(packagesDir)) ..addCommand(XcodeAnalyzeCommand(packagesDir)); diff --git a/script/tool/lib/src/update_excerpts_command.dart b/script/tool/lib/src/update_excerpts_command.dart new file mode 100644 index 000000000000..320a3c596323 --- /dev/null +++ b/script/tool/lib/src/update_excerpts_command.dart @@ -0,0 +1,225 @@ +// 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:io' as io; + +import 'package:file/file.dart'; +import 'package:flutter_plugin_tools/src/common/core.dart'; +import 'package:git/git.dart'; +import 'package:platform/platform.dart'; +import 'package:yaml/yaml.dart'; +import 'package:yaml_edit/yaml_edit.dart'; + +import 'common/package_looping_command.dart'; +import 'common/process_runner.dart'; +import 'common/repository_package.dart'; + +/// A command to update README code excerpts from code files. +class UpdateExcerptsCommand extends PackageLoopingCommand { + /// Creates a excerpt updater command instance. + UpdateExcerptsCommand( + Directory packagesDir, { + ProcessRunner processRunner = const ProcessRunner(), + Platform platform = const LocalPlatform(), + GitDir? gitDir, + }) : super( + packagesDir, + processRunner: processRunner, + platform: platform, + gitDir: gitDir, + ) { + argParser.addFlag(_failOnChangeFlag, hide: true); + } + + static const String _failOnChangeFlag = 'fail-on-change'; + + static const String _buildRunnerConfigName = 'excerpt'; + // The name of the build_runner configuration file that will be in an example + // directory if the package is set up to use `code-excerpt`. + static const String _buildRunnerConfigFile = + 'build.$_buildRunnerConfigName.yaml'; + + // The relative directory path to put the extracted excerpt yaml files. + static const String _excerptOutputDir = 'excerpts'; + + // The filename to store the pre-modification copy of the pubspec. + static const String _originalPubspecFilename = + 'pubspec.plugin_tools_original.yaml'; + + @override + final String name = 'update-excerpts'; + + @override + final String description = 'Updates code excerpts in README.md files, based ' + 'on code from code files, via code-excerpt'; + + @override + Future runForPackage(RepositoryPackage package) async { + final Iterable configuredExamples = package + .getExamples() + .where((RepositoryPackage example) => + example.directory.childFile(_buildRunnerConfigFile).existsSync()); + + if (configuredExamples.isEmpty) { + return PackageResult.skip( + 'No $_buildRunnerConfigFile found in example(s).'); + } + + final Directory repoRoot = + packagesDir.fileSystem.directory((await gitDir).path); + + for (final RepositoryPackage example in configuredExamples) { + _addSubmoduleDependencies(example, repoRoot: repoRoot); + + try { + // Ensure that dependencies are available. + final int pubGetExitCode = await processRunner.runAndStream( + 'dart', ['pub', 'get'], + workingDir: example.directory); + if (pubGetExitCode != 0) { + return PackageResult.fail( + ['Unable to get script dependencies']); + } + + // Update the excerpts. + if (!await _extractSnippets(example)) { + return PackageResult.fail(['Unable to extract excerpts']); + } + if (!await _injectSnippets(example, targetPackage: package)) { + return PackageResult.fail(['Unable to inject excerpts']); + } + } finally { + // Clean up the pubspec changes and extracted excerpts directory. + _undoPubspecChanges(example); + final Directory excerptDirectory = + example.directory.childDirectory(_excerptOutputDir); + if (excerptDirectory.existsSync()) { + excerptDirectory.deleteSync(recursive: true); + } + } + } + + if (getBoolArg(_failOnChangeFlag)) { + final String? stateError = await _validateRepositoryState(); + if (stateError != null) { + printError('README.md is out of sync with its source excerpts.\n\n' + 'If you edited code in README.md directly, you should instead edit ' + 'the example source files. If you edited source files, run the ' + 'repository tooling\'s "$name" command on this package, and update ' + 'your PR with the resulting changes.'); + return PackageResult.fail([stateError]); + } + } + + return PackageResult.success(); + } + + /// Runs the extraction step to create the excerpt files for the given + /// example, returning true on success. + Future _extractSnippets(RepositoryPackage example) async { + final int exitCode = await processRunner.runAndStream( + 'dart', + [ + 'run', + 'build_runner', + 'build', + '--config', + _buildRunnerConfigName, + '--output', + _excerptOutputDir, + '--delete-conflicting-outputs', + ], + workingDir: example.directory); + return exitCode == 0; + } + + /// Runs the injection step to update [targetPackage]'s README with the latest + /// excerpts from [example], returning true on success. + Future _injectSnippets( + RepositoryPackage example, { + required RepositoryPackage targetPackage, + }) async { + final String relativeReadmePath = + getRelativePosixPath(targetPackage.readmeFile, from: example.directory); + final int exitCode = await processRunner.runAndStream( + 'dart', + [ + 'run', + 'code_excerpt_updater', + '--write-in-place', + '--yaml', + '--no-escape-ng-interpolation', + relativeReadmePath, + ], + workingDir: example.directory); + return exitCode == 0; + } + + /// Adds `code_excerpter` and `code_excerpt_updater` to [package]'s + /// `dev_dependencies` using path-based references to the submodule copies. + /// + /// This is done on the fly rather than being checked in so that: + /// - Just building examples don't require everyone to check out submodules. + /// - Examples can be analyzed/built even on versions of Flutter that these + /// submodules do not support. + void _addSubmoduleDependencies(RepositoryPackage package, + {required Directory repoRoot}) { + final String pubspecContents = package.pubspecFile.readAsStringSync(); + // Save aside a copy of the current pubspec state. This allows restoration + // to the previous state regardless of its git status at the time the script + // ran. + package.directory + .childFile(_originalPubspecFilename) + .writeAsStringSync(pubspecContents); + + // Update the actual pubspec. + final YamlEditor editablePubspec = YamlEditor(pubspecContents); + const String devDependenciesKey = 'dev_dependencies'; + final YamlNode root = editablePubspec.parseAt([]); + // Ensure that there's a `dev_dependencies` entry to update. + if ((root as YamlMap)[devDependenciesKey] == null) { + editablePubspec.update(['dev_dependencies'], YamlMap()); + } + final Set submoduleDependencies = { + 'code_excerpter', + 'code_excerpt_updater', + }; + final String relativeRootPath = + getRelativePosixPath(repoRoot, from: package.directory); + for (final String dependency in submoduleDependencies) { + editablePubspec.update([ + devDependenciesKey, + dependency + ], { + 'path': '$relativeRootPath/site-shared/packages/$dependency' + }); + } + package.pubspecFile.writeAsStringSync(editablePubspec.toString()); + } + + /// Restores the version of the pubspec that was present before running + /// [_addSubmoduleDependencies]. + void _undoPubspecChanges(RepositoryPackage package) { + package.directory + .childFile(_originalPubspecFilename) + .renameSync(package.pubspecFile.path); + } + + /// Checks the git state, returning an error string unless nothing has + /// changed. + Future _validateRepositoryState() async { + final io.ProcessResult modifiedFiles = await processRunner.run( + 'git', + ['ls-files', '--modified'], + workingDir: packagesDir, + logOnError: true, + ); + if (modifiedFiles.exitCode != 0) { + return 'Unable to determine local file state'; + } + + final String stdout = modifiedFiles.stdout as String; + return stdout.trim().isEmpty ? null : 'Snippets are out of sync'; + } +} diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 14b22a16e3fb..9f9910f934f7 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.8.2+1 +version: 0.8.3 dependencies: args: ^2.1.0 @@ -21,6 +21,7 @@ dependencies: test: ^1.17.3 uuid: ^3.0.4 yaml: ^3.1.0 + yaml_edit: ^2.0.2 dev_dependencies: build_runner: ^2.0.3 diff --git a/script/tool/test/format_command_test.dart b/script/tool/test/format_command_test.dart index 2890c528e4c1..6c10a7dc3209 100644 --- a/script/tool/test/format_command_test.dart +++ b/script/tool/test/format_command_test.dart @@ -448,7 +448,7 @@ void main() { ])); }); - test('fails if files are changed with --file-on-change', () async { + test('fails if files are changed with --fail-on-change', () async { const List files = [ 'linux/foo_plugin.cc', 'macos/Classes/Foo.h', diff --git a/script/tool/test/license_check_command_test.dart b/script/tool/test/license_check_command_test.dart index e97274afd09e..efaf969c83fb 100644 --- a/script/tool/test/license_check_command_test.dart +++ b/script/tool/test/license_check_command_test.dart @@ -7,24 +7,35 @@ import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_plugin_tools/src/common/core.dart'; import 'package:flutter_plugin_tools/src/license_check_command.dart'; +import 'package:mockito/mockito.dart'; +import 'package:platform/platform.dart'; import 'package:test/test.dart'; +import 'common/plugin_command_test.mocks.dart'; +import 'mocks.dart'; import 'util.dart'; void main() { - group('$LicenseCheckCommand', () { + group('LicenseCheckCommand', () { late CommandRunner runner; late FileSystem fileSystem; + late Platform platform; late Directory root; setUp(() { fileSystem = MemoryFileSystem(); + platform = MockPlatformWithSeparator(); final Directory packagesDir = fileSystem.currentDirectory.childDirectory('packages'); root = packagesDir.parent; + final MockGitDir gitDir = MockGitDir(); + when(gitDir.path).thenReturn(packagesDir.parent.path); + final LicenseCheckCommand command = LicenseCheckCommand( packagesDir, + platform: platform, + gitDir: gitDir, ); runner = CommandRunner('license_test', 'Test for $LicenseCheckCommand'); @@ -123,6 +134,33 @@ void main() { } }); + test('ignores submodules', () async { + const String submoduleName = 'a_submodule'; + + final File submoduleSpec = root.childFile('.gitmodules'); + submoduleSpec.writeAsStringSync(''' +[submodule "$submoduleName"] + path = $submoduleName + url = https://github.com/foo/$submoduleName +'''); + + const List submoduleFiles = [ + '$submoduleName/foo.dart', + '$submoduleName/a/b/bar.dart', + '$submoduleName/LICENSE', + ]; + for (final String filePath in submoduleFiles) { + root.childFile(filePath).createSync(recursive: true); + } + + final List output = + await runCapturingPrint(runner, ['license-check']); + + for (final String filePath in submoduleFiles) { + expect(output, isNot(contains('Checking $filePath'))); + } + }); + test('passes if all checked files have license blocks', () async { final File checked = root.childFile('checked.cc'); checked.createSync(); @@ -509,6 +547,11 @@ void main() { }); } +class MockPlatformWithSeparator extends MockPlatform { + @override + String get pathSeparator => isWindows ? r'\' : '/'; +} + const String _correctLicenseFileText = ''' Copyright 2013 The Flutter Authors. All rights reserved. diff --git a/script/tool/test/update_excerpts_command_test.dart b/script/tool/test/update_excerpts_command_test.dart new file mode 100644 index 000000000000..30189cf23a00 --- /dev/null +++ b/script/tool/test/update_excerpts_command_test.dart @@ -0,0 +1,284 @@ +// 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:io' as io; + +import 'package:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_plugin_tools/src/common/core.dart'; +import 'package:flutter_plugin_tools/src/common/repository_package.dart'; +import 'package:flutter_plugin_tools/src/update_excerpts_command.dart'; +import 'package:mockito/mockito.dart'; +import 'package:test/test.dart'; + +import 'common/plugin_command_test.mocks.dart'; +import 'mocks.dart'; +import 'util.dart'; + +void main() { + late FileSystem fileSystem; + late Directory packagesDir; + late RecordingProcessRunner processRunner; + late CommandRunner runner; + + setUp(() { + fileSystem = MemoryFileSystem(); + packagesDir = createPackagesDirectory(fileSystem: fileSystem); + final MockGitDir gitDir = MockGitDir(); + when(gitDir.path).thenReturn(packagesDir.parent.path); + processRunner = RecordingProcessRunner(); + final UpdateExcerptsCommand command = UpdateExcerptsCommand( + packagesDir, + processRunner: processRunner, + platform: MockPlatform(), + gitDir: gitDir, + ); + + runner = CommandRunner( + 'update_excerpts_command', 'Test for update_excerpts_command'); + runner.addCommand(command); + }); + + test('runs pub get before running scripts', () async { + final Directory package = createFakePlugin('a_package', packagesDir, + extraFiles: ['example/build.excerpt.yaml']); + final Directory example = package.childDirectory('example'); + + await runCapturingPrint(runner, ['update-excerpts']); + + expect( + processRunner.recordedCalls, + containsAll([ + ProcessCall('dart', const ['pub', 'get'], example.path), + ProcessCall( + 'dart', + const [ + 'run', + 'build_runner', + 'build', + '--config', + 'excerpt', + '--output', + 'excerpts', + '--delete-conflicting-outputs', + ], + example.path), + ])); + }); + + test('runs when config is present', () async { + final Directory package = createFakePlugin('a_package', packagesDir, + extraFiles: ['example/build.excerpt.yaml']); + final Directory example = package.childDirectory('example'); + + final List output = + await runCapturingPrint(runner, ['update-excerpts']); + + expect( + processRunner.recordedCalls, + containsAll([ + ProcessCall( + 'dart', + const [ + 'run', + 'build_runner', + 'build', + '--config', + 'excerpt', + '--output', + 'excerpts', + '--delete-conflicting-outputs', + ], + example.path), + ProcessCall( + 'dart', + const [ + 'run', + 'code_excerpt_updater', + '--write-in-place', + '--yaml', + '--no-escape-ng-interpolation', + '../README.md', + ], + example.path), + ])); + + expect( + output, + containsAllInOrder([ + contains('Ran for 1 package(s)'), + ])); + }); + + test('skips when no config is present', () async { + createFakePlugin('a_package', packagesDir); + + final List output = + await runCapturingPrint(runner, ['update-excerpts']); + + expect(processRunner.recordedCalls, isEmpty); + + expect( + output, + containsAllInOrder([ + contains('Skipped 1 package(s)'), + ])); + }); + + test('restores pubspec even if running the script fails', () async { + final Directory package = createFakePlugin('a_package', packagesDir, + extraFiles: ['example/build.excerpt.yaml']); + + processRunner.mockProcessesForExecutable['dart'] = [ + MockProcess(exitCode: 1), // dart pub get + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['update-excerpts'], errorHandler: (Error e) { + commandError = e; + }); + + // Check that it's definitely a failure in a step between making the changes + // and restoring the original. + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The following packages had errors:'), + contains('a_package:\n' + ' Unable to get script dependencies') + ])); + + final String examplePubspecContent = RepositoryPackage(package) + .getExamples() + .first + .pubspecFile + .readAsStringSync(); + expect(examplePubspecContent, isNot(contains('code_excerpter'))); + expect(examplePubspecContent, isNot(contains('code_excerpt_updater'))); + }); + + test('fails if pub get fails', () async { + createFakePlugin('a_package', packagesDir, + extraFiles: ['example/build.excerpt.yaml']); + + processRunner.mockProcessesForExecutable['dart'] = [ + MockProcess(exitCode: 1), // dart pub get + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['update-excerpts'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The following packages had errors:'), + contains('a_package:\n' + ' Unable to get script dependencies') + ])); + }); + + test('fails if extraction fails', () async { + createFakePlugin('a_package', packagesDir, + extraFiles: ['example/build.excerpt.yaml']); + + processRunner.mockProcessesForExecutable['dart'] = [ + MockProcess(exitCode: 0), // dart pub get + MockProcess(exitCode: 1), // dart run build_runner ... + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['update-excerpts'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The following packages had errors:'), + contains('a_package:\n' + ' Unable to extract excerpts') + ])); + }); + + test('fails if injection fails', () async { + createFakePlugin('a_package', packagesDir, + extraFiles: ['example/build.excerpt.yaml']); + + processRunner.mockProcessesForExecutable['dart'] = [ + MockProcess(exitCode: 0), // dart pub get + MockProcess(exitCode: 0), // dart run build_runner ... + MockProcess(exitCode: 1), // dart run code_excerpt_updater ... + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['update-excerpts'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The following packages had errors:'), + contains('a_package:\n' + ' Unable to inject excerpts') + ])); + }); + + test('fails if files are changed with --fail-on-change', () async { + createFakePlugin('a_plugin', packagesDir, + extraFiles: ['example/build.excerpt.yaml']); + + const String changedFilePath = 'packages/a_plugin/linux/foo_plugin.cc'; + processRunner.mockProcessesForExecutable['git'] = [ + MockProcess(stdout: changedFilePath), + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['update-excerpts', '--fail-on-change'], + errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('README.md is out of sync with its source excerpts'), + ])); + }); + + test('fails if git ls-files fails', () async { + createFakePlugin('a_plugin', packagesDir, + extraFiles: ['example/build.excerpt.yaml']); + + processRunner.mockProcessesForExecutable['git'] = [ + MockProcess(exitCode: 1) + ]; + Error? commandError; + final List output = await runCapturingPrint( + runner, ['update-excerpts', '--fail-on-change'], + errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Unable to determine local file state'), + ])); + }); +} diff --git a/site-shared b/site-shared new file mode 160000 index 000000000000..142de133477b --- /dev/null +++ b/site-shared @@ -0,0 +1 @@ +Subproject commit 142de133477bdede1746f992e656c4b43c4c7442 From c09ef582e3455fa0988296eda33d4cb80b761cb7 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 28 Apr 2022 13:19:12 -0400 Subject: [PATCH 201/844] [flutter_plugin_tools] Remove UWP (#5432) --- .ci/scripts/build_examples_uwp.sh | 7 -- .ci/targets/uwp_build_and_platform_tests.yaml | 5 -- script/tool/CHANGELOG.md | 1 + .../tool/lib/src/build_examples_command.dart | 48 ++-------- script/tool/lib/src/common/core.dart | 19 ---- script/tool/lib/src/common/plugin_utils.dart | 21 ----- .../tool/lib/src/drive_examples_command.dart | 24 +---- .../test/build_examples_command_test.dart | 90 +------------------ .../tool/test/common/plugin_utils_test.dart | 79 ---------------- .../test/drive_examples_command_test.dart | 34 ------- script/tool/test/util.dart | 16 ---- 11 files changed, 12 insertions(+), 332 deletions(-) delete mode 100644 .ci/scripts/build_examples_uwp.sh delete mode 100644 .ci/targets/uwp_build_and_platform_tests.yaml diff --git a/.ci/scripts/build_examples_uwp.sh b/.ci/scripts/build_examples_uwp.sh deleted file mode 100644 index 04b8256891bd..000000000000 --- a/.ci/scripts/build_examples_uwp.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -# 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. - -dart ./script/tool/bin/flutter_plugin_tools.dart build-examples --winuwp \ - --packages-for-branch --log-timing diff --git a/.ci/targets/uwp_build_and_platform_tests.yaml b/.ci/targets/uwp_build_and_platform_tests.yaml deleted file mode 100644 index a7f070776ff1..000000000000 --- a/.ci/targets/uwp_build_and_platform_tests.yaml +++ /dev/null @@ -1,5 +0,0 @@ -tasks: - - name: prepare tool - script: .ci/scripts/prepare_tool.sh - - name: build examples (UWP) - script: .ci/scripts/build_examples_uwp.sh diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 82a31154e132..7587ff33f027 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -5,6 +5,7 @@ - `license-check` now ignores submodules. - Allows `make-deps-path-based` to skip packages it has alredy rewritten, so that running multiple times won't fail after the first time. +- Removes UWP support, since Flutter has dropped support for UWP. ## 0.8.2+1 diff --git a/script/tool/lib/src/build_examples_command.dart b/script/tool/lib/src/build_examples_command.dart index b88cfe309258..1aade3575559 100644 --- a/script/tool/lib/src/build_examples_command.dart +++ b/script/tool/lib/src/build_examples_command.dart @@ -37,8 +37,7 @@ const String _flutterBuildTypeIOS = 'ios'; const String _flutterBuildTypeLinux = 'linux'; const String _flutterBuildTypeMacOS = 'macos'; const String _flutterBuildTypeWeb = 'web'; -const String _flutterBuildTypeWin32 = 'windows'; -const String _flutterBuildTypeWinUwp = 'winuwp'; +const String _flutterBuildTypeWindows = 'windows'; /// A command to build the example applications for packages. class BuildExamplesCommand extends PackageLoopingCommand { @@ -52,7 +51,6 @@ class BuildExamplesCommand extends PackageLoopingCommand { argParser.addFlag(platformMacOS); argParser.addFlag(platformWeb); argParser.addFlag(platformWindows); - argParser.addFlag(platformWinUwp); argParser.addFlag(platformIOS); argParser.addFlag(_platformFlagApk); argParser.addOption( @@ -93,16 +91,9 @@ class BuildExamplesCommand extends PackageLoopingCommand { flutterBuildType: _flutterBuildTypeWeb, ), platformWindows: const _PlatformDetails( - 'Win32', + 'Windows', pluginPlatform: platformWindows, - pluginPlatformVariant: platformVariantWin32, - flutterBuildType: _flutterBuildTypeWin32, - ), - platformWinUwp: const _PlatformDetails( - 'UWP', - pluginPlatform: platformWindows, - pluginPlatformVariant: platformVariantWinUwp, - flutterBuildType: _flutterBuildTypeWinUwp, + flutterBuildType: _flutterBuildTypeWindows, ), }; @@ -146,9 +137,8 @@ class BuildExamplesCommand extends PackageLoopingCommand { // no package-level platform information for non-plugin packages. final Set<_PlatformDetails> buildPlatforms = isPlugin ? requestedPlatforms - .where((_PlatformDetails platform) => pluginSupportsPlatform( - platform.pluginPlatform, package, - variant: platform.pluginPlatformVariant)) + .where((_PlatformDetails platform) => + pluginSupportsPlatform(platform.pluginPlatform, package)) .toSet() : requestedPlatforms.toSet(); @@ -280,22 +270,6 @@ class BuildExamplesCommand extends PackageLoopingCommand { }) async { final String enableExperiment = getStringArg(kEnableExperiment); - // The UWP template is not yet stable, so the UWP directory - // needs to be created on the fly with 'flutter create .' - Directory? temporaryPlatformDirectory; - if (flutterBuildType == _flutterBuildTypeWinUwp) { - final Directory uwpDirectory = example.directory.childDirectory('winuwp'); - if (!uwpDirectory.existsSync()) { - print('Creating temporary winuwp folder'); - final int exitCode = await processRunner.runAndStream(flutterCommand, - ['create', '--platforms=$platformWinUwp', '.'], - workingDir: example.directory); - if (exitCode == 0) { - temporaryPlatformDirectory = uwpDirectory; - } - } - } - final int exitCode = await processRunner.runAndStream( flutterCommand, [ @@ -308,13 +282,6 @@ class BuildExamplesCommand extends PackageLoopingCommand { ], workingDir: example.directory, ); - - if (temporaryPlatformDirectory != null && - temporaryPlatformDirectory.existsSync()) { - print('Cleaning up ${temporaryPlatformDirectory.path}'); - temporaryPlatformDirectory.deleteSync(recursive: true); - } - return exitCode == 0; } } @@ -324,7 +291,6 @@ class _PlatformDetails { const _PlatformDetails( this.label, { required this.pluginPlatform, - this.pluginPlatformVariant, required this.flutterBuildType, this.extraBuildFlags = const [], }); @@ -335,10 +301,6 @@ class _PlatformDetails { /// The key in a pubspec's platform: entry. final String pluginPlatform; - /// The supportedVariants key under a plugin's [pluginPlatform] entry, if - /// applicable. - final String? pluginPlatformVariant; - /// The `flutter build` build type. final String flutterBuildType; diff --git a/script/tool/lib/src/common/core.dart b/script/tool/lib/src/common/core.dart index 15a0d6f1f3b2..de1cefd7225a 100644 --- a/script/tool/lib/src/common/core.dart +++ b/script/tool/lib/src/common/core.dart @@ -26,27 +26,8 @@ const String platformMacOS = 'macos'; const String platformWeb = 'web'; /// Key for windows platform. -/// -/// Note that this corresponds to the Win32 variant for flutter commands like -/// `build` and `run`, but is a general platform containing all Windows -/// variants for purposes of the `platform` section of a plugin pubspec). const String platformWindows = 'windows'; -/// Key for WinUWP platform. -/// -/// Note that UWP is a platform for the purposes of flutter commands like -/// `build` and `run`, but a variant of the `windows` platform for the purposes -/// of plugin pubspecs). -const String platformWinUwp = 'winuwp'; - -/// Key for Win32 variant of the Windows platform. -const String platformVariantWin32 = 'win32'; - -/// Key for UWP variant of the Windows platform. -/// -/// See the note on [platformWinUwp]. -const String platformVariantWinUwp = 'uwp'; - /// Key for enable experiment. const String kEnableExperiment = 'enable-experiment'; diff --git a/script/tool/lib/src/common/plugin_utils.dart b/script/tool/lib/src/common/plugin_utils.dart index 081ce7f1e815..94f294ebd964 100644 --- a/script/tool/lib/src/common/plugin_utils.dart +++ b/script/tool/lib/src/common/plugin_utils.dart @@ -37,7 +37,6 @@ bool pluginSupportsPlatform( String platform, RepositoryPackage plugin, { PlatformSupport? requiredMode, - String? variant, }) { assert(platform == platformIOS || platform == platformAndroid || @@ -61,26 +60,6 @@ bool pluginSupportsPlatform( } } - // If a variant is specified, check for that variant. - if (variant != null) { - const String variantsKey = 'supportedVariants'; - if (platformEntry.containsKey(variantsKey)) { - if (!(platformEntry['supportedVariants']! as YamlList) - .contains(variant)) { - return false; - } - } else { - // Platforms with variants have a default variant when unspecified for - // backward compatibility. Must match the flutter tool logic. - const Map defaultVariants = { - platformWindows: platformVariantWin32, - }; - if (variant != defaultVariants[platform]) { - return false; - } - } - } - return true; } diff --git a/script/tool/lib/src/drive_examples_command.dart b/script/tool/lib/src/drive_examples_command.dart index d81153a0fefa..0e1efa842eda 100644 --- a/script/tool/lib/src/drive_examples_command.dart +++ b/script/tool/lib/src/drive_examples_command.dart @@ -36,10 +36,7 @@ class DriveExamplesCommand extends PackageLoopingCommand { argParser.addFlag(platformWeb, help: 'Runs the web implementation of the examples'); argParser.addFlag(platformWindows, - help: 'Runs the Windows (Win32) implementation of the examples'); - argParser.addFlag(platformWinUwp, - help: - 'Runs the UWP implementation of the examples [currently a no-op]'); + help: 'Runs the Windows implementation of the examples'); argParser.addOption( kEnableExperiment, defaultsTo: '', @@ -70,7 +67,6 @@ class DriveExamplesCommand extends PackageLoopingCommand { platformMacOS, platformWeb, platformWindows, - platformWinUwp, ]; final int platformCount = platformSwitches .where((String platform) => getBoolArg(platform)) @@ -85,10 +81,6 @@ class DriveExamplesCommand extends PackageLoopingCommand { throw ToolExit(_exitNoPlatformFlags); } - if (getBoolArg(platformWinUwp)) { - logWarning('Driving UWP applications is not yet supported'); - } - String? androidDevice; if (getBoolArg(platformAndroid)) { final List devices = await _getDevicesForPlatform('android'); @@ -126,9 +118,6 @@ class DriveExamplesCommand extends PackageLoopingCommand { ], if (getBoolArg(platformWindows)) platformWindows: ['-d', 'windows'], - // TODO(stuartmorgan): Check these flags once drive supports UWP: - // https://github.com/flutter/flutter/issues/82821 - if (getBoolArg(platformWinUwp)) platformWinUwp: ['-d', 'winuwp'], }; } @@ -146,16 +135,7 @@ class DriveExamplesCommand extends PackageLoopingCommand { for (final MapEntry> entry in _targetDeviceFlags.entries) { final String platform = entry.key; - String? variant; - if (platform == platformWindows) { - variant = platformVariantWin32; - } else if (platform == platformWinUwp) { - variant = platformVariantWinUwp; - // TODO(stuartmorgan): Remove this once drive supports UWP. - // https://github.com/flutter/flutter/issues/82821 - return PackageResult.skip('Drive does not yet support UWP'); - } - if (pluginSupportsPlatform(platform, package, variant: variant)) { + if (pluginSupportsPlatform(platform, package)) { deviceFlags.addAll(entry.value); } else { print('Skipping unsupported platform ${entry.key}...'); diff --git a/script/tool/test/build_examples_command_test.dart b/script/tool/test/build_examples_command_test.dart index 29a879071657..2bdb1bc0c2ba 100644 --- a/script/tool/test/build_examples_command_test.dart +++ b/script/tool/test/build_examples_command_test.dart @@ -313,7 +313,7 @@ void main() { }); test( - 'building for win32 when plugin is not set up for Windows results in no-op', + 'building for Windows when plugin is not set up for Windows results in no-op', () async { mockPlatform.isWindows = true; createFakePlugin('plugin', packagesDir); @@ -325,7 +325,7 @@ void main() { output, containsAllInOrder([ contains('Running for plugin'), - contains('Win32 is not supported by this plugin'), + contains('Windows is not supported by this plugin'), ]), ); @@ -334,7 +334,7 @@ void main() { expect(processRunner.recordedCalls, orderedEquals([])); }); - test('building for win32', () async { + test('building for Windows', () async { mockPlatform.isWindows = true; final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, platformSupport: { @@ -350,7 +350,7 @@ void main() { expect( output, containsAllInOrder([ - '\nBUILDING plugin/example for Win32 (windows)', + '\nBUILDING plugin/example for Windows', ]), ); @@ -364,88 +364,6 @@ void main() { ])); }); - test('building for UWP when plugin does not support UWP is a no-op', - () async { - createFakePlugin('plugin', packagesDir); - - final List output = await runCapturingPrint( - runner, ['build-examples', '--winuwp']); - - expect( - output, - containsAllInOrder([ - contains('Running for plugin'), - contains('UWP is not supported by this plugin'), - ]), - ); - - // Output should be empty since running build-examples --macos with no macos - // implementation is a no-op. - expect(processRunner.recordedCalls, orderedEquals([])); - }); - - test('building for UWP', () async { - final Directory pluginDirectory = - createFakePlugin('plugin', packagesDir, extraFiles: [ - 'example/test', - ], platformSupport: { - platformWindows: const PlatformDetails(PlatformSupport.federated, - variants: [platformVariantWinUwp]), - }); - - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); - - final List output = await runCapturingPrint( - runner, ['build-examples', '--winuwp']); - - expect( - output, - containsAllInOrder([ - contains('BUILDING plugin/example for UWP (winuwp)'), - ]), - ); - - expect( - processRunner.recordedCalls, - containsAll([ - ProcessCall(getFlutterCommand(mockPlatform), - const ['build', 'winuwp'], pluginExampleDirectory.path), - ])); - }); - - test('building for UWP creates a folder if necessary', () async { - final Directory pluginDirectory = - createFakePlugin('plugin', packagesDir, extraFiles: [ - 'example/test', - ], platformSupport: { - platformWindows: const PlatformDetails(PlatformSupport.federated, - variants: [platformVariantWinUwp]), - }); - - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); - - final List output = await runCapturingPrint( - runner, ['build-examples', '--winuwp']); - - expect( - output, - contains('Creating temporary winuwp folder'), - ); - - expect( - processRunner.recordedCalls, - orderedEquals([ - ProcessCall( - getFlutterCommand(mockPlatform), - const ['create', '--platforms=winuwp', '.'], - pluginExampleDirectory.path), - ProcessCall(getFlutterCommand(mockPlatform), - const ['build', 'winuwp'], pluginExampleDirectory.path), - ])); - }); - test( 'building for Android when plugin is not set up for Android results in no-op', () async { diff --git a/script/tool/test/common/plugin_utils_test.dart b/script/tool/test/common/plugin_utils_test.dart index cedd40acb7d6..af5cb7dfe4c6 100644 --- a/script/tool/test/common/plugin_utils_test.dart +++ b/script/tool/test/common/plugin_utils_test.dart @@ -193,85 +193,6 @@ void main() { requiredMode: PlatformSupport.inline), isFalse); }); - - test('windows without variants is only win32', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( - 'plugin', - packagesDir, - platformSupport: { - platformWindows: const PlatformDetails(PlatformSupport.inline), - }, - )); - - expect( - pluginSupportsPlatform(platformWindows, plugin, - variant: platformVariantWin32), - isTrue); - expect( - pluginSupportsPlatform(platformWindows, plugin, - variant: platformVariantWinUwp), - isFalse); - }); - - test('windows with both variants matches win32 and winuwp', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( - 'plugin', packagesDir, - platformSupport: { - platformWindows: const PlatformDetails( - PlatformSupport.federated, - variants: [platformVariantWin32, platformVariantWinUwp], - ), - })); - - expect( - pluginSupportsPlatform(platformWindows, plugin, - variant: platformVariantWin32), - isTrue); - expect( - pluginSupportsPlatform(platformWindows, plugin, - variant: platformVariantWinUwp), - isTrue); - }); - - test('win32 plugin is only win32', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( - 'plugin', packagesDir, - platformSupport: { - platformWindows: const PlatformDetails( - PlatformSupport.federated, - variants: [platformVariantWin32], - ), - })); - - expect( - pluginSupportsPlatform(platformWindows, plugin, - variant: platformVariantWin32), - isTrue); - expect( - pluginSupportsPlatform(platformWindows, plugin, - variant: platformVariantWinUwp), - isFalse); - }); - - test('winup plugin is only winuwp', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( - 'plugin', - packagesDir, - platformSupport: { - platformWindows: const PlatformDetails(PlatformSupport.federated, - variants: [platformVariantWinUwp]), - }, - )); - - expect( - pluginSupportsPlatform(platformWindows, plugin, - variant: platformVariantWin32), - isFalse); - expect( - pluginSupportsPlatform(platformWindows, plugin, - variant: platformVariantWinUwp), - isTrue); - }); }); group('pluginHasNativeCodeForPlatform', () { diff --git a/script/tool/test/drive_examples_command_test.dart b/script/tool/test/drive_examples_command_test.dart index 9372c571b6f7..ac57eb7251a2 100644 --- a/script/tool/test/drive_examples_command_test.dart +++ b/script/tool/test/drive_examples_command_test.dart @@ -708,40 +708,6 @@ void main() { ])); }); - test('driving UWP is a no-op', () async { - createFakePlugin( - 'plugin', - packagesDir, - extraFiles: [ - 'example/test_driver/plugin_test.dart', - 'example/test_driver/plugin.dart', - ], - platformSupport: { - platformWindows: const PlatformDetails(PlatformSupport.inline, - variants: [platformVariantWinUwp]), - }, - ); - - final List output = await runCapturingPrint(runner, [ - 'drive-examples', - '--winuwp', - ]); - - expect( - output, - containsAllInOrder([ - contains('Driving UWP applications is not yet supported'), - contains('Running for plugin'), - contains('SKIPPING: Drive does not yet support UWP'), - contains('No issues found!'), - ]), - ); - - // Output should be empty since running drive-examples --windows on a - // non-Windows plugin is a no-op. - expect(processRunner.recordedCalls, []); - }); - test('driving on an Android plugin', () async { final Directory pluginDirectory = createFakePlugin( 'plugin', diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index fab4f39cc10f..2b1719ee8000 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -47,7 +47,6 @@ Directory createPackagesDirectory( class PlatformDetails { const PlatformDetails( this.type, { - this.variants = const [], this.hasNativeCode = true, this.hasDartCode = false, }); @@ -55,9 +54,6 @@ class PlatformDetails { /// The type of support for the platform. final PlatformSupport type; - /// Any 'supportVariants' to list in the pubspec. - final List variants; - /// Whether or not the plugin includes native code. /// /// Ignored for web, which does not have native code. @@ -293,18 +289,6 @@ String _pluginPlatformSection( entry = lines.join('\n') + '\n'; } - // Add any variants. - if (support.variants.isNotEmpty) { - entry += ''' - supportedVariants: -'''; - for (final String variant in support.variants) { - entry += ''' - - $variant -'''; - } - } - return entry; } From 3876f54bf2ad091f6478995da5628e08034ae968 Mon Sep 17 00:00:00 2001 From: Casey Hillers Date: Thu, 28 Apr 2022 11:34:10 -0700 Subject: [PATCH 202/844] [ci.yaml] Remove explicit caches (#5434) --- .ci.yaml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.ci.yaml b/.ci.yaml index 14772f475af6..a8fa7a5a1577 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -11,9 +11,6 @@ enabled_branches: platform_properties: linux: properties: - caches: >- - [ - ] dependencies: > [ {"dependency": "curl"} @@ -22,11 +19,6 @@ platform_properties: os: Linux windows: properties: - caches: >- - [ - {"name": "vsbuild", "path": "vsbuild"}, - {"name": "pub_cache", "path": ".pub-cache"} - ] dependencies: > [ {"dependency": "certs"} From 0605d876dc6bbb99cbf94335765d3b2c7ceaeb2a Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 28 Apr 2022 13:30:17 -0700 Subject: [PATCH 203/844] [webview_flutter_android] Updates `pigeon` version to support null safety (#5395) --- .../webview_flutter_android/CHANGELOG.md | 4 + .../webview_flutter_android/README.md | 8 +- .../GeneratedAndroidWebView.java | 563 ++++++++++++------ .../WebViewClientFlutterApiImpl.java | 40 +- .../webviewflutter/WebViewHostApiImpl.java | 30 +- .../webviewflutter/WebViewClientTest.java | 22 + .../plugins/webviewflutter/WebViewTest.java | 10 +- .../generatePigeons.sh | 10 - .../lib/src/android_webview.dart | 60 +- .../lib/src/android_webview.pigeon.dart | 275 ++++----- .../lib/src/android_webview_api_impls.dart | 47 +- .../pigeons/android_webview.dart | 71 ++- .../webview_flutter_android/pubspec.yaml | 4 +- .../test/android_webview_test.dart | 44 +- .../test/android_webview_test.mocks.dart | 17 +- ....dart => test_android_webview.pigeon.dart} | 62 +- .../test/webview_android_widget_test.dart | 2 +- 17 files changed, 722 insertions(+), 547 deletions(-) delete mode 100755 packages/webview_flutter/webview_flutter_android/generatePigeons.sh rename packages/webview_flutter/webview_flutter_android/test/{android_webview.pigeon.dart => test_android_webview.pigeon.dart} (95%) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index fb5a2b85ffed..c9d023987bb1 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.6 + +* Updates pigeon developer dependency to the latest version which adds support for null safety. + ## 2.8.5 * Migrates deprecated `Scaffold.showSnackBar` to `ScaffoldMessenger` in example app. diff --git a/packages/webview_flutter/webview_flutter_android/README.md b/packages/webview_flutter/webview_flutter_android/README.md index 020f1fcfbbf1..04cbde292356 100644 --- a/packages/webview_flutter/webview_flutter_android/README.md +++ b/packages/webview_flutter/webview_flutter_android/README.md @@ -9,7 +9,10 @@ normally. This package will be automatically included in your app when you do. ## Contributing -This package uses [pigeon][3] to generate the communication layer between Flutter and the host platform (Android). The communication interface is defined in the `pigeons/android_webview.dart` file. After editing the communication interface regenerate the communication layer by running the `./generatePigeons.sh` shell script. +This package uses [pigeon][3] to generate the communication layer between Flutter and the host +platform (Android). The communication interface is defined in the `pigeons/android_webview.dart` +file. After editing the communication interface regenerate the communication layer by running +`flutter pub run pigeon --input pigeons/android_webview.dart`. Due to [flutter/flutter#97744](https://github.com/flutter/flutter/issues/97744), the generated test pigeon file needs one of its imports updated to properly work with `mockito`. @@ -26,7 +29,8 @@ to import 'package:webview_flutter_android/src/android_webview.pigeon.dart'; ``` -Besides [pigeon][3] this package also uses [mockito][4] to generate mock objects for testing purposes. To generate the mock objects run the following command: +Besides [pigeon][3] this package also uses [mockito][4] to generate mock objects for testing +purposes. To generate the mock objects run the following command: ```bash flutter packages pub run build_runner build --delete-conflicting-outputs ``` 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 afca5ee12747..2e163311d6d4 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 @@ -1,12 +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. - -// Autogenerated from Pigeon (v1.0.9), do not edit directly. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.webviewflutter; +import android.util.Log; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import io.flutter.plugin.common.BasicMessageChannel; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MessageCodec; @@ -25,66 +27,140 @@ public class GeneratedAndroidWebView { /** Generated class from Pigeon that represents data sent in messages. */ public static class WebResourceRequestData { - private String url; + private @NonNull String url; - public String getUrl() { + public @NonNull String getUrl() { return url; } - public void setUrl(String setterArg) { + public void setUrl(@NonNull String setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"url\" is null."); + } this.url = setterArg; } - private Boolean isForMainFrame; + private @NonNull Boolean isForMainFrame; - public Boolean getIsForMainFrame() { + public @NonNull Boolean getIsForMainFrame() { return isForMainFrame; } - public void setIsForMainFrame(Boolean setterArg) { + public void setIsForMainFrame(@NonNull Boolean setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"isForMainFrame\" is null."); + } this.isForMainFrame = setterArg; } - private Boolean isRedirect; + private @Nullable Boolean isRedirect; - public Boolean getIsRedirect() { + public @Nullable Boolean getIsRedirect() { return isRedirect; } - public void setIsRedirect(Boolean setterArg) { + public void setIsRedirect(@Nullable Boolean setterArg) { this.isRedirect = setterArg; } - private Boolean hasGesture; + private @NonNull Boolean hasGesture; - public Boolean getHasGesture() { + public @NonNull Boolean getHasGesture() { return hasGesture; } - public void setHasGesture(Boolean setterArg) { + public void setHasGesture(@NonNull Boolean setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"hasGesture\" is null."); + } this.hasGesture = setterArg; } - private String method; + private @NonNull String method; - public String getMethod() { + public @NonNull String getMethod() { return method; } - public void setMethod(String setterArg) { + public void setMethod(@NonNull String setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"method\" is null."); + } this.method = setterArg; } - private Map requestHeaders; + private @NonNull Map requestHeaders; - public Map getRequestHeaders() { + public @NonNull Map getRequestHeaders() { return requestHeaders; } - public void setRequestHeaders(Map setterArg) { + public void setRequestHeaders(@NonNull Map setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"requestHeaders\" is null."); + } this.requestHeaders = setterArg; } + /** Constructor is private to enforce null safety; use Builder. */ + private WebResourceRequestData() {} + + public static final class Builder { + private @Nullable String url; + + public @NonNull Builder setUrl(@NonNull String setterArg) { + this.url = setterArg; + return this; + } + + private @Nullable Boolean isForMainFrame; + + public @NonNull Builder setIsForMainFrame(@NonNull Boolean setterArg) { + this.isForMainFrame = setterArg; + return this; + } + + private @Nullable Boolean isRedirect; + + public @NonNull Builder setIsRedirect(@Nullable Boolean setterArg) { + this.isRedirect = setterArg; + return this; + } + + private @Nullable Boolean hasGesture; + + public @NonNull Builder setHasGesture(@NonNull Boolean setterArg) { + this.hasGesture = setterArg; + return this; + } + + private @Nullable String method; + + public @NonNull Builder setMethod(@NonNull String setterArg) { + this.method = setterArg; + return this; + } + + private @Nullable Map requestHeaders; + + public @NonNull Builder setRequestHeaders(@NonNull Map setterArg) { + this.requestHeaders = setterArg; + return this; + } + + public @NonNull WebResourceRequestData build() { + WebResourceRequestData pigeonReturn = new WebResourceRequestData(); + pigeonReturn.setUrl(url); + pigeonReturn.setIsForMainFrame(isForMainFrame); + pigeonReturn.setIsRedirect(isRedirect); + pigeonReturn.setHasGesture(hasGesture); + pigeonReturn.setMethod(method); + pigeonReturn.setRequestHeaders(requestHeaders); + return pigeonReturn; + } + } + + @NonNull Map toMap() { Map toMapResult = new HashMap<>(); toMapResult.put("url", url); @@ -96,46 +172,79 @@ Map toMap() { return toMapResult; } - static WebResourceRequestData fromMap(Map map) { - WebResourceRequestData fromMapResult = new WebResourceRequestData(); + static @NonNull WebResourceRequestData fromMap(@NonNull Map map) { + WebResourceRequestData pigeonResult = new WebResourceRequestData(); Object url = map.get("url"); - fromMapResult.url = (String) url; + pigeonResult.setUrl((String) url); Object isForMainFrame = map.get("isForMainFrame"); - fromMapResult.isForMainFrame = (Boolean) isForMainFrame; + pigeonResult.setIsForMainFrame((Boolean) isForMainFrame); Object isRedirect = map.get("isRedirect"); - fromMapResult.isRedirect = (Boolean) isRedirect; + pigeonResult.setIsRedirect((Boolean) isRedirect); Object hasGesture = map.get("hasGesture"); - fromMapResult.hasGesture = (Boolean) hasGesture; + pigeonResult.setHasGesture((Boolean) hasGesture); Object method = map.get("method"); - fromMapResult.method = (String) method; + pigeonResult.setMethod((String) method); Object requestHeaders = map.get("requestHeaders"); - fromMapResult.requestHeaders = (Map) requestHeaders; - return fromMapResult; + pigeonResult.setRequestHeaders((Map) requestHeaders); + return pigeonResult; } } /** Generated class from Pigeon that represents data sent in messages. */ public static class WebResourceErrorData { - private Long errorCode; + private @NonNull Long errorCode; - public Long getErrorCode() { + public @NonNull Long getErrorCode() { return errorCode; } - public void setErrorCode(Long setterArg) { + public void setErrorCode(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"errorCode\" is null."); + } this.errorCode = setterArg; } - private String description; + private @NonNull String description; - public String getDescription() { + public @NonNull String getDescription() { return description; } - public void setDescription(String setterArg) { + public void setDescription(@NonNull String setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"description\" is null."); + } this.description = setterArg; } + /** Constructor is private to enforce null safety; use Builder. */ + private WebResourceErrorData() {} + + public static final class Builder { + private @Nullable Long errorCode; + + public @NonNull Builder setErrorCode(@NonNull Long setterArg) { + this.errorCode = setterArg; + return this; + } + + private @Nullable String description; + + public @NonNull Builder setDescription(@NonNull String setterArg) { + this.description = setterArg; + return this; + } + + public @NonNull WebResourceErrorData build() { + WebResourceErrorData pigeonReturn = new WebResourceErrorData(); + pigeonReturn.setErrorCode(errorCode); + pigeonReturn.setDescription(description); + return pigeonReturn; + } + } + + @NonNull Map toMap() { Map toMapResult = new HashMap<>(); toMapResult.put("errorCode", errorCode); @@ -143,16 +252,16 @@ Map toMap() { return toMapResult; } - static WebResourceErrorData fromMap(Map map) { - WebResourceErrorData fromMapResult = new WebResourceErrorData(); + static @NonNull WebResourceErrorData fromMap(@NonNull Map map) { + WebResourceErrorData pigeonResult = new WebResourceErrorData(); Object errorCode = map.get("errorCode"); - fromMapResult.errorCode = + pigeonResult.setErrorCode( (errorCode == null) ? null - : ((errorCode instanceof Integer) ? (Integer) errorCode : (Long) errorCode); + : ((errorCode instanceof Integer) ? (Integer) errorCode : (Long) errorCode)); Object description = map.get("description"); - fromMapResult.description = (String) description; - return fromMapResult; + pigeonResult.setDescription((String) description); + return pigeonResult; } } @@ -172,7 +281,7 @@ private CookieManagerHostApiCodec() {} public interface CookieManagerHostApi { void clearCookies(Result result); - void setCookie(String url, String value); + void setCookie(@NonNull String url, @NonNull String value); /** The codec used by CookieManagerHostApi. */ static MessageCodec getCodec() { @@ -258,63 +367,76 @@ private WebViewHostApiCodec() {} /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface WebViewHostApi { - void create(Long instanceId, Boolean useHybridComposition); + void create(@NonNull Long instanceId, @NonNull Boolean useHybridComposition); - void dispose(Long instanceId); + void dispose(@NonNull Long instanceId); - void loadData(Long instanceId, String data, String mimeType, String encoding); + void loadData( + @NonNull Long instanceId, + @NonNull String data, + @Nullable String mimeType, + @Nullable String encoding); void loadDataWithBaseUrl( - Long instanceId, - String baseUrl, - String data, - String mimeType, - String encoding, - String historyUrl); + @NonNull Long instanceId, + @Nullable String baseUrl, + @NonNull String data, + @Nullable String mimeType, + @Nullable String encoding, + @Nullable String historyUrl); - void loadUrl(Long instanceId, String url, Map headers); + void loadUrl( + @NonNull Long instanceId, @NonNull String url, @NonNull Map headers); - void postUrl(Long instanceId, String url, byte[] data); + void postUrl(@NonNull Long instanceId, @NonNull String url, @NonNull byte[] data); - String getUrl(Long instanceId); + @Nullable + String getUrl(@NonNull Long instanceId); - Boolean canGoBack(Long instanceId); + @NonNull + Boolean canGoBack(@NonNull Long instanceId); - Boolean canGoForward(Long instanceId); + @NonNull + Boolean canGoForward(@NonNull Long instanceId); - void goBack(Long instanceId); + void goBack(@NonNull Long instanceId); - void goForward(Long instanceId); + void goForward(@NonNull Long instanceId); - void reload(Long instanceId); + void reload(@NonNull Long instanceId); - void clearCache(Long instanceId, Boolean includeDiskFiles); + void clearCache(@NonNull Long instanceId, @NonNull Boolean includeDiskFiles); - void evaluateJavascript(Long instanceId, String javascriptString, Result result); + void evaluateJavascript( + @NonNull Long instanceId, @NonNull String javascriptString, Result result); - String getTitle(Long instanceId); + @Nullable + String getTitle(@NonNull Long instanceId); - void scrollTo(Long instanceId, Long x, Long y); + void scrollTo(@NonNull Long instanceId, @NonNull Long x, @NonNull Long y); - void scrollBy(Long instanceId, Long x, Long y); + void scrollBy(@NonNull Long instanceId, @NonNull Long x, @NonNull Long y); - Long getScrollX(Long instanceId); + @NonNull + Long getScrollX(@NonNull Long instanceId); - Long getScrollY(Long instanceId); + @NonNull + Long getScrollY(@NonNull Long instanceId); - void setWebContentsDebuggingEnabled(Boolean enabled); + void setWebContentsDebuggingEnabled(@NonNull Boolean enabled); - void setWebViewClient(Long instanceId, Long webViewClientInstanceId); + void setWebViewClient(@NonNull Long instanceId, @NonNull Long webViewClientInstanceId); - void addJavaScriptChannel(Long instanceId, Long javaScriptChannelInstanceId); + void addJavaScriptChannel(@NonNull Long instanceId, @NonNull Long javaScriptChannelInstanceId); - void removeJavaScriptChannel(Long instanceId, Long javaScriptChannelInstanceId); + void removeJavaScriptChannel( + @NonNull Long instanceId, @NonNull Long javaScriptChannelInstanceId); - void setDownloadListener(Long instanceId, Long listenerInstanceId); + void setDownloadListener(@NonNull Long instanceId, @Nullable Long listenerInstanceId); - void setWebChromeClient(Long instanceId, Long clientInstanceId); + void setWebChromeClient(@NonNull Long instanceId, @Nullable Long clientInstanceId); - void setBackgroundColor(Long instanceId, Long color); + void setBackgroundColor(@NonNull Long instanceId, @NonNull Long color); /** The codec used by WebViewHostApi. */ static MessageCodec getCodec() { @@ -341,7 +463,9 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (useHybridCompositionArg == null) { throw new NullPointerException("useHybridCompositionArg unexpectedly null."); } - api.create(instanceIdArg.longValue(), useHybridCompositionArg); + api.create( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + useHybridCompositionArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -366,7 +490,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - api.dispose(instanceIdArg.longValue()); + api.dispose((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -396,14 +520,12 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { throw new NullPointerException("dataArg unexpectedly null."); } String mimeTypeArg = (String) args.get(2); - if (mimeTypeArg == null) { - throw new NullPointerException("mimeTypeArg unexpectedly null."); - } String encodingArg = (String) args.get(3); - if (encodingArg == null) { - throw new NullPointerException("encodingArg unexpectedly null."); - } - api.loadData(instanceIdArg.longValue(), dataArg, mimeTypeArg, encodingArg); + api.loadData( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + dataArg, + mimeTypeArg, + encodingArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -431,27 +553,15 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { throw new NullPointerException("instanceIdArg unexpectedly null."); } String baseUrlArg = (String) args.get(1); - if (baseUrlArg == null) { - throw new NullPointerException("baseUrlArg unexpectedly null."); - } String dataArg = (String) args.get(2); if (dataArg == null) { throw new NullPointerException("dataArg unexpectedly null."); } String mimeTypeArg = (String) args.get(3); - if (mimeTypeArg == null) { - throw new NullPointerException("mimeTypeArg unexpectedly null."); - } String encodingArg = (String) args.get(4); - if (encodingArg == null) { - throw new NullPointerException("encodingArg unexpectedly null."); - } String historyUrlArg = (String) args.get(5); - if (historyUrlArg == null) { - throw new NullPointerException("historyUrlArg unexpectedly null."); - } api.loadDataWithBaseUrl( - instanceIdArg.longValue(), + (instanceIdArg == null) ? null : instanceIdArg.longValue(), baseUrlArg, dataArg, mimeTypeArg, @@ -489,7 +599,10 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (headersArg == null) { throw new NullPointerException("headersArg unexpectedly null."); } - api.loadUrl(instanceIdArg.longValue(), urlArg, headersArg); + api.loadUrl( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + urlArg, + headersArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -522,7 +635,8 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (dataArg == null) { throw new NullPointerException("dataArg unexpectedly null."); } - api.postUrl(instanceIdArg.longValue(), urlArg, dataArg); + api.postUrl( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), urlArg, dataArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -547,7 +661,8 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - String output = api.getUrl(instanceIdArg.longValue()); + String output = + api.getUrl((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", output); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -572,7 +687,8 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - Boolean output = api.canGoBack(instanceIdArg.longValue()); + Boolean output = + api.canGoBack((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", output); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -597,7 +713,8 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - Boolean output = api.canGoForward(instanceIdArg.longValue()); + Boolean output = + api.canGoForward((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", output); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -622,7 +739,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - api.goBack(instanceIdArg.longValue()); + api.goBack((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -647,7 +764,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - api.goForward(instanceIdArg.longValue()); + api.goForward((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -672,7 +789,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - api.reload(instanceIdArg.longValue()); + api.reload((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -701,7 +818,9 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (includeDiskFilesArg == null) { throw new NullPointerException("includeDiskFilesArg unexpectedly null."); } - api.clearCache(instanceIdArg.longValue(), includeDiskFilesArg); + api.clearCache( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + includeDiskFilesArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -746,7 +865,9 @@ public void error(Throwable error) { }; api.evaluateJavascript( - instanceIdArg.longValue(), javascriptStringArg, resultCallback); + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + javascriptStringArg, + resultCallback); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); reply.reply(wrapped); @@ -770,7 +891,8 @@ public void error(Throwable error) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - String output = api.getTitle(instanceIdArg.longValue()); + String output = + api.getTitle((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", output); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -803,7 +925,10 @@ public void error(Throwable error) { if (yArg == null) { throw new NullPointerException("yArg unexpectedly null."); } - api.scrollTo(instanceIdArg.longValue(), xArg.longValue(), yArg.longValue()); + api.scrollTo( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (xArg == null) ? null : xArg.longValue(), + (yArg == null) ? null : yArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -836,7 +961,10 @@ public void error(Throwable error) { if (yArg == null) { throw new NullPointerException("yArg unexpectedly null."); } - api.scrollBy(instanceIdArg.longValue(), xArg.longValue(), yArg.longValue()); + api.scrollBy( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (xArg == null) ? null : xArg.longValue(), + (yArg == null) ? null : yArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -861,7 +989,8 @@ public void error(Throwable error) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - Long output = api.getScrollX(instanceIdArg.longValue()); + Long output = + api.getScrollX((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", output); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -886,7 +1015,8 @@ public void error(Throwable error) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - Long output = api.getScrollY(instanceIdArg.longValue()); + Long output = + api.getScrollY((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", output); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -943,7 +1073,10 @@ public void error(Throwable error) { throw new NullPointerException("webViewClientInstanceIdArg unexpectedly null."); } api.setWebViewClient( - instanceIdArg.longValue(), webViewClientInstanceIdArg.longValue()); + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (webViewClientInstanceIdArg == null) + ? null + : webViewClientInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -976,7 +1109,10 @@ public void error(Throwable error) { "javaScriptChannelInstanceIdArg unexpectedly null."); } api.addJavaScriptChannel( - instanceIdArg.longValue(), javaScriptChannelInstanceIdArg.longValue()); + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (javaScriptChannelInstanceIdArg == null) + ? null + : javaScriptChannelInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1009,7 +1145,10 @@ public void error(Throwable error) { "javaScriptChannelInstanceIdArg unexpectedly null."); } api.removeJavaScriptChannel( - instanceIdArg.longValue(), javaScriptChannelInstanceIdArg.longValue()); + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (javaScriptChannelInstanceIdArg == null) + ? null + : javaScriptChannelInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1037,11 +1176,9 @@ public void error(Throwable error) { throw new NullPointerException("instanceIdArg unexpectedly null."); } Number listenerInstanceIdArg = (Number) args.get(1); - if (listenerInstanceIdArg == null) { - throw new NullPointerException("listenerInstanceIdArg unexpectedly null."); - } api.setDownloadListener( - instanceIdArg.longValue(), listenerInstanceIdArg.longValue()); + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (listenerInstanceIdArg == null) ? null : listenerInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1069,11 +1206,9 @@ public void error(Throwable error) { throw new NullPointerException("instanceIdArg unexpectedly null."); } Number clientInstanceIdArg = (Number) args.get(1); - if (clientInstanceIdArg == null) { - throw new NullPointerException("clientInstanceIdArg unexpectedly null."); - } api.setWebChromeClient( - instanceIdArg.longValue(), clientInstanceIdArg.longValue()); + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (clientInstanceIdArg == null) ? null : clientInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1104,7 +1239,9 @@ public void error(Throwable error) { if (colorArg == null) { throw new NullPointerException("colorArg unexpectedly null."); } - api.setBackgroundColor(instanceIdArg.longValue(), colorArg.longValue()); + api.setBackgroundColor( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (colorArg == null) ? null : colorArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1126,33 +1263,33 @@ private WebSettingsHostApiCodec() {} /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface WebSettingsHostApi { - void create(Long instanceId, Long webViewInstanceId); + void create(@NonNull Long instanceId, @NonNull Long webViewInstanceId); - void dispose(Long instanceId); + void dispose(@NonNull Long instanceId); - void setDomStorageEnabled(Long instanceId, Boolean flag); + void setDomStorageEnabled(@NonNull Long instanceId, @NonNull Boolean flag); - void setJavaScriptCanOpenWindowsAutomatically(Long instanceId, Boolean flag); + void setJavaScriptCanOpenWindowsAutomatically(@NonNull Long instanceId, @NonNull Boolean flag); - void setSupportMultipleWindows(Long instanceId, Boolean support); + void setSupportMultipleWindows(@NonNull Long instanceId, @NonNull Boolean support); - void setJavaScriptEnabled(Long instanceId, Boolean flag); + void setJavaScriptEnabled(@NonNull Long instanceId, @NonNull Boolean flag); - void setUserAgentString(Long instanceId, String userAgentString); + void setUserAgentString(@NonNull Long instanceId, @Nullable String userAgentString); - void setMediaPlaybackRequiresUserGesture(Long instanceId, Boolean require); + void setMediaPlaybackRequiresUserGesture(@NonNull Long instanceId, @NonNull Boolean require); - void setSupportZoom(Long instanceId, Boolean support); + void setSupportZoom(@NonNull Long instanceId, @NonNull Boolean support); - void setLoadWithOverviewMode(Long instanceId, Boolean overview); + void setLoadWithOverviewMode(@NonNull Long instanceId, @NonNull Boolean overview); - void setUseWideViewPort(Long instanceId, Boolean use); + void setUseWideViewPort(@NonNull Long instanceId, @NonNull Boolean use); - void setDisplayZoomControls(Long instanceId, Boolean enabled); + void setDisplayZoomControls(@NonNull Long instanceId, @NonNull Boolean enabled); - void setBuiltInZoomControls(Long instanceId, Boolean enabled); + void setBuiltInZoomControls(@NonNull Long instanceId, @NonNull Boolean enabled); - void setAllowFileAccess(Long instanceId, Boolean enabled); + void setAllowFileAccess(@NonNull Long instanceId, @NonNull Boolean enabled); /** The codec used by WebSettingsHostApi. */ static MessageCodec getCodec() { @@ -1181,7 +1318,9 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (webViewInstanceIdArg == null) { throw new NullPointerException("webViewInstanceIdArg unexpectedly null."); } - api.create(instanceIdArg.longValue(), webViewInstanceIdArg.longValue()); + api.create( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (webViewInstanceIdArg == null) ? null : webViewInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1206,7 +1345,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - api.dispose(instanceIdArg.longValue()); + api.dispose((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1237,7 +1376,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (flagArg == null) { throw new NullPointerException("flagArg unexpectedly null."); } - api.setDomStorageEnabled(instanceIdArg.longValue(), flagArg); + api.setDomStorageEnabled( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), flagArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1268,7 +1408,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (flagArg == null) { throw new NullPointerException("flagArg unexpectedly null."); } - api.setJavaScriptCanOpenWindowsAutomatically(instanceIdArg.longValue(), flagArg); + api.setJavaScriptCanOpenWindowsAutomatically( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), flagArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1299,7 +1440,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (supportArg == null) { throw new NullPointerException("supportArg unexpectedly null."); } - api.setSupportMultipleWindows(instanceIdArg.longValue(), supportArg); + api.setSupportMultipleWindows( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), supportArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1330,7 +1472,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (flagArg == null) { throw new NullPointerException("flagArg unexpectedly null."); } - api.setJavaScriptEnabled(instanceIdArg.longValue(), flagArg); + api.setJavaScriptEnabled( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), flagArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1358,10 +1501,9 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { throw new NullPointerException("instanceIdArg unexpectedly null."); } String userAgentStringArg = (String) args.get(1); - if (userAgentStringArg == null) { - throw new NullPointerException("userAgentStringArg unexpectedly null."); - } - api.setUserAgentString(instanceIdArg.longValue(), userAgentStringArg); + api.setUserAgentString( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + userAgentStringArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1392,7 +1534,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (requireArg == null) { throw new NullPointerException("requireArg unexpectedly null."); } - api.setMediaPlaybackRequiresUserGesture(instanceIdArg.longValue(), requireArg); + api.setMediaPlaybackRequiresUserGesture( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), requireArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1423,7 +1566,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (supportArg == null) { throw new NullPointerException("supportArg unexpectedly null."); } - api.setSupportZoom(instanceIdArg.longValue(), supportArg); + api.setSupportZoom( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), supportArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1454,7 +1598,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (overviewArg == null) { throw new NullPointerException("overviewArg unexpectedly null."); } - api.setLoadWithOverviewMode(instanceIdArg.longValue(), overviewArg); + api.setLoadWithOverviewMode( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), overviewArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1485,7 +1630,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (useArg == null) { throw new NullPointerException("useArg unexpectedly null."); } - api.setUseWideViewPort(instanceIdArg.longValue(), useArg); + api.setUseWideViewPort( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), useArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1516,7 +1662,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (enabledArg == null) { throw new NullPointerException("enabledArg unexpectedly null."); } - api.setDisplayZoomControls(instanceIdArg.longValue(), enabledArg); + api.setDisplayZoomControls( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), enabledArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1547,7 +1694,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (enabledArg == null) { throw new NullPointerException("enabledArg unexpectedly null."); } - api.setBuiltInZoomControls(instanceIdArg.longValue(), enabledArg); + api.setBuiltInZoomControls( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), enabledArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1578,7 +1726,8 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (enabledArg == null) { throw new NullPointerException("enabledArg unexpectedly null."); } - api.setAllowFileAccess(instanceIdArg.longValue(), enabledArg); + api.setAllowFileAccess( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), enabledArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1601,7 +1750,7 @@ private JavaScriptChannelHostApiCodec() {} /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface JavaScriptChannelHostApi { - void create(Long instanceId, String channelName); + void create(@NonNull Long instanceId, @NonNull String channelName); /** The codec used by JavaScriptChannelHostApi. */ static MessageCodec getCodec() { @@ -1631,7 +1780,8 @@ static void setup(BinaryMessenger binaryMessenger, JavaScriptChannelHostApi api) if (channelNameArg == null) { throw new NullPointerException("channelNameArg unexpectedly null."); } - api.create(instanceIdArg.longValue(), channelNameArg); + api.create( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), channelNameArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1668,7 +1818,7 @@ static MessageCodec getCodec() { return JavaScriptChannelFlutterApiCodec.INSTANCE; } - public void dispose(Long instanceIdArg, Reply callback) { + public void dispose(@NonNull Long instanceIdArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, @@ -1681,7 +1831,8 @@ public void dispose(Long instanceIdArg, Reply callback) { }); } - public void postMessage(Long instanceIdArg, String messageArg, Reply callback) { + public void postMessage( + @NonNull Long instanceIdArg, @NonNull String messageArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, @@ -1703,7 +1854,7 @@ private WebViewClientHostApiCodec() {} /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface WebViewClientHostApi { - void create(Long instanceId, Boolean shouldOverrideUrlLoading); + void create(@NonNull Long instanceId, @NonNull Boolean shouldOverrideUrlLoading); /** The codec used by WebViewClientHostApi. */ static MessageCodec getCodec() { @@ -1734,7 +1885,9 @@ static void setup(BinaryMessenger binaryMessenger, WebViewClientHostApi api) { throw new NullPointerException( "shouldOverrideUrlLoadingArg unexpectedly null."); } - api.create(instanceIdArg.longValue(), shouldOverrideUrlLoadingArg); + api.create( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + shouldOverrideUrlLoadingArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1797,7 +1950,7 @@ static MessageCodec getCodec() { return WebViewClientFlutterApiCodec.INSTANCE; } - public void dispose(Long instanceIdArg, Reply callback) { + public void dispose(@NonNull Long instanceIdArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, "dev.flutter.pigeon.WebViewClientFlutterApi.dispose", getCodec()); @@ -1809,7 +1962,10 @@ public void dispose(Long instanceIdArg, Reply callback) { } public void onPageStarted( - Long instanceIdArg, Long webViewInstanceIdArg, String urlArg, Reply callback) { + @NonNull Long instanceIdArg, + @NonNull Long webViewInstanceIdArg, + @NonNull String urlArg, + Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, @@ -1823,7 +1979,10 @@ public void onPageStarted( } public void onPageFinished( - Long instanceIdArg, Long webViewInstanceIdArg, String urlArg, Reply callback) { + @NonNull Long instanceIdArg, + @NonNull Long webViewInstanceIdArg, + @NonNull String urlArg, + Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, @@ -1837,10 +1996,10 @@ public void onPageFinished( } public void onReceivedRequestError( - Long instanceIdArg, - Long webViewInstanceIdArg, - WebResourceRequestData requestArg, - WebResourceErrorData errorArg, + @NonNull Long instanceIdArg, + @NonNull Long webViewInstanceIdArg, + @NonNull WebResourceRequestData requestArg, + @NonNull WebResourceErrorData errorArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( @@ -1856,11 +2015,11 @@ public void onReceivedRequestError( } public void onReceivedError( - Long instanceIdArg, - Long webViewInstanceIdArg, - Long errorCodeArg, - String descriptionArg, - String failingUrlArg, + @NonNull Long instanceIdArg, + @NonNull Long webViewInstanceIdArg, + @NonNull Long errorCodeArg, + @NonNull String descriptionArg, + @NonNull String failingUrlArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( @@ -1881,9 +2040,9 @@ public void onReceivedError( } public void requestLoading( - Long instanceIdArg, - Long webViewInstanceIdArg, - WebResourceRequestData requestArg, + @NonNull Long instanceIdArg, + @NonNull Long webViewInstanceIdArg, + @NonNull WebResourceRequestData requestArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( @@ -1898,7 +2057,10 @@ public void requestLoading( } public void urlLoading( - Long instanceIdArg, Long webViewInstanceIdArg, String urlArg, Reply callback) { + @NonNull Long instanceIdArg, + @NonNull Long webViewInstanceIdArg, + @NonNull String urlArg, + Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, "dev.flutter.pigeon.WebViewClientFlutterApi.urlLoading", getCodec()); @@ -1918,7 +2080,7 @@ private DownloadListenerHostApiCodec() {} /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface DownloadListenerHostApi { - void create(Long instanceId); + void create(@NonNull Long instanceId); /** The codec used by DownloadListenerHostApi. */ static MessageCodec getCodec() { @@ -1944,7 +2106,7 @@ static void setup(BinaryMessenger binaryMessenger, DownloadListenerHostApi api) if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - api.create(instanceIdArg.longValue()); + api.create((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -1981,7 +2143,7 @@ static MessageCodec getCodec() { return DownloadListenerFlutterApiCodec.INSTANCE; } - public void dispose(Long instanceIdArg, Reply callback) { + public void dispose(@NonNull Long instanceIdArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, "dev.flutter.pigeon.DownloadListenerFlutterApi.dispose", getCodec()); @@ -1993,12 +2155,12 @@ public void dispose(Long instanceIdArg, Reply callback) { } public void onDownloadStart( - Long instanceIdArg, - String urlArg, - String userAgentArg, - String contentDispositionArg, - String mimetypeArg, - Long contentLengthArg, + @NonNull Long instanceIdArg, + @NonNull String urlArg, + @NonNull String userAgentArg, + @NonNull String contentDispositionArg, + @NonNull String mimetypeArg, + @NonNull Long contentLengthArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( @@ -2028,7 +2190,7 @@ private WebChromeClientHostApiCodec() {} /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface WebChromeClientHostApi { - void create(Long instanceId, Long webViewClientInstanceId); + void create(@NonNull Long instanceId, @NonNull Long webViewClientInstanceId); /** The codec used by WebChromeClientHostApi. */ static MessageCodec getCodec() { @@ -2058,7 +2220,11 @@ static void setup(BinaryMessenger binaryMessenger, WebChromeClientHostApi api) { if (webViewClientInstanceIdArg == null) { throw new NullPointerException("webViewClientInstanceIdArg unexpectedly null."); } - api.create(instanceIdArg.longValue(), webViewClientInstanceIdArg.longValue()); + api.create( + (instanceIdArg == null) ? null : instanceIdArg.longValue(), + (webViewClientInstanceIdArg == null) + ? null + : webViewClientInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -2081,9 +2247,11 @@ private FlutterAssetManagerHostApiCodec() {} /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface FlutterAssetManagerHostApi { - List list(String path); + @NonNull + List list(@NonNull String path); - String getAssetFilePathByName(String name); + @NonNull + String getAssetFilePathByName(@NonNull String name); /** The codec used by FlutterAssetManagerHostApi. */ static MessageCodec getCodec() { @@ -2173,7 +2341,7 @@ static MessageCodec getCodec() { return WebChromeClientFlutterApiCodec.INSTANCE; } - public void dispose(Long instanceIdArg, Reply callback) { + public void dispose(@NonNull Long instanceIdArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, "dev.flutter.pigeon.WebChromeClientFlutterApi.dispose", getCodec()); @@ -2185,7 +2353,10 @@ public void dispose(Long instanceIdArg, Reply callback) { } public void onProgressChanged( - Long instanceIdArg, Long webViewInstanceIdArg, Long progressArg, Reply callback) { + @NonNull Long instanceIdArg, + @NonNull Long webViewInstanceIdArg, + @NonNull Long progressArg, + Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, @@ -2207,9 +2378,9 @@ private WebStorageHostApiCodec() {} /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface WebStorageHostApi { - void create(Long instanceId); + void create(@NonNull Long instanceId); - void deleteAllData(Long instanceId); + void deleteAllData(@NonNull Long instanceId); /** The codec used by WebStorageHostApi. */ static MessageCodec getCodec() { @@ -2234,7 +2405,7 @@ static void setup(BinaryMessenger binaryMessenger, WebStorageHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - api.create(instanceIdArg.longValue()); + api.create((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -2259,7 +2430,7 @@ static void setup(BinaryMessenger binaryMessenger, WebStorageHostApi api) { if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - api.deleteAllData(instanceIdArg.longValue()); + api.deleteAllData((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -2277,7 +2448,9 @@ private static Map wrapError(Throwable exception) { Map errorMap = new HashMap<>(); errorMap.put("message", exception.toString()); errorMap.put("code", exception.getClass().getSimpleName()); - errorMap.put("details", null); + errorMap.put( + "details", + "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); return errorMap; } } 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 9e462faa58a7..b4885688f7ac 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 @@ -14,6 +14,7 @@ import androidx.webkit.WebResourceErrorCompat; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebViewClientFlutterApi; +import java.util.HashMap; /** * Flutter Api implementation for {@link WebViewClient}. @@ -26,40 +27,39 @@ public class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi { @RequiresApi(api = Build.VERSION_CODES.M) static GeneratedAndroidWebView.WebResourceErrorData createWebResourceErrorData( WebResourceError error) { - final GeneratedAndroidWebView.WebResourceErrorData errorData = - new GeneratedAndroidWebView.WebResourceErrorData(); - errorData.setErrorCode((long) error.getErrorCode()); - errorData.setDescription(error.getDescription().toString()); - - return errorData; + return new GeneratedAndroidWebView.WebResourceErrorData.Builder() + .setErrorCode((long) error.getErrorCode()) + .setDescription(error.getDescription().toString()) + .build(); } @SuppressLint("RequiresFeature") static GeneratedAndroidWebView.WebResourceErrorData createWebResourceErrorData( WebResourceErrorCompat error) { - final GeneratedAndroidWebView.WebResourceErrorData errorData = - new GeneratedAndroidWebView.WebResourceErrorData(); - errorData.setErrorCode((long) error.getErrorCode()); - errorData.setDescription(error.getDescription().toString()); - - return errorData; + return new GeneratedAndroidWebView.WebResourceErrorData.Builder() + .setErrorCode((long) error.getErrorCode()) + .setDescription(error.getDescription().toString()) + .build(); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) static GeneratedAndroidWebView.WebResourceRequestData createWebResourceRequestData( WebResourceRequest request) { - final GeneratedAndroidWebView.WebResourceRequestData requestData = - new GeneratedAndroidWebView.WebResourceRequestData(); - requestData.setUrl(request.getUrl().toString()); - requestData.setIsForMainFrame(request.isForMainFrame()); + final GeneratedAndroidWebView.WebResourceRequestData.Builder requestData = + new GeneratedAndroidWebView.WebResourceRequestData.Builder() + .setUrl(request.getUrl().toString()) + .setIsForMainFrame(request.isForMainFrame()) + .setHasGesture(request.hasGesture()) + .setMethod(request.getMethod()) + .setRequestHeaders( + request.getRequestHeaders() != null + ? request.getRequestHeaders() + : new HashMap<>()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { requestData.setIsRedirect(request.isRedirect()); } - requestData.setHasGesture(request.hasGesture()); - requestData.setMethod(request.getMethod()); - requestData.setRequestHeaders(request.getRequestHeaders()); - return requestData; + return requestData.build(); } /** diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java index 0f3161355dcb..afc3efee80ff 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java @@ -28,11 +28,6 @@ *

Handles creating {@link WebView}s that intercommunicate with a paired Dart object. */ public class WebViewHostApiImpl implements WebViewHostApi { - // TODO(bparrishMines): This can be removed once pigeon supports null values: https://github.com/flutter/flutter/issues/59118 - // Workaround to represent null Strings since pigeon doesn't support null - // values. - private static final String nullStringIdentifier = ""; - private final InstanceManager instanceManager; private final WebViewProxy webViewProxy; // Only used with WebView using virtual displays. @@ -355,8 +350,7 @@ public void dispose(Long instanceId) { @Override public void loadData(Long instanceId, String data, String mimeType, String encoding) { final WebView webView = (WebView) instanceManager.getInstance(instanceId); - webView.loadData( - data, parseNullStringIdentifier(mimeType), parseNullStringIdentifier(encoding)); + webView.loadData(data, mimeType, encoding); } @Override @@ -368,12 +362,7 @@ public void loadDataWithBaseUrl( String encoding, String historyUrl) { final WebView webView = (WebView) instanceManager.getInstance(instanceId); - webView.loadDataWithBaseURL( - parseNullStringIdentifier(baseUrl), - data, - parseNullStringIdentifier(mimeType), - parseNullStringIdentifier(encoding), - parseNullStringIdentifier(historyUrl)); + webView.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl); } @Override @@ -391,8 +380,7 @@ public void postUrl(Long instanceId, String url, byte[] data) { @Override public String getUrl(Long instanceId) { final WebView webView = (WebView) instanceManager.getInstance(instanceId); - final String result = webView.getUrl(); - return result != null ? result : nullStringIdentifier; + return webView.getUrl(); } @Override @@ -441,8 +429,7 @@ public void evaluateJavascript( @Override public String getTitle(Long instanceId) { final WebView webView = (WebView) instanceManager.getInstance(instanceId); - final String result = webView.getTitle(); - return result != null ? result : nullStringIdentifier; + return webView.getTitle(); } @Override @@ -513,13 +500,4 @@ public void setBackgroundColor(Long instanceId, Long color) { final WebView webView = (WebView) instanceManager.getInstance(instanceId); webView.setBackgroundColor(color.intValue()); } - - @Nullable - private static String parseNullStringIdentifier(String value) { - if (value.equals(nullStringIdentifier)) { - return null; - } - - return value; - } } 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 62d272366a6f..c2abd25c5a66 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 @@ -4,16 +4,22 @@ package io.flutter.plugins.webviewflutter; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import android.net.Uri; +import android.webkit.WebResourceRequest; import android.webkit.WebView; import android.webkit.WebViewClient; import io.flutter.plugins.webviewflutter.WebViewClientHostApiImpl.WebViewClientCompatImpl; import io.flutter.plugins.webviewflutter.WebViewClientHostApiImpl.WebViewClientCreator; +import java.util.HashMap; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -96,4 +102,20 @@ public void urlLoading() { webViewClient.shouldOverrideUrlLoading(mockWebView, ""); verify(mockFlutterApi, never()).urlLoading((WebViewClient) any(), any(), any(), any()); } + + @Test + public void convertWebResourceRequestWithNullHeaders() { + 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 GeneratedAndroidWebView.WebResourceRequestData data = + WebViewClientFlutterApiImpl.createWebResourceRequestData(mockRequest); + assertEquals(data.getRequestHeaders(), new HashMap()); + } } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java index 2312b764342f..5be39ab963a3 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java @@ -166,8 +166,7 @@ public void loadData() { @Test public void loadDataWithNullValues() { - testHostApiImpl.loadData( - 0L, "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", "", ""); + testHostApiImpl.loadData(0L, "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", null, null); verify(mockWebView).loadData("VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", null, null); } @@ -192,12 +191,7 @@ public void loadDataWithBaseUrl() { @Test public void loadDataWithBaseUrlAndNullValues() { testHostApiImpl.loadDataWithBaseUrl( - 0L, - "", - "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", - "", - "", - ""); + 0L, null, "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", null, null, null); verify(mockWebView) .loadDataWithBaseURL(null, "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", null, null, null); } diff --git a/packages/webview_flutter/webview_flutter_android/generatePigeons.sh b/packages/webview_flutter/webview_flutter_android/generatePigeons.sh deleted file mode 100755 index 30a6918fc922..000000000000 --- a/packages/webview_flutter/webview_flutter_android/generatePigeons.sh +++ /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. - -flutter pub run pigeon \ ---input pigeons/android_webview.dart \ ---dart_out lib/src/android_webview.pigeon.dart \ ---dart_test_out test/android_webview.pigeon.dart \ ---java_out android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.Java \ ---java_package io.flutter.plugins.webviewflutter 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 bd50640919f9..f858fb39a943 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 @@ -11,11 +11,6 @@ import 'package:flutter/widgets.dart' show AndroidViewSurface; import 'android_webview.pigeon.dart'; import 'android_webview_api_impls.dart'; -// TODO(bparrishMines): This can be removed once pigeon supports null values: https://github.com/flutter/flutter/issues/59118 -// Workaround to represent null Strings since pigeon doesn't support null -// values. -const String _nullStringIdentifier = ''; - /// An Android View that displays web pages. /// /// **Basic usage** @@ -102,8 +97,8 @@ class WebView { return api.loadDataFromInstance( this, data, - mimeType ?? _nullStringIdentifier, - encoding ?? _nullStringIdentifier, + mimeType, + encoding, ); } @@ -151,11 +146,11 @@ class WebView { }) { return api.loadDataWithBaseUrlFromInstance( this, - baseUrl ?? _nullStringIdentifier, + baseUrl, data, - mimeType ?? _nullStringIdentifier, - encoding ?? _nullStringIdentifier, - historyUrl ?? _nullStringIdentifier, + mimeType, + encoding, + historyUrl, ); } @@ -184,12 +179,8 @@ class WebView { /// begun, the current page may not have changed. /// /// Returns null if no page has been loaded. - Future getUrl() async { - final String result = await api.getUrlFromInstance(this); - if (result == _nullStringIdentifier) { - return null; - } - return result; + Future getUrl() { + return api.getUrlFromInstance(this); } /// Whether this WebView has a back history item. @@ -235,27 +226,19 @@ class WebView { /// JavaScript state from an empty WebView is no longer persisted across /// navigations like [loadUrl]. For example, global variables and functions /// defined before calling [loadUrl]) will not exist in the loaded page. - Future evaluateJavascript(String javascriptString) async { - final String result = await api.evaluateJavascriptFromInstance( + Future evaluateJavascript(String javascriptString) { + return api.evaluateJavascriptFromInstance( this, javascriptString, ); - if (result == _nullStringIdentifier) { - return null; - } - return result; } // TODO(bparrishMines): Update documentation when WebViewClient.onReceivedTitle is added. /// Gets the title for the current page. /// /// Returns null if no page has been loaded. - Future getTitle() async { - final String result = await api.getTitleFromInstance(this); - if (result == _nullStringIdentifier) { - return null; - } - return result; + Future getTitle() { + return api.getTitleFromInstance(this); } // TODO(bparrishMines): Update documentation when onScrollChanged is added. @@ -337,9 +320,11 @@ class WebView { /// Registers the interface to be used when content can not be handled by the rendering engine, and should be downloaded instead. /// /// This will replace the current handler. - Future setDownloadListener(DownloadListener listener) { - DownloadListener.api.createFromInstance(listener); - return api.setDownloadListenerFromInstance(this, listener); + Future setDownloadListener(DownloadListener? listener) async { + await Future.wait(>[ + if (listener != null) DownloadListener.api.createFromInstance(listener), + api.setDownloadListenerFromInstance(this, listener) + ]); } /// Sets the chrome handler. @@ -347,7 +332,7 @@ class WebView { /// This is an implementation of [WebChromeClient] for use in handling /// JavaScript dialogs, favicons, titles, and the progress. This will replace /// the current handler. - Future setWebChromeClient(WebChromeClient client) { + Future setWebChromeClient(WebChromeClient? client) async { // WebView requires a WebViewClient because of a bug fix that makes // calls to WebViewClient.requestLoading/WebViewClient.urlLoading when a new // window is opened. This is to make sure a url opened by `Window.open` has @@ -356,8 +341,11 @@ class WebView { _currentWebViewClient != null, "Can't set a WebChromeClient without setting a WebViewClient first.", ); - WebChromeClient.api.createFromInstance(client, _currentWebViewClient!); - return api.setWebChromeClientFromInstance(this, client); + await Future.wait(>[ + if (client != null) + WebChromeClient.api.createFromInstance(client, _currentWebViewClient!), + api.setWebChromeClientFromInstance(this, client), + ]); } /// Sets the background color of this WebView. @@ -477,7 +465,7 @@ class WebSettings { /// If the string is empty, the system default value will be used. Note that /// starting from KITKAT Android version, changing the user-agent while /// loading a web page causes WebView to initiate loading once again. - Future setUserAgentString(String userAgentString) { + Future setUserAgentString(String? userAgentString) { return api.setUserAgentStringFromInstance(this, userAgentString); } diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart index 4a0965eaeac0..4491e162ce9c 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart @@ -1,8 +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 (v1.0.9), do not edit directly. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name // @dart = 2.12 @@ -13,12 +12,21 @@ import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; import 'package:flutter/services.dart'; class WebResourceRequestData { - String? url; - bool? isForMainFrame; + WebResourceRequestData({ + required this.url, + required this.isForMainFrame, + this.isRedirect, + required this.hasGesture, + required this.method, + required this.requestHeaders, + }); + + String url; + bool isForMainFrame; bool? isRedirect; - bool? hasGesture; - String? method; - Map? requestHeaders; + bool hasGesture; + String method; + Map requestHeaders; Object encode() { final Map pigeonMap = {}; @@ -33,20 +41,26 @@ class WebResourceRequestData { static WebResourceRequestData decode(Object message) { final Map pigeonMap = message as Map; - return WebResourceRequestData() - ..url = pigeonMap['url'] as String? - ..isForMainFrame = pigeonMap['isForMainFrame'] as bool? - ..isRedirect = pigeonMap['isRedirect'] as bool? - ..hasGesture = pigeonMap['hasGesture'] as bool? - ..method = pigeonMap['method'] as String? - ..requestHeaders = (pigeonMap['requestHeaders'] as Map?) - ?.cast(); + return WebResourceRequestData( + url: pigeonMap['url']! as String, + isForMainFrame: pigeonMap['isForMainFrame']! as bool, + isRedirect: pigeonMap['isRedirect'] as bool?, + hasGesture: pigeonMap['hasGesture']! as bool, + method: pigeonMap['method']! as String, + requestHeaders: (pigeonMap['requestHeaders'] as Map?)! + .cast(), + ); } } class WebResourceErrorData { - int? errorCode; - String? description; + WebResourceErrorData({ + required this.errorCode, + required this.description, + }); + + int errorCode; + String description; Object encode() { final Map pigeonMap = {}; @@ -57,9 +71,10 @@ class WebResourceErrorData { static WebResourceErrorData decode(Object message) { final Map pigeonMap = message as Map; - return WebResourceErrorData() - ..errorCode = pigeonMap['errorCode'] as int? - ..description = pigeonMap['description'] as String?; + return WebResourceErrorData( + errorCode: pigeonMap['errorCode']! as int, + description: pigeonMap['description']! as String, + ); } } @@ -88,7 +103,6 @@ class CookieManagerHostApi { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -98,6 +112,11 @@ class CookieManagerHostApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as bool?)!; } @@ -108,12 +127,11 @@ class CookieManagerHostApi { 'dev.flutter.pigeon.CookieManagerHostApi.setCookie', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_url, arg_value]) as Map?; + .send([arg_url, arg_value]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -149,13 +167,12 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_useHybridComposition]) + await channel.send([arg_instanceId, arg_useHybridComposition]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -175,12 +192,11 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.dispose', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -196,18 +212,17 @@ class WebViewHostApi { } Future loadData(int arg_instanceId, String arg_data, - String arg_mimeType, String arg_encoding) async { + String? arg_mimeType, String? arg_encoding) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.loadData', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send( - [arg_instanceId, arg_data, arg_mimeType, arg_encoding]) + [arg_instanceId, arg_data, arg_mimeType, arg_encoding]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -224,15 +239,15 @@ class WebViewHostApi { Future loadDataWithBaseUrl( int arg_instanceId, - String arg_baseUrl, + String? arg_baseUrl, String arg_data, - String arg_mimeType, - String arg_encoding, - String arg_historyUrl) async { + String? arg_mimeType, + String? arg_encoding, + String? arg_historyUrl) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel.send([ + final Map? replyMap = await channel.send([ arg_instanceId, arg_baseUrl, arg_data, @@ -244,7 +259,6 @@ class WebViewHostApi { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -265,13 +279,12 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.loadUrl', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_url, arg_headers]) + await channel.send([arg_instanceId, arg_url, arg_headers]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -292,13 +305,12 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.postUrl', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_url, arg_data]) + await channel.send([arg_instanceId, arg_url, arg_data]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -313,17 +325,16 @@ class WebViewHostApi { } } - Future getUrl(int arg_instanceId) async { + Future getUrl(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.getUrl', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -334,7 +345,7 @@ class WebViewHostApi { details: error['details'], ); } else { - return (replyMap['result'] as String?)!; + return (replyMap['result'] as String?); } } @@ -343,12 +354,11 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.canGoBack', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -358,6 +368,11 @@ class WebViewHostApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as bool?)!; } @@ -368,12 +383,11 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.canGoForward', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -383,6 +397,11 @@ class WebViewHostApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as bool?)!; } @@ -393,12 +412,11 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.goBack', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -418,12 +436,11 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.goForward', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -443,12 +460,11 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.reload', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -468,13 +484,12 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.clearCache', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_includeDiskFiles]) + await channel.send([arg_instanceId, arg_includeDiskFiles]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -489,19 +504,18 @@ class WebViewHostApi { } } - Future evaluateJavascript( + Future evaluateJavascript( int arg_instanceId, String arg_javascriptString) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.evaluateJavascript', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_javascriptString]) + await channel.send([arg_instanceId, arg_javascriptString]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -512,21 +526,20 @@ class WebViewHostApi { details: error['details'], ); } else { - return (replyMap['result'] as String?)!; + return (replyMap['result'] as String?); } } - Future getTitle(int arg_instanceId) async { + Future getTitle(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.getTitle', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -537,7 +550,7 @@ class WebViewHostApi { details: error['details'], ); } else { - return (replyMap['result'] as String?)!; + return (replyMap['result'] as String?); } } @@ -545,13 +558,13 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.scrollTo', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_x, arg_y]) as Map?; + final Map? replyMap = + await channel.send([arg_instanceId, arg_x, arg_y]) + as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -570,13 +583,13 @@ class WebViewHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.scrollBy', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_x, arg_y]) as Map?; + final Map? replyMap = + await channel.send([arg_instanceId, arg_x, arg_y]) + as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -596,12 +609,11 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.getScrollX', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -611,6 +623,11 @@ class WebViewHostApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as int?)!; } @@ -621,12 +638,11 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.getScrollY', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -636,6 +652,11 @@ class WebViewHostApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as int?)!; } @@ -647,12 +668,11 @@ class WebViewHostApi { codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_enabled]) as Map?; + await channel.send([arg_enabled]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -673,13 +693,12 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.setWebViewClient', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_webViewClientInstanceId]) + .send([arg_instanceId, arg_webViewClientInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -700,13 +719,12 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.addJavaScriptChannel', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_javaScriptChannelInstanceId]) + .send([arg_instanceId, arg_javaScriptChannelInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -727,13 +745,12 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.removeJavaScriptChannel', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_javaScriptChannelInstanceId]) + .send([arg_instanceId, arg_javaScriptChannelInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -749,18 +766,17 @@ class WebViewHostApi { } Future setDownloadListener( - int arg_instanceId, int arg_listenerInstanceId) async { + int arg_instanceId, int? arg_listenerInstanceId) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.setDownloadListener', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_listenerInstanceId]) + await channel.send([arg_instanceId, arg_listenerInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -776,18 +792,17 @@ class WebViewHostApi { } Future setWebChromeClient( - int arg_instanceId, int arg_clientInstanceId) async { + int arg_instanceId, int? arg_clientInstanceId) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.setWebChromeClient', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_clientInstanceId]) + await channel.send([arg_instanceId, arg_clientInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -807,12 +822,11 @@ class WebViewHostApi { 'dev.flutter.pigeon.WebViewHostApi.setBackgroundColor', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_color]) as Map?; + .send([arg_instanceId, arg_color]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -848,13 +862,12 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_webViewInstanceId]) + await channel.send([arg_instanceId, arg_webViewInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -874,12 +887,11 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.dispose', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -899,12 +911,11 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_flag]) as Map?; + .send([arg_instanceId, arg_flag]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -926,12 +937,11 @@ class WebSettingsHostApi { codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_flag]) as Map?; + .send([arg_instanceId, arg_flag]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -953,12 +963,11 @@ class WebSettingsHostApi { codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_support]) as Map?; + .send([arg_instanceId, arg_support]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -978,12 +987,11 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptEnabled', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_flag]) as Map?; + .send([arg_instanceId, arg_flag]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -999,18 +1007,17 @@ class WebSettingsHostApi { } Future setUserAgentString( - int arg_instanceId, String arg_userAgentString) async { + int arg_instanceId, String? arg_userAgentString) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_userAgentString]) + await channel.send([arg_instanceId, arg_userAgentString]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1032,12 +1039,11 @@ class WebSettingsHostApi { codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_require]) as Map?; + .send([arg_instanceId, arg_require]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1057,12 +1063,11 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.setSupportZoom', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_support]) as Map?; + .send([arg_instanceId, arg_support]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1082,13 +1087,13 @@ class WebSettingsHostApi { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebSettingsHostApi.setLoadWithOverviewMode', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_overview]) as Map?; + final Map? replyMap = + await channel.send([arg_instanceId, arg_overview]) + as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1108,12 +1113,11 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.setUseWideViewPort', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_use]) as Map?; + .send([arg_instanceId, arg_use]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1134,12 +1138,11 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.setDisplayZoomControls', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_enabled]) as Map?; + .send([arg_instanceId, arg_enabled]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1160,12 +1163,11 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.setBuiltInZoomControls', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_enabled]) as Map?; + .send([arg_instanceId, arg_enabled]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1185,12 +1187,11 @@ class WebSettingsHostApi { 'dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_enabled]) as Map?; + .send([arg_instanceId, arg_enabled]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1226,13 +1227,12 @@ class JavaScriptChannelHostApi { 'dev.flutter.pigeon.JavaScriptChannelHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_channelName]) + await channel.send([arg_instanceId, arg_channelName]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1325,13 +1325,12 @@ class WebViewClientHostApi { 'dev.flutter.pigeon.WebViewClientHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_shouldOverrideUrlLoading]) + .send([arg_instanceId, arg_shouldOverrideUrlLoading]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1600,12 +1599,11 @@ class DownloadListenerHostApi { 'dev.flutter.pigeon.DownloadListenerHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1712,13 +1710,12 @@ class WebChromeClientHostApi { 'dev.flutter.pigeon.WebChromeClientHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_webViewClientInstanceId]) + .send([arg_instanceId, arg_webViewClientInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1754,12 +1751,11 @@ class FlutterAssetManagerHostApi { 'dev.flutter.pigeon.FlutterAssetManagerHostApi.list', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_path]) as Map?; + await channel.send([arg_path]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1769,6 +1765,11 @@ class FlutterAssetManagerHostApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as List?)!.cast(); } @@ -1780,12 +1781,11 @@ class FlutterAssetManagerHostApi { codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_name]) as Map?; + await channel.send([arg_name]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1795,6 +1795,11 @@ class FlutterAssetManagerHostApi { message: error['message'] as String?, details: error['details'], ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { return (replyMap['result'] as String?)!; } @@ -1881,12 +1886,11 @@ class WebStorageHostApi { 'dev.flutter.pigeon.WebStorageHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = @@ -1906,12 +1910,11 @@ class WebStorageHostApi { 'dev.flutter.pigeon.WebStorageHostApi.deleteAllData', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { final Map error = 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 9c980c80d58d..b40a0518dca0 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 @@ -13,21 +13,20 @@ import 'instance_manager.dart'; /// Converts [WebResourceRequestData] to [WebResourceRequest] WebResourceRequest _toWebResourceRequest(WebResourceRequestData data) { return WebResourceRequest( - url: data.url!, - isForMainFrame: data.isForMainFrame!, + url: data.url, + isForMainFrame: data.isForMainFrame, isRedirect: data.isRedirect, - hasGesture: data.hasGesture!, - method: data.method!, - requestHeaders: - data.requestHeaders?.cast() ?? {}, + hasGesture: data.hasGesture, + method: data.method, + requestHeaders: data.requestHeaders.cast(), ); } /// Converts [WebResourceErrorData] to [WebResourceError]. WebResourceError _toWebResourceError(WebResourceErrorData data) { return WebResourceError( - errorCode: data.errorCode!, - description: data.description!, + errorCode: data.errorCode, + description: data.description, ); } @@ -115,8 +114,8 @@ class WebViewHostApiImpl extends WebViewHostApi { Future loadDataFromInstance( WebView instance, String data, - String mimeType, - String encoding, + String? mimeType, + String? encoding, ) { return loadData( instanceManager.getInstanceId(instance)!, @@ -129,11 +128,11 @@ class WebViewHostApiImpl extends WebViewHostApi { /// Helper method to convert instances ids to objects. Future loadDataWithBaseUrlFromInstance( WebView instance, - String baseUrl, + String? baseUrl, String data, - String mimeType, - String encoding, - String historyUrl, + String? mimeType, + String? encoding, + String? historyUrl, ) { return loadDataWithBaseUrl( instanceManager.getInstanceId(instance)!, @@ -164,7 +163,7 @@ class WebViewHostApiImpl extends WebViewHostApi { } /// Helper method to convert instances ids to objects. - Future getUrlFromInstance(WebView instance) { + Future getUrlFromInstance(WebView instance) { return getUrl(instanceManager.getInstanceId(instance)!); } @@ -202,16 +201,18 @@ class WebViewHostApiImpl extends WebViewHostApi { } /// Helper method to convert instances ids to objects. - Future evaluateJavascriptFromInstance( + Future evaluateJavascriptFromInstance( WebView instance, String javascriptString, ) { return evaluateJavascript( - instanceManager.getInstanceId(instance)!, javascriptString); + instanceManager.getInstanceId(instance)!, + javascriptString, + ); } /// Helper method to convert instances ids to objects. - Future getTitleFromInstance(WebView instance) { + Future getTitleFromInstance(WebView instance) { return getTitle(instanceManager.getInstanceId(instance)!); } @@ -271,22 +272,22 @@ class WebViewHostApiImpl extends WebViewHostApi { /// Helper method to convert instances ids to objects. Future setDownloadListenerFromInstance( WebView instance, - DownloadListener listener, + DownloadListener? listener, ) { return setDownloadListener( instanceManager.getInstanceId(instance)!, - instanceManager.getInstanceId(listener)!, + listener != null ? instanceManager.getInstanceId(listener) : null, ); } /// Helper method to convert instances ids to objects. Future setWebChromeClientFromInstance( WebView instance, - WebChromeClient client, + WebChromeClient? client, ) { return setWebChromeClient( instanceManager.getInstanceId(instance)!, - instanceManager.getInstanceId(client)!, + client != null ? instanceManager.getInstanceId(client) : null, ); } @@ -370,7 +371,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { /// Helper method to convert instances ids to objects. Future setUserAgentStringFromInstance( WebSettings instance, - String userAgentString, + String? userAgentString, ) { return setUserAgentString( instanceManager.getInstanceId(instance)!, 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 d3d18f64f97d..70ecd99d3638 100644 --- a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart @@ -4,18 +4,51 @@ import 'package:pigeon/pigeon.dart'; +@ConfigurePigeon( + PigeonOptions( + dartOut: 'lib/src/android_webview.pigeon.dart', + dartTestOut: 'test/test_android_webview.pigeon.dart', + dartOptions: DartOptions(copyrightHeader: [ + '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.', + ]), + javaOut: + 'android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java', + javaOptions: JavaOptions( + package: 'io.flutter.plugins.webviewflutter', + className: 'GeneratedAndroidWebView', + copyrightHeader: [ + '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.', + ], + ), + ), +) class WebResourceRequestData { - String? url; - bool? isForMainFrame; + WebResourceRequestData( + this.url, + this.isForMainFrame, + this.isRedirect, + this.hasGesture, + this.method, + this.requestHeaders, + ); + + String url; + bool isForMainFrame; bool? isRedirect; - bool? hasGesture; - String? method; - Map? requestHeaders; + bool hasGesture; + String method; + Map requestHeaders; } class WebResourceErrorData { - int? errorCode; - String? description; + WebResourceErrorData(this.errorCode, this.description); + + int errorCode; + String description; } @HostApi() @@ -35,17 +68,17 @@ abstract class WebViewHostApi { void loadData( int instanceId, String data, - String mimeType, - String encoding, + String? mimeType, + String? encoding, ); void loadDataWithBaseUrl( int instanceId, - String baseUrl, + String? baseUrl, String data, - String mimeType, - String encoding, - String historyUrl, + String? mimeType, + String? encoding, + String? historyUrl, ); void loadUrl( @@ -60,7 +93,7 @@ abstract class WebViewHostApi { Uint8List data, ); - String getUrl(int instanceId); + String? getUrl(int instanceId); bool canGoBack(int instanceId); @@ -75,12 +108,12 @@ abstract class WebViewHostApi { void clearCache(int instanceId, bool includeDiskFiles); @async - String evaluateJavascript( + String? evaluateJavascript( int instanceId, String javascriptString, ); - String getTitle(int instanceId); + String? getTitle(int instanceId); void scrollTo(int instanceId, int x, int y); @@ -98,9 +131,9 @@ abstract class WebViewHostApi { void removeJavaScriptChannel(int instanceId, int javaScriptChannelInstanceId); - void setDownloadListener(int instanceId, int listenerInstanceId); + void setDownloadListener(int instanceId, int? listenerInstanceId); - void setWebChromeClient(int instanceId, int clientInstanceId); + void setWebChromeClient(int instanceId, int? clientInstanceId); void setBackgroundColor(int instanceId, int color); } @@ -119,7 +152,7 @@ abstract class WebSettingsHostApi { void setJavaScriptEnabled(int instanceId, bool flag); - void setUserAgentString(int instanceId, String userAgentString); + void setUserAgentString(int instanceId, String? userAgentString); void setMediaPlaybackRequiresUserGesture(int instanceId, bool require); diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 3fae698d1481..9a7c48a4dcd8 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/plugins/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: 2.8.5 +version: 2.8.6 environment: sdk: ">=2.14.0 <3.0.0" @@ -29,4 +29,4 @@ dev_dependencies: sdk: flutter mockito: ^5.1.0 pedantic: ^1.10.0 - pigeon: 1.0.9 + pigeon: ^3.0.3 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 e2e6513dd841..4c63ab025702 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 @@ -10,8 +10,8 @@ import 'package:webview_flutter_android/src/android_webview.pigeon.dart'; import 'package:webview_flutter_android/src/android_webview_api_impls.dart'; import 'package:webview_flutter_android/src/instance_manager.dart'; -import 'android_webview.pigeon.dart'; import 'android_webview_test.mocks.dart'; +import 'test_android_webview.pigeon.dart'; @GenerateMocks([ CookieManagerHostApi, @@ -85,8 +85,8 @@ void main() { verify(mockPlatformHostApi.loadData( webViewInstanceId, 'hello', - '', - '', + null, + null, )); }); @@ -113,11 +113,11 @@ void main() { webView.loadDataWithBaseUrl(data: 'hello'); verify(mockPlatformHostApi.loadDataWithBaseUrl( webViewInstanceId, - '', + null, 'hello', - '', - '', - '', + null, + null, + null, )); }); @@ -527,14 +527,15 @@ void main() { flutterApi.onReceivedRequestError( mockWebViewClientInstanceId, mockWebViewInstanceId, - WebResourceRequestData() - ..url = 'https://www.google.com' - ..isForMainFrame = true - ..hasGesture = true - ..method = 'POST', - WebResourceErrorData() - ..errorCode = 34 - ..description = 'error description', + WebResourceRequestData( + url: 'https://www.google.com', + isForMainFrame: true, + hasGesture: true, + method: 'POST', + isRedirect: false, + requestHeaders: {}, + ), + WebResourceErrorData(errorCode: 34, description: 'error description'), ); verify(mockWebViewClient.onReceivedRequestError( @@ -565,11 +566,14 @@ void main() { flutterApi.requestLoading( mockWebViewClientInstanceId, mockWebViewInstanceId, - WebResourceRequestData() - ..url = 'https://www.google.com' - ..isForMainFrame = true - ..hasGesture = true - ..method = 'POST', + WebResourceRequestData( + url: 'https://www.google.com', + isForMainFrame: true, + hasGesture: true, + method: 'POST', + isRedirect: true, + requestHeaders: {}, + ), ); verify(mockWebViewClient.requestLoading( 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 e8859c9b74c5..85ab6685ca34 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 @@ -10,7 +10,7 @@ import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_android/src/android_webview.dart' as _i2; import 'package:webview_flutter_android/src/android_webview.pigeon.dart' as _i3; -import 'android_webview.pigeon.dart' as _i5; +import 'test_android_webview.pigeon.dart' as _i5; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -277,9 +277,8 @@ class MockTestWebViewHostApi extends _i1.Mock super.noSuchMethod(Invocation.method(#postUrl, [instanceId, url, data]), returnValueForMissingStub: null); @override - String getUrl(int? instanceId) => - (super.noSuchMethod(Invocation.method(#getUrl, [instanceId]), - returnValue: '') as String); + String? getUrl(int? instanceId) => + (super.noSuchMethod(Invocation.method(#getUrl, [instanceId])) as String?); @override bool canGoBack(int? instanceId) => (super.noSuchMethod(Invocation.method(#canGoBack, [instanceId]), @@ -306,16 +305,16 @@ class MockTestWebViewHostApi extends _i1.Mock Invocation.method(#clearCache, [instanceId, includeDiskFiles]), returnValueForMissingStub: null); @override - _i4.Future evaluateJavascript( + _i4.Future evaluateJavascript( int? instanceId, String? javascriptString) => (super.noSuchMethod( Invocation.method( #evaluateJavascript, [instanceId, javascriptString]), - returnValue: Future.value('')) as _i4.Future); + returnValue: Future.value()) as _i4.Future); @override - String getTitle(int? instanceId) => - (super.noSuchMethod(Invocation.method(#getTitle, [instanceId]), - returnValue: '') as String); + String? getTitle(int? instanceId) => + (super.noSuchMethod(Invocation.method(#getTitle, [instanceId])) + as String?); @override void scrollTo(int? instanceId, int? x, int? y) => super.noSuchMethod(Invocation.method(#scrollTo, [instanceId, x, y]), diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart similarity index 95% rename from packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart rename to packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart index 4ee4b1ddc0b7..e3c5909f3207 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart @@ -1,10 +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. - -// Autogenerated from Pigeon (v1.0.9), do not edit directly. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis +// ignore_for_file: avoid_relative_lib_imports // @dart = 2.12 import 'dart:async'; import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; @@ -23,20 +23,21 @@ abstract class TestWebViewHostApi { void create(int instanceId, bool useHybridComposition); void dispose(int instanceId); - void loadData(int instanceId, String data, String mimeType, String encoding); - void loadDataWithBaseUrl(int instanceId, String baseUrl, String data, - String mimeType, String encoding, String historyUrl); + void loadData( + int instanceId, String data, String? mimeType, String? encoding); + void loadDataWithBaseUrl(int instanceId, String? baseUrl, String data, + String? mimeType, String? encoding, String? historyUrl); void loadUrl(int instanceId, String url, Map headers); void postUrl(int instanceId, String url, Uint8List data); - String getUrl(int instanceId); + String? getUrl(int instanceId); bool canGoBack(int instanceId); bool canGoForward(int instanceId); void goBack(int instanceId); void goForward(int instanceId); void reload(int instanceId); void clearCache(int instanceId, bool includeDiskFiles); - Future evaluateJavascript(int instanceId, String javascriptString); - String getTitle(int instanceId); + Future evaluateJavascript(int instanceId, String javascriptString); + String? getTitle(int instanceId); void scrollTo(int instanceId, int x, int y); void scrollBy(int instanceId, int x, int y); int getScrollX(int instanceId); @@ -45,8 +46,8 @@ abstract class TestWebViewHostApi { void setWebViewClient(int instanceId, int webViewClientInstanceId); void addJavaScriptChannel(int instanceId, int javaScriptChannelInstanceId); void removeJavaScriptChannel(int instanceId, int javaScriptChannelInstanceId); - void setDownloadListener(int instanceId, int listenerInstanceId); - void setWebChromeClient(int instanceId, int clientInstanceId); + void setDownloadListener(int instanceId, int? listenerInstanceId); + void setWebChromeClient(int instanceId, int? clientInstanceId); void setBackgroundColor(int instanceId, int color); static void setup(TestWebViewHostApi? api, {BinaryMessenger? binaryMessenger}) { @@ -109,13 +110,8 @@ abstract class TestWebViewHostApi { assert(arg_data != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadData was null, expected non-null String.'); final String? arg_mimeType = (args[2] as String?); - assert(arg_mimeType != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.loadData was null, expected non-null String.'); final String? arg_encoding = (args[3] as String?); - assert(arg_encoding != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.loadData was null, expected non-null String.'); - api.loadData( - arg_instanceId!, arg_data!, arg_mimeType!, arg_encoding!); + api.loadData(arg_instanceId!, arg_data!, arg_mimeType, arg_encoding); return {}; }); } @@ -135,22 +131,14 @@ abstract class TestWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null int.'); final String? arg_baseUrl = (args[1] as String?); - assert(arg_baseUrl != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.'); final String? arg_data = (args[2] as String?); assert(arg_data != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.'); final String? arg_mimeType = (args[3] as String?); - assert(arg_mimeType != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.'); final String? arg_encoding = (args[4] as String?); - assert(arg_encoding != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.'); final String? arg_historyUrl = (args[5] as String?); - assert(arg_historyUrl != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.'); - api.loadDataWithBaseUrl(arg_instanceId!, arg_baseUrl!, arg_data!, - arg_mimeType!, arg_encoding!, arg_historyUrl!); + api.loadDataWithBaseUrl(arg_instanceId!, arg_baseUrl, arg_data!, + arg_mimeType, arg_encoding, arg_historyUrl); return {}; }); } @@ -220,7 +208,7 @@ abstract class TestWebViewHostApi { final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getUrl was null, expected non-null int.'); - final String output = api.getUrl(arg_instanceId!); + final String? output = api.getUrl(arg_instanceId!); return {'result': output}; }); } @@ -359,7 +347,7 @@ abstract class TestWebViewHostApi { final String? arg_javascriptString = (args[1] as String?); assert(arg_javascriptString != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.evaluateJavascript was null, expected non-null String.'); - final String output = await api.evaluateJavascript( + final String? output = await api.evaluateJavascript( arg_instanceId!, arg_javascriptString!); return {'result': output}; }); @@ -379,7 +367,7 @@ abstract class TestWebViewHostApi { final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getTitle was null, expected non-null int.'); - final String output = api.getTitle(arg_instanceId!); + final String? output = api.getTitle(arg_instanceId!); return {'result': output}; }); } @@ -575,9 +563,7 @@ abstract class TestWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setDownloadListener was null, expected non-null int.'); final int? arg_listenerInstanceId = (args[1] as int?); - assert(arg_listenerInstanceId != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.setDownloadListener was null, expected non-null int.'); - api.setDownloadListener(arg_instanceId!, arg_listenerInstanceId!); + api.setDownloadListener(arg_instanceId!, arg_listenerInstanceId); return {}; }); } @@ -597,9 +583,7 @@ abstract class TestWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebChromeClient was null, expected non-null int.'); final int? arg_clientInstanceId = (args[1] as int?); - assert(arg_clientInstanceId != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebChromeClient was null, expected non-null int.'); - api.setWebChromeClient(arg_instanceId!, arg_clientInstanceId!); + api.setWebChromeClient(arg_instanceId!, arg_clientInstanceId); return {}; }); } @@ -642,7 +626,7 @@ abstract class TestWebSettingsHostApi { void setJavaScriptCanOpenWindowsAutomatically(int instanceId, bool flag); void setSupportMultipleWindows(int instanceId, bool support); void setJavaScriptEnabled(int instanceId, bool flag); - void setUserAgentString(int instanceId, String userAgentString); + void setUserAgentString(int instanceId, String? userAgentString); void setMediaPlaybackRequiresUserGesture(int instanceId, bool require); void setSupportZoom(int instanceId, bool support); void setLoadWithOverviewMode(int instanceId, bool overview); @@ -799,9 +783,7 @@ abstract class TestWebSettingsHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString was null, expected non-null int.'); final String? arg_userAgentString = (args[1] as String?); - assert(arg_userAgentString != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString was null, expected non-null String.'); - api.setUserAgentString(arg_instanceId!, arg_userAgentString!); + api.setUserAgentString(arg_instanceId!, arg_userAgentString); return {}; }); } diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart index 46d6a29a1107..83662fb81f02 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart @@ -16,8 +16,8 @@ import 'package:webview_flutter_android/src/instance_manager.dart'; import 'package:webview_flutter_android/webview_android_widget.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; -import 'android_webview.pigeon.dart'; import 'android_webview_test.mocks.dart' show MockTestWebViewHostApi; +import 'test_android_webview.pigeon.dart'; import 'webview_android_widget_test.mocks.dart'; @GenerateMocks([ From 4b687c9fa75e97b219a7017720378fc7bb0f5b12 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Thu, 28 Apr 2022 14:16:04 -0700 Subject: [PATCH 204/844] [video_player] Fix XCUITest based on the new tooltip accessibility label (#5426) The accessibility label for tool tip seems change in one of the recent flutter updates(flutter/flutter#87684), which results failure in this particular XCUITest. This is blocking the flutter to plugins roll. Fixes: flutter/flutter#102698 --- .../video_player_avfoundation/CHANGELOG.md | 5 +++++ .../example/ios/RunnerUITests/VideoPlayerUITests.m | 12 ++++++++---- .../video_player_avfoundation/pubspec.yaml | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index a1f23eb670fc..3916503ce3d9 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.3.3 + +* Fix XCUITest based on the new voice over announcement for tooltips. + See: https://github.com/flutter/flutter/pull/87684 + ## 2.3.2 * Applies the standardized transform for videos with different orientations. diff --git a/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m b/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m index 2933cf36feae..b9f0f16bb27b 100644 --- a/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m +++ b/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m @@ -30,16 +30,20 @@ - (void)testPlayVideo { XCTAssertTrue([playButton waitForExistenceWithTimeout:30.0]); [playButton tap]; - XCUIElement *playbackSpeed1x = app.staticTexts[@"Playback speed\n1.0x"]; - XCTAssertTrue([playbackSpeed1x waitForExistenceWithTimeout:30.0]); + NSPredicate *find1xButton = [NSPredicate predicateWithFormat:@"label CONTAINS '1.0x'"]; + XCUIElement *playbackSpeed1x = [app.staticTexts elementMatchingPredicate:find1xButton]; + BOOL foundPlaybackSpeed1x = [playbackSpeed1x waitForExistenceWithTimeout:30.0]; + XCTAssertTrue(foundPlaybackSpeed1x); [playbackSpeed1x tap]; XCUIElement *playbackSpeed5xButton = app.buttons[@"5.0x"]; XCTAssertTrue([playbackSpeed5xButton waitForExistenceWithTimeout:30.0]); [playbackSpeed5xButton tap]; - XCUIElement *playbackSpeed5x = app.staticTexts[@"Playback speed\n5.0x"]; - XCTAssertTrue([playbackSpeed5x waitForExistenceWithTimeout:30.0]); + NSPredicate *find5xButton = [NSPredicate predicateWithFormat:@"label CONTAINS '5.0x'"]; + XCUIElement *playbackSpeed5x = [app.staticTexts elementMatchingPredicate:find5xButton]; + BOOL foundPlaybackSpeed5x = [playbackSpeed5x waitForExistenceWithTimeout:30.0]; + XCTAssertTrue(foundPlaybackSpeed5x); // Cycle through tabs. for (NSString *tabName in @[ @"Asset", @"Remote" ]) { diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index 5874b52cba6f..b3cc69eca958 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.2 +version: 2.3.3 environment: sdk: ">=2.14.0 <3.0.0" From e777e515b5f09f13015f5bec9c1d909339bfa65b Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 28 Apr 2022 18:24:16 -0400 Subject: [PATCH 205/844] Roll Flutter from 1b58a593deac to 4cea9afc1224 (147 revisions) (#5437) --- .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 23335129b903..d7dca8a86441 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1b58a593deac48f1c3a6f579d53a0401372dc59f +4cea9afc12243850d4776003349fb9cff4c031fc From 656e8c44346f920fe2455c6e4d76058c45e2c606 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 28 Apr 2022 21:34:11 -0400 Subject: [PATCH 206/844] Roll Flutter from 4cea9afc1224 to 5b713147404d (4 revisions) (#5441) --- .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 d7dca8a86441..6aede9cabd01 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -4cea9afc12243850d4776003349fb9cff4c031fc +5b713147404d076f1a955bdb0becdd5fca816ed1 From f689280bf756d65cb9c9f17fb50f3be4c78af4a3 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 28 Apr 2022 22:29:09 -0400 Subject: [PATCH 207/844] [flutter_plugin_tools] Validate code blocks in readme-check (#5436) --- .cirrus.yml | 8 +- packages/camera/camera/README.md | 2 +- packages/espresso/README.md | 4 +- .../file_selector/file_selector/README.md | 4 +- .../file_selector_macos/README.md | 4 +- .../google_sign_in_web/README.md | 2 +- packages/url_launcher/url_launcher/README.md | 2 +- script/configs/temp_exclude_excerpt.yaml | 27 ++++ script/tool/CHANGELOG.md | 7 + script/tool/lib/src/readme_check_command.dart | 93 +++++++++++-- script/tool/pubspec.yaml | 2 +- .../tool/test/readme_check_command_test.dart | 131 ++++++++++++++++++ 12 files changed, 266 insertions(+), 20 deletions(-) create mode 100644 script/configs/temp_exclude_excerpt.yaml diff --git a/.cirrus.yml b/.cirrus.yml index c256ab19426e..fe5a18097c80 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -101,7 +101,13 @@ task: always: format_script: ./script/tool_runner.sh format --fail-on-change pubspec_script: ./script/tool_runner.sh pubspec-check - readme_script: ./script/tool_runner.sh readme-check + readme_script: + - ./script/tool_runner.sh readme-check + # Re-run with --require-excerpts, skipping packages that still need + # to be converted. Once https://github.com/flutter/flutter/issues/102679 + # has been fixed, this can be removed and there can just be a single + # run with --require-excerpts and no exclusions. + - ./script/tool_runner.sh readme-check --require-excerpts --exclude=script/configs/temp_exclude_excerpt.yaml license_script: dart $PLUGIN_TOOL license-check - name: federated_safety # This check is only meaningful for PRs, as it validates changes diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index a1c60a08950d..97b16d20f48a 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -46,7 +46,7 @@ If editing `Info.plist` as text, add: Change the minimum Android sdk version to 21 (or higher) in your `android/app/build.gradle` file. -``` +```groovy minSdkVersion 21 ``` diff --git a/packages/espresso/README.md b/packages/espresso/README.md index 68c3b55089ca..6d66bfbe85b5 100644 --- a/packages/espresso/README.md +++ b/packages/espresso/README.md @@ -85,13 +85,13 @@ void main() { The following command line command runs the test locally: -``` +```sh ./gradlew app:connectedAndroidTest -Ptarget=`pwd`/../test_driver/example.dart ``` Espresso tests can also be run on [Firebase Test Lab](https://firebase.google.com/docs/test-lab): -``` +```sh ./gradlew app:assembleAndroidTest ./gradlew app:assembleDebug -Ptarget=.dart gcloud auth activate-service-account --key-file= diff --git a/packages/file_selector/file_selector/README.md b/packages/file_selector/file_selector/README.md index 544cde8218bd..89cac1e6fd5f 100644 --- a/packages/file_selector/file_selector/README.md +++ b/packages/file_selector/file_selector/README.md @@ -14,12 +14,12 @@ To use this plugin, add `file_selector` as a [dependency in your pubspec.yaml fi ### macOS You will need to [add an entitlement][entitlement] for either read-only access: -``` +```xml com.apple.security.files.user-selected.read-only ``` or read/write access: -``` +```xml com.apple.security.files.user-selected.read-write ``` diff --git a/packages/file_selector/file_selector_macos/README.md b/packages/file_selector/file_selector_macos/README.md index efa5272149be..3241b21d1e18 100644 --- a/packages/file_selector/file_selector_macos/README.md +++ b/packages/file_selector/file_selector_macos/README.md @@ -17,12 +17,12 @@ APIs directly. ### Entitlements You will need to [add an entitlement][4] for either read-only access: -``` +```xml com.apple.security.files.user-selected.read-only ``` or read/write access: -``` +```xml com.apple.security.files.user-selected.read-write ``` diff --git a/packages/google_sign_in/google_sign_in_web/README.md b/packages/google_sign_in/google_sign_in_web/README.md index 4ee1a2956b45..463603e73e54 100644 --- a/packages/google_sign_in/google_sign_in_web/README.md +++ b/packages/google_sign_in/google_sign_in_web/README.md @@ -37,7 +37,7 @@ Normally `flutter run` starts in a random port. In the case where you need to de You can tell `flutter run` to listen for requests in a specific host and port with the following: -``` +```sh flutter run -d chrome --web-hostname localhost --web-port 7357 ``` diff --git a/packages/url_launcher/url_launcher/README.md b/packages/url_launcher/url_launcher/README.md index 0cdbe1b9859e..9c9f0b57e667 100644 --- a/packages/url_launcher/url_launcher/README.md +++ b/packages/url_launcher/url_launcher/README.md @@ -46,7 +46,7 @@ See the example app for more complex examples. Add any URL schemes passed to `canLaunchUrl` as `LSApplicationQueriesSchemes` entries in your Info.plist file. Example: -``` +```xml LSApplicationQueriesSchemes https diff --git a/script/configs/temp_exclude_excerpt.yaml b/script/configs/temp_exclude_excerpt.yaml new file mode 100644 index 000000000000..fc8454d75a0c --- /dev/null +++ b/script/configs/temp_exclude_excerpt.yaml @@ -0,0 +1,27 @@ +# Packages that have not yet adopted code-excerpt. +# +# This only exists to allow incrementally adopting the new requirement. +# Packages shoud never be added to this list. + +# TODO(ecosystem): Remove everything from this list. See +# https://github.com/flutter/flutter/issues/102679 +- camera_web +- espresso +- file_selector/file_selector +- google_maps_flutter/google_maps_flutter +- google_sign_in/google_sign_in +- google_sign_in_web +- image_picker/image_picker +- image_picker_for_web +- in_app_purchase/in_app_purchase +- ios_platform_images +- local_auth/local_auth +- path_provider/path_provider +- plugin_platform_interface +- quick_actions/quick_actions +- shared_preferences/shared_preferences +- url_launcher/url_launcher +- video_player/video_player +- webview_flutter/webview_flutter +- webview_flutter_android +- webview_flutter_web diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 7587ff33f027..1bce029a559f 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.8.4 + +- `readme-check` now validates that there's a info tag on code blocks to + identify (and for supported languages, syntax highlight) the language. +- `readme-check` now has a `--require-excerpts` flag to require that any Dart + code blocks be managed by `code_excerpter`. + ## 0.8.3 - Adds a new `update-excerpts` command to maintain README files using the diff --git a/script/tool/lib/src/readme_check_command.dart b/script/tool/lib/src/readme_check_command.dart index 99e271c73388..9432c4b7fa40 100644 --- a/script/tool/lib/src/readme_check_command.dart +++ b/script/tool/lib/src/readme_check_command.dart @@ -26,7 +26,12 @@ class ReadmeCheckCommand extends PackageLoopingCommand { processRunner: processRunner, platform: platform, gitDir: gitDir, - ); + ) { + argParser.addFlag(_requireExcerptsArg, + help: 'Require that Dart code blocks be managed by code-excerpt.'); + } + + static const String _requireExcerptsArg = 'require-excerpts'; // Standardized capitalizations for platforms that a plugin can support. static const Map _standardPlatformNames = { @@ -61,8 +66,15 @@ class ReadmeCheckCommand extends PackageLoopingCommand { final Pubspec pubspec = package.parsePubspec(); final bool isPlugin = pubspec.flutter?['plugin'] != null; + final List readmeLines = package.readmeFile.readAsLinesSync(); + + final String? blockValidationError = _validateCodeBlocks(readmeLines); + if (blockValidationError != null) { + errors.add(blockValidationError); + } + if (isPlugin && (!package.isFederated || package.isAppFacing)) { - final String? error = _validateSupportedPlatforms(package, pubspec); + final String? error = _validateSupportedPlatforms(readmeLines, pubspec); if (error != null) { errors.add(error); } @@ -73,23 +85,86 @@ class ReadmeCheckCommand extends PackageLoopingCommand { : PackageResult.fail(errors); } + /// Validates that code blocks (``` ... ```) follow repository standards. + String? _validateCodeBlocks(List readmeLines) { + final RegExp codeBlockDelimiterPattern = RegExp(r'^\s*```\s*([^ ]*)\s*'); + final List missingLanguageLines = []; + final List missingExcerptLines = []; + bool inBlock = false; + for (int i = 0; i < readmeLines.length; ++i) { + final RegExpMatch? match = + codeBlockDelimiterPattern.firstMatch(readmeLines[i]); + if (match == null) { + continue; + } + if (inBlock) { + inBlock = false; + continue; + } + inBlock = true; + + final int humanReadableLineNumber = i + 1; + + // Ensure that there's a language tag. + final String infoString = match[1] ?? ''; + if (infoString.isEmpty) { + missingLanguageLines.add(humanReadableLineNumber); + continue; + } + + // Check for code-excerpt usage if requested. + if (getBoolArg(_requireExcerptsArg) && infoString == 'dart') { + const String excerptTagStart = ' ' + 'tag on the previous line, and ensure that a build.excerpt.yaml is ' + 'configured for the source example.\n'); + errorSummary ??= 'Missing code-excerpt management for code block'; + } + + return errorSummary; + } + /// Validates that the plugin has a supported platforms table following the /// expected format, returning an error string if any issues are found. String? _validateSupportedPlatforms( - RepositoryPackage package, Pubspec pubspec) { - final List contents = package.readmeFile.readAsLinesSync(); - + List readmeLines, Pubspec pubspec) { // Example table following expected format: // | | Android | iOS | Web | // |----------------|---------|----------|------------------------| // | **Support** | SDK 21+ | iOS 10+* | [See `camera_web `][1] | - final int detailsLineNumber = - contents.indexWhere((String line) => line.startsWith('| **Support**')); + final int detailsLineNumber = readmeLines + .indexWhere((String line) => line.startsWith('| **Support**')); if (detailsLineNumber == -1) { return 'No OS support table found'; } final int osLineNumber = detailsLineNumber - 2; - if (osLineNumber < 0 || !contents[osLineNumber].startsWith('|')) { + if (osLineNumber < 0 || !readmeLines[osLineNumber].startsWith('|')) { return 'OS support table does not have the expected header format'; } @@ -111,7 +186,7 @@ class ReadmeCheckCommand extends PackageLoopingCommand { final YamlMap platformSupportMaps = platformsEntry as YamlMap; final Set actuallySupportedPlatform = platformSupportMaps.keys.toSet().cast(); - final Iterable documentedPlatforms = contents[osLineNumber] + final Iterable documentedPlatforms = readmeLines[osLineNumber] .split('|') .map((String entry) => entry.trim()) .where((String entry) => entry.isNotEmpty); diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 9f9910f934f7..af38193294a5 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.8.3 +version: 0.8.4 dependencies: args: ^2.1.0 diff --git a/script/tool/test/readme_check_command_test.dart b/script/tool/test/readme_check_command_test.dart index aec2fa078454..b6e016dccab4 100644 --- a/script/tool/test/readme_check_command_test.dart +++ b/script/tool/test/readme_check_command_test.dart @@ -275,4 +275,135 @@ A very useful plugin. ); }); }); + + group('code blocks', () { + test('fails on missing info string', () async { + final Directory packageDir = createFakePackage('a_package', packagesDir); + + packageDir.childFile('README.md').writeAsStringSync(''' +Example: + +``` +void main() { + // ... +} +``` +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Code block at line 3 is missing a language identifier.'), + contains('Missing language identifier for code block'), + ]), + ); + }); + + test('allows unknown info strings', () async { + final Directory packageDir = createFakePackage('a_package', packagesDir); + + packageDir.childFile('README.md').writeAsStringSync(''' +Example: + +```someunknowninfotag +A B C +``` +'''); + + final List output = await runCapturingPrint(runner, [ + 'readme-check', + ]); + + expect( + output, + containsAll([ + contains('Running for a_package...'), + contains('No issues found!'), + ]), + ); + }); + + test('allows space around info strings', () async { + final Directory packageDir = createFakePackage('a_package', packagesDir); + + packageDir.childFile('README.md').writeAsStringSync(''' +Example: + +``` dart +A B C +``` +'''); + + final List output = await runCapturingPrint(runner, [ + 'readme-check', + ]); + + expect( + output, + containsAll([ + contains('Running for a_package...'), + contains('No issues found!'), + ]), + ); + }); + + test('passes when excerpt requirement is met', () async { + final Directory packageDir = createFakePackage('a_package', packagesDir); + + packageDir.childFile('README.md').writeAsStringSync(''' +Example: + + +```dart +A B C +``` +'''); + + final List output = await runCapturingPrint( + runner, ['readme-check', '--require-excerpts']); + + expect( + output, + containsAll([ + contains('Running for a_package...'), + contains('No issues found!'), + ]), + ); + }); + + test('fails on missing excerpt tag when requested', () async { + final Directory packageDir = createFakePackage('a_package', packagesDir); + + packageDir.childFile('README.md').writeAsStringSync(''' +Example: + +```dart +A B C +``` +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check', '--require-excerpts'], + errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Dart code block at line 3 is not managed by code-excerpt.'), + contains('Missing code-excerpt management for code block'), + ]), + ); + }); + }); } From 9c41c6895803a6174eb79b96b9f94e1b09854d39 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 28 Apr 2022 23:09:08 -0400 Subject: [PATCH 208/844] Roll Flutter from 5b713147404d to 7a74222832f8 (1 revision) (#5442) --- .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 6aede9cabd01..10fc1313c8eb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -5b713147404d076f1a955bdb0becdd5fca816ed1 +7a74222832f8da20cd7e2d202bc83089e8a7d123 From 160c714e75f3933f4c883ecd0e79ada6f729c27c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 29 Apr 2022 00:14:09 -0400 Subject: [PATCH 209/844] Roll Flutter from 7a74222832f8 to 2eed8cbf9370 (1 revision) (#5443) --- .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 10fc1313c8eb..2340a040f39e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -7a74222832f8da20cd7e2d202bc83089e8a7d123 +2eed8cbf937029f140c5bfbba920ff838e7d4871 From b247855a3b3e71886eba865cabc31becb013221e Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 29 Apr 2022 14:05:13 -0400 Subject: [PATCH 210/844] [ci] Don't run Cirrus tests on `master` (#5444) Addresses https://github.com/flutter/flutter/issues/102801 for this repository. --- .cirrus.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index fe5a18097c80..c26118d1443f 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,8 +1,9 @@ gcp_credentials: ENCRYPTED[!2c88dee9c9d9805b214c9f7ad8f3bc8fae936cdb0f881d562101151c408c7e024a41222677d5831df90c60d2dd6cd80a!] # Don't run on release tags since it creates O(n^2) tasks where n is the -# number of plugins -only_if: $CIRRUS_TAG == '' +# number of plugins. +# Don't run on 'master' since it's a mirror of 'main'. +only_if: $CIRRUS_TAG == '' && $CIRRUS_BRANCH != 'master' env: CHANNEL: "master" # Default to master when not explicitly set by a task. PLUGIN_TOOL: "./script/tool/bin/flutter_plugin_tools.dart" From 48cfd2a15b5b8c80ae7db7a9cdab1fb330b79e6a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 29 Apr 2022 14:49:09 -0400 Subject: [PATCH 211/844] Roll Flutter from 2eed8cbf9370 to ad1d30017e69 (1 revision) (#5445) --- .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 2340a040f39e..a7787d42ee77 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2eed8cbf937029f140c5bfbba920ff838e7d4871 +ad1d30017e69f91983edaf9bd6dae2a9f1c7b698 From 7a741bf0b51ecc30f8f3a9b03829d4bbcb661bd2 Mon Sep 17 00:00:00 2001 From: Nikolas Mayr Date: Fri, 29 Apr 2022 21:34:10 +0200 Subject: [PATCH 212/844] [webview_flutter] fix warning (#5131) --- .../webview_flutter_wkwebview/CHANGELOG.md | 4 +++ .../ios/Classes/FlutterWebView.m | 35 ++++++------------- .../webview_flutter_wkwebview/pubspec.yaml | 2 +- 3 files changed, 16 insertions(+), 25 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index bad900de46b4..058e8645dd7b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.7.3 + +* Removes two occurrences of the compiler warning: "'RequiresUserActionForMediaPlayback' is deprecated: first deprecated in ios 10.0". + ## 2.7.2 * Fixes an integration test race condition. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m index 61f112019686..5bb81fce89db 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m @@ -449,19 +449,14 @@ - (void)onRemoveJavaScriptChannels:(FlutterMethodCall *)call result:(FlutterResu } - (void)clearCache:(FlutterResult)result { - if (@available(iOS 9.0, *)) { - NSSet *cacheDataTypes = [WKWebsiteDataStore allWebsiteDataTypes]; - WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore]; - NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0]; - [dataStore removeDataOfTypes:cacheDataTypes - modifiedSince:dateFrom - completionHandler:^{ - result(nil); - }]; - } else { - // support for iOS8 tracked in https://github.com/flutter/flutter/issues/27624. - NSLog(@"Clearing cache is not supported for Flutter WebViews prior to iOS 9."); - } + NSSet *cacheDataTypes = [WKWebsiteDataStore allWebsiteDataTypes]; + WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore]; + NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0]; + [dataStore removeDataOfTypes:cacheDataTypes + modifiedSince:dateFrom + completionHandler:^{ + result(nil); + }]; } - (void)onGetTitle:(FlutterResult)result { @@ -571,24 +566,20 @@ - (void)updateAutoMediaPlaybackPolicy:(NSNumber *)policy case 0: // require_user_action_for_all_media_types if (@available(iOS 10.0, *)) { configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeAll; - } else if (@available(iOS 9.0, *)) { - configuration.requiresUserActionForMediaPlayback = true; } else { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - configuration.mediaPlaybackRequiresUserAction = true; + configuration.requiresUserActionForMediaPlayback = true; #pragma clang diagnostic pop } break; case 1: // always_allow if (@available(iOS 10.0, *)) { configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone; - } else if (@available(iOS 9.0, *)) { - configuration.requiresUserActionForMediaPlayback = false; } else { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - configuration.mediaPlaybackRequiresUserAction = false; + configuration.requiresUserActionForMediaPlayback = false; #pragma clang diagnostic pop } break; @@ -658,11 +649,7 @@ - (void)registerJavaScriptChannels:(NSSet *)channelNames } - (void)updateUserAgent:(NSString *)userAgent { - if (@available(iOS 9.0, *)) { - [_webView setCustomUserAgent:userAgent]; - } else { - NSLog(@"Updating UserAgent is not supported for Flutter WebViews prior to iOS 9."); - } + [_webView setCustomUserAgent:userAgent]; } /** diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 2a4a7a4f674d..e52d1df9bbb6 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/plugins/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: 2.7.2 +version: 2.7.3 environment: sdk: ">=2.14.0 <3.0.0" From a868c33370d305fb101b54a9e8f8ee769d5ef47a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 29 Apr 2022 16:14:14 -0400 Subject: [PATCH 213/844] Roll Flutter from ad1d30017e69 to 3fe2cbabc421 (2 revisions) (#5446) --- .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 a7787d42ee77..db063fd7b523 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ad1d30017e69f91983edaf9bd6dae2a9f1c7b698 +3fe2cbabc4216262e4635fe4f9fe6f53aee89fde From f66cda1a0318c54fa4b1fc3b5940032b3dcb196f Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 30 Apr 2022 07:34:08 -0400 Subject: [PATCH 214/844] Roll Flutter from 3fe2cbabc421 to 0b3c5d1228f1 (7 revisions) (#5449) --- .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 db063fd7b523..2bedcd7d5c3c 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3fe2cbabc4216262e4635fe4f9fe6f53aee89fde +0b3c5d1228f1cbd6099ba789ab7d8190cdb1a2ac From fc0905fa97e020d0ff383f55c385af2c06c73301 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 30 Apr 2022 08:39:08 -0400 Subject: [PATCH 215/844] Roll Flutter from 0b3c5d1228f1 to fa519eb22fe3 (15 revisions) (#5451) --- .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 2bedcd7d5c3c..11ab7f571fb5 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -0b3c5d1228f1cbd6099ba789ab7d8190cdb1a2ac +fa519eb22fe3fd1bc36f8a6c48b35efd99ec01eb From 3c841994ac5080a3afb9fc5810d087ef08a1dc64 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 30 Apr 2022 12:19:05 -0400 Subject: [PATCH 216/844] Roll Flutter from fa519eb22fe3 to 84e099d5f467 (1 revision) (#5452) --- .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 11ab7f571fb5..bddc8a28699d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fa519eb22fe3fd1bc36f8a6c48b35efd99ec01eb +84e099d5f46791d1304ca9bbb361390bb6521a8e From 7762d6f34cf0e6b262ba5475650694c229e38cab Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 30 Apr 2022 14:59:08 -0400 Subject: [PATCH 217/844] Roll Flutter from 84e099d5f467 to b0d5d8ab2930 (1 revision) (#5454) --- .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 bddc8a28699d..21a1d2109bee 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -84e099d5f46791d1304ca9bbb361390bb6521a8e +b0d5d8ab2930f94b10b600ba7893e3573a10a6fd From 355b6d7f21fcea6b70d09d1c3071d980ddda7469 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 30 Apr 2022 17:39:09 -0400 Subject: [PATCH 218/844] Roll Flutter from b0d5d8ab2930 to 2a24ee494db4 (2 revisions) (#5456) --- .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 21a1d2109bee..81dd56edbc55 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b0d5d8ab2930f94b10b600ba7893e3573a10a6fd +2a24ee494db49d2240692ef191e568e735518cb9 From 5c9ffc71e345d30ae5abbc8c5360d0807daa9d80 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Sat, 30 Apr 2022 15:17:33 -0700 Subject: [PATCH 219/844] [webview_flutter_wkwebview] Implementation of Objective-C WKWebView Host Apis (#5341) --- .../ios/Runner.xcodeproj/project.pbxproj | 10 +- .../ios/RunnerTests/FWFDataConvertersTests.m | 25 + .../ios/RunnerTests/FWFWebViewHostApiTests.m | 340 +++ .../ios/Classes/FLTWebViewFlutterPlugin.m | 1 + .../ios/Classes/FWFDataConverters.h | 19 + .../ios/Classes/FWFDataConverters.m | 25 + .../ios/Classes/FWFGeneratedWebKitApis.h | 437 ++++ .../ios/Classes/FWFGeneratedWebKitApis.m | 2126 +++++++++++++++++ .../ios/Classes/FWFInstanceManager.m | 6 +- .../ios/Classes/FWFWebViewHostApi.h | 40 + .../ios/Classes/FWFWebViewHostApi.m | 249 ++ .../ios/Classes/webview-umbrella.h | 3 + .../lib/src/common/web_kit.pigeon.dart | 76 +- .../pigeons/web_kit.dart | 88 +- .../webview_flutter_wkwebview/pubspec.yaml | 2 +- .../test/src/common/test_web_kit.pigeon.dart | 80 +- .../test/src/ui_kit/ui_kit_test.mocks.dart | 8 +- .../test/src/web_kit/web_kit_test.mocks.dart | 10 +- .../web_kit_webview_widget_test.mocks.dart | 29 +- 19 files changed, 3511 insertions(+), 63 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFDataConvertersTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj index a444be330949..c48b313daec2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 46; objects = { /* Begin PBXBuildFile section */ @@ -12,6 +12,8 @@ 334734022669319400DCC49E /* FLTWKNavigationDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 8FA6A87928062CD000A4B183 /* FWFInstanceManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */; }; + 8FB79B5328134C3100C101D3 /* FWFWebViewHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */; }; + 8FB79B55281B24F600C101D3 /* FWFDataConvertersTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B54281B24F600C101D3 /* FWFDataConvertersTests.m */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -69,6 +71,8 @@ 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFInstanceManagerTests.m; sourceTree = ""; }; + 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFWebViewHostApiTests.m; sourceTree = ""; }; + 8FB79B54281B24F600C101D3 /* FWFDataConvertersTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFDataConvertersTests.m; sourceTree = ""; }; 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; }; @@ -129,6 +133,8 @@ 68BDCAED23C3F7CB00D9C032 /* Info.plist */, E43693B427512C0F00382F85 /* FLTCookieManagerTests.m */, 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */, + 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */, + 8FB79B54281B24F600C101D3 /* FWFDataConvertersTests.m */, ); path = RunnerTests; sourceTree = ""; @@ -427,8 +433,10 @@ buildActionMask = 2147483647; files = ( 8FA6A87928062CD000A4B183 /* FWFInstanceManagerTests.m in Sources */, + 8FB79B5328134C3100C101D3 /* FWFWebViewHostApiTests.m in Sources */, 334734012669319100DCC49E /* FLTWebViewTests.m in Sources */, 334734022669319400DCC49E /* FLTWKNavigationDelegateTests.m in Sources */, + 8FB79B55281B24F600C101D3 /* FWFDataConvertersTests.m in Sources */, E43693B527512C0F00382F85 /* FLTCookieManagerTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; 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 new file mode 100644 index 000000000000..55952373998d --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFDataConvertersTests.m @@ -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 Flutter; +@import XCTest; +@import webview_flutter_wkwebview; + +@interface FWFDataConvertersTests : XCTestCase +@end + +@implementation FWFDataConvertersTests +- (void)testFNSURLRequestFromRequestData { + NSURLRequest *request = FWFNSURLRequestFromRequestData([FWFNSUrlRequestData + makeWithUrl:@"https://flutter.dev" + httpMethod:@"post" + httpBody:[FlutterStandardTypedData typedDataWithBytes:[NSData data]] + allHttpHeaderFields:@{@"a" : @"header"}]); + + XCTAssertEqualObjects(request.URL, [NSURL URLWithString:@"https://flutter.dev"]); + XCTAssertEqualObjects(request.HTTPMethod, @"POST"); + XCTAssertEqualObjects(request.HTTPBody, [NSData data]); + XCTAssertEqualObjects(request.allHTTPHeaderFields, @{@"a" : @"header"}); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m new file mode 100644 index 000000000000..dc0c1ce593d2 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m @@ -0,0 +1,340 @@ +// 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 XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFWebViewHostApiTests : XCTestCase +@end + +@implementation FWFWebViewHostApiTests +- (void)testLoadRequest { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + FWFNSUrlRequestData *requestData = [FWFNSUrlRequestData makeWithUrl:@"https://www.flutter.dev" + httpMethod:@"get" + httpBody:nil + allHttpHeaderFields:@{@"a" : @"header"}]; + [hostApi loadRequestForWebViewWithIdentifier:@0 request:requestData error:&error]; + + NSURL *url = [NSURL URLWithString:@"https://www.flutter.dev"]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + request.HTTPMethod = @"get"; + request.allHTTPHeaderFields = @{@"a" : @"header"}; + OCMVerify([mockWebView loadRequest:request]); + XCTAssertNil(error); +} + +- (void)testLoadRequestWithInvalidUrl { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + OCMReject([mockWebView loadRequest:OCMOCK_ANY]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + FWFNSUrlRequestData *requestData = [FWFNSUrlRequestData makeWithUrl:@"%invalidUrl%" + httpMethod:nil + httpBody:nil + allHttpHeaderFields:@{}]; + [hostApi loadRequestForWebViewWithIdentifier:@0 request:requestData error:&error]; + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.code, @"FWFURLRequestParsingError"); + XCTAssertEqualObjects(error.message, @"Failed instantiating an NSURLRequest."); + XCTAssertEqualObjects(error.details, @"URL was: '%invalidUrl%'"); +} + +- (void)testSetCustomUserAgent { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi setUserAgentForWebViewWithIdentifier:@0 userAgent:@"userA" error:&error]; + OCMVerify([mockWebView setCustomUserAgent:@"userA"]); + XCTAssertNil(error); +} + +- (void)testURL { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + OCMStub([mockWebView URL]).andReturn([NSURL URLWithString:@"https://www.flutter.dev/"]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + XCTAssertEqualObjects([hostApi URLForWebViewWithIdentifier:@0 error:&error], + @"https://www.flutter.dev/"); + XCTAssertNil(error); +} + +- (void)testCanGoBack { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + OCMStub([mockWebView canGoBack]).andReturn(YES); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + XCTAssertEqualObjects([hostApi canGoBackForWebViewWithIdentifier:@0 error:&error], @YES); + XCTAssertNil(error); +} + +- (void)testSetUIDelegate { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + id mockDelegate = OCMProtocolMock(@protocol(WKUIDelegate)); + [instanceManager addInstance:mockDelegate withIdentifier:1]; + + FlutterError *error; + [hostApi setUIDelegateForWebViewWithIdentifier:@0 delegateIdentifier:@1 error:&error]; + OCMVerify([mockWebView setUIDelegate:mockDelegate]); + XCTAssertNil(error); +} + +- (void)testSetNavigationDelegate { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + id mockDelegate = OCMProtocolMock(@protocol(WKNavigationDelegate)); + [instanceManager addInstance:mockDelegate withIdentifier:1]; + FlutterError *error; + + [hostApi setNavigationDelegateForWebViewWithIdentifier:@0 delegateIdentifier:@1 error:&error]; + OCMVerify([mockWebView setNavigationDelegate:mockDelegate]); + XCTAssertNil(error); +} + +- (void)testEstimatedProgress { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + OCMStub([mockWebView estimatedProgress]).andReturn(34.0); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + XCTAssertEqualObjects([hostApi estimatedProgressForWebViewWithIdentifier:@0 error:&error], @34.0); + XCTAssertNil(error); +} + +- (void)testloadHTMLString { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi loadHTMLForWebViewWithIdentifier:@0 + HTMLString:@"myString" + baseURL:@"myBaseUrl" + error:&error]; + OCMVerify([mockWebView loadHTMLString:@"myString" baseURL:[NSURL URLWithString:@"myBaseUrl"]]); + XCTAssertNil(error); +} + +- (void)testLoadFileURL { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi loadFileForWebViewWithIdentifier:@0 + fileURL:@"myFolder/apple.txt" + readAccessURL:@"myFolder" + error:&error]; + XCTAssertNil(error); + OCMVerify([mockWebView loadFileURL:[NSURL fileURLWithPath:@"myFolder/apple.txt" isDirectory:NO] + allowingReadAccessToURL:[NSURL fileURLWithPath:@"myFolder/" isDirectory:YES] + + ]); +} + +- (void)testLoadFlutterAsset { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFAssetManager *mockAssetManager = OCMClassMock([FWFAssetManager class]); + OCMStub([mockAssetManager lookupKeyForAsset:@"assets/index.html"]) + .andReturn(@"myFolder/assets/index.html"); + + NSBundle *mockBundle = OCMClassMock([NSBundle class]); + OCMStub([mockBundle URLForResource:@"myFolder/assets/index" withExtension:@"html"]) + .andReturn([NSURL URLWithString:@"webview_flutter/myFolder/assets/index.html"]); + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager + bundle:mockBundle + assetManager:mockAssetManager]; + + FlutterError *error; + [hostApi loadAssetForWebViewWithIdentifier:@0 assetKey:@"assets/index.html" error:&error]; + + XCTAssertNil(error); + OCMVerify([mockWebView + loadFileURL:[NSURL URLWithString:@"webview_flutter/myFolder/assets/index.html"] + allowingReadAccessToURL:[NSURL URLWithString:@"webview_flutter/myFolder/assets/"]]); +} + +- (void)testCanGoForward { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + OCMStub([mockWebView canGoForward]).andReturn(NO); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + XCTAssertEqualObjects([hostApi canGoForwardForWebViewWithIdentifier:@0 error:&error], @NO); + XCTAssertNil(error); +} + +- (void)testGoBack { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi goBackForWebViewWithIdentifier:@0 error:&error]; + OCMVerify([mockWebView goBack]); + XCTAssertNil(error); +} + +- (void)testGoForward { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi goForwardForWebViewWithIdentifier:@0 error:&error]; + OCMVerify([mockWebView goForward]); + XCTAssertNil(error); +} + +- (void)testReload { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi reloadWebViewWithIdentifier:@0 error:&error]; + OCMVerify([mockWebView reload]); + XCTAssertNil(error); +} + +- (void)testTitle { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + OCMStub([mockWebView title]).andReturn(@"myTitle"); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + XCTAssertEqualObjects([hostApi titleForWebViewWithIdentifier:@0 error:&error], @"myTitle"); + XCTAssertNil(error); +} + +- (void)testSetAllowsBackForwardNavigationGestures { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi setAllowsBackForwardForWebViewWithIdentifier:@0 isAllowed:@YES error:&error]; + OCMVerify([mockWebView setAllowsBackForwardNavigationGestures:YES]); + XCTAssertNil(error); +} + +- (void)testEvaluateJavaScript { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + OCMStub([mockWebView + evaluateJavaScript:@"runJavaScript" + completionHandler:([OCMArg invokeBlockWithArgs:@"result", [NSNull null], nil])]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + NSString __block *returnValue; + FlutterError __block *returnError; + [hostApi evaluateJavaScriptForWebViewWithIdentifier:@0 + javaScriptString:@"runJavaScript" + completion:^(id result, FlutterError *error) { + returnValue = result; + returnError = error; + }]; + + XCTAssertEqualObjects(returnValue, @"result"); + XCTAssertNil(returnError); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m index a4e87ba5177a..a63d6a60b114 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m @@ -4,6 +4,7 @@ #import "FLTWebViewFlutterPlugin.h" #import "FLTCookieManager.h" +#import "FWFWebViewHostApi.h" #import "FlutterWebView.h" @implementation FLTWebViewFlutterPlugin diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h new file mode 100644 index 000000000000..e9dbf2d9efa7 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h @@ -0,0 +1,19 @@ +// 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 "FWFGeneratedWebKitApis.h" + +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. + */ +extern NSURLRequest* _Nullable FWFNSURLRequestFromRequestData( + FWFNSUrlRequestData* data); + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m new file mode 100644 index 000000000000..1945bc3354f3 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m @@ -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 "FWFDataConverters.h" + +#import + +NSURLRequest *_Nullable FWFNSURLRequestFromRequestData(FWFNSUrlRequestData *data) { + NSURL *url = [NSURL URLWithString:data.url]; + if (!url) { + return nil; + } + + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + if (!request) { + return nil; + } + + [request setHTTPMethod:data.httpMethod]; + [request setHTTPBody:data.httpBody.data]; + [request setAllHTTPHeaderFields:data.allHttpHeaderFields]; + + return request; +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h new file mode 100644 index 000000000000..5306b300fc03 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h @@ -0,0 +1,437 @@ +// 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 (v3.0.3), do not edit directly. +// See also: https://pub.dev/packages/pigeon +#import +@protocol FlutterBinaryMessenger; +@protocol FlutterMessageCodec; +@class FlutterError; +@class FlutterStandardTypedData; + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, FWFNSKeyValueObservingOptionsEnum) { + FWFNSKeyValueObservingOptionsEnumNewValue = 0, + FWFNSKeyValueObservingOptionsEnumOldValue = 1, + FWFNSKeyValueObservingOptionsEnumInitialValue = 2, + FWFNSKeyValueObservingOptionsEnumPriorNotification = 3, +}; + +typedef NS_ENUM(NSUInteger, FWFNSKeyValueChangeEnum) { + FWFNSKeyValueChangeEnumSetting = 0, + FWFNSKeyValueChangeEnumInsertion = 1, + FWFNSKeyValueChangeEnumRemoval = 2, + FWFNSKeyValueChangeEnumReplacement = 3, +}; + +typedef NS_ENUM(NSUInteger, FWFNSKeyValueChangeKeyEnum) { + FWFNSKeyValueChangeKeyEnumIndexes = 0, + FWFNSKeyValueChangeKeyEnumKind = 1, + FWFNSKeyValueChangeKeyEnumNewValue = 2, + FWFNSKeyValueChangeKeyEnumNotificationIsPrior = 3, + FWFNSKeyValueChangeKeyEnumOldValue = 4, +}; + +typedef NS_ENUM(NSUInteger, FWFWKUserScriptInjectionTimeEnum) { + FWFWKUserScriptInjectionTimeEnumAtDocumentStart = 0, + FWFWKUserScriptInjectionTimeEnumAtDocumentEnd = 1, +}; + +typedef NS_ENUM(NSUInteger, FWFWKAudiovisualMediaTypeEnum) { + FWFWKAudiovisualMediaTypeEnumNone = 0, + FWFWKAudiovisualMediaTypeEnumAudio = 1, + FWFWKAudiovisualMediaTypeEnumVideo = 2, + FWFWKAudiovisualMediaTypeEnumAll = 3, +}; + +typedef NS_ENUM(NSUInteger, FWFWKWebsiteDataTypesEnum) { + FWFWKWebsiteDataTypesEnumCookies = 0, + FWFWKWebsiteDataTypesEnumMemoryCache = 1, + FWFWKWebsiteDataTypesEnumDiskCache = 2, + FWFWKWebsiteDataTypesEnumOfflineWebApplicationCache = 3, + FWFWKWebsiteDataTypesEnumLocalStroage = 4, + FWFWKWebsiteDataTypesEnumSessionStorage = 5, + FWFWKWebsiteDataTypesEnumSqlDatabases = 6, + FWFWKWebsiteDataTypesEnumIndexedDBDatabases = 7, +}; + +typedef NS_ENUM(NSUInteger, FWFWKNavigationActionPolicyEnum) { + FWFWKNavigationActionPolicyEnumAllow = 0, + FWFWKNavigationActionPolicyEnumCancel = 1, +}; + +typedef NS_ENUM(NSUInteger, FWFNSHttpCookiePropertyKeyEnum) { + FWFNSHttpCookiePropertyKeyEnumComment = 0, + FWFNSHttpCookiePropertyKeyEnumCommentUrl = 1, + FWFNSHttpCookiePropertyKeyEnumDiscard = 2, + FWFNSHttpCookiePropertyKeyEnumDomain = 3, + FWFNSHttpCookiePropertyKeyEnumExpires = 4, + FWFNSHttpCookiePropertyKeyEnumMaximumAge = 5, + FWFNSHttpCookiePropertyKeyEnumName = 6, + FWFNSHttpCookiePropertyKeyEnumOriginUrl = 7, + FWFNSHttpCookiePropertyKeyEnumPath = 8, + FWFNSHttpCookiePropertyKeyEnumPort = 9, + FWFNSHttpCookiePropertyKeyEnumSameSitePolicy = 10, + FWFNSHttpCookiePropertyKeyEnumSecure = 11, + FWFNSHttpCookiePropertyKeyEnumValue = 12, + FWFNSHttpCookiePropertyKeyEnumVersion = 13, +}; + +@class FWFNSKeyValueObservingOptionsEnumData; +@class FWFWKUserScriptInjectionTimeEnumData; +@class FWFWKAudiovisualMediaTypeEnumData; +@class FWFWKWebsiteDataTypesEnumData; +@class FWFNSHttpCookiePropertyKeyEnumData; +@class FWFNSUrlRequestData; +@class FWFWKUserScriptData; +@class FWFNSHttpCookieData; + +@interface FWFNSKeyValueObservingOptionsEnumData : NSObject ++ (instancetype)makeWithValue:(FWFNSKeyValueObservingOptionsEnum)value; +@property(nonatomic, assign) FWFNSKeyValueObservingOptionsEnum value; +@end + +@interface FWFWKUserScriptInjectionTimeEnumData : NSObject ++ (instancetype)makeWithValue:(FWFWKUserScriptInjectionTimeEnum)value; +@property(nonatomic, assign) FWFWKUserScriptInjectionTimeEnum value; +@end + +@interface FWFWKAudiovisualMediaTypeEnumData : NSObject ++ (instancetype)makeWithValue:(FWFWKAudiovisualMediaTypeEnum)value; +@property(nonatomic, assign) FWFWKAudiovisualMediaTypeEnum value; +@end + +@interface FWFWKWebsiteDataTypesEnumData : NSObject ++ (instancetype)makeWithValue:(FWFWKWebsiteDataTypesEnum)value; +@property(nonatomic, assign) FWFWKWebsiteDataTypesEnum value; +@end + +@interface FWFNSHttpCookiePropertyKeyEnumData : NSObject ++ (instancetype)makeWithValue:(FWFNSHttpCookiePropertyKeyEnum)value; +@property(nonatomic, assign) FWFNSHttpCookiePropertyKeyEnum value; +@end + +@interface FWFNSUrlRequestData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithUrl:(NSString *)url + httpMethod:(nullable NSString *)httpMethod + httpBody:(nullable FlutterStandardTypedData *)httpBody + allHttpHeaderFields:(NSDictionary *)allHttpHeaderFields; +@property(nonatomic, copy) NSString *url; +@property(nonatomic, copy, nullable) NSString *httpMethod; +@property(nonatomic, strong, nullable) FlutterStandardTypedData *httpBody; +@property(nonatomic, strong) NSDictionary *allHttpHeaderFields; +@end + +@interface FWFWKUserScriptData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithSource:(NSString *)source + injectionTime:(nullable FWFWKUserScriptInjectionTimeEnumData *)injectionTime + isMainFrameOnly:(NSNumber *)isMainFrameOnly; +@property(nonatomic, copy) NSString *source; +@property(nonatomic, strong, nullable) FWFWKUserScriptInjectionTimeEnumData *injectionTime; +@property(nonatomic, strong) NSNumber *isMainFrameOnly; +@end + +@interface FWFNSHttpCookieData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithProperties: + (NSDictionary *)properties; +@property(nonatomic, strong) + NSDictionary *properties; +@end + +/// The codec used by FWFWKWebsiteDataStoreHostApi. +NSObject *FWFWKWebsiteDataStoreHostApiGetCodec(void); + +@protocol FWFWKWebsiteDataStoreHostApi +- (void)createDataStoreFromConfigurationWithIdentifier:(NSNumber *)instanceId + configurationIdentifier:(NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)createDefaultDataStoreWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)removeDataFromDataStoreWithIdentifier:(NSNumber *)instanceId + ofTypes:(NSArray *)dataTypes + secondsModifiedSinceEpoch:(NSNumber *)secondsModifiedSinceEpoch + completion:(void (^)(NSNumber *_Nullable, + FlutterError *_Nullable))completion; +@end + +extern void FWFWKWebsiteDataStoreHostApiSetup( + id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFUIViewHostApi. +NSObject *FWFUIViewHostApiGetCodec(void); + +@protocol FWFUIViewHostApi +/// @return `nil` only when `error != nil`. +- (nullable NSArray *) + contentOffsetForViewWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setBackgroundColorForViewWithIdentifier:(NSNumber *)instanceId + toValue:(nullable NSNumber *)value + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setOpaqueForViewWithIdentifier:(NSNumber *)instanceId + isOpaque:(NSNumber *)opaque + error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FWFUIViewHostApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFUIScrollViewHostApi. +NSObject *FWFUIScrollViewHostApiGetCodec(void); + +@protocol FWFUIScrollViewHostApi +- (void)createFromWebViewWithIdentifier:(NSNumber *)instanceId + webViewIdentifier:(NSNumber *)webViewInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable NSArray *) + contentOffsetForScrollViewWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)scrollByForScrollViewWithIdentifier:(NSNumber *)instanceId + toX:(NSNumber *)x + y:(NSNumber *)y + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setContentOffsetForScrollViewWithIdentifier:(NSNumber *)instanceId + toX:(NSNumber *)x + y:(NSNumber *)y + error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FWFUIScrollViewHostApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFWKWebViewConfigurationHostApi. +NSObject *FWFWKWebViewConfigurationHostApiGetCodec(void); + +@protocol FWFWKWebViewConfigurationHostApi +- (void)createWithIdentifier:(NSNumber *)instanceId error:(FlutterError *_Nullable *_Nonnull)error; +- (void)createFromWebViewWithIdentifier:(NSNumber *)instanceId + webViewIdentifier:(NSNumber *)webViewInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:(NSNumber *)instanceId + isAlowed:(NSNumber *)allow + error: + (FlutterError *_Nullable *_Nonnull) + error; +- (void) + setMediaTypesRequiresUserActionForConfigurationWithIdentifier:(NSNumber *)instanceId + forTypes: + (NSArray< + FWFWKAudiovisualMediaTypeEnumData + *> *)types + error: + (FlutterError *_Nullable *_Nonnull) + error; +@end + +extern void FWFWKWebViewConfigurationHostApiSetup( + id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFWKUserContentControllerHostApi. +NSObject *FWFWKUserContentControllerHostApiGetCodec(void); + +@protocol FWFWKUserContentControllerHostApi +- (void)createFromWebViewConfigurationWithIdentifier:(NSNumber *)instanceId + configurationIdentifier:(NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)addScriptMessageHandlerForControllerWithIdentifier:(NSNumber *)instanceId + handlerIdentifier:(NSNumber *)handlerInstanceid + ofName:(NSString *)name + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)removeScriptMessageHandlerForControllerWithIdentifier:(NSNumber *)instanceId + name:(NSString *)name + error:(FlutterError *_Nullable *_Nonnull) + error; +- (void)removeAllScriptMessageHandlersForControllerWithIdentifier:(NSNumber *)instanceId + error: + (FlutterError *_Nullable *_Nonnull) + error; +- (void)addUserScriptForControllerWithIdentifier:(NSNumber *)instanceId + userScript:(FWFWKUserScriptData *)userScript + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)removeAllUserScriptsForControllerWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FWFWKUserContentControllerHostApiSetup( + id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFWKPreferencesHostApi. +NSObject *FWFWKPreferencesHostApiGetCodec(void); + +@protocol FWFWKPreferencesHostApi +- (void)createFromWebViewConfiguration:(NSNumber *)instanceId + configurationIdentifier:(NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setJavaScriptEnabledForPreferencesWithIdentifier:(NSNumber *)instanceId + isEnabled:(NSNumber *)enabled + error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FWFWKPreferencesHostApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFWKScriptMessageHandlerHostApi. +NSObject *FWFWKScriptMessageHandlerHostApiGetCodec(void); + +@protocol FWFWKScriptMessageHandlerHostApi +- (void)createWithIdentifier:(NSNumber *)instanceId error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FWFWKScriptMessageHandlerHostApiSetup( + id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFWKNavigationDelegateHostApi. +NSObject *FWFWKNavigationDelegateHostApiGetCodec(void); + +@protocol FWFWKNavigationDelegateHostApi +- (void)createWithIdentifier:(NSNumber *)instanceId error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setDidFinishNavigationForDelegateWithIdentifier:(NSNumber *)instanceId + functionIdentifier:(nullable NSNumber *)functionInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FWFWKNavigationDelegateHostApiSetup( + id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFWKNavigationDelegateFlutterApi. +NSObject *FWFWKNavigationDelegateFlutterApiGetCodec(void); + +@interface FWFWKNavigationDelegateFlutterApi : NSObject +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger; +- (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)functionInstanceId + webViewIdentifier:(NSNumber *)webViewInstanceId + URL:(nullable NSString *)url + completion:(void (^)(NSError *_Nullable))completion; +@end +/// The codec used by FWFNSObjectHostApi. +NSObject *FWFNSObjectHostApiGetCodec(void); + +@protocol FWFNSObjectHostApi +- (void)disposeObjectWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)addObserverForObjectWithIdentifier:(NSNumber *)instanceId + observerIdentifier:(NSNumber *)observerInstanceId + keyPath:(NSString *)keyPath + options: + (NSArray *)options + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)removeObserverForObjectWithIdentifier:(NSNumber *)instanceId + observerIdentifier:(NSNumber *)observerInstanceId + keyPath:(NSString *)keyPath + error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FWFNSObjectHostApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFFunctionFlutterApi. +NSObject *FWFFunctionFlutterApiGetCodec(void); + +@interface FWFFunctionFlutterApi : NSObject +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger; +- (void)disposeFunctionWithIdentifier:(NSNumber *)instanceId + completion:(void (^)(NSError *_Nullable))completion; +@end +/// The codec used by FWFWKWebViewHostApi. +NSObject *FWFWKWebViewHostApiGetCodec(void); + +@protocol FWFWKWebViewHostApi +- (void)createWithIdentifier:(NSNumber *)instanceId + configurationIdentifier:(NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setUIDelegateForWebViewWithIdentifier:(NSNumber *)instanceId + delegateIdentifier:(nullable NSNumber *)uiDelegateInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setNavigationDelegateForWebViewWithIdentifier:(NSNumber *)instanceId + delegateIdentifier: + (nullable NSNumber *)navigationDelegateInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (nullable NSString *)URLForWebViewWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable NSNumber *)estimatedProgressForWebViewWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull) + error; +- (void)loadRequestForWebViewWithIdentifier:(NSNumber *)instanceId + request:(FWFNSUrlRequestData *)request + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)loadHTMLForWebViewWithIdentifier:(NSNumber *)instanceId + HTMLString:(NSString *)string + baseURL:(nullable NSString *)baseUrl + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)loadFileForWebViewWithIdentifier:(NSNumber *)instanceId + fileURL:(NSString *)url + readAccessURL:(NSString *)readAccessUrl + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)loadAssetForWebViewWithIdentifier:(NSNumber *)instanceId + assetKey:(NSString *)key + error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable NSNumber *)canGoBackForWebViewWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable NSNumber *)canGoForwardForWebViewWithIdentifier:(NSNumber *)instanceId + error: + (FlutterError *_Nullable *_Nonnull)error; +- (void)goBackForWebViewWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)goForwardForWebViewWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)reloadWebViewWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (nullable NSString *)titleForWebViewWithIdentifier:(NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setAllowsBackForwardForWebViewWithIdentifier:(NSNumber *)instanceId + isAllowed:(NSNumber *)allow + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setUserAgentForWebViewWithIdentifier:(NSNumber *)instanceId + userAgent:(nullable NSString *)userAgent + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)evaluateJavaScriptForWebViewWithIdentifier:(NSNumber *)instanceId + javaScriptString:(NSString *)javaScriptString + completion:(void (^)(id _Nullable, + FlutterError *_Nullable))completion; +@end + +extern void FWFWKWebViewHostApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFWKUIDelegateHostApi. +NSObject *FWFWKUIDelegateHostApiGetCodec(void); + +@protocol FWFWKUIDelegateHostApi +- (void)createWithIdentifier:(NSNumber *)instanceId error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FWFWKUIDelegateHostApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +/// The codec used by FWFWKHttpCookieStoreHostApi. +NSObject *FWFWKHttpCookieStoreHostApiGetCodec(void); + +@protocol FWFWKHttpCookieStoreHostApi +- (void)createFromWebsiteDataStoreWithIdentifier:(NSNumber *)instanceId + dataStoreIdentifier:(NSNumber *)websiteDataStoreInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setCookieForStoreWithIdentifier:(NSNumber *)instanceId + cookie:(FWFNSHttpCookieData *)cookie + error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FWFWKHttpCookieStoreHostApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m new file mode 100644 index 000000000000..070b3c5cc033 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m @@ -0,0 +1,2126 @@ +// 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 (v3.0.3), do not edit directly. +// See also: https://pub.dev/packages/pigeon +#import "FWFGeneratedWebKitApis.h" +#import + +#if !__has_feature(objc_arc) +#error File requires ARC to be enabled. +#endif + +static NSDictionary *wrapResult(id result, FlutterError *error) { + NSDictionary *errorDict = (NSDictionary *)[NSNull null]; + if (error) { + errorDict = @{ + @"code" : (error.code ? error.code : [NSNull null]), + @"message" : (error.message ? error.message : [NSNull null]), + @"details" : (error.details ? error.details : [NSNull null]), + }; + } + return @{ + @"result" : (result ? result : [NSNull null]), + @"error" : errorDict, + }; +} +static id GetNullableObject(NSDictionary *dict, id key) { + id result = dict[key]; + return (result == [NSNull null]) ? nil : result; +} +static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { + id result = array[key]; + return (result == [NSNull null]) ? nil : result; +} + +@interface FWFNSKeyValueObservingOptionsEnumData () ++ (FWFNSKeyValueObservingOptionsEnumData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FWFWKUserScriptInjectionTimeEnumData () ++ (FWFWKUserScriptInjectionTimeEnumData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FWFWKAudiovisualMediaTypeEnumData () ++ (FWFWKAudiovisualMediaTypeEnumData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FWFWKWebsiteDataTypesEnumData () ++ (FWFWKWebsiteDataTypesEnumData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FWFNSHttpCookiePropertyKeyEnumData () ++ (FWFNSHttpCookiePropertyKeyEnumData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FWFNSUrlRequestData () ++ (FWFNSUrlRequestData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FWFWKUserScriptData () ++ (FWFWKUserScriptData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FWFNSHttpCookieData () ++ (FWFNSHttpCookieData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end + +@implementation FWFNSKeyValueObservingOptionsEnumData ++ (instancetype)makeWithValue:(FWFNSKeyValueObservingOptionsEnum)value { + FWFNSKeyValueObservingOptionsEnumData *pigeonResult = + [[FWFNSKeyValueObservingOptionsEnumData alloc] init]; + pigeonResult.value = value; + return pigeonResult; +} ++ (FWFNSKeyValueObservingOptionsEnumData *)fromMap:(NSDictionary *)dict { + FWFNSKeyValueObservingOptionsEnumData *pigeonResult = + [[FWFNSKeyValueObservingOptionsEnumData alloc] init]; + pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary dictionaryWithObjectsAndKeys:@(self.value), @"value", nil]; +} +@end + +@implementation FWFWKUserScriptInjectionTimeEnumData ++ (instancetype)makeWithValue:(FWFWKUserScriptInjectionTimeEnum)value { + FWFWKUserScriptInjectionTimeEnumData *pigeonResult = + [[FWFWKUserScriptInjectionTimeEnumData alloc] init]; + pigeonResult.value = value; + return pigeonResult; +} ++ (FWFWKUserScriptInjectionTimeEnumData *)fromMap:(NSDictionary *)dict { + FWFWKUserScriptInjectionTimeEnumData *pigeonResult = + [[FWFWKUserScriptInjectionTimeEnumData alloc] init]; + pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary dictionaryWithObjectsAndKeys:@(self.value), @"value", nil]; +} +@end + +@implementation FWFWKAudiovisualMediaTypeEnumData ++ (instancetype)makeWithValue:(FWFWKAudiovisualMediaTypeEnum)value { + FWFWKAudiovisualMediaTypeEnumData *pigeonResult = + [[FWFWKAudiovisualMediaTypeEnumData alloc] init]; + pigeonResult.value = value; + return pigeonResult; +} ++ (FWFWKAudiovisualMediaTypeEnumData *)fromMap:(NSDictionary *)dict { + FWFWKAudiovisualMediaTypeEnumData *pigeonResult = + [[FWFWKAudiovisualMediaTypeEnumData alloc] init]; + pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary dictionaryWithObjectsAndKeys:@(self.value), @"value", nil]; +} +@end + +@implementation FWFWKWebsiteDataTypesEnumData ++ (instancetype)makeWithValue:(FWFWKWebsiteDataTypesEnum)value { + FWFWKWebsiteDataTypesEnumData *pigeonResult = [[FWFWKWebsiteDataTypesEnumData alloc] init]; + pigeonResult.value = value; + return pigeonResult; +} ++ (FWFWKWebsiteDataTypesEnumData *)fromMap:(NSDictionary *)dict { + FWFWKWebsiteDataTypesEnumData *pigeonResult = [[FWFWKWebsiteDataTypesEnumData alloc] init]; + pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary dictionaryWithObjectsAndKeys:@(self.value), @"value", nil]; +} +@end + +@implementation FWFNSHttpCookiePropertyKeyEnumData ++ (instancetype)makeWithValue:(FWFNSHttpCookiePropertyKeyEnum)value { + FWFNSHttpCookiePropertyKeyEnumData *pigeonResult = + [[FWFNSHttpCookiePropertyKeyEnumData alloc] init]; + pigeonResult.value = value; + return pigeonResult; +} ++ (FWFNSHttpCookiePropertyKeyEnumData *)fromMap:(NSDictionary *)dict { + FWFNSHttpCookiePropertyKeyEnumData *pigeonResult = + [[FWFNSHttpCookiePropertyKeyEnumData alloc] init]; + pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary dictionaryWithObjectsAndKeys:@(self.value), @"value", nil]; +} +@end + +@implementation FWFNSUrlRequestData ++ (instancetype)makeWithUrl:(NSString *)url + httpMethod:(nullable NSString *)httpMethod + httpBody:(nullable FlutterStandardTypedData *)httpBody + allHttpHeaderFields:(NSDictionary *)allHttpHeaderFields { + FWFNSUrlRequestData *pigeonResult = [[FWFNSUrlRequestData alloc] init]; + pigeonResult.url = url; + pigeonResult.httpMethod = httpMethod; + pigeonResult.httpBody = httpBody; + pigeonResult.allHttpHeaderFields = allHttpHeaderFields; + return pigeonResult; +} ++ (FWFNSUrlRequestData *)fromMap:(NSDictionary *)dict { + FWFNSUrlRequestData *pigeonResult = [[FWFNSUrlRequestData alloc] init]; + pigeonResult.url = GetNullableObject(dict, @"url"); + NSAssert(pigeonResult.url != nil, @""); + pigeonResult.httpMethod = GetNullableObject(dict, @"httpMethod"); + pigeonResult.httpBody = GetNullableObject(dict, @"httpBody"); + pigeonResult.allHttpHeaderFields = GetNullableObject(dict, @"allHttpHeaderFields"); + NSAssert(pigeonResult.allHttpHeaderFields != nil, @""); + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary + dictionaryWithObjectsAndKeys:(self.url ? self.url : [NSNull null]), @"url", + (self.httpMethod ? self.httpMethod : [NSNull null]), + @"httpMethod", (self.httpBody ? self.httpBody : [NSNull null]), + @"httpBody", + (self.allHttpHeaderFields ? self.allHttpHeaderFields + : [NSNull null]), + @"allHttpHeaderFields", nil]; +} +@end + +@implementation FWFWKUserScriptData ++ (instancetype)makeWithSource:(NSString *)source + injectionTime:(nullable FWFWKUserScriptInjectionTimeEnumData *)injectionTime + isMainFrameOnly:(NSNumber *)isMainFrameOnly { + FWFWKUserScriptData *pigeonResult = [[FWFWKUserScriptData alloc] init]; + pigeonResult.source = source; + pigeonResult.injectionTime = injectionTime; + pigeonResult.isMainFrameOnly = isMainFrameOnly; + return pigeonResult; +} ++ (FWFWKUserScriptData *)fromMap:(NSDictionary *)dict { + FWFWKUserScriptData *pigeonResult = [[FWFWKUserScriptData alloc] init]; + pigeonResult.source = GetNullableObject(dict, @"source"); + NSAssert(pigeonResult.source != nil, @""); + pigeonResult.injectionTime = + [FWFWKUserScriptInjectionTimeEnumData fromMap:GetNullableObject(dict, @"injectionTime")]; + pigeonResult.isMainFrameOnly = GetNullableObject(dict, @"isMainFrameOnly"); + NSAssert(pigeonResult.isMainFrameOnly != nil, @""); + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary + dictionaryWithObjectsAndKeys:(self.source ? self.source : [NSNull null]), @"source", + (self.injectionTime ? [self.injectionTime toMap] + : [NSNull null]), + @"injectionTime", + (self.isMainFrameOnly ? self.isMainFrameOnly : [NSNull null]), + @"isMainFrameOnly", nil]; +} +@end + +@implementation FWFNSHttpCookieData ++ (instancetype)makeWithProperties: + (NSDictionary *)properties { + FWFNSHttpCookieData *pigeonResult = [[FWFNSHttpCookieData alloc] init]; + pigeonResult.properties = properties; + return pigeonResult; +} ++ (FWFNSHttpCookieData *)fromMap:(NSDictionary *)dict { + FWFNSHttpCookieData *pigeonResult = [[FWFNSHttpCookieData alloc] init]; + pigeonResult.properties = GetNullableObject(dict, @"properties"); + NSAssert(pigeonResult.properties != nil, @""); + return pigeonResult; +} +- (NSDictionary *)toMap { + return [NSDictionary + dictionaryWithObjectsAndKeys:(self.properties ? self.properties : [NSNull null]), + @"properties", nil]; +} +@end + +@interface FWFWKWebsiteDataStoreHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKWebsiteDataStoreHostApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FWFWKWebsiteDataTypesEnumData fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} +@end + +@interface FWFWKWebsiteDataStoreHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKWebsiteDataStoreHostApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FWFWKWebsiteDataTypesEnumData class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface FWFWKWebsiteDataStoreHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKWebsiteDataStoreHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKWebsiteDataStoreHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKWebsiteDataStoreHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKWebsiteDataStoreHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKWebsiteDataStoreHostApiCodecReaderWriter *readerWriter = + [[FWFWKWebsiteDataStoreHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFWKWebsiteDataStoreHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createFromWebViewConfiguration" + binaryMessenger:binaryMessenger + codec:FWFWKWebsiteDataStoreHostApiGetCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(createDataStoreFromConfigurationWithIdentifier: + configurationIdentifier:error:)], + @"FWFWKWebsiteDataStoreHostApi api (%@) doesn't respond to " + @"@selector(createDataStoreFromConfigurationWithIdentifier:configurationIdentifier:error:" + @")", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_configurationInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api createDataStoreFromConfigurationWithIdentifier:arg_instanceId + configurationIdentifier:arg_configurationInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createDefaultDataStore" + binaryMessenger:binaryMessenger + codec:FWFWKWebsiteDataStoreHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createDefaultDataStoreWithIdentifier:error:)], + @"FWFWKWebsiteDataStoreHostApi api (%@) doesn't respond to " + @"@selector(createDefaultDataStoreWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api createDefaultDataStoreWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes" + binaryMessenger:binaryMessenger + codec:FWFWKWebsiteDataStoreHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (removeDataFromDataStoreWithIdentifier: + ofTypes:secondsModifiedSinceEpoch:completion:)], + @"FWFWKWebsiteDataStoreHostApi api (%@) doesn't respond to " + @"@selector(removeDataFromDataStoreWithIdentifier:ofTypes:" + @"secondsModifiedSinceEpoch:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSArray *arg_dataTypes = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_secondsModifiedSinceEpoch = GetNullableObjectAtIndex(args, 2); + [api removeDataFromDataStoreWithIdentifier:arg_instanceId + ofTypes:arg_dataTypes + secondsModifiedSinceEpoch:arg_secondsModifiedSinceEpoch + completion:^(NSNumber *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFUIViewHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFUIViewHostApiCodecReader +@end + +@interface FWFUIViewHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFUIViewHostApiCodecWriter +@end + +@interface FWFUIViewHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFUIViewHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFUIViewHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFUIViewHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFUIViewHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFUIViewHostApiCodecReaderWriter *readerWriter = + [[FWFUIViewHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFUIViewHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.UIViewHostApi.getContentOffset" + binaryMessenger:binaryMessenger + codec:FWFUIViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(contentOffsetForViewWithIdentifier:error:)], + @"FWFUIViewHostApi api (%@) doesn't respond to " + @"@selector(contentOffsetForViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + NSArray *output = [api contentOffsetForViewWithIdentifier:arg_instanceId + error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.UIViewHostApi.setBackgroundColor" + binaryMessenger:binaryMessenger + codec:FWFUIViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setBackgroundColorForViewWithIdentifier: + toValue:error:)], + @"FWFUIViewHostApi api (%@) doesn't respond to " + @"@selector(setBackgroundColorForViewWithIdentifier:toValue:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_value = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setBackgroundColorForViewWithIdentifier:arg_instanceId toValue:arg_value error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.UIViewHostApi.setOpaque" + binaryMessenger:binaryMessenger + codec:FWFUIViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setOpaqueForViewWithIdentifier:isOpaque:error:)], + @"FWFUIViewHostApi api (%@) doesn't respond to " + @"@selector(setOpaqueForViewWithIdentifier:isOpaque:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_opaque = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setOpaqueForViewWithIdentifier:arg_instanceId isOpaque:arg_opaque error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFUIScrollViewHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFUIScrollViewHostApiCodecReader +@end + +@interface FWFUIScrollViewHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFUIScrollViewHostApiCodecWriter +@end + +@interface FWFUIScrollViewHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFUIScrollViewHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFUIScrollViewHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFUIScrollViewHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFUIScrollViewHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFUIScrollViewHostApiCodecReaderWriter *readerWriter = + [[FWFUIScrollViewHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFUIScrollViewHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.UIScrollViewHostApi.createFromWebView" + binaryMessenger:binaryMessenger + codec:FWFUIScrollViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createFromWebViewWithIdentifier: + webViewIdentifier:error:)], + @"FWFUIScrollViewHostApi api (%@) doesn't respond to " + @"@selector(createFromWebViewWithIdentifier:webViewIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_webViewInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api createFromWebViewWithIdentifier:arg_instanceId + webViewIdentifier:arg_webViewInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.UIScrollViewHostApi.getContentOffset" + binaryMessenger:binaryMessenger + codec:FWFUIScrollViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(contentOffsetForScrollViewWithIdentifier:error:)], + @"FWFUIScrollViewHostApi api (%@) doesn't respond to " + @"@selector(contentOffsetForScrollViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + NSArray *output = [api contentOffsetForScrollViewWithIdentifier:arg_instanceId + error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.UIScrollViewHostApi.scrollBy" + binaryMessenger:binaryMessenger + codec:FWFUIScrollViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(scrollByForScrollViewWithIdentifier: + toX:y:error:)], + @"FWFUIScrollViewHostApi api (%@) doesn't respond to " + @"@selector(scrollByForScrollViewWithIdentifier:toX:y:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_x = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_y = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + [api scrollByForScrollViewWithIdentifier:arg_instanceId toX:arg_x y:arg_y error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.UIScrollViewHostApi.setContentOffset" + binaryMessenger:binaryMessenger + codec:FWFUIScrollViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (setContentOffsetForScrollViewWithIdentifier:toX:y:error:)], + @"FWFUIScrollViewHostApi api (%@) doesn't respond to " + @"@selector(setContentOffsetForScrollViewWithIdentifier:toX:y:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_x = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_y = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + [api setContentOffsetForScrollViewWithIdentifier:arg_instanceId + toX:arg_x + y:arg_y + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFWKWebViewConfigurationHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKWebViewConfigurationHostApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FWFWKAudiovisualMediaTypeEnumData fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} +@end + +@interface FWFWKWebViewConfigurationHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKWebViewConfigurationHostApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FWFWKAudiovisualMediaTypeEnumData class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface FWFWKWebViewConfigurationHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKWebViewConfigurationHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKWebViewConfigurationHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKWebViewConfigurationHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKWebViewConfigurationHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKWebViewConfigurationHostApiCodecReaderWriter *readerWriter = + [[FWFWKWebViewConfigurationHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFWKWebViewConfigurationHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewConfigurationHostApi.create" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewConfigurationHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createWithIdentifier:error:)], + @"FWFWKWebViewConfigurationHostApi api (%@) doesn't respond to " + @"@selector(createWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api createWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewConfigurationHostApi.createFromWebView" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewConfigurationHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createFromWebViewWithIdentifier: + webViewIdentifier:error:)], + @"FWFWKWebViewConfigurationHostApi api (%@) doesn't respond to " + @"@selector(createFromWebViewWithIdentifier:webViewIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_webViewInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api createFromWebViewWithIdentifier:arg_instanceId + webViewIdentifier:arg_webViewInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.WKWebViewConfigurationHostApi.setAllowsInlineMediaPlayback" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewConfigurationHostApiGetCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector + (setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:isAlowed:error:)], + @"FWFWKWebViewConfigurationHostApi api (%@) doesn't respond to " + @"@selector(setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:isAlowed:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_allow = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:arg_instanceId + isAlowed:arg_allow + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewConfigurationHostApi." + @"setMediaTypesRequiringUserActionForPlayback" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewConfigurationHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (setMediaTypesRequiresUserActionForConfigurationWithIdentifier: + forTypes:error:)], + @"FWFWKWebViewConfigurationHostApi api (%@) doesn't respond to " + @"@selector(setMediaTypesRequiresUserActionForConfigurationWithIdentifier:forTypes:" + @"error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSArray *arg_types = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setMediaTypesRequiresUserActionForConfigurationWithIdentifier:arg_instanceId + forTypes:arg_types + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFWKUserContentControllerHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKUserContentControllerHostApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FWFWKUserScriptData fromMap:[self readValue]]; + + case 129: + return [FWFWKUserScriptInjectionTimeEnumData fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} +@end + +@interface FWFWKUserContentControllerHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKUserContentControllerHostApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FWFWKUserScriptData class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKUserScriptInjectionTimeEnumData class]]) { + [self writeByte:129]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface FWFWKUserContentControllerHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKUserContentControllerHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKUserContentControllerHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKUserContentControllerHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKUserContentControllerHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKUserContentControllerHostApiCodecReaderWriter *readerWriter = + [[FWFWKUserContentControllerHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFWKUserContentControllerHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.WKUserContentControllerHostApi.createFromWebViewConfiguration" + binaryMessenger:binaryMessenger + codec:FWFWKUserContentControllerHostApiGetCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(createFromWebViewConfigurationWithIdentifier: + configurationIdentifier:error:)], + @"FWFWKUserContentControllerHostApi api (%@) doesn't respond to " + @"@selector(createFromWebViewConfigurationWithIdentifier:configurationIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_configurationInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api createFromWebViewConfigurationWithIdentifier:arg_instanceId + configurationIdentifier:arg_configurationInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKUserContentControllerHostApi.addScriptMessageHandler" + binaryMessenger:binaryMessenger + codec:FWFWKUserContentControllerHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (addScriptMessageHandlerForControllerWithIdentifier: + handlerIdentifier:ofName:error:)], + @"FWFWKUserContentControllerHostApi api (%@) doesn't respond to " + @"@selector(addScriptMessageHandlerForControllerWithIdentifier:handlerIdentifier:" + @"ofName:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_handlerInstanceid = GetNullableObjectAtIndex(args, 1); + NSString *arg_name = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + [api addScriptMessageHandlerForControllerWithIdentifier:arg_instanceId + handlerIdentifier:arg_handlerInstanceid + ofName:arg_name + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.WKUserContentControllerHostApi.removeScriptMessageHandler" + binaryMessenger:binaryMessenger + codec:FWFWKUserContentControllerHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (removeScriptMessageHandlerForControllerWithIdentifier:name:error:)], + @"FWFWKUserContentControllerHostApi api (%@) doesn't respond to " + @"@selector(removeScriptMessageHandlerForControllerWithIdentifier:name:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSString *arg_name = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api removeScriptMessageHandlerForControllerWithIdentifier:arg_instanceId + name:arg_name + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllScriptMessageHandlers" + binaryMessenger:binaryMessenger + codec:FWFWKUserContentControllerHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (removeAllScriptMessageHandlersForControllerWithIdentifier:error:)], + @"FWFWKUserContentControllerHostApi api (%@) doesn't respond to " + @"@selector(removeAllScriptMessageHandlersForControllerWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api removeAllScriptMessageHandlersForControllerWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKUserContentControllerHostApi.addUserScript" + binaryMessenger:binaryMessenger + codec:FWFWKUserContentControllerHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(addUserScriptForControllerWithIdentifier: + userScript:error:)], + @"FWFWKUserContentControllerHostApi api (%@) doesn't respond to " + @"@selector(addUserScriptForControllerWithIdentifier:userScript:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FWFWKUserScriptData *arg_userScript = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api addUserScriptForControllerWithIdentifier:arg_instanceId + userScript:arg_userScript + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllUserScripts" + binaryMessenger:binaryMessenger + codec:FWFWKUserContentControllerHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (removeAllUserScriptsForControllerWithIdentifier:error:)], + @"FWFWKUserContentControllerHostApi api (%@) doesn't respond to " + @"@selector(removeAllUserScriptsForControllerWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api removeAllUserScriptsForControllerWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFWKPreferencesHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKPreferencesHostApiCodecReader +@end + +@interface FWFWKPreferencesHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKPreferencesHostApiCodecWriter +@end + +@interface FWFWKPreferencesHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKPreferencesHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKPreferencesHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKPreferencesHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKPreferencesHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKPreferencesHostApiCodecReaderWriter *readerWriter = + [[FWFWKPreferencesHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFWKPreferencesHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKPreferencesHostApi.createFromWebViewConfiguration" + binaryMessenger:binaryMessenger + codec:FWFWKPreferencesHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createFromWebViewConfiguration: + configurationIdentifier:error:)], + @"FWFWKPreferencesHostApi api (%@) doesn't respond to " + @"@selector(createFromWebViewConfiguration:configurationIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_configurationInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api createFromWebViewConfiguration:arg_instanceId + configurationIdentifier:arg_configurationInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKPreferencesHostApi.setJavaScriptEnabled" + binaryMessenger:binaryMessenger + codec:FWFWKPreferencesHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (setJavaScriptEnabledForPreferencesWithIdentifier:isEnabled:error:)], + @"FWFWKPreferencesHostApi api (%@) doesn't respond to " + @"@selector(setJavaScriptEnabledForPreferencesWithIdentifier:isEnabled:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_enabled = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setJavaScriptEnabledForPreferencesWithIdentifier:arg_instanceId + isEnabled:arg_enabled + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFWKScriptMessageHandlerHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKScriptMessageHandlerHostApiCodecReader +@end + +@interface FWFWKScriptMessageHandlerHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKScriptMessageHandlerHostApiCodecWriter +@end + +@interface FWFWKScriptMessageHandlerHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKScriptMessageHandlerHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKScriptMessageHandlerHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKScriptMessageHandlerHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKScriptMessageHandlerHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKScriptMessageHandlerHostApiCodecReaderWriter *readerWriter = + [[FWFWKScriptMessageHandlerHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFWKScriptMessageHandlerHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKScriptMessageHandlerHostApi.create" + binaryMessenger:binaryMessenger + codec:FWFWKScriptMessageHandlerHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createWithIdentifier:error:)], + @"FWFWKScriptMessageHandlerHostApi api (%@) doesn't respond to " + @"@selector(createWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api createWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFWKNavigationDelegateHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKNavigationDelegateHostApiCodecReader +@end + +@interface FWFWKNavigationDelegateHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKNavigationDelegateHostApiCodecWriter +@end + +@interface FWFWKNavigationDelegateHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKNavigationDelegateHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKNavigationDelegateHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKNavigationDelegateHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKNavigationDelegateHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKNavigationDelegateHostApiCodecReaderWriter *readerWriter = + [[FWFWKNavigationDelegateHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFWKNavigationDelegateHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKNavigationDelegateHostApi.create" + binaryMessenger:binaryMessenger + codec:FWFWKNavigationDelegateHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createWithIdentifier:error:)], + @"FWFWKNavigationDelegateHostApi api (%@) doesn't respond to " + @"@selector(createWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api createWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation" + binaryMessenger:binaryMessenger + codec:FWFWKNavigationDelegateHostApiGetCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector + (setDidFinishNavigationForDelegateWithIdentifier:functionIdentifier:error:)], + @"FWFWKNavigationDelegateHostApi api (%@) doesn't respond to " + @"@selector(setDidFinishNavigationForDelegateWithIdentifier:functionIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_functionInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setDidFinishNavigationForDelegateWithIdentifier:arg_instanceId + functionIdentifier:arg_functionInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFWKNavigationDelegateFlutterApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKNavigationDelegateFlutterApiCodecReader +@end + +@interface FWFWKNavigationDelegateFlutterApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKNavigationDelegateFlutterApiCodecWriter +@end + +@interface FWFWKNavigationDelegateFlutterApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKNavigationDelegateFlutterApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKNavigationDelegateFlutterApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKNavigationDelegateFlutterApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKNavigationDelegateFlutterApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKNavigationDelegateFlutterApiCodecReaderWriter *readerWriter = + [[FWFWKNavigationDelegateFlutterApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +@interface FWFWKNavigationDelegateFlutterApi () +@property(nonatomic, strong) NSObject *binaryMessenger; +@end + +@implementation FWFWKNavigationDelegateFlutterApi + +- (instancetype)initWithBinaryMessenger:(NSObject *)binaryMessenger { + self = [super init]; + if (self) { + _binaryMessenger = binaryMessenger; + } + return self; +} +- (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)arg_functionInstanceId + webViewIdentifier:(NSNumber *)arg_webViewInstanceId + URL:(nullable NSString *)arg_url + completion:(void (^)(NSError *_Nullable))completion { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName: + @"dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation" + binaryMessenger:self.binaryMessenger + codec:FWFWKNavigationDelegateFlutterApiGetCodec()]; + [channel sendMessage:@[ + (arg_functionInstanceId == nil) ? [NSNull null] : arg_functionInstanceId, + (arg_webViewInstanceId == nil) ? [NSNull null] : arg_webViewInstanceId, + (arg_url == nil) ? [NSNull null] : arg_url + ] + reply:^(id reply) { + completion(nil); + }]; +} +@end +@interface FWFNSObjectHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFNSObjectHostApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FWFNSKeyValueObservingOptionsEnumData fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} +@end + +@interface FWFNSObjectHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFNSObjectHostApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FWFNSKeyValueObservingOptionsEnumData class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface FWFNSObjectHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFNSObjectHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFNSObjectHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFNSObjectHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFNSObjectHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFNSObjectHostApiCodecReaderWriter *readerWriter = + [[FWFNSObjectHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFNSObjectHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.NSObjectHostApi.dispose" + binaryMessenger:binaryMessenger + codec:FWFNSObjectHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(disposeObjectWithIdentifier:error:)], + @"FWFNSObjectHostApi api (%@) doesn't respond to " + @"@selector(disposeObjectWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api disposeObjectWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.NSObjectHostApi.addObserver" + binaryMessenger:binaryMessenger + codec:FWFNSObjectHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (addObserverForObjectWithIdentifier: + observerIdentifier:keyPath:options:error:)], + @"FWFNSObjectHostApi api (%@) doesn't respond to " + @"@selector(addObserverForObjectWithIdentifier:observerIdentifier:keyPath:options:" + @"error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_observerInstanceId = GetNullableObjectAtIndex(args, 1); + NSString *arg_keyPath = GetNullableObjectAtIndex(args, 2); + NSArray *arg_options = + GetNullableObjectAtIndex(args, 3); + FlutterError *error; + [api addObserverForObjectWithIdentifier:arg_instanceId + observerIdentifier:arg_observerInstanceId + keyPath:arg_keyPath + options:arg_options + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.NSObjectHostApi.removeObserver" + binaryMessenger:binaryMessenger + codec:FWFNSObjectHostApiGetCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(removeObserverForObjectWithIdentifier: + observerIdentifier:keyPath:error:)], + @"FWFNSObjectHostApi api (%@) doesn't respond to " + @"@selector(removeObserverForObjectWithIdentifier:observerIdentifier:keyPath:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_observerInstanceId = GetNullableObjectAtIndex(args, 1); + NSString *arg_keyPath = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + [api removeObserverForObjectWithIdentifier:arg_instanceId + observerIdentifier:arg_observerInstanceId + keyPath:arg_keyPath + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFFunctionFlutterApiCodecReader : FlutterStandardReader +@end +@implementation FWFFunctionFlutterApiCodecReader +@end + +@interface FWFFunctionFlutterApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFFunctionFlutterApiCodecWriter +@end + +@interface FWFFunctionFlutterApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFFunctionFlutterApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFFunctionFlutterApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFFunctionFlutterApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFFunctionFlutterApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFFunctionFlutterApiCodecReaderWriter *readerWriter = + [[FWFFunctionFlutterApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +@interface FWFFunctionFlutterApi () +@property(nonatomic, strong) NSObject *binaryMessenger; +@end + +@implementation FWFFunctionFlutterApi + +- (instancetype)initWithBinaryMessenger:(NSObject *)binaryMessenger { + self = [super init]; + if (self) { + _binaryMessenger = binaryMessenger; + } + return self; +} +- (void)disposeFunctionWithIdentifier:(NSNumber *)arg_instanceId + completion:(void (^)(NSError *_Nullable))completion { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.FunctionFlutterApi.dispose" + binaryMessenger:self.binaryMessenger + codec:FWFFunctionFlutterApiGetCodec()]; + [channel sendMessage:@[ (arg_instanceId == nil) ? [NSNull null] : arg_instanceId ] + reply:^(id reply) { + completion(nil); + }]; +} +@end +@interface FWFWKWebViewHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKWebViewHostApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FWFNSHttpCookieData fromMap:[self readValue]]; + + case 129: + return [FWFNSHttpCookiePropertyKeyEnumData fromMap:[self readValue]]; + + case 130: + return [FWFNSKeyValueObservingOptionsEnumData fromMap:[self readValue]]; + + case 131: + return [FWFNSUrlRequestData fromMap:[self readValue]]; + + case 132: + return [FWFWKAudiovisualMediaTypeEnumData fromMap:[self readValue]]; + + case 133: + return [FWFWKUserScriptData fromMap:[self readValue]]; + + case 134: + return [FWFWKUserScriptInjectionTimeEnumData fromMap:[self readValue]]; + + case 135: + return [FWFWKWebsiteDataTypesEnumData fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} +@end + +@interface FWFWKWebViewHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKWebViewHostApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FWFNSHttpCookieData class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFNSHttpCookiePropertyKeyEnumData class]]) { + [self writeByte:129]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFNSKeyValueObservingOptionsEnumData class]]) { + [self writeByte:130]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFNSUrlRequestData class]]) { + [self writeByte:131]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKAudiovisualMediaTypeEnumData class]]) { + [self writeByte:132]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKUserScriptData class]]) { + [self writeByte:133]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKUserScriptInjectionTimeEnumData class]]) { + [self writeByte:134]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKWebsiteDataTypesEnumData class]]) { + [self writeByte:135]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface FWFWKWebViewHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKWebViewHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKWebViewHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKWebViewHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKWebViewHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKWebViewHostApiCodecReaderWriter *readerWriter = + [[FWFWKWebViewHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFWKWebViewHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.create" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createWithIdentifier: + configurationIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(createWithIdentifier:configurationIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_configurationInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api createWithIdentifier:arg_instanceId + configurationIdentifier:arg_configurationInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.setUIDelegate" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setUIDelegateForWebViewWithIdentifier: + delegateIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(setUIDelegateForWebViewWithIdentifier:delegateIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_uiDelegateInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setUIDelegateForWebViewWithIdentifier:arg_instanceId + delegateIdentifier:arg_uiDelegateInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.setNavigationDelegate" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(setNavigationDelegateForWebViewWithIdentifier: + delegateIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(setNavigationDelegateForWebViewWithIdentifier:delegateIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_navigationDelegateInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setNavigationDelegateForWebViewWithIdentifier:arg_instanceId + delegateIdentifier:arg_navigationDelegateInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.getUrl" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(URLForWebViewWithIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(URLForWebViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + NSString *output = [api URLForWebViewWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.getEstimatedProgress" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(estimatedProgressForWebViewWithIdentifier: + error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(estimatedProgressForWebViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + NSNumber *output = [api estimatedProgressForWebViewWithIdentifier:arg_instanceId + error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.loadRequest" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(loadRequestForWebViewWithIdentifier: + request:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(loadRequestForWebViewWithIdentifier:request:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FWFNSUrlRequestData *arg_request = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api loadRequestForWebViewWithIdentifier:arg_instanceId request:arg_request error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(loadHTMLForWebViewWithIdentifier: + HTMLString:baseURL:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(loadHTMLForWebViewWithIdentifier:HTMLString:baseURL:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSString *arg_string = GetNullableObjectAtIndex(args, 1); + NSString *arg_baseUrl = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + [api loadHTMLForWebViewWithIdentifier:arg_instanceId + HTMLString:arg_string + baseURL:arg_baseUrl + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.loadFileUrl" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (loadFileForWebViewWithIdentifier:fileURL:readAccessURL:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(loadFileForWebViewWithIdentifier:fileURL:readAccessURL:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSString *arg_url = GetNullableObjectAtIndex(args, 1); + NSString *arg_readAccessUrl = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + [api loadFileForWebViewWithIdentifier:arg_instanceId + fileURL:arg_url + readAccessURL:arg_readAccessUrl + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.loadFlutterAsset" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(loadAssetForWebViewWithIdentifier: + assetKey:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(loadAssetForWebViewWithIdentifier:assetKey:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSString *arg_key = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api loadAssetForWebViewWithIdentifier:arg_instanceId assetKey:arg_key error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.canGoBack" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(canGoBackForWebViewWithIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(canGoBackForWebViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + NSNumber *output = [api canGoBackForWebViewWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.canGoForward" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(canGoForwardForWebViewWithIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(canGoForwardForWebViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + NSNumber *output = [api canGoForwardForWebViewWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.goBack" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(goBackForWebViewWithIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(goBackForWebViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api goBackForWebViewWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.goForward" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(goForwardForWebViewWithIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(goForwardForWebViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api goForwardForWebViewWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.reload" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(reloadWebViewWithIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(reloadWebViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api reloadWebViewWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.getTitle" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(titleForWebViewWithIdentifier:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(titleForWebViewWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + NSString *output = [api titleForWebViewWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.WKWebViewHostApi.setAllowsBackForwardNavigationGestures" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (setAllowsBackForwardForWebViewWithIdentifier:isAllowed:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(setAllowsBackForwardForWebViewWithIdentifier:isAllowed:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_allow = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setAllowsBackForwardForWebViewWithIdentifier:arg_instanceId + isAllowed:arg_allow + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setUserAgentForWebViewWithIdentifier: + userAgent:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(setUserAgentForWebViewWithIdentifier:userAgent:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSString *arg_userAgent = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setUserAgentForWebViewWithIdentifier:arg_instanceId + userAgent:arg_userAgent + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector + (evaluateJavaScriptForWebViewWithIdentifier:javaScriptString:completion:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(evaluateJavaScriptForWebViewWithIdentifier:javaScriptString:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSString *arg_javaScriptString = GetNullableObjectAtIndex(args, 1); + [api evaluateJavaScriptForWebViewWithIdentifier:arg_instanceId + javaScriptString:arg_javaScriptString + completion:^(id _Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFWKUIDelegateHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKUIDelegateHostApiCodecReader +@end + +@interface FWFWKUIDelegateHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKUIDelegateHostApiCodecWriter +@end + +@interface FWFWKUIDelegateHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKUIDelegateHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKUIDelegateHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKUIDelegateHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKUIDelegateHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKUIDelegateHostApiCodecReaderWriter *readerWriter = + [[FWFWKUIDelegateHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFWKUIDelegateHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKUIDelegateHostApi.create" + binaryMessenger:binaryMessenger + codec:FWFWKUIDelegateHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createWithIdentifier:error:)], + @"FWFWKUIDelegateHostApi api (%@) doesn't respond to " + @"@selector(createWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api createWithIdentifier:arg_instanceId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} +@interface FWFWKHttpCookieStoreHostApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKHttpCookieStoreHostApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FWFNSHttpCookieData fromMap:[self readValue]]; + + case 129: + return [FWFNSHttpCookiePropertyKeyEnumData fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} +@end + +@interface FWFWKHttpCookieStoreHostApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKHttpCookieStoreHostApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FWFNSHttpCookieData class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFNSHttpCookiePropertyKeyEnumData class]]) { + [self writeByte:129]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface FWFWKHttpCookieStoreHostApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKHttpCookieStoreHostApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKHttpCookieStoreHostApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKHttpCookieStoreHostApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKHttpCookieStoreHostApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKHttpCookieStoreHostApiCodecReaderWriter *readerWriter = + [[FWFWKHttpCookieStoreHostApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FWFWKHttpCookieStoreHostApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKHttpCookieStoreHostApi.createFromWebsiteDataStore" + binaryMessenger:binaryMessenger + codec:FWFWKHttpCookieStoreHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(createFromWebsiteDataStoreWithIdentifier: + dataStoreIdentifier:error:)], + @"FWFWKHttpCookieStoreHostApi api (%@) doesn't respond to " + @"@selector(createFromWebsiteDataStoreWithIdentifier:dataStoreIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_websiteDataStoreInstanceId = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api createFromWebsiteDataStoreWithIdentifier:arg_instanceId + dataStoreIdentifier:arg_websiteDataStoreInstanceId + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.WKHttpCookieStoreHostApi.setCookie" + binaryMessenger:binaryMessenger + codec:FWFWKHttpCookieStoreHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setCookieForStoreWithIdentifier:cookie:error:)], + @"FWFWKHttpCookieStoreHostApi api (%@) doesn't respond to " + @"@selector(setCookieForStoreWithIdentifier:cookie:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + FWFNSHttpCookieData *arg_cookie = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setCookieForStoreWithIdentifier:arg_instanceId cookie:arg_cookie error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m index 0d88d88ba282..445ecd3e2cf9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m @@ -13,9 +13,9 @@ @interface FWFInstanceManager () @implementation FWFInstanceManager - (instancetype)init { if (self) { - self.lockQueue = dispatch_queue_create("FWFInstanceManager", DISPATCH_QUEUE_SERIAL); - self.identifiersToInstances = [NSMapTable strongToStrongObjectsMapTable]; - self.instancesToIdentifiers = [NSMapTable strongToStrongObjectsMapTable]; + _lockQueue = dispatch_queue_create("FWFInstanceManager", DISPATCH_QUEUE_SERIAL); + _identifiersToInstances = [NSMapTable strongToStrongObjectsMapTable]; + _instancesToIdentifiers = [NSMapTable strongToStrongObjectsMapTable]; } return self; } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.h new file mode 100644 index 000000000000..968a9a85b2e7 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.h @@ -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 +#import + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * 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. + */ +@interface FWFWebView : WKWebView +@end + +/** + * Host api implementation for WKWebView. + * + * Handles creating WKWebViews that intercommunicate with a paired Dart object. + */ +@interface FWFWebViewHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager + bundle:(NSBundle *)bundle + assetManager:(FWFAssetManager *)assetManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m new file mode 100644 index 000000000000..35677575dddb --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m @@ -0,0 +1,249 @@ +// 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 "FWFWebViewHostApi.h" +#import "FWFDataConverters.h" + +@implementation FWFAssetManager +- (NSString *)lookupKeyForAsset:(NSString *)asset { + return [FlutterDartProject lookupKeyForAsset:asset]; +} +@end + +@implementation FWFWebView +- (void)setFrame:(CGRect)frame { + [super setFrame:frame]; + // Prevents the contentInsets from being adjusted by iOS and gives control to Flutter. + self.scrollView.contentInset = UIEdgeInsetsZero; + if (@available(iOS 11, *)) { + // Above iOS 11, adjust contentInset to compensate the adjustedContentInset so the sum will + // always be 0. + if (UIEdgeInsetsEqualToEdgeInsets(self.scrollView.adjustedContentInset, UIEdgeInsetsZero)) { + return; + } + UIEdgeInsets insetToAdjust = self.scrollView.adjustedContentInset; + self.scrollView.contentInset = UIEdgeInsetsMake(-insetToAdjust.top, -insetToAdjust.left, + -insetToAdjust.bottom, -insetToAdjust.right); + } +} + +- (nonnull UIView *)view { + return self; +} +@end + +@interface FWFWebViewHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@property NSBundle *bundle; +@property FWFAssetManager *assetManager; +@end + +@implementation FWFWebViewHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + return [self initWithInstanceManager:instanceManager + bundle:[NSBundle mainBundle] + assetManager:[[FWFAssetManager alloc] init]]; +} + +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager + bundle:(NSBundle *)bundle + assetManager:(FWFAssetManager *)assetManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + _bundle = bundle; + _assetManager = assetManager; + } + return self; +} + +- (FWFWebView *)webViewForIdentifier:(NSNumber *)instanceId { + return (FWFWebView *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +} + ++ (nonnull FlutterError *)errorForURLString:(nonnull NSString *)string { + NSString *errorDetails = [NSString stringWithFormat:@"Initializing NSURL with the supplied " + @"'%@' path resulted in a nil value.", + string]; + return [FlutterError errorWithCode:@"FWFURLParsingError" + message:@"Failed parsing file path." + details:errorDetails]; +} + +- (void)createWithIdentifier:(nonnull NSNumber *)instanceId + configurationIdentifier:(nonnull NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + WKWebViewConfiguration *configuration = (WKWebViewConfiguration *)[self.instanceManager + instanceForIdentifier:configurationInstanceId.longValue]; + FWFWebView *webView = [[FWFWebView alloc] initWithFrame:CGRectMake(0, 0, 0, 0) + configuration:configuration]; + [self.instanceManager addInstance:webView withIdentifier:instanceId.longValue]; +} + +- (void)loadRequestForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + request:(nonnull FWFNSUrlRequestData *)request + error: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error { + NSURLRequest *urlRequest = FWFNSURLRequestFromRequestData(request); + if (!urlRequest) { + *error = [FlutterError errorWithCode:@"FWFURLRequestParsingError" + message:@"Failed instantiating an NSURLRequest." + details:[NSString stringWithFormat:@"URL was: '%@'", request.url]]; + return; + } + [[self webViewForIdentifier:instanceId] loadRequest:urlRequest]; +} + +- (void)setUserAgentForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + userAgent:(nullable NSString *)userAgent + error:(FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + [[self webViewForIdentifier:instanceId] setCustomUserAgent:userAgent]; +} + +- (nullable NSNumber *) + canGoBackForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + return @([self webViewForIdentifier:instanceId].canGoBack); +} + +- (nullable NSString *) + URLForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + return [self webViewForIdentifier:instanceId].URL.absoluteString; +} + +- (nullable NSNumber *) + canGoForwardForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + return @([[self webViewForIdentifier:instanceId] canGoForward]); +} + +- (nullable NSNumber *) + estimatedProgressForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + return @([[self webViewForIdentifier:instanceId] estimatedProgress]); +} + +- (void)evaluateJavaScriptForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + javaScriptString:(nonnull NSString *)javaScriptString + completion: + (nonnull void (^)(id _Nullable, + FlutterError *_Nullable))completion { + [[self webViewForIdentifier:instanceId] + evaluateJavaScript:javaScriptString + completionHandler:^(id _Nullable result, NSError *_Nullable error) { + id returnValue = nil; + FlutterError *flutterError = nil; + if (!error) { + if (!result || [result isKindOfClass:[NSString class]] || + [result isKindOfClass:[NSNumber class]]) { + returnValue = result; + } else { + NSString *className = NSStringFromClass([result class]); + NSLog(@"Return type of evaluateJavaScript is not directly supported: %@. Returned " + @"description of value.", + className); + returnValue = [result description]; + } + } else { + flutterError = [FlutterError errorWithCode:@"FWFEvaluateJavaScriptError" + message:@"Failed evaluating JavaScript." + details:error]; + } + + completion(returnValue, flutterError); + }]; +} + +- (void)goBackForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [[self webViewForIdentifier:instanceId] goBack]; +} + +- (void)goForwardForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [[self webViewForIdentifier:instanceId] goForward]; +} + +- (void)loadAssetForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + assetKey:(nonnull NSString *)key + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + NSString *assetFilePath = [self.assetManager lookupKeyForAsset:key]; + + NSURL *url = [self.bundle URLForResource:[assetFilePath stringByDeletingPathExtension] + withExtension:assetFilePath.pathExtension]; + if (!url) { + *error = [FWFWebViewHostApiImpl errorForURLString:assetFilePath]; + } else { + [[self webViewForIdentifier:instanceId] loadFileURL:url + allowingReadAccessToURL:[url URLByDeletingLastPathComponent]]; + } +} + +- (void)loadFileForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + fileURL:(nonnull NSString *)url + readAccessURL:(nonnull NSString *)readAccessUrl + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + NSURL *fileURL = [NSURL fileURLWithPath:url isDirectory:NO]; + NSURL *readAccessNSURL = [NSURL fileURLWithPath:readAccessUrl isDirectory:YES]; + + if (!fileURL) { + *error = [FWFWebViewHostApiImpl errorForURLString:url]; + } else if (!readAccessNSURL) { + *error = [FWFWebViewHostApiImpl errorForURLString:readAccessUrl]; + } else { + [[self webViewForIdentifier:instanceId] loadFileURL:fileURL + allowingReadAccessToURL:readAccessNSURL]; + } +} + +- (void)loadHTMLForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + HTMLString:(nonnull NSString *)string + baseURL:(nullable NSString *)baseUrl + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [[self webViewForIdentifier:instanceId] loadHTMLString:string + baseURL:[NSURL URLWithString:baseUrl]]; +} + +- (void)reloadWebViewWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [[self webViewForIdentifier:instanceId] reload]; +} + +- (void) + setAllowsBackForwardForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + isAllowed:(nonnull NSNumber *)allow + error:(FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + [[self webViewForIdentifier:instanceId] setAllowsBackForwardNavigationGestures:allow.boolValue]; +} + +- (void) + setNavigationDelegateForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + delegateIdentifier:(nullable NSNumber *)navigationDelegateInstanceId + error: + (FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + id navigationDelegate = (id)[self.instanceManager + instanceForIdentifier:navigationDelegateInstanceId.longValue]; + [[self webViewForIdentifier:instanceId] setNavigationDelegate:navigationDelegate]; +} + +- (void)setUIDelegateForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + delegateIdentifier:(nullable NSNumber *)uiDelegateInstanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + id navigationDelegate = + (id)[self.instanceManager instanceForIdentifier:uiDelegateInstanceId.longValue]; + [[self webViewForIdentifier:instanceId] setUIDelegate:navigationDelegate]; +} + +- (nullable NSString *) + titleForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + return [[self webViewForIdentifier:instanceId] title]; +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h index 3743f13f4ac7..9a7fd1b5ef3f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h @@ -7,6 +7,9 @@ #import #import #import +#import +#import #import +#import #import #import diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart index 6395f4171f45..fa52b43c396e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.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 (v2.0.3), do not edit directly. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name // @dart = 2.12 @@ -87,7 +87,7 @@ class NSKeyValueObservingOptionsEnumData { Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value == null ? null : value!.index; + pigeonMap['value'] = value?.index; return pigeonMap; } @@ -110,7 +110,7 @@ class WKUserScriptInjectionTimeEnumData { Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value == null ? null : value!.index; + pigeonMap['value'] = value?.index; return pigeonMap; } @@ -133,7 +133,7 @@ class WKAudiovisualMediaTypeEnumData { Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value == null ? null : value!.index; + pigeonMap['value'] = value?.index; return pigeonMap; } @@ -156,7 +156,7 @@ class WKWebsiteDataTypesEnumData { Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value == null ? null : value!.index; + pigeonMap['value'] = value?.index; return pigeonMap; } @@ -179,7 +179,7 @@ class NSHttpCookiePropertyKeyEnumData { Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value == null ? null : value!.index; + pigeonMap['value'] = value?.index; return pigeonMap; } @@ -242,8 +242,7 @@ class WKUserScriptData { Object encode() { final Map pigeonMap = {}; pigeonMap['source'] = source; - pigeonMap['injectionTime'] = - injectionTime == null ? null : injectionTime!.encode(); + pigeonMap['injectionTime'] = injectionTime?.encode(); pigeonMap['isMainFrameOnly'] = isMainFrameOnly; return pigeonMap; } @@ -1169,10 +1168,8 @@ abstract class WKNavigationDelegateFlutterApi { assert(arg_webViewInstanceId != null, 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null int.'); final String? arg_url = (args[2] as String?); - assert(arg_url != null, - 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null String.'); api.didFinishNavigation( - arg_functionInstanceId!, arg_webViewInstanceId!, arg_url!); + arg_functionInstanceId!, arg_webViewInstanceId!, arg_url); return; }); } @@ -1334,9 +1331,30 @@ class _WKWebViewHostApiCodec extends StandardMessageCodec { const _WKWebViewHostApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is NSUrlRequestData) { + if (value is NSHttpCookieData) { buffer.putUint8(128); writeValue(buffer, value.encode()); + } else if (value is NSHttpCookiePropertyKeyEnumData) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is NSKeyValueObservingOptionsEnumData) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is NSUrlRequestData) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is WKAudiovisualMediaTypeEnumData) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else if (value is WKUserScriptData) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); + } else if (value is WKUserScriptInjectionTimeEnumData) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else if (value is WKWebsiteDataTypesEnumData) { + buffer.putUint8(135); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -1346,8 +1364,29 @@ class _WKWebViewHostApiCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 128: + return NSHttpCookieData.decode(readValue(buffer)!); + + case 129: + return NSHttpCookiePropertyKeyEnumData.decode(readValue(buffer)!); + + case 130: + return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!); + + case 131: return NSUrlRequestData.decode(readValue(buffer)!); + case 132: + return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!); + + case 133: + return WKUserScriptData.decode(readValue(buffer)!); + + case 134: + return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); + + case 135: + return WKWebsiteDataTypesEnumData.decode(readValue(buffer)!); + default: return super.readValueOfType(type, buffer); } @@ -1803,13 +1842,13 @@ class WKWebViewHostApi { } } - Future evaluateJavaScript( - int arg_instanceId, String arg_javascriptString) async { + Future evaluateJavaScript( + int arg_instanceId, String arg_javaScriptString) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_javascriptString]) + await channel.send([arg_instanceId, arg_javaScriptString]) as Map?; if (replyMap == null) { throw PlatformException( @@ -1824,13 +1863,8 @@ class WKWebViewHostApi { message: error['message'] as String?, details: error['details'], ); - } else if (replyMap['result'] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); } else { - return (replyMap['result'] as String?)!; + return (replyMap['result'] as Object?); } } } 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 23d8712b0486..509fa8e1f7dd 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -8,11 +8,22 @@ import 'package:pigeon/pigeon.dart'; PigeonOptions( dartOut: 'lib/src/common/web_kit.pigeon.dart', dartTestOut: 'test/src/common/test_web_kit.pigeon.dart', - dartOptions: DartOptions(isNullSafe: true, copyrightHeader: [ + dartOptions: DartOptions(copyrightHeader: [ '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.', ]), + objcHeaderOut: 'ios/Classes/FWFGeneratedWebKitApis.h', + objcSourceOut: 'ios/Classes/FWFGeneratedWebKitApis.m', + objcOptions: ObjcOptions( + header: 'ios/Classes/FWFGeneratedWebKitApis.h', + prefix: 'FWF', + copyrightHeader: [ + '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.', + ], + ), ), ) @@ -204,13 +215,20 @@ class NSHttpCookieData { /// See https://developer.apple.com/documentation/webkit/wkwebsitedatastore?language=objc. @HostApi(dartHostTestHandler: 'TestWKWebsiteDataStoreHostApi') abstract class WKWebsiteDataStoreHostApi { + @ObjCSelector( + 'createDataStoreFromConfigurationWithIdentifier:configurationIdentifier:', + ) void createFromWebViewConfiguration( int instanceId, int configurationInstanceId, ); + @ObjCSelector('createDefaultDataStoreWithIdentifier:') void createDefaultDataStore(int instanceId); + @ObjCSelector( + 'removeDataFromDataStoreWithIdentifier:ofTypes:secondsModifiedSinceEpoch:', + ) @async bool removeDataOfTypes( int instanceId, @@ -224,10 +242,13 @@ abstract class WKWebsiteDataStoreHostApi { /// See https://developer.apple.com/documentation/uikit/uiview?language=objc. @HostApi(dartHostTestHandler: 'TestUIViewHostApi') abstract class UIViewHostApi { + @ObjCSelector('contentOffsetForViewWithIdentifier:') List getContentOffset(int instanceId); + @ObjCSelector('setBackgroundColorForViewWithIdentifier:toValue:') void setBackgroundColor(int instanceId, int? value); + @ObjCSelector('setOpaqueForViewWithIdentifier:isOpaque:') void setOpaque(int instanceId, bool opaque); } @@ -236,12 +257,16 @@ abstract class UIViewHostApi { /// See https://developer.apple.com/documentation/uikit/uiscrollview?language=objc. @HostApi(dartHostTestHandler: 'TestUIScrollViewHostApi') abstract class UIScrollViewHostApi { + @ObjCSelector('createFromWebViewWithIdentifier:webViewIdentifier:') void createFromWebView(int instanceId, int webViewInstanceId); + @ObjCSelector('contentOffsetForScrollViewWithIdentifier:') List getContentOffset(int instanceId); + @ObjCSelector('scrollByForScrollViewWithIdentifier:toX:y:') void scrollBy(int instanceId, double x, double y); + @ObjCSelector('setContentOffsetForScrollViewWithIdentifier:toX:y:') void setContentOffset(int instanceId, double x, double y); } @@ -250,12 +275,20 @@ abstract class UIScrollViewHostApi { /// See https://developer.apple.com/documentation/webkit/wkwebviewconfiguration?language=objc. @HostApi(dartHostTestHandler: 'TestWKWebViewConfigurationHostApi') abstract class WKWebViewConfigurationHostApi { + @ObjCSelector('createWithIdentifier:') void create(int instanceId); + @ObjCSelector('createFromWebViewWithIdentifier:webViewIdentifier:') void createFromWebView(int instanceId, int webViewInstanceId); + @ObjCSelector( + 'setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:isAlowed:', + ) void setAllowsInlineMediaPlayback(int instanceId, bool allow); + @ObjCSelector( + 'setMediaTypesRequiresUserActionForConfigurationWithIdentifier:forTypes:', + ) void setMediaTypesRequiringUserActionForPlayback( int instanceId, List types, @@ -267,23 +300,33 @@ abstract class WKWebViewConfigurationHostApi { /// See https://developer.apple.com/documentation/webkit/wkusercontentcontroller?language=objc. @HostApi(dartHostTestHandler: 'TestWKUserContentControllerHostApi') abstract class WKUserContentControllerHostApi { + @ObjCSelector( + 'createFromWebViewConfigurationWithIdentifier:configurationIdentifier:', + ) void createFromWebViewConfiguration( int instanceId, int configurationInstanceId, ); + @ObjCSelector( + 'addScriptMessageHandlerForControllerWithIdentifier:handlerIdentifier:ofName:', + ) void addScriptMessageHandler( int instanceId, int handlerInstanceid, String name, ); + @ObjCSelector('removeScriptMessageHandlerForControllerWithIdentifier:name:') void removeScriptMessageHandler(int instanceId, String name); + @ObjCSelector('removeAllScriptMessageHandlersForControllerWithIdentifier:') void removeAllScriptMessageHandlers(int instanceId); + @ObjCSelector('addUserScriptForControllerWithIdentifier:userScript:') void addUserScript(int instanceId, WKUserScriptData userScript); + @ObjCSelector('removeAllUserScriptsForControllerWithIdentifier:') void removeAllUserScripts(int instanceId); } @@ -292,11 +335,13 @@ abstract class WKUserContentControllerHostApi { /// See https://developer.apple.com/documentation/webkit/wkpreferences?language=objc. @HostApi(dartHostTestHandler: 'TestWKPreferencesHostApi') abstract class WKPreferencesHostApi { + @ObjCSelector('createFromWebViewConfiguration:configurationIdentifier:') void createFromWebViewConfiguration( int instanceId, int configurationInstanceId, ); + @ObjCSelector('setJavaScriptEnabledForPreferencesWithIdentifier:isEnabled:') void setJavaScriptEnabled(int instanceId, bool enabled); } @@ -305,6 +350,7 @@ abstract class WKPreferencesHostApi { /// See https://developer.apple.com/documentation/webkit/wkscriptmessagehandler?language=objc. @HostApi(dartHostTestHandler: 'TestWKScriptMessageHandlerHostApi') abstract class WKScriptMessageHandlerHostApi { + @ObjCSelector('createWithIdentifier:') void create(int instanceId); } @@ -313,8 +359,12 @@ abstract class WKScriptMessageHandlerHostApi { /// See https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc. @HostApi(dartHostTestHandler: 'TestWKNavigationDelegateHostApi') abstract class WKNavigationDelegateHostApi { + @ObjCSelector('createWithIdentifier:') void create(int instanceId); + @ObjCSelector( + 'setDidFinishNavigationForDelegateWithIdentifier:functionIdentifier:', + ) void setDidFinishNavigation(int instanceId, int? functionInstanceId); } @@ -323,6 +373,9 @@ abstract class WKNavigationDelegateHostApi { /// See https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc. @FlutterApi() abstract class WKNavigationDelegateFlutterApi { + @ObjCSelector( + 'didFinishNavigationForDelegateWithIdentifier:webViewIdentifier:URL:', + ) void didFinishNavigation( int functionInstanceId, int webViewInstanceId, @@ -335,8 +388,12 @@ abstract class WKNavigationDelegateFlutterApi { /// See https://developer.apple.com/documentation/objectivec/nsobject. @HostApi(dartHostTestHandler: 'TestNSObjectHostApi') abstract class NSObjectHostApi { + @ObjCSelector('disposeObjectWithIdentifier:') void dispose(int instanceId); + @ObjCSelector( + 'addObserverForObjectWithIdentifier:observerIdentifier:keyPath:options:', + ) void addObserver( int instanceId, int observerInstanceId, @@ -344,12 +401,16 @@ abstract class NSObjectHostApi { List options, ); + @ObjCSelector( + 'removeObserverForObjectWithIdentifier:observerIdentifier:keyPath:', + ) void removeObserver(int instanceId, int observerInstanceId, String keyPath); } /// Disposes references to functions. @FlutterApi() abstract class FunctionFlutterApi { + @ObjCSelector('disposeFunctionWithIdentifier:') void dispose(int instanceId); } @@ -358,42 +419,62 @@ abstract class FunctionFlutterApi { /// See https://developer.apple.com/documentation/webkit/wkwebview?language=objc. @HostApi(dartHostTestHandler: 'TestWKWebViewHostApi') abstract class WKWebViewHostApi { + @ObjCSelector('createWithIdentifier:configurationIdentifier:') void create(int instanceId, int configurationInstanceId); + @ObjCSelector('setUIDelegateForWebViewWithIdentifier:delegateIdentifier:') void setUIDelegate(int instanceId, int? uiDelegateInstanceId); + @ObjCSelector( + 'setNavigationDelegateForWebViewWithIdentifier:delegateIdentifier:', + ) void setNavigationDelegate(int instanceId, int? navigationDelegateInstanceId); + @ObjCSelector('URLForWebViewWithIdentifier:') String? getUrl(int instanceId); + @ObjCSelector('estimatedProgressForWebViewWithIdentifier:') double getEstimatedProgress(int instanceId); + @ObjCSelector('loadRequestForWebViewWithIdentifier:request:') void loadRequest(int instanceId, NSUrlRequestData request); + @ObjCSelector('loadHTMLForWebViewWithIdentifier:HTMLString:baseURL:') void loadHtmlString(int instanceId, String string, String? baseUrl); + @ObjCSelector('loadFileForWebViewWithIdentifier:fileURL:readAccessURL:') void loadFileUrl(int instanceId, String url, String readAccessUrl); + @ObjCSelector('loadAssetForWebViewWithIdentifier:assetKey:') void loadFlutterAsset(int instanceId, String key); + @ObjCSelector('canGoBackForWebViewWithIdentifier:') bool canGoBack(int instanceId); + @ObjCSelector('canGoForwardForWebViewWithIdentifier:') bool canGoForward(int instanceId); + @ObjCSelector('goBackForWebViewWithIdentifier:') void goBack(int instanceId); + @ObjCSelector('goForwardForWebViewWithIdentifier:') void goForward(int instanceId); + @ObjCSelector('reloadWebViewWithIdentifier:') void reload(int instanceId); + @ObjCSelector('titleForWebViewWithIdentifier:') String? getTitle(int instanceId); + @ObjCSelector('setAllowsBackForwardForWebViewWithIdentifier:isAllowed:') void setAllowsBackForwardNavigationGestures(int instanceId, bool allow); + @ObjCSelector('setUserAgentForWebViewWithIdentifier:userAgent:') void setCustomUserAgent(int instanceId, String? userAgent); + @ObjCSelector('evaluateJavaScriptForWebViewWithIdentifier:javaScriptString:') @async - String evaluateJavaScript(int instanceId, String javascriptString); + Object? evaluateJavaScript(int instanceId, String javaScriptString); } /// Mirror of WKUIDelegate. @@ -401,6 +482,7 @@ abstract class WKWebViewHostApi { /// See https://developer.apple.com/documentation/webkit/wkuidelegate?language=objc. @HostApi(dartHostTestHandler: 'TestWKUIDelegateHostApi') abstract class WKUIDelegateHostApi { + @ObjCSelector('createWithIdentifier:') void create(int instanceId); } @@ -409,10 +491,12 @@ abstract class WKUIDelegateHostApi { /// See https://developer.apple.com/documentation/webkit/wkhttpcookiestore?language=objc. @HostApi(dartHostTestHandler: 'TestWKHttpCookieStoreHostApi') abstract class WKHttpCookieStoreHostApi { + @ObjCSelector('createFromWebsiteDataStoreWithIdentifier:dataStoreIdentifier:') void createFromWebsiteDataStore( int instanceId, int websiteDataStoreInstanceId, ); + @ObjCSelector('setCookieForStoreWithIdentifier:cookie:') void setCookie(int instanceId, NSHttpCookieData cookie); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index e52d1df9bbb6..f4e72b8f14eb 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -29,4 +29,4 @@ dev_dependencies: sdk: flutter mockito: ^5.1.0 pedantic: ^1.10.0 - pigeon: ^2.0.3 + pigeon: ^3.0.3 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart index 67ae8882cbd3..042ddedbd769 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.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 (v2.0.3), do not edit directly. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis // ignore_for_file: avoid_relative_lib_imports @@ -172,9 +172,7 @@ abstract class TestUIViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.UIViewHostApi.setBackgroundColor was null, expected non-null int.'); final int? arg_value = (args[1] as int?); - assert(arg_value != null, - 'Argument for dev.flutter.pigeon.UIViewHostApi.setBackgroundColor was null, expected non-null int.'); - api.setBackgroundColor(arg_instanceId!, arg_value!); + api.setBackgroundColor(arg_instanceId!, arg_value); return {}; }); } @@ -766,9 +764,7 @@ abstract class TestWKNavigationDelegateHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation was null, expected non-null int.'); final int? arg_functionInstanceId = (args[1] as int?); - assert(arg_functionInstanceId != null, - 'Argument for dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation was null, expected non-null int.'); - api.setDidFinishNavigation(arg_instanceId!, arg_functionInstanceId!); + api.setDidFinishNavigation(arg_instanceId!, arg_functionInstanceId); return {}; }); } @@ -892,9 +888,30 @@ class _TestWKWebViewHostApiCodec extends StandardMessageCodec { const _TestWKWebViewHostApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is NSUrlRequestData) { + if (value is NSHttpCookieData) { buffer.putUint8(128); writeValue(buffer, value.encode()); + } else if (value is NSHttpCookiePropertyKeyEnumData) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is NSKeyValueObservingOptionsEnumData) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is NSUrlRequestData) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is WKAudiovisualMediaTypeEnumData) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else if (value is WKUserScriptData) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); + } else if (value is WKUserScriptInjectionTimeEnumData) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else if (value is WKWebsiteDataTypesEnumData) { + buffer.putUint8(135); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -904,8 +921,29 @@ class _TestWKWebViewHostApiCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 128: + return NSHttpCookieData.decode(readValue(buffer)!); + + case 129: + return NSHttpCookiePropertyKeyEnumData.decode(readValue(buffer)!); + + case 130: + return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!); + + case 131: return NSUrlRequestData.decode(readValue(buffer)!); + case 132: + return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!); + + case 133: + return WKUserScriptData.decode(readValue(buffer)!); + + case 134: + return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); + + case 135: + return WKWebsiteDataTypesEnumData.decode(readValue(buffer)!); + default: return super.readValueOfType(type, buffer); } @@ -932,7 +970,7 @@ abstract class TestWKWebViewHostApi { String? getTitle(int instanceId); void setAllowsBackForwardNavigationGestures(int instanceId, bool allow); void setCustomUserAgent(int instanceId, String? userAgent); - Future evaluateJavaScript(int instanceId, String javascriptString); + Future evaluateJavaScript(int instanceId, String javaScriptString); static void setup(TestWKWebViewHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -972,9 +1010,7 @@ abstract class TestWKWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setUIDelegate was null, expected non-null int.'); final int? arg_uiDelegateInstanceId = (args[1] as int?); - assert(arg_uiDelegateInstanceId != null, - 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setUIDelegate was null, expected non-null int.'); - api.setUIDelegate(arg_instanceId!, arg_uiDelegateInstanceId!); + api.setUIDelegate(arg_instanceId!, arg_uiDelegateInstanceId); return {}; }); } @@ -994,10 +1030,8 @@ abstract class TestWKWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setNavigationDelegate was null, expected non-null int.'); final int? arg_navigationDelegateInstanceId = (args[1] as int?); - assert(arg_navigationDelegateInstanceId != null, - 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setNavigationDelegate was null, expected non-null int.'); api.setNavigationDelegate( - arg_instanceId!, arg_navigationDelegateInstanceId!); + arg_instanceId!, arg_navigationDelegateInstanceId); return {}; }); } @@ -1080,9 +1114,7 @@ abstract class TestWKWebViewHostApi { assert(arg_string != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString was null, expected non-null String.'); final String? arg_baseUrl = (args[2] as String?); - assert(arg_baseUrl != null, - 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString was null, expected non-null String.'); - api.loadHtmlString(arg_instanceId!, arg_string!, arg_baseUrl!); + api.loadHtmlString(arg_instanceId!, arg_string!, arg_baseUrl); return {}; }); } @@ -1287,9 +1319,7 @@ abstract class TestWKWebViewHostApi { assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent was null, expected non-null int.'); final String? arg_userAgent = (args[1] as String?); - assert(arg_userAgent != null, - 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent was null, expected non-null String.'); - api.setCustomUserAgent(arg_instanceId!, arg_userAgent!); + api.setCustomUserAgent(arg_instanceId!, arg_userAgent); return {}; }); } @@ -1308,11 +1338,11 @@ abstract class TestWKWebViewHostApi { final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript was null, expected non-null int.'); - final String? arg_javascriptString = (args[1] as String?); - assert(arg_javascriptString != null, + final String? arg_javaScriptString = (args[1] as String?); + assert(arg_javaScriptString != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript was null, expected non-null String.'); - final String output = await api.evaluateJavaScript( - arg_instanceId!, arg_javascriptString!); + final Object? output = await api.evaluateJavaScript( + arg_instanceId!, arg_javaScriptString!); return {'result': output}; }); } 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 e876aa63cd29..908709c90134 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 @@ -140,12 +140,12 @@ class MockTestWKWebViewHostApi extends _i1.Mock Invocation.method(#setCustomUserAgent, [instanceId, userAgent]), returnValueForMissingStub: null); @override - _i4.Future evaluateJavaScript( - int? instanceId, String? javascriptString) => + _i4.Future evaluateJavaScript( + int? instanceId, String? javaScriptString) => (super.noSuchMethod( Invocation.method( - #evaluateJavaScript, [instanceId, javascriptString]), - returnValue: Future.value('')) as _i4.Future); + #evaluateJavaScript, [instanceId, javaScriptString]), + returnValue: Future.value()) as _i4.Future); } /// A class which mocks [TestUIScrollViewHostApi]. 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 67c06286185c..d4f1fefc190c 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 @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. // Do not manually edit this file. import 'dart:async' as _i4; @@ -278,12 +278,12 @@ class MockTestWKWebViewHostApi extends _i1.Mock Invocation.method(#setCustomUserAgent, [instanceId, userAgent]), returnValueForMissingStub: null); @override - _i4.Future evaluateJavaScript( - int? instanceId, String? javascriptString) => + _i4.Future evaluateJavaScript( + int? instanceId, String? javaScriptString) => (super.noSuchMethod( Invocation.method( - #evaluateJavaScript, [instanceId, javascriptString]), - returnValue: Future.value('')) as _i4.Future); + #evaluateJavaScript, [instanceId, javaScriptString]), + returnValue: Future.value()) as _i4.Future); } /// A class which mocks [TestWKWebsiteDataStoreHostApi]. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index bc79f656b6e3..1138d13f0c25 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. // Do not manually edit this file. import 'dart:async' as _i5; @@ -179,6 +179,33 @@ class MockWKNavigationDelegate extends _i1.Mock [webViewWebContentProcessDidTerminate]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future dispose() => + (super.noSuchMethod(Invocation.method(#dispose, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setObserveValue( + void Function( + String, _i7.NSObject, Map<_i7.NSKeyValueChangeKey, Object?>)? + observeValue) => + (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKPreferences]. From bd6406b99eba3814101e8d40d2ef2e134d22fc82 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 30 Apr 2022 18:59:07 -0400 Subject: [PATCH 220/844] Roll Flutter from 2a24ee494db4 to 7b8d50020338 (1 revision) (#5457) --- .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 81dd56edbc55..8c88219c1b12 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2a24ee494db49d2240692ef191e568e735518cb9 +7b8d500203381fedc9f89e6c6915c5431f310e09 From 60a676ee1e2df32038947270e85ff25abe44b0be Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 30 Apr 2022 20:04:08 -0400 Subject: [PATCH 221/844] Roll Flutter from 7b8d50020338 to 6755ba59f436 (1 revision) (#5458) --- .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 8c88219c1b12..3647d7f9f53e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -7b8d500203381fedc9f89e6c6915c5431f310e09 +6755ba59f43644cfeb82116b9b5595a8ddf464be From 629d506932d9d25c663ea19bd24912c8fa88b0ed Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 1 May 2022 00:39:09 -0400 Subject: [PATCH 222/844] Roll Flutter from 6755ba59f436 to 43555d05040c (1 revision) (#5459) --- .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 3647d7f9f53e..39c16ba28144 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -6755ba59f43644cfeb82116b9b5595a8ddf464be +43555d05040c7c4d7db2dabc39c407567df4823e From 45bc9e431471dbbbc710fbbc8bde2a97a663743a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 1 May 2022 03:24:06 -0400 Subject: [PATCH 223/844] Roll Flutter from 43555d05040c to 0f1eb8bad788 (2 revisions) (#5461) --- .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 39c16ba28144..4d7165f03617 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -43555d05040c7c4d7db2dabc39c407567df4823e +0f1eb8bad7885317be8d843d4486c110be3132bf From 3ded4261346e44948cba2da1138c38ba8e1eb6db Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 1 May 2022 10:49:08 -0400 Subject: [PATCH 224/844] Roll Flutter from 0f1eb8bad788 to e3d39f62a26b (1 revision) (#5462) --- .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 4d7165f03617..2d12523bd5ec 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -0f1eb8bad7885317be8d843d4486c110be3132bf +e3d39f62a26bc256f992101c5ae2f95c26a8fd3d From 3a6cab77f1fc0a5b2fc567d2a64fda35fc0512d7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 1 May 2022 13:24:11 -0400 Subject: [PATCH 225/844] Roll Flutter from e3d39f62a26b to e66662f1cb75 (1 revision) (#5463) --- .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 2d12523bd5ec..b2cde3347077 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e3d39f62a26bc256f992101c5ae2f95c26a8fd3d +e66662f1cb75d513935b5c67eb9678d1b3273187 From 9304c3d1d01131cd786837767a100a109ec54773 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 1 May 2022 14:29:08 -0400 Subject: [PATCH 226/844] Roll Flutter from e66662f1cb75 to a35af5b4659a (1 revision) (#5464) --- .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 b2cde3347077..bc0a82139dcb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e66662f1cb75d513935b5c67eb9678d1b3273187 +a35af5b4659a39eeba47cff0b863c9a5883f8686 From 29a954e02c482c890eefb39964d7530ad75c36b3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 1 May 2022 15:34:06 -0400 Subject: [PATCH 227/844] Roll Flutter from a35af5b4659a to d600a3b48801 (1 revision) (#5465) --- .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 bc0a82139dcb..f5e79613312f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a35af5b4659a39eeba47cff0b863c9a5883f8686 +d600a3b48801e856a1345e4d36b705de467093cf From f7d26e94df0b9be2c1ab28b467cd28f23e0e7193 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 1 May 2022 23:44:05 -0400 Subject: [PATCH 228/844] Roll Flutter from d600a3b48801 to f7df22590adf (1 revision) (#5466) --- .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 f5e79613312f..7249b2760418 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -d600a3b48801e856a1345e4d36b705de467093cf +f7df22590adf2fad70f6e83a2df73e9fa866ddfe From bbcb88d3bae4fa1637cc61681e9ab8862e3146c8 Mon Sep 17 00:00:00 2001 From: Jeevan Chandra Joshi Date: Mon, 2 May 2022 23:29:12 +0530 Subject: [PATCH 229/844] remove unnecessary imports (#5410) --- analysis_options.yaml | 3 --- analysis_options_legacy.yaml | 3 --- packages/camera/camera/CHANGELOG.md | 4 ++++ packages/camera/camera/lib/src/camera_image.dart | 1 - packages/camera/camera/test/camera_image_test.dart | 2 -- packages/camera/camera/test/camera_preview_test.dart | 2 -- packages/camera/camera/test/camera_test.dart | 1 - packages/camera/camera/test/camera_value_test.dart | 1 - packages/camera/camera_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/events/camera_event.dart | 1 - .../lib/src/method_channel/method_channel_camera.dart | 4 ---- .../lib/src/platform_interface/camera_platform.dart | 5 ----- .../test/events/camera_event_test.dart | 2 -- .../test/method_channel/method_channel_camera_test.dart | 3 --- packages/camera/camera_windows/CHANGELOG.md | 4 ++++ packages/camera/camera_windows/lib/camera_windows.dart | 1 - packages/file_selector/file_selector/CHANGELOG.md | 1 + .../file_selector/test/file_selector_test.dart | 1 - packages/file_selector/file_selector_macos/CHANGELOG.md | 4 ++++ .../file_selector_macos/lib/file_selector_macos.dart | 1 - .../file_selector_platform_interface/CHANGELOG.md | 4 ++++ .../src/method_channel/method_channel_file_selector.dart | 1 - .../lib/src/platform_interface/file_selector_interface.dart | 1 - packages/file_selector/file_selector_windows/CHANGELOG.md | 4 ++++ .../file_selector_windows/lib/file_selector_windows.dart | 1 - .../google_maps_flutter/google_maps_flutter/CHANGELOG.md | 4 ++++ .../example/integration_test/google_maps_test.dart | 1 - .../google_maps_flutter/example/lib/lite_mode.dart | 1 - .../google_maps_flutter/example/lib/map_click.dart | 1 - .../google_maps_flutter/example/lib/map_coordinates.dart | 1 - .../google_maps_flutter/example/lib/scrolling_map.dart | 1 - .../google_maps_flutter/lib/google_maps_flutter.dart | 1 - .../google_maps_flutter_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/events/map_event.dart | 1 - .../method_channel/method_channel_google_maps_flutter.dart | 1 - .../platform_interface/google_maps_flutter_platform.dart | 1 - .../method_channel_google_maps_flutter_test.dart | 3 --- .../google_maps_flutter_platform_test.dart | 1 - .../google_maps_flutter_web/CHANGELOG.md | 4 ++++ .../example/integration_test/projection_test.dart | 1 - .../lib/google_maps_flutter_web.dart | 1 - .../google_sign_in_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/method_channel_google_sign_in.dart | 1 - .../test/method_channel_google_sign_in_test.dart | 1 - packages/image_picker/image_picker_ios/CHANGELOG.md | 4 ++++ packages/image_picker/image_picker_ios/test/test_api.dart | 1 - packages/image_picker/image_picker_windows/CHANGELOG.md | 6 +++++- .../image_picker/image_picker_windows/example/lib/main.dart | 1 - packages/in_app_purchase/in_app_purchase/CHANGELOG.md | 1 + .../in_app_purchase/lib/in_app_purchase.dart | 1 - .../in_app_purchase/in_app_purchase_android/CHANGELOG.md | 4 ++++ .../src/billing_client_wrappers/billing_client_wrapper.dart | 2 -- .../lib/src/in_app_purchase_android_platform.dart | 1 - .../lib/src/in_app_purchase_android_platform_addition.dart | 1 - .../in_app_purchase_android_platform_addition_test.dart | 1 - .../in_app_purchase_platform_interface/CHANGELOG.md | 4 ++++ .../test/src/types/product_details_test.dart | 1 - .../in_app_purchase/in_app_purchase_storekit/CHANGELOG.md | 4 ++++ .../lib/src/in_app_purchase_storekit_platform.dart | 1 - .../src/store_kit_wrappers/sk_payment_queue_wrapper.dart | 3 --- .../test/fakes/fake_storekit_platform.dart | 1 - .../test/store_kit_wrappers/sk_product_test.dart | 1 - packages/ios_platform_images/CHANGELOG.md | 4 ++++ packages/ios_platform_images/lib/ios_platform_images.dart | 1 - packages/local_auth/local_auth/CHANGELOG.md | 4 ++++ packages/local_auth/local_auth/lib/src/local_auth.dart | 2 -- packages/local_auth/local_auth/test/local_auth_test.dart | 3 --- packages/local_auth/local_auth_android/CHANGELOG.md | 4 ++++ .../local_auth_android/lib/local_auth_android.dart | 3 --- .../local_auth/local_auth_android/test/local_auth_test.dart | 3 --- packages/local_auth/local_auth_ios/CHANGELOG.md | 4 ++++ packages/local_auth/local_auth_ios/example/lib/main.dart | 2 -- packages/local_auth/local_auth_ios/lib/local_auth_ios.dart | 3 --- .../local_auth/local_auth_ios/test/local_auth_test.dart | 3 --- .../local_auth/local_auth_platform_interface/CHANGELOG.md | 4 ++++ .../lib/default_method_channel_platform.dart | 3 --- .../test/default_method_channel_platform_test.dart | 4 ---- packages/path_provider/path_provider/CHANGELOG.md | 1 + .../path_provider/test/path_provider_test.dart | 1 - .../path_provider_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/method_channel_path_provider.dart | 2 -- packages/quick_actions/quick_actions/CHANGELOG.md | 1 + .../quick_actions/test/quick_actions_test.dart | 2 -- .../shared_preferences_linux/CHANGELOG.md | 4 ++++ .../example/integration_test/shared_preferences_test.dart | 2 -- .../shared_preferences_macos/CHANGELOG.md | 4 ++++ .../example/integration_test/shared_preferences_test.dart | 2 -- packages/url_launcher/url_launcher/CHANGELOG.md | 4 ++++ .../url_launcher/url_launcher/lib/src/url_launcher_uri.dart | 2 -- .../url_launcher/url_launcher/test/src/legacy_api_test.dart | 1 - packages/video_player/video_player/CHANGELOG.md | 4 ++++ packages/video_player/video_player/example/lib/main.dart | 1 - .../video_player/video_player/test/video_player_test.dart | 2 -- packages/video_player/video_player_android/CHANGELOG.md | 4 ++++ .../video_player/video_player_android/example/lib/main.dart | 1 - .../video_player_android/example/lib/mini_controller.dart | 1 - .../video_player_android/lib/src/android_video_player.dart | 1 - .../video_player/video_player_avfoundation/CHANGELOG.md | 4 ++++ .../video_player_avfoundation/example/lib/main.dart | 1 - .../example/lib/mini_controller.dart | 1 - .../lib/src/avfoundation_video_player.dart | 1 - .../video_player_platform_interface/CHANGELOG.md | 4 ++++ .../lib/method_channel_video_player.dart | 1 - packages/video_player/video_player_web/CHANGELOG.md | 4 ++++ .../video_player/video_player_web/lib/src/video_player.dart | 1 - packages/webview_flutter/webview_flutter/CHANGELOG.md | 4 ++++ .../example/integration_test/webview_flutter_test.dart | 2 -- .../webview_flutter/webview_flutter/lib/src/webview.dart | 3 --- .../webview_flutter/webview_flutter_android/CHANGELOG.md | 4 ++++ .../example/integration_test/webview_flutter_test.dart | 1 - .../webview_flutter_android/example/lib/main.dart | 1 - .../test/webview_android_widget_test.dart | 1 - .../webview_flutter_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/method_channel/webview_method_channel.dart | 1 - .../lib/src/types/creation_params.dart | 3 --- .../src/method_channel/webview_method_channel_test.dart | 1 - .../javascript_channel_registry_test.dart | 1 - packages/webview_flutter/webview_flutter_web/CHANGELOG.md | 1 + .../webview_flutter_web/example/lib/main.dart | 1 - .../webview_flutter_web/example/lib/web_view.dart | 1 - .../webview_flutter/webview_flutter_wkwebview/CHANGELOG.md | 4 ++++ .../example/integration_test/webview_flutter_test.dart | 1 - .../webview_flutter_wkwebview/example/lib/main.dart | 1 - .../lib/src/web_kit_webview_widget.dart | 1 - .../test/src/web_kit_cookie_manager_test.dart | 2 -- .../test/src/web_kit_webview_widget_test.dart | 2 -- 126 files changed, 138 insertions(+), 140 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 60d2c33601eb..f6177cd9939a 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -49,9 +49,6 @@ analyzer: # Allow null checks for as long as mixed mode is officially supported. unnecessary_null_comparison: false always_require_non_null_named_parameters: false # not needed with nnbd - # TODO(https://github.com/flutter/flutter/issues/74381): - # Clean up existing unnecessary imports, and remove line to ignore. - unnecessary_import: ignore exclude: # Ignore generated files - '**/*.g.dart' diff --git a/analysis_options_legacy.yaml b/analysis_options_legacy.yaml index b2a343f220c8..da3c18071650 100644 --- a/analysis_options_legacy.yaml +++ b/analysis_options_legacy.yaml @@ -8,9 +8,6 @@ analyzer: - '**/*.pigeon.dart' # Pigeon generated file errors: always_require_non_null_named_parameters: false # not needed with nnbd - # TODO(https://github.com/flutter/flutter/issues/74381): - # Clean up existing unnecessary imports, and remove line to ignore. - unnecessary_import: ignore unnecessary_null_comparison: false # Turned as long as nnbd mix-mode is supported. linter: rules: diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index a9986697b048..4d7e9bbeb218 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 0.9.4+21 * Fixes README code samples. diff --git a/packages/camera/camera/lib/src/camera_image.dart b/packages/camera/camera/lib/src/camera_image.dart index fd3a3d6233bc..0f2377ed170c 100644 --- a/packages/camera/camera/lib/src/camera_image.dart +++ b/packages/camera/camera/lib/src/camera_image.dart @@ -6,7 +6,6 @@ import 'dart:typed_data'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; /// A single color plane of image data. /// diff --git a/packages/camera/camera/test/camera_image_test.dart b/packages/camera/camera/test/camera_image_test.dart index b09a14177121..55bf4a2727e2 100644 --- a/packages/camera/camera/test/camera_image_test.dart +++ b/packages/camera/camera/test/camera_image_test.dart @@ -5,8 +5,6 @@ import 'dart:typed_data'; import 'package:camera/camera.dart'; -import 'package:camera_platform_interface/camera_platform_interface.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/packages/camera/camera/test/camera_preview_test.dart b/packages/camera/camera/test/camera_preview_test.dart index 76bfe40605d7..fe2f4f4e35c7 100644 --- a/packages/camera/camera/test/camera_preview_test.dart +++ b/packages/camera/camera/test/camera_preview_test.dart @@ -2,8 +2,6 @@ // 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:camera/camera.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; diff --git a/packages/camera/camera/test/camera_test.dart b/packages/camera/camera/test/camera_test.dart index c4e0c9388231..34a474b2b4f3 100644 --- a/packages/camera/camera/test/camera_test.dart +++ b/packages/camera/camera/test/camera_test.dart @@ -4,7 +4,6 @@ import 'dart:async'; import 'dart:math'; -import 'dart:ui'; import 'package:camera/camera.dart'; import 'package:camera_platform_interface/camera_platform_interface.dart'; diff --git a/packages/camera/camera/test/camera_value_test.dart b/packages/camera/camera/test/camera_value_test.dart index 62df1fd1a2a1..d718d5e48f02 100644 --- a/packages/camera/camera/test/camera_value_test.dart +++ b/packages/camera/camera/test/camera_value_test.dart @@ -5,7 +5,6 @@ import 'dart:ui'; import 'package:camera/camera.dart'; -import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index ef251eab9b93..3cad35d71ae5 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.1.6 * Adopts `Object.hash`. diff --git a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart b/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart index 755006cab8ab..a6ace8f9ae74 100644 --- a/packages/camera/camera_platform_interface/lib/src/events/camera_event.dart +++ b/packages/camera/camera_platform_interface/lib/src/events/camera_event.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:camera_platform_interface/src/types/focus_mode.dart'; import 'package:flutter/foundation.dart' show immutable; import '../../camera_platform_interface.dart'; diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index ec84c204b2c6..c856f3467821 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -6,11 +6,7 @@ import 'dart:async'; import 'dart:math'; import 'package:camera_platform_interface/camera_platform_interface.dart'; -import 'package:camera_platform_interface/src/events/device_event.dart'; -import 'package:camera_platform_interface/src/types/focus_mode.dart'; -import 'package:camera_platform_interface/src/types/image_format_group.dart'; import 'package:camera_platform_interface/src/utils/utils.dart'; -import 'package:cross_file/cross_file.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; 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 0d240496086d..daa19b8b4011 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 @@ -6,12 +6,7 @@ import 'dart:async'; import 'dart:math'; import 'package:camera_platform_interface/camera_platform_interface.dart'; -import 'package:camera_platform_interface/src/events/device_event.dart'; import 'package:camera_platform_interface/src/method_channel/method_channel_camera.dart'; -import 'package:camera_platform_interface/src/types/exposure_mode.dart'; -import 'package:camera_platform_interface/src/types/focus_mode.dart'; -import 'package:camera_platform_interface/src/types/image_format_group.dart'; -import 'package:cross_file/cross_file.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; diff --git a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart index 8a428e2fd43a..3914859d44b0 100644 --- a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart +++ b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart @@ -3,8 +3,6 @@ // found in the LICENSE file. import 'package:camera_platform_interface/camera_platform_interface.dart'; -import 'package:camera_platform_interface/src/types/exposure_mode.dart'; -import 'package:camera_platform_interface/src/types/focus_mode.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { diff --git a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart index 27fe7c6b7166..7da4262cdf79 100644 --- a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart +++ b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart @@ -7,11 +7,8 @@ import 'dart:math'; import 'package:async/async.dart'; import 'package:camera_platform_interface/camera_platform_interface.dart'; -import 'package:camera_platform_interface/src/events/device_event.dart'; import 'package:camera_platform_interface/src/method_channel/method_channel_camera.dart'; -import 'package:camera_platform_interface/src/types/focus_mode.dart'; import 'package:camera_platform_interface/src/utils/utils.dart'; -import 'package:flutter/services.dart' hide DeviceOrientation; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index 1318780830f8..b1383dc54993 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 0.1.0 * Initial release diff --git a/packages/camera/camera_windows/lib/camera_windows.dart b/packages/camera/camera_windows/lib/camera_windows.dart index 33f8bfb68fac..d998863d43a7 100644 --- a/packages/camera/camera_windows/lib/camera_windows.dart +++ b/packages/camera/camera_windows/lib/camera_windows.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'dart:math'; import 'package:camera_platform_interface/camera_platform_interface.dart'; -import 'package:cross_file/cross_file.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:stream_transform/stream_transform.dart'; diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 7eeedad37348..c0821fed7446 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Removes unnecessary imports. * Adds OS version support information to README. ## 0.8.4+1 diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index 6ab0bd975036..fc3e668f9d9e 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -6,7 +6,6 @@ import 'package:file_selector/file_selector.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:test/fake.dart'; void main() { late FakeFileSelector fakePlatformImplementation; diff --git a/packages/file_selector/file_selector_macos/CHANGELOG.md b/packages/file_selector/file_selector_macos/CHANGELOG.md index 794d056811f4..b46a174bd323 100644 --- a/packages/file_selector/file_selector_macos/CHANGELOG.md +++ b/packages/file_selector/file_selector_macos/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 0.8.2 * Moves source to flutter/plugins. diff --git a/packages/file_selector/file_selector_macos/lib/file_selector_macos.dart b/packages/file_selector/file_selector_macos/lib/file_selector_macos.dart index e321d331961b..e50c296b005f 100644 --- a/packages/file_selector/file_selector_macos/lib/file_selector_macos.dart +++ b/packages/file_selector/file_selector_macos/lib/file_selector_macos.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:cross_file/cross_file.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; diff --git a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md index b633bd35a59e..100b6ad136a7 100644 --- a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md +++ b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.0.4 * Removes dependency on `meta`. diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index f1fa82b6f3c6..c6d0f4a56155 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.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:cross_file/cross_file.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index f8fa83bd18d2..a23957af9110 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -4,7 +4,6 @@ import 'dart:async'; -import 'package:cross_file/cross_file.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; diff --git a/packages/file_selector/file_selector_windows/CHANGELOG.md b/packages/file_selector/file_selector_windows/CHANGELOG.md index 63999f245d82..ae3cd13342b1 100644 --- a/packages/file_selector/file_selector_windows/CHANGELOG.md +++ b/packages/file_selector/file_selector_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 0.8.2 * Moves source to flutter/plugins, and restructures to allow for unit testing. diff --git a/packages/file_selector/file_selector_windows/lib/file_selector_windows.dart b/packages/file_selector/file_selector_windows/lib/file_selector_windows.dart index a8b159711e2a..b91a22355572 100644 --- a/packages/file_selector/file_selector_windows/lib/file_selector_windows.dart +++ b/packages/file_selector/file_selector_windows/lib/file_selector_windows.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:cross_file/cross_file.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 61b151427520..b0662b89d9cc 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.1.4 * Updates Android Google maps sdk version to `18.0.2`. diff --git a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart index a007dddd9188..35351505e495 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart @@ -8,7 +8,6 @@ import 'dart:typed_data'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:integration_test/integration_test.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart index 0ecc5ed38e87..b1b58fdc91bf 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart @@ -5,7 +5,6 @@ // ignore_for_file: public_member_api_docs import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart index ef1bfe2b11dc..bbe2372dce9a 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart @@ -5,7 +5,6 @@ // ignore_for_file: public_member_api_docs import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart index dc4376a19547..8e4853c040ed 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart @@ -5,7 +5,6 @@ // ignore_for_file: public_member_api_docs import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart index 8d046fc6b387..04769315e685 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart @@ -7,7 +7,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart b/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart index 5b1e67c943dd..9736b8b5e06f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart +++ b/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart @@ -7,7 +7,6 @@ library google_maps_flutter; import 'dart:async'; import 'dart:io'; import 'dart:typed_data'; -import 'dart:ui'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; 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 b955bc033210..b6e15d2ad14c 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 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.1.6 * Migrates from `ui.hash*` to `Object.hash*`. 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 614cbe8e29fb..bb4124612be4 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 @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; -import 'package:google_maps_flutter_platform_interface/src/method_channel/method_channel_google_maps_flutter.dart'; /// Generic Event coming from the native side of Maps. /// 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 99f4fddaccd3..9c5cbf5a54f0 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 @@ -14,7 +14,6 @@ import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platf import 'package:stream_transform/stream_transform.dart'; import '../types/tile_overlay_updates.dart'; -import '../types/utils/tile_overlay.dart'; /// Error thrown when an unknown map ID is provided to a method channel API. class UnknownMapIDError extends Error { 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 6b39973134be..b6c7b8be692d 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 @@ -11,7 +11,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; -import 'package:google_maps_flutter_platform_interface/src/method_channel/method_channel_google_maps_flutter.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/method_channel/method_channel_google_maps_flutter_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/method_channel/method_channel_google_maps_flutter_test.dart index 176f702ff0ff..9ae42ced6c42 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/method_channel/method_channel_google_maps_flutter_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/method_channel/method_channel_google_maps_flutter_test.dart @@ -5,10 +5,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:google_maps_flutter_platform_interface/src/events/map_event.dart'; -import 'package:google_maps_flutter_platform_interface/src/method_channel/method_channel_google_maps_flutter.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; -import 'dart:async'; import 'package:async/async.dart'; 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 c381f9e30750..bdbaff7e2599 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 @@ -10,7 +10,6 @@ import 'package:mockito/mockito.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:google_maps_flutter_platform_interface/src/method_channel/method_channel_google_maps_flutter.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; void main() { 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 8a3b94151de2..48908b984b0f 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 @@ +## NEXT + +* Removes unnecessary imports. + ## 0.3.2+1 * Removes dependency on `meta`. 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 8a5a62013538..1bf0f10f50c8 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 @@ -10,7 +10,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart' show GoogleMap, GoogleMapController; 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 0355f2923528..c3079dc2492d 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 @@ -10,7 +10,6 @@ import 'dart:js_util'; import 'src/shims/dart_ui.dart' as ui; // Conditionally imports dart:ui in web import 'dart:convert'; -import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; diff --git a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md index 66fdb3e72a56..da214d3ce6a9 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.1.2 * Internal code cleanup for stricter analysis options. diff --git a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart index 1abda09fa99f..e56d2028a205 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart @@ -8,7 +8,6 @@ import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; import '../google_sign_in_platform_interface.dart'; -import 'types.dart'; import 'utils.dart'; /// An implementation of [GoogleSignInPlatform] that uses method channels. diff --git a/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart b/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart index a1d83c3f05e6..b6604d1e658e 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart @@ -5,7 +5,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; -import 'package:google_sign_in_platform_interface/src/types.dart'; import 'package:google_sign_in_platform_interface/src/utils.dart'; const Map kUserData = { diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index 3380d14418c2..31a0795a4e30 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 0.8.5 * Switches to an in-package method channel based on Pigeon. diff --git a/packages/image_picker/image_picker_ios/test/test_api.dart b/packages/image_picker/image_picker_ios/test/test_api.dart index 1f76e871521d..d22a26b2489b 100644 --- a/packages/image_picker/image_picker_ios/test/test_api.dart +++ b/packages/image_picker/image_picker_ios/test/test_api.dart @@ -6,7 +6,6 @@ // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis // ignore_for_file: avoid_relative_lib_imports // @dart = 2.12 -import 'dart:async'; import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; import 'package:flutter/services.dart'; diff --git a/packages/image_picker/image_picker_windows/CHANGELOG.md b/packages/image_picker/image_picker_windows/CHANGELOG.md index c4a71a0176b5..d98656b849c8 100644 --- a/packages/image_picker/image_picker_windows/CHANGELOG.md +++ b/packages/image_picker/image_picker_windows/CHANGELOG.md @@ -1,3 +1,7 @@ -# 0.1.0 +## NEXT + +* Removes unnecessary imports. + +## 0.1.0 * Initial Windows support. diff --git a/packages/image_picker/image_picker_windows/example/lib/main.dart b/packages/image_picker/image_picker_windows/example/lib/main.dart index af97115676c0..577d6dadf2d9 100644 --- a/packages/image_picker/image_picker_windows/example/lib/main.dart +++ b/packages/image_picker/image_picker_windows/example/lib/main.dart @@ -7,7 +7,6 @@ import 'dart:async'; import 'dart:io'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'package:video_player/video_player.dart'; diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index cfe625a0c5b4..24ef9eaffd1d 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Removes unnecessary imports. * Adds OS version support information to README. ## 3.0.2 diff --git a/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart b/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart index df05d8ce86c3..d550e48ebc3a 100644 --- a/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart +++ b/packages/in_app_purchase/in_app_purchase/lib/in_app_purchase.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; import 'package:in_app_purchase_android/in_app_purchase_android.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'; 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 626dba704ad7..2657d504ac91 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 0.2.2+3 * Migrates from `ui.hash*` to `Object.hash*`. 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 416eb5680770..7378aeb84cfc 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 @@ -10,8 +10,6 @@ import 'package:json_annotation/json_annotation.dart'; import '../../billing_client_wrappers.dart'; import '../channel.dart'; -import 'purchase_wrapper.dart'; -import 'sku_details_wrapper.dart'; part 'billing_client_wrapper.g.dart'; diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart index 61af75688a01..14dd69364497 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart @@ -7,7 +7,6 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:in_app_purchase_android/in_app_purchase_android.dart'; -import 'package:in_app_purchase_android/src/in_app_purchase_android_platform_addition.dart'; import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; import '../billing_client_wrappers.dart'; 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 9bcfc3d1b007..dd629164866f 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 @@ -7,7 +7,6 @@ import 'package:in_app_purchase_android/in_app_purchase_android.dart'; import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; import '../billing_client_wrappers.dart'; -import 'types/types.dart'; /// Contains InApp Purchase features that are only available on PlayStore. class InAppPurchaseAndroidPlatformAddition 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 9d2045b4c229..c87d0e39f0c2 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 @@ -8,7 +8,6 @@ import 'package:flutter_test/flutter_test.dart'; 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/channel.dart'; -import 'package:in_app_purchase_android/src/in_app_purchase_android_platform_addition.dart'; import 'billing_client_wrappers/purchase_wrapper_test.dart'; import 'stub_in_app_purchase_platform.dart'; diff --git a/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md index a52d8d244f5f..f7d3268d1cae 100644 --- a/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 1.3.1 * Update to use the `verify` method introduced in plugin_platform_interface 2.1.0. diff --git a/packages/in_app_purchase/in_app_purchase_platform_interface/test/src/types/product_details_test.dart b/packages/in_app_purchase/in_app_purchase_platform_interface/test/src/types/product_details_test.dart index 737f0d00b392..486f38fa850c 100644 --- a/packages/in_app_purchase/in_app_purchase_platform_interface/test/src/types/product_details_test.dart +++ b/packages/in_app_purchase/in_app_purchase_platform_interface/test/src/types/product_details_test.dart @@ -4,7 +4,6 @@ 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_platform_interface/src/types/purchase_status.dart'; void main() { group('Constructor Tests', () { 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 4af33f177799..403ee32be2ae 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 @@ +## NEXT + +* Removes unnecessary imports. + ## 0.3.0+5 * Migrates from `ui.hash*` to `Object.hash*`. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart index 6db2e59e1485..629355d12d4c 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart @@ -7,7 +7,6 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; -import 'package:in_app_purchase_storekit/src/in_app_purchase_storekit_platform_addition.dart'; import '../in_app_purchase_storekit.dart'; import '../store_kit_wrappers.dart'; 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 819c291a1441..70db7da2e275 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 @@ -12,9 +12,6 @@ import 'package:json_annotation/json_annotation.dart'; import '../channel.dart'; import '../in_app_purchase_storekit_platform.dart'; -import 'sk_payment_queue_delegate_wrapper.dart'; -import 'sk_payment_transaction_wrappers.dart'; -import 'sk_product_wrapper.dart'; part 'sk_payment_queue_wrapper.g.dart'; 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 9667d789f1f7..7c543750c25e 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 @@ -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 'dart:async'; import 'dart:io'; import 'package:flutter/services.dart'; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart index fdf80d68a3ea..41329335dcf4 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_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:in_app_purchase_storekit/src/store_kit_wrappers/sk_product_wrapper.dart'; import 'package:in_app_purchase_storekit/src/types/app_store_product_details.dart'; import 'package:in_app_purchase_storekit/src/types/app_store_purchase_details.dart'; import 'package:in_app_purchase_storekit/store_kit_wrappers.dart'; diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index 5e7d997cc711..0a3755afa7e7 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 0.2.0+5 * Migrates from `ui.hash*` to `Object.hash*`. diff --git a/packages/ios_platform_images/lib/ios_platform_images.dart b/packages/ios_platform_images/lib/ios_platform_images.dart index 9632b3b2c05d..4064fb312506 100644 --- a/packages/ios_platform_images/lib/ios_platform_images.dart +++ b/packages/ios_platform_images/lib/ios_platform_images.dart @@ -8,7 +8,6 @@ import 'dart:ui' as ui; import 'package:flutter/foundation.dart' show SynchronousFuture, describeIdentity, immutable, objectRuntimeType; -import 'package:flutter/painting.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index deac871d935f..c495acae5a81 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.0.0 * Migrates plugin to federated architecture. 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 508e2b14e129..77db4d57f018 100644 --- a/packages/local_auth/local_auth/lib/src/local_auth.dart +++ b/packages/local_auth/local_auth/lib/src/local_auth.dart @@ -15,8 +15,6 @@ import 'package:local_auth/src/types/error_codes.dart'; import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_ios/local_auth_ios.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; -import 'package:local_auth_platform_interface/types/biometric_type.dart'; /// A Flutter plugin for authenticating the user identity locally. class LocalAuthentication { 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 d3f92dfe95db..069f9fec2966 100644 --- a/packages/local_auth/local_auth/test/local_auth_test.dart +++ b/packages/local_auth/local_auth/test/local_auth_test.dart @@ -5,12 +5,9 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:local_auth/local_auth.dart'; -import 'package:local_auth/src/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_platform_interface/local_auth_platform_interface.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; -import 'package:local_auth_platform_interface/types/auth_options.dart'; import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index cbf686a2abcb..6afcf1ffed07 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 1.0.2 * Fixes `getEnrolledBiometrics` to match documented behaviour: diff --git a/packages/local_auth/local_auth_android/lib/local_auth_android.dart b/packages/local_auth/local_auth_android/lib/local_auth_android.dart index 2ce0f88477c7..dfe785cc176f 100644 --- a/packages/local_auth/local_auth_android/lib/local_auth_android.dart +++ b/packages/local_auth/local_auth_android/lib/local_auth_android.dart @@ -5,9 +5,6 @@ import 'package:flutter/services.dart'; import 'package:local_auth_android/types/auth_messages_android.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; -import 'package:local_auth_platform_interface/types/auth_options.dart'; -import 'package:local_auth_platform_interface/types/biometric_type.dart'; export 'package:local_auth_android/types/auth_messages_android.dart'; export 'package:local_auth_platform_interface/types/auth_messages.dart'; diff --git a/packages/local_auth/local_auth_android/test/local_auth_test.dart b/packages/local_auth/local_auth_android/test/local_auth_test.dart index 55ac92626aea..86e5713f4bd6 100644 --- a/packages/local_auth/local_auth_android/test/local_auth_test.dart +++ b/packages/local_auth/local_auth_android/test/local_auth_test.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. -import 'dart:async'; - import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:local_auth_android/local_auth_android.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index 0fc716e19f25..ca6ac43eb52f 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 1.0.4 * Fixes `deviceSupportsBiometrics` to return true when biometric hardware diff --git a/packages/local_auth/local_auth_ios/example/lib/main.dart b/packages/local_auth/local_auth_ios/example/lib/main.dart index c966d67ec9c6..a8bf23b78a52 100644 --- a/packages/local_auth/local_auth_ios/example/lib/main.dart +++ b/packages/local_auth/local_auth_ios/example/lib/main.dart @@ -10,8 +10,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:local_auth_ios/local_auth_ios.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; -import 'package:local_auth_platform_interface/types/biometric_type.dart'; void main() { runApp(MyApp()); diff --git a/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart b/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart index ae25bd50ef5e..d9df89a656a8 100644 --- a/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart +++ b/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart @@ -5,9 +5,6 @@ import 'package:flutter/services.dart'; import 'package:local_auth_ios/types/auth_messages_ios.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; -import 'package:local_auth_platform_interface/types/auth_options.dart'; -import 'package:local_auth_platform_interface/types/biometric_type.dart'; export 'package:local_auth_ios/types/auth_messages_ios.dart'; export 'package:local_auth_platform_interface/types/auth_messages.dart'; diff --git a/packages/local_auth/local_auth_ios/test/local_auth_test.dart b/packages/local_auth/local_auth_ios/test/local_auth_test.dart index 192d69ca8ec4..0ad89e52f5ce 100644 --- a/packages/local_auth/local_auth_ios/test/local_auth_test.dart +++ b/packages/local_auth/local_auth_ios/test/local_auth_test.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. -import 'dart:async'; - import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:local_auth_ios/local_auth_ios.dart'; -import 'package:local_auth_platform_interface/types/auth_options.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); diff --git a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md index 9a2ae7aea8e8..10020be3391f 100644 --- a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md +++ b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 1.0.3 * Fixes regression in the default method channel implementation of diff --git a/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart b/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart index 3e695fa41f17..9ded078c3a90 100644 --- a/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart +++ b/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart @@ -4,9 +4,6 @@ import 'package:flutter/services.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; -import 'package:local_auth_platform_interface/types/auth_options.dart'; -import 'package:local_auth_platform_interface/types/biometric_type.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/local_auth'); diff --git a/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart b/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart index 96e78e17cfa8..824597ab2953 100644 --- a/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart +++ b/packages/local_auth/local_auth_platform_interface/test/default_method_channel_platform_test.dart @@ -2,14 +2,10 @@ // 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:local_auth_platform_interface/default_method_channel_platform.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; -import 'package:local_auth_platform_interface/types/auth_messages.dart'; -import 'package:local_auth_platform_interface/types/auth_options.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index 4714ad9ac15d..a26a8901d1d7 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Removes unnecessary imports. * Adds OS version support information to README. ## 2.0.9 diff --git a/packages/path_provider/path_provider/test/path_provider_test.dart b/packages/path_provider/path_provider/test/path_provider_test.dart index 218861606209..aa6d325574df 100644 --- a/packages/path_provider/path_provider/test/path_provider_test.dart +++ b/packages/path_provider/path_provider/test/path_provider_test.dart @@ -8,7 +8,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:path_provider/path_provider.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:test/fake.dart'; const String kTemporaryPath = 'temporaryPath'; const String kApplicationSupportPath = 'applicationSupportPath'; diff --git a/packages/path_provider/path_provider_platform_interface/CHANGELOG.md b/packages/path_provider/path_provider_platform_interface/CHANGELOG.md index 8be2da70c20d..4ed22f09a893 100644 --- a/packages/path_provider/path_provider_platform_interface/CHANGELOG.md +++ b/packages/path_provider/path_provider_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.0.3 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart b/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart index c3e5eccbffba..73e6ab48a585 100644 --- a/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart +++ b/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart @@ -7,8 +7,6 @@ import 'package:flutter/services.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; import 'package:platform/platform.dart'; -import 'enums.dart'; - /// An implementation of [PathProviderPlatform] that uses method channels. class MethodChannelPathProvider extends PathProviderPlatform { /// The method channel used to interact with the native platform. diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index 2a764a57e59b..c30d7052320e 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Removes unnecessary imports. * Updates minimum Flutter version to 2.8. * Adds OS version support information to README. diff --git a/packages/quick_actions/quick_actions/test/quick_actions_test.dart b/packages/quick_actions/quick_actions/test/quick_actions_test.dart index 09fcc9799c11..2747818ae302 100644 --- a/packages/quick_actions/quick_actions/test/quick_actions_test.dart +++ b/packages/quick_actions/quick_actions/test/quick_actions_test.dart @@ -6,9 +6,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'package:quick_actions/quick_actions.dart'; -import 'package:quick_actions_platform_interface/platform_interface/quick_actions_platform.dart'; import 'package:quick_actions_platform_interface/quick_actions_platform_interface.dart'; -import 'package:quick_actions_platform_interface/types/shortcut_item.dart'; void main() { group('$QuickActions', () { diff --git a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md index 7c86b3c80dc8..34dd631746bb 100644 --- a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.1.0 * Deprecated `SharedPreferencesWindows.instance` in favor of `SharedPreferencesStorePlatform.instance`. diff --git a/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart index 1d83eead9f25..664048ab98e4 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart @@ -2,8 +2,6 @@ // 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_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:shared_preferences_linux/shared_preferences_linux.dart'; diff --git a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md index 1f586a2a9581..0f194de44224 100644 --- a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.0.3 * Switches to an in-package method channel implementation. diff --git a/packages/shared_preferences/shared_preferences_macos/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_macos/example/integration_test/shared_preferences_test.dart index 66e3be30ee5d..874ceb4c51a7 100644 --- a/packages/shared_preferences/shared_preferences_macos/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_macos/example/integration_test/shared_preferences_test.dart @@ -2,8 +2,6 @@ // 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_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index b1ebc4b35a8e..b25956fd5919 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 6.1.0 * Introduces new `launchUrl` and `canLaunchUrl` APIs; `launch` and `canLaunch` diff --git a/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart b/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart index 1ca787f44180..fc33f05e5afb 100644 --- a/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart +++ b/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart @@ -7,8 +7,6 @@ import 'dart:async'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; -import 'types.dart'; - /// Passes [url] to the underlying platform for handling. /// /// [mode] support varies significantly by platform: 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 4594ab21bffc..e94f1847ef51 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 @@ -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 'dart:async'; import 'dart:ui'; import 'package:flutter/foundation.dart'; diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 363546c211e3..af01c64fd554 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.4.0 * Updates minimum Flutter version to 2.10. diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index 5d496a9f5e7d..f5875975cea5 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -7,7 +7,6 @@ /// An example of using the plugin, controlling lifecycle and playback of the /// video. -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:video_player/video_player.dart'; diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index 64113337a860..9eca7f921acb 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -5,10 +5,8 @@ import 'dart:async'; import 'dart:io'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:video_player/video_player.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 1f0f06f739b2..16dd52ca6da0 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.3.2 * Updates ExoPlayer to 2.17.0. diff --git a/packages/video_player/video_player_android/example/lib/main.dart b/packages/video_player/video_player_android/example/lib/main.dart index cab6eb802ca5..bca4e291efff 100644 --- a/packages/video_player/video_player_android/example/lib/main.dart +++ b/packages/video_player/video_player_android/example/lib/main.dart @@ -4,7 +4,6 @@ // ignore_for_file: public_member_api_docs -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'mini_controller.dart'; diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart index 9bb8e90b65ae..498dbffc9e84 100644 --- a/packages/video_player/video_player_android/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart @@ -8,7 +8,6 @@ import 'dart:async'; import 'dart:io'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; diff --git a/packages/video_player/video_player_android/lib/src/android_video_player.dart b/packages/video_player/video_player_android/lib/src/android_video_player.dart index 31d0744e51dc..5c5fd809c199 100644 --- a/packages/video_player/video_player_android/lib/src/android_video_player.dart +++ b/packages/video_player/video_player_android/lib/src/android_video_player.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:ui'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 3916503ce3d9..d77c36c915b6 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.3.3 * Fix XCUITest based on the new voice over announcement for tooltips. diff --git a/packages/video_player/video_player_avfoundation/example/lib/main.dart b/packages/video_player/video_player_avfoundation/example/lib/main.dart index cab6eb802ca5..bca4e291efff 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/main.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/main.dart @@ -4,7 +4,6 @@ // ignore_for_file: public_member_api_docs -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'mini_controller.dart'; diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart index 9bb8e90b65ae..498dbffc9e84 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart @@ -8,7 +8,6 @@ import 'dart:async'; import 'dart:io'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; diff --git a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart index 5dc6862c41df..b5ebedda41e1 100644 --- a/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart +++ b/packages/video_player/video_player_avfoundation/lib/src/avfoundation_video_player.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:ui'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index 9843fd81e82d..4304fd470ba2 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 5.1.2 * Adopts `Object.hash`. diff --git a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart b/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart index 097ab29c48a3..be264ca25061 100644 --- a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart +++ b/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:ui'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 3310660137a2..00788c4386fe 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.0.8 * Ensures `buffering` state is only removed when the browser reports enough data 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 eda188cb1b9f..45d90d675b83 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 @@ -5,7 +5,6 @@ import 'dart:async'; import 'dart:html' as html; -import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index f8a6e2514d7e..7a56f3f176d0 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 3.0.2 * Migrates deprecated `Scaffold.showSnackBar` to `ScaffoldMessenger` in example app. diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index 70e8179e1dd0..ba321264ee1e 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -14,10 +14,8 @@ import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; -import 'package:webview_flutter/platform_interface.dart'; import 'package:webview_flutter/webview_flutter.dart'; Future main() async { diff --git a/packages/webview_flutter/webview_flutter/lib/src/webview.dart b/packages/webview_flutter/webview_flutter/lib/src/webview.dart index 6a24d3d4cb2d..697eb487b953 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/webview.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/webview.dart @@ -7,15 +7,12 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; -import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'package:webview_flutter_android/webview_android_cookie_manager.dart'; import 'package:webview_flutter_android/webview_surface_android.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart'; -import '../platform_interface.dart'; - /// Optional callback invoked when a web view is first created. [controller] is /// the [WebViewController] for the created web view. typedef WebViewCreatedCallback = void Function(WebViewController controller); diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index c9d023987bb1..edaa0883713f 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.8.6 * Updates pigeon developer dependency to the latest version which adds support for null safety. 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 f1e95288383c..51e09912da23 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 @@ -14,7 +14,6 @@ import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:webview_flutter_android/webview_android.dart'; 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 3dd107bd557b..5d19ca71ac84 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -8,7 +8,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_driver/driver_extension.dart'; import 'package:path_provider/path_provider.dart'; diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart index 83662fb81f02..a987f1cf548d 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_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 'dart:async'; import 'dart:typed_data'; import 'package:flutter/widgets.dart'; diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index f3f612adac35..c7462ddd47d0 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 1.8.2 * Migrates from `ui.hash*` to `Object.hash*`. diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart index bb2a4ac9fb50..f32881701cfb 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'package:flutter/services.dart'; -import '../platform_interface/javascript_channel_registry.dart'; import '../platform_interface/platform_interface.dart'; import '../types/types.dart'; diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.dart index 6add51ce2d73..c1763cdae501 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.dart @@ -5,9 +5,6 @@ import 'package:flutter/widgets.dart'; import 'package:webview_flutter_platform_interface/src/types/types.dart'; -import 'auto_media_playback_policy.dart'; -import 'web_settings.dart'; - /// Configuration to use when creating a new [WebViewPlatformController]. /// /// The `autoMediaPlaybackPolicy` parameter must not be null. diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart index 5d2c717be0e5..a34262fe460a 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart @@ -8,7 +8,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; -import 'package:webview_flutter_platform_interface/src/method_channel/webview_method_channel.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; void main() { diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/platform_interface/javascript_channel_registry_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/platform_interface/javascript_channel_registry_test.dart index df1b53090fc8..30795b01c83f 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/test/src/platform_interface/javascript_channel_registry_test.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/platform_interface/javascript_channel_registry_test.dart @@ -4,7 +4,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:webview_flutter_platform_interface/src/platform_interface/javascript_channel_registry.dart'; -import 'package:webview_flutter_platform_interface/src/types/javascript_channel.dart'; import 'package:webview_flutter_platform_interface/src/types/types.dart'; void main() { diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index ba8fc0fb01c9..9f7ebe368941 100644 --- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Removes unnecessary imports. * Fixes unit tests to run on latest `master` version of Flutter. ## 0.1.0+1 diff --git a/packages/webview_flutter/webview_flutter_web/example/lib/main.dart b/packages/webview_flutter/webview_flutter_web/example/lib/main.dart index 9cf412d74e50..c183625be634 100644 --- a/packages/webview_flutter/webview_flutter_web/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_web/example/lib/main.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'dart:typed_data'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; diff --git a/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart index 787b016d2b77..8cd74f660f58 100644 --- a/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart @@ -4,7 +4,6 @@ import 'dart:async'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'package:webview_flutter_web/webview_flutter_web.dart'; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 058e8645dd7b..f042dd081475 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Removes unnecessary imports. + ## 2.7.3 * Removes two occurrences of the compiler warning: "'RequiresUserActionForMediaPlayback' is deprecated: first deprecated in ios 10.0". 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 924a6caa0f85..ceff62e3d5e8 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 @@ -14,7 +14,6 @@ import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; 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 e126de8491e5..e8b86d9c5773 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -9,7 +9,6 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index d63cbef71f9f..012cd221599b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -8,7 +8,6 @@ import 'dart:math'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; import 'package:path/path.dart' as path; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart index 5238c0bb2c56..8f23ff6b4a3d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart @@ -2,8 +2,6 @@ // 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_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 8c104bdb3ee2..e5042dab78e4 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -2,13 +2,11 @@ // 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 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; From c8a0086b91b700f79fc84f5055b70ba397ee62cd Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 2 May 2022 14:49:10 -0400 Subject: [PATCH 230/844] Roll Flutter from f7df22590adf to ea080e58ba42 (1 revision) (#5467) --- .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 7249b2760418..e0de586dc6ba 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f7df22590adf2fad70f6e83a2df73e9fa866ddfe +ea080e58ba4245036318b8967e667522bccb930c From 998ade43833f0f377270b14881136dfdba4a3b73 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 2 May 2022 15:54:12 -0400 Subject: [PATCH 231/844] Roll Flutter from ea080e58ba42 to 36be63ba19f5 (5 revisions) (#5469) --- .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 e0de586dc6ba..d37d138094f0 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ea080e58ba4245036318b8967e667522bccb930c +36be63ba19f54e0c82156b7ceb8ee89111bc6731 From 3ff23a2cb4d8023431a49c0f1becf91a35ea7207 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 2 May 2022 16:59:12 -0400 Subject: [PATCH 232/844] Roll Flutter from 36be63ba19f5 to bd2ca58db388 (5 revisions) (#5470) --- .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 d37d138094f0..0fb69462cc90 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -36be63ba19f54e0c82156b7ceb8ee89111bc6731 +bd2ca58db388bcdc0593fef9422f48f9b1ea6445 From 698c82db93cd75e4694c64fa519927ec3c3bf414 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 2 May 2022 17:44:12 -0400 Subject: [PATCH 233/844] [flutter_plugin_tools] Support non-plugin packages for `drive-examples` (#5468) --- script/tool/CHANGELOG.md | 5 + script/tool/lib/src/common/core.dart | 23 +-- .../src/common/package_looping_command.dart | 1 - script/tool/lib/src/common/plugin_utils.dart | 10 +- .../lib/src/common/repository_package.dart | 14 +- .../tool/lib/src/drive_examples_command.dart | 68 ++++++-- .../src/federation_safety_check_command.dart | 1 - .../lib/src/make_deps_path_based_command.dart | 3 +- .../tool/lib/src/publish_check_command.dart | 1 - .../tool/lib/src/publish_plugin_command.dart | 1 - .../tool/lib/src/pubspec_check_command.dart | 1 - script/tool/lib/src/readme_check_command.dart | 1 - script/tool/lib/src/test_command.dart | 2 +- .../tool/lib/src/version_check_command.dart | 1 - .../test/common/repository_package_test.dart | 43 ++++- .../create_all_plugins_app_command_test.dart | 1 - .../test/drive_examples_command_test.dart | 161 ++++++++++++++++++ script/tool/test/list_command_test.dart | 2 +- .../tool/test/pubspec_check_command_test.dart | 115 +++++++++---- script/tool/test/util.dart | 56 +++--- 20 files changed, 398 insertions(+), 112 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 1bce029a559f..2ce644178fa6 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,8 @@ +## NEXT + +- `drive-examples` now supports non-plugin packages. +- Commands that iterate over examples now include non-Flutter example packages. + ## 0.8.4 - `readme-check` now validates that there's a info tag on code blocks to diff --git a/script/tool/lib/src/common/core.dart b/script/tool/lib/src/common/core.dart index de1cefd7225a..13678d720a76 100644 --- a/script/tool/lib/src/common/core.dart +++ b/script/tool/lib/src/common/core.dart @@ -4,7 +4,6 @@ import 'package:colorize/colorize.dart'; import 'package:file/file.dart'; -import 'package:yaml/yaml.dart'; /// The signature for a print handler for commands that allow overriding the /// print destination. @@ -31,26 +30,14 @@ const String platformWindows = 'windows'; /// Key for enable experiment. const String kEnableExperiment = 'enable-experiment'; -/// Returns whether the given directory contains a Flutter package. -bool isFlutterPackage(FileSystemEntity entity) { +/// Returns whether the given directory is a Dart package. +bool isPackage(FileSystemEntity entity) { if (entity is! Directory) { return false; } - - try { - final File pubspecFile = entity.childFile('pubspec.yaml'); - final YamlMap pubspecYaml = - loadYaml(pubspecFile.readAsStringSync()) as YamlMap; - final YamlMap? dependencies = pubspecYaml['dependencies'] as YamlMap?; - if (dependencies == null) { - return false; - } - return dependencies.containsKey('flutter'); - } on FileSystemException { - return false; - } on YamlException { - return false; - } + // Per https://dart.dev/guides/libraries/create-library-packages#what-makes-a-library-package + return entity.childFile('pubspec.yaml').existsSync() && + entity.childDirectory('lib').existsSync(); } /// Prints `successMessage` in green. diff --git a/script/tool/lib/src/common/package_looping_command.dart b/script/tool/lib/src/common/package_looping_command.dart index b75aaa4a4a49..b48743be3170 100644 --- a/script/tool/lib/src/common/package_looping_command.dart +++ b/script/tool/lib/src/common/package_looping_command.dart @@ -10,7 +10,6 @@ import 'package:git/git.dart'; import 'package:path/path.dart' as p; import 'package:platform/platform.dart'; import 'package:pub_semver/pub_semver.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'core.dart'; import 'plugin_command.dart'; diff --git a/script/tool/lib/src/common/plugin_utils.dart b/script/tool/lib/src/common/plugin_utils.dart index 94f294ebd964..f33d3d73bb75 100644 --- a/script/tool/lib/src/common/plugin_utils.dart +++ b/script/tool/lib/src/common/plugin_utils.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:file/file.dart'; import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:yaml/yaml.dart'; @@ -111,13 +110,8 @@ YamlMap? _readPlatformPubspecSectionForPlugin( /// section from [plugin]'s pubspec.yaml, or null if either it is not present, /// or the pubspec couldn't be read. YamlMap? _readPluginPubspecSection(RepositoryPackage package) { - final File pubspecFile = package.pubspecFile; - if (!pubspecFile.existsSync()) { - return null; - } - final YamlMap pubspecYaml = - loadYaml(pubspecFile.readAsStringSync()) as YamlMap; - final YamlMap? flutterSection = pubspecYaml['flutter'] as YamlMap?; + final Pubspec pubspec = package.parsePubspec(); + final Map? flutterSection = pubspec.flutter; if (flutterSection == null) { return null; } diff --git a/script/tool/lib/src/common/repository_package.dart b/script/tool/lib/src/common/repository_package.dart index 72e7f948fe9e..76519e040ae1 100644 --- a/script/tool/lib/src/common/repository_package.dart +++ b/script/tool/lib/src/common/repository_package.dart @@ -8,6 +8,8 @@ import 'package:pubspec_parse/pubspec_parse.dart'; import 'core.dart'; +export 'package:pubspec_parse/pubspec_parse.dart' show Pubspec; + /// A package in the repository. // // TODO(stuartmorgan): Add more package-related info here, such as an on-demand @@ -59,6 +61,12 @@ class RepositoryPackage { /// Caches for future use. Pubspec parsePubspec() => _parsedPubspec; + /// Returns true if the package depends on Flutter. + bool requiresFlutter() { + final Pubspec pubspec = parsePubspec(); + return pubspec.dependencies.containsKey('flutter'); + } + /// True if this appears to be a federated plugin package, according to /// repository conventions. bool get isFederated => @@ -91,7 +99,7 @@ class RepositoryPackage { if (!exampleDirectory.existsSync()) { return []; } - if (isFlutterPackage(exampleDirectory)) { + if (isPackage(exampleDirectory)) { return [RepositoryPackage(exampleDirectory)]; } // Only look at the subdirectories of the example directory if the example @@ -99,8 +107,8 @@ class RepositoryPackage { // example directory for other Dart packages. return exampleDirectory .listSync() - .where((FileSystemEntity entity) => isFlutterPackage(entity)) - // isFlutterPackage guarantees that the cast to Directory is safe. + .where((FileSystemEntity entity) => isPackage(entity)) + // isPackage guarantees that the cast to Directory is safe. .map((FileSystemEntity entity) => RepositoryPackage(entity as Directory)); } diff --git a/script/tool/lib/src/drive_examples_command.dart b/script/tool/lib/src/drive_examples_command.dart index 0e1efa842eda..68ad9713a9b7 100644 --- a/script/tool/lib/src/drive_examples_command.dart +++ b/script/tool/lib/src/drive_examples_command.dart @@ -123,6 +123,8 @@ class DriveExamplesCommand extends PackageLoopingCommand { @override Future runForPackage(RepositoryPackage package) async { + final bool isPlugin = isFlutterPlugin(package); + if (package.isPlatformInterface && !package.getSingleExampleDeprecated().directory.existsSync()) { // Platform interface packages generally aren't intended to have @@ -131,23 +133,23 @@ class DriveExamplesCommand extends PackageLoopingCommand { 'Platform interfaces are not expected to have integration tests.'); } - final List deviceFlags = []; - for (final MapEntry> entry - in _targetDeviceFlags.entries) { - final String platform = entry.key; - if (pluginSupportsPlatform(platform, package)) { - deviceFlags.addAll(entry.value); - } else { - print('Skipping unsupported platform ${entry.key}...'); + // For plugin packages, skip if the plugin itself doesn't support any + // requested platform(s). + if (isPlugin) { + final Iterable requestedPlatforms = _targetDeviceFlags.keys; + final Iterable unsupportedPlatforms = requestedPlatforms.where( + (String platform) => !pluginSupportsPlatform(platform, package)); + for (final String platform in unsupportedPlatforms) { + print('Skipping unsupported platform $platform...'); + } + if (unsupportedPlatforms.length == requestedPlatforms.length) { + return PackageResult.skip( + '${package.displayName} does not support any requested platform.'); } - } - // If there is no supported target platform, skip the plugin. - if (deviceFlags.isEmpty) { - return PackageResult.skip( - '${package.displayName} does not support any requested platform.'); } int examplesFound = 0; + int supportedExamplesFound = 0; bool testsRan = false; final List errors = []; for (final RepositoryPackage example in package.getExamples()) { @@ -155,6 +157,15 @@ class DriveExamplesCommand extends PackageLoopingCommand { final String exampleName = getRelativePosixPath(example.directory, from: packagesDir); + // Skip examples that don't support any requested platform(s). + final List deviceFlags = _deviceFlagsForExample(example); + if (deviceFlags.isEmpty) { + print( + 'Skipping $exampleName; does not support any requested platforms.'); + continue; + } + ++supportedExamplesFound; + final List drivers = await _getDrivers(example); if (drivers.isEmpty) { print('No driver tests found for $exampleName'); @@ -195,14 +206,41 @@ class DriveExamplesCommand extends PackageLoopingCommand { } } if (!testsRan) { - printError('No driver tests were run ($examplesFound example(s) found).'); - errors.add('No tests ran (use --exclude if this is intentional).'); + // It is an error for a plugin not to have integration tests, because that + // is the only way to test the method channel communication. + if (isPlugin) { + printError( + 'No driver tests were run ($examplesFound example(s) found).'); + errors.add('No tests ran (use --exclude if this is intentional).'); + } else { + return PackageResult.skip(supportedExamplesFound == 0 + ? 'No example supports requested platform(s).' + : 'No example is configured for driver tests.'); + } } return errors.isEmpty ? PackageResult.success() : PackageResult.fail(errors); } + /// Returns the device flags for the intersection of the requested platforms + /// and the platforms supported by [example]. + List _deviceFlagsForExample(RepositoryPackage example) { + final List deviceFlags = []; + for (final MapEntry> entry + in _targetDeviceFlags.entries) { + final String platform = entry.key; + if (example.directory.childDirectory(platform).existsSync()) { + deviceFlags.addAll(entry.value); + } else { + final String exampleName = + getRelativePosixPath(example.directory, from: packagesDir); + print('Skipping unsupported platform $platform for $exampleName'); + } + } + return deviceFlags; + } + Future> _getDevicesForPlatform(String platform) async { final List deviceIds = []; diff --git a/script/tool/lib/src/federation_safety_check_command.dart b/script/tool/lib/src/federation_safety_check_command.dart index df9d86892e13..383637a9e896 100644 --- a/script/tool/lib/src/federation_safety_check_command.dart +++ b/script/tool/lib/src/federation_safety_check_command.dart @@ -8,7 +8,6 @@ import 'package:git/git.dart'; import 'package:path/path.dart' as p; import 'package:platform/platform.dart'; import 'package:pub_semver/pub_semver.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'common/core.dart'; import 'common/file_utils.dart'; diff --git a/script/tool/lib/src/make_deps_path_based_command.dart b/script/tool/lib/src/make_deps_path_based_command.dart index 45a4427d3217..370fc3559f71 100644 --- a/script/tool/lib/src/make_deps_path_based_command.dart +++ b/script/tool/lib/src/make_deps_path_based_command.dart @@ -3,15 +3,14 @@ // found in the LICENSE file. import 'package:file/file.dart'; -import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:git/git.dart'; import 'package:path/path.dart' as p; import 'package:pub_semver/pub_semver.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'common/core.dart'; import 'common/git_version_finder.dart'; import 'common/plugin_command.dart'; +import 'common/repository_package.dart'; const int _exitPackageNotFound = 3; const int _exitCannotUpdatePubspec = 4; diff --git a/script/tool/lib/src/publish_check_command.dart b/script/tool/lib/src/publish_check_command.dart index 8fd96b818c1d..b6b83dabcb49 100644 --- a/script/tool/lib/src/publish_check_command.dart +++ b/script/tool/lib/src/publish_check_command.dart @@ -10,7 +10,6 @@ import 'package:file/file.dart'; import 'package:http/http.dart' as http; import 'package:platform/platform.dart'; import 'package:pub_semver/pub_semver.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'common/core.dart'; import 'common/package_looping_command.dart'; diff --git a/script/tool/lib/src/publish_plugin_command.dart b/script/tool/lib/src/publish_plugin_command.dart index 28d17a3a2487..05f0afd0c06f 100644 --- a/script/tool/lib/src/publish_plugin_command.dart +++ b/script/tool/lib/src/publish_plugin_command.dart @@ -13,7 +13,6 @@ import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:platform/platform.dart'; import 'package:pub_semver/pub_semver.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:yaml/yaml.dart'; import 'common/core.dart'; diff --git a/script/tool/lib/src/pubspec_check_command.dart b/script/tool/lib/src/pubspec_check_command.dart index 2c27c91e0490..654675ebb858 100644 --- a/script/tool/lib/src/pubspec_check_command.dart +++ b/script/tool/lib/src/pubspec_check_command.dart @@ -5,7 +5,6 @@ import 'package:file/file.dart'; import 'package:git/git.dart'; import 'package:platform/platform.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:yaml/yaml.dart'; import 'common/core.dart'; diff --git a/script/tool/lib/src/readme_check_command.dart b/script/tool/lib/src/readme_check_command.dart index 9432c4b7fa40..0cb64920dea4 100644 --- a/script/tool/lib/src/readme_check_command.dart +++ b/script/tool/lib/src/readme_check_command.dart @@ -5,7 +5,6 @@ import 'package:file/file.dart'; import 'package:git/git.dart'; import 'package:platform/platform.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:yaml/yaml.dart'; import 'common/core.dart'; diff --git a/script/tool/lib/src/test_command.dart b/script/tool/lib/src/test_command.dart index 2c5dd9934b45..d115b6557adc 100644 --- a/script/tool/lib/src/test_command.dart +++ b/script/tool/lib/src/test_command.dart @@ -43,7 +43,7 @@ class TestCommand extends PackageLoopingCommand { } bool passed; - if (isFlutterPackage(package.directory)) { + if (package.requiresFlutter()) { passed = await _runFlutterTests(package); } else { passed = await _runDartTests(package); diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart index fcaea335920f..f69611f5a06d 100644 --- a/script/tool/lib/src/version_check_command.dart +++ b/script/tool/lib/src/version_check_command.dart @@ -9,7 +9,6 @@ import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:platform/platform.dart'; import 'package:pub_semver/pub_semver.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'common/core.dart'; import 'common/git_version_finder.dart'; diff --git a/script/tool/test/common/repository_package_test.dart b/script/tool/test/common/repository_package_test.dart index 0a8ea36eb905..2d0e11475c36 100644 --- a/script/tool/test/common/repository_package_test.dart +++ b/script/tool/test/common/repository_package_test.dart @@ -5,7 +5,6 @@ import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_plugin_tools/src/common/repository_package.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:test/test.dart'; import '../util.dart'; @@ -97,7 +96,7 @@ void main() { }); group('getExamples', () { - test('handles a single example', () async { + test('handles a single Flutter example', () async { final Directory plugin = createFakePlugin('a_plugin', packagesDir); final List examples = @@ -107,7 +106,7 @@ void main() { expect(examples[0].path, plugin.childDirectory('example').path); }); - test('handles multiple examples', () async { + test('handles multiple Flutter examples', () async { final Directory plugin = createFakePlugin('a_plugin', packagesDir, examples: ['example1', 'example2']); @@ -120,6 +119,30 @@ void main() { expect(examples[1].path, plugin.childDirectory('example').childDirectory('example2').path); }); + + test('handles a single non-Flutter example', () async { + final Directory package = createFakePackage('a_package', packagesDir); + + final List examples = + RepositoryPackage(package).getExamples().toList(); + + expect(examples.length, 1); + expect(examples[0].path, package.childDirectory('example').path); + }); + + test('handles multiple non-Flutter examples', () async { + final Directory package = createFakePackage('a_package', packagesDir, + examples: ['example1', 'example2']); + + final List examples = + RepositoryPackage(package).getExamples().toList(); + + expect(examples.length, 2); + expect(examples[0].path, + package.childDirectory('example').childDirectory('example1').path); + expect(examples[1].path, + package.childDirectory('example').childDirectory('example2').path); + }); }); group('federated plugin queries', () { @@ -179,4 +202,18 @@ void main() { expect(pubspec.name, 'a_plugin'); }); }); + + group('requiresFlutter', () { + test('returns true for Flutter package', () async { + final Directory package = + createFakePackage('a_package', packagesDir, isFlutter: true); + expect(RepositoryPackage(package).requiresFlutter(), true); + }); + + test('returns false for non-Flutter package', () async { + final Directory package = + createFakePackage('a_package', packagesDir, isFlutter: false); + expect(RepositoryPackage(package).requiresFlutter(), false); + }); + }); } diff --git a/script/tool/test/create_all_plugins_app_command_test.dart b/script/tool/test/create_all_plugins_app_command_test.dart index 917adca020d4..9e2ee29326d7 100644 --- a/script/tool/test/create_all_plugins_app_command_test.dart +++ b/script/tool/test/create_all_plugins_app_command_test.dart @@ -10,7 +10,6 @@ import 'package:file/local.dart'; import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:flutter_plugin_tools/src/create_all_plugins_app_command.dart'; import 'package:platform/platform.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:test/test.dart'; import 'util.dart'; diff --git a/script/tool/test/drive_examples_command_test.dart b/script/tool/test/drive_examples_command_test.dart index ac57eb7251a2..214efb420227 100644 --- a/script/tool/test/drive_examples_command_test.dart +++ b/script/tool/test/drive_examples_command_test.dart @@ -128,6 +128,7 @@ void main() { extraFiles: [ 'example/test_driver/integration_test.dart', 'example/integration_test/foo_test.dart', + 'example/ios/ios.m', ], platformSupport: { platformIOS: const PlatformDetails(PlatformSupport.inline), @@ -193,6 +194,8 @@ void main() { extraFiles: [ 'example/test_driver/plugin_test.dart', 'example/test_driver/plugin.dart', + 'example/android/android.java', + 'example/ios/ios.m', ], platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), @@ -243,6 +246,8 @@ void main() { packagesDir, extraFiles: [ 'example/test_driver/plugin_test.dart', + 'example/android/android.java', + 'example/ios/ios.m', ], platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), @@ -276,6 +281,8 @@ void main() { packagesDir, extraFiles: [ 'example/lib/main.dart', + 'example/android/android.java', + 'example/ios/ios.m', ], platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), @@ -312,6 +319,8 @@ void main() { 'example/integration_test/bar_test.dart', 'example/integration_test/foo_test.dart', 'example/integration_test/ignore_me.dart', + 'example/android/android.java', + 'example/ios/ios.m', ], platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), @@ -398,6 +407,7 @@ void main() { extraFiles: [ 'example/test_driver/plugin_test.dart', 'example/test_driver/plugin.dart', + 'example/linux/linux.cc', ], platformSupport: { platformLinux: const PlatformDetails(PlatformSupport.inline), @@ -542,6 +552,7 @@ void main() { extraFiles: [ 'example/test_driver/plugin_test.dart', 'example/test_driver/plugin.dart', + 'example/web/index.html', ], platformSupport: { platformWeb: const PlatformDetails(PlatformSupport.inline), @@ -591,6 +602,7 @@ void main() { extraFiles: [ 'example/test_driver/plugin_test.dart', 'example/test_driver/plugin.dart', + 'example/web/index.html', ], platformSupport: { platformWeb: const PlatformDetails(PlatformSupport.inline), @@ -668,6 +680,7 @@ void main() { extraFiles: [ 'example/test_driver/plugin_test.dart', 'example/test_driver/plugin.dart', + 'example/windows/windows.cpp', ], platformSupport: { platformWindows: const PlatformDetails(PlatformSupport.inline), @@ -715,6 +728,7 @@ void main() { extraFiles: [ 'example/test_driver/plugin_test.dart', 'example/test_driver/plugin.dart', + 'example/android/android.java', ], platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), @@ -853,6 +867,8 @@ void main() { extraFiles: [ 'example/test_driver/plugin_test.dart', 'example/test_driver/plugin.dart', + 'example/android/android.java', + 'example/ios/ios.m', ], platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), @@ -927,6 +943,7 @@ void main() { extraFiles: [ 'example/integration_test/bar_test.dart', 'example/integration_test/foo_test.dart', + 'example/web/index.html', ], platformSupport: { platformWeb: const PlatformDetails(PlatformSupport.inline), @@ -959,6 +976,7 @@ void main() { packagesDir, extraFiles: [ 'example/test_driver/integration_test.dart', + 'example/web/index.html', ], platformSupport: { platformWeb: const PlatformDetails(PlatformSupport.inline), @@ -995,6 +1013,7 @@ void main() { 'example/test_driver/integration_test.dart', 'example/integration_test/bar_test.dart', 'example/integration_test/foo_test.dart', + 'example/macos/macos.swift', ], platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), @@ -1060,5 +1079,147 @@ void main() { pluginExampleDirectory.path), ])); }); + + group('packages', () { + test('can be driven', () async { + final Directory package = + createFakePackage('a_package', packagesDir, extraFiles: [ + 'example/integration_test/foo_test.dart', + 'example/test_driver/integration_test.dart', + 'example/web/index.html', + ]); + final Directory exampleDirectory = package.childDirectory('example'); + + final List output = await runCapturingPrint(runner, [ + 'drive-examples', + '--web', + ]); + + expect( + output, + containsAllInOrder([ + contains('Running for a_package'), + contains('No issues found!'), + ]), + ); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall( + getFlutterCommand(mockPlatform), + const [ + 'drive', + '-d', + 'web-server', + '--web-port=7357', + '--browser-name=chrome', + '--driver', + 'test_driver/integration_test.dart', + '--target', + 'integration_test/foo_test.dart' + ], + exampleDirectory.path), + ])); + }); + + test('are skipped when example does not support platform', () async { + createFakePackage('a_package', packagesDir, + isFlutter: true, + extraFiles: [ + 'example/integration_test/foo_test.dart', + 'example/test_driver/integration_test.dart', + ]); + + final List output = await runCapturingPrint(runner, [ + 'drive-examples', + '--web', + ]); + + expect( + output, + containsAllInOrder([ + contains('Running for a_package'), + contains('Skipping a_package/example; does not support any ' + 'requested platforms'), + contains('SKIPPING: No example supports requested platform(s).'), + ]), + ); + + expect(processRunner.recordedCalls.isEmpty, true); + }); + + test('drive only supported examples if there is more than one', () async { + final Directory package = createFakePackage('a_package', packagesDir, + isFlutter: true, + examples: [ + 'with_web', + 'without_web' + ], + extraFiles: [ + 'example/with_web/integration_test/foo_test.dart', + 'example/with_web/test_driver/integration_test.dart', + 'example/with_web/web/index.html', + 'example/without_web/integration_test/foo_test.dart', + 'example/without_web/test_driver/integration_test.dart', + ]); + final Directory supportedExampleDirectory = + package.childDirectory('example').childDirectory('with_web'); + + final List output = await runCapturingPrint(runner, [ + 'drive-examples', + '--web', + ]); + + expect( + output, + containsAllInOrder([ + contains('Running for a_package'), + contains( + 'Skipping a_package/example/without_web; does not support any requested platforms.'), + contains('No issues found!'), + ]), + ); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall( + getFlutterCommand(mockPlatform), + const [ + 'drive', + '-d', + 'web-server', + '--web-port=7357', + '--browser-name=chrome', + '--driver', + 'test_driver/integration_test.dart', + '--target', + 'integration_test/foo_test.dart' + ], + supportedExampleDirectory.path), + ])); + }); + + test('are skipped when there is no integration testing', () async { + createFakePackage('a_package', packagesDir, + isFlutter: true, extraFiles: ['example/web/index.html']); + + final List output = await runCapturingPrint(runner, [ + 'drive-examples', + '--web', + ]); + + expect( + output, + containsAllInOrder([ + contains('Running for a_package'), + contains('SKIPPING: No example is configured for driver tests.'), + ]), + ); + + expect(processRunner.recordedCalls.isEmpty, true); + }); + }); }); } diff --git a/script/tool/test/list_command_test.dart b/script/tool/test/list_command_test.dart index fcdf9fafdb63..9e70f72e7483 100644 --- a/script/tool/test/list_command_test.dart +++ b/script/tool/test/list_command_test.dart @@ -12,7 +12,7 @@ import 'mocks.dart'; import 'util.dart'; void main() { - group('$ListCommand', () { + group('ListCommand', () { late FileSystem fileSystem; late MockPlatform mockPlatform; late Directory packagesDir; diff --git a/script/tool/test/pubspec_check_command_test.dart b/script/tool/test/pubspec_check_command_test.dart index 42d20240da87..30b6ab6004e7 100644 --- a/script/tool/test/pubspec_check_command_test.dart +++ b/script/tool/test/pubspec_check_command_test.dart @@ -155,6 +155,21 @@ ${_flutterSection(isPlugin: true)} ${_dependenciesSection()} ${_devDependenciesSection()} ${_falseSecretsSection()} +'''); + + pluginDirectory + .childDirectory('example') + .childFile('pubspec.yaml') + .writeAsStringSync(''' +${_headerSection( + 'plugin_example', + publishable: false, + includeRepository: false, + includeIssueTracker: false, + )} +${_environmentSection()} +${_dependenciesSection()} +${_flutterSection()} '''); final List output = await runCapturingPrint(runner, [ @@ -172,15 +187,31 @@ ${_falseSecretsSection()} }); test('passes for a Flutter package following conventions', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory packageDirectory = + createFakePackage('a_package', packagesDir); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' -${_headerSection('plugin')} + packageDirectory.childFile('pubspec.yaml').writeAsStringSync(''' +${_headerSection('a_package')} ${_environmentSection()} ${_dependenciesSection()} ${_devDependenciesSection()} ${_flutterSection()} ${_falseSecretsSection()} +'''); + + packageDirectory + .childDirectory('example') + .childFile('pubspec.yaml') + .writeAsStringSync(''' +${_headerSection( + 'a_package', + publishable: false, + includeRepository: false, + includeIssueTracker: false, + )} +${_environmentSection()} +${_dependenciesSection()} +${_flutterSection()} '''); final List output = await runCapturingPrint(runner, [ @@ -190,8 +221,8 @@ ${_falseSecretsSection()} expect( output, containsAllInOrder([ - contains('Running for plugin...'), - contains('Running for plugin/example...'), + contains('Running for a_package...'), + contains('Running for a_package/example...'), contains('No issues found!'), ]), ); @@ -221,7 +252,8 @@ ${_dependenciesSection()} }); test('fails when homepage is included', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, includeHomepage: true)} @@ -248,7 +280,8 @@ ${_devDependenciesSection()} }); test('fails when repository is missing', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, includeRepository: false)} @@ -274,7 +307,8 @@ ${_devDependenciesSection()} }); test('fails when homepage is given instead of repository', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, includeHomepage: true, includeRepository: false)} @@ -301,7 +335,8 @@ ${_devDependenciesSection()} }); test('fails when repository is incorrect', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, repositoryPackagesDirRelativePath: 'different_plugin')} @@ -327,7 +362,8 @@ ${_devDependenciesSection()} }); test('fails when issue tracker is missing', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, includeIssueTracker: false)} @@ -353,8 +389,9 @@ ${_devDependenciesSection()} }); test('fails when description is too short', () async { - final Directory pluginDirectory = - createFakePlugin('a_plugin', packagesDir.childDirectory('a_plugin')); + final Directory pluginDirectory = createFakePlugin( + 'a_plugin', packagesDir.childDirectory('a_plugin'), + examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, description: 'Too short')} @@ -383,7 +420,8 @@ ${_devDependenciesSection()} test( 'allows short descriptions for non-app-facing parts of federated plugins', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, description: 'Too short')} @@ -410,7 +448,8 @@ ${_devDependenciesSection()} }); test('fails when description is too long', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); const String description = 'This description is too long. It just goes ' 'on and on and on and on and on. pub.dev will down-score it because ' @@ -442,7 +481,8 @@ ${_devDependenciesSection()} }); test('fails when environment section is out of order', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} @@ -469,7 +509,8 @@ ${_environmentSection()} }); test('fails when flutter section is out of order', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} @@ -496,7 +537,8 @@ ${_devDependenciesSection()} }); test('fails when dependencies section is out of order', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} @@ -550,7 +592,8 @@ ${_dependenciesSection()} }); test('fails when false_secrets section is out of order', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} @@ -580,7 +623,8 @@ ${_devDependenciesSection()} test('fails when an implemenation package is missing "implements"', () async { final Directory pluginDirectory = createFakePlugin( - 'plugin_a_foo', packagesDir.childDirectory('plugin_a')); + 'plugin_a_foo', packagesDir.childDirectory('plugin_a'), + examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin_a_foo', isPlugin: true)} @@ -608,7 +652,8 @@ ${_devDependenciesSection()} test('fails when an implemenation package has the wrong "implements"', () async { final Directory pluginDirectory = createFakePlugin( - 'plugin_a_foo', packagesDir.childDirectory('plugin_a')); + 'plugin_a_foo', packagesDir.childDirectory('plugin_a'), + examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('plugin_a_foo', isPlugin: true)} @@ -636,7 +681,8 @@ ${_devDependenciesSection()} test('passes for a correct implemenation package', () async { final Directory pluginDirectory = createFakePlugin( - 'plugin_a_foo', packagesDir.childDirectory('plugin_a')); + 'plugin_a_foo', packagesDir.childDirectory('plugin_a'), + examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection( @@ -663,8 +709,9 @@ ${_devDependenciesSection()} }); test('fails when a "default_package" looks incorrect', () async { - final Directory pluginDirectory = - createFakePlugin('plugin_a', packagesDir.childDirectory('plugin_a')); + final Directory pluginDirectory = createFakePlugin( + 'plugin_a', packagesDir.childDirectory('plugin_a'), + examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection( @@ -702,8 +749,9 @@ ${_devDependenciesSection()} test( 'fails when a "default_package" does not have a corresponding dependency', () async { - final Directory pluginDirectory = - createFakePlugin('plugin_a', packagesDir.childDirectory('plugin_a')); + final Directory pluginDirectory = createFakePlugin( + 'plugin_a', packagesDir.childDirectory('plugin_a'), + examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection( @@ -739,8 +787,9 @@ ${_devDependenciesSection()} }); test('passes for an app-facing package without "implements"', () async { - final Directory pluginDirectory = - createFakePlugin('plugin_a', packagesDir.childDirectory('plugin_a')); + final Directory pluginDirectory = createFakePlugin( + 'plugin_a', packagesDir.childDirectory('plugin_a'), + examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection( @@ -769,8 +818,8 @@ ${_devDependenciesSection()} test('passes for a platform interface package without "implements"', () async { final Directory pluginDirectory = createFakePlugin( - 'plugin_a_platform_interface', - packagesDir.childDirectory('plugin_a')); + 'plugin_a_platform_interface', packagesDir.childDirectory('plugin_a'), + examples: []); pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection( @@ -799,7 +848,8 @@ ${_devDependenciesSection()} test('validates some properties even for unpublished packages', () async { final Directory pluginDirectory = createFakePlugin( - 'plugin_a_foo', packagesDir.childDirectory('plugin_a')); + 'plugin_a_foo', packagesDir.childDirectory('plugin_a'), + examples: []); // Environment section is in the wrong location. // Missing 'implements'. @@ -829,7 +879,8 @@ ${_environmentSection()} }); test('ignores some checks for unpublished packages', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final Directory pluginDirectory = + createFakePlugin('plugin', packagesDir, examples: []); // Missing metadata that is only useful for published packages, such as // repository and issue tracker. @@ -886,7 +937,7 @@ ${_devDependenciesSection()} test('repository check works', () async { final Directory packageDirectory = - createFakePackage('package', packagesDir); + createFakePackage('package', packagesDir, examples: []); packageDirectory.childFile('pubspec.yaml').writeAsStringSync(''' ${_headerSection('package')} diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index 2b1719ee8000..8a2bf099cc8a 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -107,6 +107,12 @@ Directory createFakePlugin( /// /// [extraFiles] is an optional list of package-relative paths, using unix-style /// separators, of extra files to create in the package. +/// +/// If [includeCommonFiles] is true, common but non-critical files like +/// CHANGELOG.md and AUTHORS will be included. +/// +/// If non-null, [directoryName] will be used for the directory instead of +/// [name]. // TODO(stuartmorgan): Convert the return to a RepositoryPackage. Directory createFakePackage( String name, @@ -116,37 +122,43 @@ Directory createFakePackage( bool isFlutter = false, String? version = '0.0.1', String flutterConstraint = '>=2.5.0', + bool includeCommonFiles = true, + String? directoryName, + String? publishTo, }) { - final Directory packageDirectory = parentDirectory.childDirectory(name); + final Directory packageDirectory = + parentDirectory.childDirectory(directoryName ?? name); packageDirectory.createSync(recursive: true); + packageDirectory.childDirectory('lib').createSync(); createFakePubspec(packageDirectory, name: name, isFlutter: isFlutter, version: version, flutterConstraint: flutterConstraint); - createFakeCHANGELOG(packageDirectory, ''' + if (includeCommonFiles) { + createFakeCHANGELOG(packageDirectory, ''' ## $version * Some changes. '''); - createFakeAuthors(packageDirectory); + createFakeAuthors(packageDirectory); + } if (examples.length == 1) { - final Directory exampleDir = packageDirectory.childDirectory(examples.first) - ..createSync(); - createFakePubspec(exampleDir, - name: '${name}_example', + createFakePackage('${name}_example', packageDirectory, + directoryName: examples.first, + examples: [], + includeCommonFiles: false, isFlutter: isFlutter, publishTo: 'none', flutterConstraint: flutterConstraint); } else if (examples.isNotEmpty) { - final Directory exampleDir = packageDirectory.childDirectory('example') - ..createSync(); - for (final String example in examples) { - final Directory currentExample = exampleDir.childDirectory(example) - ..createSync(); - createFakePubspec(currentExample, - name: example, + final Directory examplesDirectory = + packageDirectory.childDirectory('example')..createSync(); + for (final String exampleName in examples) { + createFakePackage(exampleName, examplesDirectory, + examples: [], + includeCommonFiles: false, isFlutter: isFlutter, publishTo: 'none', flutterConstraint: flutterConstraint); @@ -179,7 +191,7 @@ void createFakePubspec( bool isPlugin = false, Map platformSupport = const {}, - String publishTo = 'http://no_pub_server.com', + String? publishTo, String? version, String dartConstraint = '>=2.0.0 <3.0.0', String flutterConstraint = '>=2.5.0', @@ -219,9 +231,16 @@ flutter: } } - String yaml = ''' + // Default to a fake server to avoid ever accidentally publishing something + // from a test. Does not use 'none' since that changes the behavior of some + // commands. + final String publishToSection = + 'publish_to: ${publishTo ?? 'http://no_pub_server.com'}'; + + final String yaml = ''' name: $name ${(version != null) ? 'version: $version' : ''} +$publishToSection $environmentSection @@ -230,11 +249,6 @@ $dependenciesSection $pluginSection '''; - if (publishTo.isNotEmpty) { - yaml += ''' -publish_to: $publishTo # Hardcoded safeguard to prevent this from somehow being published by a broken test. -'''; - } parent.childFile('pubspec.yaml').createSync(); parent.childFile('pubspec.yaml').writeAsStringSync(yaml); } From bee239586eea46f3afeeeefa79b7de1b2196c2f4 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 2 May 2022 18:24:08 -0400 Subject: [PATCH 234/844] Roll Flutter from bd2ca58db388 to 7965ee25dd63 (2 revisions) (#5471) --- .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 0fb69462cc90..a750a5ec244f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -bd2ca58db388bcdc0593fef9422f48f9b1ea6445 +7965ee25dd632d907f69e5ea6f4f81f35c0cf6a7 From cd89420e390ea04108d345566dac8065cf82046b Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Mon, 2 May 2022 18:04:13 -0700 Subject: [PATCH 235/844] Add dependabot for Gradle dependencies (#5440) --- .github/dependabot.yml | 289 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..92e313d21441 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,289 @@ +version: 2 +updates: + - package-ecosystem: "gradle" + directory: "/packages/camera/camera/android" + commit-message: + prefix: "[camera]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/camera/camera/example/android/app" + commit-message: + prefix: "[camera]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/espresso/android" + commit-message: + prefix: "[espresso]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/espresso/example/android/app" + commit-message: + prefix: "[espresso]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/flutter_plugin_android_lifecycle/android" + commit-message: + prefix: "[flutter_plugin_android_lifecycle]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/flutter_plugin_android_lifecycle/example/android/app" + commit-message: + prefix: "[flutter_plugin_android_lifecycle]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/google_maps_flutter/google_maps_flutter/android" + commit-message: + prefix: "[google_maps_flutter]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/google_maps_flutter/google_maps_flutter/example/android/app" + commit-message: + prefix: "[google_maps_flutter]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/google_sign_in/google_sign_in/example/android/app" + commit-message: + prefix: "[google_sign_in]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/google_sign_in/google_sign_in_android/android" + commit-message: + prefix: "[google_sign_in_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/google_sign_in/google_sign_in_android/example/android/app" + commit-message: + prefix: "[google_sign_in_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/in_app_purchase/in_app_purchase_android/android" + commit-message: + prefix: "[in_app_purchase_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/in_app_purchase/in_app_purchase_android/example/android/app" + commit-message: + prefix: "[in_app_purchase_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/in_app_purchase/in_app_purchase/example/android/app" + commit-message: + prefix: "[in_app_purchase]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/image_picker/image_picker/example/android/app" + commit-message: + prefix: "[image_picker]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/image_picker/image_picker_android/android" + commit-message: + prefix: "[image_picker_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/image_picker/image_picker_android/example/android/app" + commit-message: + prefix: "[image_picker_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/local_auth/local_auth_android/android" + commit-message: + prefix: "[local_auth_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/local_auth/local_auth_android/example/android/app" + commit-message: + prefix: "[local_auth_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/local_auth/local_auth/example/android/app" + commit-message: + prefix: "[local_auth]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/path_provider/path_provider/example/android/app" + commit-message: + prefix: "[path_provider]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/path_provider/path_provider_android/android" + commit-message: + prefix: "[path_provider_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/path_provider/path_provider_android/example/android/app" + commit-message: + prefix: "[path_provider_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/quick_actions/quick_actions_android/android" + commit-message: + prefix: "[quick_actions_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/quick_actions/quick_actions_android/example/android/app" + commit-message: + prefix: "[quick_actions_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/quick_actions/quick_actions/example/android/app" + commit-message: + prefix: "[quick_actions]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/shared_preferences/shared_preferences/example/android/app" + commit-message: + prefix: "[shared_preferences]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/shared_preferences/shared_preferences_android/example/android/app" + commit-message: + prefix: "[shared_preferences_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/url_launcher/url_launcher_android/android" + commit-message: + prefix: "[url_launcher_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/url_launcher/url_launcher_android/example/android/app" + commit-message: + prefix: "[url_launcher_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/url_launcher/url_launcher/example/android/app" + commit-message: + prefix: "[url_launcher]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/video_player/video_player_android/android" + commit-message: + prefix: "[video_player_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/video_player/video_player_android/example/android/app" + commit-message: + prefix: "[video_player_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/webview_flutter/webview_flutter/example/android/app" + commit-message: + prefix: "[webview_flutter]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/webview_flutter/webview_flutter_android/android" + commit-message: + prefix: "[webview_flutter_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/webview_flutter/webview_flutter_android/example/android" + commit-message: + prefix: "[webview_flutter_android]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 From 580de325ffcd1cbf35221751c122744425fe445f Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Mon, 2 May 2022 19:05:05 -0700 Subject: [PATCH 236/844] [ci] Shorten dependabot prefixes to comply with config spec. (#5474) (Landing on red to reopen tree) --- .github/dependabot.yml | 54 +++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 92e313d21441..676ac1da5930 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -35,7 +35,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/flutter_plugin_android_lifecycle/android" commit-message: - prefix: "[flutter_plugin_android_lifecycle]" + prefix: "[lifecycle]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -43,7 +43,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/flutter_plugin_android_lifecycle/example/android/app" commit-message: - prefix: "[flutter_plugin_android_lifecycle]" + prefix: "[lifecycle]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -51,7 +51,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/google_maps_flutter/google_maps_flutter/android" commit-message: - prefix: "[google_maps_flutter]" + prefix: "[google_maps]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -59,7 +59,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/google_maps_flutter/google_maps_flutter/example/android/app" commit-message: - prefix: "[google_maps_flutter]" + prefix: "[google_maps]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -67,7 +67,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/google_sign_in/google_sign_in/example/android/app" commit-message: - prefix: "[google_sign_in]" + prefix: "[sign_in]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -75,7 +75,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/google_sign_in/google_sign_in_android/android" commit-message: - prefix: "[google_sign_in_android]" + prefix: "[sign_in]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -83,7 +83,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/google_sign_in/google_sign_in_android/example/android/app" commit-message: - prefix: "[google_sign_in_android]" + prefix: "[sign_in]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -91,7 +91,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/in_app_purchase/in_app_purchase_android/android" commit-message: - prefix: "[in_app_purchase_android]" + prefix: "[in_app_pur]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -99,7 +99,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/in_app_purchase/in_app_purchase_android/example/android/app" commit-message: - prefix: "[in_app_purchase_android]" + prefix: "[in_app_pur]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -107,7 +107,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/in_app_purchase/in_app_purchase/example/android/app" commit-message: - prefix: "[in_app_purchase]" + prefix: "[in_app_pur]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -123,7 +123,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/image_picker/image_picker_android/android" commit-message: - prefix: "[image_picker_android]" + prefix: "[image_picker]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -131,7 +131,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/image_picker/image_picker_android/example/android/app" commit-message: - prefix: "[image_picker_android]" + prefix: "[image_picker]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -139,7 +139,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/local_auth/local_auth_android/android" commit-message: - prefix: "[local_auth_android]" + prefix: "[local_auth]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -147,7 +147,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/local_auth/local_auth_android/example/android/app" commit-message: - prefix: "[local_auth_android]" + prefix: "[local_auth]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -171,7 +171,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/path_provider/path_provider_android/android" commit-message: - prefix: "[path_provider_android]" + prefix: "[path_provider]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -179,7 +179,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/path_provider/path_provider_android/example/android/app" commit-message: - prefix: "[path_provider_android]" + prefix: "[path_provider]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -187,7 +187,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/quick_actions/quick_actions_android/android" commit-message: - prefix: "[quick_actions_android]" + prefix: "[quick_actions]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -195,7 +195,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/quick_actions/quick_actions_android/example/android/app" commit-message: - prefix: "[quick_actions_android]" + prefix: "[quick_actions]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -211,7 +211,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/shared_preferences/shared_preferences/example/android/app" commit-message: - prefix: "[shared_preferences]" + prefix: "[shared_pref]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -219,7 +219,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/shared_preferences/shared_preferences_android/example/android/app" commit-message: - prefix: "[shared_preferences_android]" + prefix: "[shared_pref]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -227,7 +227,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/url_launcher/url_launcher_android/android" commit-message: - prefix: "[url_launcher_android]" + prefix: "[url_launcher]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -235,7 +235,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/url_launcher/url_launcher_android/example/android/app" commit-message: - prefix: "[url_launcher_android]" + prefix: "[url_launcher]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -251,7 +251,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/video_player/video_player_android/android" commit-message: - prefix: "[video_player_android]" + prefix: "[video_player]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -259,7 +259,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/video_player/video_player_android/example/android/app" commit-message: - prefix: "[video_player_android]" + prefix: "[video_player]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -267,7 +267,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/webview_flutter/webview_flutter/example/android/app" commit-message: - prefix: "[webview_flutter]" + prefix: "[webview]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -275,7 +275,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/webview_flutter/webview_flutter_android/android" commit-message: - prefix: "[webview_flutter_android]" + prefix: "[webview]" schedule: interval: "daily" open-pull-requests-limit: 10 @@ -283,7 +283,7 @@ updates: - package-ecosystem: "gradle" directory: "/packages/webview_flutter/webview_flutter_android/example/android" commit-message: - prefix: "[webview_flutter_android]" + prefix: "[webview]" schedule: interval: "daily" open-pull-requests-limit: 10 From bce32f6d841aad4cab51a3b7f2bd51e35cfd7f82 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 3 May 2022 00:44:10 -0400 Subject: [PATCH 237/844] [flutter_plugin_tools] Include examples in `test` (#5453) --- script/tool/CHANGELOG.md | 3 +- script/tool/lib/src/test_command.dart | 4 +- script/tool/pubspec.yaml | 2 +- script/tool/test/test_command_test.dart | 50 +++++++++++++++++++++++-- 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 2ce644178fa6..b2319c63dc48 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.8.5 +- Updates `test` to inculde the Dart unit tests of examples, if any. - `drive-examples` now supports non-plugin packages. - Commands that iterate over examples now include non-Flutter example packages. diff --git a/script/tool/lib/src/test_command.dart b/script/tool/lib/src/test_command.dart index d115b6557adc..a1a995dbd88f 100644 --- a/script/tool/lib/src/test_command.dart +++ b/script/tool/lib/src/test_command.dart @@ -36,6 +36,9 @@ class TestCommand extends PackageLoopingCommand { final String description = 'Runs the Dart tests for all packages.\n\n' 'This command requires "flutter" to be in your path.'; + @override + bool get includeSubpackages => true; + @override Future runForPackage(RepositoryPackage package) async { if (!package.directory.childDirectory('test').existsSync()) { @@ -88,7 +91,6 @@ class TestCommand extends PackageLoopingCommand { exitCode = await processRunner.runAndStream( 'dart', [ - 'pub', 'run', if (experiment.isNotEmpty) '--enable-experiment=$experiment', 'test', diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index af38193294a5..32bfc1b62281 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.8.4 +version: 0.8.5 dependencies: args: ^2.1.0 diff --git a/script/tool/test/test_command_test.dart b/script/tool/test/test_command_test.dart index 9bcd8d1ae67a..386eaf0d345b 100644 --- a/script/tool/test/test_command_test.dart +++ b/script/tool/test/test_command_test.dart @@ -58,6 +58,28 @@ void main() { ); }); + test('runs flutter test on Flutter package example tests', () async { + final Directory pluginDir = createFakePlugin('a_plugin', packagesDir, + extraFiles: [ + 'test/empty_test.dart', + 'example/test/an_example_test.dart' + ]); + + await runCapturingPrint(runner, ['test']); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall(getFlutterCommand(mockPlatform), + const ['test', '--color'], pluginDir.path), + ProcessCall( + getFlutterCommand(mockPlatform), + const ['test', '--color'], + pluginDir.childDirectory('example').path), + ]), + ); + }); + test('fails when Flutter tests fail', () async { createFakePlugin('plugin1', packagesDir, extraFiles: ['test/empty_test.dart']); @@ -102,7 +124,7 @@ void main() { ); }); - test('runs pub run test on non-Flutter packages', () async { + test('runs dart run test on non-Flutter packages', () async { final Directory pluginDir = createFakePlugin('a', packagesDir, extraFiles: ['test/empty_test.dart']); final Directory packageDir = createFakePackage('b', packagesDir, @@ -121,12 +143,34 @@ void main() { ProcessCall('dart', const ['pub', 'get'], packageDir.path), ProcessCall( 'dart', - const ['pub', 'run', '--enable-experiment=exp1', 'test'], + const ['run', '--enable-experiment=exp1', 'test'], packageDir.path), ]), ); }); + test('runs dart run test on non-Flutter package examples', () async { + final Directory packageDir = createFakePackage('a_package', packagesDir, + extraFiles: [ + 'test/empty_test.dart', + 'example/test/an_example_test.dart' + ]); + + await runCapturingPrint(runner, ['test']); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall('dart', const ['pub', 'get'], packageDir.path), + ProcessCall('dart', const ['run', 'test'], packageDir.path), + ProcessCall('dart', const ['pub', 'get'], + packageDir.childDirectory('example').path), + ProcessCall('dart', const ['run', 'test'], + packageDir.childDirectory('example').path), + ]), + ); + }); + test('fails when getting non-Flutter package dependencies fails', () async { createFakePackage('a_package', packagesDir, extraFiles: ['test/empty_test.dart']); @@ -217,7 +261,7 @@ void main() { ProcessCall('dart', const ['pub', 'get'], packageDir.path), ProcessCall( 'dart', - const ['pub', 'run', '--enable-experiment=exp1', 'test'], + const ['run', '--enable-experiment=exp1', 'test'], packageDir.path), ]), ); From aaad0202ab0594ed138c2304ce557ba17ad69e4e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 01:29:11 -0400 Subject: [PATCH 238/844] Roll Flutter from 7965ee25dd63 to 597835d6bfc8 (4 revisions) (#5473) --- .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 a750a5ec244f..d0ee9fb91c4d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -7965ee25dd632d907f69e5ea6f4f81f35c0cf6a7 +597835d6bfc88880d72bbac124bf163b99b45aad From 79bd6024740af6192885a281711178830961c0ae Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 03:59:09 -0400 Subject: [PATCH 239/844] Roll Flutter from 597835d6bfc8 to 8ad88f162d78 (3 revisions) (#5589) --- .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 d0ee9fb91c4d..2e1abc6dbffd 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -597835d6bfc88880d72bbac124bf163b99b45aad +8ad88f162d78626e67c207fdddc71ae4f0a2e432 From 20251d635acebdf070847dd92cfd97719ad0d7cd Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 05:04:11 -0400 Subject: [PATCH 240/844] Roll Flutter from 8ad88f162d78 to 0d56b91324f6 (1 revision) (#5590) --- .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 2e1abc6dbffd..c10577854def 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8ad88f162d78626e67c207fdddc71ae4f0a2e432 +0d56b91324f6446c66dea97ce8b8d9613ac28b1c From d43c5e3ee1afe0326d16d9bedd7712c05bbdfd09 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 06:09:08 -0400 Subject: [PATCH 241/844] Roll Flutter from 0d56b91324f6 to 2998734bf4ce (1 revision) (#5591) --- .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 c10577854def..4061d0e483ab 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -0d56b91324f6446c66dea97ce8b8d9613ac28b1c +2998734bf4ce652e531be9870ce855c45582dbf6 From 9f440ecac5bf1c3aea92c28b360ae3aebb94cfb9 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 07:14:08 -0400 Subject: [PATCH 242/844] Roll Flutter from 2998734bf4ce to 912943be79db (1 revision) (#5593) --- .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 4061d0e483ab..3a91d4015f16 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2998734bf4ce652e531be9870ce855c45582dbf6 +912943be79dbaf2fd0ec0528db00e88bdf7a34c9 From 60b0b2d31ee04539af2d2e0c637185e724bea504 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 08:44:07 -0400 Subject: [PATCH 243/844] Roll Flutter from 912943be79db to 2a102fe5bc7e (1 revision) (#5599) --- .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 3a91d4015f16..440eb8d55be2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -912943be79dbaf2fd0ec0528db00e88bdf7a34c9 +2a102fe5bc7ebfeadc3825b06b227ed4600f775b From 7e22dfab9a8b5c07991f23098cf78baf9ce2ec6d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 13:09:08 -0400 Subject: [PATCH 244/844] Roll Flutter from 2a102fe5bc7e to 2e1ef66665b2 (1 revision) (#5601) --- .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 440eb8d55be2..27b06c9dba1e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2a102fe5bc7ebfeadc3825b06b227ed4600f775b +2e1ef66665b2fc9c1efd3020d60c1785eed24399 From e40abaa59ea5c551a4300dd879bbf4e058a3426a Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 3 May 2022 13:59:11 -0400 Subject: [PATCH 245/844] [tools] Remove single-example RepositoryPackage method (#5600) --- .../lib/src/common/repository_package.dart | 9 --- .../tool/lib/src/drive_examples_command.dart | 3 +- .../lib/src/firebase_test_lab_command.dart | 42 +++++++--- script/tool/lib/src/lint_android_command.dart | 39 +++++---- .../test/firebase_test_lab_command_test.dart | 81 +++++++++++++++++-- .../tool/test/lint_android_command_test.dart | 64 +++++++++++++-- 6 files changed, 185 insertions(+), 53 deletions(-) diff --git a/script/tool/lib/src/common/repository_package.dart b/script/tool/lib/src/common/repository_package.dart index 76519e040ae1..579ba01868e4 100644 --- a/script/tool/lib/src/common/repository_package.dart +++ b/script/tool/lib/src/common/repository_package.dart @@ -112,13 +112,4 @@ class RepositoryPackage { .map((FileSystemEntity entity) => RepositoryPackage(entity as Directory)); } - - /// Returns the example directory, assuming there is only one. - /// - /// DO NOT USE THIS METHOD. It exists only to easily find code that was - /// written to use a single example and needs to be restructured to handle - /// multiple examples. New code should always use [getExamples]. - // TODO(stuartmorgan): Eliminate all uses of this. - RepositoryPackage getSingleExampleDeprecated() => - RepositoryPackage(directory.childDirectory('example')); } diff --git a/script/tool/lib/src/drive_examples_command.dart b/script/tool/lib/src/drive_examples_command.dart index 68ad9713a9b7..15366e17ae85 100644 --- a/script/tool/lib/src/drive_examples_command.dart +++ b/script/tool/lib/src/drive_examples_command.dart @@ -125,8 +125,7 @@ class DriveExamplesCommand extends PackageLoopingCommand { Future runForPackage(RepositoryPackage package) async { final bool isPlugin = isFlutterPlugin(package); - if (package.isPlatformInterface && - !package.getSingleExampleDeprecated().directory.existsSync()) { + if (package.isPlatformInterface && package.getExamples().isEmpty) { // Platform interface packages generally aren't intended to have // examples, and don't need integration tests, so skip rather than fail. return PackageResult.skip( diff --git a/script/tool/lib/src/firebase_test_lab_command.dart b/script/tool/lib/src/firebase_test_lab_command.dart index e824d8ad1a90..6cc3c129c6bd 100644 --- a/script/tool/lib/src/firebase_test_lab_command.dart +++ b/script/tool/lib/src/firebase_test_lab_command.dart @@ -119,7 +119,32 @@ class FirebaseTestLabCommand extends PackageLoopingCommand { @override Future runForPackage(RepositoryPackage package) async { - final RepositoryPackage example = package.getSingleExampleDeprecated(); + final List results = []; + for (final RepositoryPackage example in package.getExamples()) { + results.add(await _runForExample(example, package: package)); + } + + // If all results skipped, report skip overall. + if (results + .every((PackageResult result) => result.state == RunState.skipped)) { + return PackageResult.skip('No examples support Android.'); + } + // Otherwise, report failure if there were any failures. + final List allErrors = results + .map((PackageResult result) => + result.state == RunState.failed ? result.details : []) + .expand((List list) => list) + .toList(); + return allErrors.isEmpty + ? PackageResult.success() + : PackageResult.fail(allErrors); + } + + /// Runs the test for the given example of [package]. + Future _runForExample( + RepositoryPackage example, { + required RepositoryPackage package, + }) async { final Directory androidDirectory = example.directory.childDirectory('android'); if (!androidDirectory.existsSync()) { @@ -163,7 +188,7 @@ class FirebaseTestLabCommand extends PackageLoopingCommand { // Used within the loop to ensure a unique GCS output location for each // test file's run. int resultsCounter = 0; - for (final File test in _findIntegrationTestFiles(package)) { + for (final File test in _findIntegrationTestFiles(example)) { final String testName = getRelativePosixPath(test, from: package.directory); print('Testing $testName...'); @@ -175,7 +200,8 @@ class FirebaseTestLabCommand extends PackageLoopingCommand { final String buildId = getStringArg('build-id'); final String testRunId = getStringArg('test-run-id'); final String resultsDir = - 'plugins_android_test/${package.displayName}/$buildId/$testRunId/${resultsCounter++}/'; + 'plugins_android_test/${package.displayName}/$buildId/$testRunId/' + '${example.directory.basename}/${resultsCounter++}/'; // Automatically retry failures; there is significant flake with these // tests whose cause isn't yet understood, and having to re-run the @@ -299,12 +325,10 @@ class FirebaseTestLabCommand extends PackageLoopingCommand { return true; } - /// Finds and returns all integration test files for [package]. - Iterable _findIntegrationTestFiles(RepositoryPackage package) sync* { - final Directory integrationTestDir = package - .getSingleExampleDeprecated() - .directory - .childDirectory('integration_test'); + /// Finds and returns all integration test files for [example]. + Iterable _findIntegrationTestFiles(RepositoryPackage example) sync* { + final Directory integrationTestDir = + example.directory.childDirectory('integration_test'); if (!integrationTestDir.existsSync()) { return; diff --git a/script/tool/lib/src/lint_android_command.dart b/script/tool/lib/src/lint_android_command.dart index 8368160c4c95..467dc7942d95 100644 --- a/script/tool/lib/src/lint_android_command.dart +++ b/script/tool/lib/src/lint_android_command.dart @@ -28,7 +28,7 @@ class LintAndroidCommand extends PackageLoopingCommand { @override final String description = 'Runs "gradlew lint" on Android plugins.\n\n' - 'Requires the example to have been build at least once before running.'; + 'Requires the examples to have been build at least once before running.'; @override Future runForPackage(RepositoryPackage package) async { @@ -38,25 +38,30 @@ class LintAndroidCommand extends PackageLoopingCommand { 'Plugin does not have an Android implemenatation.'); } - final RepositoryPackage example = package.getSingleExampleDeprecated(); - final GradleProject project = GradleProject(example.directory, - processRunner: processRunner, platform: platform); + bool failed = false; + for (final RepositoryPackage example in package.getExamples()) { + final GradleProject project = GradleProject(example.directory, + processRunner: processRunner, platform: platform); - if (!project.isConfigured()) { - return PackageResult.fail(['Build example before linting']); - } + if (!project.isConfigured()) { + return PackageResult.fail(['Build examples before linting']); + } - final String packageName = package.directory.basename; + final String packageName = package.directory.basename; - // Only lint one build mode to avoid extra work. - // Only lint the plugin project itself, to avoid failing due to errors in - // dependencies. - // - // TODO(stuartmorgan): Consider adding an XML parser to read and summarize - // all results. Currently, only the first three errors will be shown inline, - // and the rest have to be checked via the CI-uploaded artifact. - final int exitCode = await project.runCommand('$packageName:lintDebug'); + // Only lint one build mode to avoid extra work. + // Only lint the plugin project itself, to avoid failing due to errors in + // dependencies. + // + // TODO(stuartmorgan): Consider adding an XML parser to read and summarize + // all results. Currently, only the first three errors will be shown + // inline, and the rest have to be checked via the CI-uploaded artifact. + final int exitCode = await project.runCommand('$packageName:lintDebug'); + if (exitCode != 0) { + failed = true; + } + } - return exitCode == 0 ? PackageResult.success() : PackageResult.fail(); + return failed ? PackageResult.fail() : PackageResult.success(); } } diff --git a/script/tool/test/firebase_test_lab_command_test.dart b/script/tool/test/firebase_test_lab_command_test.dart index 1dfd8ba66b58..db658e19b28e 100644 --- a/script/tool/test/firebase_test_lab_command_test.dart +++ b/script/tool/test/firebase_test_lab_command_test.dart @@ -17,7 +17,7 @@ import 'mocks.dart'; import 'util.dart'; void main() { - group('$FirebaseTestLabCommand', () { + group('FirebaseTestLabCommand', () { FileSystem fileSystem; late MockPlatform mockPlatform; late Directory packagesDir; @@ -173,7 +173,7 @@ public class MainActivityTest { '/packages/plugin1/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin1/buildId/testRunId/0/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin1/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin1/example'), ProcessCall( @@ -187,7 +187,7 @@ public class MainActivityTest { '/packages/plugin2/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin2/buildId/testRunId/0/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin2/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin2/example'), ]), @@ -254,7 +254,7 @@ public class MainActivityTest { '/packages/plugin/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/0/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin/example'), ProcessCall( @@ -264,13 +264,78 @@ public class MainActivityTest { '/packages/plugin/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/1/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example/1/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin/example'), ]), ); }); + test('runs for all examples', () async { + const List examples = ['example1', 'example2']; + const String javaTestFileExampleRelativePath = + 'android/app/src/androidTest/MainActivityTest.java'; + final Directory pluginDir = createFakePlugin('plugin', packagesDir, + examples: examples, + extraFiles: [ + for (final String example in examples) ...[ + 'example/$example/integration_test/a_test.dart', + 'example/$example/android/gradlew', + 'example/$example/$javaTestFileExampleRelativePath', + ], + ]); + for (final String example in examples) { + _writeJavaTestFile( + pluginDir, 'example/$example/$javaTestFileExampleRelativePath'); + } + + final List output = await runCapturingPrint(runner, [ + 'firebase-test-lab', + '--device', + 'model=redfin,version=30', + '--device', + 'model=seoul,version=26', + '--test-run-id', + 'testRunId', + '--build-id', + 'buildId', + ]); + + expect( + output, + containsAllInOrder([ + contains('Testing example/example1/integration_test/a_test.dart...'), + contains('Testing example/example2/integration_test/a_test.dart...'), + ]), + ); + + expect( + processRunner.recordedCalls, + containsAll([ + ProcessCall( + '/packages/plugin/example/example1/android/gradlew', + 'app:assembleDebug -Pverbose=true -Ptarget=/packages/plugin/example/example1/integration_test/a_test.dart' + .split(' '), + '/packages/plugin/example/example1/android'), + ProcessCall( + 'gcloud', + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example1/0/ --device model=redfin,version=30 --device model=seoul,version=26' + .split(' '), + '/packages/plugin/example/example1'), + ProcessCall( + '/packages/plugin/example/example2/android/gradlew', + 'app:assembleDebug -Pverbose=true -Ptarget=/packages/plugin/example/example2/integration_test/a_test.dart' + .split(' '), + '/packages/plugin/example/example2/android'), + ProcessCall( + 'gcloud', + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example2/0/ --device model=redfin,version=30 --device model=seoul,version=26' + .split(' '), + '/packages/plugin/example/example2'), + ]), + ); + }); + test('fails if a test fails twice', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; @@ -479,7 +544,7 @@ public class MainActivityTest { output, containsAllInOrder([ contains('Running for package'), - contains('package/example does not support Android'), + contains('No examples support Android'), ]), ); expect(output, @@ -547,7 +612,7 @@ public class MainActivityTest { '/packages/plugin/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/0/ --device model=redfin,version=30' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30' .split(' '), '/packages/plugin/example'), ]), @@ -717,7 +782,7 @@ public class MainActivityTest { '/packages/plugin/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/0/ --device model=redfin,version=30' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30' .split(' '), '/packages/plugin/example'), ]), diff --git a/script/tool/test/lint_android_command_test.dart b/script/tool/test/lint_android_command_test.dart index a9ad510f7ee9..91608d3785cc 100644 --- a/script/tool/test/lint_android_command_test.dart +++ b/script/tool/test/lint_android_command_test.dart @@ -16,7 +16,7 @@ import 'mocks.dart'; import 'util.dart'; void main() { - group('$LintAndroidCommand', () { + group('LintAndroidCommand', () { FileSystem fileSystem; late Directory packagesDir; late CommandRunner runner; @@ -72,6 +72,47 @@ void main() { ])); }); + test('runs on all examples', () async { + final List examples = ['example1', 'example2']; + final Directory pluginDir = createFakePlugin('plugin1', packagesDir, + examples: examples, + extraFiles: [ + 'example/example1/android/gradlew', + 'example/example2/android/gradlew', + ], + platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline) + }); + + final Iterable exampleAndroidDirs = examples.map( + (String example) => pluginDir + .childDirectory('example') + .childDirectory(example) + .childDirectory('android')); + + final List output = + await runCapturingPrint(runner, ['lint-android']); + + expect( + processRunner.recordedCalls, + orderedEquals([ + for (final Directory directory in exampleAndroidDirs) + ProcessCall( + directory.childFile('gradlew').path, + const ['plugin1:lintDebug'], + directory.path, + ), + ]), + ); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin1'), + contains('No issues found!'), + ])); + }); + test('fails if gradlew is missing', () async { createFakePlugin('plugin1', packagesDir, platformSupport: { @@ -89,18 +130,25 @@ void main() { output, containsAllInOrder( [ - contains('Build example before linting'), + contains('Build examples before linting'), ], )); }); test('fails if linting finds issues', () async { - createFakePlugin('plugin1', packagesDir, - platformSupport: { - platformAndroid: const PlatformDetails(PlatformSupport.inline) - }); + final Directory pluginDir = + createFakePlugin('plugin1', packagesDir, extraFiles: [ + 'example/android/gradlew', + ], platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline) + }); - processRunner.mockProcessesForExecutable['gradlew'] = [ + final String gradlewPath = pluginDir + .childDirectory('example') + .childDirectory('android') + .childFile('gradlew') + .path; + processRunner.mockProcessesForExecutable[gradlewPath] = [ MockProcess(exitCode: 1), ]; @@ -115,7 +163,7 @@ void main() { output, containsAllInOrder( [ - contains('Build example before linting'), + contains('The following packages had errors:'), ], )); }); From 01ba538e910e9cf2dd3f7cb820c8c1739b479609 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 15:14:12 -0400 Subject: [PATCH 246/844] Roll Flutter from 2e1ef66665b2 to adb8b607e3d2 (2 revisions) (#5602) --- .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 27b06c9dba1e..955d5ddc4f22 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2e1ef66665b2fc9c1efd3020d60c1785eed24399 +adb8b607e3d2b41c68d87eb80c94bdb0776a9363 From cc132b4dcc24b4a6f1fdeaedc3033f8fb7a7c15e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 16:19:11 -0400 Subject: [PATCH 247/844] Roll Flutter from adb8b607e3d2 to 1aab2d83d932 (3 revisions) (#5606) --- .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 955d5ddc4f22..368d3f785e14 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -adb8b607e3d2b41c68d87eb80c94bdb0776a9363 +1aab2d83d9324a426c2cfc3468f6d98e5a2d5e43 From 92333f71801f43e1e5143768bc15e9ecc3db3758 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 3 May 2022 17:14:11 -0400 Subject: [PATCH 248/844] [tools] Convert test utils to RepositoryPackage (#5605) --- script/tool/lib/src/common/core.dart | 14 +- script/tool/lib/src/common/gradle.dart | 6 +- .../tool/lib/src/common/plugin_command.dart | 14 +- .../lib/src/common/repository_package.dart | 39 +++ .../src/create_all_plugins_app_command.dart | 22 +- .../lib/src/firebase_test_lab_command.dart | 4 +- script/tool/lib/src/lint_android_command.dart | 2 +- .../lib/src/make_deps_path_based_command.dart | 2 +- script/tool/lib/src/native_test_command.dart | 14 +- .../tool/lib/src/publish_check_command.dart | 4 +- script/tool/lib/src/test_command.dart | 2 +- .../tool/lib/src/version_check_command.dart | 2 +- script/tool/test/analyze_command_test.dart | 84 +++--- .../test/build_examples_command_test.dart | 53 ++-- script/tool/test/common/gradle_test.dart | 39 +-- .../common/package_looping_command_test.dart | 210 +++++++++------ .../tool/test/common/plugin_command_test.dart | 201 ++++++++++----- .../tool/test/common/plugin_utils_test.dart | 40 ++- .../test/common/repository_package_test.dart | 97 +++---- .../create_all_plugins_app_command_test.dart | 14 +- .../tool/test/custom_test_command_test.dart | 24 +- .../test/drive_examples_command_test.dart | 59 ++--- .../federation_safety_check_command_test.dart | 100 ++++---- .../test/firebase_test_lab_command_test.dart | 79 +++--- script/tool/test/format_command_test.dart | 39 +-- .../tool/test/lint_android_command_test.dart | 23 +- .../tool/test/lint_podspecs_command_test.dart | 10 +- script/tool/test/list_command_test.dart | 32 +-- .../make_deps_path_based_command_test.dart | 56 ++-- .../tool/test/native_test_command_test.dart | 242 +++++++++--------- .../tool/test/publish_check_command_test.dart | 25 +- .../test/publish_plugin_command_test.dart | 112 ++++---- .../tool/test/pubspec_check_command_test.dart | 116 ++++----- .../tool/test/readme_check_command_test.dart | 57 +++-- script/tool/test/test_command_test.dart | 64 +++-- .../test/update_excerpts_command_test.dart | 18 +- script/tool/test/util.dart | 71 ++--- .../tool/test/version_check_command_test.dart | 80 +++--- .../tool/test/xcode_analyze_command_test.dart | 40 ++- 39 files changed, 1124 insertions(+), 986 deletions(-) diff --git a/script/tool/lib/src/common/core.dart b/script/tool/lib/src/common/core.dart index 13678d720a76..b91029f1a5c8 100644 --- a/script/tool/lib/src/common/core.dart +++ b/script/tool/lib/src/common/core.dart @@ -30,14 +30,22 @@ const String platformWindows = 'windows'; /// Key for enable experiment. const String kEnableExperiment = 'enable-experiment'; +/// Target platforms supported by Flutter. +// ignore: public_member_api_docs +enum FlutterPlatform { android, ios, linux, macos, web, windows } + /// Returns whether the given directory is a Dart package. bool isPackage(FileSystemEntity entity) { if (entity is! Directory) { return false; } - // Per https://dart.dev/guides/libraries/create-library-packages#what-makes-a-library-package - return entity.childFile('pubspec.yaml').existsSync() && - entity.childDirectory('lib').existsSync(); + // According to + // https://dart.dev/guides/libraries/create-library-packages#what-makes-a-library-package + // a package must also have a `lib/` directory, but in practice that's not + // always true. flutter/plugins has some special cases (espresso, some + // federated implementation packages) that don't have any source, so this + // deliberately doesn't check that there's a lib directory. + return entity.childFile('pubspec.yaml').existsSync(); } /// Prints `successMessage` in green. diff --git a/script/tool/lib/src/common/gradle.dart b/script/tool/lib/src/common/gradle.dart index 9da4e89811e6..746536075014 100644 --- a/script/tool/lib/src/common/gradle.dart +++ b/script/tool/lib/src/common/gradle.dart @@ -6,6 +6,7 @@ import 'package:file/file.dart'; import 'package:platform/platform.dart'; import 'process_runner.dart'; +import 'repository_package.dart'; const String _gradleWrapperWindows = 'gradlew.bat'; const String _gradleWrapperNonWindows = 'gradlew'; @@ -21,7 +22,7 @@ class GradleProject { }); /// The directory of a Flutter project to run Gradle commands in. - final Directory flutterProject; + final RepositoryPackage flutterProject; /// The [ProcessRunner] used to run commands. Overridable for testing. final ProcessRunner processRunner; @@ -30,7 +31,8 @@ class GradleProject { final Platform platform; /// The project's 'android' directory. - Directory get androidDirectory => flutterProject.childDirectory('android'); + Directory get androidDirectory => + flutterProject.platformDirectory(FlutterPlatform.android); /// The path to the Gradle wrapper file for the project. File get gradleWrapper => androidDirectory.childFile( diff --git a/script/tool/lib/src/common/plugin_command.dart b/script/tool/lib/src/common/plugin_command.dart index d21002899669..0ec890368fb5 100644 --- a/script/tool/lib/src/common/plugin_command.dart +++ b/script/tool/lib/src/common/plugin_command.dart @@ -368,7 +368,7 @@ abstract class PluginCommand extends Command { await for (final FileSystemEntity entity in dir.list(followLinks: false)) { // A top-level Dart package is a plugin package. - if (_isDartPackage(entity)) { + if (isPackage(entity)) { if (packages.isEmpty || packages.contains(p.basename(entity.path))) { yield PackageEnumerationEntry( RepositoryPackage(entity as Directory), @@ -378,7 +378,7 @@ abstract class PluginCommand extends Command { // Look for Dart packages under this top-level directory. await for (final FileSystemEntity subdir in entity.list(followLinks: false)) { - if (_isDartPackage(subdir)) { + 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) @@ -427,9 +427,9 @@ abstract class PluginCommand extends Command { {bool filterExcluded = true}) async* { yield* package.directory .list(recursive: true, followLinks: false) - .where(_isDartPackage) + .where(isPackage) .map((FileSystemEntity directory) => - // _isDartPackage guarantees that this cast is valid. + // isPackage guarantees that this cast is valid. RepositoryPackage(directory as Directory)); } @@ -448,12 +448,6 @@ abstract class PluginCommand extends Command { .cast(); } - /// Returns whether the specified entity is a directory containing a - /// `pubspec.yaml` file. - bool _isDartPackage(FileSystemEntity entity) { - return entity is Directory && entity.childFile('pubspec.yaml').existsSync(); - } - /// Retrieve an instance of [GitVersionFinder] based on `_baseShaArg` and [gitDir]. /// /// Throws tool exit if [gitDir] nor root directory is a git directory. diff --git a/script/tool/lib/src/common/repository_package.dart b/script/tool/lib/src/common/repository_package.dart index 579ba01868e4..5f448d36d7e2 100644 --- a/script/tool/lib/src/common/repository_package.dart +++ b/script/tool/lib/src/common/repository_package.dart @@ -9,6 +9,7 @@ import 'package:pubspec_parse/pubspec_parse.dart'; import 'core.dart'; export 'package:pubspec_parse/pubspec_parse.dart' show Pubspec; +export 'core.dart' show FlutterPlatform; /// A package in the repository. // @@ -53,6 +54,44 @@ class RepositoryPackage { /// The package's top-level README. File get readmeFile => directory.childFile('README.md'); + /// The package's top-level README. + File get changelogFile => directory.childFile('CHANGELOG.md'); + + /// The package's top-level README. + File get authorsFile => directory.childFile('AUTHORS'); + + /// The lib directory containing the package's code. + Directory get libDirectory => directory.childDirectory('lib'); + + /// The test directory containing the package's Dart tests. + Directory get testDirectory => directory.childDirectory('test'); + + /// Returns the directory containing support for [platform]. + Directory platformDirectory(FlutterPlatform platform) { + late final String directoryName; + switch (platform) { + case FlutterPlatform.android: + directoryName = 'android'; + break; + case FlutterPlatform.ios: + directoryName = 'ios'; + break; + case FlutterPlatform.linux: + directoryName = 'linux'; + break; + case FlutterPlatform.macos: + directoryName = 'macos'; + break; + case FlutterPlatform.web: + directoryName = 'web'; + break; + case FlutterPlatform.windows: + directoryName = 'windows'; + break; + } + return directory.childDirectory(directoryName); + } + late final Pubspec _parsedPubspec = Pubspec.parse(pubspecFile.readAsStringSync()); diff --git a/script/tool/lib/src/create_all_plugins_app_command.dart b/script/tool/lib/src/create_all_plugins_app_command.dart index 6b44ab788786..ad836e19d9c6 100644 --- a/script/tool/lib/src/create_all_plugins_app_command.dart +++ b/script/tool/lib/src/create_all_plugins_app_command.dart @@ -30,11 +30,14 @@ class CreateAllPluginsAppCommand extends PluginCommand { 'Defaults to the repository root.'); } - /// The location of the synthesized app project. - Directory get appDirectory => packagesDir.fileSystem + /// The location to create the synthesized app project. + Directory get _appDirectory => packagesDir.fileSystem .directory(getStringArg(_outputDirectoryFlag)) .childDirectory('all_plugins'); + /// The synthesized app project. + RepositoryPackage get app => RepositoryPackage(_appDirectory); + @override String get description => 'Generate Flutter app that includes all plugins in packages.'; @@ -73,7 +76,7 @@ class CreateAllPluginsAppCommand extends PluginCommand { '--template=app', '--project-name=all_plugins', '--android-language=java', - appDirectory.path, + _appDirectory.path, ], ); @@ -83,8 +86,8 @@ class CreateAllPluginsAppCommand extends PluginCommand { } Future _updateAppGradle() async { - final File gradleFile = appDirectory - .childDirectory('android') + final File gradleFile = app + .platformDirectory(FlutterPlatform.android) .childDirectory('app') .childFile('build.gradle'); if (!gradleFile.existsSync()) { @@ -119,8 +122,8 @@ class CreateAllPluginsAppCommand extends PluginCommand { } Future _updateManifest() async { - final File manifestFile = appDirectory - .childDirectory('android') + final File manifestFile = app + .platformDirectory(FlutterPlatform.android) .childDirectory('app') .childDirectory('src') .childDirectory('main') @@ -147,12 +150,11 @@ class CreateAllPluginsAppCommand extends PluginCommand { } Future _genPubspecWithAllPlugins() async { - final RepositoryPackage buildAllApp = RepositoryPackage(appDirectory); // Read the old pubspec file's Dart SDK version, in order to preserve it // in the new file. The template sometimes relies on having opted in to // specific language features via SDK version, so using a different one // can cause compilation failures. - final Pubspec originalPubspec = buildAllApp.parsePubspec(); + final Pubspec originalPubspec = app.parsePubspec(); const String dartSdkKey = 'sdk'; final VersionConstraint dartSdkConstraint = originalPubspec.environment?[dartSdkKey] ?? @@ -177,7 +179,7 @@ class CreateAllPluginsAppCommand extends PluginCommand { }, dependencyOverrides: pluginDeps, ); - buildAllApp.pubspecFile.writeAsStringSync(_pubspecToString(pubspec)); + app.pubspecFile.writeAsStringSync(_pubspecToString(pubspec)); } Future> _getValidPathDependencies() async { diff --git a/script/tool/lib/src/firebase_test_lab_command.dart b/script/tool/lib/src/firebase_test_lab_command.dart index 6cc3c129c6bd..4505259b731f 100644 --- a/script/tool/lib/src/firebase_test_lab_command.dart +++ b/script/tool/lib/src/firebase_test_lab_command.dart @@ -146,7 +146,7 @@ class FirebaseTestLabCommand extends PackageLoopingCommand { required RepositoryPackage package, }) async { final Directory androidDirectory = - example.directory.childDirectory('android'); + example.platformDirectory(FlutterPlatform.android); if (!androidDirectory.existsSync()) { return PackageResult.skip( '${example.displayName} does not support Android.'); @@ -171,7 +171,7 @@ class FirebaseTestLabCommand extends PackageLoopingCommand { } // Ensures that gradle wrapper exists - final GradleProject project = GradleProject(example.directory, + final GradleProject project = GradleProject(example, processRunner: processRunner, platform: platform); if (!await _ensureGradleWrapperExists(project)) { return PackageResult.fail(['Unable to build example apk']); diff --git a/script/tool/lib/src/lint_android_command.dart b/script/tool/lib/src/lint_android_command.dart index 467dc7942d95..8ba1d643a89b 100644 --- a/script/tool/lib/src/lint_android_command.dart +++ b/script/tool/lib/src/lint_android_command.dart @@ -40,7 +40,7 @@ class LintAndroidCommand extends PackageLoopingCommand { bool failed = false; for (final RepositoryPackage example in package.getExamples()) { - final GradleProject project = GradleProject(example.directory, + final GradleProject project = GradleProject(example, processRunner: processRunner, platform: platform); if (!project.isConfigured()) { diff --git a/script/tool/lib/src/make_deps_path_based_command.dart b/script/tool/lib/src/make_deps_path_based_command.dart index 370fc3559f71..9b861c34ec91 100644 --- a/script/tool/lib/src/make_deps_path_based_command.dart +++ b/script/tool/lib/src/make_deps_path_based_command.dart @@ -178,7 +178,7 @@ dependency_overrides: for (final String packageName in packagesToOverride) { // Find the relative path from the common base to the local package. final List repoRelativePathComponents = path.split( - path.relative(localDependencies[packageName]!.directory.path, + path.relative(localDependencies[packageName]!.path, from: commonBasePath)); newPubspecContents += ''' $packageName: diff --git a/script/tool/lib/src/native_test_command.dart b/script/tool/lib/src/native_test_command.dart index a0d2ebd4e23c..81b13cbb75e2 100644 --- a/script/tool/lib/src/native_test_command.dart +++ b/script/tool/lib/src/native_test_command.dart @@ -198,22 +198,22 @@ this command. Future<_PlatformResult> _testAndroid( RepositoryPackage plugin, _TestMode mode) async { bool exampleHasUnitTests(RepositoryPackage example) { - return example.directory - .childDirectory('android') + return example + .platformDirectory(FlutterPlatform.android) .childDirectory('app') .childDirectory('src') .childDirectory('test') .existsSync() || - example.directory.parent - .childDirectory('android') + plugin + .platformDirectory(FlutterPlatform.android) .childDirectory('src') .childDirectory('test') .existsSync(); } bool exampleHasNativeIntegrationTests(RepositoryPackage example) { - final Directory integrationTestDirectory = example.directory - .childDirectory('android') + final Directory integrationTestDirectory = example + .platformDirectory(FlutterPlatform.android) .childDirectory('app') .childDirectory('src') .childDirectory('androidTest'); @@ -269,7 +269,7 @@ this command. _printRunningExampleTestsMessage(example, 'Android'); final GradleProject project = GradleProject( - example.directory, + example, processRunner: processRunner, platform: platform, ); diff --git a/script/tool/lib/src/publish_check_command.dart b/script/tool/lib/src/publish_check_command.dart index b6b83dabcb49..af8eac2257eb 100644 --- a/script/tool/lib/src/publish_check_command.dart +++ b/script/tool/lib/src/publish_check_command.dart @@ -246,12 +246,12 @@ HTTP response: ${pubVersionFinderResponse.httpResponse.body} bool _passesAuthorsCheck(RepositoryPackage package) { final List pathComponents = - package.directory.fileSystem.path.split(package.directory.path); + package.directory.fileSystem.path.split(package.path); if (pathComponents.contains('third_party')) { // Third-party packages aren't required to have an AUTHORS file. return true; } - return package.directory.childFile('AUTHORS').existsSync(); + return package.authorsFile.existsSync(); } void _printImportantStatusMessage(String message, {required bool isError}) { diff --git a/script/tool/lib/src/test_command.dart b/script/tool/lib/src/test_command.dart index a1a995dbd88f..27a01c95e851 100644 --- a/script/tool/lib/src/test_command.dart +++ b/script/tool/lib/src/test_command.dart @@ -41,7 +41,7 @@ class TestCommand extends PackageLoopingCommand { @override Future runForPackage(RepositoryPackage package) async { - if (!package.directory.childDirectory('test').existsSync()) { + if (!package.testDirectory.existsSync()) { return PackageResult.skip('No test/ directory.'); } diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart index f69611f5a06d..fb768c19b524 100644 --- a/script/tool/lib/src/version_check_command.dart +++ b/script/tool/lib/src/version_check_command.dart @@ -384,7 +384,7 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} final Version fromPubspec = pubspec.version!; // get first version from CHANGELOG - final File changelog = package.directory.childFile('CHANGELOG.md'); + final File changelog = package.changelogFile; final List lines = changelog.readAsLinesSync(); String? firstLineWithText; final Iterator iterator = lines.iterator; diff --git a/script/tool/test/analyze_command_test.dart b/script/tool/test/analyze_command_test.dart index 98fb9382a3aa..e293e8b85e98 100644 --- a/script/tool/test/analyze_command_test.dart +++ b/script/tool/test/analyze_command_test.dart @@ -37,45 +37,45 @@ void main() { }); test('analyzes all packages', () async { - final Directory plugin1Dir = createFakePlugin('a', packagesDir); - final Directory plugin2Dir = createFakePlugin('b', packagesDir); + final RepositoryPackage plugin1 = createFakePlugin('a', packagesDir); + final RepositoryPackage plugin2 = createFakePlugin('b', packagesDir); await runCapturingPrint(runner, ['analyze']); expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall('flutter', const ['pub', 'get'], plugin1Dir.path), - ProcessCall('dart', const ['analyze', '--fatal-infos'], - plugin1Dir.path), - ProcessCall('flutter', const ['pub', 'get'], plugin2Dir.path), - ProcessCall('dart', const ['analyze', '--fatal-infos'], - plugin2Dir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin1.path), + ProcessCall( + 'dart', const ['analyze', '--fatal-infos'], plugin1.path), + ProcessCall('flutter', const ['pub', 'get'], plugin2.path), + ProcessCall( + 'dart', const ['analyze', '--fatal-infos'], plugin2.path), ])); }); test('skips flutter pub get for examples', () async { - final Directory plugin1Dir = createFakePlugin('a', packagesDir); + final RepositoryPackage plugin1 = createFakePlugin('a', packagesDir); await runCapturingPrint(runner, ['analyze']); expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall('flutter', const ['pub', 'get'], plugin1Dir.path), - ProcessCall('dart', const ['analyze', '--fatal-infos'], - plugin1Dir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin1.path), + ProcessCall( + 'dart', const ['analyze', '--fatal-infos'], plugin1.path), ])); }); test('runs flutter pub get for non-example subpackages', () async { - final Directory mainPackageDir = createFakePackage('a', packagesDir); - final Directory otherPackages = - mainPackageDir.childDirectory('other_packages'); - final Directory subpackage1 = - createFakePackage('subpackage1', otherPackages); - final Directory subpackage2 = - createFakePackage('subpackage2', otherPackages); + final RepositoryPackage mainPackage = createFakePackage('a', packagesDir); + final Directory otherPackagesDir = + mainPackage.directory.childDirectory('other_packages'); + final RepositoryPackage subpackage1 = + createFakePackage('subpackage1', otherPackagesDir); + final RepositoryPackage subpackage2 = + createFakePackage('subpackage2', otherPackagesDir); await runCapturingPrint(runner, ['analyze']); @@ -83,36 +83,36 @@ void main() { processRunner.recordedCalls, orderedEquals([ ProcessCall( - 'flutter', const ['pub', 'get'], mainPackageDir.path), + 'flutter', const ['pub', 'get'], mainPackage.path), ProcessCall( 'flutter', const ['pub', 'get'], subpackage1.path), ProcessCall( 'flutter', const ['pub', 'get'], subpackage2.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], - mainPackageDir.path), + mainPackage.path), ])); }); test('don\'t elide a non-contained example package', () async { - final Directory plugin1Dir = createFakePlugin('a', packagesDir); - final Directory plugin2Dir = createFakePlugin('example', packagesDir); + final RepositoryPackage plugin1 = createFakePlugin('a', packagesDir); + final RepositoryPackage plugin2 = createFakePlugin('example', packagesDir); await runCapturingPrint(runner, ['analyze']); expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall('flutter', const ['pub', 'get'], plugin1Dir.path), - ProcessCall('dart', const ['analyze', '--fatal-infos'], - plugin1Dir.path), - ProcessCall('flutter', const ['pub', 'get'], plugin2Dir.path), - ProcessCall('dart', const ['analyze', '--fatal-infos'], - plugin2Dir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin1.path), + ProcessCall( + 'dart', const ['analyze', '--fatal-infos'], plugin1.path), + ProcessCall('flutter', const ['pub', 'get'], plugin2.path), + ProcessCall( + 'dart', const ['analyze', '--fatal-infos'], plugin2.path), ])); }); test('uses a separate analysis sdk', () async { - final Directory pluginDir = createFakePlugin('a', packagesDir); + final RepositoryPackage plugin = createFakePlugin('a', packagesDir); await runCapturingPrint( runner, ['analyze', '--analysis-sdk', 'foo/bar/baz']); @@ -123,12 +123,12 @@ void main() { ProcessCall( 'flutter', const ['pub', 'get'], - pluginDir.path, + plugin.path, ), ProcessCall( 'foo/bar/baz/bin/dart', const ['analyze', '--fatal-infos'], - pluginDir.path, + plugin.path, ), ]), ); @@ -180,7 +180,7 @@ void main() { }); test('takes an allow list', () async { - final Directory pluginDir = createFakePlugin('foo', packagesDir, + final RepositoryPackage plugin = createFakePlugin('foo', packagesDir, extraFiles: ['analysis_options.yaml']); await runCapturingPrint( @@ -189,15 +189,14 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall( - 'flutter', const ['pub', 'get'], pluginDir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], - pluginDir.path), + plugin.path), ])); }); test('takes an allow config file', () async { - final Directory pluginDir = createFakePlugin('foo', packagesDir, + final RepositoryPackage plugin = createFakePlugin('foo', packagesDir, extraFiles: ['analysis_options.yaml']); final File allowFile = packagesDir.childFile('custom.yaml'); allowFile.writeAsStringSync('- foo'); @@ -208,10 +207,9 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall( - 'flutter', const ['pub', 'get'], pluginDir.path), + ProcessCall('flutter', const ['pub', 'get'], plugin.path), ProcessCall('dart', const ['analyze', '--fatal-infos'], - pluginDir.path), + plugin.path), ])); }); @@ -280,7 +278,7 @@ void main() { // modify the script above, as it is run from source, but out-of-repo. // Contact stuartmorgan or devoncarew for assistance. test('Dart repo analyze command works', () async { - final Directory pluginDir = createFakePlugin('foo', packagesDir, + final RepositoryPackage plugin = createFakePlugin('foo', packagesDir, extraFiles: ['analysis_options.yaml']); final File allowFile = packagesDir.childFile('custom.yaml'); allowFile.writeAsStringSync('- foo'); @@ -300,12 +298,12 @@ void main() { ProcessCall( 'flutter', const ['pub', 'get'], - pluginDir.path, + plugin.path, ), ProcessCall( 'foo/bar/baz/bin/dart', const ['analyze', '--fatal-infos'], - pluginDir.path, + plugin.path, ), ]), ); diff --git a/script/tool/test/build_examples_command_test.dart b/script/tool/test/build_examples_command_test.dart index 2bdb1bc0c2ba..420b3b1161db 100644 --- a/script/tool/test/build_examples_command_test.dart +++ b/script/tool/test/build_examples_command_test.dart @@ -134,13 +134,12 @@ void main() { test('building for iOS', () async { mockPlatform.isMacOS = true; - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformIOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, ['build-examples', '--ios', '--enable-experiment=exp1']); @@ -191,13 +190,12 @@ void main() { test('building for Linux', () async { mockPlatform.isLinux = true; - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformLinux: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint( runner, ['build-examples', '--linux']); @@ -240,13 +238,12 @@ void main() { test('building for macOS', () async { mockPlatform.isMacOS = true; - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint( runner, ['build-examples', '--macos']); @@ -286,13 +283,12 @@ void main() { }); test('building for web', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformWeb: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, ['build-examples', '--web']); @@ -336,13 +332,12 @@ void main() { test('building for Windows', () async { mockPlatform.isWindows = true; - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformWindows: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint( runner, ['build-examples', '--windows']); @@ -386,13 +381,12 @@ void main() { }); test('building for Android', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'build-examples', @@ -415,13 +409,12 @@ void main() { }); test('enable-experiment flag for Android', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); await runCapturingPrint(runner, ['build-examples', '--apk', '--enable-experiment=exp1']); @@ -437,13 +430,12 @@ void main() { }); test('enable-experiment flag for ios', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformIOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); await runCapturingPrint(runner, ['build-examples', '--ios', '--enable-experiment=exp1']); @@ -481,7 +473,7 @@ void main() { group('packages', () { test('builds when requested platform is supported by example', () async { - final Directory packageDirectory = createFakePackage( + final RepositoryPackage package = createFakePackage( 'package', packagesDir, isFlutter: true, extraFiles: [ 'example/ios/Runner.xcodeproj/project.pbxproj' ]); @@ -507,7 +499,7 @@ void main() { 'ios', '--no-codesign', ], - packageDirectory.childDirectory('example').path), + getExampleDir(package).path), ])); }); @@ -567,7 +559,7 @@ void main() { }); test('logs skipped platforms when only some are supported', () async { - final Directory packageDirectory = createFakePackage( + final RepositoryPackage package = createFakePackage( 'package', packagesDir, isFlutter: true, extraFiles: ['example/linux/CMakeLists.txt']); @@ -590,21 +582,20 @@ void main() { ProcessCall( getFlutterCommand(mockPlatform), const ['build', 'linux'], - packageDirectory.childDirectory('example').path), + getExampleDir(package).path), ])); }); }); test('The .pluginToolsConfig.yaml file', () async { mockPlatform.isLinux = true; - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformLinux: const PlatformDetails(PlatformSupport.inline), platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final File pluginExampleConfigFile = pluginExampleDirectory.childFile('.pluginToolsConfig.yaml'); diff --git a/script/tool/test/common/gradle_test.dart b/script/tool/test/common/gradle_test.dart index 3eac60baf3c3..8df4a65b93a5 100644 --- a/script/tool/test/common/gradle_test.dart +++ b/script/tool/test/common/gradle_test.dart @@ -23,7 +23,7 @@ void main() { group('isConfigured', () { test('reports true when configured on Windows', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', fileSystem.directory('/'), extraFiles: ['android/gradlew.bat']); final GradleProject project = GradleProject( @@ -36,7 +36,7 @@ void main() { }); test('reports true when configured on non-Windows', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', fileSystem.directory('/'), extraFiles: ['android/gradlew']); final GradleProject project = GradleProject( @@ -49,7 +49,7 @@ void main() { }); test('reports false when not configured on Windows', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', fileSystem.directory('/'), extraFiles: ['android/foo']); final GradleProject project = GradleProject( @@ -62,7 +62,7 @@ void main() { }); test('reports true when configured on non-Windows', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', fileSystem.directory('/'), extraFiles: ['android/foo']); final GradleProject project = GradleProject( @@ -75,9 +75,9 @@ void main() { }); }); - group('runXcodeBuild', () { + group('runCommand', () { test('runs without arguments', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', fileSystem.directory('/'), extraFiles: ['android/gradlew']); final GradleProject project = GradleProject( @@ -93,16 +93,19 @@ void main() { processRunner.recordedCalls, orderedEquals([ ProcessCall( - plugin.childDirectory('android').childFile('gradlew').path, + plugin + .platformDirectory(FlutterPlatform.android) + .childFile('gradlew') + .path, const [ 'foo', ], - plugin.childDirectory('android').path), + plugin.platformDirectory(FlutterPlatform.android).path), ])); }); test('runs with arguments', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', fileSystem.directory('/'), extraFiles: ['android/gradlew']); final GradleProject project = GradleProject( @@ -121,18 +124,21 @@ void main() { processRunner.recordedCalls, orderedEquals([ ProcessCall( - plugin.childDirectory('android').childFile('gradlew').path, + plugin + .platformDirectory(FlutterPlatform.android) + .childFile('gradlew') + .path, const [ 'foo', '--bar', '--baz', ], - plugin.childDirectory('android').path), + plugin.platformDirectory(FlutterPlatform.android).path), ])); }); test('runs with the correct wrapper on Windows', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', fileSystem.directory('/'), extraFiles: ['android/gradlew.bat']); final GradleProject project = GradleProject( @@ -148,16 +154,19 @@ void main() { processRunner.recordedCalls, orderedEquals([ ProcessCall( - plugin.childDirectory('android').childFile('gradlew.bat').path, + plugin + .platformDirectory(FlutterPlatform.android) + .childFile('gradlew.bat') + .path, const [ 'foo', ], - plugin.childDirectory('android').path), + plugin.platformDirectory(FlutterPlatform.android).path), ])); }); test('returns error codes', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', fileSystem.directory('/'), extraFiles: ['android/gradlew.bat']); final GradleProject project = GradleProject( diff --git a/script/tool/test/common/package_looping_command_test.dart b/script/tool/test/common/package_looping_command_test.dart index ea02bd4ea387..3dd6a47ae2fb 100644 --- a/script/tool/test/common/package_looping_command_test.dart +++ b/script/tool/test/common/package_looping_command_test.dart @@ -11,7 +11,6 @@ import 'package:file/memory.dart'; import 'package:flutter_plugin_tools/src/common/core.dart'; import 'package:flutter_plugin_tools/src/common/package_looping_command.dart'; import 'package:flutter_plugin_tools/src/common/process_runner.dart'; -import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:git/git.dart'; import 'package:mockito/mockito.dart'; import 'package:platform/platform.dart'; @@ -31,6 +30,21 @@ const String _startSuccessColor = '\x1B[32m'; const String _startWarningColor = '\x1B[33m'; const String _endColor = '\x1B[0m'; +// The filename within a package containing warnings to log during runForPackage. +enum _ResultFileType { + /// A file containing errors to return. + errors, + + /// A file containing warnings that should be logged. + warns, + + /// A file indicating that the package should be skipped, and why. + skips, + + /// A file indicating that the package should throw. + throws, +} + // The filename within a package containing errors to return from runForPackage. const String _errorFile = 'errors'; // The filename within a package indicating that it should be skipped. @@ -40,6 +54,30 @@ const String _warningFile = 'warnings'; // The filename within a package indicating that it should throw. const String _throwFile = 'throw'; +/// Writes a file to [package] to control the behavior of +/// [TestPackageLoopingCommand] for that package. +void _addResultFile(RepositoryPackage package, _ResultFileType type, + {String? contents}) { + final File file = package.directory.childFile(_filenameForType(type)); + file.createSync(); + if (contents != null) { + file.writeAsStringSync(contents); + } +} + +String _filenameForType(_ResultFileType type) { + switch (type) { + case _ResultFileType.errors: + return _errorFile; + case _ResultFileType.warns: + return _warningFile; + case _ResultFileType.skips: + return _skipFile; + case _ResultFileType.throws: + return _throwFile; + } +} + void main() { late FileSystem fileSystem; late MockPlatform mockPlatform; @@ -122,10 +160,10 @@ void main() { test('does not stop looping on error', () async { createFakePackage('package_a', packagesDir); - final Directory failingPackage = + final RepositoryPackage failingPackage = createFakePlugin('package_b', packagesDir); createFakePackage('package_c', packagesDir); - failingPackage.childFile(_errorFile).createSync(); + _addResultFile(failingPackage, _ResultFileType.errors); final TestPackageLoopingCommand command = createTestCommand(hasLongOutput: false); @@ -147,10 +185,10 @@ void main() { test('does not stop looping on exceptions', () async { createFakePackage('package_a', packagesDir); - final Directory failingPackage = + final RepositoryPackage failingPackage = createFakePlugin('package_b', packagesDir); createFakePackage('package_c', packagesDir); - failingPackage.childFile(_throwFile).createSync(); + _addResultFile(failingPackage, _ResultFileType.throws); final TestPackageLoopingCommand command = createTestCommand(hasLongOutput: false); @@ -173,8 +211,10 @@ void main() { group('package iteration', () { test('includes plugins and packages', () async { - final Directory plugin = createFakePlugin('a_plugin', packagesDir); - final Directory package = createFakePackage('a_package', packagesDir); + final RepositoryPackage plugin = + createFakePlugin('a_plugin', packagesDir); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); final TestPackageLoopingCommand command = createTestCommand(); await runCommand(command); @@ -184,8 +224,9 @@ void main() { }); test('includes third_party/packages', () async { - final Directory package1 = createFakePackage('a_package', packagesDir); - final Directory package2 = + final RepositoryPackage package1 = + createFakePackage('a_package', packagesDir); + final RepositoryPackage package2 = createFakePackage('another_package', thirdPartyPackagesDir); final TestPackageLoopingCommand command = createTestCommand(); @@ -196,9 +237,10 @@ void main() { }); test('includes subpackages when requested', () async { - final Directory plugin = createFakePlugin('a_plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir, examples: ['example1', 'example2']); - final Directory package = createFakePackage('a_package', packagesDir); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); final TestPackageLoopingCommand command = createTestCommand(includeSubpackages: true); @@ -208,39 +250,45 @@ void main() { command.checkedPackages, unorderedEquals([ plugin.path, - plugin.childDirectory('example').childDirectory('example1').path, - plugin.childDirectory('example').childDirectory('example2').path, + getExampleDir(plugin).childDirectory('example1').path, + getExampleDir(plugin).childDirectory('example2').path, package.path, - package.childDirectory('example').path, + getExampleDir(package).path, ])); }); test('excludes subpackages when main package is excluded', () async { - final Directory excluded = createFakePlugin('a_plugin', packagesDir, + final RepositoryPackage excluded = createFakePlugin( + 'a_plugin', packagesDir, examples: ['example1', 'example2']); - final Directory included = createFakePackage('a_package', packagesDir); + final RepositoryPackage included = + createFakePackage('a_package', packagesDir); final TestPackageLoopingCommand command = createTestCommand(includeSubpackages: true); await runCommand(command, arguments: ['--exclude=a_plugin']); + final Iterable examples = excluded.getExamples(); + expect( command.checkedPackages, unorderedEquals([ included.path, - included.childDirectory('example').path, + getExampleDir(included).path, ])); expect(command.checkedPackages, isNot(contains(excluded.path))); - expect(command.checkedPackages, - isNot(contains(excluded.childDirectory('example1').path))); - expect(command.checkedPackages, - isNot(contains(excluded.childDirectory('example2').path))); + expect(examples.length, 2); + for (final RepositoryPackage example in examples) { + expect(command.checkedPackages, isNot(contains(example.path))); + } }); test('skips unsupported versions when requested', () async { - final Directory excluded = createFakePlugin('a_plugin', packagesDir, + final RepositoryPackage excluded = createFakePlugin( + 'a_plugin', packagesDir, flutterConstraint: '>=2.10.0'); - final Directory included = createFakePackage('a_package', packagesDir); + final RepositoryPackage included = + createFakePackage('a_package', packagesDir); final TestPackageLoopingCommand command = createTestCommand(includeSubpackages: true, hasLongOutput: false); @@ -252,7 +300,7 @@ void main() { command.checkedPackages, unorderedEquals([ included.path, - included.childDirectory('example').path, + getExampleDir(included).path, ])); expect(command.checkedPackages, isNot(contains(excluded.path))); @@ -360,13 +408,13 @@ void main() { test('shows failure summaries when something fails without extra details', () async { createFakePackage('package_a', packagesDir); - final Directory failingPackage1 = + final RepositoryPackage failingPackage1 = createFakePlugin('package_b', packagesDir); createFakePackage('package_c', packagesDir); - final Directory failingPackage2 = + final RepositoryPackage failingPackage2 = createFakePlugin('package_d', packagesDir); - failingPackage1.childFile(_errorFile).createSync(); - failingPackage2.childFile(_errorFile).createSync(); + _addResultFile(failingPackage1, _ResultFileType.errors); + _addResultFile(failingPackage2, _ResultFileType.errors); final TestPackageLoopingCommand command = createTestCommand(hasLongOutput: false); @@ -390,13 +438,13 @@ void main() { test('uses custom summary header and footer if provided', () async { createFakePackage('package_a', packagesDir); - final Directory failingPackage1 = + final RepositoryPackage failingPackage1 = createFakePlugin('package_b', packagesDir); createFakePackage('package_c', packagesDir); - final Directory failingPackage2 = + final RepositoryPackage failingPackage2 = createFakePlugin('package_d', packagesDir); - failingPackage1.childFile(_errorFile).createSync(); - failingPackage2.childFile(_errorFile).createSync(); + _addResultFile(failingPackage1, _ResultFileType.errors); + _addResultFile(failingPackage2, _ResultFileType.errors); final TestPackageLoopingCommand command = createTestCommand( hasLongOutput: false, @@ -423,17 +471,15 @@ void main() { test('shows failure summaries when something fails with extra details', () async { createFakePackage('package_a', packagesDir); - final Directory failingPackage1 = + final RepositoryPackage failingPackage1 = createFakePlugin('package_b', packagesDir); createFakePackage('package_c', packagesDir); - final Directory failingPackage2 = + final RepositoryPackage failingPackage2 = createFakePlugin('package_d', packagesDir); - final File errorFile1 = failingPackage1.childFile(_errorFile); - errorFile1.createSync(); - errorFile1.writeAsStringSync('just one detail'); - final File errorFile2 = failingPackage2.childFile(_errorFile); - errorFile2.createSync(); - errorFile2.writeAsStringSync('first detail\nsecond detail'); + _addResultFile(failingPackage1, _ResultFileType.errors, + contents: 'just one detail'); + _addResultFile(failingPackage2, _ResultFileType.errors, + contents: 'first detail\nsecond detail'); final TestPackageLoopingCommand command = createTestCommand(hasLongOutput: false); @@ -479,8 +525,10 @@ void main() { test('logs skips', () async { createFakePackage('package_a', packagesDir); - final Directory skipPackage = createFakePackage('package_b', packagesDir); - skipPackage.childFile(_skipFile).writeAsStringSync('For a reason'); + final RepositoryPackage skipPackage = + createFakePackage('package_b', packagesDir); + _addResultFile(skipPackage, _ResultFileType.skips, + contents: 'For a reason'); final TestPackageLoopingCommand command = createTestCommand(hasLongOutput: false); @@ -513,10 +561,10 @@ void main() { }); test('logs warnings', () async { - final Directory warnPackage = createFakePackage('package_a', packagesDir); - warnPackage - .childFile(_warningFile) - .writeAsStringSync('Warning 1\nWarning 2'); + final RepositoryPackage warnPackage = + createFakePackage('package_a', packagesDir); + _addResultFile(warnPackage, _ResultFileType.warns, + contents: 'Warning 1\nWarning 2'); createFakePackage('package_b', packagesDir); final TestPackageLoopingCommand command = @@ -535,10 +583,10 @@ void main() { test('logs unhandled exceptions as errors', () async { createFakePackage('package_a', packagesDir); - final Directory failingPackage = + final RepositoryPackage failingPackage = createFakePlugin('package_b', packagesDir); createFakePackage('package_c', packagesDir); - failingPackage.childFile(_throwFile).createSync(); + _addResultFile(failingPackage, _ResultFileType.throws); final TestPackageLoopingCommand command = createTestCommand(hasLongOutput: false); @@ -559,23 +607,30 @@ void main() { }); test('prints run summary on success', () async { - final Directory warnPackage1 = + final RepositoryPackage warnPackage1 = createFakePackage('package_a', packagesDir); - warnPackage1 - .childFile(_warningFile) - .writeAsStringSync('Warning 1\nWarning 2'); + _addResultFile(warnPackage1, _ResultFileType.warns, + contents: 'Warning 1\nWarning 2'); + createFakePackage('package_b', packagesDir); - final Directory skipPackage = createFakePackage('package_c', packagesDir); - skipPackage.childFile(_skipFile).writeAsStringSync('For a reason'); - final Directory skipAndWarnPackage = + + final RepositoryPackage skipPackage = + createFakePackage('package_c', packagesDir); + _addResultFile(skipPackage, _ResultFileType.skips, + contents: 'For a reason'); + + final RepositoryPackage skipAndWarnPackage = createFakePackage('package_d', packagesDir); - skipAndWarnPackage.childFile(_warningFile).writeAsStringSync('Warning'); - skipAndWarnPackage.childFile(_skipFile).writeAsStringSync('See warning'); - final Directory warnPackage2 = + _addResultFile(skipAndWarnPackage, _ResultFileType.warns, + contents: 'Warning'); + _addResultFile(skipAndWarnPackage, _ResultFileType.skips, + contents: 'See warning'); + + final RepositoryPackage warnPackage2 = createFakePackage('package_e', packagesDir); - warnPackage2 - .childFile(_warningFile) - .writeAsStringSync('Warning 1\nWarning 2'); + _addResultFile(warnPackage2, _ResultFileType.warns, + contents: 'Warning 1\nWarning 2'); + createFakePackage('package_f', packagesDir); final TestPackageLoopingCommand command = @@ -615,23 +670,30 @@ void main() { }); test('prints long-form run summary for long-output commands', () async { - final Directory warnPackage1 = + final RepositoryPackage warnPackage1 = createFakePackage('package_a', packagesDir); - warnPackage1 - .childFile(_warningFile) - .writeAsStringSync('Warning 1\nWarning 2'); + _addResultFile(warnPackage1, _ResultFileType.warns, + contents: 'Warning 1\nWarning 2'); + createFakePackage('package_b', packagesDir); - final Directory skipPackage = createFakePackage('package_c', packagesDir); - skipPackage.childFile(_skipFile).writeAsStringSync('For a reason'); - final Directory skipAndWarnPackage = + + final RepositoryPackage skipPackage = + createFakePackage('package_c', packagesDir); + _addResultFile(skipPackage, _ResultFileType.skips, + contents: 'For a reason'); + + final RepositoryPackage skipAndWarnPackage = createFakePackage('package_d', packagesDir); - skipAndWarnPackage.childFile(_warningFile).writeAsStringSync('Warning'); - skipAndWarnPackage.childFile(_skipFile).writeAsStringSync('See warning'); - final Directory warnPackage2 = + _addResultFile(skipAndWarnPackage, _ResultFileType.warns, + contents: 'Warning'); + _addResultFile(skipAndWarnPackage, _ResultFileType.skips, + contents: 'See warning'); + + final RepositoryPackage warnPackage2 = createFakePackage('package_e', packagesDir); - warnPackage2 - .childFile(_warningFile) - .writeAsStringSync('Warning 1\nWarning 2'); + _addResultFile(warnPackage2, _ResultFileType.warns, + contents: 'Warning 1\nWarning 2'); + createFakePackage('package_f', packagesDir); final TestPackageLoopingCommand command = diff --git a/script/tool/test/common/plugin_command_test.dart b/script/tool/test/common/plugin_command_test.dart index 782ea7267ae7..7ed3d239b2ad 100644 --- a/script/tool/test/common/plugin_command_test.dart +++ b/script/tool/test/common/plugin_command_test.dart @@ -62,18 +62,24 @@ void main() { group('plugin iteration', () { test('all plugins from file system', () async { - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample']); expect(command.plugins, unorderedEquals([plugin1.path, plugin2.path])); }); test('includes both plugins and packages', () async { - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); - final Directory package3 = createFakePackage('package3', packagesDir); - final Directory package4 = createFakePackage('package4', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); + final RepositoryPackage package3 = + createFakePackage('package3', packagesDir); + final RepositoryPackage package4 = + createFakePackage('package4', packagesDir); await runCapturingPrint(runner, ['sample']); expect( command.plugins, @@ -85,10 +91,25 @@ void main() { ])); }); + test('includes packages without source', () async { + final RepositoryPackage package = + createFakePackage('package', packagesDir); + package.libDirectory.deleteSync(recursive: true); + + await runCapturingPrint(runner, ['sample']); + expect( + command.plugins, + unorderedEquals([ + package.path, + ])); + }); + test('all plugins includes third_party/packages', () async { - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); - final Directory plugin3 = + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin3 = createFakePlugin('plugin3', thirdPartyPackagesDir); await runCapturingPrint(runner, ['sample']); expect(command.plugins, @@ -96,10 +117,12 @@ void main() { }); test('--packages limits packages', () async { - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); createFakePlugin('plugin2', packagesDir); createFakePackage('package3', packagesDir); - final Directory package4 = createFakePackage('package4', packagesDir); + final RepositoryPackage package4 = + createFakePackage('package4', packagesDir); await runCapturingPrint( runner, ['sample', '--packages=plugin1,package4']); expect( @@ -111,10 +134,12 @@ void main() { }); test('--plugins acts as an alias to --packages', () async { - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); createFakePlugin('plugin2', packagesDir); createFakePackage('package3', packagesDir); - final Directory package4 = createFakePackage('package4', packagesDir); + final RepositoryPackage package4 = + createFakePackage('package4', packagesDir); await runCapturingPrint( runner, ['sample', '--plugins=plugin1,package4']); expect( @@ -127,7 +152,8 @@ void main() { test('exclude packages when packages flag is specified', () async { createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, [ 'sample', '--packages=plugin1,plugin2', @@ -146,7 +172,8 @@ void main() { test('exclude federated plugins when packages flag is specified', () async { createFakePlugin('plugin1', packagesDir.childDirectory('federated')); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, [ 'sample', '--packages=federated/plugin1,plugin2', @@ -158,7 +185,8 @@ void main() { test('exclude entire federated plugins when packages flag is specified', () async { createFakePlugin('plugin1', packagesDir.childDirectory('federated')); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, [ 'sample', '--packages=federated/plugin1,plugin2', @@ -189,11 +217,11 @@ packages/plugin1/plugin1/plugin1.dart '''), ]; final Directory pluginGroup = packagesDir.childDirectory('plugin1'); - final Directory appFacingPackage = + final RepositoryPackage appFacingPackage = createFakePlugin('plugin1', pluginGroup); - final Directory platformInterfacePackage = + final RepositoryPackage platformInterfacePackage = createFakePlugin('plugin1_platform_interface', pluginGroup); - final Directory implementationPackage = + final RepositoryPackage implementationPackage = createFakePlugin('plugin1_web', pluginGroup); await runCapturingPrint( @@ -217,7 +245,7 @@ packages/plugin1/plugin1/plugin1.dart '''), ]; final Directory pluginGroup = packagesDir.childDirectory('plugin1'); - final Directory appFacingPackage = + final RepositoryPackage appFacingPackage = createFakePlugin('plugin1', pluginGroup); createFakePlugin('plugin1_platform_interface', pluginGroup); createFakePlugin('plugin1_web', pluginGroup); @@ -239,7 +267,7 @@ packages/plugin1/plugin1/plugin1.dart final Directory pluginGroup = packagesDir.childDirectory('plugin1'); createFakePlugin('plugin1', pluginGroup); - final Directory platformInterfacePackage = + final RepositoryPackage platformInterfacePackage = createFakePlugin('plugin1_platform_interface', pluginGroup); createFakePlugin('plugin1_web', pluginGroup); @@ -265,14 +293,15 @@ packages/plugin1/plugin1/plugin1.dart CommandRunner('common_command', 'subpackage testing'); localRunner.addCommand(localCommand); - final Directory package = createFakePackage('apackage', packagesDir); + final RepositoryPackage package = + createFakePackage('apackage', packagesDir); await runCapturingPrint(localRunner, ['sample']); expect( localCommand.plugins, containsAllInOrder([ package.path, - package.childDirectory('example').path, + getExampleDir(package).path, ])); }); @@ -340,8 +369,10 @@ packages/plugin1/plugin1/plugin1.dart group('test run-on-changed-packages', () { test('all plugins should be tested if there are no changes.', () async { - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -355,8 +386,10 @@ packages/plugin1/plugin1/plugin1.dart processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: 'AUTHORS'), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -371,8 +404,10 @@ packages/plugin1/plugin1/plugin1.dart packages/plugin1/CHANGELOG '''), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -387,8 +422,10 @@ packages/plugin1/CHANGELOG packages/plugin1/CHANGELOG '''), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -404,8 +441,10 @@ packages/plugin1/CHANGELOG packages/plugin1/CHANGELOG '''), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -421,8 +460,10 @@ script/tool_runner.sh packages/plugin1/CHANGELOG '''), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -438,8 +479,10 @@ analysis_options.yaml packages/plugin1/CHANGELOG '''), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -455,8 +498,10 @@ packages/plugin1/CHANGELOG packages/plugin1/CHANGELOG '''), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -468,7 +513,8 @@ packages/plugin1/CHANGELOG processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: 'packages/plugin1/plugin1.dart'), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); createFakePlugin('plugin2', packagesDir); final List output = await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -491,7 +537,8 @@ packages/plugin1/plugin1.dart packages/plugin1/ios/plugin1.m '''), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); createFakePlugin('plugin2', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -507,8 +554,10 @@ packages/plugin1/plugin1.dart packages/plugin2/ios/plugin2.m '''), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); createFakePlugin('plugin3', packagesDir); await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); @@ -527,7 +576,7 @@ packages/plugin1/plugin1_platform_interface/plugin1_platform_interface.dart packages/plugin1/plugin1_web/plugin1_web.dart '''), ]; - final Directory plugin1 = + final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir.childDirectory('plugin1')); createFakePlugin('plugin2', packagesDir); createFakePlugin('plugin3', packagesDir); @@ -545,7 +594,7 @@ packages/plugin1/plugin1_web/plugin1_web.dart packages/plugin1/plugin1/plugin1.dart '''), ]; - final Directory plugin1 = + final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir.childDirectory('plugin1')); createFakePlugin('plugin1_platform_interface', packagesDir.childDirectory('plugin1')); @@ -564,7 +613,7 @@ packages/plugin2/ios/plugin2.m packages/plugin3/plugin3.dart '''), ]; - final Directory plugin1 = + final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir.childDirectory('plugin1')); createFakePlugin('plugin2', packagesDir); createFakePlugin('plugin3', packagesDir); @@ -624,7 +673,8 @@ script/tool_runner.sh processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: 'packages/a_package/lib/a_package.dart'), ]; - final Directory packageA = createFakePackage('a_package', packagesDir); + final RepositoryPackage packageA = + createFakePackage('a_package', packagesDir); createFakePlugin('b_package', packagesDir); final List output = await runCapturingPrint( runner, ['sample', '--run-on-dirty-packages']); @@ -647,8 +697,10 @@ packages/a_package/lib/a_package.dart packages/b_package/lib/src/foo.dart '''), ]; - final Directory packageA = createFakePackage('a_package', packagesDir); - final Directory packageB = createFakePackage('b_package', packagesDir); + final RepositoryPackage packageA = + createFakePackage('a_package', packagesDir); + final RepositoryPackage packageB = + createFakePackage('b_package', packagesDir); createFakePackage('c_package', packagesDir); await runCapturingPrint( runner, ['sample', '--run-on-dirty-packages']); @@ -664,7 +716,8 @@ packages/a_package/lib/a_package.dart packages/b_package/lib/src/foo.dart '''), ]; - final Directory packageA = createFakePackage('a_package', packagesDir); + final RepositoryPackage packageA = + createFakePackage('a_package', packagesDir); createFakePackage('b_package', packagesDir); createFakePackage('c_package', packagesDir); await runCapturingPrint(runner, [ @@ -686,7 +739,8 @@ packages/b_package/lib/src/foo.dart processRunner.mockProcessesForExecutable['git-rev-parse'] = [ MockProcess(stdout: 'a-branch'), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); createFakePlugin('plugin2', packagesDir); final List output = await runCapturingPrint( @@ -707,8 +761,10 @@ packages/b_package/lib/src/foo.dart processRunner.mockProcessesForExecutable['git-rev-parse'] = [ MockProcess(stdout: 'main'), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); final List output = await runCapturingPrint( runner, ['sample', '--packages-for-branch']); @@ -729,8 +785,10 @@ packages/b_package/lib/src/foo.dart processRunner.mockProcessesForExecutable['git-rev-parse'] = [ MockProcess(stdout: 'master'), ]; - final Directory plugin1 = createFakePlugin('plugin1', packagesDir); - final Directory plugin2 = createFakePlugin('plugin2', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin2 = + createFakePlugin('plugin2', packagesDir); final List output = await runCapturingPrint( runner, ['sample', '--packages-for-branch']); @@ -770,18 +828,19 @@ packages/b_package/lib/src/foo.dart group('sharding', () { test('distributes evenly when evenly divisible', () async { - final List> expectedShards = >[ - [ + final List> expectedShards = + >[ + [ createFakePackage('package1', packagesDir), createFakePackage('package2', packagesDir), createFakePackage('package3', packagesDir), ], - [ + [ createFakePackage('package4', packagesDir), createFakePackage('package5', packagesDir), createFakePackage('package6', packagesDir), ], - [ + [ createFakePackage('package7', packagesDir), createFakePackage('package8', packagesDir), createFakePackage('package9', packagesDir), @@ -807,25 +866,26 @@ packages/b_package/lib/src/foo.dart expect( localCommand.plugins, unorderedEquals(expectedShards[i] - .map((Directory packageDir) => packageDir.path) + .map((RepositoryPackage package) => package.path) .toList())); } }); test('distributes as evenly as possible when not evenly divisible', () async { - final List> expectedShards = >[ - [ + final List> expectedShards = + >[ + [ createFakePackage('package1', packagesDir), createFakePackage('package2', packagesDir), createFakePackage('package3', packagesDir), ], - [ + [ createFakePackage('package4', packagesDir), createFakePackage('package5', packagesDir), createFakePackage('package6', packagesDir), ], - [ + [ createFakePackage('package7', packagesDir), createFakePackage('package8', packagesDir), ], @@ -850,7 +910,7 @@ packages/b_package/lib/src/foo.dart expect( localCommand.plugins, unorderedEquals(expectedShards[i] - .map((Directory packageDir) => packageDir.path) + .map((RepositoryPackage package) => package.path) .toList())); } }); @@ -864,18 +924,19 @@ packages/b_package/lib/src/foo.dart // excluding some plugins from the later step shouldn't change what's tested // in each shard, as it may no longer align with what was built. test('counts excluded plugins when sharding', () async { - final List> expectedShards = >[ - [ + final List> expectedShards = + >[ + [ createFakePackage('package1', packagesDir), createFakePackage('package2', packagesDir), createFakePackage('package3', packagesDir), ], - [ + [ createFakePackage('package4', packagesDir), createFakePackage('package5', packagesDir), createFakePackage('package6', packagesDir), ], - [ + [ createFakePackage('package7', packagesDir), ], ]; @@ -903,7 +964,7 @@ packages/b_package/lib/src/foo.dart expect( localCommand.plugins, unorderedEquals(expectedShards[i] - .map((Directory packageDir) => packageDir.path) + .map((RepositoryPackage package) => package.path) .toList())); } }); diff --git a/script/tool/test/common/plugin_utils_test.dart b/script/tool/test/common/plugin_utils_test.dart index af5cb7dfe4c6..9c5ddc3f85b9 100644 --- a/script/tool/test/common/plugin_utils_test.dart +++ b/script/tool/test/common/plugin_utils_test.dart @@ -6,7 +6,6 @@ import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_plugin_tools/src/common/core.dart'; import 'package:flutter_plugin_tools/src/common/plugin_utils.dart'; -import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:test/test.dart'; import '../util.dart'; @@ -22,8 +21,7 @@ void main() { group('pluginSupportsPlatform', () { test('no platforms', () async { - final RepositoryPackage plugin = - RepositoryPackage(createFakePlugin('plugin', packagesDir)); + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir); expect(pluginSupportsPlatform(platformAndroid, plugin), isFalse); expect(pluginSupportsPlatform(platformIOS, plugin), isFalse); @@ -34,8 +32,7 @@ void main() { }); test('all platforms', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), platformIOS: const PlatformDetails(PlatformSupport.inline), @@ -43,7 +40,7 @@ void main() { platformMacOS: const PlatformDetails(PlatformSupport.inline), platformWeb: const PlatformDetails(PlatformSupport.inline), platformWindows: const PlatformDetails(PlatformSupport.inline), - })); + }); expect(pluginSupportsPlatform(platformAndroid, plugin), isTrue); expect(pluginSupportsPlatform(platformIOS, plugin), isTrue); @@ -54,13 +51,12 @@ void main() { }); test('some platforms', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), platformLinux: const PlatformDetails(PlatformSupport.inline), platformWeb: const PlatformDetails(PlatformSupport.inline), - })); + }); expect(pluginSupportsPlatform(platformAndroid, plugin), isTrue); expect(pluginSupportsPlatform(platformIOS, plugin), isFalse); @@ -71,8 +67,7 @@ void main() { }); test('inline plugins are only detected as inline', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline), platformIOS: const PlatformDetails(PlatformSupport.inline), @@ -80,7 +75,7 @@ void main() { platformMacOS: const PlatformDetails(PlatformSupport.inline), platformWeb: const PlatformDetails(PlatformSupport.inline), platformWindows: const PlatformDetails(PlatformSupport.inline), - })); + }); expect( pluginSupportsPlatform(platformAndroid, plugin, @@ -133,8 +128,7 @@ void main() { }); test('federated plugins are only detected as federated', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.federated), platformIOS: const PlatformDetails(PlatformSupport.federated), @@ -142,7 +136,7 @@ void main() { platformMacOS: const PlatformDetails(PlatformSupport.federated), platformWeb: const PlatformDetails(PlatformSupport.federated), platformWindows: const PlatformDetails(PlatformSupport.federated), - })); + }); expect( pluginSupportsPlatform(platformAndroid, plugin, @@ -197,19 +191,19 @@ void main() { group('pluginHasNativeCodeForPlatform', () { test('returns false for web', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { platformWeb: const PlatformDetails(PlatformSupport.inline), }, - )); + ); expect(pluginHasNativeCodeForPlatform(platformWeb, plugin), isFalse); }); test('returns false for a native-only plugin', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -217,7 +211,7 @@ void main() { platformMacOS: const PlatformDetails(PlatformSupport.inline), platformWindows: const PlatformDetails(PlatformSupport.inline), }, - )); + ); expect(pluginHasNativeCodeForPlatform(platformLinux, plugin), isTrue); expect(pluginHasNativeCodeForPlatform(platformMacOS, plugin), isTrue); @@ -225,7 +219,7 @@ void main() { }); test('returns true for a native+Dart plugin', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -236,7 +230,7 @@ void main() { platformWindows: const PlatformDetails(PlatformSupport.inline, hasNativeCode: true, hasDartCode: true), }, - )); + ); expect(pluginHasNativeCodeForPlatform(platformLinux, plugin), isTrue); expect(pluginHasNativeCodeForPlatform(platformMacOS, plugin), isTrue); @@ -244,7 +238,7 @@ void main() { }); test('returns false for a Dart-only plugin', () async { - final RepositoryPackage plugin = RepositoryPackage(createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -255,7 +249,7 @@ void main() { platformWindows: const PlatformDetails(PlatformSupport.inline, hasNativeCode: false, hasDartCode: true), }, - )); + ); expect(pluginHasNativeCodeForPlatform(platformLinux, plugin), isFalse); expect(pluginHasNativeCodeForPlatform(platformMacOS, plugin), isFalse); diff --git a/script/tool/test/common/repository_package_test.dart b/script/tool/test/common/repository_package_test.dart index 2d0e11475c36..dadfc8832997 100644 --- a/script/tool/test/common/repository_package_test.dart +++ b/script/tool/test/common/repository_package_test.dart @@ -4,7 +4,6 @@ import 'package:file/file.dart'; import 'package:file/memory.dart'; -import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:test/test.dart'; import '../util.dart'; @@ -97,107 +96,109 @@ void main() { group('getExamples', () { test('handles a single Flutter example', () async { - final Directory plugin = createFakePlugin('a_plugin', packagesDir); + final RepositoryPackage plugin = + createFakePlugin('a_plugin', packagesDir); - final List examples = - RepositoryPackage(plugin).getExamples().toList(); + final List examples = plugin.getExamples().toList(); expect(examples.length, 1); - expect(examples[0].path, plugin.childDirectory('example').path); + expect(examples[0].path, getExampleDir(plugin).path); }); test('handles multiple Flutter examples', () async { - final Directory plugin = createFakePlugin('a_plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir, examples: ['example1', 'example2']); - final List examples = - RepositoryPackage(plugin).getExamples().toList(); + final List examples = plugin.getExamples().toList(); expect(examples.length, 2); expect(examples[0].path, - plugin.childDirectory('example').childDirectory('example1').path); + getExampleDir(plugin).childDirectory('example1').path); expect(examples[1].path, - plugin.childDirectory('example').childDirectory('example2').path); + getExampleDir(plugin).childDirectory('example2').path); }); test('handles a single non-Flutter example', () async { - final Directory package = createFakePackage('a_package', packagesDir); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); - final List examples = - RepositoryPackage(package).getExamples().toList(); + final List examples = package.getExamples().toList(); expect(examples.length, 1); - expect(examples[0].path, package.childDirectory('example').path); + expect(examples[0].path, getExampleDir(package).path); }); test('handles multiple non-Flutter examples', () async { - final Directory package = createFakePackage('a_package', packagesDir, + final RepositoryPackage package = createFakePackage( + 'a_package', packagesDir, examples: ['example1', 'example2']); - final List examples = - RepositoryPackage(package).getExamples().toList(); + final List examples = package.getExamples().toList(); expect(examples.length, 2); expect(examples[0].path, - package.childDirectory('example').childDirectory('example1').path); + getExampleDir(package).childDirectory('example1').path); expect(examples[1].path, - package.childDirectory('example').childDirectory('example2').path); + getExampleDir(package).childDirectory('example2').path); }); }); group('federated plugin queries', () { test('all return false for a simple plugin', () { - final Directory plugin = createFakePlugin('a_plugin', packagesDir); - expect(RepositoryPackage(plugin).isFederated, false); - expect(RepositoryPackage(plugin).isAppFacing, false); - expect(RepositoryPackage(plugin).isPlatformInterface, false); - expect(RepositoryPackage(plugin).isFederated, false); + final RepositoryPackage plugin = + createFakePlugin('a_plugin', packagesDir); + expect(plugin.isFederated, false); + expect(plugin.isAppFacing, false); + expect(plugin.isPlatformInterface, false); + expect(plugin.isFederated, false); }); test('handle app-facing packages', () { - final Directory plugin = + final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir.childDirectory('a_plugin')); - expect(RepositoryPackage(plugin).isFederated, true); - expect(RepositoryPackage(plugin).isAppFacing, true); - expect(RepositoryPackage(plugin).isPlatformInterface, false); - expect(RepositoryPackage(plugin).isPlatformImplementation, false); + expect(plugin.isFederated, true); + expect(plugin.isAppFacing, true); + expect(plugin.isPlatformInterface, false); + expect(plugin.isPlatformImplementation, false); }); test('handle platform interface packages', () { - final Directory plugin = createFakePlugin('a_plugin_platform_interface', + final RepositoryPackage plugin = createFakePlugin( + 'a_plugin_platform_interface', packagesDir.childDirectory('a_plugin')); - expect(RepositoryPackage(plugin).isFederated, true); - expect(RepositoryPackage(plugin).isAppFacing, false); - expect(RepositoryPackage(plugin).isPlatformInterface, true); - expect(RepositoryPackage(plugin).isPlatformImplementation, false); + expect(plugin.isFederated, true); + expect(plugin.isAppFacing, false); + expect(plugin.isPlatformInterface, true); + expect(plugin.isPlatformImplementation, false); }); test('handle platform implementation packages', () { // A platform interface can end with anything, not just one of the known // platform names, because of cases like webview_flutter_wkwebview. - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin_foo', packagesDir.childDirectory('a_plugin')); - expect(RepositoryPackage(plugin).isFederated, true); - expect(RepositoryPackage(plugin).isAppFacing, false); - expect(RepositoryPackage(plugin).isPlatformInterface, false); - expect(RepositoryPackage(plugin).isPlatformImplementation, true); + expect(plugin.isFederated, true); + expect(plugin.isAppFacing, false); + expect(plugin.isPlatformInterface, false); + expect(plugin.isPlatformImplementation, true); }); }); group('pubspec', () { test('file', () async { - final Directory plugin = createFakePlugin('a_plugin', packagesDir); + final RepositoryPackage plugin = + createFakePlugin('a_plugin', packagesDir); - final File pubspecFile = RepositoryPackage(plugin).pubspecFile; + final File pubspecFile = plugin.pubspecFile; - expect(pubspecFile.path, plugin.childFile('pubspec.yaml').path); + expect(pubspecFile.path, plugin.directory.childFile('pubspec.yaml').path); }); test('parsing', () async { - final Directory plugin = createFakePlugin('a_plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir, examples: ['example1', 'example2']); - final Pubspec pubspec = RepositoryPackage(plugin).parsePubspec(); + final Pubspec pubspec = plugin.parsePubspec(); expect(pubspec.name, 'a_plugin'); }); @@ -205,15 +206,15 @@ void main() { group('requiresFlutter', () { test('returns true for Flutter package', () async { - final Directory package = + final RepositoryPackage package = createFakePackage('a_package', packagesDir, isFlutter: true); - expect(RepositoryPackage(package).requiresFlutter(), true); + expect(package.requiresFlutter(), true); }); test('returns false for non-Flutter package', () async { - final Directory package = + final RepositoryPackage package = createFakePackage('a_package', packagesDir, isFlutter: false); - expect(RepositoryPackage(package).requiresFlutter(), false); + expect(package.requiresFlutter(), false); }); }); } diff --git a/script/tool/test/create_all_plugins_app_command_test.dart b/script/tool/test/create_all_plugins_app_command_test.dart index 9e2ee29326d7..830dd59a8d42 100644 --- a/script/tool/test/create_all_plugins_app_command_test.dart +++ b/script/tool/test/create_all_plugins_app_command_test.dart @@ -7,7 +7,6 @@ import 'dart:io' as io; import 'package:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:file/local.dart'; -import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:flutter_plugin_tools/src/create_all_plugins_app_command.dart'; import 'package:platform/platform.dart'; import 'package:test/test.dart'; @@ -49,8 +48,7 @@ void main() { createFakePlugin('pluginc', packagesDir); await runCapturingPrint(runner, ['all-plugins-app']); - final List pubspec = - command.appDirectory.childFile('pubspec.yaml').readAsLinesSync(); + final List pubspec = command.app.pubspecFile.readAsLinesSync(); expect( pubspec, @@ -67,8 +65,7 @@ void main() { createFakePlugin('pluginc', packagesDir); await runCapturingPrint(runner, ['all-plugins-app']); - final List pubspec = - command.appDirectory.childFile('pubspec.yaml').readAsLinesSync(); + final List pubspec = command.app.pubspecFile.readAsLinesSync(); expect( pubspec, @@ -99,8 +96,7 @@ void main() { createFakePlugin('plugina', packagesDir); await runCapturingPrint(runner, ['all-plugins-app']); - final Pubspec generatedPubspec = - RepositoryPackage(command.appDirectory).parsePubspec(); + final Pubspec generatedPubspec = command.app.parsePubspec(); const String dartSdkKey = 'sdk'; expect(generatedPubspec.environment?[dartSdkKey], @@ -115,8 +111,8 @@ void main() { await runCapturingPrint(runner, ['all-plugins-app', '--output-dir=${customOutputDir.path}']); - expect(command.appDirectory.path, - customOutputDir.childDirectory('all_plugins').path); + expect( + command.app.path, customOutputDir.childDirectory('all_plugins').path); }); test('logs exclusions', () async { diff --git a/script/tool/test/custom_test_command_test.dart b/script/tool/test/custom_test_command_test.dart index bc30d9a1d2e3..54a1acf8b82b 100644 --- a/script/tool/test/custom_test_command_test.dart +++ b/script/tool/test/custom_test_command_test.dart @@ -39,7 +39,7 @@ void main() { }); test('runs both new and legacy when both are present', () async { - final Directory package = + final RepositoryPackage package = createFakePlugin('a_package', packagesDir, extraFiles: [ 'tool/run_tests.dart', 'run_tests.sh', @@ -51,7 +51,7 @@ void main() { expect( processRunner.recordedCalls, containsAll([ - ProcessCall(package.childFile('run_tests.sh').path, + ProcessCall(package.directory.childFile('run_tests.sh').path, const [], package.path), ProcessCall('dart', const ['run', 'tool/run_tests.dart'], package.path), @@ -65,7 +65,8 @@ void main() { }); test('runs when only new is present', () async { - final Directory package = createFakePlugin('a_package', packagesDir, + final RepositoryPackage package = createFakePlugin( + 'a_package', packagesDir, extraFiles: ['tool/run_tests.dart']); final List output = @@ -86,7 +87,8 @@ void main() { }); test('runs pub get before running Dart test script', () async { - final Directory package = createFakePlugin('a_package', packagesDir, + final RepositoryPackage package = createFakePlugin( + 'a_package', packagesDir, extraFiles: ['tool/run_tests.dart']); await runCapturingPrint(runner, ['custom-test']); @@ -101,7 +103,8 @@ void main() { }); test('runs when only legacy is present', () async { - final Directory package = createFakePlugin('a_package', packagesDir, + final RepositoryPackage package = createFakePlugin( + 'a_package', packagesDir, extraFiles: ['run_tests.sh']); final List output = @@ -110,7 +113,7 @@ void main() { expect( processRunner.recordedCalls, containsAll([ - ProcessCall(package.childFile('run_tests.sh').path, + ProcessCall(package.directory.childFile('run_tests.sh').path, const [], package.path), ])); @@ -189,14 +192,14 @@ void main() { }); test('fails if legacy fails', () async { - final Directory package = + final RepositoryPackage package = createFakePlugin('a_package', packagesDir, extraFiles: [ 'tool/run_tests.dart', 'run_tests.sh', ]); processRunner.mockProcessesForExecutable[ - package.childFile('run_tests.sh').path] = [ + package.directory.childFile('run_tests.sh').path] = [ MockProcess(exitCode: 1), ]; @@ -234,7 +237,7 @@ void main() { }); test('runs new and skips old when both are present', () async { - final Directory package = + final RepositoryPackage package = createFakePlugin('a_package', packagesDir, extraFiles: [ 'tool/run_tests.dart', 'run_tests.sh', @@ -258,7 +261,8 @@ void main() { }); test('runs when only new is present', () async { - final Directory package = createFakePlugin('a_package', packagesDir, + final RepositoryPackage package = createFakePlugin( + 'a_package', packagesDir, extraFiles: ['tool/run_tests.dart']); final List output = diff --git a/script/tool/test/drive_examples_command_test.dart b/script/tool/test/drive_examples_command_test.dart index 214efb420227..23318f7cd604 100644 --- a/script/tool/test/drive_examples_command_test.dart +++ b/script/tool/test/drive_examples_command_test.dart @@ -188,7 +188,7 @@ void main() { }); test('driving under folder "test_driver"', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -203,8 +203,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); setMockFlutterDevicesOutput(); final List output = @@ -311,7 +310,7 @@ void main() { test( 'driving under folder "test_driver" when targets are under "integration_test"', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -328,8 +327,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); setMockFlutterDevicesOutput(); final List output = @@ -401,7 +399,7 @@ void main() { }); test('driving on a Linux plugin', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -414,8 +412,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'drive-examples', @@ -474,7 +471,7 @@ void main() { }); test('driving on a macOS plugin', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -487,8 +484,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'drive-examples', @@ -546,7 +542,7 @@ void main() { }); test('driving a web plugin', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -559,8 +555,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'drive-examples', @@ -596,7 +591,7 @@ void main() { }); test('driving a web plugin with CHROME_EXECUTABLE', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -609,8 +604,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); mockPlatform.environment['CHROME_EXECUTABLE'] = '/path/to/chrome'; @@ -674,7 +668,7 @@ void main() { }); test('driving on a Windows plugin', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -687,8 +681,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'drive-examples', @@ -722,7 +715,7 @@ void main() { }); test('driving on an Android plugin', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -735,8 +728,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); setMockFlutterDevicesOutput(); final List output = await runCapturingPrint(runner, [ @@ -861,7 +853,7 @@ void main() { }); test('enable-experiment flag', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -876,8 +868,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); setMockFlutterDevicesOutput(); await runCapturingPrint(runner, [ @@ -1006,7 +997,7 @@ void main() { }); test('reports test failures', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -1048,8 +1039,7 @@ void main() { ]), ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); expect( processRunner.recordedCalls, orderedEquals([ @@ -1082,13 +1072,13 @@ void main() { group('packages', () { test('can be driven', () async { - final Directory package = + final RepositoryPackage package = createFakePackage('a_package', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', 'example/test_driver/integration_test.dart', 'example/web/index.html', ]); - final Directory exampleDirectory = package.childDirectory('example'); + final Directory exampleDirectory = getExampleDir(package); final List output = await runCapturingPrint(runner, [ 'drive-examples', @@ -1150,7 +1140,8 @@ void main() { }); test('drive only supported examples if there is more than one', () async { - final Directory package = createFakePackage('a_package', packagesDir, + final RepositoryPackage package = createFakePackage( + 'a_package', packagesDir, isFlutter: true, examples: [ 'with_web', @@ -1164,7 +1155,7 @@ void main() { 'example/without_web/test_driver/integration_test.dart', ]); final Directory supportedExampleDirectory = - package.childDirectory('example').childDirectory('with_web'); + getExampleDir(package).childDirectory('with_web'); final List output = await runCapturingPrint(runner, [ 'drive-examples', diff --git a/script/tool/test/federation_safety_check_command_test.dart b/script/tool/test/federation_safety_check_command_test.dart index 126aa8b41c83..015a0eb634d9 100644 --- a/script/tool/test/federation_safety_check_command_test.dart +++ b/script/tool/test/federation_safety_check_command_test.dart @@ -8,7 +8,6 @@ import 'package:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_plugin_tools/src/common/core.dart'; -import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:flutter_plugin_tools/src/federation_safety_check_command.dart'; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; @@ -55,10 +54,10 @@ void main() { }); test('skips non-plugin packages', () async { - final Directory package = createFakePackage('foo', packagesDir); + final RepositoryPackage package = createFakePackage('foo', packagesDir); final String changedFileOutput = [ - package.childDirectory('lib').childFile('foo.dart'), + package.libDirectory.childFile('foo.dart'), ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), @@ -78,10 +77,10 @@ void main() { }); test('skips unfederated plugins', () async { - final Directory package = createFakePlugin('foo', packagesDir); + final RepositoryPackage package = createFakePlugin('foo', packagesDir); final String changedFileOutput = [ - package.childDirectory('lib').childFile('foo.dart'), + package.libDirectory.childFile('foo.dart'), ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), @@ -102,11 +101,11 @@ void main() { test('skips interface packages', () async { final Directory pluginGroupDir = packagesDir.childDirectory('foo'); - final Directory platformInterface = + final RepositoryPackage platformInterface = createFakePlugin('foo_platform_interface', pluginGroupDir); final String changedFileOutput = [ - platformInterface.childDirectory('lib').childFile('foo.dart'), + platformInterface.libDirectory.childFile('foo.dart'), ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), @@ -127,15 +126,15 @@ void main() { test('allows changes to just an interface package', () async { final Directory pluginGroupDir = packagesDir.childDirectory('foo'); - final Directory platformInterface = + final RepositoryPackage platformInterface = createFakePlugin('foo_platform_interface', pluginGroupDir); createFakePlugin('foo', pluginGroupDir); createFakePlugin('foo_ios', pluginGroupDir); createFakePlugin('foo_android', pluginGroupDir); final String changedFileOutput = [ - platformInterface.childDirectory('lib').childFile('foo.dart'), - platformInterface.childFile('pubspec.yaml'), + platformInterface.libDirectory.childFile('foo.dart'), + platformInterface.pubspecFile, ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), @@ -168,14 +167,14 @@ void main() { test('allows changes to multiple non-interface packages', () async { final Directory pluginGroupDir = packagesDir.childDirectory('foo'); - final Directory appFacing = createFakePlugin('foo', pluginGroupDir); - final Directory implementation = + final RepositoryPackage appFacing = createFakePlugin('foo', pluginGroupDir); + final RepositoryPackage implementation = createFakePlugin('foo_bar', pluginGroupDir); createFakePlugin('foo_platform_interface', pluginGroupDir); final String changedFileOutput = [ - appFacing.childFile('foo.dart'), - implementation.childFile('foo.dart'), + appFacing.libDirectory.childFile('foo.dart'), + implementation.libDirectory.childFile('foo.dart'), ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), @@ -199,17 +198,17 @@ void main() { 'fails on changes to interface and non-interface packages in the same plugin', () async { final Directory pluginGroupDir = packagesDir.childDirectory('foo'); - final Directory appFacing = createFakePlugin('foo', pluginGroupDir); - final Directory implementation = + final RepositoryPackage appFacing = createFakePlugin('foo', pluginGroupDir); + final RepositoryPackage implementation = createFakePlugin('foo_bar', pluginGroupDir); - final Directory platformInterface = + final RepositoryPackage platformInterface = createFakePlugin('foo_platform_interface', pluginGroupDir); final String changedFileOutput = [ - appFacing.childFile('foo.dart'), - implementation.childFile('foo.dart'), - platformInterface.childFile('pubspec.yaml'), - platformInterface.childDirectory('lib').childFile('foo.dart'), + appFacing.libDirectory.childFile('foo.dart'), + implementation.libDirectory.childFile('foo.dart'), + platformInterface.pubspecFile, + platformInterface.libDirectory.childFile('foo.dart'), ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), @@ -244,17 +243,17 @@ void main() { test('ignores test-only changes to interface packages', () async { final Directory pluginGroupDir = packagesDir.childDirectory('foo'); - final Directory appFacing = createFakePlugin('foo', pluginGroupDir); - final Directory implementation = + final RepositoryPackage appFacing = createFakePlugin('foo', pluginGroupDir); + final RepositoryPackage implementation = createFakePlugin('foo_bar', pluginGroupDir); - final Directory platformInterface = + final RepositoryPackage platformInterface = createFakePlugin('foo_platform_interface', pluginGroupDir); final String changedFileOutput = [ - appFacing.childFile('foo.dart'), - implementation.childFile('foo.dart'), - platformInterface.childFile('pubspec.yaml'), - platformInterface.childDirectory('test').childFile('foo.dart'), + appFacing.libDirectory.childFile('foo.dart'), + implementation.libDirectory.childFile('foo.dart'), + platformInterface.pubspecFile, + platformInterface.testDirectory.childFile('foo.dart'), ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), @@ -276,27 +275,24 @@ void main() { test('ignores unpublished changes to interface packages', () async { final Directory pluginGroupDir = packagesDir.childDirectory('foo'); - final Directory appFacing = createFakePlugin('foo', pluginGroupDir); - final Directory implementation = + final RepositoryPackage appFacing = createFakePlugin('foo', pluginGroupDir); + final RepositoryPackage implementation = createFakePlugin('foo_bar', pluginGroupDir); - final Directory platformInterface = + final RepositoryPackage platformInterface = createFakePlugin('foo_platform_interface', pluginGroupDir); final String changedFileOutput = [ - appFacing.childFile('foo.dart'), - implementation.childFile('foo.dart'), - platformInterface.childFile('pubspec.yaml'), - platformInterface.childDirectory('lib').childFile('foo.dart'), + appFacing.libDirectory.childFile('foo.dart'), + implementation.libDirectory.childFile('foo.dart'), + platformInterface.pubspecFile, + platformInterface.libDirectory.childFile('foo.dart'), ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), ]; // Simulate no change to the version in the interface's pubspec.yaml. processRunner.mockProcessesForExecutable['git-show'] = [ - MockProcess( - stdout: RepositoryPackage(platformInterface) - .pubspecFile - .readAsStringSync()), + MockProcess(stdout: platformInterface.pubspecFile.readAsStringSync()), ]; final List output = @@ -315,22 +311,22 @@ void main() { test('allows things that look like mass changes, with warning', () async { final Directory pluginGroupDir = packagesDir.childDirectory('foo'); - final Directory appFacing = createFakePlugin('foo', pluginGroupDir); - final Directory implementation = + final RepositoryPackage appFacing = createFakePlugin('foo', pluginGroupDir); + final RepositoryPackage implementation = createFakePlugin('foo_bar', pluginGroupDir); - final Directory platformInterface = + final RepositoryPackage platformInterface = createFakePlugin('foo_platform_interface', pluginGroupDir); - final Directory otherPlugin1 = createFakePlugin('bar', packagesDir); - final Directory otherPlugin2 = createFakePlugin('baz', packagesDir); + final RepositoryPackage otherPlugin1 = createFakePlugin('bar', packagesDir); + final RepositoryPackage otherPlugin2 = createFakePlugin('baz', packagesDir); final String changedFileOutput = [ - appFacing.childFile('foo.dart'), - implementation.childFile('foo.dart'), - platformInterface.childFile('pubspec.yaml'), - platformInterface.childDirectory('lib').childFile('foo.dart'), - otherPlugin1.childFile('bar.dart'), - otherPlugin2.childFile('baz.dart'), + appFacing.libDirectory.childFile('foo.dart'), + implementation.libDirectory.childFile('foo.dart'), + platformInterface.pubspecFile, + platformInterface.libDirectory.childFile('foo.dart'), + otherPlugin1.libDirectory.childFile('bar.dart'), + otherPlugin2.libDirectory.childFile('baz.dart'), ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), @@ -355,11 +351,11 @@ void main() { test('handles top-level files that match federated package heuristics', () async { - final Directory plugin = createFakePlugin('foo', packagesDir); + final RepositoryPackage plugin = createFakePlugin('foo', packagesDir); final String changedFileOutput = [ // This should be picked up as a change to 'foo', and not crash. - plugin.childFile('foo_bar.baz'), + plugin.directory.childFile('foo_bar.baz'), ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), diff --git a/script/tool/test/firebase_test_lab_command_test.dart b/script/tool/test/firebase_test_lab_command_test.dart index db658e19b28e..a41409394728 100644 --- a/script/tool/test/firebase_test_lab_command_test.dart +++ b/script/tool/test/firebase_test_lab_command_test.dart @@ -40,9 +40,10 @@ void main() { runner.addCommand(command); }); - void _writeJavaTestFile(Directory pluginDir, String relativeFilePath, + void _writeJavaTestFile(RepositoryPackage plugin, String relativeFilePath, {String runnerClass = 'FlutterTestRunner'}) { - childFileWithSubcomponents(pluginDir, p.posix.split(relativeFilePath)) + childFileWithSubcomponents( + plugin.directory, p.posix.split(relativeFilePath)) .writeAsStringSync(''' @DartIntegrationTest @RunWith($runnerClass.class) @@ -60,13 +61,13 @@ public class MainActivityTest { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); Error? commandError; final List output = await runCapturingPrint( @@ -90,13 +91,13 @@ public class MainActivityTest { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); final List output = await runCapturingPrint(runner, ['firebase-test-lab']); @@ -112,22 +113,22 @@ public class MainActivityTest { test('only runs gcloud configuration once', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory plugin1Dir = + final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir, extraFiles: [ 'test/plugin_test.dart', 'example/integration_test/foo_test.dart', 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(plugin1Dir, javaTestFileRelativePath); - final Directory plugin2Dir = + _writeJavaTestFile(plugin1, javaTestFileRelativePath); + final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir, extraFiles: [ 'test/plugin_test.dart', 'example/integration_test/bar_test.dart', 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(plugin2Dir, javaTestFileRelativePath); + _writeJavaTestFile(plugin2, javaTestFileRelativePath); final List output = await runCapturingPrint(runner, [ 'firebase-test-lab', @@ -197,7 +198,7 @@ public class MainActivityTest { test('runs integration tests', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'test/plugin_test.dart', 'example/integration_test/bar_test.dart', @@ -206,7 +207,7 @@ public class MainActivityTest { 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); final List output = await runCapturingPrint(runner, [ 'firebase-test-lab', @@ -275,7 +276,7 @@ public class MainActivityTest { const List examples = ['example1', 'example2']; const String javaTestFileExampleRelativePath = 'android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: examples, extraFiles: [ for (final String example in examples) ...[ @@ -286,7 +287,7 @@ public class MainActivityTest { ]); for (final String example in examples) { _writeJavaTestFile( - pluginDir, 'example/$example/$javaTestFileExampleRelativePath'); + plugin, 'example/$example/$javaTestFileExampleRelativePath'); } final List output = await runCapturingPrint(runner, [ @@ -339,14 +340,14 @@ public class MainActivityTest { test('fails if a test fails twice', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/bar_test.dart', 'example/integration_test/foo_test.dart', 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); processRunner.mockProcessesForExecutable['gcloud'] = [ MockProcess(), // auth @@ -385,14 +386,14 @@ public class MainActivityTest { () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/bar_test.dart', 'example/integration_test/foo_test.dart', 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); processRunner.mockProcessesForExecutable['gcloud'] = [ MockProcess(), // auth @@ -454,12 +455,12 @@ public class MainActivityTest { test('fails for packages with no integration test files', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); Error? commandError; final List output = await runCapturingPrint( @@ -490,7 +491,7 @@ public class MainActivityTest { test('fails for packages with no integration_test runner', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'test/plugin_test.dart', 'example/integration_test/bar_test.dart', @@ -500,7 +501,7 @@ public class MainActivityTest { javaTestFileRelativePath, ]); // Use the wrong @RunWith annotation. - _writeJavaTestFile(pluginDir, javaTestFileRelativePath, + _writeJavaTestFile(plugin, javaTestFileRelativePath, runnerClass: 'AndroidJUnit4.class'); Error? commandError; @@ -559,12 +560,12 @@ public class MainActivityTest { test('builds if gradlew is missing', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); final List output = await runCapturingPrint(runner, [ 'firebase-test-lab', @@ -622,12 +623,12 @@ public class MainActivityTest { test('fails if building to generate gradlew fails', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); processRunner.mockProcessesForExecutable['flutter'] = [ MockProcess(exitCode: 1) // flutter build @@ -657,16 +658,17 @@ public class MainActivityTest { test('fails if assembleAndroidTest fails', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); - final String gradlewPath = pluginDir - .childDirectory('example') - .childDirectory('android') + final String gradlewPath = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android) .childFile('gradlew') .path; processRunner.mockProcessesForExecutable[gradlewPath] = [ @@ -697,16 +699,17 @@ public class MainActivityTest { test('fails if assembleDebug fails', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); - final String gradlewPath = pluginDir - .childDirectory('example') - .childDirectory('android') + final String gradlewPath = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android) .childFile('gradlew') .path; processRunner.mockProcessesForExecutable[gradlewPath] = [ @@ -741,13 +744,13 @@ public class MainActivityTest { test('experimental flag', () async { const String javaTestFileRelativePath = 'example/android/app/src/androidTest/MainActivityTest.java'; - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/integration_test/foo_test.dart', 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(pluginDir, javaTestFileRelativePath); + _writeJavaTestFile(plugin, javaTestFileRelativePath); await runCapturingPrint(runner, [ 'firebase-test-lab', diff --git a/script/tool/test/format_command_test.dart b/script/tool/test/format_command_test.dart index 6c10a7dc3209..3fa7782245a7 100644 --- a/script/tool/test/format_command_test.dart +++ b/script/tool/test/format_command_test.dart @@ -50,10 +50,10 @@ void main() { /// Returns a modified version of a list of [relativePaths] that are relative /// to [package] to instead be relative to [packagesDir]. List _getPackagesDirRelativePaths( - Directory packageDir, List relativePaths) { + RepositoryPackage package, List relativePaths) { final p.Context path = analyzeCommand.path; final String relativeBase = - path.relative(packageDir.path, from: packagesDir.path); + path.relative(package.path, from: packagesDir.path); return relativePaths .map((String relativePath) => path.join(relativeBase, relativePath)) .toList(); @@ -86,7 +86,7 @@ void main() { 'lib/src/b.dart', 'lib/src/c.dart', ]; - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, extraFiles: files, @@ -101,7 +101,7 @@ void main() { getFlutterCommand(mockPlatform), [ 'format', - ..._getPackagesDirRelativePaths(pluginDir, files) + ..._getPackagesDirRelativePaths(plugin, files) ], packagesDir.path), ])); @@ -114,7 +114,7 @@ void main() { 'lib/src/c.dart', ]; const String unformattedFile = 'lib/src/d.dart'; - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, extraFiles: [ @@ -124,7 +124,8 @@ void main() { ); final p.Context posixContext = p.posix; - childFileWithSubcomponents(pluginDir, posixContext.split(unformattedFile)) + childFileWithSubcomponents( + plugin.directory, posixContext.split(unformattedFile)) .writeAsStringSync( '// copyright bla bla\n// This file is hand-formatted.\ncode...'); @@ -137,7 +138,7 @@ void main() { getFlutterCommand(mockPlatform), [ 'format', - ..._getPackagesDirRelativePaths(pluginDir, formattedFiles) + ..._getPackagesDirRelativePaths(plugin, formattedFiles) ], packagesDir.path), ])); @@ -172,7 +173,7 @@ void main() { 'android/src/main/java/io/flutter/plugins/a_plugin/a.java', 'android/src/main/java/io/flutter/plugins/a_plugin/b.java', ]; - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, extraFiles: files, @@ -190,7 +191,7 @@ void main() { '-jar', javaFormatPath, '--replace', - ..._getPackagesDirRelativePaths(pluginDir, files) + ..._getPackagesDirRelativePaths(plugin, files) ], packagesDir.path), ])); @@ -252,7 +253,7 @@ void main() { 'android/src/main/java/io/flutter/plugins/a_plugin/a.java', 'android/src/main/java/io/flutter/plugins/a_plugin/b.java', ]; - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, extraFiles: files, @@ -270,7 +271,7 @@ void main() { '-jar', javaFormatPath, '--replace', - ..._getPackagesDirRelativePaths(pluginDir, files) + ..._getPackagesDirRelativePaths(plugin, files) ], packagesDir.path), ])); @@ -285,7 +286,7 @@ void main() { 'macos/Classes/Foo.mm', 'windows/foo_plugin.cpp', ]; - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, extraFiles: files, @@ -302,7 +303,7 @@ void main() { [ '-i', '--style=file', - ..._getPackagesDirRelativePaths(pluginDir, files) + ..._getPackagesDirRelativePaths(plugin, files) ], packagesDir.path), ])); @@ -339,7 +340,7 @@ void main() { const List files = [ 'windows/foo_plugin.cpp', ]; - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, extraFiles: files, @@ -358,7 +359,7 @@ void main() { [ '-i', '--style=file', - ..._getPackagesDirRelativePaths(pluginDir, files) + ..._getPackagesDirRelativePaths(plugin, files) ], packagesDir.path), ])); @@ -403,7 +404,7 @@ void main() { const List javaFiles = [ 'android/src/main/java/io/flutter/plugins/a_plugin/a.java' ]; - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, extraFiles: [ @@ -426,14 +427,14 @@ void main() { [ '-i', '--style=file', - ..._getPackagesDirRelativePaths(pluginDir, clangFiles) + ..._getPackagesDirRelativePaths(plugin, clangFiles) ], packagesDir.path), ProcessCall( getFlutterCommand(mockPlatform), [ 'format', - ..._getPackagesDirRelativePaths(pluginDir, dartFiles) + ..._getPackagesDirRelativePaths(plugin, dartFiles) ], packagesDir.path), ProcessCall( @@ -442,7 +443,7 @@ void main() { '-jar', javaFormatPath, '--replace', - ..._getPackagesDirRelativePaths(pluginDir, javaFiles) + ..._getPackagesDirRelativePaths(plugin, javaFiles) ], packagesDir.path), ])); diff --git a/script/tool/test/lint_android_command_test.dart b/script/tool/test/lint_android_command_test.dart index 91608d3785cc..b072946ff959 100644 --- a/script/tool/test/lint_android_command_test.dart +++ b/script/tool/test/lint_android_command_test.dart @@ -40,7 +40,7 @@ void main() { }); test('runs gradle lint', () async { - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin1', packagesDir, extraFiles: [ 'example/android/gradlew', ], platformSupport: { @@ -48,7 +48,7 @@ void main() { }); final Directory androidDir = - pluginDir.childDirectory('example').childDirectory('android'); + plugin.getExamples().first.platformDirectory(FlutterPlatform.android); final List output = await runCapturingPrint(runner, ['lint-android']); @@ -74,7 +74,7 @@ void main() { test('runs on all examples', () async { final List examples = ['example1', 'example2']; - final Directory pluginDir = createFakePlugin('plugin1', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin1', packagesDir, examples: examples, extraFiles: [ 'example/example1/android/gradlew', @@ -84,11 +84,9 @@ void main() { platformAndroid: const PlatformDetails(PlatformSupport.inline) }); - final Iterable exampleAndroidDirs = examples.map( - (String example) => pluginDir - .childDirectory('example') - .childDirectory(example) - .childDirectory('android')); + final Iterable exampleAndroidDirs = plugin.getExamples().map( + (RepositoryPackage example) => + example.platformDirectory(FlutterPlatform.android)); final List output = await runCapturingPrint(runner, ['lint-android']); @@ -136,16 +134,17 @@ void main() { }); test('fails if linting finds issues', () async { - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('plugin1', packagesDir, extraFiles: [ 'example/android/gradlew', ], platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline) }); - final String gradlewPath = pluginDir - .childDirectory('example') - .childDirectory('android') + final String gradlewPath = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android) .childFile('gradlew') .path; processRunner.mockProcessesForExecutable[gradlewPath] = [ diff --git a/script/tool/test/lint_podspecs_command_test.dart b/script/tool/test/lint_podspecs_command_test.dart index bccbec678666..516a32fa6925 100644 --- a/script/tool/test/lint_podspecs_command_test.dart +++ b/script/tool/test/lint_podspecs_command_test.dart @@ -65,7 +65,7 @@ void main() { }); test('runs pod lib lint on a podspec', () async { - final Directory plugin1Dir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin1', packagesDir, extraFiles: [ @@ -91,8 +91,8 @@ void main() { [ 'lib', 'lint', - plugin1Dir - .childDirectory('ios') + plugin + .platformDirectory(FlutterPlatform.ios) .childFile('plugin1.podspec') .path, '--configuration=Debug', @@ -106,8 +106,8 @@ void main() { [ 'lib', 'lint', - plugin1Dir - .childDirectory('ios') + plugin + .platformDirectory(FlutterPlatform.ios) .childFile('plugin1.podspec') .path, '--configuration=Debug', diff --git a/script/tool/test/list_command_test.dart b/script/tool/test/list_command_test.dart index 9e70f72e7483..c2042c26638c 100644 --- a/script/tool/test/list_command_test.dart +++ b/script/tool/test/list_command_test.dart @@ -119,17 +119,11 @@ void main() { // Create a federated plugin by creating a directory under the packages // directory with several packages underneath. - final Directory federatedPlugin = packagesDir.childDirectory('my_plugin') - ..createSync(); - final Directory clientLibrary = - federatedPlugin.childDirectory('my_plugin')..createSync(); - createFakePubspec(clientLibrary); - final Directory webLibrary = - federatedPlugin.childDirectory('my_plugin_web')..createSync(); - createFakePubspec(webLibrary); - final Directory macLibrary = - federatedPlugin.childDirectory('my_plugin_macos')..createSync(); - createFakePubspec(macLibrary); + final Directory federatedPluginDir = + packagesDir.childDirectory('my_plugin')..createSync(); + createFakePlugin('my_plugin', federatedPluginDir); + createFakePlugin('my_plugin_web', federatedPluginDir); + createFakePlugin('my_plugin_macos', federatedPluginDir); // Test without specifying `--type`. final List plugins = @@ -151,17 +145,11 @@ void main() { // Create a federated plugin by creating a directory under the packages // directory with several packages underneath. - final Directory federatedPlugin = packagesDir.childDirectory('my_plugin') - ..createSync(); - final Directory clientLibrary = - federatedPlugin.childDirectory('my_plugin')..createSync(); - createFakePubspec(clientLibrary); - final Directory webLibrary = - federatedPlugin.childDirectory('my_plugin_web')..createSync(); - createFakePubspec(webLibrary); - final Directory macLibrary = - federatedPlugin.childDirectory('my_plugin_macos')..createSync(); - createFakePubspec(macLibrary); + final Directory federatedPluginDir = + packagesDir.childDirectory('my_plugin')..createSync(); + createFakePlugin('my_plugin', federatedPluginDir); + createFakePlugin('my_plugin_web', federatedPluginDir); + createFakePlugin('my_plugin_macos', federatedPluginDir); List plugins = await runCapturingPrint( runner, ['list', '--packages=plugin1']); diff --git a/script/tool/test/make_deps_path_based_command_test.dart b/script/tool/test/make_deps_path_based_command_test.dart index da241c3d83f7..2644e814f578 100644 --- a/script/tool/test/make_deps_path_based_command_test.dart +++ b/script/tool/test/make_deps_path_based_command_test.dart @@ -7,7 +7,6 @@ import 'dart:io' as io; import 'package:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:file/memory.dart'; -import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:flutter_plugin_tools/src/make_deps_path_based_command.dart'; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; @@ -62,9 +61,9 @@ void main() { } test('no-ops for no plugins', () async { - RepositoryPackage(createFakePackage('foo', packagesDir, isFlutter: true)); - final RepositoryPackage packageBar = RepositoryPackage( - createFakePackage('bar', packagesDir, isFlutter: true)); + createFakePackage('foo', packagesDir, isFlutter: true); + final RepositoryPackage packageBar = + createFakePackage('bar', packagesDir, isFlutter: true); _addDependencies(packageBar, ['foo']); final String originalPubspecContents = packageBar.pubspecFile.readAsStringSync(); @@ -83,16 +82,15 @@ void main() { }); test('rewrites references', () async { - final RepositoryPackage simplePackage = RepositoryPackage( - createFakePackage('foo', packagesDir, isFlutter: true)); + final RepositoryPackage simplePackage = + createFakePackage('foo', packagesDir, isFlutter: true); final Directory pluginGroup = packagesDir.childDirectory('bar'); - RepositoryPackage(createFakePackage('bar_platform_interface', pluginGroup, - isFlutter: true)); + createFakePackage('bar_platform_interface', pluginGroup, isFlutter: true); final RepositoryPackage pluginImplementation = - RepositoryPackage(createFakePlugin('bar_android', pluginGroup)); + createFakePlugin('bar_android', pluginGroup); final RepositoryPackage pluginAppFacing = - RepositoryPackage(createFakePlugin('bar', pluginGroup)); + createFakePlugin('bar', pluginGroup); _addDependencies(simplePackage, [ 'bar', @@ -147,16 +145,15 @@ void main() { // This test case ensures that running CI using this command on an interim // PR that itself used this command won't fail on the rewrite step. test('running a second time no-ops without failing', () async { - final RepositoryPackage simplePackage = RepositoryPackage( - createFakePackage('foo', packagesDir, isFlutter: true)); + final RepositoryPackage simplePackage = + createFakePackage('foo', packagesDir, isFlutter: true); final Directory pluginGroup = packagesDir.childDirectory('bar'); - RepositoryPackage(createFakePackage('bar_platform_interface', pluginGroup, - isFlutter: true)); + createFakePackage('bar_platform_interface', pluginGroup, isFlutter: true); final RepositoryPackage pluginImplementation = - RepositoryPackage(createFakePlugin('bar_android', pluginGroup)); + createFakePlugin('bar_android', pluginGroup); final RepositoryPackage pluginAppFacing = - RepositoryPackage(createFakePlugin('bar', pluginGroup)); + createFakePlugin('bar', pluginGroup); _addDependencies(simplePackage, [ 'bar', @@ -192,18 +189,17 @@ void main() { group('target-dependencies-with-non-breaking-updates', () { test('no-ops for no published changes', () async { - final Directory package = createFakePackage('foo', packagesDir); + final RepositoryPackage package = createFakePackage('foo', packagesDir); final String changedFileOutput = [ - package.childFile('pubspec.yaml'), + package.pubspecFile, ].map((File file) => file.path).join('\n'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: changedFileOutput), ]; // Simulate no change to the version in the interface's pubspec.yaml. processRunner.mockProcessesForExecutable['git-show'] = [ - MockProcess( - stdout: RepositoryPackage(package).pubspecFile.readAsStringSync()), + MockProcess(stdout: package.pubspecFile.readAsStringSync()), ]; final List output = await runCapturingPrint(runner, [ @@ -244,10 +240,10 @@ void main() { test('includes bugfix version changes as targets', () async { const String newVersion = '1.0.1'; - final Directory package = + final RepositoryPackage package = createFakePackage('foo', packagesDir, version: newVersion); - final File pubspecFile = RepositoryPackage(package).pubspecFile; + final File pubspecFile = package.pubspecFile; final String changedFileOutput = [ pubspecFile, ].map((File file) => file.path).join('\n'); @@ -276,10 +272,10 @@ void main() { test('includes minor version changes to 1.0+ as targets', () async { const String newVersion = '1.1.0'; - final Directory package = + final RepositoryPackage package = createFakePackage('foo', packagesDir, version: newVersion); - final File pubspecFile = RepositoryPackage(package).pubspecFile; + final File pubspecFile = package.pubspecFile; final String changedFileOutput = [ pubspecFile, ].map((File file) => file.path).join('\n'); @@ -308,10 +304,10 @@ void main() { test('does not include major version changes as targets', () async { const String newVersion = '2.0.0'; - final Directory package = + final RepositoryPackage package = createFakePackage('foo', packagesDir, version: newVersion); - final File pubspecFile = RepositoryPackage(package).pubspecFile; + final File pubspecFile = package.pubspecFile; final String changedFileOutput = [ pubspecFile, ].map((File file) => file.path).join('\n'); @@ -340,10 +336,10 @@ void main() { test('does not include minor version changes to 0.x as targets', () async { const String newVersion = '0.8.0'; - final Directory package = + final RepositoryPackage package = createFakePackage('foo', packagesDir, version: newVersion); - final File pubspecFile = RepositoryPackage(package).pubspecFile; + final File pubspecFile = package.pubspecFile; final String changedFileOutput = [ pubspecFile, ].map((File file) => file.path).join('\n'); @@ -373,12 +369,12 @@ void main() { test('skips anything outside of the packages directory', () async { final Directory toolDir = packagesDir.parent.childDirectory('tool'); const String newVersion = '1.1.0'; - final Directory package = createFakePackage( + final RepositoryPackage package = createFakePackage( 'flutter_plugin_tools', toolDir, version: newVersion); // Simulate a minor version change so it would be a target. - final File pubspecFile = RepositoryPackage(package).pubspecFile; + final File pubspecFile = package.pubspecFile; final String changedFileOutput = [ pubspecFile, ].map((File file) => file.path).join('\n'); diff --git a/script/tool/test/native_test_command_test.dart b/script/tool/test/native_test_command_test.dart index 1069a68107c5..d420184b6125 100644 --- a/script/tool/test/native_test_command_test.dart +++ b/script/tool/test/native_test_command_test.dart @@ -57,8 +57,8 @@ final Map _kDeviceListMap = { const String _fakeCmakeCommand = 'path/to/cmake'; -void _createFakeCMakeCache(Directory pluginDir, Platform platform) { - final CMakeProject project = CMakeProject(pluginDir.childDirectory('example'), +void _createFakeCMakeCache(RepositoryPackage plugin, Platform platform) { + final CMakeProject project = CMakeProject(getExampleDir(plugin), platform: platform, buildMode: 'Release'); final File cache = project.buildDirectory.childFile('CMakeCache.txt'); cache.createSync(recursive: true); @@ -150,13 +150,12 @@ void main() { // Returns the ProcessCall to expect for build the Linux unit tests for the // given plugin. - ProcessCall _getLinuxBuildCall(Directory pluginDir) { + ProcessCall _getLinuxBuildCall(RepositoryPackage plugin) { return ProcessCall( 'cmake', [ '--build', - pluginDir - .childDirectory('example') + getExampleDir(plugin) .childDirectory('build') .childDirectory('linux') .childDirectory('x64') @@ -205,13 +204,12 @@ void main() { }); test('reports skips with no tests', () async { - final Directory pluginDirectory1 = createFakePlugin('plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ _getMockXcodebuildListProcess(['RunnerTests', 'RunnerUITests']), @@ -273,13 +271,12 @@ void main() { }); test('running with correct destination', () async { - final Directory pluginDirectory = createFakePlugin( - 'plugin', packagesDir, platformSupport: { - platformIOS: const PlatformDetails(PlatformSupport.inline) - }); + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, + platformSupport: { + platformIOS: const PlatformDetails(PlatformSupport.inline) + }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ _getMockXcodebuildListProcess( @@ -311,12 +308,11 @@ void main() { test('Not specifying --ios-destination assigns an available simulator', () async { - final Directory pluginDirectory = createFakePlugin( - 'plugin', packagesDir, platformSupport: { - platformIOS: const PlatformDetails(PlatformSupport.inline) - }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, + platformSupport: { + platformIOS: const PlatformDetails(PlatformSupport.inline) + }); + final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ MockProcess(stdout: jsonEncode(_kDeviceListMap)), // simctl @@ -382,14 +378,12 @@ void main() { }); test('runs for macOS plugin', () async { - final Directory pluginDirectory1 = createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ _getMockXcodebuildListProcess( @@ -417,7 +411,7 @@ void main() { group('Android', () { test('runs Java unit tests in Android implementation folder', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -431,8 +425,10 @@ void main() { await runCapturingPrint(runner, ['native-test', '--android']); - final Directory androidFolder = - plugin.childDirectory('example').childDirectory('android'); + final Directory androidFolder = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android); expect( processRunner.recordedCalls, @@ -447,7 +443,7 @@ void main() { }); test('runs Java unit tests in example folder', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -461,8 +457,10 @@ void main() { await runCapturingPrint(runner, ['native-test', '--android']); - final Directory androidFolder = - plugin.childDirectory('example').childDirectory('android'); + final Directory androidFolder = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android); expect( processRunner.recordedCalls, @@ -477,7 +475,7 @@ void main() { }); test('runs Java integration tests', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -492,8 +490,10 @@ void main() { await runCapturingPrint( runner, ['native-test', '--android', '--no-unit']); - final Directory androidFolder = - plugin.childDirectory('example').childDirectory('android'); + final Directory androidFolder = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android); expect( processRunner.recordedCalls, @@ -539,7 +539,7 @@ void main() { }); test('runs all tests when present', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -554,8 +554,10 @@ void main() { await runCapturingPrint(runner, ['native-test', '--android']); - final Directory androidFolder = - plugin.childDirectory('example').childDirectory('android'); + final Directory androidFolder = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android); expect( processRunner.recordedCalls, @@ -578,7 +580,7 @@ void main() { }); test('honors --no-unit', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -594,8 +596,10 @@ void main() { await runCapturingPrint( runner, ['native-test', '--android', '--no-unit']); - final Directory androidFolder = - plugin.childDirectory('example').childDirectory('android'); + final Directory androidFolder = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android); expect( processRunner.recordedCalls, @@ -613,7 +617,7 @@ void main() { }); test('honors --no-integration', () async { - final Directory plugin = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -629,8 +633,10 @@ void main() { await runCapturingPrint( runner, ['native-test', '--android', '--no-integration']); - final Directory androidFolder = - plugin.childDirectory('example').childDirectory('android'); + final Directory androidFolder = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android); expect( processRunner.recordedCalls, @@ -720,7 +726,7 @@ void main() { }); test('fails when a unit test fails', () async { - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -732,9 +738,10 @@ void main() { ], ); - final String gradlewPath = pluginDir - .childDirectory('example') - .childDirectory('android') + final String gradlewPath = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android) .childFile('gradlew') .path; processRunner.mockProcessesForExecutable[gradlewPath] = [ @@ -761,7 +768,7 @@ void main() { }); test('fails when an integration test fails', () async { - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -774,9 +781,10 @@ void main() { ], ); - final String gradlewPath = pluginDir - .childDirectory('example') - .childDirectory('android') + final String gradlewPath = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android) .childFile('gradlew') .path; processRunner.mockProcessesForExecutable[gradlewPath] = [ @@ -882,15 +890,15 @@ void main() { test('builds and runs unit tests', () async { const String testBinaryRelativePath = 'build/linux/x64/release/bar/plugin_test'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/$testBinaryRelativePath' ], platformSupport: { platformLinux: const PlatformDetails(PlatformSupport.inline), }); - _createFakeCMakeCache(pluginDirectory, mockPlatform); + _createFakeCMakeCache(plugin, mockPlatform); - final File testBinary = childFileWithSubcomponents(pluginDirectory, + final File testBinary = childFileWithSubcomponents(plugin.directory, ['example', ...testBinaryRelativePath.split('/')]); final List output = await runCapturingPrint(runner, [ @@ -910,7 +918,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getLinuxBuildCall(pluginDirectory), + _getLinuxBuildCall(plugin), ProcessCall(testBinary.path, const [], null), ])); }); @@ -920,17 +928,17 @@ void main() { 'build/linux/x64/debug/bar/plugin_test'; const String releaseTestBinaryRelativePath = 'build/linux/x64/release/bar/plugin_test'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/$debugTestBinaryRelativePath', 'example/$releaseTestBinaryRelativePath' ], platformSupport: { platformLinux: const PlatformDetails(PlatformSupport.inline), }); - _createFakeCMakeCache(pluginDirectory, mockPlatform); + _createFakeCMakeCache(plugin, mockPlatform); final File releaseTestBinary = childFileWithSubcomponents( - pluginDirectory, + plugin.directory, ['example', ...releaseTestBinaryRelativePath.split('/')]); final List output = await runCapturingPrint(runner, [ @@ -950,7 +958,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getLinuxBuildCall(pluginDirectory), + _getLinuxBuildCall(plugin), ProcessCall(releaseTestBinary.path, const [], null), ])); }); @@ -983,12 +991,11 @@ void main() { }); test('fails if there are no unit tests', () async { - final Directory pluginDirectory = createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformLinux: const PlatformDetails(PlatformSupport.inline), }); - _createFakeCMakeCache(pluginDirectory, mockPlatform); + _createFakeCMakeCache(plugin, mockPlatform); Error? commandError; final List output = await runCapturingPrint(runner, [ @@ -1010,22 +1017,22 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getLinuxBuildCall(pluginDirectory), + _getLinuxBuildCall(plugin), ])); }); test('fails if a unit test fails', () async { const String testBinaryRelativePath = 'build/linux/x64/release/bar/plugin_test'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/$testBinaryRelativePath' ], platformSupport: { platformLinux: const PlatformDetails(PlatformSupport.inline), }); - _createFakeCMakeCache(pluginDirectory, mockPlatform); + _createFakeCMakeCache(plugin, mockPlatform); - final File testBinary = childFileWithSubcomponents(pluginDirectory, + final File testBinary = childFileWithSubcomponents(plugin.directory, ['example', ...testBinaryRelativePath.split('/')]); processRunner.mockProcessesForExecutable[testBinary.path] = @@ -1051,7 +1058,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getLinuxBuildCall(pluginDirectory), + _getLinuxBuildCall(plugin), ProcessCall(testBinary.path, const [], null), ])); }); @@ -1087,14 +1094,12 @@ void main() { }); test('honors unit-only', () async { - final Directory pluginDirectory1 = createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ _getMockXcodebuildListProcess( @@ -1123,14 +1128,13 @@ void main() { }); test('honors integration-only', () async { - final Directory pluginDirectory1 = createFakePlugin( + final RepositoryPackage plugin1 = createFakePlugin( 'plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin1); processRunner.mockProcessesForExecutable['xcrun'] = [ _getMockXcodebuildListProcess( @@ -1159,14 +1163,13 @@ void main() { }); test('skips when the requested target is not present', () async { - final Directory pluginDirectory1 = createFakePlugin( + final RepositoryPackage plugin1 = createFakePlugin( 'plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin1); // Simulate a project with unit tests but no integration tests... processRunner.mockProcessesForExecutable['xcrun'] = [ @@ -1195,14 +1198,13 @@ void main() { }); test('fails if there are no unit tests', () async { - final Directory pluginDirectory1 = createFakePlugin( + final RepositoryPackage plugin1 = createFakePlugin( 'plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin1); processRunner.mockProcessesForExecutable['xcrun'] = [ _getMockXcodebuildListProcess(['RunnerUITests']), @@ -1235,14 +1237,13 @@ void main() { }); test('fails if unable to check for requested target', () async { - final Directory pluginDirectory1 = createFakePlugin( + final RepositoryPackage plugin1 = createFakePlugin( 'plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin1); processRunner.mockProcessesForExecutable['xcrun'] = [ MockProcess(exitCode: 1), // xcodebuild -list @@ -1275,7 +1276,7 @@ void main() { group('multiplatform', () { test('runs all platfroms when supported', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: [ @@ -1289,8 +1290,7 @@ void main() { }, ); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final Directory androidFolder = pluginExampleDirectory.childDirectory('android'); @@ -1334,14 +1334,12 @@ void main() { }); test('runs only macOS for a macOS plugin', () async { - final Directory pluginDirectory1 = createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ _getMockXcodebuildListProcess( @@ -1372,13 +1370,12 @@ void main() { }); test('runs only iOS for a iOS plugin', () async { - final Directory pluginDirectory = createFakePlugin( - 'plugin', packagesDir, platformSupport: { - platformIOS: const PlatformDetails(PlatformSupport.inline) - }); + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, + platformSupport: { + platformIOS: const PlatformDetails(PlatformSupport.inline) + }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ _getMockXcodebuildListProcess( @@ -1466,7 +1463,7 @@ void main() { }); test('failing one platform does not stop the tests', () async { - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -1485,9 +1482,10 @@ void main() { ]; // Simulate failing Android, but not iOS. - final String gradlewPath = pluginDir - .childDirectory('example') - .childDirectory('android') + final String gradlewPath = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android) .childFile('gradlew') .path; processRunner.mockProcessesForExecutable[gradlewPath] = [ @@ -1522,7 +1520,7 @@ void main() { }); test('failing multiple platforms reports multiple failures', () async { - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, platformSupport: { @@ -1536,9 +1534,10 @@ void main() { ); // Simulate failing Android. - final String gradlewPath = pluginDir - .childDirectory('example') - .childDirectory('android') + final String gradlewPath = plugin + .getExamples() + .first + .platformDirectory(FlutterPlatform.android) .childFile('gradlew') .path; processRunner.mockProcessesForExecutable[gradlewPath] = [ @@ -1599,13 +1598,12 @@ void main() { // Returns the ProcessCall to expect for build the Windows unit tests for // the given plugin. - ProcessCall _getWindowsBuildCall(Directory pluginDir) { + ProcessCall _getWindowsBuildCall(RepositoryPackage plugin) { return ProcessCall( _fakeCmakeCommand, [ '--build', - pluginDir - .childDirectory('example') + getExampleDir(plugin) .childDirectory('build') .childDirectory('windows') .path, @@ -1621,15 +1619,15 @@ void main() { test('runs unit tests', () async { const String testBinaryRelativePath = 'build/windows/Debug/bar/plugin_test.exe'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/$testBinaryRelativePath' ], platformSupport: { platformWindows: const PlatformDetails(PlatformSupport.inline), }); - _createFakeCMakeCache(pluginDirectory, mockPlatform); + _createFakeCMakeCache(plugin, mockPlatform); - final File testBinary = childFileWithSubcomponents(pluginDirectory, + final File testBinary = childFileWithSubcomponents(plugin.directory, ['example', ...testBinaryRelativePath.split('/')]); final List output = await runCapturingPrint(runner, [ @@ -1649,7 +1647,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getWindowsBuildCall(pluginDirectory), + _getWindowsBuildCall(plugin), ProcessCall(testBinary.path, const [], null), ])); }); @@ -1659,16 +1657,17 @@ void main() { 'build/windows/Debug/bar/plugin_test.exe'; const String releaseTestBinaryRelativePath = 'build/windows/Release/bar/plugin_test.exe'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/$debugTestBinaryRelativePath', 'example/$releaseTestBinaryRelativePath' ], platformSupport: { platformWindows: const PlatformDetails(PlatformSupport.inline), }); - _createFakeCMakeCache(pluginDirectory, mockPlatform); + _createFakeCMakeCache(plugin, mockPlatform); - final File debugTestBinary = childFileWithSubcomponents(pluginDirectory, + final File debugTestBinary = childFileWithSubcomponents( + plugin.directory, ['example', ...debugTestBinaryRelativePath.split('/')]); final List output = await runCapturingPrint(runner, [ @@ -1688,7 +1687,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getWindowsBuildCall(pluginDirectory), + _getWindowsBuildCall(plugin), ProcessCall(debugTestBinary.path, const [], null), ])); }); @@ -1721,12 +1720,11 @@ void main() { }); test('fails if there are no unit tests', () async { - final Directory pluginDirectory = createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformWindows: const PlatformDetails(PlatformSupport.inline), }); - _createFakeCMakeCache(pluginDirectory, mockPlatform); + _createFakeCMakeCache(plugin, mockPlatform); Error? commandError; final List output = await runCapturingPrint(runner, [ @@ -1748,22 +1746,22 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getWindowsBuildCall(pluginDirectory), + _getWindowsBuildCall(plugin), ])); }); test('fails if a unit test fails', () async { const String testBinaryRelativePath = 'build/windows/Debug/bar/plugin_test.exe'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, extraFiles: [ 'example/$testBinaryRelativePath' ], platformSupport: { platformWindows: const PlatformDetails(PlatformSupport.inline), }); - _createFakeCMakeCache(pluginDirectory, mockPlatform); + _createFakeCMakeCache(plugin, mockPlatform); - final File testBinary = childFileWithSubcomponents(pluginDirectory, + final File testBinary = childFileWithSubcomponents(plugin.directory, ['example', ...testBinaryRelativePath.split('/')]); processRunner.mockProcessesForExecutable[testBinary.path] = @@ -1789,7 +1787,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getWindowsBuildCall(pluginDirectory), + _getWindowsBuildCall(plugin), ProcessCall(testBinary.path, const [], null), ])); }); diff --git a/script/tool/test/publish_check_command_test.dart b/script/tool/test/publish_check_command_test.dart index c5527af21736..e6c5b9cdebc5 100644 --- a/script/tool/test/publish_check_command_test.dart +++ b/script/tool/test/publish_check_command_test.dart @@ -44,9 +44,9 @@ void main() { }); test('publish check all packages', () async { - final Directory plugin1Dir = + final RepositoryPackage plugin1 = createFakePlugin('plugin_tools_test_package_a', packagesDir); - final Directory plugin2Dir = + final RepositoryPackage plugin2 = createFakePlugin('plugin_tools_test_package_b', packagesDir); await runCapturingPrint(runner, ['publish-check']); @@ -57,11 +57,11 @@ void main() { ProcessCall( 'flutter', const ['pub', 'publish', '--', '--dry-run'], - plugin1Dir.path), + plugin1.path), ProcessCall( 'flutter', const ['pub', 'publish', '--', '--dry-run'], - plugin2Dir.path), + plugin2.path), ])); }); @@ -89,8 +89,8 @@ void main() { }); test('fail on bad pubspec', () async { - final Directory dir = createFakePlugin('c', packagesDir); - await dir.childFile('pubspec.yaml').writeAsString('bad-yaml'); + final RepositoryPackage package = createFakePlugin('c', packagesDir); + await package.pubspecFile.writeAsString('bad-yaml'); Error? commandError; final List output = await runCapturingPrint( @@ -108,8 +108,9 @@ void main() { }); test('fails if AUTHORS is missing', () async { - final Directory package = createFakePackage('a_package', packagesDir); - package.childFile('AUTHORS').delete(); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + package.authorsFile.delete(); Error? commandError; final List output = await runCapturingPrint( @@ -128,12 +129,12 @@ void main() { }); test('does not require AUTHORS for third-party', () async { - final Directory package = createFakePackage( + final RepositoryPackage package = createFakePackage( 'a_package', packagesDir.parent .childDirectory('third_party') .childDirectory('packages')); - package.childFile('AUTHORS').delete(); + package.authorsFile.delete(); final List output = await runCapturingPrint(runner, ['publish-check']); @@ -372,11 +373,11 @@ void main() { ); runner.addCommand(command); - final Directory plugin1Dir = + final RepositoryPackage plugin = createFakePlugin('no_publish_a', packagesDir, version: '0.1.0'); createFakePlugin('no_publish_b', packagesDir, version: '0.2.0'); - await plugin1Dir.childFile('pubspec.yaml').writeAsString('bad-yaml'); + await plugin.pubspecFile.writeAsString('bad-yaml'); bool hasError = false; final List output = await runCapturingPrint( diff --git a/script/tool/test/publish_plugin_command_test.dart b/script/tool/test/publish_plugin_command_test.dart index 2cb3fc25af2e..857828ab9306 100644 --- a/script/tool/test/publish_plugin_command_test.dart +++ b/script/tool/test/publish_plugin_command_test.dart @@ -83,11 +83,11 @@ void main() { group('Initial validation', () { test('refuses to proceed with dirty files', () async { - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('foo', packagesDir, examples: []); processRunner.mockProcessesForExecutable['git-status'] = [ - MockProcess(stdout: '?? ${pluginDir.childFile('tmp').path}\n') + MockProcess(stdout: '?? ${plugin.directory.childFile('tmp').path}\n') ]; Error? commandError; @@ -183,7 +183,7 @@ void main() { }); test('forwards --pub-publish-flags to pub publish', () async { - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('foo', packagesDir, examples: []); await runCapturingPrint(commandRunner, [ @@ -198,14 +198,14 @@ void main() { contains(ProcessCall( flutterCommand, const ['pub', 'publish', '--dry-run', '--server=bar'], - pluginDir.path))); + plugin.path))); }); test( '--skip-confirmation flag automatically adds --force to --pub-publish-flags', () async { _createMockCredentialFile(); - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('foo', packagesDir, examples: []); await runCapturingPrint(commandRunner, [ @@ -221,7 +221,7 @@ void main() { contains(ProcessCall( flutterCommand, const ['pub', 'publish', '--server=bar', '--force'], - pluginDir.path))); + plugin.path))); }); test('throws if pub publish fails', () async { @@ -249,7 +249,7 @@ void main() { }); test('publish, dry run', () async { - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('foo', packagesDir, examples: []); final List output = @@ -268,7 +268,7 @@ void main() { containsAllInOrder([ contains('=============== DRY RUN ==============='), contains('Running for foo'), - contains('Running `pub publish ` in ${pluginDir.path}...'), + contains('Running `pub publish ` in ${plugin.path}...'), contains('Tagging release foo-v0.0.1...'), contains('Pushing tag to upstream...'), contains('Published foo successfully!'), @@ -386,7 +386,7 @@ void main() { }); test('to upstream by default, dry run', () async { - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('foo', packagesDir, examples: []); mockStdin.readLineOutput = 'y'; @@ -402,7 +402,7 @@ void main() { output, containsAllInOrder([ contains('=============== DRY RUN ==============='), - contains('Running `pub publish ` in ${pluginDir.path}...'), + contains('Running `pub publish ` in ${plugin.path}...'), contains('Tagging release foo-v0.0.1...'), contains('Pushing tag to upstream...'), contains('Published foo successfully!'), @@ -447,16 +447,17 @@ void main() { }; // Non-federated - final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); // federated - final Directory pluginDir2 = createFakePlugin( + final RepositoryPackage plugin2 = createFakePlugin( 'plugin2', packagesDir.childDirectory('plugin2'), ); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess( - stdout: '${pluginDir1.childFile('pubspec.yaml').path}\n' - '${pluginDir2.childFile('pubspec.yaml').path}\n') + stdout: '${plugin1.pubspecFile.path}\n' + '${plugin2.pubspecFile.path}\n') ]; mockStdin.readLineOutput = 'y'; @@ -468,8 +469,8 @@ void main() { containsAllInOrder([ contains( 'Publishing all packages that have changed relative to "HEAD~"'), - contains('Running `pub publish ` in ${pluginDir1.path}...'), - contains('Running `pub publish ` in ${pluginDir2.path}...'), + contains('Running `pub publish ` in ${plugin1.path}...'), + contains('Running `pub publish ` in ${plugin2.path}...'), contains('plugin1 - \x1B[32mpublished\x1B[0m'), contains('plugin2/plugin2 - \x1B[32mpublished\x1B[0m'), ])); @@ -503,9 +504,10 @@ void main() { // The existing plugin. createFakePlugin('plugin0', packagesDir); // Non-federated - final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); // federated - final Directory pluginDir2 = + final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir.childDirectory('plugin2')); // Git results for plugin0 having been released already, and plugin1 and @@ -515,8 +517,8 @@ void main() { ]; processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess( - stdout: '${pluginDir1.childFile('pubspec.yaml').path}\n' - '${pluginDir2.childFile('pubspec.yaml').path}\n') + stdout: '${plugin1.pubspecFile.path}\n' + '${plugin2.pubspecFile.path}\n') ]; mockStdin.readLineOutput = 'y'; @@ -527,8 +529,8 @@ void main() { expect( output, containsAllInOrder([ - 'Running `pub publish ` in ${pluginDir1.path}...\n', - 'Running `pub publish ` in ${pluginDir2.path}...\n', + 'Running `pub publish ` in ${plugin1.path}...\n', + 'Running `pub publish ` in ${plugin2.path}...\n', ])); expect( processRunner.recordedCalls, @@ -552,15 +554,16 @@ void main() { }; // Non-federated - final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); // federated - final Directory pluginDir2 = + final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir.childDirectory('plugin2')); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess( - stdout: '${pluginDir1.childFile('pubspec.yaml').path}\n' - '${pluginDir2.childFile('pubspec.yaml').path}\n') + stdout: '${plugin1.pubspecFile.path}\n' + '${plugin2.pubspecFile.path}\n') ]; mockStdin.readLineOutput = 'y'; @@ -576,11 +579,11 @@ void main() { output, containsAllInOrder([ contains('=============== DRY RUN ==============='), - contains('Running `pub publish ` in ${pluginDir1.path}...'), + contains('Running `pub publish ` in ${plugin1.path}...'), contains('Tagging release plugin1-v0.0.1...'), contains('Pushing tag to upstream...'), contains('Published plugin1 successfully!'), - contains('Running `pub publish ` in ${pluginDir2.path}...'), + contains('Running `pub publish ` in ${plugin2.path}...'), contains('Tagging release plugin2-v0.0.1...'), contains('Pushing tag to upstream...'), contains('Published plugin2 successfully!'), @@ -603,17 +606,17 @@ void main() { }; // Non-federated - final Directory pluginDir1 = + final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir, version: '0.0.2'); // federated - final Directory pluginDir2 = createFakePlugin( + final RepositoryPackage plugin2 = createFakePlugin( 'plugin2', packagesDir.childDirectory('plugin2'), version: '0.0.2'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess( - stdout: '${pluginDir1.childFile('pubspec.yaml').path}\n' - '${pluginDir2.childFile('pubspec.yaml').path}\n') + stdout: '${plugin1.pubspecFile.path}\n' + '${plugin2.pubspecFile.path}\n') ]; mockStdin.readLineOutput = 'y'; @@ -623,9 +626,9 @@ void main() { expect( output2, containsAllInOrder([ - contains('Running `pub publish ` in ${pluginDir1.path}...'), + contains('Running `pub publish ` in ${plugin1.path}...'), contains('Published plugin1 successfully!'), - contains('Running `pub publish ` in ${pluginDir2.path}...'), + contains('Running `pub publish ` in ${plugin2.path}...'), contains('Published plugin2 successfully!'), ])); expect( @@ -652,17 +655,17 @@ void main() { }; // Non-federated - final Directory pluginDir1 = + final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir, version: '0.0.2'); // federated - final Directory pluginDir2 = + final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir.childDirectory('plugin2')); - pluginDir2.deleteSync(recursive: true); + plugin2.directory.deleteSync(recursive: true); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess( - stdout: '${pluginDir1.childFile('pubspec.yaml').path}\n' - '${pluginDir2.childFile('pubspec.yaml').path}\n') + stdout: '${plugin1.pubspecFile.path}\n' + '${plugin2.pubspecFile.path}\n') ]; mockStdin.readLineOutput = 'y'; @@ -672,7 +675,7 @@ void main() { expect( output2, containsAllInOrder([ - contains('Running `pub publish ` in ${pluginDir1.path}...'), + contains('Running `pub publish ` in ${plugin1.path}...'), contains('Published plugin1 successfully!'), contains( 'The pubspec file for plugin2/plugin2 does not exist, so no publishing will happen.\nSafe to ignore if the package is deleted in this commit.\n'), @@ -698,17 +701,17 @@ void main() { }; // Non-federated - final Directory pluginDir1 = + final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir, version: '0.0.2'); // federated - final Directory pluginDir2 = createFakePlugin( + final RepositoryPackage plugin2 = createFakePlugin( 'plugin2', packagesDir.childDirectory('plugin2'), version: '0.0.2'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess( - stdout: '${pluginDir1.childFile('pubspec.yaml').path}\n' - '${pluginDir2.childFile('pubspec.yaml').path}\n') + stdout: '${plugin1.pubspecFile.path}\n' + '${plugin2.pubspecFile.path}\n') ]; processRunner.mockProcessesForExecutable['git-tag'] = [ MockProcess( @@ -748,17 +751,17 @@ void main() { }; // Non-federated - final Directory pluginDir1 = + final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir, version: '0.0.2'); // federated - final Directory pluginDir2 = createFakePlugin( + final RepositoryPackage plugin2 = createFakePlugin( 'plugin2', packagesDir.childDirectory('plugin2'), version: '0.0.2'); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess( - stdout: '${pluginDir1.childFile('pubspec.yaml').path}\n' - '${pluginDir2.childFile('pubspec.yaml').path}\n') + stdout: '${plugin1.pubspecFile.path}\n' + '${plugin2.pubspecFile.path}\n') ]; Error? commandError; @@ -785,15 +788,16 @@ void main() { test('No version change does not release any plugins', () async { // Non-federated - final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir); + final RepositoryPackage plugin1 = + createFakePlugin('plugin1', packagesDir); // federated - final Directory pluginDir2 = + final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir.childDirectory('plugin2')); processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess( - stdout: '${pluginDir1.childFile('plugin1.dart').path}\n' - '${pluginDir2.childFile('plugin2.dart').path}\n') + stdout: '${plugin1.libDirectory.childFile('plugin1.dart').path}\n' + '${plugin2.libDirectory.childFile('plugin2.dart').path}\n') ]; final List output = await runCapturingPrint(commandRunner, @@ -812,10 +816,10 @@ void main() { 'versions': [], }; - final Directory flutterPluginTools = + final RepositoryPackage flutterPluginTools = createFakePlugin('flutter_plugin_tools', packagesDir); processRunner.mockProcessesForExecutable['git-diff'] = [ - MockProcess(stdout: flutterPluginTools.childFile('pubspec.yaml').path) + MockProcess(stdout: flutterPluginTools.pubspecFile.path) ]; final List output = await runCapturingPrint(commandRunner, diff --git a/script/tool/test/pubspec_check_command_test.dart b/script/tool/test/pubspec_check_command_test.dart index 30b6ab6004e7..89bb98abd80c 100644 --- a/script/tool/test/pubspec_check_command_test.dart +++ b/script/tool/test/pubspec_check_command_test.dart @@ -146,9 +146,9 @@ void main() { }); test('passes for a plugin following conventions', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -157,10 +157,7 @@ ${_devDependenciesSection()} ${_falseSecretsSection()} '''); - pluginDirectory - .childDirectory('example') - .childFile('pubspec.yaml') - .writeAsStringSync(''' + plugin.getExamples().first.pubspecFile.writeAsStringSync(''' ${_headerSection( 'plugin_example', publishable: false, @@ -187,10 +184,10 @@ ${_flutterSection()} }); test('passes for a Flutter package following conventions', () async { - final Directory packageDirectory = + final RepositoryPackage package = createFakePackage('a_package', packagesDir); - packageDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + package.pubspecFile.writeAsStringSync(''' ${_headerSection('a_package')} ${_environmentSection()} ${_dependenciesSection()} @@ -199,10 +196,7 @@ ${_flutterSection()} ${_falseSecretsSection()} '''); - packageDirectory - .childDirectory('example') - .childFile('pubspec.yaml') - .writeAsStringSync(''' + package.getExamples().first.pubspecFile.writeAsStringSync(''' ${_headerSection( 'a_package', publishable: false, @@ -229,10 +223,10 @@ ${_flutterSection()} }); test('passes for a minimal package following conventions', () async { - final Directory packageDirectory = packagesDir.childDirectory('package'); - packageDirectory.createSync(recursive: true); + final RepositoryPackage package = + createFakePackage('package', packagesDir, examples: []); - packageDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + package.pubspecFile.writeAsStringSync(''' ${_headerSection('package')} ${_environmentSection()} ${_dependenciesSection()} @@ -252,10 +246,10 @@ ${_dependenciesSection()} }); test('fails when homepage is included', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, includeHomepage: true)} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -280,10 +274,10 @@ ${_devDependenciesSection()} }); test('fails when repository is missing', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, includeRepository: false)} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -307,10 +301,10 @@ ${_devDependenciesSection()} }); test('fails when homepage is given instead of repository', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, includeHomepage: true, includeRepository: false)} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -335,10 +329,10 @@ ${_devDependenciesSection()} }); test('fails when repository is incorrect', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, repositoryPackagesDirRelativePath: 'different_plugin')} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -362,10 +356,10 @@ ${_devDependenciesSection()} }); test('fails when issue tracker is missing', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, includeIssueTracker: false)} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -389,11 +383,11 @@ ${_devDependenciesSection()} }); test('fails when description is too short', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir.childDirectory('a_plugin'), examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, description: 'Too short')} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -420,10 +414,10 @@ ${_devDependenciesSection()} test( 'allows short descriptions for non-app-facing parts of federated plugins', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, description: 'Too short')} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -448,7 +442,7 @@ ${_devDependenciesSection()} }); test('fails when description is too long', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); const String description = 'This description is too long. It just goes ' @@ -456,7 +450,7 @@ ${_devDependenciesSection()} 'there is just too much here. Someone shoul really cut this down to just ' 'the core description so that search results are more useful and the ' 'package does not lose pub points.'; - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true, description: description)} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -481,10 +475,10 @@ ${_devDependenciesSection()} }); test('fails when environment section is out of order', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} ${_flutterSection(isPlugin: true)} ${_dependenciesSection()} @@ -509,10 +503,10 @@ ${_environmentSection()} }); test('fails when flutter section is out of order', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} ${_flutterSection(isPlugin: true)} ${_environmentSection()} @@ -537,10 +531,10 @@ ${_devDependenciesSection()} }); test('fails when dependencies section is out of order', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -565,9 +559,9 @@ ${_dependenciesSection()} }); test('fails when dev_dependencies section is out of order', () async { - final Directory pluginDirectory = createFakePlugin('plugin', packagesDir); + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} ${_environmentSection()} ${_devDependenciesSection()} @@ -592,10 +586,10 @@ ${_dependenciesSection()} }); test('fails when false_secrets section is out of order', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin', isPlugin: true)} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -622,11 +616,11 @@ ${_devDependenciesSection()} test('fails when an implemenation package is missing "implements"', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin_a_foo', packagesDir.childDirectory('plugin_a'), examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin_a_foo', isPlugin: true)} ${_environmentSection()} ${_flutterSection(isPlugin: true)} @@ -651,11 +645,11 @@ ${_devDependenciesSection()} test('fails when an implemenation package has the wrong "implements"', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin_a_foo', packagesDir.childDirectory('plugin_a'), examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin_a_foo', isPlugin: true)} ${_environmentSection()} ${_flutterSection(isPlugin: true, implementedPackage: 'plugin_a_foo')} @@ -680,11 +674,11 @@ ${_devDependenciesSection()} }); test('passes for a correct implemenation package', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin_a_foo', packagesDir.childDirectory('plugin_a'), examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection( 'plugin_a_foo', isPlugin: true, @@ -709,11 +703,11 @@ ${_devDependenciesSection()} }); test('fails when a "default_package" looks incorrect', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin_a', packagesDir.childDirectory('plugin_a'), examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection( 'plugin_a', isPlugin: true, @@ -749,11 +743,11 @@ ${_devDependenciesSection()} test( 'fails when a "default_package" does not have a corresponding dependency', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin_a', packagesDir.childDirectory('plugin_a'), examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection( 'plugin_a', isPlugin: true, @@ -787,11 +781,11 @@ ${_devDependenciesSection()} }); test('passes for an app-facing package without "implements"', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin_a', packagesDir.childDirectory('plugin_a'), examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection( 'plugin_a', isPlugin: true, @@ -817,11 +811,11 @@ ${_devDependenciesSection()} test('passes for a platform interface package without "implements"', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin_a_platform_interface', packagesDir.childDirectory('plugin_a'), examples: []); - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection( 'plugin_a_platform_interface', isPlugin: true, @@ -847,13 +841,13 @@ ${_devDependenciesSection()} }); test('validates some properties even for unpublished packages', () async { - final Directory pluginDirectory = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin_a_foo', packagesDir.childDirectory('plugin_a'), examples: []); // Environment section is in the wrong location. // Missing 'implements'. - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection('plugin_a_foo', isPlugin: true, publishable: false)} ${_flutterSection(isPlugin: true)} ${_dependenciesSection()} @@ -879,12 +873,12 @@ ${_environmentSection()} }); test('ignores some checks for unpublished packages', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); // Missing metadata that is only useful for published packages, such as // repository and issue tracker. - pluginDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + plugin.pubspecFile.writeAsStringSync(''' ${_headerSection( 'plugin', isPlugin: true, @@ -936,10 +930,10 @@ ${_devDependenciesSection()} }); test('repository check works', () async { - final Directory packageDirectory = + final RepositoryPackage package = createFakePackage('package', packagesDir, examples: []); - packageDirectory.childFile('pubspec.yaml').writeAsStringSync(''' + package.pubspecFile.writeAsStringSync(''' ${_headerSection('package')} ${_environmentSection()} ${_dependenciesSection()} diff --git a/script/tool/test/readme_check_command_test.dart b/script/tool/test/readme_check_command_test.dart index b6e016dccab4..f53fa06a8133 100644 --- a/script/tool/test/readme_check_command_test.dart +++ b/script/tool/test/readme_check_command_test.dart @@ -62,7 +62,7 @@ void main() { const String federatedPluginName = 'a_federated_plugin'; final Directory federatedDir = packagesDir.childDirectory(federatedPluginName); - final List packageDirectories = [ + final List packages = [ // A non-plugin package. createFakePackage('a_package', packagesDir), // Non-app-facing parts of a federated plugin. @@ -71,8 +71,8 @@ void main() { createFakePlugin('${federatedPluginName}_android', federatedDir), ]; - for (final Directory package in packageDirectories) { - package.childFile('README.md').writeAsStringSync(''' + for (final RepositoryPackage package in packages) { + package.readmeFile.writeAsStringSync(''' A very useful package. '''); } @@ -94,9 +94,10 @@ A very useful package. test('fails when non-federated plugin is missing an OS support table', () async { - final Directory pluginDir = createFakePlugin('a_plugin', packagesDir); + final RepositoryPackage plugin = + createFakePlugin('a_plugin', packagesDir); - pluginDir.childFile('README.md').writeAsStringSync(''' + plugin.readmeFile.writeAsStringSync(''' A very useful plugin. '''); @@ -118,10 +119,10 @@ A very useful plugin. test( 'fails when app-facing part of a federated plugin is missing an OS support table', () async { - final Directory pluginDir = + final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir.childDirectory('a_plugin')); - pluginDir.childFile('README.md').writeAsStringSync(''' + plugin.readmeFile.writeAsStringSync(''' A very useful plugin. '''); @@ -141,9 +142,10 @@ A very useful plugin. }); test('fails the OS support table is missing the header', () async { - final Directory pluginDir = createFakePlugin('a_plugin', packagesDir); + final RepositoryPackage plugin = + createFakePlugin('a_plugin', packagesDir); - pluginDir.childFile('README.md').writeAsStringSync(''' + plugin.readmeFile.writeAsStringSync(''' A very useful plugin. | **Support** | SDK 21+ | iOS 10+* | [See `camera_web `][1] | @@ -165,7 +167,7 @@ A very useful plugin. }); test('fails if the OS support table is missing a supported OS', () async { - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, platformSupport: { @@ -175,7 +177,7 @@ A very useful plugin. }, ); - pluginDir.childFile('README.md').writeAsStringSync(''' + plugin.readmeFile.writeAsStringSync(''' A very useful plugin. | | Android | iOS | @@ -202,7 +204,7 @@ A very useful plugin. }); test('fails if the OS support table lists an extra OS', () async { - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, platformSupport: { @@ -211,7 +213,7 @@ A very useful plugin. }, ); - pluginDir.childFile('README.md').writeAsStringSync(''' + plugin.readmeFile.writeAsStringSync(''' A very useful plugin. | | Android | iOS | Web | @@ -239,7 +241,7 @@ A very useful plugin. test('fails if the OS support table has unexpected OS formatting', () async { - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'a_plugin', packagesDir, platformSupport: { @@ -250,7 +252,7 @@ A very useful plugin. }, ); - pluginDir.childFile('README.md').writeAsStringSync(''' + plugin.readmeFile.writeAsStringSync(''' A very useful plugin. | | android | ios | MacOS | web | @@ -278,9 +280,10 @@ A very useful plugin. group('code blocks', () { test('fails on missing info string', () async { - final Directory packageDir = createFakePackage('a_package', packagesDir); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); - packageDir.childFile('README.md').writeAsStringSync(''' + package.readmeFile.writeAsStringSync(''' Example: ``` @@ -307,9 +310,10 @@ void main() { }); test('allows unknown info strings', () async { - final Directory packageDir = createFakePackage('a_package', packagesDir); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); - packageDir.childFile('README.md').writeAsStringSync(''' + package.readmeFile.writeAsStringSync(''' Example: ```someunknowninfotag @@ -331,9 +335,10 @@ A B C }); test('allows space around info strings', () async { - final Directory packageDir = createFakePackage('a_package', packagesDir); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); - packageDir.childFile('README.md').writeAsStringSync(''' + package.readmeFile.writeAsStringSync(''' Example: ``` dart @@ -355,9 +360,10 @@ A B C }); test('passes when excerpt requirement is met', () async { - final Directory packageDir = createFakePackage('a_package', packagesDir); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); - packageDir.childFile('README.md').writeAsStringSync(''' + package.readmeFile.writeAsStringSync(''' Example: @@ -379,9 +385,10 @@ A B C }); test('fails on missing excerpt tag when requested', () async { - final Directory packageDir = createFakePackage('a_package', packagesDir); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); - packageDir.childFile('README.md').writeAsStringSync(''' + package.readmeFile.writeAsStringSync(''' Example: ```dart diff --git a/script/tool/test/test_command_test.dart b/script/tool/test/test_command_test.dart index 386eaf0d345b..14a1e4a67c1f 100644 --- a/script/tool/test/test_command_test.dart +++ b/script/tool/test/test_command_test.dart @@ -40,9 +40,9 @@ void main() { }); test('runs flutter test on each plugin', () async { - final Directory plugin1Dir = createFakePlugin('plugin1', packagesDir, + final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir, extraFiles: ['test/empty_test.dart']); - final Directory plugin2Dir = createFakePlugin('plugin2', packagesDir, + final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir, extraFiles: ['test/empty_test.dart']); await runCapturingPrint(runner, ['test']); @@ -51,15 +51,15 @@ void main() { processRunner.recordedCalls, orderedEquals([ ProcessCall(getFlutterCommand(mockPlatform), - const ['test', '--color'], plugin1Dir.path), + const ['test', '--color'], plugin1.path), ProcessCall(getFlutterCommand(mockPlatform), - const ['test', '--color'], plugin2Dir.path), + const ['test', '--color'], plugin2.path), ]), ); }); test('runs flutter test on Flutter package example tests', () async { - final Directory pluginDir = createFakePlugin('a_plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir, extraFiles: [ 'test/empty_test.dart', 'example/test/an_example_test.dart' @@ -71,11 +71,9 @@ void main() { processRunner.recordedCalls, orderedEquals([ ProcessCall(getFlutterCommand(mockPlatform), - const ['test', '--color'], pluginDir.path), - ProcessCall( - getFlutterCommand(mockPlatform), - const ['test', '--color'], - pluginDir.childDirectory('example').path), + const ['test', '--color'], plugin.path), + ProcessCall(getFlutterCommand(mockPlatform), + const ['test', '--color'], getExampleDir(plugin).path), ]), ); }); @@ -110,7 +108,7 @@ void main() { test('skips testing plugins without test directory', () async { createFakePlugin('plugin1', packagesDir); - final Directory plugin2Dir = createFakePlugin('plugin2', packagesDir, + final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir, extraFiles: ['test/empty_test.dart']); await runCapturingPrint(runner, ['test']); @@ -119,15 +117,15 @@ void main() { processRunner.recordedCalls, orderedEquals([ ProcessCall(getFlutterCommand(mockPlatform), - const ['test', '--color'], plugin2Dir.path), + const ['test', '--color'], plugin2.path), ]), ); }); test('runs dart run test on non-Flutter packages', () async { - final Directory pluginDir = createFakePlugin('a', packagesDir, + final RepositoryPackage plugin = createFakePlugin('a', packagesDir, extraFiles: ['test/empty_test.dart']); - final Directory packageDir = createFakePackage('b', packagesDir, + final RepositoryPackage package = createFakePackage('b', packagesDir, extraFiles: ['test/empty_test.dart']); await runCapturingPrint( @@ -139,34 +137,34 @@ void main() { ProcessCall( getFlutterCommand(mockPlatform), const ['test', '--color', '--enable-experiment=exp1'], - pluginDir.path), - ProcessCall('dart', const ['pub', 'get'], packageDir.path), + plugin.path), + ProcessCall('dart', const ['pub', 'get'], package.path), ProcessCall( 'dart', const ['run', '--enable-experiment=exp1', 'test'], - packageDir.path), + package.path), ]), ); }); test('runs dart run test on non-Flutter package examples', () async { - final Directory packageDir = createFakePackage('a_package', packagesDir, - extraFiles: [ - 'test/empty_test.dart', - 'example/test/an_example_test.dart' - ]); + final RepositoryPackage package = createFakePackage( + 'a_package', packagesDir, extraFiles: [ + 'test/empty_test.dart', + 'example/test/an_example_test.dart' + ]); await runCapturingPrint(runner, ['test']); expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall('dart', const ['pub', 'get'], packageDir.path), - ProcessCall('dart', const ['run', 'test'], packageDir.path), + ProcessCall('dart', const ['pub', 'get'], package.path), + ProcessCall('dart', const ['run', 'test'], package.path), ProcessCall('dart', const ['pub', 'get'], - packageDir.childDirectory('example').path), + getExampleDir(package).path), ProcessCall('dart', const ['run', 'test'], - packageDir.childDirectory('example').path), + getExampleDir(package).path), ]), ); }); @@ -220,7 +218,7 @@ void main() { }); test('runs on Chrome for web plugins', () async { - final Directory pluginDir = createFakePlugin( + final RepositoryPackage plugin = createFakePlugin( 'plugin', packagesDir, extraFiles: ['test/empty_test.dart'], @@ -237,15 +235,15 @@ void main() { ProcessCall( getFlutterCommand(mockPlatform), const ['test', '--color', '--platform=chrome'], - pluginDir.path), + plugin.path), ]), ); }); test('enable-experiment flag', () async { - final Directory pluginDir = createFakePlugin('a', packagesDir, + final RepositoryPackage plugin = createFakePlugin('a', packagesDir, extraFiles: ['test/empty_test.dart']); - final Directory packageDir = createFakePackage('b', packagesDir, + final RepositoryPackage package = createFakePackage('b', packagesDir, extraFiles: ['test/empty_test.dart']); await runCapturingPrint( @@ -257,12 +255,12 @@ void main() { ProcessCall( getFlutterCommand(mockPlatform), const ['test', '--color', '--enable-experiment=exp1'], - pluginDir.path), - ProcessCall('dart', const ['pub', 'get'], packageDir.path), + plugin.path), + ProcessCall('dart', const ['pub', 'get'], package.path), ProcessCall( 'dart', const ['run', '--enable-experiment=exp1', 'test'], - packageDir.path), + package.path), ]), ); }); diff --git a/script/tool/test/update_excerpts_command_test.dart b/script/tool/test/update_excerpts_command_test.dart index 30189cf23a00..5c1d74444eea 100644 --- a/script/tool/test/update_excerpts_command_test.dart +++ b/script/tool/test/update_excerpts_command_test.dart @@ -8,7 +8,6 @@ import 'package:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_plugin_tools/src/common/core.dart'; -import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:flutter_plugin_tools/src/update_excerpts_command.dart'; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; @@ -42,9 +41,9 @@ void main() { }); test('runs pub get before running scripts', () async { - final Directory package = createFakePlugin('a_package', packagesDir, + final RepositoryPackage package = createFakePlugin('a_package', packagesDir, extraFiles: ['example/build.excerpt.yaml']); - final Directory example = package.childDirectory('example'); + final Directory example = getExampleDir(package); await runCapturingPrint(runner, ['update-excerpts']); @@ -69,9 +68,9 @@ void main() { }); test('runs when config is present', () async { - final Directory package = createFakePlugin('a_package', packagesDir, + final RepositoryPackage package = createFakePlugin('a_package', packagesDir, extraFiles: ['example/build.excerpt.yaml']); - final Directory example = package.childDirectory('example'); + final Directory example = getExampleDir(package); final List output = await runCapturingPrint(runner, ['update-excerpts']); @@ -128,7 +127,7 @@ void main() { }); test('restores pubspec even if running the script fails', () async { - final Directory package = createFakePlugin('a_package', packagesDir, + final RepositoryPackage package = createFakePlugin('a_package', packagesDir, extraFiles: ['example/build.excerpt.yaml']); processRunner.mockProcessesForExecutable['dart'] = [ @@ -152,11 +151,8 @@ void main() { ' Unable to get script dependencies') ])); - final String examplePubspecContent = RepositoryPackage(package) - .getExamples() - .first - .pubspecFile - .readAsStringSync(); + final String examplePubspecContent = + package.getExamples().first.pubspecFile.readAsStringSync(); expect(examplePubspecContent, isNot(contains('code_excerpter'))); expect(examplePubspecContent, isNot(contains('code_excerpt_updater'))); }); diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index 8a2bf099cc8a..5c38bd5f7033 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -13,6 +13,7 @@ import 'package:flutter_plugin_tools/src/common/core.dart'; import 'package:flutter_plugin_tools/src/common/file_utils.dart'; import 'package:flutter_plugin_tools/src/common/plugin_utils.dart'; import 'package:flutter_plugin_tools/src/common/process_runner.dart'; +import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:platform/platform.dart'; @@ -20,6 +21,8 @@ import 'package:quiver/collection.dart'; import 'mocks.dart'; +export 'package:flutter_plugin_tools/src/common/repository_package.dart'; + /// Returns the exe name that command will use when running Flutter on /// [platform]. String getFlutterCommand(Platform platform) => @@ -65,6 +68,20 @@ class PlatformDetails { final bool hasDartCode; } +/// Returns the 'example' directory for [package]. +/// +/// This is deliberately not a method on [RepositoryPackage] since actual tool +/// code should essentially never need this, and instead be using +/// [RepositoryPackage.getExamples] to avoid assuming there's a single example +/// directory. However, needing to construct paths with the example directory +/// is very common in test code. +/// +/// This returns a Directory rather than a RepositoryPackage because there is no +/// guarantee that the returned directory is a package. +Directory getExampleDir(RepositoryPackage package) { + return package.directory.childDirectory('example'); +} + /// Creates a plugin package with the given [name] in [packagesDirectory]. /// /// [platformSupport] is a map of platform string to the support details for @@ -72,8 +89,7 @@ class PlatformDetails { /// /// [extraFiles] is an optional list of plugin-relative paths, using Posix /// separators, of extra files to create in the plugin. -// TODO(stuartmorgan): Convert the return to a RepositoryPackage. -Directory createFakePlugin( +RepositoryPackage createFakePlugin( String name, Directory parentDirectory, { List examples = const ['example'], @@ -83,7 +99,7 @@ Directory createFakePlugin( String? version = '0.0.1', String flutterConstraint = '>=2.5.0', }) { - final Directory pluginDirectory = createFakePackage(name, parentDirectory, + final RepositoryPackage package = createFakePackage(name, parentDirectory, isFlutter: true, examples: examples, extraFiles: extraFiles, @@ -91,7 +107,7 @@ Directory createFakePlugin( flutterConstraint: flutterConstraint); createFakePubspec( - pluginDirectory, + package, name: name, isFlutter: true, isPlugin: true, @@ -100,7 +116,7 @@ Directory createFakePlugin( flutterConstraint: flutterConstraint, ); - return pluginDirectory; + return package; } /// Creates a plugin package with the given [name] in [packagesDirectory]. @@ -113,8 +129,7 @@ Directory createFakePlugin( /// /// If non-null, [directoryName] will be used for the directory instead of /// [name]. -// TODO(stuartmorgan): Convert the return to a RepositoryPackage. -Directory createFakePackage( +RepositoryPackage createFakePackage( String name, Directory parentDirectory, { List examples = const ['example'], @@ -126,26 +141,26 @@ Directory createFakePackage( String? directoryName, String? publishTo, }) { - final Directory packageDirectory = - parentDirectory.childDirectory(directoryName ?? name); - packageDirectory.createSync(recursive: true); + final RepositoryPackage package = + RepositoryPackage(parentDirectory.childDirectory(directoryName ?? name)); + package.directory.createSync(recursive: true); - packageDirectory.childDirectory('lib').createSync(); - createFakePubspec(packageDirectory, + package.libDirectory.createSync(); + createFakePubspec(package, name: name, isFlutter: isFlutter, version: version, flutterConstraint: flutterConstraint); if (includeCommonFiles) { - createFakeCHANGELOG(packageDirectory, ''' + createFakeCHANGELOG(package, ''' ## $version * Some changes. '''); - createFakeAuthors(packageDirectory); + createFakeAuthors(package); } if (examples.length == 1) { - createFakePackage('${name}_example', packageDirectory, + createFakePackage('${name}_example', package.directory, directoryName: examples.first, examples: [], includeCommonFiles: false, @@ -153,8 +168,7 @@ Directory createFakePackage( publishTo: 'none', flutterConstraint: flutterConstraint); } else if (examples.isNotEmpty) { - final Directory examplesDirectory = - packageDirectory.childDirectory('example')..createSync(); + final Directory examplesDirectory = getExampleDir(package)..createSync(); for (final String exampleName in examples) { createFakePackage(exampleName, examplesDirectory, examples: [], @@ -167,16 +181,16 @@ Directory createFakePackage( final p.Context posixContext = p.posix; for (final String file in extraFiles) { - childFileWithSubcomponents(packageDirectory, posixContext.split(file)) + childFileWithSubcomponents(package.directory, posixContext.split(file)) .createSync(recursive: true); } - return packageDirectory; + return package; } -void createFakeCHANGELOG(Directory parent, String texts) { - parent.childFile('CHANGELOG.md').createSync(); - parent.childFile('CHANGELOG.md').writeAsStringSync(texts); +void createFakeCHANGELOG(RepositoryPackage package, String texts) { + package.changelogFile.createSync(); + package.changelogFile.writeAsStringSync(texts); } /// Creates a `pubspec.yaml` file with a flutter dependency. @@ -185,7 +199,7 @@ void createFakeCHANGELOG(Directory parent, String texts) { /// that platform. If empty, no `plugin` entry will be created unless `isPlugin` /// is set to `true`. void createFakePubspec( - Directory parent, { + RepositoryPackage package, { String name = 'fake_package', bool isFlutter = true, bool isPlugin = false, @@ -249,14 +263,13 @@ $dependenciesSection $pluginSection '''; - parent.childFile('pubspec.yaml').createSync(); - parent.childFile('pubspec.yaml').writeAsStringSync(yaml); + package.pubspecFile.createSync(); + package.pubspecFile.writeAsStringSync(yaml); } -void createFakeAuthors(Directory parent) { - final File authorsFile = parent.childFile('AUTHORS'); - authorsFile.createSync(); - authorsFile.writeAsStringSync('Google Inc.'); +void createFakeAuthors(RepositoryPackage package) { + package.authorsFile.createSync(); + package.authorsFile.writeAsStringSync('Google Inc.'); } String _pluginPlatformSection( diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart index 5a5a0a108a32..aeacd77635df 100644 --- a/script/tool/test/version_check_command_test.dart +++ b/script/tool/test/version_check_command_test.dart @@ -414,14 +414,14 @@ This is necessary because of X, Y, and Z test('Allow empty lines in front of the first version in CHANGELOG', () async { const String version = '1.0.1'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: version); const String changelog = ''' ## $version * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main']); expect( @@ -433,13 +433,13 @@ This is necessary because of X, Y, and Z }); test('Throws if versions in changelog and pubspec do not match', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.1'); const String changelog = ''' ## 1.0.2 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); Error? commandError; final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main', '--against-pub'], @@ -458,14 +458,14 @@ This is necessary because of X, Y, and Z test('Success if CHANGELOG and pubspec versions match', () async { const String version = '1.0.1'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: version); const String changelog = ''' ## $version * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main']); expect( @@ -479,7 +479,7 @@ This is necessary because of X, Y, and Z test( 'Fail if pubspec version only matches an older version listed in CHANGELOG', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' @@ -488,7 +488,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); bool hasError = false; final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main', '--against-pub'], @@ -509,7 +509,7 @@ This is necessary because of X, Y, and Z test('Allow NEXT as a placeholder for gathering CHANGELOG entries', () async { const String version = '1.0.0'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: version); const String changelog = ''' @@ -518,7 +518,7 @@ This is necessary because of X, Y, and Z ## $version * Some other changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -536,7 +536,7 @@ This is necessary because of X, Y, and Z test('Fail if NEXT appears after a version', () async { const String version = '1.0.1'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: version); const String changelog = ''' @@ -547,7 +547,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); bool hasError = false; final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main', '--against-pub'], @@ -569,7 +569,7 @@ This is necessary because of X, Y, and Z test('Fail if NEXT is left in the CHANGELOG when adding a version bump', () async { const String version = '1.0.1'; - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: version); const String changelog = ''' @@ -580,7 +580,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); bool hasError = false; final List output = await runCapturingPrint( @@ -603,7 +603,7 @@ This is necessary because of X, Y, and Z }); test('Fail if the version changes without replacing NEXT', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.1'); const String changelog = ''' @@ -612,7 +612,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); bool hasError = false; final List output = await runCapturingPrint( @@ -635,7 +635,7 @@ This is necessary because of X, Y, and Z test( 'fails gracefully if the version headers are not found due to using the wrong style', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' @@ -644,7 +644,7 @@ This is necessary because of X, Y, and Z # 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -670,14 +670,14 @@ This is necessary because of X, Y, and Z }); test('fails gracefully if the version is unparseable', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## Alpha * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -715,14 +715,14 @@ This is necessary because of X, Y, and Z } test('passes for unchanged packages', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -744,14 +744,14 @@ This is necessary because of X, Y, and Z test( 'fails if a version change is missing from a change that does not ' 'pass the exemption check', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -779,14 +779,14 @@ packages/plugin/lib/plugin.dart }); test('passes version change requirement when version changes', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.1'); const String changelog = ''' ## 1.0.1 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -810,14 +810,14 @@ packages/plugin/pubspec.yaml }); test('version change check ignores files outside the package', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -840,14 +840,14 @@ tool/plugin/lib/plugin.dart }); test('allows missing version change for exempt changes', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -873,14 +873,14 @@ packages/plugin/CHANGELOG.md }); test('allows missing version change with justification', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -914,14 +914,14 @@ No version change: Code change is only to implementation comments. }); test('fails if a CHANGELOG change is missing', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -949,14 +949,14 @@ packages/plugin/example/lib/foo.dart }); test('passes CHANGELOG check when the CHANGELOG is changed', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -980,14 +980,14 @@ packages/plugin/CHANGELOG.md test('fails CHANGELOG check if only another package CHANGELOG chages', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -1014,14 +1014,14 @@ packages/another_plugin/CHANGELOG.md }); test('allows missing CHANGELOG change with justification', () async { - final Directory pluginDirectory = + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); const String changelog = ''' ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(pluginDirectory, changelog); + createFakeCHANGELOG(plugin, changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; diff --git a/script/tool/test/xcode_analyze_command_test.dart b/script/tool/test/xcode_analyze_command_test.dart index 097d4d21cb19..51e8e3283295 100644 --- a/script/tool/test/xcode_analyze_command_test.dart +++ b/script/tool/test/xcode_analyze_command_test.dart @@ -82,13 +82,12 @@ void main() { }); test('runs for iOS plugin', () async { - final Directory pluginDirectory = createFakePlugin( - 'plugin', packagesDir, platformSupport: { - platformIOS: const PlatformDetails(PlatformSupport.inline) - }); + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, + platformSupport: { + platformIOS: const PlatformDetails(PlatformSupport.inline) + }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'xcode-analyze', @@ -184,14 +183,12 @@ void main() { }); test('runs for macOS plugin', () async { - final Directory pluginDirectory1 = createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'xcode-analyze', @@ -251,15 +248,13 @@ void main() { group('combined', () { test('runs both iOS and macOS when supported', () async { - final Directory pluginDirectory1 = createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformIOS: const PlatformDetails(PlatformSupport.inline), platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'xcode-analyze', @@ -311,14 +306,12 @@ void main() { }); test('runs only macOS for a macOS plugin', () async { - final Directory pluginDirectory1 = createFakePlugin( - 'plugin', packagesDir, + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); - final Directory pluginExampleDirectory = - pluginDirectory1.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'xcode-analyze', @@ -353,13 +346,12 @@ void main() { }); test('runs only iOS for a iOS plugin', () async { - final Directory pluginDirectory = createFakePlugin( - 'plugin', packagesDir, platformSupport: { - platformIOS: const PlatformDetails(PlatformSupport.inline) - }); + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, + platformSupport: { + platformIOS: const PlatformDetails(PlatformSupport.inline) + }); - final Directory pluginExampleDirectory = - pluginDirectory.childDirectory('example'); + final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'xcode-analyze', From bb6fc5e05165ab316fb2ed2a0485614b564c9397 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 18:29:08 -0400 Subject: [PATCH 249/844] Roll Flutter from 1aab2d83d932 to 8b395090c9c1 (3 revisions) (#5608) --- .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 368d3f785e14..b91311ac768b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1aab2d83d9324a426c2cfc3468f6d98e5a2d5e43 +8b395090c9c1e20c5df24945fb8f0d272ea42aac From 7a31f83289e8c6c08fda0bdfa4affbb50e527729 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 19:34:10 -0400 Subject: [PATCH 250/844] Roll Flutter from 8b395090c9c1 to b4f8d7c79296 (1 revision) (#5610) --- .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 b91311ac768b..0dedab86f82a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8b395090c9c1e20c5df24945fb8f0d272ea42aac +b4f8d7c7929648cf2e4f51fb4ab746473ff09142 From 9b8659aaf672d6c5b332f36fc4644c56db8dd7fb Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 20:39:07 -0400 Subject: [PATCH 251/844] Roll Flutter from b4f8d7c79296 to 896e5b332d36 (1 revision) (#5611) --- .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 0dedab86f82a..c9f3e69b4be6 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b4f8d7c7929648cf2e4f51fb4ab746473ff09142 +896e5b332d364cbe407966787115ce6cc4e6dde4 From 8c261a223285e69702d96ef006001e623f0ee300 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 3 May 2022 22:49:09 -0400 Subject: [PATCH 252/844] Roll Flutter from 896e5b332d36 to 6f8ffecc7263 (2 revisions) (#5613) --- .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 c9f3e69b4be6..fdccafa80965 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -896e5b332d364cbe407966787115ce6cc4e6dde4 +6f8ffecc726305bf050dfa9fe16dcee39e18a5d8 From 21e2f99e1ae06e014033171cd4640d5870a9ff20 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 00:29:09 -0400 Subject: [PATCH 253/844] Roll Flutter from 6f8ffecc7263 to e291b58813f7 (1 revision) (#5615) --- .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 fdccafa80965..5fb7f2fb5a18 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -6f8ffecc726305bf050dfa9fe16dcee39e18a5d8 +e291b58813f78177e959117bcdf86c298ad4da60 From 182071df2bf86faaeb4d2af2ed62f8b3ff833f62 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 01:34:08 -0400 Subject: [PATCH 254/844] Roll Flutter from e291b58813f7 to e9c0ee1e08d6 (1 revision) (#5616) --- .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 5fb7f2fb5a18..31ebd1f2f443 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e291b58813f78177e959117bcdf86c298ad4da60 +e9c0ee1e08d64cdda1cc05e40f7688e2aae7041e From cafc154de894293ec032fe5c9247775ae363a7c5 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 04:29:11 -0400 Subject: [PATCH 255/844] Roll Flutter from e9c0ee1e08d6 to b3838eb3de6d (1 revision) (#5617) --- .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 31ebd1f2f443..e3f0de9a31e4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e9c0ee1e08d64cdda1cc05e40f7688e2aae7041e +b3838eb3de6d10bfcd91ccca2d44b1939d625bb8 From 0f0f6d70ba36ded848359562eebc4b994076f109 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 13:49:13 -0400 Subject: [PATCH 256/844] Roll Flutter from b3838eb3de6d to b5321d182a6b (1 revision) (#5618) --- .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 e3f0de9a31e4..b28761746824 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b3838eb3de6d10bfcd91ccca2d44b1939d625bb8 +b5321d182a6bf89fd3d86ceee52179867ffa7c89 From 8a62a57c5d0c32ae88d8de346899f8f25fd09108 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Wed, 4 May 2022 11:44:19 -0700 Subject: [PATCH 257/844] [camera] Update mocktail to latest. (#5614) --- .../camera_web/example/integration_test/camera_test.dart | 2 +- .../example/integration_test/camera_web_test.dart | 6 +++--- packages/camera/camera_web/example/pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/camera/camera_web/example/integration_test/camera_test.dart b/packages/camera/camera_web/example/integration_test/camera_test.dart index 4e8050eef6a8..20d1cbaf8e36 100644 --- a/packages/camera/camera_web/example/integration_test/camera_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_test.dart @@ -53,7 +53,7 @@ void main() { }); setUpAll(() { - registerFallbackValue(MockCameraOptions()); + registerFallbackValue(MockCameraOptions()); }); group('initialize', () { 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 9f21eb43fb34..5a564dd66021 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 @@ -76,9 +76,9 @@ void main() { }); setUpAll(() { - registerFallbackValue(MockMediaStreamTrack()); - registerFallbackValue(MockCameraOptions()); - registerFallbackValue(FlashMode.off); + registerFallbackValue(MockMediaStreamTrack()); + registerFallbackValue(MockCameraOptions()); + registerFallbackValue(FlashMode.off); }); testWidgets('CameraPlugin is the live instance', diff --git a/packages/camera/camera_web/example/pubspec.yaml b/packages/camera/camera_web/example/pubspec.yaml index 0457e8fdfcf2..911648ef030d 100644 --- a/packages/camera/camera_web/example/pubspec.yaml +++ b/packages/camera/camera_web/example/pubspec.yaml @@ -18,4 +18,4 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - mocktail: ^0.1.4 + mocktail: ^0.3.0 From e5afe6d49b016327307e98403b6748dff8ccdfb7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 15:49:14 -0400 Subject: [PATCH 258/844] Roll Flutter from b5321d182a6b to 82b91b0b45e5 (8 revisions) (#5626) --- .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 b28761746824..d178b7bc2506 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b5321d182a6bf89fd3d86ceee52179867ffa7c89 +82b91b0b45e53f57c9a48fd43906f77ad73003f6 From 24b8e3bbbc659871b84b58293e57f56feda8acd8 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 16:54:14 -0400 Subject: [PATCH 259/844] Roll Flutter from 82b91b0b45e5 to 3c9f417326fa (2 revisions) (#5629) --- .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 d178b7bc2506..d440c987fb9e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -82b91b0b45e53f57c9a48fd43906f77ad73003f6 +3c9f417326fa0d9ce296465fb0b213958927cee3 From 9a12b66452f98a485db152294c806cc60756197c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 17:59:12 -0400 Subject: [PATCH 260/844] Roll Flutter from 3c9f417326fa to 2e1c146eec61 (4 revisions) (#5630) --- .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 d440c987fb9e..bb3f35211141 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3c9f417326fa0d9ce296465fb0b213958927cee3 +2e1c146eec61aa534890a49d219e55a56da039c0 From d648000d1366edd4b85ce479c584b08542e82213 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 19:39:15 -0400 Subject: [PATCH 261/844] Roll Flutter from 2e1c146eec61 to 122ab838f48b (1 revision) (#5631) --- .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 bb3f35211141..508a93dc7479 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2e1c146eec61aa534890a49d219e55a56da039c0 +122ab838f48b728784f088b1560ee52f44ea7054 From 665628766a2b8783c772de2dc00d00438b876b86 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 20:44:10 -0400 Subject: [PATCH 262/844] Roll Flutter from 122ab838f48b to 051f1b027f0d (1 revision) (#5632) --- .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 508a93dc7479..6766638ed22b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -122ab838f48b728784f088b1560ee52f44ea7054 +051f1b027f0dada4eb09930f9b08061d7bfb3ce4 From 1dbf987a25d8ac559f100efa15e4a07a8d7e2aff Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 4 May 2022 21:49:09 -0400 Subject: [PATCH 263/844] Roll Flutter from 051f1b027f0d to 35513c265c94 (1 revision) (#5633) --- .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 6766638ed22b..c99bf3cbe23c 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -051f1b027f0dada4eb09930f9b08061d7bfb3ce4 +35513c265c948260421ffdcc5ef9520349a91289 From 2d5121f4b77f5111afb6db219389e959e4dd2698 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 5 May 2022 09:39:09 -0400 Subject: [PATCH 264/844] Roll Flutter from 35513c265c94 to f7db4893667e (1 revision) (#5634) --- .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 c99bf3cbe23c..3d1bf9d0a4d2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -35513c265c948260421ffdcc5ef9520349a91289 +f7db4893667ee8190b2b52f82509bd84577292d7 From 1761e10db058c3a80879c4c8ed0b7db22890a517 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 5 May 2022 10:44:09 -0400 Subject: [PATCH 265/844] Roll Flutter from f7db4893667e to 0ea21bc5b397 (12 revisions) (#5637) --- .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 3d1bf9d0a4d2..a77459034db0 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f7db4893667ee8190b2b52f82509bd84577292d7 +0ea21bc5b397d9814bb8f2803a4533fae1fd24d6 From 810d8344aa9e8d99acdcfc8e6a8ede16763180bf Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 5 May 2022 13:54:14 -0400 Subject: [PATCH 266/844] Roll Flutter from 0ea21bc5b397 to f771c9fee734 (1 revision) (#5638) --- .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 a77459034db0..4a584cd7c024 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -0ea21bc5b397d9814bb8f2803a4533fae1fd24d6 +f771c9fee734efb4912ecc2615990318b91a8c15 From 8907fe9f6e28392963cfdb129d20357657d9bfad Mon Sep 17 00:00:00 2001 From: Jesse Seales <103135467+sealesj@users.noreply.github.com> Date: Thu, 5 May 2022 15:49:11 -0400 Subject: [PATCH 267/844] [github workflow] Resolve token-permissions security alerts (#5627) --- .github/workflows/mirror.yml | 3 +++ .github/workflows/pull_request_label.yml | 3 +++ .github/workflows/release.yml | 3 +++ 3 files changed, 9 insertions(+) diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml index 9026643d7b2e..9cfaf20d6cf0 100644 --- a/.github/workflows/mirror.yml +++ b/.github/workflows/mirror.yml @@ -7,6 +7,9 @@ on: push: branches: - 'main' + +# Declare default permissions as read only. +permissions: read-all jobs: mirror_job: diff --git a/.github/workflows/pull_request_label.yml b/.github/workflows/pull_request_label.yml index 825a3afd8508..54cae2deca98 100644 --- a/.github/workflows/pull_request_label.yml +++ b/.github/workflows/pull_request_label.yml @@ -12,6 +12,9 @@ on: pull_request_target: types: [opened, synchronize, reopened, closed] +# Declare default permissions as read only. +permissions: read-all + jobs: label: permissions: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1bf50a0d1dfc..a67f8f1575e4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,9 @@ on: branches: - main +# Declare default permissions as read only. +permissions: read-all + jobs: release: if: github.repository_owner == 'flutter' From ced642f92b900c127f7edc027558b568f186b74b Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 5 May 2022 16:54:13 -0400 Subject: [PATCH 268/844] Roll Flutter from f771c9fee734 to 0a26277b58c4 (3 revisions) (#5641) --- .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 4a584cd7c024..b1dba7fa567b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f771c9fee734efb4912ecc2615990318b91a8c15 +0a26277b58c401936e7cada2d17fb9278a1dfb40 From 6706418e2baa771eecd1345091c856f0cfb7b4cb Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 5 May 2022 17:59:13 -0400 Subject: [PATCH 269/844] Roll Flutter from 0a26277b58c4 to 603eb82734ab (1 revision) (#5644) --- .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 b1dba7fa567b..8913c71b316c 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -0a26277b58c401936e7cada2d17fb9278a1dfb40 +603eb82734ab09e94c34c70324b915521d409991 From 49b06b36c9986b6d78babcf48c82dc4626a30fd3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 5 May 2022 21:39:13 -0400 Subject: [PATCH 270/844] Roll Flutter from 603eb82734ab to 758ebda16dc1 (6 revisions) (#5646) --- .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 8913c71b316c..9d16b7153de8 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -603eb82734ab09e94c34c70324b915521d409991 +758ebda16dc16994edac84257ffef92324eaf1bb From b5f505ee0afc144933551d36b73578a3000ab8d7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 6 May 2022 13:20:47 -0400 Subject: [PATCH 271/844] Roll Flutter from 758ebda16dc1 to 2f3f053c0dfb (5 revisions) (#5648) * 543cc6032 Roll Engine from 387a9b9e300f to b1b0a5adffe9 (1 revision) (flutter/flutter#103159) * 55881f7ae Reland "Fix crash from alt-tab'ing just after startup" (flutter/flutter#103093) * 82afe3ea4 Clear the cached data of `RenderBox` if its parent re-layout (flutter/flutter#101493) * b20e27e77 Does not replace the root layer unnecessarily (flutter/flutter#101748) * 2f3f053c0 Roll Engine from b1b0a5adffe9 to 47c8a6acf9f3 (1 revision) (flutter/flutter#103164) --- .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 9d16b7153de8..8fe5ef2b1e32 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -758ebda16dc16994edac84257ffef92324eaf1bb +2f3f053c0dfb2101d2e3466144ef50f7836b47b6 From c5f1c6bbf3bf281735d29f9a25ab4443dfc79690 Mon Sep 17 00:00:00 2001 From: moko256 Date: Sat, 7 May 2022 03:09:11 +0900 Subject: [PATCH 272/844] Add issues/PRs badges to README (#5649) --- README.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 30c6a2f00e66..cc6543d4ecee 100644 --- a/README.md +++ b/README.md @@ -40,22 +40,22 @@ and send a [pull request](https://github.com/flutter/plugins/pulls). ## Plugins These are the available plugins in this repository. -| Plugin | Pub | Points | Popularity | Likes | -|--------|-----|--------|------------|-------| -| [camera](./packages/camera/) | [![pub package](https://img.shields.io/pub/v/camera.svg)](https://pub.dev/packages/camera) | [![pub points](https://badges.bar/camera/pub%20points)](https://pub.dev/packages/camera/score) | [![popularity](https://badges.bar/camera/popularity)](https://pub.dev/packages/camera/score) | [![likes](https://badges.bar/camera/likes)](https://pub.dev/packages/camera/score) | -| [espresso](./packages/espresso/) | [![pub package](https://img.shields.io/pub/v/espresso.svg)](https://pub.dev/packages/espresso) | [![pub points](https://badges.bar/espresso/pub%20points)](https://pub.dev/packages/espresso/score) | [![popularity](https://badges.bar/espresso/popularity)](https://pub.dev/packages/espresso/score) | [![likes](https://badges.bar/espresso/likes)](https://pub.dev/packages/espresso/score) | -| [file_selector](./packages/file_selector/) | [![pub package](https://img.shields.io/pub/v/file_selector.svg)](https://pub.dev/packages/file_selector) | [![pub points](https://badges.bar/file_selector/pub%20points)](https://pub.dev/packages/file_selector/score) | [![popularity](https://badges.bar/file_selector/popularity)](https://pub.dev/packages/file_selector/score) | [![likes](https://badges.bar/file_selector/likes)](https://pub.dev/packages/file_selector/score) | -| [flutter_plugin_android_lifecycle](./packages/flutter_plugin_android_lifecycle/) | [![pub package](https://img.shields.io/pub/v/flutter_plugin_android_lifecycle.svg)](https://pub.dev/packages/flutter_plugin_android_lifecycle) | [![pub points](https://badges.bar/flutter_plugin_android_lifecycle/pub%20points)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![popularity](https://badges.bar/flutter_plugin_android_lifecycle/popularity)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![likes](https://badges.bar/flutter_plugin_android_lifecycle/likes)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | -| [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://badges.bar/google_maps_flutter/pub%20points)](https://pub.dev/packages/google_maps_flutter/score) | [![popularity](https://badges.bar/google_maps_flutter/popularity)](https://pub.dev/packages/google_maps_flutter/score) | [![likes](https://badges.bar/google_maps_flutter/likes)](https://pub.dev/packages/google_maps_flutter/score) | -| [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://badges.bar/google_sign_in/pub%20points)](https://pub.dev/packages/google_sign_in/score) | [![popularity](https://badges.bar/google_sign_in/popularity)](https://pub.dev/packages/google_sign_in/score) | [![likes](https://badges.bar/google_sign_in/likes)](https://pub.dev/packages/google_sign_in/score) | -| [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://badges.bar/image_picker/pub%20points)](https://pub.dev/packages/image_picker/score) | [![popularity](https://badges.bar/image_picker/popularity)](https://pub.dev/packages/image_picker/score) | [![likes](https://badges.bar/image_picker/likes)](https://pub.dev/packages/image_picker/score) | -| [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://badges.bar/in_app_purchase/pub%20points)](https://pub.dev/packages/in_app_purchase/score) | [![popularity](https://badges.bar/in_app_purchase/popularity)](https://pub.dev/packages/in_app_purchase/score) | [![likes](https://badges.bar/in_app_purchase/likes)](https://pub.dev/packages/in_app_purchase/score) | -| [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://badges.bar/ios_platform_images/pub%20points)](https://pub.dev/packages/ios_platform_images/score) | [![popularity](https://badges.bar/ios_platform_images/popularity)](https://pub.dev/packages/ios_platform_images/score) | [![likes](https://badges.bar/ios_platform_images/likes)](https://pub.dev/packages/ios_platform_images/score) | -| [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://badges.bar/local_auth/pub%20points)](https://pub.dev/packages/local_auth/score) | [![popularity](https://badges.bar/local_auth/popularity)](https://pub.dev/packages/local_auth/score) | [![likes](https://badges.bar/local_auth/likes)](https://pub.dev/packages/local_auth/score) | -| [path_provider](./packages/path_provider/) | [![pub package](https://img.shields.io/pub/v/path_provider.svg)](https://pub.dev/packages/path_provider) | [![pub points](https://badges.bar/path_provider/pub%20points)](https://pub.dev/packages/path_provider/score) | [![popularity](https://badges.bar/path_provider/popularity)](https://pub.dev/packages/path_provider/score) | [![likes](https://badges.bar/path_provider/likes)](https://pub.dev/packages/path_provider/score) | -| [plugin_platform_interface](./packages/plugin_platform_interface/) | [![pub package](https://img.shields.io/pub/v/plugin_platform_interface.svg)](https://pub.dev/packages/plugin_platform_interface) | [![pub points](https://badges.bar/plugin_platform_interface/pub%20points)](https://pub.dev/packages/plugin_platform_interface/score) | [![popularity](https://badges.bar/plugin_platform_interface/popularity)](https://pub.dev/packages/plugin_platform_interface/score) | [![likes](https://badges.bar/plugin_platform_interface/likes)](https://pub.dev/packages/plugin_platform_interface/score) | -| [quick_actions](./packages/quick_actions/) | [![pub package](https://img.shields.io/pub/v/quick_actions.svg)](https://pub.dev/packages/quick_actions) | [![pub points](https://badges.bar/quick_actions/pub%20points)](https://pub.dev/packages/quick_actions/score) | [![popularity](https://badges.bar/quick_actions/popularity)](https://pub.dev/packages/quick_actions/score) | [![likes](https://badges.bar/quick_actions/likes)](https://pub.dev/packages/quick_actions/score) | -| [shared_preferences](./packages/shared_preferences/) | [![pub package](https://img.shields.io/pub/v/shared_preferences.svg)](https://pub.dev/packages/shared_preferences) | [![pub points](https://badges.bar/shared_preferences/pub%20points)](https://pub.dev/packages/shared_preferences/score) | [![popularity](https://badges.bar/shared_preferences/popularity)](https://pub.dev/packages/shared_preferences/score) | [![likes](https://badges.bar/shared_preferences/likes)](https://pub.dev/packages/shared_preferences/score) | -| [url_launcher](./packages/url_launcher/) | [![pub package](https://img.shields.io/pub/v/url_launcher.svg)](https://pub.dev/packages/url_launcher) | [![pub points](https://badges.bar/url_launcher/pub%20points)](https://pub.dev/packages/url_launcher/score) | [![popularity](https://badges.bar/url_launcher/popularity)](https://pub.dev/packages/url_launcher/score) | [![likes](https://badges.bar/url_launcher/likes)](https://pub.dev/packages/url_launcher/score) | -| [video_player](./packages/video_player/) | [![pub package](https://img.shields.io/pub/v/video_player.svg)](https://pub.dev/packages/video_player) | [![pub points](https://badges.bar/video_player/pub%20points)](https://pub.dev/packages/video_player/score) | [![popularity](https://badges.bar/video_player/popularity)](https://pub.dev/packages/video_player/score) | [![likes](https://badges.bar/video_player/likes)](https://pub.dev/packages/video_player/score) | -| [webview_flutter](./packages/webview_flutter/) | [![pub package](https://img.shields.io/pub/v/webview_flutter.svg)](https://pub.dev/packages/webview_flutter) | [![pub points](https://badges.bar/webview_flutter/pub%20points)](https://pub.dev/packages/webview_flutter/score) | [![popularity](https://badges.bar/webview_flutter/popularity)](https://pub.dev/packages/webview_flutter/score) | [![likes](https://badges.bar/webview_flutter/likes)](https://pub.dev/packages/webview_flutter/score) | +| Plugin | Pub | Points | Popularity | Likes | Issues | Pull requests | +|--------|-----|--------|------------|-------|--------|---------------| +| [camera](./packages/camera/) | [![pub package](https://img.shields.io/pub/v/camera.svg)](https://pub.dev/packages/camera) | [![pub points](https://badges.bar/camera/pub%20points)](https://pub.dev/packages/camera/score) | [![popularity](https://badges.bar/camera/popularity)](https://pub.dev/packages/camera/score) | [![likes](https://badges.bar/camera/likes)](https://pub.dev/packages/camera/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20camera?label=)](https://github.com/flutter/flutter/labels/p%3A%20camera) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20camera?label=)](https://github.com/flutter/plugins/labels/p%3A%20camera) | +| [espresso](./packages/espresso/) | [![pub package](https://img.shields.io/pub/v/espresso.svg)](https://pub.dev/packages/espresso) | [![pub points](https://badges.bar/espresso/pub%20points)](https://pub.dev/packages/espresso/score) | [![popularity](https://badges.bar/espresso/popularity)](https://pub.dev/packages/espresso/score) | [![likes](https://badges.bar/espresso/likes)](https://pub.dev/packages/espresso/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20espresso?label=)](https://github.com/flutter/flutter/labels/p%3A%20espresso) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20espresso?label=)](https://github.com/flutter/plugins/labels/p%3A%20espresso) | +| [file_selector](./packages/file_selector/) | [![pub package](https://img.shields.io/pub/v/file_selector.svg)](https://pub.dev/packages/file_selector) | [![pub points](https://badges.bar/file_selector/pub%20points)](https://pub.dev/packages/file_selector/score) | [![popularity](https://badges.bar/file_selector/popularity)](https://pub.dev/packages/file_selector/score) | [![likes](https://badges.bar/file_selector/likes)](https://pub.dev/packages/file_selector/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20file_selector?label=)](https://github.com/flutter/flutter/labels/p%3A%20file_selector) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20file_selector?label=)](https://github.com/flutter/plugins/labels/p%3A%20file_selector) | +| [flutter_plugin_android_lifecycle](./packages/flutter_plugin_android_lifecycle/) | [![pub package](https://img.shields.io/pub/v/flutter_plugin_android_lifecycle.svg)](https://pub.dev/packages/flutter_plugin_android_lifecycle) | [![pub points](https://badges.bar/flutter_plugin_android_lifecycle/pub%20points)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![popularity](https://badges.bar/flutter_plugin_android_lifecycle/popularity)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![likes](https://badges.bar/flutter_plugin_android_lifecycle/likes)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20flutter_plugin_android_lifecycle?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_plugin_android_lifecycle) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20flutter_plugin_android_lifecycle?label=)](https://github.com/flutter/plugins/labels/p%3A%20flutter_plugin_android_lifecycle) | +| [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://badges.bar/google_maps_flutter/pub%20points)](https://pub.dev/packages/google_maps_flutter/score) | [![popularity](https://badges.bar/google_maps_flutter/popularity)](https://pub.dev/packages/google_maps_flutter/score) | [![likes](https://badges.bar/google_maps_flutter/likes)](https://pub.dev/packages/google_maps_flutter/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20maps?label=)](https://github.com/flutter/flutter/labels/p%3A%20maps) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20google_maps_flutter?label=)](https://github.com/flutter/plugins/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://badges.bar/google_sign_in/pub%20points)](https://pub.dev/packages/google_sign_in/score) | [![popularity](https://badges.bar/google_sign_in/popularity)](https://pub.dev/packages/google_sign_in/score) | [![likes](https://badges.bar/google_sign_in/likes)](https://pub.dev/packages/google_sign_in/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%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/plugins/p:%20google_sign_in?label=)](https://github.com/flutter/plugins/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://badges.bar/image_picker/pub%20points)](https://pub.dev/packages/image_picker/score) | [![popularity](https://badges.bar/image_picker/popularity)](https://pub.dev/packages/image_picker/score) | [![likes](https://badges.bar/image_picker/likes)](https://pub.dev/packages/image_picker/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%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/plugins/p:%20image_picker?label=)](https://github.com/flutter/plugins/labels/p%3A%20image_picker) | +| [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://badges.bar/in_app_purchase/pub%20points)](https://pub.dev/packages/in_app_purchase/score) | [![popularity](https://badges.bar/in_app_purchase/popularity)](https://pub.dev/packages/in_app_purchase/score) | [![likes](https://badges.bar/in_app_purchase/likes)](https://pub.dev/packages/in_app_purchase/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%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/plugins/p:%20in_app_purchase?label=)](https://github.com/flutter/plugins/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://badges.bar/ios_platform_images/pub%20points)](https://pub.dev/packages/ios_platform_images/score) | [![popularity](https://badges.bar/ios_platform_images/popularity)](https://pub.dev/packages/ios_platform_images/score) | [![likes](https://badges.bar/ios_platform_images/likes)](https://pub.dev/packages/ios_platform_images/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%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/plugins/p:%20ios_platform_images?label=)](https://github.com/flutter/plugins/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://badges.bar/local_auth/pub%20points)](https://pub.dev/packages/local_auth/score) | [![popularity](https://badges.bar/local_auth/popularity)](https://pub.dev/packages/local_auth/score) | [![likes](https://badges.bar/local_auth/likes)](https://pub.dev/packages/local_auth/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%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/plugins/p:%20local_auth?label=)](https://github.com/flutter/plugins/labels/p%3A%20local_auth) | +| [path_provider](./packages/path_provider/) | [![pub package](https://img.shields.io/pub/v/path_provider.svg)](https://pub.dev/packages/path_provider) | [![pub points](https://badges.bar/path_provider/pub%20points)](https://pub.dev/packages/path_provider/score) | [![popularity](https://badges.bar/path_provider/popularity)](https://pub.dev/packages/path_provider/score) | [![likes](https://badges.bar/path_provider/likes)](https://pub.dev/packages/path_provider/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20path_provider?label=)](https://github.com/flutter/flutter/labels/p%3A%20path_provider) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20path_provider?label=)](https://github.com/flutter/plugins/labels/p%3A%20path_provider) | +| [plugin_platform_interface](./packages/plugin_platform_interface/) | [![pub package](https://img.shields.io/pub/v/plugin_platform_interface.svg)](https://pub.dev/packages/plugin_platform_interface) | [![pub points](https://badges.bar/plugin_platform_interface/pub%20points)](https://pub.dev/packages/plugin_platform_interface/score) | [![popularity](https://badges.bar/plugin_platform_interface/popularity)](https://pub.dev/packages/plugin_platform_interface/score) | [![likes](https://badges.bar/plugin_platform_interface/likes)](https://pub.dev/packages/plugin_platform_interface/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20plugin_platform_interface?label=)](https://github.com/flutter/flutter/labels/p%3A%20plugin_platform_interface) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20plugin_platform_interface?label=)](https://github.com/flutter/plugins/labels/p%3A%20plugin_platform_interface) | +| [quick_actions](./packages/quick_actions/) | [![pub package](https://img.shields.io/pub/v/quick_actions.svg)](https://pub.dev/packages/quick_actions) | [![pub points](https://badges.bar/quick_actions/pub%20points)](https://pub.dev/packages/quick_actions/score) | [![popularity](https://badges.bar/quick_actions/popularity)](https://pub.dev/packages/quick_actions/score) | [![likes](https://badges.bar/quick_actions/likes)](https://pub.dev/packages/quick_actions/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20quick_actions?label=)](https://github.com/flutter/flutter/labels/p%3A%20quick_actions) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20quick_actions?label=)](https://github.com/flutter/plugins/labels/p%3A%20quick_actions) | +| [shared_preferences](./packages/shared_preferences/) | [![pub package](https://img.shields.io/pub/v/shared_preferences.svg)](https://pub.dev/packages/shared_preferences) | [![pub points](https://badges.bar/shared_preferences/pub%20points)](https://pub.dev/packages/shared_preferences/score) | [![popularity](https://badges.bar/shared_preferences/popularity)](https://pub.dev/packages/shared_preferences/score) | [![likes](https://badges.bar/shared_preferences/likes)](https://pub.dev/packages/shared_preferences/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20shared_preferences?label=)](https://github.com/flutter/flutter/labels/p%3A%20shared_preferences) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20shared_preferences?label=)](https://github.com/flutter/plugins/labels/p%3A%20shared_preferences) | +| [url_launcher](./packages/url_launcher/) | [![pub package](https://img.shields.io/pub/v/url_launcher.svg)](https://pub.dev/packages/url_launcher) | [![pub points](https://badges.bar/url_launcher/pub%20points)](https://pub.dev/packages/url_launcher/score) | [![popularity](https://badges.bar/url_launcher/popularity)](https://pub.dev/packages/url_launcher/score) | [![likes](https://badges.bar/url_launcher/likes)](https://pub.dev/packages/url_launcher/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20url_launcher?label=)](https://github.com/flutter/flutter/labels/p%3A%20url_launcher) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20url_launcher?label=)](https://github.com/flutter/plugins/labels/p%3A%20url_launcher) | +| [video_player](./packages/video_player/) | [![pub package](https://img.shields.io/pub/v/video_player.svg)](https://pub.dev/packages/video_player) | [![pub points](https://badges.bar/video_player/pub%20points)](https://pub.dev/packages/video_player/score) | [![popularity](https://badges.bar/video_player/popularity)](https://pub.dev/packages/video_player/score) | [![likes](https://badges.bar/video_player/likes)](https://pub.dev/packages/video_player/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20video_player?label=)](https://github.com/flutter/flutter/labels/p%3A%20video_player) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20video_player?label=)](https://github.com/flutter/plugins/labels/p%3A%20video_player) | +| [webview_flutter](./packages/webview_flutter/) | [![pub package](https://img.shields.io/pub/v/webview_flutter.svg)](https://pub.dev/packages/webview_flutter) | [![pub points](https://badges.bar/webview_flutter/pub%20points)](https://pub.dev/packages/webview_flutter/score) | [![popularity](https://badges.bar/webview_flutter/popularity)](https://pub.dev/packages/webview_flutter/score) | [![likes](https://badges.bar/webview_flutter/likes)](https://pub.dev/packages/webview_flutter/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20webview?label=)](https://github.com/flutter/flutter/labels/p%3A%20webview) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20webview_flutter?label=)](https://github.com/flutter/plugins/labels/p%3A%20webview_flutter) | From 7960ff2d6707f885ce6b4737b65e83ceea121a9d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 6 May 2022 14:44:12 -0400 Subject: [PATCH 273/844] Roll Flutter from 2f3f053c0dfb to ac30842c8259 (13 revisions) (#5650) --- .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 8fe5ef2b1e32..cfb6754642fb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2f3f053c0dfb2101d2e3466144ef50f7836b47b6 +ac30842c82596539e6f04c5aac86dfedb3904e3a From 3eba6615700c88cd5846a0a76da06a04ac3cd9bc Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 6 May 2022 20:19:13 -0400 Subject: [PATCH 274/844] Roll Flutter from ac30842c8259 to e5718354020e (4 revisions) (#5652) --- .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 cfb6754642fb..8a369244f133 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ac30842c82596539e6f04c5aac86dfedb3904e3a +e5718354020e9245e1a7b2cfd2486bfb424a826e From bc5acb9a25a970a9e72a31fc4e923fa9ba522c9b Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 6 May 2022 21:14:13 -0400 Subject: [PATCH 275/844] [local_auth] Overhaul README, and fix `error_codes.dart` visibility (#5653) --- packages/local_auth/local_auth/CHANGELOG.md | 11 +- packages/local_auth/local_auth/README.md | 290 ++++++++++-------- .../local_auth/example/build.excerpt.yaml | 15 + .../example/lib/readme_excerpts.dart | 164 ++++++++++ .../local_auth/example/pubspec.yaml | 1 + .../lib/{src/types => }/error_codes.dart | 8 +- .../local_auth/lib/src/local_auth.dart | 1 - packages/local_auth/local_auth/pubspec.yaml | 2 +- script/configs/temp_exclude_excerpt.yaml | 1 - 9 files changed, 351 insertions(+), 142 deletions(-) create mode 100644 packages/local_auth/local_auth/example/build.excerpt.yaml create mode 100644 packages/local_auth/local_auth/example/lib/readme_excerpts.dart rename packages/local_auth/local_auth/lib/{src/types => }/error_codes.dart (73%) diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index c495acae5a81..570d8eef7b9b 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,5 +1,8 @@ -## NEXT +## 2.0.1 +* Restores the ability to import `error_codes.dart`. +* Updates README to match API changes in 2.0, and to improve clarity in + general. * Removes unnecessary imports. ## 2.0.0 @@ -9,11 +12,11 @@ * BREAKING CHANGE: Deprecated method `authenticateWithBiometrics` has been removed. Use `authenticate` instead. * BREAKING CHANGE: Enum `BiometricType` has been expanded with options for `strong` and `weak`, - and applications should be updated to handle these accordingly. + and applications should be updated to handle these accordingly. * BREAKING CHANGE: Parameters of `authenticate` have been changed. - + Example: - ```dart + ```dart // Old way of calling `authenticate`. Future authenticate( localizedReason: 'localized reason', diff --git a/packages/local_auth/local_auth/README.md b/packages/local_auth/local_auth/README.md index 3a60e45a57eb..7ec4829ba521 100644 --- a/packages/local_auth/local_auth/README.md +++ b/packages/local_auth/local_auth/README.md @@ -1,10 +1,12 @@ # local_auth + + This Flutter plugin provides means to perform local, on-device authentication of the user. -This means referring to biometric authentication on iOS (Touch ID or lock code) -and the fingerprint APIs on Android (introduced in Android 6.0). +On supported devices, this includes authentication with biometrics such as +fingerprint or facial recognition. | | Android | iOS | |-------------|-----------|------| @@ -12,137 +14,175 @@ and the fingerprint APIs on Android (introduced in Android 6.0). ## Usage -Import the relevant file: - -```dart -import 'package:local_auth/local_auth.dart'; -``` +### Device Capabilities -To check whether there is local authentication available on this device or not, call canCheckBiometrics: +To check whether there is local authentication available on this device or not, +call `canCheckBiometrics` (if you need biometrics support) and/or +`isDeviceSupported()` (if you just need some device-level authentication): + ```dart -bool canCheckBiometrics = - await localAuth.canCheckBiometrics; +import 'package:local_auth/local_auth.dart'; +// ··· + final LocalAuthentication auth = LocalAuthentication(); + // ··· + final bool canAuthenticateWithBiometrics = await auth.canCheckBiometrics; + final bool canAuthenticate = + canAuthenticateWithBiometrics || await auth.isDeviceSupported(); ``` Currently the following biometric types are implemented: - BiometricType.face - BiometricType.fingerprint +- BiometricType.weak +- BiometricType.strong + +### Enrolled Biometrics + +`canCheckBiometrics` only indicates whether hardware support is available, not +whether the device has any biometrics enrolled. To get a list of enrolled +biometrics, call `getAvailableBiometrics()`. -To get a list of enrolled biometrics, call getAvailableBiometrics: +The types are device-specific and platform-specific, and other types may be +added in the future, so when possible you should not rely on specific biometric +types and only check that some biometric is enrolled: + ```dart -List availableBiometrics = +final List availableBiometrics = await auth.getAvailableBiometrics(); -if (Platform.isIOS) { - if (availableBiometrics.contains(BiometricType.face)) { - // Face ID. - } else if (availableBiometrics.contains(BiometricType.fingerprint)) { - // Touch ID. - } +if (availableBiometrics.isNotEmpty) { + // Some biometrics are enrolled. } -``` - -We have default dialogs with an 'OK' button to show authentication error -messages for the following 2 cases: - -1. Passcode/PIN/Pattern Not Set. The user has not yet configured a passcode on - iOS or PIN/pattern on Android. -2. Touch ID/Fingerprint Not Enrolled. The user has not enrolled any - fingerprints on the device. -Which means, if there's no fingerprint on the user's device, a dialog with -instructions will pop up to let the user set up fingerprint. If the user clicks -'OK' button, it will return 'false'. +if (availableBiometrics.contains(BiometricType.strong) || + availableBiometrics.contains(BiometricType.face)) { + // Specific types of biometrics are available. + // Use checks like this with caution! +} +``` -Use the exported APIs to trigger local authentication with default dialogs: +### Options -The `authenticate()` method uses biometric authentication, but also allows -users to use pin, pattern, or passcode. +The `authenticate()` method uses biometric authentication when possible, but +also allows fallback to pin, pattern, or passcode. + ```dart -var localAuth = LocalAuthentication(); -bool didAuthenticate = - await localAuth.authenticate( - localizedReason: 'Please authenticate to show account balance'); +try { + final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance'); + // ··· +} on PlatformException { + // ... +} ``` -To authenticate using biometric authentication only, set `biometricOnly` to `true`. +To require biometric authentication, pass `AuthenticationOptions` with +`biometricOnly` set to `true`. + ```dart -var localAuth = LocalAuthentication(); -bool didAuthenticate = - await localAuth.authenticate( - localizedReason: 'Please authenticate to show account balance', - biometricOnly: true); +final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance', + options: const AuthenticationOptions(biometricOnly: true)); ``` -If you don't want to use the default dialogs, call this API with -'useErrorDialogs = false'. In this case, it will throw the error message back -and you need to handle them in your dart code: - -```dart -bool didAuthenticate = - await localAuth.authenticate( - localizedReason: 'Please authenticate to show account balance', - useErrorDialogs: false); -``` +#### Dialogs -You can use our default dialog messages, or you can use your own messages by -passing in IOSAuthMessages and AndroidAuthMessages: +The plugin provides default dialogs for the following cases: -```dart -import 'package:local_auth/auth_strings.dart'; - -const iosStrings = const IOSAuthMessages( - cancelButton: 'cancel', - goToSettingsButton: 'settings', - goToSettingsDescription: 'Please set up your Touch ID.', - lockOut: 'Please reenable your Touch ID'); -await localAuth.authenticate( - localizedReason: 'Please authenticate to show account balance', - useErrorDialogs: false, - iOSAuthStrings: iosStrings); +1. Passcode/PIN/Pattern Not Set: The user has not yet configured a passcode on + iOS or PIN/pattern on Android. +2. Biometrics Not Enrolled: The user has not enrolled any biometrics on the + device. -``` +If a user does not have the necessary authentication enrolled when +`authenticate` is called, they will be given the option to enroll at that point, +or cancel authentication. -If needed, you can manually stop authentication for android: +If you don't want to use the default dialogs, set the `useErrorDialogs` option +to `false` to have `authenticate` immediately return an error in those cases. + ```dart +import 'package:local_auth/error_codes.dart' as auth_error; +// ··· + try { + final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance', + options: const AuthenticationOptions(useErrorDialogs: false)); + // ··· + } on PlatformException catch (e) { + if (e.code == auth_error.notAvailable) { + // Add handling of no hardware here. + } else if (e.code == auth_error.notEnrolled) { + // ... + } else { + // ... + } + } +``` -void _cancelAuthentication() { - localAuth.stopAuthentication(); -} +If you want to customize the messages in the dialogs, you can pass +`AuthMessages` for each platform you support. These are platform-specific, so +you will need to import the platform-specific implementation packages. For +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'; +// ··· + final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance', + authMessages: const [ + AndroidAuthMessages( + signInTitle: 'Oops! Biometric authentication required!', + cancelButton: 'No thanks', + ), + IOSAuthMessages( + cancelButton: 'No thanks', + ), + ]); ``` +See the platform-specific classes for details about what can be customized on +each platform. + ### Exceptions -There are 6 types of exceptions: PasscodeNotSet, NotEnrolled, NotAvailable, OtherOperatingSystem, LockedOut and PermanentlyLockedOut. -They are wrapped in LocalAuthenticationError class. You can -catch the exception and handle them by different types. For example: +`authenticate` throws `PlatformException`s in many error cases. See +`error_codes.dart` for known error codes that you may want to have specific +handling for. For example: + ```dart import 'package:flutter/services.dart'; import 'package:local_auth/error_codes.dart' as auth_error; - -try { - bool didAuthenticate = await local_auth.authenticate( - localizedReason: 'Please authenticate to show account balance'); -} on PlatformException catch (e) { - if (e.code == auth_error.notAvailable) { - // Handle this exception here. - } -} +import 'package:local_auth/local_auth.dart'; +// ··· + final LocalAuthentication auth = LocalAuthentication(); + // ··· + try { + final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance', + options: const AuthenticationOptions(useErrorDialogs: false)); + // ··· + } on PlatformException catch (e) { + if (e.code == auth_error.notEnrolled) { + // Add handling of no hardware here. + } else if (e.code == auth_error.lockedOut || + e.code == auth_error.permanentlyLockedOut) { + // ... + } else { + // ... + } + } ``` -### Android - -\* The plugin will build and run on SDK 16+, but `isDeviceSupported()` will -always return false before SDK 23 (Android 6.0). - ## iOS Integration Note that this plugin works with both Touch ID and Face ID. However, to use the latter, @@ -158,46 +198,39 @@ app has not been updated to use Face ID. ## Android Integration -Note that local_auth plugin requires the use of a FragmentActivity as -opposed to Activity. This can be easily done by switching to use -`FlutterFragmentActivity` as opposed to `FlutterActivity` in your -manifest (or your own Activity class if you are extending the base class). - -Update your MainActivity.java: - -```java -import android.os.Bundle; -import io.flutter.app.FlutterFragmentActivity; -import io.flutter.plugins.flutter_plugin_android_lifecycle.FlutterAndroidLifecyclePlugin; -import io.flutter.plugins.localauth.LocalAuthPlugin; - -public class MainActivity extends FlutterFragmentActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - FlutterAndroidLifecyclePlugin.registerWith( - registrarFor( - "io.flutter.plugins.flutter_plugin_android_lifecycle.FlutterAndroidLifecyclePlugin")); - LocalAuthPlugin.registerWith(registrarFor("io.flutter.plugins.localauth.LocalAuthPlugin")); - } -} -``` +\* The plugin will build and run on SDK 16+, but `isDeviceSupported()` will +always return false before SDK 23 (Android 6.0). -OR +### Activity Changes -Update your MainActivity.kt: +Note that `local_auth` requires the use of a `FragmentActivity` instead of an +`Activity`. To update your application: -```kotlin -import io.flutter.embedding.android.FlutterFragmentActivity -import io.flutter.embedding.engine.FlutterEngine -import io.flutter.plugins.GeneratedPluginRegistrant +* If you are using `FlutterActivity` directly, change it to +`FlutterFragmentActivity` in your `AndroidManifest.xml`. +* If you are using a custom activity, update your `MainActivity.java`: -class MainActivity: FlutterFragmentActivity() { - override fun configureFlutterEngine(flutterEngine: FlutterEngine) { - GeneratedPluginRegistrant.registerWith(flutterEngine) + ```java + import io.flutter.embedding.android.FlutterFragmentActivity; + + public class MainActivity extends FlutterFragmentActivity { + // ... } -} -``` + ``` + + or MainActivity.kt: + + ```kotlin + import io.flutter.embedding.android.FlutterFragmentActivity + + class MainActivity: FlutterFragmentActivity() { + // ... + } + ``` + + to inherit from `FlutterFragmentActivity`. + +### Permissions Update your project's `AndroidManifest.xml` file to include the `USE_FINGERPRINT` permissions: @@ -209,6 +242,8 @@ Update your project's `AndroidManifest.xml` file to include the ``` +### Compatibility + On Android, you can check only for existence of fingerprint hardware prior to API 29 (Android Q). Therefore, if you would like to support other biometrics types (such as face scanning) and you want to support SDKs lower than Q, @@ -223,10 +258,3 @@ if the user receives a phone call before they get a chance to authenticate. With `stickyAuth` set to false, this would result in plugin returning failure result to the Dart app. If set to true, the plugin will retry authenticating when the app resumes. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). - -For help on editing plugin code, view the [documentation](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin). diff --git a/packages/local_auth/local_auth/example/build.excerpt.yaml b/packages/local_auth/local_auth/example/build.excerpt.yaml new file mode 100644 index 000000000000..e317efa11cb3 --- /dev/null +++ b/packages/local_auth/local_auth/example/build.excerpt.yaml @@ -0,0 +1,15 @@ +targets: + $default: + sources: + include: + - lib/** + # Some default includes that aren't really used here but will prevent + # false-negative warnings: + - $package$ + - lib/$lib$ + exclude: + - '**/.*/**' + - '**/build/**' + builders: + code_excerpter|code_excerpter: + enabled: true diff --git a/packages/local_auth/local_auth/example/lib/readme_excerpts.dart b/packages/local_auth/local_auth/example/lib/readme_excerpts.dart new file mode 100644 index 000000000000..25ddfe0fa62f --- /dev/null +++ b/packages/local_auth/local_auth/example/lib/readme_excerpts.dart @@ -0,0 +1,164 @@ +// 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 exists solely to host compiled excerpts for README.md, and is not +// intended for use as an actual example application. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +// #docregion ErrorHandling +import 'package:flutter/services.dart'; +// #docregion NoErrorDialogs +import 'package:local_auth/error_codes.dart' as auth_error; +// #enddocregion NoErrorDialogs +// #docregion CanCheck +import 'package:local_auth/local_auth.dart'; +// #enddocregion CanCheck +// #enddocregion ErrorHandling + +// #docregion CustomMessages +import 'package:local_auth_android/local_auth_android.dart'; +import 'package:local_auth_ios/local_auth_ios.dart'; +// #enddocregion CustomMessages + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatefulWidget { + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + // #docregion CanCheck + // #docregion ErrorHandling + final LocalAuthentication auth = LocalAuthentication(); + // #enddocregion CanCheck + // #enddocregion ErrorHandling + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('README example app'), + ), + body: const Text('See example in main.dart'), + ), + ); + } + + Future checkSupport() async { + // #docregion CanCheck + final bool canAuthenticateWithBiometrics = await auth.canCheckBiometrics; + final bool canAuthenticate = + canAuthenticateWithBiometrics || await auth.isDeviceSupported(); + // #enddocregion CanCheck + + print('Can authenticate: $canAuthenticate'); + print('Can authenticate with biometrics: $canAuthenticateWithBiometrics'); + } + + Future getEnrolledBiometrics() async { + // #docregion Enrolled + final List availableBiometrics = + await auth.getAvailableBiometrics(); + + if (availableBiometrics.isNotEmpty) { + // Some biometrics are enrolled. + } + + if (availableBiometrics.contains(BiometricType.strong) || + availableBiometrics.contains(BiometricType.face)) { + // Specific types of biometrics are available. + // Use checks like this with caution! + } + // #enddocregion Enrolled + } + + Future authenticate() async { + // #docregion AuthAny + try { + final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance'); + // #enddocregion AuthAny + print(didAuthenticate); + // #docregion AuthAny + } on PlatformException { + // ... + } + // #enddocregion AuthAny + } + + Future authenticateWithBiometrics() async { + // #docregion AuthBioOnly + final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance', + options: const AuthenticationOptions(biometricOnly: true)); + // #enddocregion AuthBioOnly + print(didAuthenticate); + } + + Future authenticateWithoutDialogs() async { + // #docregion NoErrorDialogs + try { + final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance', + options: const AuthenticationOptions(useErrorDialogs: false)); + // #enddocregion NoErrorDialogs + print(didAuthenticate ? 'Success!' : 'Failure'); + // #docregion NoErrorDialogs + } on PlatformException catch (e) { + if (e.code == auth_error.notAvailable) { + // Add handling of no hardware here. + } else if (e.code == auth_error.notEnrolled) { + // ... + } else { + // ... + } + } + // #enddocregion NoErrorDialogs + } + + Future authenticateWithErrorHandling() async { + // #docregion ErrorHandling + try { + final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance', + options: const AuthenticationOptions(useErrorDialogs: false)); + // #enddocregion ErrorHandling + print(didAuthenticate ? 'Success!' : 'Failure'); + // #docregion ErrorHandling + } on PlatformException catch (e) { + if (e.code == auth_error.notEnrolled) { + // Add handling of no hardware here. + } else if (e.code == auth_error.lockedOut || + e.code == auth_error.permanentlyLockedOut) { + // ... + } else { + // ... + } + } + // #enddocregion ErrorHandling + } + + Future authenticateWithCustomDialogMessages() async { + // #docregion CustomMessages + final bool didAuthenticate = await auth.authenticate( + localizedReason: 'Please authenticate to show account balance', + authMessages: const [ + AndroidAuthMessages( + signInTitle: 'Oops! Biometric authentication required!', + cancelButton: 'No thanks', + ), + IOSAuthMessages( + cancelButton: 'No thanks', + ), + ]); + // #enddocregion CustomMessages + print(didAuthenticate ? 'Success!' : 'Failure'); + } +} diff --git a/packages/local_auth/local_auth/example/pubspec.yaml b/packages/local_auth/local_auth/example/pubspec.yaml index ad0de2002a4c..c8496fcc0da7 100644 --- a/packages/local_auth/local_auth/example/pubspec.yaml +++ b/packages/local_auth/local_auth/example/pubspec.yaml @@ -18,6 +18,7 @@ dependencies: path: ../ dev_dependencies: + build_runner: ^2.1.10 flutter_driver: sdk: flutter integration_test: diff --git a/packages/local_auth/local_auth/lib/src/types/error_codes.dart b/packages/local_auth/local_auth/lib/error_codes.dart similarity index 73% rename from packages/local_auth/local_auth/lib/src/types/error_codes.dart rename to packages/local_auth/local_auth/lib/error_codes.dart index 3426099bacbd..15660ee948df 100644 --- a/packages/local_auth/local_auth/lib/src/types/error_codes.dart +++ b/packages/local_auth/local_auth/lib/error_codes.dart @@ -9,18 +9,18 @@ /// PIN/pattern/password (Android) on the device. const String passcodeNotSet = 'PasscodeNotSet'; -/// Indicates the user has not enrolled any fingerprints on the device. +/// Indicates the user has not enrolled any biometrics on the device. const String notEnrolled = 'NotEnrolled'; -/// Indicates the device does not have a Touch ID/fingerprint scanner. +/// Indicates the device does not have hardware support for biometrics. const String notAvailable = 'NotAvailable'; /// Indicates the device operating system is unsupported. const String otherOperatingSystem = 'OtherOperatingSystem'; -/// Indicates the API lock out due to too many attempts. +/// Indicates the API is temporarily locked out due to too many attempts. const String lockedOut = 'LockedOut'; -/// Indicates the API being disabled due to too many lock outs. +/// Indicates the API is locked out more persistently than [lockedOut]. /// Strong authentication like PIN/Pattern/Password is required to unlock. const String permanentlyLockedOut = 'PermanentlyLockedOut'; 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 77db4d57f018..206bd04f7b32 100644 --- a/packages/local_auth/local_auth/lib/src/local_auth.dart +++ b/packages/local_auth/local_auth/lib/src/local_auth.dart @@ -11,7 +11,6 @@ import 'dart:async'; import 'package:flutter/services.dart'; -import 'package:local_auth/src/types/error_codes.dart'; import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_ios/local_auth_ios.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index fa055fab17f8..087b84920bb9 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -3,7 +3,7 @@ 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/plugins/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.0.0 +version: 2.0.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/script/configs/temp_exclude_excerpt.yaml b/script/configs/temp_exclude_excerpt.yaml index fc8454d75a0c..bd73880ee5ee 100644 --- a/script/configs/temp_exclude_excerpt.yaml +++ b/script/configs/temp_exclude_excerpt.yaml @@ -15,7 +15,6 @@ - image_picker_for_web - in_app_purchase/in_app_purchase - ios_platform_images -- local_auth/local_auth - path_provider/path_provider - plugin_platform_interface - quick_actions/quick_actions From b5b88c949dafd36c669d3e6c02a3e768f0874a1c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 6 May 2022 21:59:12 -0400 Subject: [PATCH 276/844] Roll Flutter from e5718354020e to 47f48e4888e4 (5 revisions) (#5654) --- .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 8a369244f133..5eca3dc9d3fb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e5718354020e9245e1a7b2cfd2486bfb424a826e +47f48e4888e406436f1c9732e5ce7147e6401ea2 From 166ae43cae65a07d81d27d3497cf6ae1b271f398 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 6 May 2022 23:04:12 -0400 Subject: [PATCH 277/844] Roll Flutter from 47f48e4888e4 to 8e532db7b6ff (1 revision) (#5655) --- .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 5eca3dc9d3fb..2ed7c491b124 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -47f48e4888e406436f1c9732e5ce7147e6401ea2 +8e532db7b6ff7758f4fb0d1be6060d26222a1fca From c46bb0e6d73b40fe1b2d1bf62f9d28697c54e5bc Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 7 May 2022 00:09:14 -0400 Subject: [PATCH 278/844] Roll Flutter from 8e532db7b6ff to cc9ac0787871 (1 revision) (#5656) --- .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 2ed7c491b124..20ac900f049f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8e532db7b6ff7758f4fb0d1be6060d26222a1fca +cc9ac078787187b35cba5d3f65223ca682528271 From dbd0c99aa46e67949ab4fe53a9bd29bb5c9e127d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 7 May 2022 01:14:10 -0400 Subject: [PATCH 279/844] Roll Flutter from cc9ac0787871 to f56c0b3bbe69 (1 revision) (#5657) --- .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 20ac900f049f..c76300c1d811 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -cc9ac078787187b35cba5d3f65223ca682528271 +f56c0b3bbe6974982d2602a6329f5c316ef94f37 From caa5475ec359721654b1fd543a69b30e0f1559b6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 7 May 2022 02:19:12 -0400 Subject: [PATCH 280/844] Roll Flutter from f56c0b3bbe69 to d01b0f5d23f9 (1 revision) (#5658) --- .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 c76300c1d811..6e9590186026 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f56c0b3bbe6974982d2602a6329f5c316ef94f37 +d01b0f5d23f982da3d79452e281dad24818196f6 From 9db48b8cef95397bcdbd92c748a1988461b3a79a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 7 May 2022 04:29:12 -0400 Subject: [PATCH 281/844] Roll Flutter from d01b0f5d23f9 to b05b44e88ad9 (3 revisions) (#5660) --- .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 6e9590186026..383e106d95c2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -d01b0f5d23f982da3d79452e281dad24818196f6 +b05b44e88ad9cf4c0b383fed554561718bae4c3a From 1c646b6193c5fa682537db7af7a789e4aa037cb6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 7 May 2022 06:39:09 -0400 Subject: [PATCH 282/844] Roll Flutter from b05b44e88ad9 to 90868d3ba33a (3 revisions) (#5662) --- .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 383e106d95c2..3d826fc7993b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b05b44e88ad9cf4c0b383fed554561718bae4c3a +90868d3ba33a3571fb3a8918472dfa736ba5bea7 From d8e8bb5a2ab91afe884791c66ed31bba434aa947 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 7 May 2022 10:59:09 -0400 Subject: [PATCH 283/844] Roll Flutter from 90868d3ba33a to ae7fcc7e51f6 (2 revisions) (#5663) --- .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 3d826fc7993b..c370f81e1635 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -90868d3ba33a3571fb3a8918472dfa736ba5bea7 +ae7fcc7e51f604ce615bc4b2b0930091f720910e From 32f2476aca4c08d0c615a8f21fdc81afe9301832 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 7 May 2022 12:04:07 -0400 Subject: [PATCH 284/844] Roll Flutter from ae7fcc7e51f6 to c18097178c91 (1 revision) (#5664) --- .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 c370f81e1635..c459b8fc5cc7 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ae7fcc7e51f604ce615bc4b2b0930091f720910e +c18097178c91e8440db2daf9ff56a0b48ca0cee8 From c5c319aa2524583fca09a6c4ea151bf295979e67 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 7 May 2022 13:09:07 -0400 Subject: [PATCH 285/844] Roll Flutter from c18097178c91 to c6ced845e389 (2 revisions) (#5665) --- .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 c459b8fc5cc7..0b4741df25f1 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c18097178c91e8440db2daf9ff56a0b48ca0cee8 +c6ced845e389d4ba2778007642ef439d7e6654c9 From fc0dd3352b0569753a2e279a40d525dc77eb2ff7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 9 May 2022 12:49:07 -0400 Subject: [PATCH 286/844] Roll Flutter from c6ced845e389 to 4bed76757db7 (1 revision) (#5666) --- .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 0b4741df25f1..40188c21bd43 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c6ced845e389d4ba2778007642ef439d7e6654c9 +4bed76757db7a4eea74d77d5a63804ef52198f37 From 4b7b67916242a9c82283a642f169fbdb7f9045be Mon Sep 17 00:00:00 2001 From: Ahmed Ashour Date: Mon, 9 May 2022 20:54:11 +0200 Subject: [PATCH 287/844] Enable lints `library_private_types_in_public_api`, `sort_child_properties_last` and `use_key_in_widget_constructors` (#5428) --- analysis_options.yaml | 5 +- packages/camera/camera/CHANGELOG.md | 4 +- packages/camera/camera/README.md | 14 ++- packages/camera/camera/example/lib/main.dart | 60 +++++----- .../example/lib/readme_full_example.dart | 16 +-- .../camera/camera/example/test/main_test.dart | 2 +- .../camera/camera/lib/src/camera_preview.dart | 3 +- packages/camera/camera/pubspec.yaml | 2 +- packages/camera/camera_web/CHANGELOG.md | 5 + .../camera/camera_web/example/lib/main.dart | 5 +- packages/camera/camera_web/pubspec.yaml | 2 +- packages/camera/camera_windows/CHANGELOG.md | 4 +- .../camera_windows/example/lib/main.dart | 5 +- packages/camera/camera_windows/pubspec.yaml | 2 +- packages/espresso/CHANGELOG.md | 7 +- packages/espresso/example/lib/main.dart | 7 +- packages/espresso/pubspec.yaml | 2 +- .../file_selector/file_selector/CHANGELOG.md | 4 +- .../example/lib/get_directory_page.dart | 5 +- .../file_selector/example/lib/home_page.dart | 3 + .../file_selector/example/lib/main.dart | 16 ++- .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 5 +- .../example/lib/open_text_page.dart | 6 +- .../example/lib/save_text_page.dart | 3 + .../file_selector/file_selector/pubspec.yaml | 2 +- .../file_selector_macos/CHANGELOG.md | 4 +- .../example/lib/get_directory_page.dart | 5 +- .../example/lib/home_page.dart | 3 + .../file_selector_macos/example/lib/main.dart | 16 ++- .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 5 +- .../example/lib/open_text_page.dart | 6 +- .../example/lib/save_text_page.dart | 5 +- .../file_selector_macos/pubspec.yaml | 2 +- .../CHANGELOG.md | 4 +- .../pubspec.yaml | 2 +- .../file_selector_web/CHANGELOG.md | 5 + .../file_selector_web/example/lib/main.dart | 7 +- .../file_selector_web/pubspec.yaml | 2 +- .../file_selector_windows/CHANGELOG.md | 4 +- .../example/lib/get_directory_page.dart | 5 +- .../example/lib/home_page.dart | 3 + .../example/lib/main.dart | 16 ++- .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 5 +- .../example/lib/open_text_page.dart | 6 +- .../example/lib/save_text_page.dart | 5 +- .../file_selector_windows/pubspec.yaml | 2 +- .../CHANGELOG.md | 4 +- ...flutter_plugin_android_lifecycle_test.dart | 2 +- .../example/lib/main.dart | 8 +- .../pubspec.yaml | 2 +- .../google_maps_flutter/CHANGELOG.md | 6 +- .../example/lib/animate_camera.dart | 6 +- .../example/lib/lite_mode.dart | 3 +- .../google_maps_flutter/example/lib/main.dart | 8 +- .../example/lib/map_click.dart | 3 +- .../example/lib/map_coordinates.dart | 3 +- .../example/lib/map_ui.dart | 5 +- .../example/lib/marker_icons.dart | 5 +- .../example/lib/move_camera.dart | 5 +- .../example/lib/padding.dart | 5 +- .../google_maps_flutter/example/lib/page.dart | 3 +- .../example/lib/place_circle.dart | 18 +-- .../example/lib/place_marker.dart | 31 ++--- .../example/lib/place_polygon.dart | 24 ++-- .../example/lib/place_polyline.dart | 26 ++-- .../example/lib/scrolling_map.dart | 5 +- .../example/lib/snapshot.dart | 5 +- .../example/lib/tile_overlay.dart | 11 +- .../lib/src/controller.dart | 2 + .../google_maps_flutter/pubspec.yaml | 2 +- .../google_maps_flutter_web/CHANGELOG.md | 4 +- .../example/lib/main.dart | 2 +- .../google_maps_flutter_web/pubspec.yaml | 2 +- .../google_sign_in/CHANGELOG.md | 5 + .../google_sign_in/example/lib/main.dart | 8 +- .../google_sign_in/lib/widgets.dart | 4 +- .../google_sign_in/pubspec.yaml | 2 +- .../google_sign_in_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 8 +- .../google_sign_in_android/pubspec.yaml | 2 +- .../google_sign_in_ios/CHANGELOG.md | 5 + .../google_sign_in_ios/example/lib/main.dart | 8 +- .../google_sign_in_ios/pubspec.yaml | 2 +- .../google_sign_in_web/CHANGELOG.md | 5 + .../google_sign_in_web/example/lib/main.dart | 7 +- .../google_sign_in_web/pubspec.yaml | 2 +- .../image_picker/image_picker/CHANGELOG.md | 5 + .../image_picker/example/lib/main.dart | 39 +++--- .../image_picker/image_picker/pubspec.yaml | 2 +- .../image_picker_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 39 +++--- .../image_picker_android/pubspec.yaml | 2 +- .../image_picker_for_web/CHANGELOG.md | 5 + .../example/lib/main.dart | 7 +- .../image_picker_for_web/pubspec.yaml | 2 +- .../image_picker_ios/CHANGELOG.md | 4 +- .../image_picker_ios/example/lib/main.dart | 39 +++--- .../image_picker_ios/pubspec.yaml | 2 +- .../image_picker_windows/CHANGELOG.md | 4 +- .../example/lib/main.dart | 31 ++--- .../image_picker_windows/pubspec.yaml | 2 +- .../in_app_purchase/CHANGELOG.md | 4 +- .../in_app_purchase/example/lib/main.dart | 111 +++++++++--------- .../in_app_purchase/pubspec.yaml | 2 +- .../in_app_purchase_android/CHANGELOG.md | 4 +- .../example/lib/main.dart | 6 +- .../in_app_purchase_android/pubspec.yaml | 2 +- .../in_app_purchase_storekit/CHANGELOG.md | 4 +- .../example/lib/main.dart | 8 +- .../in_app_purchase_storekit/pubspec.yaml | 2 +- packages/ios_platform_images/CHANGELOG.md | 4 +- .../ios_platform_images/example/lib/main.dart | 7 +- .../example/test/widget_test.dart | 2 +- packages/ios_platform_images/pubspec.yaml | 2 +- packages/local_auth/local_auth/CHANGELOG.md | 2 + .../local_auth/example/lib/main.dart | 14 ++- .../local_auth_android/CHANGELOG.md | 4 +- .../local_auth_android/example/lib/main.dart | 14 ++- .../local_auth_android/pubspec.yaml | 2 +- .../local_auth/local_auth_ios/CHANGELOG.md | 4 +- .../local_auth_ios/example/lib/main.dart | 14 ++- .../local_auth/local_auth_ios/pubspec.yaml | 2 +- .../path_provider/path_provider/CHANGELOG.md | 4 +- .../path_provider/example/lib/main.dart | 44 +++---- .../path_provider/path_provider/pubspec.yaml | 2 +- .../path_provider_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 16 +-- .../path_provider_android/pubspec.yaml | 2 +- .../path_provider_ios/CHANGELOG.md | 5 + .../path_provider_ios/example/lib/main.dart | 14 ++- .../path_provider_ios/pubspec.yaml | 2 +- .../path_provider_linux/CHANGELOG.md | 5 + .../path_provider_linux/example/lib/main.dart | 7 +- .../path_provider_linux/pubspec.yaml | 2 +- .../path_provider_macos/CHANGELOG.md | 5 + .../path_provider_macos/example/lib/main.dart | 6 +- .../path_provider_macos/pubspec.yaml | 2 +- .../path_provider_windows/CHANGELOG.md | 5 + .../example/lib/main.dart | 6 +- .../path_provider_windows/pubspec.yaml | 2 +- .../quick_actions/quick_actions/CHANGELOG.md | 4 +- .../quick_actions/example/lib/main.dart | 6 +- .../quick_actions/quick_actions/pubspec.yaml | 2 +- .../quick_actions_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 6 +- .../quick_actions_android/pubspec.yaml | 2 +- .../quick_actions_ios/CHANGELOG.md | 4 + .../quick_actions_ios/example/lib/main.dart | 6 +- .../quick_actions_ios/pubspec.yaml | 2 +- .../shared_preferences/CHANGELOG.md | 4 +- .../shared_preferences/example/lib/main.dart | 4 +- .../shared_preferences/pubspec.yaml | 2 +- .../shared_preferences_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 4 +- .../shared_preferences_android/pubspec.yaml | 2 +- .../shared_preferences_ios/CHANGELOG.md | 5 + .../example/lib/main.dart | 4 +- .../shared_preferences_ios/pubspec.yaml | 2 +- .../shared_preferences_linux/CHANGELOG.md | 4 +- .../example/lib/main.dart | 4 +- .../shared_preferences_linux/pubspec.yaml | 2 +- .../shared_preferences_macos/CHANGELOG.md | 4 +- .../example/lib/main.dart | 4 +- .../shared_preferences_macos/pubspec.yaml | 2 +- .../shared_preferences_web/CHANGELOG.md | 5 + .../example/lib/main.dart | 7 +- .../shared_preferences_web/pubspec.yaml | 2 +- .../shared_preferences_windows/CHANGELOG.md | 5 + .../example/lib/main.dart | 4 +- .../shared_preferences_windows/pubspec.yaml | 2 +- .../url_launcher/url_launcher/CHANGELOG.md | 4 +- .../url_launcher/example/lib/main.dart | 6 +- .../url_launcher/lib/src/link.dart | 2 +- .../url_launcher/url_launcher/pubspec.yaml | 2 +- .../url_launcher_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 6 +- .../url_launcher_android/pubspec.yaml | 2 +- .../url_launcher_ios/CHANGELOG.md | 5 + .../url_launcher_ios/example/lib/main.dart | 6 +- .../url_launcher_ios/pubspec.yaml | 2 +- .../url_launcher_linux/CHANGELOG.md | 5 + .../url_launcher_linux/example/lib/main.dart | 6 +- .../url_launcher_linux/pubspec.yaml | 2 +- .../url_launcher_macos/CHANGELOG.md | 5 + .../url_launcher_macos/example/lib/main.dart | 6 +- .../url_launcher_macos/pubspec.yaml | 2 +- .../url_launcher_web/CHANGELOG.md | 5 + .../url_launcher_web/example/lib/main.dart | 7 +- .../url_launcher_web/lib/src/link.dart | 2 +- .../url_launcher_web/pubspec.yaml | 2 +- .../url_launcher_windows/CHANGELOG.md | 5 + .../example/lib/main.dart | 6 +- .../url_launcher_windows/pubspec.yaml | 2 +- .../video_player/video_player/CHANGELOG.md | 4 +- .../video_player/lib/video_player.dart | 11 +- .../video_player/video_player/pubspec.yaml | 2 +- .../video_player_android/CHANGELOG.md | 4 +- .../example/lib/mini_controller.dart | 10 +- .../video_player_android/pubspec.yaml | 2 +- .../video_player_avfoundation/CHANGELOG.md | 4 +- .../example/lib/mini_controller.dart | 10 +- .../video_player_avfoundation/pubspec.yaml | 2 +- .../video_player_web/CHANGELOG.md | 4 +- .../video_player_web/example/lib/main.dart | 7 +- .../video_player_web/pubspec.yaml | 2 +- .../webview_flutter/CHANGELOG.md | 6 +- .../webview_flutter_test.dart | 3 +- .../webview_flutter/example/lib/main.dart | 16 +-- .../webview_flutter/pubspec.yaml | 2 +- .../webview_flutter_android/CHANGELOG.md | 4 +- .../webview_flutter_test.dart | 3 +- .../example/lib/main.dart | 2 +- .../example/lib/web_view.dart | 2 +- .../lib/webview_android_widget.dart | 3 +- .../webview_flutter_android/pubspec.yaml | 2 +- .../webview_flutter_web/CHANGELOG.md | 4 +- .../example/lib/web_view.dart | 2 +- .../webview_flutter_web/pubspec.yaml | 2 +- .../webview_flutter_wkwebview/CHANGELOG.md | 4 +- .../webview_flutter_test.dart | 3 +- .../example/lib/main.dart | 2 +- .../example/lib/web_view.dart | 2 +- .../lib/src/web_kit_webview_widget.dart | 3 +- .../webview_flutter_wkwebview/pubspec.yaml | 2 +- script/tool/CHANGELOG.md | 2 +- script/tool/test/util.dart | 4 +- 229 files changed, 936 insertions(+), 526 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index f6177cd9939a..ba5e0a9c4ced 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -134,6 +134,7 @@ linter: - leading_newlines_in_multiline_strings - library_names - library_prefixes + - library_private_types_in_public_api # - lines_longer_than_80_chars # not required by flutter style - list_remove_unrelated_type # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181 @@ -197,7 +198,7 @@ linter: - recursive_getters # - sized_box_for_whitespace # not yet tested - slash_for_doc_comments - # - sort_child_properties_last # not yet tested + - sort_child_properties_last - sort_constructors_first - sort_unnamed_constructors_first - test_types_in_equals @@ -229,7 +230,7 @@ linter: - use_full_hex_values_for_flutter_colors # - use_function_type_syntax_for_parameters # not yet tested - use_is_even_rather_than_modulo - # - use_key_in_widget_constructors # not yet tested + - use_key_in_widget_constructors - use_late_for_private_fields_and_variables - use_raw_strings - use_rethrow_when_possible diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 4d7e9bbeb218..bf0ccf86a82e 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.9.4+22 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.9.4+21 diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index 97b16d20f48a..0bcaeaeb3b7c 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -89,18 +89,22 @@ Here is a small example flutter app displaying a full screen camera preview. import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; -late List cameras; +late List _cameras; Future main() async { WidgetsFlutterBinding.ensureInitialized(); - cameras = await availableCameras(); - runApp(CameraApp()); + _cameras = await availableCameras(); + runApp(const CameraApp()); } +/// CameraApp is the Main Application. class CameraApp extends StatefulWidget { + /// Default Constructor + const CameraApp({Key? key}) : super(key: key); + @override - _CameraAppState createState() => _CameraAppState(); + State createState() => _CameraAppState(); } class _CameraAppState extends State { @@ -109,7 +113,7 @@ class _CameraAppState extends State { @override void initState() { super.initState(); - controller = CameraController(cameras[0], ResolutionPreset.max); + controller = CameraController(_cameras[0], ResolutionPreset.max); controller.initialize().then((_) { if (!mounted) { return; diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index aabbe249313d..a645326f2803 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: public_member_api_docs - import 'dart:async'; import 'dart:io'; @@ -13,9 +11,13 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:video_player/video_player.dart'; +/// Camera example home widget. class CameraExampleHome extends StatefulWidget { + /// Default Constructor + const CameraExampleHome({Key? key}) : super(key: key); + @override - _CameraExampleHomeState createState() { + State createState() { return _CameraExampleHomeState(); } } @@ -34,7 +36,7 @@ IconData getCameraLensIcon(CameraLensDirection direction) { } } -void logError(String code, String? message) { +void _logError(String code, String? message) { if (message != null) { print('Error: $code\nError Message: $message'); } else { @@ -134,12 +136,6 @@ class _CameraExampleHomeState extends State children: [ Expanded( child: Container( - child: Padding( - padding: const EdgeInsets.all(1.0), - child: Center( - child: _cameraPreviewWidget(), - ), - ), decoration: BoxDecoration( color: Colors.black, border: Border.all( @@ -150,6 +146,12 @@ class _CameraExampleHomeState extends State width: 3.0, ), ), + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Center( + child: _cameraPreviewWidget(), + ), + ), ), ), _captureControlRowWidget(), @@ -233,6 +235,8 @@ class _CameraExampleHomeState extends State Container() else SizedBox( + width: 64.0, + height: 64.0, child: (localVideoController == null) ? ( // The captured image on the web contains a network-accessible URL @@ -243,6 +247,8 @@ class _CameraExampleHomeState extends State ? Image.network(imageFile!.path) : Image.file(File(imageFile!.path))) : Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.pink)), child: Center( child: AspectRatio( aspectRatio: @@ -251,11 +257,7 @@ class _CameraExampleHomeState extends State : 1.0, child: VideoPlayer(localVideoController)), ), - decoration: BoxDecoration( - border: Border.all(color: Colors.pink)), ), - width: 64.0, - height: 64.0, ), ], ), @@ -394,7 +396,6 @@ class _CameraExampleHomeState extends State mainAxisSize: MainAxisSize.max, children: [ TextButton( - child: const Text('AUTO'), style: styleAuto, onPressed: controller != null ? () => @@ -406,21 +407,22 @@ class _CameraExampleHomeState extends State showInSnackBar('Resetting exposure point'); } }, + child: const Text('AUTO'), ), TextButton( - child: const Text('LOCKED'), style: styleLocked, onPressed: controller != null ? () => onSetExposureModeButtonPressed(ExposureMode.locked) : null, + child: const Text('LOCKED'), ), TextButton( - child: const Text('RESET OFFSET'), style: styleLocked, onPressed: controller != null ? () => controller!.setExposureOffset(0.0) : null, + child: const Text('RESET OFFSET'), ), ], ), @@ -479,7 +481,6 @@ class _CameraExampleHomeState extends State mainAxisSize: MainAxisSize.max, children: [ TextButton( - child: const Text('AUTO'), style: styleAuto, onPressed: controller != null ? () => onSetFocusModeButtonPressed(FocusMode.auto) @@ -490,13 +491,14 @@ class _CameraExampleHomeState extends State } showInSnackBar('Resetting focus point'); }, + child: const Text('AUTO'), ), TextButton( - child: const Text('LOCKED'), style: styleLocked, onPressed: controller != null ? () => onSetFocusModeButtonPressed(FocusMode.locked) : null, + child: const Text('LOCKED'), ), ], ), @@ -582,13 +584,13 @@ class _CameraExampleHomeState extends State onNewCameraSelected(description); }; - if (cameras.isEmpty) { + if (_cameras.isEmpty) { _ambiguate(SchedulerBinding.instance)?.addPostFrameCallback((_) async { showInSnackBar('No camera found.'); }); return const Text('None'); } else { - for (final CameraDescription cameraDescription in cameras) { + for (final CameraDescription cameraDescription in _cameras) { toggles.add( SizedBox( width: 90.0, @@ -1014,31 +1016,35 @@ class _CameraExampleHomeState extends State } void _showCameraException(CameraException e) { - logError(e.code, e.description); + _logError(e.code, e.description); showInSnackBar('Error: ${e.code}\n${e.description}'); } } +/// CameraApp is the Main Application. class CameraApp extends StatelessWidget { + /// Default Constructor + const CameraApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { - return MaterialApp( + return const MaterialApp( home: CameraExampleHome(), ); } } -List cameras = []; +List _cameras = []; Future main() async { // Fetch the available cameras before initializing the app. try { WidgetsFlutterBinding.ensureInitialized(); - cameras = await availableCameras(); + _cameras = await availableCameras(); } on CameraException catch (e) { - logError(e.code, e.description); + _logError(e.code, e.description); } - runApp(CameraApp()); + runApp(const CameraApp()); } /// This allows a value of type T or T? to be treated as a value of type T?. diff --git a/packages/camera/camera/example/lib/readme_full_example.dart b/packages/camera/camera/example/lib/readme_full_example.dart index b25e637a0c95..a310fd9daeb0 100644 --- a/packages/camera/camera/example/lib/readme_full_example.dart +++ b/packages/camera/camera/example/lib/readme_full_example.dart @@ -2,24 +2,26 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: public_member_api_docs - // #docregion FullAppExample import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; -late List cameras; +late List _cameras; Future main() async { WidgetsFlutterBinding.ensureInitialized(); - cameras = await availableCameras(); - runApp(CameraApp()); + _cameras = await availableCameras(); + runApp(const CameraApp()); } +/// CameraApp is the Main Application. class CameraApp extends StatefulWidget { + /// Default Constructor + const CameraApp({Key? key}) : super(key: key); + @override - _CameraAppState createState() => _CameraAppState(); + State createState() => _CameraAppState(); } class _CameraAppState extends State { @@ -28,7 +30,7 @@ class _CameraAppState extends State { @override void initState() { super.initState(); - controller = CameraController(cameras[0], ResolutionPreset.max); + controller = CameraController(_cameras[0], ResolutionPreset.max); controller.initialize().then((_) { if (!mounted) { return; diff --git a/packages/camera/camera/example/test/main_test.dart b/packages/camera/camera/example/test/main_test.dart index 9a5fcdf2d5ea..6e909efcfc62 100644 --- a/packages/camera/camera/example/test/main_test.dart +++ b/packages/camera/camera/example/test/main_test.dart @@ -9,7 +9,7 @@ import 'package:flutter_test/flutter_test.dart'; void main() { testWidgets('Test snackbar', (WidgetTester tester) async { WidgetsFlutterBinding.ensureInitialized(); - await tester.pumpWidget(CameraApp()); + await tester.pumpWidget(const CameraApp()); await tester.pumpAndSettle(); expect(find.byType(SnackBar), findsOneWidget); }); diff --git a/packages/camera/camera/lib/src/camera_preview.dart b/packages/camera/camera/lib/src/camera_preview.dart index a9b3f2143b49..94ffca649fa6 100644 --- a/packages/camera/camera/lib/src/camera_preview.dart +++ b/packages/camera/camera/lib/src/camera_preview.dart @@ -10,7 +10,8 @@ import 'package:flutter/services.dart'; /// A widget showing a live camera preview. class CameraPreview extends StatelessWidget { /// Creates a preview widget for the given camera controller. - const CameraPreview(this.controller, {this.child}); + const CameraPreview(this.controller, {Key? key, this.child}) + : super(key: key); /// The controller for the camera that the preview is shown for. final CameraController controller; diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index f62777044617..fde6663844c2 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+21 +version: 0.9.4+22 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 852e4a03fd43..7a24e12e5029 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.2.1+5 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.2.1+4 * Migrates from `ui.hash*` to `Object.hash*`. diff --git a/packages/camera/camera_web/example/lib/main.dart b/packages/camera/camera_web/example/lib/main.dart index ab04ce2ca2c7..670891fa5009 100644 --- a/packages/camera/camera_web/example/lib/main.dart +++ b/packages/camera/camera_web/example/lib/main.dart @@ -4,10 +4,13 @@ import 'package:flutter/material.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); /// App for testing class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const Directionality( diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 2d1a4508eb73..8bef974190b2 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.2.1+4 +version: 0.2.1+5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index b1383dc54993..0f3bf441b05a 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.1.0+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.1.0 diff --git a/packages/camera/camera_windows/example/lib/main.dart b/packages/camera/camera_windows/example/lib/main.dart index b73e00cac52b..5758b0f1e397 100644 --- a/packages/camera/camera_windows/example/lib/main.dart +++ b/packages/camera/camera_windows/example/lib/main.dart @@ -9,11 +9,14 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// Example app for Camera Windows plugin. class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override State createState() => _MyAppState(); } diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index 1081c3dfc01f..fe655b04e8c8 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -1,8 +1,8 @@ name: camera_windows description: A Flutter plugin for getting information about and controlling the camera on Windows. -version: 0.1.0 repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 +version: 0.1.0+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/espresso/CHANGELOG.md b/packages/espresso/CHANGELOG.md index eb1f267ca1d3..dad0a912e174 100644 --- a/packages/espresso/CHANGELOG.md +++ b/packages/espresso/CHANGELOG.md @@ -1,8 +1,13 @@ +## 0.2.0+2 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.2.0+1 * Adds OS version support information to README. * Updates `androidx.test.ext:junit` and `androidx.test.ext:truth` for - compatibilty with updated Flutter template. + compatibility with updated Flutter template. ## 0.2.0 diff --git a/packages/espresso/example/lib/main.dart b/packages/espresso/example/lib/main.dart index 14f94abb28c8..741cd9cf9fa2 100644 --- a/packages/espresso/example/lib/main.dart +++ b/packages/espresso/example/lib/main.dart @@ -4,10 +4,13 @@ import 'package:flutter/material.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); /// Example app for Espresso plugin. class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + // This widget is the root of your application. @override Widget build(BuildContext context) { @@ -45,7 +48,7 @@ class _MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State<_MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<_MyHomePage> { diff --git a/packages/espresso/pubspec.yaml b/packages/espresso/pubspec.yaml index 7737fc46d4b6..ac0199cf045f 100644 --- a/packages/espresso/pubspec.yaml +++ b/packages/espresso/pubspec.yaml @@ -3,7 +3,7 @@ description: Java classes for testing Flutter apps using Espresso. Allows driving Flutter widgets from a native Espresso test. repository: https://github.com/flutter/plugins/tree/main/packages/espresso issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+espresso%22 -version: 0.2.0+1 +version: 0.2.0+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index c0821fed7446..17baf9f12469 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,7 +1,9 @@ -## NEXT +## 0.8.4+2 * Removes unnecessary imports. * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.8.4+1 diff --git a/packages/file_selector/file_selector/example/lib/get_directory_page.dart b/packages/file_selector/file_selector/example/lib/get_directory_page.dart index b3ed9d0eeaca..506134d66bd8 100644 --- a/packages/file_selector/file_selector/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector/example/lib/get_directory_page.dart @@ -7,6 +7,9 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of getDirectoryPath class GetDirectoryPage extends StatelessWidget { + /// Default Constructor + const GetDirectoryPage({Key? key}) : super(key: key); + Future _getDirectoryPath(BuildContext context) async { const String confirmButtonText = 'Choose'; final String? directoryPath = await getDirectoryPath( @@ -50,7 +53,7 @@ class GetDirectoryPage extends StatelessWidget { /// Widget that displays a text file in a dialog class TextDisplay extends StatelessWidget { /// Default Constructor - const TextDisplay(this.directoryPath); + const TextDisplay(this.directoryPath, {Key? key}) : super(key: key); /// Directory path final String directoryPath; diff --git a/packages/file_selector/file_selector/example/lib/home_page.dart b/packages/file_selector/file_selector/example/lib/home_page.dart index c598cbdf2611..9a0733b56b93 100644 --- a/packages/file_selector/file_selector/example/lib/home_page.dart +++ b/packages/file_selector/file_selector/example/lib/home_page.dart @@ -6,6 +6,9 @@ import 'package:flutter/material.dart'; /// Home Page of the application class HomePage extends StatelessWidget { + /// Default Constructor + const HomePage({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 14ce3f593f33..34f5857ab0bc 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -11,11 +11,14 @@ import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// MyApp is the Main Application class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -24,13 +27,14 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: HomePage(), + home: const HomePage(), routes: { - '/open/image': (BuildContext context) => OpenImagePage(), - '/open/images': (BuildContext context) => OpenMultipleImagesPage(), - '/open/text': (BuildContext context) => OpenTextPage(), + '/open/image': (BuildContext context) => const OpenImagePage(), + '/open/images': (BuildContext context) => + const OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => const OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), - '/directory': (BuildContext context) => GetDirectoryPage(), + '/directory': (BuildContext context) => const GetDirectoryPage(), }, ); } diff --git a/packages/file_selector/file_selector/example/lib/open_image_page.dart b/packages/file_selector/file_selector/example/lib/open_image_page.dart index 0abdba6eb72d..e520ffb402aa 100644 --- a/packages/file_selector/file_selector/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_image_page.dart @@ -10,6 +10,9 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of openFiles class OpenImagePage extends StatelessWidget { + /// Default Constructor + const OpenImagePage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'images', @@ -59,7 +62,8 @@ class OpenImagePage extends StatelessWidget { /// Widget that displays a text file in a dialog class ImageDisplay extends StatelessWidget { /// Default Constructor - const ImageDisplay(this.fileName, this.filePath); + const ImageDisplay(this.fileName, this.filePath, {Key? key}) + : super(key: key); /// Image's name final String fileName; diff --git a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart index 9a1101214aaa..e2d21c7f04d1 100644 --- a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart @@ -10,6 +10,9 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of openFiles class OpenMultipleImagesPage extends StatelessWidget { + /// Default Constructor + const OpenMultipleImagesPage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', @@ -61,7 +64,7 @@ class OpenMultipleImagesPage extends StatelessWidget { /// Widget that displays a text file in a dialog class MultipleImagesDisplay extends StatelessWidget { /// Default Constructor - const MultipleImagesDisplay(this.files); + const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); /// The files containing the images final List files; diff --git a/packages/file_selector/file_selector/example/lib/open_text_page.dart b/packages/file_selector/file_selector/example/lib/open_text_page.dart index 652e8596cf81..be48a434282b 100644 --- a/packages/file_selector/file_selector/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_text_page.dart @@ -7,6 +7,9 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of openFile class OpenTextPage extends StatelessWidget { + /// Default Constructor + const OpenTextPage({Key? key}) : super(key: key); + Future _openTextFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'text', @@ -55,7 +58,8 @@ class OpenTextPage extends StatelessWidget { /// Widget that displays a text file in a dialog class TextDisplay extends StatelessWidget { /// Default Constructor - const TextDisplay(this.fileName, this.fileContent); + const TextDisplay(this.fileName, this.fileContent, {Key? key}) + : super(key: key); /// File's name final String fileName; diff --git a/packages/file_selector/file_selector/example/lib/save_text_page.dart b/packages/file_selector/file_selector/example/lib/save_text_page.dart index 108ef89b0248..b0317844ec36 100644 --- a/packages/file_selector/file_selector/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/save_text_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Page for showing an example of saving with file_selector class SaveTextPage extends StatelessWidget { + /// Default Constructor + SaveTextPage({Key? key}) : super(key: key); + final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index c05900f650cf..7026f7f32287 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.4+1 +version: 0.8.4+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_macos/CHANGELOG.md b/packages/file_selector/file_selector_macos/CHANGELOG.md index b46a174bd323..19724a513a9e 100644 --- a/packages/file_selector/file_selector_macos/CHANGELOG.md +++ b/packages/file_selector/file_selector_macos/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.8.2+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.8.2 diff --git a/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart b/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart index 0e55df8ce622..a27ab2b40aba 100644 --- a/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a directory using `getDirectoryPath`, /// then displays the selected directory in a dialog. class GetDirectoryPage extends StatelessWidget { + /// Default Constructor + const GetDirectoryPage({Key? key}) : super(key: key); + Future _getDirectoryPath(BuildContext context) async { const String confirmButtonText = 'Choose'; final String? directoryPath = @@ -52,7 +55,7 @@ class GetDirectoryPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Creates a `TextDisplay`. - const TextDisplay(this.directoryPath); + const TextDisplay(this.directoryPath, {Key? key}) : super(key: key); /// The path selected in the dialog. final String directoryPath; diff --git a/packages/file_selector/file_selector_macos/example/lib/home_page.dart b/packages/file_selector/file_selector_macos/example/lib/home_page.dart index 958680be0e3b..4d6ca7e6e6e3 100644 --- a/packages/file_selector/file_selector_macos/example/lib/home_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/home_page.dart @@ -6,6 +6,9 @@ import 'package:flutter/material.dart'; /// Home Page of the application. class HomePage extends StatelessWidget { + /// Default Constructor + const HomePage({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( diff --git a/packages/file_selector/file_selector_macos/example/lib/main.dart b/packages/file_selector/file_selector_macos/example/lib/main.dart index a49ebac1aea5..cbe268e1c7ab 100644 --- a/packages/file_selector/file_selector_macos/example/lib/main.dart +++ b/packages/file_selector/file_selector_macos/example/lib/main.dart @@ -11,11 +11,14 @@ import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// MyApp is the Main Application. class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -24,13 +27,14 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: HomePage(), + home: const HomePage(), routes: { - '/open/image': (BuildContext context) => OpenImagePage(), - '/open/images': (BuildContext context) => OpenMultipleImagesPage(), - '/open/text': (BuildContext context) => OpenTextPage(), + '/open/image': (BuildContext context) => const OpenImagePage(), + '/open/images': (BuildContext context) => + const OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => const OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), - '/directory': (BuildContext context) => GetDirectoryPage(), + '/directory': (BuildContext context) => const GetDirectoryPage(), }, ); } diff --git a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart index aaf083603e72..1a05343b27ab 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart @@ -11,6 +11,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select an image file using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenImagePage extends StatelessWidget { + /// Default Constructor + const OpenImagePage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'images', @@ -59,7 +62,8 @@ class OpenImagePage extends StatelessWidget { /// Widget that displays an image in a dialog. class ImageDisplay extends StatelessWidget { /// Default Constructor. - const ImageDisplay(this.fileName, this.filePath); + const ImageDisplay(this.fileName, this.filePath, {Key? key}) + : super(key: key); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart index a030b8b4b10b..9c3c8e369f5b 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart @@ -11,6 +11,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select multiple image files using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenMultipleImagesPage extends StatelessWidget { + /// Default Constructor + const OpenMultipleImagesPage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', @@ -63,7 +66,7 @@ class OpenMultipleImagesPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class MultipleImagesDisplay extends StatelessWidget { /// Default Constructor. - const MultipleImagesDisplay(this.files); + const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); /// The files containing the images. final List files; diff --git a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart index fa281a0020d5..9adde400b5e1 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a text file using `openFile`, then /// displays its contents in a dialog. class OpenTextPage extends StatelessWidget { + /// Default Constructor + const OpenTextPage({Key? key}) : super(key: key); + Future _openTextFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'text', @@ -56,7 +59,8 @@ class OpenTextPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Default Constructor. - const TextDisplay(this.fileName, this.fileContent); + const TextDisplay(this.fileName, this.fileContent, {Key? key}) + : super(key: key); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart index 3989c62b7442..a44a387c019b 100644 --- a/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart @@ -9,6 +9,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a save location using `getSavePath`, /// then writes text to a file at that location. class SaveTextPage extends StatelessWidget { + /// Default Constructor + SaveTextPage({Key? key}) : super(key: key); + final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); @@ -67,8 +70,8 @@ class SaveTextPage extends StatelessWidget { primary: Colors.blue, onPrimary: Colors.white, ), - child: const Text('Press to save a text file'), onPressed: _saveFile, + child: const Text('Press to save a text file'), ), ], ), diff --git a/packages/file_selector/file_selector_macos/pubspec.yaml b/packages/file_selector/file_selector_macos/pubspec.yaml index 071d261c4bf8..41077c1c04e6 100644 --- a/packages/file_selector/file_selector_macos/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_macos description: macOS implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.2 +version: 0.8.2+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md index 100b6ad136a7..934d38827ddb 100644 --- a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md +++ b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.0.5 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.4 diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml index 42917751ed58..d280b0f5c71c 100644 --- a/packages/file_selector/file_selector_platform_interface/pubspec.yaml +++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%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.0.4 +version: 2.0.5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md index 5927239ef9e3..ce9d5590f9a9 100644 --- a/packages/file_selector/file_selector_web/CHANGELOG.md +++ b/packages/file_selector/file_selector_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.1+4 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.8.1+3 * Minor code cleanup for new analysis rules. diff --git a/packages/file_selector/file_selector_web/example/lib/main.dart b/packages/file_selector/file_selector_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/file_selector/file_selector_web/example/lib/main.dart +++ b/packages/file_selector/file_selector_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index 74d0412b440f..2e12b6d175a3 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/plugins/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.8.1+3 +version: 0.8.1+4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_windows/CHANGELOG.md b/packages/file_selector/file_selector_windows/CHANGELOG.md index ae3cd13342b1..c242717c3267 100644 --- a/packages/file_selector/file_selector_windows/CHANGELOG.md +++ b/packages/file_selector/file_selector_windows/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.8.2+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.8.2 diff --git a/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart b/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart index b282b9030d67..8fc1a9001465 100644 --- a/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a directory using `getDirectoryPath`, /// then displays the selected directory in a dialog. class GetDirectoryPage extends StatelessWidget { + /// Default Constructor + const GetDirectoryPage({Key? key}) : super(key: key); + Future _getDirectoryPath(BuildContext context) async { const String confirmButtonText = 'Choose'; final String? directoryPath = @@ -52,7 +55,7 @@ class GetDirectoryPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Creates a `TextDisplay`. - const TextDisplay(this.directoryPath); + const TextDisplay(this.directoryPath, {Key? key}) : super(key: key); /// The path selected in the dialog. final String directoryPath; diff --git a/packages/file_selector/file_selector_windows/example/lib/home_page.dart b/packages/file_selector/file_selector_windows/example/lib/home_page.dart index 958680be0e3b..4d6ca7e6e6e3 100644 --- a/packages/file_selector/file_selector_windows/example/lib/home_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/home_page.dart @@ -6,6 +6,9 @@ import 'package:flutter/material.dart'; /// Home Page of the application. class HomePage extends StatelessWidget { + /// Default Constructor + const HomePage({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( diff --git a/packages/file_selector/file_selector_windows/example/lib/main.dart b/packages/file_selector/file_selector_windows/example/lib/main.dart index a49ebac1aea5..cbe268e1c7ab 100644 --- a/packages/file_selector/file_selector_windows/example/lib/main.dart +++ b/packages/file_selector/file_selector_windows/example/lib/main.dart @@ -11,11 +11,14 @@ import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// MyApp is the Main Application. class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -24,13 +27,14 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: HomePage(), + home: const HomePage(), routes: { - '/open/image': (BuildContext context) => OpenImagePage(), - '/open/images': (BuildContext context) => OpenMultipleImagesPage(), - '/open/text': (BuildContext context) => OpenTextPage(), + '/open/image': (BuildContext context) => const OpenImagePage(), + '/open/images': (BuildContext context) => + const OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => const OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), - '/directory': (BuildContext context) => GetDirectoryPage(), + '/directory': (BuildContext context) => const GetDirectoryPage(), }, ); } diff --git a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart index aaf083603e72..1a05343b27ab 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart @@ -11,6 +11,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select an image file using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenImagePage extends StatelessWidget { + /// Default Constructor + const OpenImagePage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'images', @@ -59,7 +62,8 @@ class OpenImagePage extends StatelessWidget { /// Widget that displays an image in a dialog. class ImageDisplay extends StatelessWidget { /// Default Constructor. - const ImageDisplay(this.fileName, this.filePath); + const ImageDisplay(this.fileName, this.filePath, {Key? key}) + : super(key: key); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart index a030b8b4b10b..9c3c8e369f5b 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart @@ -11,6 +11,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select multiple image files using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenMultipleImagesPage extends StatelessWidget { + /// Default Constructor + const OpenMultipleImagesPage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', @@ -63,7 +66,7 @@ class OpenMultipleImagesPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class MultipleImagesDisplay extends StatelessWidget { /// Default Constructor. - const MultipleImagesDisplay(this.files); + const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); /// The files containing the images. final List files; diff --git a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart index fa281a0020d5..9adde400b5e1 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a text file using `openFile`, then /// displays its contents in a dialog. class OpenTextPage extends StatelessWidget { + /// Default Constructor + const OpenTextPage({Key? key}) : super(key: key); + Future _openTextFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'text', @@ -56,7 +59,8 @@ class OpenTextPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Default Constructor. - const TextDisplay(this.fileName, this.fileContent); + const TextDisplay(this.fileName, this.fileContent, {Key? key}) + : super(key: key); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart index b87a51c3877d..961e0fb2fbc3 100644 --- a/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart @@ -9,6 +9,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a save location using `getSavePath`, /// then writes text to a file at that location. class SaveTextPage extends StatelessWidget { + /// Default Constructor + SaveTextPage({Key? key}) : super(key: key); + final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); @@ -67,8 +70,8 @@ class SaveTextPage extends StatelessWidget { primary: Colors.blue, onPrimary: Colors.white, ), - child: const Text('Press to save a text file'), onPressed: _saveFile, + child: const Text('Press to save a text file'), ), ], ), diff --git a/packages/file_selector/file_selector_windows/pubspec.yaml b/packages/file_selector/file_selector_windows/pubspec.yaml index 7b035e974293..152b63ef4a3f 100644 --- a/packages/file_selector/file_selector_windows/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_windows description: Windows implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.2 +version: 0.8.2+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md index 8fdfc39f3bf9..a0a3f67bed46 100644 --- a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md +++ b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.0.6 * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.5 diff --git a/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart b/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart index 1d329a02f93b..1198c6f01806 100644 --- a/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart +++ b/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart @@ -10,6 +10,6 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('loads', (WidgetTester tester) async { - await tester.pumpWidget(MyApp()); + await tester.pumpWidget(const MyApp()); }); } diff --git a/packages/flutter_plugin_android_lifecycle/example/lib/main.dart b/packages/flutter_plugin_android_lifecycle/example/lib/main.dart index 3ef6794dfad2..c465b3b687f2 100644 --- a/packages/flutter_plugin_android_lifecycle/example/lib/main.dart +++ b/packages/flutter_plugin_android_lifecycle/example/lib/main.dart @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: public_member_api_docs - import 'package:flutter/material.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); +/// MyApp is the Main Application. class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( diff --git a/packages/flutter_plugin_android_lifecycle/pubspec.yaml b/packages/flutter_plugin_android_lifecycle/pubspec.yaml index b359cc55c351..c109d0936589 100644 --- a/packages/flutter_plugin_android_lifecycle/pubspec.yaml +++ b/packages/flutter_plugin_android_lifecycle/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_plugin_android_lifecycle description: Flutter plugin for accessing an Android Lifecycle within other plugins. repository: https://github.com/flutter/plugins/tree/main/packages/flutter_plugin_android_lifecycle issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_plugin_android_lifecycle%22 -version: 2.0.5 +version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index b0662b89d9cc..d1f2f926e2ab 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.1.5 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.1.4 @@ -223,7 +225,7 @@ GoogleMapController is now uniformly driven by implementing `DefaultLifecycleObs ## 0.5.26+1 -* Removes a errorneously added method from the GoogleMapController.h header file. +* Removes an erroneously added method from the GoogleMapController.h header file. ## 0.5.26 diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart index f8072eee7c85..09df2b98b146 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart @@ -10,8 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class AnimateCameraPage extends GoogleMapExampleAppPage { - const AnimateCameraPage() - : super(const Icon(Icons.map), 'Camera control, animated'); + const AnimateCameraPage({Key? key}) + : super(const Icon(Icons.map), 'Camera control, animated', key: key); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class AnimateCameraPage extends GoogleMapExampleAppPage { } class AnimateCamera extends StatefulWidget { - const AnimateCamera(); + const AnimateCamera({Key? key}) : super(key: key); @override State createState() => AnimateCameraState(); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart index b1b58fdc91bf..fd95cf864a7c 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart @@ -12,7 +12,8 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class LiteModePage extends GoogleMapExampleAppPage { - const LiteModePage() : super(const Icon(Icons.map), 'Lite mode'); + const LiteModePage({Key? key}) + : super(const Icon(Icons.map), 'Lite mode', key: key); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart index f4d420a72f7c..8932705bc6d5 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: public_member_api_docs - import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -43,7 +41,11 @@ final List _allPages = [ const TileOverlayPage(), ]; +/// MapsDemo is the Main Application. class MapsDemo extends StatelessWidget { + /// Default Constructor + const MapsDemo({Key? key}) : super(key: key); + void _pushPage(BuildContext context, GoogleMapExampleAppPage page) { Navigator.of(context).push(MaterialPageRoute( builder: (_) => Scaffold( @@ -72,5 +74,5 @@ void main() { if (defaultTargetPlatform == TargetPlatform.android) { AndroidGoogleMapsFlutter.useAndroidViewSurface = true; } - runApp(MaterialApp(home: MapsDemo())); + runApp(const MaterialApp(home: MapsDemo())); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart index bbe2372dce9a..9c96f25d5fa7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart @@ -12,7 +12,8 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class MapClickPage extends GoogleMapExampleAppPage { - const MapClickPage() : super(const Icon(Icons.mouse), 'Map click'); + const MapClickPage({Key? key}) + : super(const Icon(Icons.mouse), 'Map click', key: key); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart index 8e4853c040ed..1179acd46ffa 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart @@ -12,7 +12,8 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class MapCoordinatesPage extends GoogleMapExampleAppPage { - const MapCoordinatesPage() : super(const Icon(Icons.map), 'Map coordinates'); + const MapCoordinatesPage({Key? key}) + : super(const Icon(Icons.map), 'Map coordinates', key: key); @override Widget build(BuildContext context) { 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 48ef1f570e02..fbfeda56a968 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 @@ -16,7 +16,8 @@ final LatLngBounds sydneyBounds = LatLngBounds( ); class MapUiPage extends GoogleMapExampleAppPage { - const MapUiPage() : super(const Icon(Icons.map), 'User interface'); + const MapUiPage({Key? key}) + : super(const Icon(Icons.map), 'User interface', key: key); @override Widget build(BuildContext context) { @@ -25,7 +26,7 @@ class MapUiPage extends GoogleMapExampleAppPage { } class MapUiBody extends StatefulWidget { - const MapUiBody(); + const MapUiBody({Key? key}) : super(key: key); @override State createState() => MapUiBodyState(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart index 95ace9d7c482..58d266c95d1d 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart @@ -11,7 +11,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class MarkerIconsPage extends GoogleMapExampleAppPage { - const MarkerIconsPage() : super(const Icon(Icons.image), 'Marker icons'); + const MarkerIconsPage({Key? key}) + : super(const Icon(Icons.image), 'Marker icons', key: key); @override Widget build(BuildContext context) { @@ -20,7 +21,7 @@ class MarkerIconsPage extends GoogleMapExampleAppPage { } class MarkerIconsBody extends StatefulWidget { - const MarkerIconsBody(); + const MarkerIconsBody({Key? key}) : super(key: key); @override State createState() => MarkerIconsBodyState(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart index 33da90f32c1b..a6bae3009f0b 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart @@ -10,7 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class MoveCameraPage extends GoogleMapExampleAppPage { - const MoveCameraPage() : super(const Icon(Icons.map), 'Camera control'); + const MoveCameraPage({Key? key}) + : super(const Icon(Icons.map), 'Camera control', key: key); @override Widget build(BuildContext context) { @@ -19,7 +20,7 @@ class MoveCameraPage extends GoogleMapExampleAppPage { } class MoveCamera extends StatefulWidget { - const MoveCamera(); + const MoveCamera({Key? key}) : super(key: key); @override State createState() => MoveCameraState(); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart index 77091909e970..a4bfa88d559f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart @@ -9,7 +9,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PaddingPage extends GoogleMapExampleAppPage { - const PaddingPage() : super(const Icon(Icons.map), 'Add padding to the map'); + const PaddingPage({Key? key}) + : super(const Icon(Icons.map), 'Add padding to the map', key: key); @override Widget build(BuildContext context) { @@ -18,7 +19,7 @@ class PaddingPage extends GoogleMapExampleAppPage { } class MarkerIconsBody extends StatefulWidget { - const MarkerIconsBody(); + const MarkerIconsBody({Key? key}) : super(key: key); @override State createState() => MarkerIconsBodyState(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart index fb6eb3260f6d..eb01ab07a6f3 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart @@ -7,7 +7,8 @@ import 'package:flutter/material.dart'; abstract class GoogleMapExampleAppPage extends StatelessWidget { - const GoogleMapExampleAppPage(this.leading, this.title); + const GoogleMapExampleAppPage(this.leading, this.title, {Key? key}) + : super(key: key); final Widget leading; final String title; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart index c6f1509af69f..ef5033cfa1ee 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart @@ -10,8 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlaceCirclePage extends GoogleMapExampleAppPage { - const PlaceCirclePage() - : super(const Icon(Icons.linear_scale), 'Place circle'); + const PlaceCirclePage({Key? key}) + : super(const Icon(Icons.linear_scale), 'Place circle', key: key); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class PlaceCirclePage extends GoogleMapExampleAppPage { } class PlaceCircleBody extends StatefulWidget { - const PlaceCircleBody(); + const PlaceCircleBody({Key? key}) : super(key: key); @override State createState() => PlaceCircleBodyState(); @@ -170,42 +170,42 @@ class PlaceCircleBodyState extends State { Column( children: [ TextButton( - child: const Text('add'), onPressed: _add, + child: const Text('add'), ), TextButton( - child: const Text('remove'), onPressed: (selectedId == null) ? null : () => _remove(selectedId), + child: const Text('remove'), ), TextButton( - child: const Text('toggle visible'), onPressed: (selectedId == null) ? null : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), ), ], ), Column( children: [ TextButton( - child: const Text('change stroke width'), onPressed: (selectedId == null) ? null : () => _changeStrokeWidth(selectedId), + child: const Text('change stroke width'), ), TextButton( - child: const Text('change stroke color'), onPressed: (selectedId == null) ? null : () => _changeStrokeColor(selectedId), + child: const Text('change stroke color'), ), TextButton( - child: const Text('change fill color'), onPressed: (selectedId == null) ? null : () => _changeFillColor(selectedId), + child: const Text('change fill color'), ), ], ) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart index 4291cac6841e..1238d61547b8 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart @@ -15,7 +15,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlaceMarkerPage extends GoogleMapExampleAppPage { - const PlaceMarkerPage() : super(const Icon(Icons.place), 'Place marker'); + const PlaceMarkerPage({Key? key}) + : super(const Icon(Icons.place), 'Place marker', key: key); @override Widget build(BuildContext context) { @@ -24,7 +25,7 @@ class PlaceMarkerPage extends GoogleMapExampleAppPage { } class PlaceMarkerBody extends StatefulWidget { - const PlaceMarkerBody(); + const PlaceMarkerBody({Key? key}) : super(key: key); @override State createState() => PlaceMarkerBodyState(); @@ -308,13 +309,13 @@ class PlaceMarkerBodyState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ TextButton( - child: const Text('Add'), onPressed: _add, + child: const Text('Add'), ), TextButton( - child: const Text('Remove'), onPressed: selectedId == null ? null : () => _remove(selectedId), + child: const Text('Remove'), ), ], ), @@ -322,62 +323,61 @@ class PlaceMarkerBodyState extends State { alignment: WrapAlignment.spaceEvenly, children: [ TextButton( - child: const Text('change info'), onPressed: selectedId == null ? null : () => _changeInfo(selectedId), + child: const Text('change info'), ), TextButton( - child: const Text('change info anchor'), onPressed: selectedId == null ? null : () => _changeInfoAnchor(selectedId), + child: const Text('change info anchor'), ), TextButton( - child: const Text('change alpha'), onPressed: selectedId == null ? null : () => _changeAlpha(selectedId), + child: const Text('change alpha'), ), TextButton( - child: const Text('change anchor'), onPressed: selectedId == null ? null : () => _changeAnchor(selectedId), + child: const Text('change anchor'), ), TextButton( - child: const Text('toggle draggable'), onPressed: selectedId == null ? null : () => _toggleDraggable(selectedId), + child: const Text('toggle draggable'), ), TextButton( - child: const Text('toggle flat'), onPressed: selectedId == null ? null : () => _toggleFlat(selectedId), + child: const Text('toggle flat'), ), TextButton( - child: const Text('change position'), onPressed: selectedId == null ? null : () => _changePosition(selectedId), + child: const Text('change position'), ), TextButton( - child: const Text('change rotation'), onPressed: selectedId == null ? null : () => _changeRotation(selectedId), + child: const Text('change rotation'), ), TextButton( - child: const Text('toggle visible'), onPressed: selectedId == null ? null : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), ), TextButton( - child: const Text('change zIndex'), onPressed: selectedId == null ? null : () => _changeZIndex(selectedId), + child: const Text('change zIndex'), ), TextButton( - child: const Text('set marker icon'), onPressed: selectedId == null ? null : () { @@ -387,6 +387,7 @@ class PlaceMarkerBodyState extends State { }, ); }, + child: const Text('set marker icon'), ), ], ), diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart index 8d4fa3e07c36..f1932141b8ab 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart @@ -10,8 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlacePolygonPage extends GoogleMapExampleAppPage { - const PlacePolygonPage() - : super(const Icon(Icons.linear_scale), 'Place polygon'); + const PlacePolygonPage({Key? key}) + : super(const Icon(Icons.linear_scale), 'Place polygon', key: key); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class PlacePolygonPage extends GoogleMapExampleAppPage { } class PlacePolygonBody extends StatefulWidget { - const PlacePolygonBody(); + const PlacePolygonBody({Key? key}) : super(key: key); @override State createState() => PlacePolygonBodyState(); @@ -196,64 +196,64 @@ class PlacePolygonBodyState extends State { Column( children: [ TextButton( - child: const Text('add'), onPressed: _add, + child: const Text('add'), ), TextButton( - child: const Text('remove'), onPressed: (selectedId == null) ? null : () => _remove(selectedId), + child: const Text('remove'), ), TextButton( - child: const Text('toggle visible'), onPressed: (selectedId == null) ? null : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), ), TextButton( - child: const Text('toggle geodesic'), onPressed: (selectedId == null) ? null : () => _toggleGeodesic(selectedId), + child: const Text('toggle geodesic'), ), ], ), Column( children: [ TextButton( - child: const Text('add holes'), onPressed: (selectedId == null) ? null : ((polygons[selectedId]!.holes.isNotEmpty) ? null : () => _addHoles(selectedId)), + child: const Text('add holes'), ), TextButton( - child: const Text('remove holes'), onPressed: (selectedId == null) ? null : ((polygons[selectedId]!.holes.isEmpty) ? null : () => _removeHoles(selectedId)), + child: const Text('remove holes'), ), TextButton( - child: const Text('change stroke width'), onPressed: (selectedId == null) ? null : () => _changeWidth(selectedId), + child: const Text('change stroke width'), ), TextButton( - child: const Text('change stroke color'), onPressed: (selectedId == null) ? null : () => _changeStrokeColor(selectedId), + child: const Text('change stroke color'), ), TextButton( - child: const Text('change fill color'), onPressed: (selectedId == null) ? null : () => _changeFillColor(selectedId), + child: const Text('change fill color'), ), ], ) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart index 434920d293be..b3a637ce7d15 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart @@ -11,8 +11,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlacePolylinePage extends GoogleMapExampleAppPage { - const PlacePolylinePage() - : super(const Icon(Icons.linear_scale), 'Place polyline'); + const PlacePolylinePage({Key? key}) + : super(const Icon(Icons.linear_scale), 'Place polyline', key: key); @override Widget build(BuildContext context) { @@ -21,7 +21,7 @@ class PlacePolylinePage extends GoogleMapExampleAppPage { } class PlacePolylineBody extends StatefulWidget { - const PlacePolylineBody(); + const PlacePolylineBody({Key? key}) : super(key: key); @override State createState() => PlacePolylineBodyState(); @@ -234,66 +234,66 @@ class PlacePolylineBodyState extends State { Column( children: [ TextButton( - child: const Text('add'), onPressed: _add, + child: const Text('add'), ), TextButton( - child: const Text('remove'), onPressed: (selectedId == null) ? null : () => _remove(selectedId), + child: const Text('remove'), ), TextButton( - child: const Text('toggle visible'), onPressed: (selectedId == null) ? null : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), ), TextButton( - child: const Text('toggle geodesic'), onPressed: (selectedId == null) ? null : () => _toggleGeodesic(selectedId), + child: const Text('toggle geodesic'), ), ], ), Column( children: [ TextButton( - child: const Text('change width'), onPressed: (selectedId == null) ? null : () => _changeWidth(selectedId), + child: const Text('change width'), ), TextButton( - child: const Text('change color'), onPressed: (selectedId == null) ? null : () => _changeColor(selectedId), + child: const Text('change color'), ), TextButton( - child: const Text('change start cap [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changeStartCap(selectedId), + child: const Text('change start cap [Android only]'), ), TextButton( - child: const Text('change end cap [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changeEndCap(selectedId), + child: const Text('change end cap [Android only]'), ), TextButton( - child: const Text('change joint type [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changeJointType(selectedId), + child: const Text('change joint type [Android only]'), ), TextButton( - child: const Text('change pattern [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changePattern(selectedId), + child: const Text('change pattern [Android only]'), ), ], ) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart index 04769315e685..ca9d3962ddd7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart @@ -14,7 +14,8 @@ import 'page.dart'; const LatLng _center = LatLng(32.080664, 34.9563837); class ScrollingMapPage extends GoogleMapExampleAppPage { - const ScrollingMapPage() : super(const Icon(Icons.map), 'Scrolling map'); + const ScrollingMapPage({Key? key}) + : super(const Icon(Icons.map), 'Scrolling map', key: key); @override Widget build(BuildContext context) { @@ -23,7 +24,7 @@ class ScrollingMapPage extends GoogleMapExampleAppPage { } class ScrollingMapBody extends StatelessWidget { - const ScrollingMapBody(); + const ScrollingMapBody({Key? key}) : super(key: key); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart index 9afc28869490..849a9f469938 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart @@ -15,8 +15,9 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class SnapshotPage extends GoogleMapExampleAppPage { - const SnapshotPage() - : super(const Icon(Icons.camera_alt), 'Take a snapshot of the map'); + const SnapshotPage({Key? key}) + : super(const Icon(Icons.camera_alt), 'Take a snapshot of the map', + key: key); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart index dc7f7d1d6f33..81dfc2815866 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart @@ -13,7 +13,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class TileOverlayPage extends GoogleMapExampleAppPage { - const TileOverlayPage() : super(const Icon(Icons.map), 'Tile overlay'); + const TileOverlayPage({Key? key}) + : super(const Icon(Icons.map), 'Tile overlay', key: key); @override Widget build(BuildContext context) { @@ -22,7 +23,7 @@ class TileOverlayPage extends GoogleMapExampleAppPage { } class TileOverlayBody extends StatefulWidget { - const TileOverlayBody(); + const TileOverlayBody({Key? key}) : super(key: key); @override State createState() => TileOverlayBodyState(); @@ -90,16 +91,16 @@ class TileOverlayBodyState extends State { ), ), TextButton( - child: const Text('Add tile overlay'), onPressed: _addTileOverlay, + child: const Text('Add tile overlay'), ), TextButton( - child: const Text('Remove tile overlay'), onPressed: _removeTileOverlay, + child: const Text('Remove tile overlay'), ), TextButton( - child: const Text('Clear tile cache'), onPressed: _clearTileCache, + child: const Text('Clear tile cache'), ), ], ); 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 088589e4a2ce..dfc6e579d560 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 @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: library_private_types_in_public_api + part of google_maps_flutter; /// Controller for a single GoogleMap instance running on the host platform. diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index c10f9c679a17..a294dd09981f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/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.1.4 +version: 2.1.5 environment: sdk: ">=2.14.0 <3.0.0" 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 48908b984b0f..f2fe971f4591 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,8 @@ -## NEXT +## 0.3.2+2 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.3.2+1 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 10415204570c..d1ba571b5bd0 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 @@ -11,7 +11,7 @@ void main() { /// App for testing class MyApp extends StatefulWidget { @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { 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 2780175d29e2..271d87d21092 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/plugins/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.3.2+1 +version: 0.3.2+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index caab46de7c5b..5b47536cd2e2 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,3 +1,8 @@ +## 5.3.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 5.3.0 * Moves Android and iOS implementations to federated packages. diff --git a/packages/google_sign_in/google_sign_in/example/lib/main.dart b/packages/google_sign_in/google_sign_in/example/lib/main.dart index 9840a1e0a9f6..4c27543f5b18 100644 --- a/packages/google_sign_in/google_sign_in/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in/example/lib/main.dart @@ -22,7 +22,7 @@ GoogleSignIn _googleSignIn = GoogleSignIn( void main() { runApp( - MaterialApp( + const MaterialApp( title: 'Google Sign In', home: SignInDemo(), ), @@ -30,6 +30,8 @@ void main() { } class SignInDemo extends StatefulWidget { + const SignInDemo({Key? key}) : super(key: key); + @override State createState() => SignInDemoState(); } @@ -125,8 +127,8 @@ class SignInDemoState extends State { const Text('Signed in successfully.'), Text(_contactText), ElevatedButton( - child: const Text('SIGN OUT'), onPressed: _handleSignOut, + child: const Text('SIGN OUT'), ), ElevatedButton( child: const Text('REFRESH'), @@ -140,8 +142,8 @@ class SignInDemoState extends State { children: [ const Text('You are not currently signed in.'), ElevatedButton( - child: const Text('SIGN IN'), onPressed: _handleSignIn, + child: const Text('SIGN IN'), ), ], ); diff --git a/packages/google_sign_in/google_sign_in/lib/widgets.dart b/packages/google_sign_in/google_sign_in/lib/widgets.dart index 61f89133fba4..f7ae5f9a6e5f 100644 --- a/packages/google_sign_in/google_sign_in/lib/widgets.dart +++ b/packages/google_sign_in/google_sign_in/lib/widgets.dart @@ -22,11 +22,13 @@ class GoogleUserCircleAvatar extends StatelessWidget { /// in place of a profile photo, or a default profile photo if the user's /// identity does not specify a `displayName`. const GoogleUserCircleAvatar({ + Key? key, required this.identity, this.placeholderPhotoUrl, this.foregroundColor, this.backgroundColor, - }) : assert(identity != null); + }) : assert(identity != null), + super(key: key); /// A regular expression that matches against the "size directive" path /// segment of Google profile image URLs. diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 760706f2e7bc..e58b27af08b7 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/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 and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.3.0 +version: 5.3.1 environment: 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 3ffa6b5b7d6b..90069d05f442 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 5.2.7 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 5.2.6 * Switches to an internal method channel, rather than the default. diff --git a/packages/google_sign_in/google_sign_in_android/example/lib/main.dart b/packages/google_sign_in/google_sign_in_android/example/lib/main.dart index a750c330001d..526cf8b69ccf 100644 --- a/packages/google_sign_in/google_sign_in_android/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_android/example/lib/main.dart @@ -14,7 +14,7 @@ import 'package:http/http.dart' as http; void main() { runApp( - MaterialApp( + const MaterialApp( title: 'Google Sign In', home: SignInDemo(), ), @@ -22,6 +22,8 @@ void main() { } class SignInDemo extends StatefulWidget { + const SignInDemo({Key? key}) : super(key: key); + @override State createState() => SignInDemoState(); } @@ -142,8 +144,8 @@ class SignInDemoState extends State { const Text('Signed in successfully.'), Text(_contactText), ElevatedButton( - child: const Text('SIGN OUT'), onPressed: _handleSignOut, + child: const Text('SIGN OUT'), ), ElevatedButton( child: const Text('REFRESH'), @@ -157,8 +159,8 @@ class SignInDemoState extends State { children: [ const Text('You are not currently signed in.'), ElevatedButton( - child: const Text('SIGN IN'), onPressed: _handleSignIn, + child: const Text('SIGN IN'), ), ], ); 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 fa3dc1489b26..d9b272320694 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/plugins/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: 5.2.6 +version: 5.2.7 environment: sdk: ">=2.14.0 <3.0.0" 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 3ffa6b5b7d6b..90069d05f442 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,8 @@ +## 5.2.7 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 5.2.6 * Switches to an internal method channel, rather than the default. diff --git a/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart b/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart index a750c330001d..526cf8b69ccf 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart @@ -14,7 +14,7 @@ import 'package:http/http.dart' as http; void main() { runApp( - MaterialApp( + const MaterialApp( title: 'Google Sign In', home: SignInDemo(), ), @@ -22,6 +22,8 @@ void main() { } class SignInDemo extends StatefulWidget { + const SignInDemo({Key? key}) : super(key: key); + @override State createState() => SignInDemoState(); } @@ -142,8 +144,8 @@ class SignInDemoState extends State { const Text('Signed in successfully.'), Text(_contactText), ElevatedButton( - child: const Text('SIGN OUT'), onPressed: _handleSignOut, + child: const Text('SIGN OUT'), ), ElevatedButton( child: const Text('REFRESH'), @@ -157,8 +159,8 @@ class SignInDemoState extends State { children: [ const Text('You are not currently signed in.'), ElevatedButton( - child: const Text('SIGN IN'), onPressed: _handleSignIn, + child: const Text('SIGN IN'), ), ], ); 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 e5ef3832ff0e..b6f541b22ce1 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: Android implementation of the google_sign_in plugin. repository: https://github.com/flutter/plugins/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.2.6 +version: 5.2.7 environment: sdk: ">=2.14.0 <3.0.0" 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 aab4acae0376..6e87bebf5d02 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.10.1+1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.10.1 * Updates minimum Flutter version to 2.8. diff --git a/packages/google_sign_in/google_sign_in_web/example/lib/main.dart b/packages/google_sign_in/google_sign_in_web/example/lib/main.dart index d381fb4af3ab..b23015c811e8 100644 --- a/packages/google_sign_in/google_sign_in_web/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { 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 5a09453b8e86..3dcd0e8eef29 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/plugins/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.10.1 +version: 0.10.1+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index f1bf54c5cd35..a384a5272e05 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.5+1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.8.5 * Moves Android and iOS implementations to federated packages. diff --git a/packages/image_picker/image_picker/example/lib/main.dart b/packages/image_picker/image_picker/example/lib/main.dart index f3ad2375b8f2..a6f0e83c3abb 100755 --- a/packages/image_picker/image_picker/example/lib/main.dart +++ b/packages/image_picker/image_picker/example/lib/main.dart @@ -13,10 +13,12 @@ import 'package:image_picker/image_picker.dart'; import 'package:video_player/video_player.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( @@ -32,7 +34,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -177,21 +179,22 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - // Why network for web? - // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform - return Semantics( - label: 'image_picker_example_picked_image', - child: kIsWeb - ? Image.network(_imageFileList![index].path) - : Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - label: 'image_picker_example_picked_images'); + label: 'image_picker_example_picked_images', + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + ); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -417,7 +420,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller); + const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 77a50916283e..818486d8e145 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5 +version: 0.8.5+1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index 3472ade28d5b..0514fc33d420 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.4+12 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.8.4+11 * Splits from `image_picker` as a federated implementation. diff --git a/packages/image_picker/image_picker_android/example/lib/main.dart b/packages/image_picker/image_picker_android/example/lib/main.dart index 48eee35445da..d56aeb866195 100755 --- a/packages/image_picker/image_picker_android/example/lib/main.dart +++ b/packages/image_picker/image_picker_android/example/lib/main.dart @@ -13,10 +13,12 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:video_player/video_player.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( @@ -32,7 +34,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -177,21 +179,22 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - // Why network for web? - // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform - return Semantics( - label: 'image_picker_example_picked_image', - child: kIsWeb - ? Image.network(_imageFileList![index].path) - : Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - label: 'image_picker_example_picked_images'); + label: 'image_picker_example_picked_images', + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + ); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -417,7 +420,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller); + const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index dbeef9bed193..90d136c2c89b 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/plugins/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.4+11 +version: 0.8.4+12 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index dcf353fe19b1..c33b3b9981de 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.7 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.1.6 * Internal code cleanup for stricter analysis options. diff --git a/packages/image_picker/image_picker_for_web/example/lib/main.dart b/packages/image_picker/image_picker_for_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/image_picker/image_picker_for_web/example/lib/main.dart +++ b/packages/image_picker/image_picker_for_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index deccd2b50a1f..b0c5deb0da7a 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_for_web description: Web platform implementation of image_picker repository: https://github.com/flutter/plugins/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: 2.1.6 +version: 2.1.7 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index 31a0795a4e30..af391db02689 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.8.5+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.8.5 diff --git a/packages/image_picker/image_picker_ios/example/lib/main.dart b/packages/image_picker/image_picker_ios/example/lib/main.dart index 48eee35445da..d56aeb866195 100755 --- a/packages/image_picker/image_picker_ios/example/lib/main.dart +++ b/packages/image_picker/image_picker_ios/example/lib/main.dart @@ -13,10 +13,12 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:video_player/video_player.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( @@ -32,7 +34,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -177,21 +179,22 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - // Why network for web? - // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform - return Semantics( - label: 'image_picker_example_picked_image', - child: kIsWeb - ? Image.network(_imageFileList![index].path) - : Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - label: 'image_picker_example_picked_images'); + label: 'image_picker_example_picked_images', + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + ); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -417,7 +420,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller); + const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index a9cd052be56a..76ca20614f18 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 video_picker plugin. repository: https://github.com/flutter/plugins/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.5 +version: 0.8.5+1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_windows/CHANGELOG.md b/packages/image_picker/image_picker_windows/CHANGELOG.md index d98656b849c8..e72ab244068f 100644 --- a/packages/image_picker/image_picker_windows/CHANGELOG.md +++ b/packages/image_picker/image_picker_windows/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.1.0+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.1.0 diff --git a/packages/image_picker/image_picker_windows/example/lib/main.dart b/packages/image_picker/image_picker_windows/example/lib/main.dart index 577d6dadf2d9..b3ba3574522a 100644 --- a/packages/image_picker/image_picker_windows/example/lib/main.dart +++ b/packages/image_picker/image_picker_windows/example/lib/main.dart @@ -12,10 +12,12 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:video_player/video_player.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( @@ -31,7 +33,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -176,17 +178,18 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - return Semantics( - label: 'image_picker_example_picked_image', - child: Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - label: 'image_picker_example_picked_images'); + label: 'image_picker_example_picked_images', + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + return Semantics( + label: 'image_picker_example_picked_image', + child: Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + ); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -363,7 +366,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller); + const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker_windows/pubspec.yaml b/packages/image_picker/image_picker_windows/pubspec.yaml index eec41f7bfa0d..afadf3e39148 100644 --- a/packages/image_picker/image_picker_windows/pubspec.yaml +++ b/packages/image_picker/image_picker_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_windows description: Windows platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.1.0 +version: 0.1.0+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 24ef9eaffd1d..8412c23ee8e8 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,7 +1,9 @@ -## NEXT +## 3.0.3 * Removes unnecessary imports. * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 3.0.2 diff --git a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart index 651652b40c3a..34346a0bd339 100644 --- a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart @@ -33,7 +33,7 @@ const List _kProductIds = [ class _MyApp extends StatefulWidget { @override - _MyAppState createState() => _MyAppState(); + State<_MyApp> createState() => _MyAppState(); } class _MyAppState extends State<_MyApp> { @@ -247,60 +247,61 @@ class _MyAppState extends State<_MyApp> { (ProductDetails productDetails) { final PurchaseDetails? previousPurchase = purchases[productDetails.id]; return ListTile( - title: Text( - productDetails.title, - ), - subtitle: Text( - productDetails.description, - ), - trailing: previousPurchase != null - ? IconButton( - onPressed: () => confirmPriceChange(context), - icon: const Icon(Icons.upgrade)) - : TextButton( - child: Text(productDetails.price), - style: TextButton.styleFrom( - backgroundColor: Colors.green[800], - primary: Colors.white, - ), - onPressed: () { - late PurchaseParam purchaseParam; - - if (Platform.isAndroid) { - // NOTE: If you are making a subscription purchase/upgrade/downgrade, we recommend you to - // verify the latest status of you your subscription by using server side receipt validation - // and update the UI accordingly. The subscription purchase status shown - // inside the app may not be accurate. - final GooglePlayPurchaseDetails? oldSubscription = - _getOldSubscription(productDetails, purchases); - - purchaseParam = GooglePlayPurchaseParam( - productDetails: productDetails, - applicationUserName: null, - changeSubscriptionParam: (oldSubscription != null) - ? ChangeSubscriptionParam( - oldPurchaseDetails: oldSubscription, - prorationMode: ProrationMode - .immediateWithTimeProration, - ) - : null); - } else { - purchaseParam = PurchaseParam( + title: Text( + productDetails.title, + ), + subtitle: Text( + productDetails.description, + ), + trailing: previousPurchase != null + ? IconButton( + onPressed: () => confirmPriceChange(context), + icon: const Icon(Icons.upgrade)) + : TextButton( + style: TextButton.styleFrom( + backgroundColor: Colors.green[800], + primary: Colors.white, + ), + onPressed: () { + late PurchaseParam purchaseParam; + + if (Platform.isAndroid) { + // NOTE: If you are making a subscription purchase/upgrade/downgrade, we recommend you to + // verify the latest status of you your subscription by using server side receipt validation + // and update the UI accordingly. The subscription purchase status shown + // inside the app may not be accurate. + final GooglePlayPurchaseDetails? oldSubscription = + _getOldSubscription(productDetails, purchases); + + purchaseParam = GooglePlayPurchaseParam( productDetails: productDetails, applicationUserName: null, - ); - } - - if (productDetails.id == _kConsumableId) { - _inAppPurchase.buyConsumable( - purchaseParam: purchaseParam, - autoConsume: _kAutoConsume || Platform.isIOS); - } else { - _inAppPurchase.buyNonConsumable( - purchaseParam: purchaseParam); - } - }, - )); + changeSubscriptionParam: (oldSubscription != null) + ? ChangeSubscriptionParam( + oldPurchaseDetails: oldSubscription, + prorationMode: + ProrationMode.immediateWithTimeProration, + ) + : null); + } else { + purchaseParam = PurchaseParam( + productDetails: productDetails, + applicationUserName: null, + ); + } + + if (productDetails.id == _kConsumableId) { + _inAppPurchase.buyConsumable( + purchaseParam: purchaseParam, + autoConsume: _kAutoConsume || Platform.isIOS); + } else { + _inAppPurchase.buyNonConsumable( + purchaseParam: purchaseParam); + } + }, + child: Text(productDetails.price), + ), + ); }, )); @@ -340,9 +341,9 @@ class _MyAppState extends State<_MyApp> { const Divider(), GridView.count( crossAxisCount: 5, - children: tokens, shrinkWrap: true, padding: const EdgeInsets.all(16.0), + children: tokens, ) ])); } @@ -359,12 +360,12 @@ class _MyAppState extends State<_MyApp> { mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( - child: const Text('Restore purchases'), style: TextButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, primary: Colors.white, ), onPressed: () => _inAppPurchase.restorePurchases(), + child: const Text('Restore purchases'), ), ], ), diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index af8f5f3c2773..d2f875293876 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 3.0.2 +version: 3.0.3 environment: sdk: ">=2.12.0 <3.0.0" 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 2657d504ac91..4b9e58c08d06 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.2.2+4 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.2.2+3 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 939bc43bea63..1da943535f70 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 @@ -37,7 +37,7 @@ const List _kProductIds = [ class _MyApp extends StatefulWidget { @override - _MyAppState createState() => _MyAppState(); + State<_MyApp> createState() => _MyAppState(); } class _MyAppState extends State<_MyApp> { @@ -264,7 +264,6 @@ class _MyAppState extends State<_MyApp> { }, icon: const Icon(Icons.upgrade)) : TextButton( - child: Text(productDetails.price), style: TextButton.styleFrom( backgroundColor: Colors.green[800], primary: Colors.white, @@ -297,6 +296,7 @@ class _MyAppState extends State<_MyApp> { purchaseParam: purchaseParam); } }, + child: Text(productDetails.price), )); }, )); @@ -337,9 +337,9 @@ class _MyAppState extends State<_MyApp> { const Divider(), GridView.count( crossAxisCount: 5, - children: tokens, shrinkWrap: true, padding: const EdgeInsets.all(16.0), + children: tokens, ) ])); } 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 62888e6dfb73..7de778177c31 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/plugins/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.2.2+3 +version: 0.2.2+4 environment: sdk: ">=2.14.0 <3.0.0" 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 403ee32be2ae..7342077ab176 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.3.0+6 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.3.0+5 diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart index 2ee2deb7fe35..f45a8c7f8741 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart @@ -37,7 +37,7 @@ const List _kProductIds = [ class _MyApp extends StatefulWidget { @override - _MyAppState createState() => _MyAppState(); + State<_MyApp> createState() => _MyAppState(); } class _MyAppState extends State<_MyApp> { @@ -259,7 +259,6 @@ class _MyAppState extends State<_MyApp> { }, icon: const Icon(Icons.upgrade)) : TextButton( - child: Text(productDetails.price), style: TextButton.styleFrom( backgroundColor: Colors.green[800], primary: Colors.white, @@ -278,6 +277,7 @@ class _MyAppState extends State<_MyApp> { purchaseParam: purchaseParam); } }, + child: Text(productDetails.price), )); }, )); @@ -318,9 +318,9 @@ class _MyAppState extends State<_MyApp> { const Divider(), GridView.count( crossAxisCount: 5, - children: tokens, shrinkWrap: true, padding: const EdgeInsets.all(16.0), + children: tokens, ) ])); } @@ -337,12 +337,12 @@ class _MyAppState extends State<_MyApp> { mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( - child: const Text('Restore purchases'), style: TextButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, primary: Colors.white, ), onPressed: () => _iapStoreKitPlatform.restorePurchases(), + child: const Text('Restore purchases'), ), ], ), 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 9693c186119c..24b693c98c36 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 platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/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.0+5 +version: 0.3.0+6 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index 0a3755afa7e7..cf2632feaac7 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.2.0+6 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.2.0+5 diff --git a/packages/ios_platform_images/example/lib/main.dart b/packages/ios_platform_images/example/lib/main.dart index ecdfeb2cba01..929814ecce00 100644 --- a/packages/ios_platform_images/example/lib/main.dart +++ b/packages/ios_platform_images/example/lib/main.dart @@ -5,12 +5,15 @@ import 'package:flutter/material.dart'; import 'package:ios_platform_images/ios_platform_images.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); /// Main widget for the example app. class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/ios_platform_images/example/test/widget_test.dart b/packages/ios_platform_images/example/test/widget_test.dart index 18e9e657ddb9..f3cd4c68b65b 100644 --- a/packages/ios_platform_images/example/test/widget_test.dart +++ b/packages/ios_platform_images/example/test/widget_test.dart @@ -12,7 +12,7 @@ import 'package:ios_platform_images_example/main.dart'; void main() { testWidgets('Verify loads image', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); + await tester.pumpWidget(const MyApp()); expect( find.byWidgetPredicate( diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index 7f80714e4c1c..41a177560299 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -2,7 +2,7 @@ name: ios_platform_images description: A plugin to share images between Flutter and iOS in add-to-app setups. repository: https://github.com/flutter/plugins/tree/main/packages/ios_platform_images issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+ios_platform_images%22 -version: 0.2.0+5 +version: 0.2.0+6 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 570d8eef7b9b..6765b497e506 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -4,6 +4,8 @@ * Updates README to match API changes in 2.0, and to improve clarity in general. * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.0 diff --git a/packages/local_auth/local_auth/example/lib/main.dart b/packages/local_auth/local_auth/example/lib/main.dart index 92ad7cf4fb3f..cc687f562402 100644 --- a/packages/local_auth/local_auth/example/lib/main.dart +++ b/packages/local_auth/local_auth/example/lib/main.dart @@ -11,12 +11,14 @@ import 'package:flutter/services.dart'; import 'package:local_auth/local_auth.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { @@ -169,14 +171,14 @@ class _MyAppState extends State { const Divider(height: 100), Text('Can check biometrics: $_canCheckBiometrics\n'), ElevatedButton( - child: const Text('Check biometrics'), onPressed: _checkBiometrics, + child: const Text('Check biometrics'), ), const Divider(height: 100), Text('Available biometrics: $_availableBiometrics\n'), ElevatedButton( - child: const Text('Get available biometrics'), onPressed: _getAvailableBiometrics, + child: const Text('Get available biometrics'), ), const Divider(height: 100), Text('Current State: $_authorized\n'), @@ -195,6 +197,7 @@ class _MyAppState extends State { Column( children: [ ElevatedButton( + onPressed: _authenticate, child: Row( mainAxisSize: MainAxisSize.min, children: const [ @@ -202,9 +205,9 @@ class _MyAppState extends State { Icon(Icons.perm_device_information), ], ), - onPressed: _authenticate, ), ElevatedButton( + onPressed: _authenticateWithBiometrics, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -214,7 +217,6 @@ class _MyAppState extends State { const Icon(Icons.fingerprint), ], ), - onPressed: _authenticateWithBiometrics, ), ], ), diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 6afcf1ffed07..9f9bd7b535a9 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 1.0.3 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 1.0.2 diff --git a/packages/local_auth/local_auth_android/example/lib/main.dart b/packages/local_auth/local_auth_android/example/lib/main.dart index 29b1d66440eb..016d955f0a3f 100644 --- a/packages/local_auth/local_auth_android/example/lib/main.dart +++ b/packages/local_auth/local_auth_android/example/lib/main.dart @@ -12,12 +12,14 @@ import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { @@ -174,14 +176,14 @@ class _MyAppState extends State { Text( 'Device supports biometrics: $_deviceSupportsBiometrics\n'), ElevatedButton( - child: const Text('Check biometrics'), onPressed: _checkBiometrics, + child: const Text('Check biometrics'), ), const Divider(height: 100), Text('Enrolled biometrics: $_enrolledBiometrics\n'), ElevatedButton( - child: const Text('Get enrolled biometrics'), onPressed: _getEnrolledBiometrics, + child: const Text('Get enrolled biometrics'), ), const Divider(height: 100), Text('Current State: $_authorized\n'), @@ -200,6 +202,7 @@ class _MyAppState extends State { Column( children: [ ElevatedButton( + onPressed: _authenticate, child: Row( mainAxisSize: MainAxisSize.min, children: const [ @@ -207,9 +210,9 @@ class _MyAppState extends State { Icon(Icons.perm_device_information), ], ), - onPressed: _authenticate, ), ElevatedButton( + onPressed: _authenticateWithBiometrics, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -219,7 +222,6 @@ class _MyAppState extends State { const Icon(Icons.fingerprint), ], ), - onPressed: _authenticateWithBiometrics, ), ], ), diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index aad0b9b92e70..cdd4e8225504 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.2 +version: 1.0.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index ca6ac43eb52f..2237cbe216f0 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 1.0.5 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 1.0.4 diff --git a/packages/local_auth/local_auth_ios/example/lib/main.dart b/packages/local_auth/local_auth_ios/example/lib/main.dart index a8bf23b78a52..479a96ba809c 100644 --- a/packages/local_auth/local_auth_ios/example/lib/main.dart +++ b/packages/local_auth/local_auth_ios/example/lib/main.dart @@ -12,12 +12,14 @@ import 'package:local_auth_ios/local_auth_ios.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { @@ -173,14 +175,14 @@ class _MyAppState extends State { const Divider(height: 100), Text('Device supports biometrics: $_canCheckBiometrics\n'), ElevatedButton( - child: const Text('Check biometrics'), onPressed: _checkBiometrics, + child: const Text('Check biometrics'), ), const Divider(height: 100), Text('Enrolled biometrics: $_enrolledBiometrics\n'), ElevatedButton( - child: const Text('Get enrolled biometrics'), onPressed: _getEnrolledBiometrics, + child: const Text('Get enrolled biometrics'), ), const Divider(height: 100), Text('Current State: $_authorized\n'), @@ -199,6 +201,7 @@ class _MyAppState extends State { Column( children: [ ElevatedButton( + onPressed: _authenticate, child: Row( mainAxisSize: MainAxisSize.min, children: const [ @@ -206,9 +209,9 @@ class _MyAppState extends State { Icon(Icons.perm_device_information), ], ), - onPressed: _authenticate, ), ElevatedButton( + onPressed: _authenticateWithBiometrics, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -218,7 +221,6 @@ class _MyAppState extends State { const Icon(Icons.fingerprint), ], ), - onPressed: _authenticateWithBiometrics, ), ], ), diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index 77ab74d383c8..dded42b673f4 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.4 +version: 1.0.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index a26a8901d1d7..990021671801 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,7 +1,9 @@ -## NEXT +## 2.0.10 * Removes unnecessary imports. * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.9 diff --git a/packages/path_provider/path_provider/example/lib/main.dart b/packages/path_provider/path_provider/example/lib/main.dart index 90c2ccb93154..cb9c2eb1798d 100644 --- a/packages/path_provider/path_provider/example/lib/main.dart +++ b/packages/path_provider/path_provider/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -31,7 +33,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -138,10 +140,10 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: _requestTempDirectory, child: const Text( 'Get Temporary Directory', ), - onPressed: _requestTempDirectory, ), ), FutureBuilder( @@ -155,10 +157,10 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: _requestAppDocumentsDirectory, child: const Text( 'Get Application Documents Directory', ), - onPressed: _requestAppDocumentsDirectory, ), ), FutureBuilder( @@ -172,10 +174,10 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: _requestAppSupportDirectory, child: const Text( 'Get Application Support Directory', ), - onPressed: _requestAppSupportDirectory, ), ), FutureBuilder( @@ -189,13 +191,13 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: + Platform.isAndroid ? null : _requestAppLibraryDirectory, child: Text( Platform.isAndroid ? 'Application Library Directory unavailable' : 'Get Application Library Directory', ), - onPressed: - Platform.isAndroid ? null : _requestAppLibraryDirectory, ), ), FutureBuilder( @@ -209,14 +211,14 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: !Platform.isAndroid + ? null + : _requestExternalStorageDirectory, child: Text( !Platform.isAndroid ? 'External storage is unavailable' : 'Get External Storage Directory', ), - onPressed: !Platform.isAndroid - ? null - : _requestExternalStorageDirectory, ), ), FutureBuilder( @@ -230,11 +232,6 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: Text( - !Platform.isAndroid - ? 'External directories are unavailable' - : 'Get External Storage Directories', - ), onPressed: !Platform.isAndroid ? null : () { @@ -242,6 +239,11 @@ class _MyHomePageState extends State { StorageDirectory.music, ); }, + child: Text( + !Platform.isAndroid + ? 'External directories are unavailable' + : 'Get External Storage Directories', + ), ), ), FutureBuilder?>( @@ -255,14 +257,14 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: !Platform.isAndroid + ? null + : _requestExternalCacheDirectories, child: Text( !Platform.isAndroid ? 'External directories are unavailable' : 'Get External Cache Directories', ), - onPressed: !Platform.isAndroid - ? null - : _requestExternalCacheDirectories, ), ), FutureBuilder?>( @@ -276,14 +278,14 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: Platform.isAndroid || Platform.isIOS + ? null + : _requestDownloadsDirectory, child: Text( Platform.isAndroid || Platform.isIOS ? 'Downloads directory is unavailable' : 'Get Downloads Directory', ), - onPressed: Platform.isAndroid || Platform.isIOS - ? null - : _requestDownloadsDirectory, ), ), FutureBuilder( diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml index 30ddfdcfb119..6ca8325843e4 100644 --- a/packages/path_provider/path_provider/pubspec.yaml +++ b/packages/path_provider/path_provider/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider description: Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.9 +version: 2.0.10 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index 31f8c81f8a65..4b15e2605038 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.14 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.13 * Fixes typing build warning. diff --git a/packages/path_provider/path_provider_android/example/lib/main.dart b/packages/path_provider/path_provider_android/example/lib/main.dart index 6e04f865bfcf..fc9424a33542 100644 --- a/packages/path_provider/path_provider_android/example/lib/main.dart +++ b/packages/path_provider/path_provider_android/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -29,7 +31,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -121,8 +123,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Temporary Directory'), onPressed: _requestTempDirectory, + child: const Text('Get Temporary Directory'), ), ), FutureBuilder( @@ -130,8 +132,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Documents Directory'), onPressed: _requestAppDocumentsDirectory, + child: const Text('Get Application Documents Directory'), ), ), FutureBuilder( @@ -139,8 +141,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Support Directory'), onPressed: _requestAppSupportDirectory, + child: const Text('Get Application Support Directory'), ), ), FutureBuilder( @@ -148,8 +150,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get External Storage Directory'), onPressed: _requestExternalStorageDirectory, + child: const Text('Get External Storage Directory'), ), ), FutureBuilder( @@ -174,8 +176,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get External Cache Directories'), onPressed: _requestExternalCacheDirectories, + child: const Text('Get External Cache Directories'), ), ), ]), diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index 93ed9848f75b..f1dc92abdefc 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.13 +version: 2.0.14 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_ios/CHANGELOG.md b/packages/path_provider/path_provider_ios/CHANGELOG.md index 543af778d2e2..1940f5c7888e 100644 --- a/packages/path_provider/path_provider_ios/CHANGELOG.md +++ b/packages/path_provider/path_provider_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.9 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.8 * Switches to a package-internal implementation of the platform interface. diff --git a/packages/path_provider/path_provider_ios/example/lib/main.dart b/packages/path_provider/path_provider_ios/example/lib/main.dart index 8c8d5410f923..d7140b76a06b 100644 --- a/packages/path_provider/path_provider_ios/example/lib/main.dart +++ b/packages/path_provider/path_provider_ios/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -29,7 +31,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -90,8 +92,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Temporary Directory'), onPressed: _requestTempDirectory, + child: const Text('Get Temporary Directory'), ), ), FutureBuilder( @@ -99,8 +101,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Documents Directory'), onPressed: _requestAppDocumentsDirectory, + child: const Text('Get Application Documents Directory'), ), ), FutureBuilder( @@ -108,8 +110,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Support Directory'), onPressed: _requestAppSupportDirectory, + child: const Text('Get Application Support Directory'), ), ), FutureBuilder( @@ -117,8 +119,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Library Directory'), onPressed: _requestAppLibraryDirectory, + child: const Text('Get Application Library Directory'), ), ), FutureBuilder( diff --git a/packages/path_provider/path_provider_ios/pubspec.yaml b/packages/path_provider/path_provider_ios/pubspec.yaml index 282f8e4f0ebd..d6c7de108c97 100644 --- a/packages/path_provider/path_provider_ios/pubspec.yaml +++ b/packages/path_provider/path_provider_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_ios description: iOS implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.8 +version: 2.0.9 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_linux/CHANGELOG.md b/packages/path_provider/path_provider_linux/CHANGELOG.md index 55235c3542f9..c9c4bb3cc906 100644 --- a/packages/path_provider/path_provider_linux/CHANGELOG.md +++ b/packages/path_provider/path_provider_linux/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.6 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.1.5 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_linux/example/lib/main.dart b/packages/path_provider/path_provider_linux/example/lib/main.dart index d365e6bdeab4..1c7c7e87397a 100644 --- a/packages/path_provider/path_provider_linux/example/lib/main.dart +++ b/packages/path_provider/path_provider_linux/example/lib/main.dart @@ -7,13 +7,16 @@ import 'package:flutter/services.dart'; import 'package:path_provider_linux/path_provider_linux.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// Sample app class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index 91304fc0b268..16438a3870d1 100644 --- a/packages/path_provider/path_provider_linux/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_linux description: Linux implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.1.5 +version: 2.1.6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/path_provider/path_provider_macos/CHANGELOG.md b/packages/path_provider/path_provider_macos/CHANGELOG.md index 047792f8bcc4..c59ba971d461 100644 --- a/packages/path_provider/path_provider_macos/CHANGELOG.md +++ b/packages/path_provider/path_provider_macos/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.6 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.5 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_macos/example/lib/main.dart b/packages/path_provider/path_provider_macos/example/lib/main.dart index 67a0eb32eeda..13a6fada5fef 100644 --- a/packages/path_provider/path_provider_macos/example/lib/main.dart +++ b/packages/path_provider/path_provider_macos/example/lib/main.dart @@ -8,13 +8,15 @@ import 'package:flutter/material.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// Sample app class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/path_provider/path_provider_macos/pubspec.yaml b/packages/path_provider/path_provider_macos/pubspec.yaml index 2451b6dedf80..444165b86c3f 100644 --- a/packages/path_provider/path_provider_macos/pubspec.yaml +++ b/packages/path_provider/path_provider_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_macos description: macOS implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.5 +version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/path_provider/path_provider_windows/CHANGELOG.md b/packages/path_provider/path_provider_windows/CHANGELOG.md index cd33e85a851d..014b6b36da2b 100644 --- a/packages/path_provider/path_provider_windows/CHANGELOG.md +++ b/packages/path_provider/path_provider_windows/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.6 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.5 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_windows/example/lib/main.dart b/packages/path_provider/path_provider_windows/example/lib/main.dart index 509292bf7405..4c63d245a16a 100644 --- a/packages/path_provider/path_provider_windows/example/lib/main.dart +++ b/packages/path_provider/path_provider_windows/example/lib/main.dart @@ -8,13 +8,15 @@ import 'package:flutter/material.dart'; import 'package:path_provider_windows/path_provider_windows.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// Sample app class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/path_provider/path_provider_windows/pubspec.yaml b/packages/path_provider/path_provider_windows/pubspec.yaml index 873fa0a6861b..49afdd6293e7 100644 --- a/packages/path_provider/path_provider_windows/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_windows description: Windows implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.5 +version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index c30d7052320e..73540a863364 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,8 +1,10 @@ -## NEXT +## 0.6.0+11 * Removes unnecessary imports. * Updates minimum Flutter version to 2.8. * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.6.0+10 diff --git a/packages/quick_actions/quick_actions/example/lib/main.dart b/packages/quick_actions/quick_actions/example/lib/main.dart index 1ce6f51d71de..cafbf0c351d9 100644 --- a/packages/quick_actions/quick_actions/example/lib/main.dart +++ b/packages/quick_actions/quick_actions/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:quick_actions/quick_actions.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -28,7 +30,7 @@ class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/quick_actions/quick_actions/pubspec.yaml b/packages/quick_actions/quick_actions/pubspec.yaml index 8ef2d3ab4e02..37e8dbe5f3e3 100644 --- a/packages/quick_actions/quick_actions/pubspec.yaml +++ b/packages/quick_actions/quick_actions/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for creating shortcuts on home screen, also known as Quick Actions on iOS and App Shortcuts on Android. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%22 -version: 0.6.0+10 +version: 0.6.0+11 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions_android/CHANGELOG.md b/packages/quick_actions/quick_actions_android/CHANGELOG.md index 98e8cf5e333b..56accc9d044c 100644 --- a/packages/quick_actions/quick_actions_android/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.6.0+10 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.6.0+9 * Switches to a package-internal implementation of the platform interface. \ No newline at end of file diff --git a/packages/quick_actions/quick_actions_android/example/lib/main.dart b/packages/quick_actions/quick_actions_android/example/lib/main.dart index 06f141073b33..d8b7832bf9dc 100644 --- a/packages/quick_actions/quick_actions_android/example/lib/main.dart +++ b/packages/quick_actions/quick_actions_android/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:quick_actions_android/quick_actions_android.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -28,7 +30,7 @@ class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/quick_actions/quick_actions_android/pubspec.yaml b/packages/quick_actions/quick_actions_android/pubspec.yaml index cf9971dca945..4ddbc79ee5e9 100644 --- a/packages/quick_actions/quick_actions_android/pubspec.yaml +++ b/packages/quick_actions/quick_actions_android/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_android description: An implementation for the Android platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.6.0+9 +version: 0.6.0+10 environment: sdk: ">=2.15.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions_ios/CHANGELOG.md b/packages/quick_actions/quick_actions_ios/CHANGELOG.md index d48afbd8d13a..56accc9d044c 100644 --- a/packages/quick_actions/quick_actions_ios/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.0+10 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.6.0+9 diff --git a/packages/quick_actions/quick_actions_ios/example/lib/main.dart b/packages/quick_actions/quick_actions_ios/example/lib/main.dart index 5173d952d623..008917b724e0 100644 --- a/packages/quick_actions/quick_actions_ios/example/lib/main.dart +++ b/packages/quick_actions/quick_actions_ios/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:quick_actions_ios/quick_actions_ios.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -28,7 +30,7 @@ class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/quick_actions/quick_actions_ios/pubspec.yaml b/packages/quick_actions/quick_actions_ios/pubspec.yaml index 26644ba12fde..47748b9789ad 100644 --- a/packages/quick_actions/quick_actions_ios/pubspec.yaml +++ b/packages/quick_actions/quick_actions_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_ios description: An implementation for the iOS platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.6.0+9 +version: 0.6.0+10 environment: sdk: ">=2.15.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index 84566e26e2c0..22c39aad98fd 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.0.14 * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.13 diff --git a/packages/shared_preferences/shared_preferences/example/lib/main.dart b/packages/shared_preferences/shared_preferences/example/lib/main.dart index 43f3c78ad920..a2e72b446925 100644 --- a/packages/shared_preferences/shared_preferences/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index 39b48ef51ed2..4218095c0efe 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.13 +version: 2.0.14 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index 5321e869c497..51e99ec6d3d5 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.12 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.11 * Switches to an in-package method channel implementation. diff --git a/packages/shared_preferences/shared_preferences_android/example/lib/main.dart b/packages/shared_preferences/shared_preferences_android/example/lib/main.dart index 06ee9434ba84..bb513b09f6d5 100644 --- a/packages/shared_preferences/shared_preferences_android/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_android/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_android/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/pubspec.yaml index 7eb180f3ab48..2d8cc88d3703 100644 --- a/packages/shared_preferences/shared_preferences_android/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_android description: Android implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.11 +version: 2.0.12 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md index a5cc1d34e034..29ade8d496a1 100644 --- a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.1.0 * Upgrades to using Pigeon. diff --git a/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart b/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart index 06ee9434ba84..bb513b09f6d5 100644 --- a/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml index 33bf5baffd18..a8bde2e9f87f 100644 --- a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_ios description: iOS implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.0 +version: 2.1.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md index 34dd631746bb..f0cb8322f385 100644 --- a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.1.1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.1.0 diff --git a/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart b/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart index 4b71c7ea3beb..d51be33baeed 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_linux/shared_preferences_linux.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml index 8ab692a613e2..8f3ce1723bc9 100644 --- a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_linux description: Linux implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.0 +version: 2.1.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md index 0f194de44224..8ba116a74fe0 100644 --- a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.0.4 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.3 diff --git a/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart b/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart index 349e9c45405a..e6bbe5931471 100644 --- a/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml index 0873696e6d4f..615d0b05ba99 100644 --- a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_macos description: macOS implementation of the shared_preferences plugin. repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.3 +version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md index 2a6ffa20e37b..9ea249034105 100644 --- a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.4 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.3 * Fixes newly enabled analyzer options. diff --git a/packages/shared_preferences/shared_preferences_web/example/lib/main.dart b/packages/shared_preferences/shared_preferences_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/shared_preferences/shared_preferences_web/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/shared_preferences/shared_preferences_web/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/pubspec.yaml index 232c87c426fc..9ff76d27714c 100644 --- a/packages/shared_preferences/shared_preferences_web/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_web description: Web platform implementation of shared_preferences repository: https://github.com/flutter/plugins/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.0.3 +version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md index 6c96681ce5b7..f79f9e3d5d39 100644 --- a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.1.0 * Deprecated `SharedPreferencesWindows.instance` in favor of `SharedPreferencesStorePlatform.instance`. diff --git a/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart b/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart index 40a9159cee70..74d5e4c68772 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_windows/shared_preferences_windows.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml index 6dcb5997f131..99326cb24f18 100644 --- a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_windows description: Windows implementation of shared_preferences repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.0 +version: 2.1.1 environment: sdk: '>=2.12.0 <3.0.0' diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index b25956fd5919..493412c3e006 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 6.1.1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 6.1.0 diff --git a/packages/url_launcher/url_launcher/example/lib/main.dart b/packages/url_launcher/url_launcher/example/lib/main.dart index 898e80661296..a538940f1a68 100644 --- a/packages/url_launcher/url_launcher/example/lib/main.dart +++ b/packages/url_launcher/url_launcher/example/lib/main.dart @@ -11,10 +11,12 @@ import 'package:url_launcher/link.dart'; import 'package:url_launcher/url_launcher.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -32,7 +34,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher/lib/src/link.dart b/packages/url_launcher/url_launcher/lib/src/link.dart index 76cb97748003..8c0c18e820e3 100644 --- a/packages/url_launcher/url_launcher/lib/src/link.dart +++ b/packages/url_launcher/url_launcher/lib/src/link.dart @@ -86,7 +86,7 @@ class Link extends StatelessWidget implements LinkInfo { /// event channel messages to instruct the framework to push the route name. class DefaultLinkDelegate extends StatelessWidget { /// Creates a delegate for the given [link]. - const DefaultLinkDelegate(this.link); + const DefaultLinkDelegate(this.link, {Key? key}) : super(key: key); /// Given a [link], creates an instance of [DefaultLinkDelegate]. /// diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 6803d71032cb..c14b62a1e70a 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/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.1.0 +version: 6.1.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md index 69b96156d849..887178c479e4 100644 --- a/packages/url_launcher/url_launcher_android/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.0.17 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 6.0.16 * Adds fallback querying for `canLaunch` with web URLs, to avoid false negatives diff --git a/packages/url_launcher/url_launcher_android/example/lib/main.dart b/packages/url_launcher/url_launcher_android/example/lib/main.dart index 7abc73430e8b..672ae4a27665 100644 --- a/packages/url_launcher/url_launcher_android/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_android/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -31,7 +33,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index 3230dfeffd2e..3c80170f1422 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_android description: Android implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.16 +version: 6.0.17 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_ios/CHANGELOG.md b/packages/url_launcher/url_launcher_ios/CHANGELOG.md index 6e0c8d6a20d7..5f6dd37142bb 100644 --- a/packages/url_launcher/url_launcher_ios/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.0.16 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 6.0.15 * Switches to an in-package method channel implementation. diff --git a/packages/url_launcher/url_launcher_ios/example/lib/main.dart b/packages/url_launcher/url_launcher_ios/example/lib/main.dart index 2f73622ebb41..7aa3a4b74e83 100644 --- a/packages/url_launcher/url_launcher_ios/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_ios/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -31,7 +33,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_ios/pubspec.yaml b/packages/url_launcher/url_launcher_ios/pubspec.yaml index 8a5bfd20c8f4..0b21bad35204 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/plugins/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.0.15 +version: 6.0.16 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_linux/CHANGELOG.md b/packages/url_launcher/url_launcher_linux/CHANGELOG.md index 0fc373f2ebb1..27c18a66805b 100644 --- a/packages/url_launcher/url_launcher_linux/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_linux/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.0.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 3.0.0 * Changes the major version since, due to a typo in `default_package` in diff --git a/packages/url_launcher/url_launcher_linux/example/lib/main.dart b/packages/url_launcher/url_launcher_linux/example/lib/main.dart index a9a5d22dadd5..0b985e78ac0d 100644 --- a/packages/url_launcher/url_launcher_linux/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_linux/example/lib/main.dart @@ -9,10 +9,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -30,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_linux/pubspec.yaml b/packages/url_launcher/url_launcher_linux/pubspec.yaml index cb9a0be0aa41..c9472045e499 100644 --- a/packages/url_launcher/url_launcher_linux/pubspec.yaml +++ b/packages/url_launcher/url_launcher_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_linux description: Linux implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.0 +version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_macos/CHANGELOG.md b/packages/url_launcher/url_launcher_macos/CHANGELOG.md index 082bc45fc2e8..2fa5e918eadd 100644 --- a/packages/url_launcher/url_launcher_macos/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_macos/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.0.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 3.0.0 * Changes the major version since, due to a typo in `default_package` in diff --git a/packages/url_launcher/url_launcher_macos/example/lib/main.dart b/packages/url_launcher/url_launcher_macos/example/lib/main.dart index a9a5d22dadd5..0b985e78ac0d 100644 --- a/packages/url_launcher/url_launcher_macos/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_macos/example/lib/main.dart @@ -9,10 +9,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -30,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_macos/pubspec.yaml b/packages/url_launcher/url_launcher_macos/pubspec.yaml index 8b5183b1914d..edda6b67cfb3 100644 --- a/packages/url_launcher/url_launcher_macos/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_macos description: macOS implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.0 +version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md index a434b7af70c2..b53a92cee707 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.10 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.9 - Fixes invalid routes when opening a `Link` in a new tab diff --git a/packages/url_launcher/url_launcher_web/example/lib/main.dart b/packages/url_launcher/url_launcher_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/url_launcher/url_launcher_web/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { 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 4498e74ea9ce..eccd9aef80e3 100644 --- a/packages/url_launcher/url_launcher_web/lib/src/link.dart +++ b/packages/url_launcher/url_launcher_web/lib/src/link.dart @@ -31,7 +31,7 @@ HtmlViewFactory get linkViewFactory => LinkViewController._viewFactory; /// It uses a platform view to render an anchor element in the DOM. class WebLinkDelegate extends StatefulWidget { /// Creates a delegate for the given [link]. - const WebLinkDelegate(this.link); + const WebLinkDelegate(this.link, {Key? key}) : super(key: key); /// Information about the link built by the app. final LinkInfo link; diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index c45c062255ad..cd8ed2d269c8 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_web description: Web platform implementation of url_launcher repository: https://github.com/flutter/plugins/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.0.9 +version: 2.0.10 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_windows/CHANGELOG.md b/packages/url_launcher/url_launcher_windows/CHANGELOG.md index e02f5a2288e1..3ff14fd2f18a 100644 --- a/packages/url_launcher/url_launcher_windows/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_windows/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.0.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 3.0.0 * Changes the major version since, due to a typo in `default_package` in diff --git a/packages/url_launcher/url_launcher_windows/example/lib/main.dart b/packages/url_launcher/url_launcher_windows/example/lib/main.dart index a9a5d22dadd5..0b985e78ac0d 100644 --- a/packages/url_launcher/url_launcher_windows/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_windows/example/lib/main.dart @@ -9,10 +9,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -30,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml index 95f17ad9e921..c3f224e26adf 100644 --- a/packages/url_launcher/url_launcher_windows/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_windows description: Windows implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.0 +version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index af01c64fd554..ede890162f86 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.4.1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.4.0 diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 39cd415dbb79..a6ec51015c33 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -708,14 +708,14 @@ class _VideoAppLifeCycleObserver extends Object with WidgetsBindingObserver { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - const VideoPlayer(this.controller); + const VideoPlayer(this.controller, {Key? key}) : super(key: key); /// The [VideoPlayerController] responsible for the video being rendered in /// this widget. final VideoPlayerController controller; @override - _VideoPlayerState createState() => _VideoPlayerState(); + State createState() => _VideoPlayerState(); } class _VideoPlayerState extends State { @@ -883,10 +883,11 @@ class VideoProgressIndicator extends StatefulWidget { /// to `top: 5.0`. const VideoProgressIndicator( this.controller, { + Key? key, this.colors = const VideoProgressColors(), required this.allowScrubbing, this.padding = const EdgeInsets.only(top: 5.0), - }); + }) : super(key: key); /// The [VideoPlayerController] that actually associates a video with this /// widget. @@ -910,7 +911,7 @@ class VideoProgressIndicator extends StatefulWidget { final EdgeInsets padding; @override - _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); + State createState() => _VideoProgressIndicatorState(); } class _VideoProgressIndicatorState extends State { @@ -984,8 +985,8 @@ class _VideoProgressIndicatorState extends State { ); if (widget.allowScrubbing) { return _VideoScrubber( - child: paddedProgressIndicator, controller: controller, + child: paddedProgressIndicator, ); } else { return paddedProgressIndicator; diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 0d654a4330a7..b0ca56429271 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/plugins/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.4.0 +version: 2.4.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 16dd52ca6da0..08acba895eba 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.3.3 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.3.2 diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart index 498dbffc9e84..5bce3117d0d6 100644 --- a/packages/video_player/video_player_android/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart @@ -351,14 +351,14 @@ class MiniController extends ValueNotifier { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - const VideoPlayer(this.controller); + const VideoPlayer(this.controller, {Key? key}) : super(key: key); /// The [MiniController] responsible for the video being rendered in /// this widget. final MiniController controller; @override - _VideoPlayerState createState() => _VideoPlayerState(); + State createState() => _VideoPlayerState(); } class _VideoPlayerState extends State { @@ -450,14 +450,14 @@ class _VideoScrubberState extends State<_VideoScrubber> { class VideoProgressIndicator extends StatefulWidget { /// Construct an instance that displays the play/buffering status of the video /// controlled by [controller]. - const VideoProgressIndicator(this.controller); + const VideoProgressIndicator(this.controller, {Key? key}) : super(key: key); /// The [MiniController] that actually associates a video with this /// widget. final MiniController controller; @override - _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); + State createState() => _VideoProgressIndicatorState(); } class _VideoProgressIndicatorState extends State { @@ -527,11 +527,11 @@ class _VideoProgressIndicatorState extends State { ); } return _VideoScrubber( + controller: controller, child: Padding( padding: const EdgeInsets.only(top: 5.0), child: progressIndicator, ), - controller: controller, ); } } diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index aa288ed71eac..bc69fd41369a 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.2 +version: 2.3.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index d77c36c915b6..6ab5398f7013 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.3.4 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.3.3 diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart index 498dbffc9e84..5bce3117d0d6 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart @@ -351,14 +351,14 @@ class MiniController extends ValueNotifier { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - const VideoPlayer(this.controller); + const VideoPlayer(this.controller, {Key? key}) : super(key: key); /// The [MiniController] responsible for the video being rendered in /// this widget. final MiniController controller; @override - _VideoPlayerState createState() => _VideoPlayerState(); + State createState() => _VideoPlayerState(); } class _VideoPlayerState extends State { @@ -450,14 +450,14 @@ class _VideoScrubberState extends State<_VideoScrubber> { class VideoProgressIndicator extends StatefulWidget { /// Construct an instance that displays the play/buffering status of the video /// controlled by [controller]. - const VideoProgressIndicator(this.controller); + const VideoProgressIndicator(this.controller, {Key? key}) : super(key: key); /// The [MiniController] that actually associates a video with this /// widget. final MiniController controller; @override - _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); + State createState() => _VideoProgressIndicatorState(); } class _VideoProgressIndicatorState extends State { @@ -527,11 +527,11 @@ class _VideoProgressIndicatorState extends State { ); } return _VideoScrubber( + controller: controller, child: Padding( padding: const EdgeInsets.only(top: 5.0), child: progressIndicator, ), - controller: controller, ); } } diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index b3cc69eca958..380d8343c024 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.3 +version: 2.3.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 00788c4386fe..094ffda207c5 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.0.9 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.8 diff --git a/packages/video_player/video_player_web/example/lib/main.dart b/packages/video_player/video_player_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/video_player/video_player_web/example/lib/main.dart +++ b/packages/video_player/video_player_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 064517e1f264..7af0dc46dde8 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/plugins/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.0.8 +version: 2.0.9 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 7a56f3f176d0..fa47a6cc3143 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 3.0.3 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 3.0.2 @@ -11,7 +13,7 @@ * Removes a duplicate Android-specific integration test. * Fixes an integration test race condition. -* Fixes comments (accidentially mixed // with ///). +* Fixes comments (accidentally mixed // with ///). ## 3.0.0 diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index ba321264ee1e..cc001f336849 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -1306,7 +1306,8 @@ Future _runJavascriptReturningResult( class ResizableWebView extends StatefulWidget { const ResizableWebView( - {required this.onResize, required this.onPageFinished}); + {Key? key, required this.onResize, required this.onPageFinished}) + : super(key: key); final JavascriptMessageHandler onResize; final VoidCallback onPageFinished; diff --git a/packages/webview_flutter/webview_flutter/example/lib/main.dart b/packages/webview_flutter/webview_flutter/example/lib/main.dart index 51080be01df0..3d8731127970 100644 --- a/packages/webview_flutter/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter/example/lib/main.dart @@ -71,12 +71,12 @@ const String kTransparentBackgroundPage = ''' '''; class WebViewExample extends StatefulWidget { - const WebViewExample({this.cookieManager}); + const WebViewExample({Key? key, this.cookieManager}) : super(key: key); final CookieManager? cookieManager; @override - _WebViewExampleState createState() => _WebViewExampleState(); + State createState() => _WebViewExampleState(); } class _WebViewExampleState extends State { @@ -190,8 +190,9 @@ enum MenuOptions { } class SampleMenu extends StatelessWidget { - SampleMenu(this.controller, CookieManager? cookieManager) - : cookieManager = cookieManager ?? CookieManager(); + SampleMenu(this.controller, CookieManager? cookieManager, {Key? key}) + : cookieManager = cookieManager ?? CookieManager(), + super(key: key); final Future controller; late final CookieManager cookieManager; @@ -250,8 +251,8 @@ class SampleMenu extends StatelessWidget { itemBuilder: (BuildContext context) => >[ PopupMenuItem( value: MenuOptions.showUserAgent, - child: const Text('Show user agent'), enabled: controller.hasData, + child: const Text('Show user agent'), ), const PopupMenuItem( value: MenuOptions.listCookies, @@ -443,8 +444,9 @@ class SampleMenu extends StatelessWidget { } class NavigationControls extends StatelessWidget { - const NavigationControls(this._webViewControllerFuture) - : assert(_webViewControllerFuture != null); + const NavigationControls(this._webViewControllerFuture, {Key? key}) + : assert(_webViewControllerFuture != null), + super(key: key); final Future _webViewControllerFuture; diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 10350984ce9a..a48f6f912c2d 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 3.0.2 +version: 3.0.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index edaa0883713f..4a451442f6cc 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.8.7 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.8.6 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 51e09912da23..4c06fa6b3c18 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 @@ -1446,9 +1446,10 @@ Future _runJavaScriptReturningResult( class ResizableWebView extends StatefulWidget { const ResizableWebView({ + Key? key, required this.onResize, required this.onPageFinished, - }); + }) : super(key: key); final JavascriptMessageHandler onResize; final VoidCallback onPageFinished; 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 5d19ca71ac84..349a64916e8b 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -247,8 +247,8 @@ class _SampleMenu extends StatelessWidget { itemBuilder: (BuildContext context) => >[ PopupMenuItem<_MenuOptions>( value: _MenuOptions.showUserAgent, - child: const Text('Show user agent'), enabled: controller.hasData, + child: const Text('Show user agent'), ), const PopupMenuItem<_MenuOptions>( value: _MenuOptions.listCookies, diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart index 91ea66376904..56745314d92b 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart @@ -250,7 +250,7 @@ class WebView extends StatefulWidget { final Color? backgroundColor; @override - _WebViewState createState() => _WebViewState(); + State createState() => _WebViewState(); } class _WebViewState extends State { diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart index 28d169c9cb94..f1b130c7e365 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart @@ -15,6 +15,7 @@ import 'src/android_webview.dart' as android_webview; class WebViewAndroidWidget extends StatefulWidget { /// Constructs a [WebViewAndroidWidget]. const WebViewAndroidWidget({ + Key? key, required this.creationParams, required this.useHybridComposition, required this.callbacksHandler, @@ -24,7 +25,7 @@ class WebViewAndroidWidget extends StatefulWidget { @visibleForTesting this.flutterAssetManager = const android_webview.FlutterAssetManager(), @visibleForTesting this.webStorage, - }); + }) : super(key: key); /// Initial parameters used to setup the WebView. final CreationParams creationParams; diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 9a7c48a4dcd8..407887f2ba95 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/plugins/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: 2.8.6 +version: 2.8.7 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index 9f7ebe368941..b7254e1a0a7a 100644 --- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -1,7 +1,9 @@ -## NEXT +## 0.1.0+2 * Removes unnecessary imports. * Fixes unit tests to run on latest `master` version of Flutter. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.1.0+1 diff --git a/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart index 8cd74f660f58..ffd3367d33f4 100644 --- a/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart @@ -46,7 +46,7 @@ class WebView extends StatefulWidget { final String? initialUrl; @override - _WebViewState createState() => _WebViewState(); + State createState() => _WebViewState(); } class _WebViewState extends State { diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml index bd154387097e..35a7b74a764c 100644 --- a/packages/webview_flutter/webview_flutter_web/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_web description: A Flutter plugin that provides a WebView widget on web. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 0.1.0+1 +version: 0.1.0+2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index f042dd081475..6db769b0d922 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.7.4 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.7.3 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 ceff62e3d5e8..aa376f8358e9 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 @@ -1181,7 +1181,8 @@ Future _getUserAgent(WebViewController controller) async { class ResizableWebView extends StatefulWidget { const ResizableWebView( - {required this.onResize, required this.onPageFinished}); + {Key? key, required this.onResize, required this.onPageFinished}) + : super(key: key); final JavascriptMessageHandler onResize; final VoidCallback onPageFinished; 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 e8b86d9c5773..7b30923e1e54 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -232,8 +232,8 @@ class _SampleMenu extends StatelessWidget { itemBuilder: (BuildContext context) => >[ PopupMenuItem<_MenuOptions>( value: _MenuOptions.showUserAgent, - child: const Text('Show user agent'), enabled: controller.hasData, + child: const Text('Show user agent'), ), const PopupMenuItem<_MenuOptions>( value: _MenuOptions.listCookies, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart index 4d479f943d62..c44c4e743669 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart @@ -240,7 +240,7 @@ class WebView extends StatefulWidget { final Color? backgroundColor; @override - _WebViewState createState() => _WebViewState(); + State createState() => _WebViewState(); } class _WebViewState extends State { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 012cd221599b..8c37112d7a24 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -18,13 +18,14 @@ import 'web_kit/web_kit.dart'; class WebKitWebViewWidget extends StatefulWidget { /// Constructs a [WebKitWebViewWidget]. const WebKitWebViewWidget({ + Key? key, required this.creationParams, required this.callbacksHandler, required this.javascriptChannelRegistry, required this.onBuildWidget, this.configuration, @visibleForTesting this.webViewProxy = const WebViewWidgetProxy(), - }); + }) : super(key: key); /// The initial parameters used to setup the WebView. final CreationParams creationParams; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index f4e72b8f14eb..365e64720d4f 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/plugins/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: 2.7.3 +version: 2.7.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index b2319c63dc48..5faa7c8201d9 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -322,7 +322,7 @@ and `firebase-test-lab`. ## v.0.0.36+2 -- Default to showing podspec lint warnings +- Default to showing podspec lint warnings. ## v.0.0.36+1 diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index 5c38bd5f7033..b0a8990e1300 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -319,14 +319,14 @@ String _pluginPlatformSection( return entry; } -typedef _ErrorHandler = void Function(Error error); +typedef ErrorHandler = void Function(Error error); /// Run the command [runner] with the given [args] and return /// what was printed. /// A custom [errorHandler] can be used to handle the runner error as desired without throwing. Future> runCapturingPrint( CommandRunner runner, List args, - {_ErrorHandler? errorHandler}) async { + {ErrorHandler? errorHandler}) async { final List prints = []; final ZoneSpecification spec = ZoneSpecification( print: (_, __, ___, String message) { From edfd9c0bf2602e3000f9689bc59431cf56fa99bc Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 10 May 2022 13:05:18 -0400 Subject: [PATCH 288/844] Revert "Enable lints `library_private_types_in_public_api`, `sort_child_properties_last` and `use_key_in_widget_constructors`" (#5691) This reverts commit 4b7b67916242a9c82283a642f169fbdb7f9045be. This includes a fix for a latent bug in the version-check repo tooling command that caused it to fail when reverting a package that previously had a NEXT section, so that tests will pass. --- analysis_options.yaml | 5 +- packages/camera/camera/CHANGELOG.md | 4 +- packages/camera/camera/README.md | 14 +-- packages/camera/camera/example/lib/main.dart | 60 +++++----- .../example/lib/readme_full_example.dart | 16 ++- .../camera/camera/example/test/main_test.dart | 2 +- .../camera/camera/lib/src/camera_preview.dart | 3 +- packages/camera/camera/pubspec.yaml | 2 +- packages/camera/camera_web/CHANGELOG.md | 5 - .../camera/camera_web/example/lib/main.dart | 5 +- packages/camera/camera_web/pubspec.yaml | 2 +- packages/camera/camera_windows/CHANGELOG.md | 4 +- .../camera_windows/example/lib/main.dart | 5 +- packages/camera/camera_windows/pubspec.yaml | 2 +- packages/espresso/CHANGELOG.md | 7 +- packages/espresso/example/lib/main.dart | 7 +- packages/espresso/pubspec.yaml | 2 +- .../file_selector/file_selector/CHANGELOG.md | 4 +- .../example/lib/get_directory_page.dart | 5 +- .../file_selector/example/lib/home_page.dart | 3 - .../file_selector/example/lib/main.dart | 16 +-- .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 5 +- .../example/lib/open_text_page.dart | 6 +- .../example/lib/save_text_page.dart | 3 - .../file_selector/file_selector/pubspec.yaml | 2 +- .../file_selector_macos/CHANGELOG.md | 4 +- .../example/lib/get_directory_page.dart | 5 +- .../example/lib/home_page.dart | 3 - .../file_selector_macos/example/lib/main.dart | 16 +-- .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 5 +- .../example/lib/open_text_page.dart | 6 +- .../example/lib/save_text_page.dart | 5 +- .../file_selector_macos/pubspec.yaml | 2 +- .../CHANGELOG.md | 4 +- .../pubspec.yaml | 2 +- .../file_selector_web/CHANGELOG.md | 5 - .../file_selector_web/example/lib/main.dart | 7 +- .../file_selector_web/pubspec.yaml | 2 +- .../file_selector_windows/CHANGELOG.md | 4 +- .../example/lib/get_directory_page.dart | 5 +- .../example/lib/home_page.dart | 3 - .../example/lib/main.dart | 16 +-- .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 5 +- .../example/lib/open_text_page.dart | 6 +- .../example/lib/save_text_page.dart | 5 +- .../file_selector_windows/pubspec.yaml | 2 +- .../CHANGELOG.md | 4 +- ...flutter_plugin_android_lifecycle_test.dart | 2 +- .../example/lib/main.dart | 8 +- .../pubspec.yaml | 2 +- .../google_maps_flutter/CHANGELOG.md | 6 +- .../example/lib/animate_camera.dart | 6 +- .../example/lib/lite_mode.dart | 3 +- .../google_maps_flutter/example/lib/main.dart | 8 +- .../example/lib/map_click.dart | 3 +- .../example/lib/map_coordinates.dart | 3 +- .../example/lib/map_ui.dart | 5 +- .../example/lib/marker_icons.dart | 5 +- .../example/lib/move_camera.dart | 5 +- .../example/lib/padding.dart | 5 +- .../google_maps_flutter/example/lib/page.dart | 3 +- .../example/lib/place_circle.dart | 18 +-- .../example/lib/place_marker.dart | 31 +++-- .../example/lib/place_polygon.dart | 24 ++-- .../example/lib/place_polyline.dart | 26 ++-- .../example/lib/scrolling_map.dart | 5 +- .../example/lib/snapshot.dart | 5 +- .../example/lib/tile_overlay.dart | 11 +- .../lib/src/controller.dart | 2 - .../google_maps_flutter/pubspec.yaml | 2 +- .../google_maps_flutter_web/CHANGELOG.md | 4 +- .../example/lib/main.dart | 2 +- .../google_maps_flutter_web/pubspec.yaml | 2 +- .../google_sign_in/CHANGELOG.md | 5 - .../google_sign_in/example/lib/main.dart | 8 +- .../google_sign_in/lib/widgets.dart | 4 +- .../google_sign_in/pubspec.yaml | 2 +- .../google_sign_in_android/CHANGELOG.md | 5 - .../example/lib/main.dart | 8 +- .../google_sign_in_android/pubspec.yaml | 2 +- .../google_sign_in_ios/CHANGELOG.md | 5 - .../google_sign_in_ios/example/lib/main.dart | 8 +- .../google_sign_in_ios/pubspec.yaml | 2 +- .../google_sign_in_web/CHANGELOG.md | 5 - .../google_sign_in_web/example/lib/main.dart | 7 +- .../google_sign_in_web/pubspec.yaml | 2 +- .../image_picker/image_picker/CHANGELOG.md | 5 - .../image_picker/example/lib/main.dart | 39 +++--- .../image_picker/image_picker/pubspec.yaml | 2 +- .../image_picker_android/CHANGELOG.md | 5 - .../example/lib/main.dart | 39 +++--- .../image_picker_android/pubspec.yaml | 2 +- .../image_picker_for_web/CHANGELOG.md | 5 - .../example/lib/main.dart | 7 +- .../image_picker_for_web/pubspec.yaml | 2 +- .../image_picker_ios/CHANGELOG.md | 4 +- .../image_picker_ios/example/lib/main.dart | 39 +++--- .../image_picker_ios/pubspec.yaml | 2 +- .../image_picker_windows/CHANGELOG.md | 4 +- .../example/lib/main.dart | 31 +++-- .../image_picker_windows/pubspec.yaml | 2 +- .../in_app_purchase/CHANGELOG.md | 4 +- .../in_app_purchase/example/lib/main.dart | 111 +++++++++--------- .../in_app_purchase/pubspec.yaml | 2 +- .../in_app_purchase_android/CHANGELOG.md | 4 +- .../example/lib/main.dart | 6 +- .../in_app_purchase_android/pubspec.yaml | 2 +- .../in_app_purchase_storekit/CHANGELOG.md | 4 +- .../example/lib/main.dart | 8 +- .../in_app_purchase_storekit/pubspec.yaml | 2 +- packages/ios_platform_images/CHANGELOG.md | 4 +- .../ios_platform_images/example/lib/main.dart | 7 +- .../example/test/widget_test.dart | 2 +- packages/ios_platform_images/pubspec.yaml | 2 +- packages/local_auth/local_auth/CHANGELOG.md | 2 - .../local_auth/example/lib/main.dart | 14 +-- .../local_auth_android/CHANGELOG.md | 4 +- .../local_auth_android/example/lib/main.dart | 14 +-- .../local_auth_android/pubspec.yaml | 2 +- .../local_auth/local_auth_ios/CHANGELOG.md | 4 +- .../local_auth_ios/example/lib/main.dart | 14 +-- .../local_auth/local_auth_ios/pubspec.yaml | 2 +- .../path_provider/path_provider/CHANGELOG.md | 4 +- .../path_provider/example/lib/main.dart | 44 ++++--- .../path_provider/path_provider/pubspec.yaml | 2 +- .../path_provider_android/CHANGELOG.md | 5 - .../example/lib/main.dart | 16 ++- .../path_provider_android/pubspec.yaml | 2 +- .../path_provider_ios/CHANGELOG.md | 5 - .../path_provider_ios/example/lib/main.dart | 14 +-- .../path_provider_ios/pubspec.yaml | 2 +- .../path_provider_linux/CHANGELOG.md | 5 - .../path_provider_linux/example/lib/main.dart | 7 +- .../path_provider_linux/pubspec.yaml | 2 +- .../path_provider_macos/CHANGELOG.md | 5 - .../path_provider_macos/example/lib/main.dart | 6 +- .../path_provider_macos/pubspec.yaml | 2 +- .../path_provider_windows/CHANGELOG.md | 5 - .../example/lib/main.dart | 6 +- .../path_provider_windows/pubspec.yaml | 2 +- .../quick_actions/quick_actions/CHANGELOG.md | 4 +- .../quick_actions/example/lib/main.dart | 6 +- .../quick_actions/quick_actions/pubspec.yaml | 2 +- .../quick_actions_android/CHANGELOG.md | 5 - .../example/lib/main.dart | 6 +- .../quick_actions_android/pubspec.yaml | 2 +- .../quick_actions_ios/CHANGELOG.md | 4 - .../quick_actions_ios/example/lib/main.dart | 6 +- .../quick_actions_ios/pubspec.yaml | 2 +- .../shared_preferences/CHANGELOG.md | 4 +- .../shared_preferences/example/lib/main.dart | 4 +- .../shared_preferences/pubspec.yaml | 2 +- .../shared_preferences_android/CHANGELOG.md | 5 - .../example/lib/main.dart | 4 +- .../shared_preferences_android/pubspec.yaml | 2 +- .../shared_preferences_ios/CHANGELOG.md | 5 - .../example/lib/main.dart | 4 +- .../shared_preferences_ios/pubspec.yaml | 2 +- .../shared_preferences_linux/CHANGELOG.md | 4 +- .../example/lib/main.dart | 4 +- .../shared_preferences_linux/pubspec.yaml | 2 +- .../shared_preferences_macos/CHANGELOG.md | 4 +- .../example/lib/main.dart | 4 +- .../shared_preferences_macos/pubspec.yaml | 2 +- .../shared_preferences_web/CHANGELOG.md | 5 - .../example/lib/main.dart | 7 +- .../shared_preferences_web/pubspec.yaml | 2 +- .../shared_preferences_windows/CHANGELOG.md | 5 - .../example/lib/main.dart | 4 +- .../shared_preferences_windows/pubspec.yaml | 2 +- .../url_launcher/url_launcher/CHANGELOG.md | 4 +- .../url_launcher/example/lib/main.dart | 6 +- .../url_launcher/lib/src/link.dart | 2 +- .../url_launcher/url_launcher/pubspec.yaml | 2 +- .../url_launcher_android/CHANGELOG.md | 5 - .../example/lib/main.dart | 6 +- .../url_launcher_android/pubspec.yaml | 2 +- .../url_launcher_ios/CHANGELOG.md | 5 - .../url_launcher_ios/example/lib/main.dart | 6 +- .../url_launcher_ios/pubspec.yaml | 2 +- .../url_launcher_linux/CHANGELOG.md | 5 - .../url_launcher_linux/example/lib/main.dart | 6 +- .../url_launcher_linux/pubspec.yaml | 2 +- .../url_launcher_macos/CHANGELOG.md | 5 - .../url_launcher_macos/example/lib/main.dart | 6 +- .../url_launcher_macos/pubspec.yaml | 2 +- .../url_launcher_web/CHANGELOG.md | 5 - .../url_launcher_web/example/lib/main.dart | 7 +- .../url_launcher_web/lib/src/link.dart | 2 +- .../url_launcher_web/pubspec.yaml | 2 +- .../url_launcher_windows/CHANGELOG.md | 5 - .../example/lib/main.dart | 6 +- .../url_launcher_windows/pubspec.yaml | 2 +- .../video_player/video_player/CHANGELOG.md | 4 +- .../video_player/lib/video_player.dart | 11 +- .../video_player/video_player/pubspec.yaml | 2 +- .../video_player_android/CHANGELOG.md | 4 +- .../example/lib/mini_controller.dart | 10 +- .../video_player_android/pubspec.yaml | 2 +- .../video_player_avfoundation/CHANGELOG.md | 4 +- .../example/lib/mini_controller.dart | 10 +- .../video_player_avfoundation/pubspec.yaml | 2 +- .../video_player_web/CHANGELOG.md | 4 +- .../video_player_web/example/lib/main.dart | 7 +- .../video_player_web/pubspec.yaml | 2 +- .../webview_flutter/CHANGELOG.md | 6 +- .../webview_flutter_test.dart | 3 +- .../webview_flutter/example/lib/main.dart | 16 ++- .../webview_flutter/pubspec.yaml | 2 +- .../webview_flutter_android/CHANGELOG.md | 4 +- .../webview_flutter_test.dart | 3 +- .../example/lib/main.dart | 2 +- .../example/lib/web_view.dart | 2 +- .../lib/webview_android_widget.dart | 3 +- .../webview_flutter_android/pubspec.yaml | 2 +- .../webview_flutter_web/CHANGELOG.md | 4 +- .../example/lib/web_view.dart | 2 +- .../webview_flutter_web/pubspec.yaml | 2 +- .../webview_flutter_wkwebview/CHANGELOG.md | 4 +- .../webview_flutter_test.dart | 3 +- .../example/lib/main.dart | 2 +- .../example/lib/web_view.dart | 2 +- .../lib/src/web_kit_webview_widget.dart | 3 +- .../webview_flutter_wkwebview/pubspec.yaml | 2 +- script/tool/CHANGELOG.md | 6 +- .../tool/lib/src/version_check_command.dart | 25 ++-- script/tool/test/util.dart | 4 +- .../tool/test/version_check_command_test.dart | 31 ++++- 231 files changed, 574 insertions(+), 948 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index ba5e0a9c4ced..f6177cd9939a 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -134,7 +134,6 @@ linter: - leading_newlines_in_multiline_strings - library_names - library_prefixes - - library_private_types_in_public_api # - lines_longer_than_80_chars # not required by flutter style - list_remove_unrelated_type # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181 @@ -198,7 +197,7 @@ linter: - recursive_getters # - sized_box_for_whitespace # not yet tested - slash_for_doc_comments - - sort_child_properties_last + # - sort_child_properties_last # not yet tested - sort_constructors_first - sort_unnamed_constructors_first - test_types_in_equals @@ -230,7 +229,7 @@ linter: - use_full_hex_values_for_flutter_colors # - use_function_type_syntax_for_parameters # not yet tested - use_is_even_rather_than_modulo - - use_key_in_widget_constructors + # - use_key_in_widget_constructors # not yet tested - use_late_for_private_fields_and_variables - use_raw_strings - use_rethrow_when_possible diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index bf0ccf86a82e..4d7e9bbeb218 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.9.4+22 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.9.4+21 diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index 0bcaeaeb3b7c..97b16d20f48a 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -89,22 +89,18 @@ Here is a small example flutter app displaying a full screen camera preview. import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; -late List _cameras; +late List cameras; Future main() async { WidgetsFlutterBinding.ensureInitialized(); - _cameras = await availableCameras(); - runApp(const CameraApp()); + cameras = await availableCameras(); + runApp(CameraApp()); } -/// CameraApp is the Main Application. class CameraApp extends StatefulWidget { - /// Default Constructor - const CameraApp({Key? key}) : super(key: key); - @override - State createState() => _CameraAppState(); + _CameraAppState createState() => _CameraAppState(); } class _CameraAppState extends State { @@ -113,7 +109,7 @@ class _CameraAppState extends State { @override void initState() { super.initState(); - controller = CameraController(_cameras[0], ResolutionPreset.max); + controller = CameraController(cameras[0], ResolutionPreset.max); controller.initialize().then((_) { if (!mounted) { return; diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index a645326f2803..aabbe249313d 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.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. +// ignore_for_file: public_member_api_docs + import 'dart:async'; import 'dart:io'; @@ -11,13 +13,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:video_player/video_player.dart'; -/// Camera example home widget. class CameraExampleHome extends StatefulWidget { - /// Default Constructor - const CameraExampleHome({Key? key}) : super(key: key); - @override - State createState() { + _CameraExampleHomeState createState() { return _CameraExampleHomeState(); } } @@ -36,7 +34,7 @@ IconData getCameraLensIcon(CameraLensDirection direction) { } } -void _logError(String code, String? message) { +void logError(String code, String? message) { if (message != null) { print('Error: $code\nError Message: $message'); } else { @@ -136,6 +134,12 @@ class _CameraExampleHomeState extends State children: [ Expanded( child: Container( + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Center( + child: _cameraPreviewWidget(), + ), + ), decoration: BoxDecoration( color: Colors.black, border: Border.all( @@ -146,12 +150,6 @@ class _CameraExampleHomeState extends State width: 3.0, ), ), - child: Padding( - padding: const EdgeInsets.all(1.0), - child: Center( - child: _cameraPreviewWidget(), - ), - ), ), ), _captureControlRowWidget(), @@ -235,8 +233,6 @@ class _CameraExampleHomeState extends State Container() else SizedBox( - width: 64.0, - height: 64.0, child: (localVideoController == null) ? ( // The captured image on the web contains a network-accessible URL @@ -247,8 +243,6 @@ class _CameraExampleHomeState extends State ? Image.network(imageFile!.path) : Image.file(File(imageFile!.path))) : Container( - decoration: BoxDecoration( - border: Border.all(color: Colors.pink)), child: Center( child: AspectRatio( aspectRatio: @@ -257,7 +251,11 @@ class _CameraExampleHomeState extends State : 1.0, child: VideoPlayer(localVideoController)), ), + decoration: BoxDecoration( + border: Border.all(color: Colors.pink)), ), + width: 64.0, + height: 64.0, ), ], ), @@ -396,6 +394,7 @@ class _CameraExampleHomeState extends State mainAxisSize: MainAxisSize.max, children: [ TextButton( + child: const Text('AUTO'), style: styleAuto, onPressed: controller != null ? () => @@ -407,22 +406,21 @@ class _CameraExampleHomeState extends State showInSnackBar('Resetting exposure point'); } }, - child: const Text('AUTO'), ), TextButton( + child: const Text('LOCKED'), style: styleLocked, onPressed: controller != null ? () => onSetExposureModeButtonPressed(ExposureMode.locked) : null, - child: const Text('LOCKED'), ), TextButton( + child: const Text('RESET OFFSET'), style: styleLocked, onPressed: controller != null ? () => controller!.setExposureOffset(0.0) : null, - child: const Text('RESET OFFSET'), ), ], ), @@ -481,6 +479,7 @@ class _CameraExampleHomeState extends State mainAxisSize: MainAxisSize.max, children: [ TextButton( + child: const Text('AUTO'), style: styleAuto, onPressed: controller != null ? () => onSetFocusModeButtonPressed(FocusMode.auto) @@ -491,14 +490,13 @@ class _CameraExampleHomeState extends State } showInSnackBar('Resetting focus point'); }, - child: const Text('AUTO'), ), TextButton( + child: const Text('LOCKED'), style: styleLocked, onPressed: controller != null ? () => onSetFocusModeButtonPressed(FocusMode.locked) : null, - child: const Text('LOCKED'), ), ], ), @@ -584,13 +582,13 @@ class _CameraExampleHomeState extends State onNewCameraSelected(description); }; - if (_cameras.isEmpty) { + if (cameras.isEmpty) { _ambiguate(SchedulerBinding.instance)?.addPostFrameCallback((_) async { showInSnackBar('No camera found.'); }); return const Text('None'); } else { - for (final CameraDescription cameraDescription in _cameras) { + for (final CameraDescription cameraDescription in cameras) { toggles.add( SizedBox( width: 90.0, @@ -1016,35 +1014,31 @@ class _CameraExampleHomeState extends State } void _showCameraException(CameraException e) { - _logError(e.code, e.description); + logError(e.code, e.description); showInSnackBar('Error: ${e.code}\n${e.description}'); } } -/// CameraApp is the Main Application. class CameraApp extends StatelessWidget { - /// Default Constructor - const CameraApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { - return const MaterialApp( + return MaterialApp( home: CameraExampleHome(), ); } } -List _cameras = []; +List cameras = []; Future main() async { // Fetch the available cameras before initializing the app. try { WidgetsFlutterBinding.ensureInitialized(); - _cameras = await availableCameras(); + cameras = await availableCameras(); } on CameraException catch (e) { - _logError(e.code, e.description); + logError(e.code, e.description); } - runApp(const CameraApp()); + runApp(CameraApp()); } /// This allows a value of type T or T? to be treated as a value of type T?. diff --git a/packages/camera/camera/example/lib/readme_full_example.dart b/packages/camera/camera/example/lib/readme_full_example.dart index a310fd9daeb0..b25e637a0c95 100644 --- a/packages/camera/camera/example/lib/readme_full_example.dart +++ b/packages/camera/camera/example/lib/readme_full_example.dart @@ -2,26 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: public_member_api_docs + // #docregion FullAppExample import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; -late List _cameras; +late List cameras; Future main() async { WidgetsFlutterBinding.ensureInitialized(); - _cameras = await availableCameras(); - runApp(const CameraApp()); + cameras = await availableCameras(); + runApp(CameraApp()); } -/// CameraApp is the Main Application. class CameraApp extends StatefulWidget { - /// Default Constructor - const CameraApp({Key? key}) : super(key: key); - @override - State createState() => _CameraAppState(); + _CameraAppState createState() => _CameraAppState(); } class _CameraAppState extends State { @@ -30,7 +28,7 @@ class _CameraAppState extends State { @override void initState() { super.initState(); - controller = CameraController(_cameras[0], ResolutionPreset.max); + controller = CameraController(cameras[0], ResolutionPreset.max); controller.initialize().then((_) { if (!mounted) { return; diff --git a/packages/camera/camera/example/test/main_test.dart b/packages/camera/camera/example/test/main_test.dart index 6e909efcfc62..9a5fcdf2d5ea 100644 --- a/packages/camera/camera/example/test/main_test.dart +++ b/packages/camera/camera/example/test/main_test.dart @@ -9,7 +9,7 @@ import 'package:flutter_test/flutter_test.dart'; void main() { testWidgets('Test snackbar', (WidgetTester tester) async { WidgetsFlutterBinding.ensureInitialized(); - await tester.pumpWidget(const CameraApp()); + await tester.pumpWidget(CameraApp()); await tester.pumpAndSettle(); expect(find.byType(SnackBar), findsOneWidget); }); diff --git a/packages/camera/camera/lib/src/camera_preview.dart b/packages/camera/camera/lib/src/camera_preview.dart index 94ffca649fa6..a9b3f2143b49 100644 --- a/packages/camera/camera/lib/src/camera_preview.dart +++ b/packages/camera/camera/lib/src/camera_preview.dart @@ -10,8 +10,7 @@ import 'package:flutter/services.dart'; /// A widget showing a live camera preview. class CameraPreview extends StatelessWidget { /// Creates a preview widget for the given camera controller. - const CameraPreview(this.controller, {Key? key, this.child}) - : super(key: key); + const CameraPreview(this.controller, {this.child}); /// The controller for the camera that the preview is shown for. final CameraController controller; diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index fde6663844c2..f62777044617 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+22 +version: 0.9.4+21 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 7a24e12e5029..852e4a03fd43 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,8 +1,3 @@ -## 0.2.1+5 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 0.2.1+4 * Migrates from `ui.hash*` to `Object.hash*`. diff --git a/packages/camera/camera_web/example/lib/main.dart b/packages/camera/camera_web/example/lib/main.dart index 670891fa5009..ab04ce2ca2c7 100644 --- a/packages/camera/camera_web/example/lib/main.dart +++ b/packages/camera/camera_web/example/lib/main.dart @@ -4,13 +4,10 @@ import 'package:flutter/material.dart'; -void main() => runApp(const MyApp()); +void main() => runApp(MyApp()); /// App for testing class MyApp extends StatelessWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const Directionality( diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 8bef974190b2..2d1a4508eb73 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.2.1+5 +version: 0.2.1+4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index 0f3bf441b05a..b1383dc54993 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.1.0+1 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.1.0 diff --git a/packages/camera/camera_windows/example/lib/main.dart b/packages/camera/camera_windows/example/lib/main.dart index 5758b0f1e397..b73e00cac52b 100644 --- a/packages/camera/camera_windows/example/lib/main.dart +++ b/packages/camera/camera_windows/example/lib/main.dart @@ -9,14 +9,11 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// Example app for Camera Windows plugin. class MyApp extends StatefulWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override State createState() => _MyAppState(); } diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index fe655b04e8c8..1081c3dfc01f 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -1,8 +1,8 @@ name: camera_windows description: A Flutter plugin for getting information about and controlling the camera on Windows. +version: 0.1.0 repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.1.0+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/espresso/CHANGELOG.md b/packages/espresso/CHANGELOG.md index dad0a912e174..eb1f267ca1d3 100644 --- a/packages/espresso/CHANGELOG.md +++ b/packages/espresso/CHANGELOG.md @@ -1,13 +1,8 @@ -## 0.2.0+2 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 0.2.0+1 * Adds OS version support information to README. * Updates `androidx.test.ext:junit` and `androidx.test.ext:truth` for - compatibility with updated Flutter template. + compatibilty with updated Flutter template. ## 0.2.0 diff --git a/packages/espresso/example/lib/main.dart b/packages/espresso/example/lib/main.dart index 741cd9cf9fa2..14f94abb28c8 100644 --- a/packages/espresso/example/lib/main.dart +++ b/packages/espresso/example/lib/main.dart @@ -4,13 +4,10 @@ import 'package:flutter/material.dart'; -void main() => runApp(const MyApp()); +void main() => runApp(MyApp()); /// Example app for Espresso plugin. class MyApp extends StatelessWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - // This widget is the root of your application. @override Widget build(BuildContext context) { @@ -48,7 +45,7 @@ class _MyHomePage extends StatefulWidget { final String title; @override - State<_MyHomePage> createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<_MyHomePage> { diff --git a/packages/espresso/pubspec.yaml b/packages/espresso/pubspec.yaml index ac0199cf045f..7737fc46d4b6 100644 --- a/packages/espresso/pubspec.yaml +++ b/packages/espresso/pubspec.yaml @@ -3,7 +3,7 @@ description: Java classes for testing Flutter apps using Espresso. Allows driving Flutter widgets from a native Espresso test. repository: https://github.com/flutter/plugins/tree/main/packages/espresso issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+espresso%22 -version: 0.2.0+2 +version: 0.2.0+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 17baf9f12469..c0821fed7446 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,9 +1,7 @@ -## 0.8.4+2 +## NEXT * Removes unnecessary imports. * Adds OS version support information to README. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.8.4+1 diff --git a/packages/file_selector/file_selector/example/lib/get_directory_page.dart b/packages/file_selector/file_selector/example/lib/get_directory_page.dart index 506134d66bd8..b3ed9d0eeaca 100644 --- a/packages/file_selector/file_selector/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector/example/lib/get_directory_page.dart @@ -7,9 +7,6 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of getDirectoryPath class GetDirectoryPage extends StatelessWidget { - /// Default Constructor - const GetDirectoryPage({Key? key}) : super(key: key); - Future _getDirectoryPath(BuildContext context) async { const String confirmButtonText = 'Choose'; final String? directoryPath = await getDirectoryPath( @@ -53,7 +50,7 @@ class GetDirectoryPage extends StatelessWidget { /// Widget that displays a text file in a dialog class TextDisplay extends StatelessWidget { /// Default Constructor - const TextDisplay(this.directoryPath, {Key? key}) : super(key: key); + const TextDisplay(this.directoryPath); /// Directory path final String directoryPath; diff --git a/packages/file_selector/file_selector/example/lib/home_page.dart b/packages/file_selector/file_selector/example/lib/home_page.dart index 9a0733b56b93..c598cbdf2611 100644 --- a/packages/file_selector/file_selector/example/lib/home_page.dart +++ b/packages/file_selector/file_selector/example/lib/home_page.dart @@ -6,9 +6,6 @@ import 'package:flutter/material.dart'; /// Home Page of the application class HomePage extends StatelessWidget { - /// Default Constructor - const HomePage({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 34f5857ab0bc..14ce3f593f33 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -11,14 +11,11 @@ import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// MyApp is the Main Application class MyApp extends StatelessWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -27,14 +24,13 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: const HomePage(), + home: HomePage(), routes: { - '/open/image': (BuildContext context) => const OpenImagePage(), - '/open/images': (BuildContext context) => - const OpenMultipleImagesPage(), - '/open/text': (BuildContext context) => const OpenTextPage(), + '/open/image': (BuildContext context) => OpenImagePage(), + '/open/images': (BuildContext context) => OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), - '/directory': (BuildContext context) => const GetDirectoryPage(), + '/directory': (BuildContext context) => GetDirectoryPage(), }, ); } diff --git a/packages/file_selector/file_selector/example/lib/open_image_page.dart b/packages/file_selector/file_selector/example/lib/open_image_page.dart index e520ffb402aa..0abdba6eb72d 100644 --- a/packages/file_selector/file_selector/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_image_page.dart @@ -10,9 +10,6 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of openFiles class OpenImagePage extends StatelessWidget { - /// Default Constructor - const OpenImagePage({Key? key}) : super(key: key); - Future _openImageFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'images', @@ -62,8 +59,7 @@ class OpenImagePage extends StatelessWidget { /// Widget that displays a text file in a dialog class ImageDisplay extends StatelessWidget { /// Default Constructor - const ImageDisplay(this.fileName, this.filePath, {Key? key}) - : super(key: key); + const ImageDisplay(this.fileName, this.filePath); /// Image's name final String fileName; diff --git a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart index e2d21c7f04d1..9a1101214aaa 100644 --- a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart @@ -10,9 +10,6 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of openFiles class OpenMultipleImagesPage extends StatelessWidget { - /// Default Constructor - const OpenMultipleImagesPage({Key? key}) : super(key: key); - Future _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', @@ -64,7 +61,7 @@ class OpenMultipleImagesPage extends StatelessWidget { /// Widget that displays a text file in a dialog class MultipleImagesDisplay extends StatelessWidget { /// Default Constructor - const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); + const MultipleImagesDisplay(this.files); /// The files containing the images final List files; diff --git a/packages/file_selector/file_selector/example/lib/open_text_page.dart b/packages/file_selector/file_selector/example/lib/open_text_page.dart index be48a434282b..652e8596cf81 100644 --- a/packages/file_selector/file_selector/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_text_page.dart @@ -7,9 +7,6 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of openFile class OpenTextPage extends StatelessWidget { - /// Default Constructor - const OpenTextPage({Key? key}) : super(key: key); - Future _openTextFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'text', @@ -58,8 +55,7 @@ class OpenTextPage extends StatelessWidget { /// Widget that displays a text file in a dialog class TextDisplay extends StatelessWidget { /// Default Constructor - const TextDisplay(this.fileName, this.fileContent, {Key? key}) - : super(key: key); + const TextDisplay(this.fileName, this.fileContent); /// File's name final String fileName; diff --git a/packages/file_selector/file_selector/example/lib/save_text_page.dart b/packages/file_selector/file_selector/example/lib/save_text_page.dart index b0317844ec36..108ef89b0248 100644 --- a/packages/file_selector/file_selector/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/save_text_page.dart @@ -8,9 +8,6 @@ import 'package:flutter/material.dart'; /// Page for showing an example of saving with file_selector class SaveTextPage extends StatelessWidget { - /// Default Constructor - SaveTextPage({Key? key}) : super(key: key); - final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index 7026f7f32287..c05900f650cf 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.4+2 +version: 0.8.4+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_macos/CHANGELOG.md b/packages/file_selector/file_selector_macos/CHANGELOG.md index 19724a513a9e..b46a174bd323 100644 --- a/packages/file_selector/file_selector_macos/CHANGELOG.md +++ b/packages/file_selector/file_selector_macos/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.8.2+1 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.8.2 diff --git a/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart b/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart index a27ab2b40aba..0e55df8ce622 100644 --- a/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart @@ -8,9 +8,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a directory using `getDirectoryPath`, /// then displays the selected directory in a dialog. class GetDirectoryPage extends StatelessWidget { - /// Default Constructor - const GetDirectoryPage({Key? key}) : super(key: key); - Future _getDirectoryPath(BuildContext context) async { const String confirmButtonText = 'Choose'; final String? directoryPath = @@ -55,7 +52,7 @@ class GetDirectoryPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Creates a `TextDisplay`. - const TextDisplay(this.directoryPath, {Key? key}) : super(key: key); + const TextDisplay(this.directoryPath); /// The path selected in the dialog. final String directoryPath; diff --git a/packages/file_selector/file_selector_macos/example/lib/home_page.dart b/packages/file_selector/file_selector_macos/example/lib/home_page.dart index 4d6ca7e6e6e3..958680be0e3b 100644 --- a/packages/file_selector/file_selector_macos/example/lib/home_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/home_page.dart @@ -6,9 +6,6 @@ import 'package:flutter/material.dart'; /// Home Page of the application. class HomePage extends StatelessWidget { - /// Default Constructor - const HomePage({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( diff --git a/packages/file_selector/file_selector_macos/example/lib/main.dart b/packages/file_selector/file_selector_macos/example/lib/main.dart index cbe268e1c7ab..a49ebac1aea5 100644 --- a/packages/file_selector/file_selector_macos/example/lib/main.dart +++ b/packages/file_selector/file_selector_macos/example/lib/main.dart @@ -11,14 +11,11 @@ import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// MyApp is the Main Application. class MyApp extends StatelessWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -27,14 +24,13 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: const HomePage(), + home: HomePage(), routes: { - '/open/image': (BuildContext context) => const OpenImagePage(), - '/open/images': (BuildContext context) => - const OpenMultipleImagesPage(), - '/open/text': (BuildContext context) => const OpenTextPage(), + '/open/image': (BuildContext context) => OpenImagePage(), + '/open/images': (BuildContext context) => OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), - '/directory': (BuildContext context) => const GetDirectoryPage(), + '/directory': (BuildContext context) => GetDirectoryPage(), }, ); } diff --git a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart index 1a05343b27ab..aaf083603e72 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart @@ -11,9 +11,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select an image file using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenImagePage extends StatelessWidget { - /// Default Constructor - const OpenImagePage({Key? key}) : super(key: key); - Future _openImageFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'images', @@ -62,8 +59,7 @@ class OpenImagePage extends StatelessWidget { /// Widget that displays an image in a dialog. class ImageDisplay extends StatelessWidget { /// Default Constructor. - const ImageDisplay(this.fileName, this.filePath, {Key? key}) - : super(key: key); + const ImageDisplay(this.fileName, this.filePath); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart index 9c3c8e369f5b..a030b8b4b10b 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart @@ -11,9 +11,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select multiple image files using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenMultipleImagesPage extends StatelessWidget { - /// Default Constructor - const OpenMultipleImagesPage({Key? key}) : super(key: key); - Future _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', @@ -66,7 +63,7 @@ class OpenMultipleImagesPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class MultipleImagesDisplay extends StatelessWidget { /// Default Constructor. - const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); + const MultipleImagesDisplay(this.files); /// The files containing the images. final List files; diff --git a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart index 9adde400b5e1..fa281a0020d5 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart @@ -8,9 +8,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a text file using `openFile`, then /// displays its contents in a dialog. class OpenTextPage extends StatelessWidget { - /// Default Constructor - const OpenTextPage({Key? key}) : super(key: key); - Future _openTextFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'text', @@ -59,8 +56,7 @@ class OpenTextPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Default Constructor. - const TextDisplay(this.fileName, this.fileContent, {Key? key}) - : super(key: key); + const TextDisplay(this.fileName, this.fileContent); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart index a44a387c019b..3989c62b7442 100644 --- a/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart @@ -9,9 +9,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a save location using `getSavePath`, /// then writes text to a file at that location. class SaveTextPage extends StatelessWidget { - /// Default Constructor - SaveTextPage({Key? key}) : super(key: key); - final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); @@ -70,8 +67,8 @@ class SaveTextPage extends StatelessWidget { primary: Colors.blue, onPrimary: Colors.white, ), - onPressed: _saveFile, child: const Text('Press to save a text file'), + onPressed: _saveFile, ), ], ), diff --git a/packages/file_selector/file_selector_macos/pubspec.yaml b/packages/file_selector/file_selector_macos/pubspec.yaml index 41077c1c04e6..071d261c4bf8 100644 --- a/packages/file_selector/file_selector_macos/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_macos description: macOS implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.2+1 +version: 0.8.2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md index 934d38827ddb..100b6ad136a7 100644 --- a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md +++ b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.0.5 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.0.4 diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml index d280b0f5c71c..42917751ed58 100644 --- a/packages/file_selector/file_selector_platform_interface/pubspec.yaml +++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%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.0.5 +version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md index ce9d5590f9a9..5927239ef9e3 100644 --- a/packages/file_selector/file_selector_web/CHANGELOG.md +++ b/packages/file_selector/file_selector_web/CHANGELOG.md @@ -1,8 +1,3 @@ -## 0.8.1+4 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 0.8.1+3 * Minor code cleanup for new analysis rules. diff --git a/packages/file_selector/file_selector_web/example/lib/main.dart b/packages/file_selector/file_selector_web/example/lib/main.dart index 87422953de6a..341913a18490 100644 --- a/packages/file_selector/file_selector_web/example/lib/main.dart +++ b/packages/file_selector/file_selector_web/example/lib/main.dart @@ -5,16 +5,13 @@ import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// App for testing class MyApp extends StatefulWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index 2e12b6d175a3..74d0412b440f 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/plugins/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.8.1+4 +version: 0.8.1+3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_windows/CHANGELOG.md b/packages/file_selector/file_selector_windows/CHANGELOG.md index c242717c3267..ae3cd13342b1 100644 --- a/packages/file_selector/file_selector_windows/CHANGELOG.md +++ b/packages/file_selector/file_selector_windows/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.8.2+1 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.8.2 diff --git a/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart b/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart index 8fc1a9001465..b282b9030d67 100644 --- a/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart @@ -8,9 +8,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a directory using `getDirectoryPath`, /// then displays the selected directory in a dialog. class GetDirectoryPage extends StatelessWidget { - /// Default Constructor - const GetDirectoryPage({Key? key}) : super(key: key); - Future _getDirectoryPath(BuildContext context) async { const String confirmButtonText = 'Choose'; final String? directoryPath = @@ -55,7 +52,7 @@ class GetDirectoryPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Creates a `TextDisplay`. - const TextDisplay(this.directoryPath, {Key? key}) : super(key: key); + const TextDisplay(this.directoryPath); /// The path selected in the dialog. final String directoryPath; diff --git a/packages/file_selector/file_selector_windows/example/lib/home_page.dart b/packages/file_selector/file_selector_windows/example/lib/home_page.dart index 4d6ca7e6e6e3..958680be0e3b 100644 --- a/packages/file_selector/file_selector_windows/example/lib/home_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/home_page.dart @@ -6,9 +6,6 @@ import 'package:flutter/material.dart'; /// Home Page of the application. class HomePage extends StatelessWidget { - /// Default Constructor - const HomePage({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( diff --git a/packages/file_selector/file_selector_windows/example/lib/main.dart b/packages/file_selector/file_selector_windows/example/lib/main.dart index cbe268e1c7ab..a49ebac1aea5 100644 --- a/packages/file_selector/file_selector_windows/example/lib/main.dart +++ b/packages/file_selector/file_selector_windows/example/lib/main.dart @@ -11,14 +11,11 @@ import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// MyApp is the Main Application. class MyApp extends StatelessWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -27,14 +24,13 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: const HomePage(), + home: HomePage(), routes: { - '/open/image': (BuildContext context) => const OpenImagePage(), - '/open/images': (BuildContext context) => - const OpenMultipleImagesPage(), - '/open/text': (BuildContext context) => const OpenTextPage(), + '/open/image': (BuildContext context) => OpenImagePage(), + '/open/images': (BuildContext context) => OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), - '/directory': (BuildContext context) => const GetDirectoryPage(), + '/directory': (BuildContext context) => GetDirectoryPage(), }, ); } diff --git a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart index 1a05343b27ab..aaf083603e72 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart @@ -11,9 +11,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select an image file using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenImagePage extends StatelessWidget { - /// Default Constructor - const OpenImagePage({Key? key}) : super(key: key); - Future _openImageFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'images', @@ -62,8 +59,7 @@ class OpenImagePage extends StatelessWidget { /// Widget that displays an image in a dialog. class ImageDisplay extends StatelessWidget { /// Default Constructor. - const ImageDisplay(this.fileName, this.filePath, {Key? key}) - : super(key: key); + const ImageDisplay(this.fileName, this.filePath); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart index 9c3c8e369f5b..a030b8b4b10b 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart @@ -11,9 +11,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select multiple image files using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenMultipleImagesPage extends StatelessWidget { - /// Default Constructor - const OpenMultipleImagesPage({Key? key}) : super(key: key); - Future _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', @@ -66,7 +63,7 @@ class OpenMultipleImagesPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class MultipleImagesDisplay extends StatelessWidget { /// Default Constructor. - const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); + const MultipleImagesDisplay(this.files); /// The files containing the images. final List files; diff --git a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart index 9adde400b5e1..fa281a0020d5 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart @@ -8,9 +8,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a text file using `openFile`, then /// displays its contents in a dialog. class OpenTextPage extends StatelessWidget { - /// Default Constructor - const OpenTextPage({Key? key}) : super(key: key); - Future _openTextFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'text', @@ -59,8 +56,7 @@ class OpenTextPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Default Constructor. - const TextDisplay(this.fileName, this.fileContent, {Key? key}) - : super(key: key); + const TextDisplay(this.fileName, this.fileContent); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart index 961e0fb2fbc3..b87a51c3877d 100644 --- a/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart @@ -9,9 +9,6 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a save location using `getSavePath`, /// then writes text to a file at that location. class SaveTextPage extends StatelessWidget { - /// Default Constructor - SaveTextPage({Key? key}) : super(key: key); - final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); @@ -70,8 +67,8 @@ class SaveTextPage extends StatelessWidget { primary: Colors.blue, onPrimary: Colors.white, ), - onPressed: _saveFile, child: const Text('Press to save a text file'), + onPressed: _saveFile, ), ], ), diff --git a/packages/file_selector/file_selector_windows/pubspec.yaml b/packages/file_selector/file_selector_windows/pubspec.yaml index 152b63ef4a3f..7b035e974293 100644 --- a/packages/file_selector/file_selector_windows/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_windows description: Windows implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.2+1 +version: 0.8.2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md index a0a3f67bed46..8fdfc39f3bf9 100644 --- a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md +++ b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.0.6 +## NEXT * Adds OS version support information to README. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.0.5 diff --git a/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart b/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart index 1198c6f01806..1d329a02f93b 100644 --- a/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart +++ b/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart @@ -10,6 +10,6 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('loads', (WidgetTester tester) async { - await tester.pumpWidget(const MyApp()); + await tester.pumpWidget(MyApp()); }); } diff --git a/packages/flutter_plugin_android_lifecycle/example/lib/main.dart b/packages/flutter_plugin_android_lifecycle/example/lib/main.dart index c465b3b687f2..3ef6794dfad2 100644 --- a/packages/flutter_plugin_android_lifecycle/example/lib/main.dart +++ b/packages/flutter_plugin_android_lifecycle/example/lib/main.dart @@ -2,15 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: public_member_api_docs + import 'package:flutter/material.dart'; -void main() => runApp(const MyApp()); +void main() => runApp(MyApp()); -/// MyApp is the Main Application. class MyApp extends StatelessWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( diff --git a/packages/flutter_plugin_android_lifecycle/pubspec.yaml b/packages/flutter_plugin_android_lifecycle/pubspec.yaml index c109d0936589..b359cc55c351 100644 --- a/packages/flutter_plugin_android_lifecycle/pubspec.yaml +++ b/packages/flutter_plugin_android_lifecycle/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_plugin_android_lifecycle description: Flutter plugin for accessing an Android Lifecycle within other plugins. repository: https://github.com/flutter/plugins/tree/main/packages/flutter_plugin_android_lifecycle issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_plugin_android_lifecycle%22 -version: 2.0.6 +version: 2.0.5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index d1f2f926e2ab..b0662b89d9cc 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.1.5 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.1.4 @@ -225,7 +223,7 @@ GoogleMapController is now uniformly driven by implementing `DefaultLifecycleObs ## 0.5.26+1 -* Removes an erroneously added method from the GoogleMapController.h header file. +* Removes a errorneously added method from the GoogleMapController.h header file. ## 0.5.26 diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart index 09df2b98b146..f8072eee7c85 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart @@ -10,8 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class AnimateCameraPage extends GoogleMapExampleAppPage { - const AnimateCameraPage({Key? key}) - : super(const Icon(Icons.map), 'Camera control, animated', key: key); + const AnimateCameraPage() + : super(const Icon(Icons.map), 'Camera control, animated'); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class AnimateCameraPage extends GoogleMapExampleAppPage { } class AnimateCamera extends StatefulWidget { - const AnimateCamera({Key? key}) : super(key: key); + const AnimateCamera(); @override State createState() => AnimateCameraState(); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart index fd95cf864a7c..b1b58fdc91bf 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart @@ -12,8 +12,7 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class LiteModePage extends GoogleMapExampleAppPage { - const LiteModePage({Key? key}) - : super(const Icon(Icons.map), 'Lite mode', key: key); + const LiteModePage() : super(const Icon(Icons.map), 'Lite mode'); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart index 8932705bc6d5..f4d420a72f7c 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.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. +// ignore_for_file: public_member_api_docs + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -41,11 +43,7 @@ final List _allPages = [ const TileOverlayPage(), ]; -/// MapsDemo is the Main Application. class MapsDemo extends StatelessWidget { - /// Default Constructor - const MapsDemo({Key? key}) : super(key: key); - void _pushPage(BuildContext context, GoogleMapExampleAppPage page) { Navigator.of(context).push(MaterialPageRoute( builder: (_) => Scaffold( @@ -74,5 +72,5 @@ void main() { if (defaultTargetPlatform == TargetPlatform.android) { AndroidGoogleMapsFlutter.useAndroidViewSurface = true; } - runApp(const MaterialApp(home: MapsDemo())); + runApp(MaterialApp(home: MapsDemo())); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart index 9c96f25d5fa7..bbe2372dce9a 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart @@ -12,8 +12,7 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class MapClickPage extends GoogleMapExampleAppPage { - const MapClickPage({Key? key}) - : super(const Icon(Icons.mouse), 'Map click', key: key); + const MapClickPage() : super(const Icon(Icons.mouse), 'Map click'); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart index 1179acd46ffa..8e4853c040ed 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart @@ -12,8 +12,7 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class MapCoordinatesPage extends GoogleMapExampleAppPage { - const MapCoordinatesPage({Key? key}) - : super(const Icon(Icons.map), 'Map coordinates', key: key); + const MapCoordinatesPage() : super(const Icon(Icons.map), 'Map coordinates'); @override Widget build(BuildContext context) { 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 fbfeda56a968..48ef1f570e02 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 @@ -16,8 +16,7 @@ final LatLngBounds sydneyBounds = LatLngBounds( ); class MapUiPage extends GoogleMapExampleAppPage { - const MapUiPage({Key? key}) - : super(const Icon(Icons.map), 'User interface', key: key); + const MapUiPage() : super(const Icon(Icons.map), 'User interface'); @override Widget build(BuildContext context) { @@ -26,7 +25,7 @@ class MapUiPage extends GoogleMapExampleAppPage { } class MapUiBody extends StatefulWidget { - const MapUiBody({Key? key}) : super(key: key); + const MapUiBody(); @override State createState() => MapUiBodyState(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart index 58d266c95d1d..95ace9d7c482 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart @@ -11,8 +11,7 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class MarkerIconsPage extends GoogleMapExampleAppPage { - const MarkerIconsPage({Key? key}) - : super(const Icon(Icons.image), 'Marker icons', key: key); + const MarkerIconsPage() : super(const Icon(Icons.image), 'Marker icons'); @override Widget build(BuildContext context) { @@ -21,7 +20,7 @@ class MarkerIconsPage extends GoogleMapExampleAppPage { } class MarkerIconsBody extends StatefulWidget { - const MarkerIconsBody({Key? key}) : super(key: key); + const MarkerIconsBody(); @override State createState() => MarkerIconsBodyState(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart index a6bae3009f0b..33da90f32c1b 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart @@ -10,8 +10,7 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class MoveCameraPage extends GoogleMapExampleAppPage { - const MoveCameraPage({Key? key}) - : super(const Icon(Icons.map), 'Camera control', key: key); + const MoveCameraPage() : super(const Icon(Icons.map), 'Camera control'); @override Widget build(BuildContext context) { @@ -20,7 +19,7 @@ class MoveCameraPage extends GoogleMapExampleAppPage { } class MoveCamera extends StatefulWidget { - const MoveCamera({Key? key}) : super(key: key); + const MoveCamera(); @override State createState() => MoveCameraState(); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart index a4bfa88d559f..77091909e970 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart @@ -9,8 +9,7 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PaddingPage extends GoogleMapExampleAppPage { - const PaddingPage({Key? key}) - : super(const Icon(Icons.map), 'Add padding to the map', key: key); + const PaddingPage() : super(const Icon(Icons.map), 'Add padding to the map'); @override Widget build(BuildContext context) { @@ -19,7 +18,7 @@ class PaddingPage extends GoogleMapExampleAppPage { } class MarkerIconsBody extends StatefulWidget { - const MarkerIconsBody({Key? key}) : super(key: key); + const MarkerIconsBody(); @override State createState() => MarkerIconsBodyState(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart index eb01ab07a6f3..fb6eb3260f6d 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart @@ -7,8 +7,7 @@ import 'package:flutter/material.dart'; abstract class GoogleMapExampleAppPage extends StatelessWidget { - const GoogleMapExampleAppPage(this.leading, this.title, {Key? key}) - : super(key: key); + const GoogleMapExampleAppPage(this.leading, this.title); final Widget leading; final String title; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart index ef5033cfa1ee..c6f1509af69f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart @@ -10,8 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlaceCirclePage extends GoogleMapExampleAppPage { - const PlaceCirclePage({Key? key}) - : super(const Icon(Icons.linear_scale), 'Place circle', key: key); + const PlaceCirclePage() + : super(const Icon(Icons.linear_scale), 'Place circle'); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class PlaceCirclePage extends GoogleMapExampleAppPage { } class PlaceCircleBody extends StatefulWidget { - const PlaceCircleBody({Key? key}) : super(key: key); + const PlaceCircleBody(); @override State createState() => PlaceCircleBodyState(); @@ -170,42 +170,42 @@ class PlaceCircleBodyState extends State { Column( children: [ TextButton( - onPressed: _add, child: const Text('add'), + onPressed: _add, ), TextButton( + child: const Text('remove'), onPressed: (selectedId == null) ? null : () => _remove(selectedId), - child: const Text('remove'), ), TextButton( + child: const Text('toggle visible'), onPressed: (selectedId == null) ? null : () => _toggleVisible(selectedId), - child: const Text('toggle visible'), ), ], ), Column( children: [ TextButton( + child: const Text('change stroke width'), onPressed: (selectedId == null) ? null : () => _changeStrokeWidth(selectedId), - child: const Text('change stroke width'), ), TextButton( + child: const Text('change stroke color'), onPressed: (selectedId == null) ? null : () => _changeStrokeColor(selectedId), - child: const Text('change stroke color'), ), TextButton( + child: const Text('change fill color'), onPressed: (selectedId == null) ? null : () => _changeFillColor(selectedId), - child: const Text('change fill color'), ), ], ) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart index 1238d61547b8..4291cac6841e 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart @@ -15,8 +15,7 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlaceMarkerPage extends GoogleMapExampleAppPage { - const PlaceMarkerPage({Key? key}) - : super(const Icon(Icons.place), 'Place marker', key: key); + const PlaceMarkerPage() : super(const Icon(Icons.place), 'Place marker'); @override Widget build(BuildContext context) { @@ -25,7 +24,7 @@ class PlaceMarkerPage extends GoogleMapExampleAppPage { } class PlaceMarkerBody extends StatefulWidget { - const PlaceMarkerBody({Key? key}) : super(key: key); + const PlaceMarkerBody(); @override State createState() => PlaceMarkerBodyState(); @@ -309,13 +308,13 @@ class PlaceMarkerBodyState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ TextButton( - onPressed: _add, child: const Text('Add'), + onPressed: _add, ), TextButton( + child: const Text('Remove'), onPressed: selectedId == null ? null : () => _remove(selectedId), - child: const Text('Remove'), ), ], ), @@ -323,61 +322,62 @@ class PlaceMarkerBodyState extends State { alignment: WrapAlignment.spaceEvenly, children: [ TextButton( + child: const Text('change info'), onPressed: selectedId == null ? null : () => _changeInfo(selectedId), - child: const Text('change info'), ), TextButton( + child: const Text('change info anchor'), onPressed: selectedId == null ? null : () => _changeInfoAnchor(selectedId), - child: const Text('change info anchor'), ), TextButton( + child: const Text('change alpha'), onPressed: selectedId == null ? null : () => _changeAlpha(selectedId), - child: const Text('change alpha'), ), TextButton( + child: const Text('change anchor'), onPressed: selectedId == null ? null : () => _changeAnchor(selectedId), - child: const Text('change anchor'), ), TextButton( + child: const Text('toggle draggable'), onPressed: selectedId == null ? null : () => _toggleDraggable(selectedId), - child: const Text('toggle draggable'), ), TextButton( + child: const Text('toggle flat'), onPressed: selectedId == null ? null : () => _toggleFlat(selectedId), - child: const Text('toggle flat'), ), TextButton( + child: const Text('change position'), onPressed: selectedId == null ? null : () => _changePosition(selectedId), - child: const Text('change position'), ), TextButton( + child: const Text('change rotation'), onPressed: selectedId == null ? null : () => _changeRotation(selectedId), - child: const Text('change rotation'), ), TextButton( + child: const Text('toggle visible'), onPressed: selectedId == null ? null : () => _toggleVisible(selectedId), - child: const Text('toggle visible'), ), TextButton( + child: const Text('change zIndex'), onPressed: selectedId == null ? null : () => _changeZIndex(selectedId), - child: const Text('change zIndex'), ), TextButton( + child: const Text('set marker icon'), onPressed: selectedId == null ? null : () { @@ -387,7 +387,6 @@ class PlaceMarkerBodyState extends State { }, ); }, - child: const Text('set marker icon'), ), ], ), diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart index f1932141b8ab..8d4fa3e07c36 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart @@ -10,8 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlacePolygonPage extends GoogleMapExampleAppPage { - const PlacePolygonPage({Key? key}) - : super(const Icon(Icons.linear_scale), 'Place polygon', key: key); + const PlacePolygonPage() + : super(const Icon(Icons.linear_scale), 'Place polygon'); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class PlacePolygonPage extends GoogleMapExampleAppPage { } class PlacePolygonBody extends StatefulWidget { - const PlacePolygonBody({Key? key}) : super(key: key); + const PlacePolygonBody(); @override State createState() => PlacePolygonBodyState(); @@ -196,64 +196,64 @@ class PlacePolygonBodyState extends State { Column( children: [ TextButton( - onPressed: _add, child: const Text('add'), + onPressed: _add, ), TextButton( + child: const Text('remove'), onPressed: (selectedId == null) ? null : () => _remove(selectedId), - child: const Text('remove'), ), TextButton( + child: const Text('toggle visible'), onPressed: (selectedId == null) ? null : () => _toggleVisible(selectedId), - child: const Text('toggle visible'), ), TextButton( + child: const Text('toggle geodesic'), onPressed: (selectedId == null) ? null : () => _toggleGeodesic(selectedId), - child: const Text('toggle geodesic'), ), ], ), Column( children: [ TextButton( + child: const Text('add holes'), onPressed: (selectedId == null) ? null : ((polygons[selectedId]!.holes.isNotEmpty) ? null : () => _addHoles(selectedId)), - child: const Text('add holes'), ), TextButton( + child: const Text('remove holes'), onPressed: (selectedId == null) ? null : ((polygons[selectedId]!.holes.isEmpty) ? null : () => _removeHoles(selectedId)), - child: const Text('remove holes'), ), TextButton( + child: const Text('change stroke width'), onPressed: (selectedId == null) ? null : () => _changeWidth(selectedId), - child: const Text('change stroke width'), ), TextButton( + child: const Text('change stroke color'), onPressed: (selectedId == null) ? null : () => _changeStrokeColor(selectedId), - child: const Text('change stroke color'), ), TextButton( + child: const Text('change fill color'), onPressed: (selectedId == null) ? null : () => _changeFillColor(selectedId), - child: const Text('change fill color'), ), ], ) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart index b3a637ce7d15..434920d293be 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart @@ -11,8 +11,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlacePolylinePage extends GoogleMapExampleAppPage { - const PlacePolylinePage({Key? key}) - : super(const Icon(Icons.linear_scale), 'Place polyline', key: key); + const PlacePolylinePage() + : super(const Icon(Icons.linear_scale), 'Place polyline'); @override Widget build(BuildContext context) { @@ -21,7 +21,7 @@ class PlacePolylinePage extends GoogleMapExampleAppPage { } class PlacePolylineBody extends StatefulWidget { - const PlacePolylineBody({Key? key}) : super(key: key); + const PlacePolylineBody(); @override State createState() => PlacePolylineBodyState(); @@ -234,66 +234,66 @@ class PlacePolylineBodyState extends State { Column( children: [ TextButton( - onPressed: _add, child: const Text('add'), + onPressed: _add, ), TextButton( + child: const Text('remove'), onPressed: (selectedId == null) ? null : () => _remove(selectedId), - child: const Text('remove'), ), TextButton( + child: const Text('toggle visible'), onPressed: (selectedId == null) ? null : () => _toggleVisible(selectedId), - child: const Text('toggle visible'), ), TextButton( + child: const Text('toggle geodesic'), onPressed: (selectedId == null) ? null : () => _toggleGeodesic(selectedId), - child: const Text('toggle geodesic'), ), ], ), Column( children: [ TextButton( + child: const Text('change width'), onPressed: (selectedId == null) ? null : () => _changeWidth(selectedId), - child: const Text('change width'), ), TextButton( + child: const Text('change color'), onPressed: (selectedId == null) ? null : () => _changeColor(selectedId), - child: const Text('change color'), ), TextButton( + child: const Text('change start cap [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changeStartCap(selectedId), - child: const Text('change start cap [Android only]'), ), TextButton( + child: const Text('change end cap [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changeEndCap(selectedId), - child: const Text('change end cap [Android only]'), ), TextButton( + child: const Text('change joint type [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changeJointType(selectedId), - child: const Text('change joint type [Android only]'), ), TextButton( + child: const Text('change pattern [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changePattern(selectedId), - child: const Text('change pattern [Android only]'), ), ], ) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart index ca9d3962ddd7..04769315e685 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart @@ -14,8 +14,7 @@ import 'page.dart'; const LatLng _center = LatLng(32.080664, 34.9563837); class ScrollingMapPage extends GoogleMapExampleAppPage { - const ScrollingMapPage({Key? key}) - : super(const Icon(Icons.map), 'Scrolling map', key: key); + const ScrollingMapPage() : super(const Icon(Icons.map), 'Scrolling map'); @override Widget build(BuildContext context) { @@ -24,7 +23,7 @@ class ScrollingMapPage extends GoogleMapExampleAppPage { } class ScrollingMapBody extends StatelessWidget { - const ScrollingMapBody({Key? key}) : super(key: key); + const ScrollingMapBody(); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart index 849a9f469938..9afc28869490 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart @@ -15,9 +15,8 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class SnapshotPage extends GoogleMapExampleAppPage { - const SnapshotPage({Key? key}) - : super(const Icon(Icons.camera_alt), 'Take a snapshot of the map', - key: key); + const SnapshotPage() + : super(const Icon(Icons.camera_alt), 'Take a snapshot of the map'); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart index 81dfc2815866..dc7f7d1d6f33 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart @@ -13,8 +13,7 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class TileOverlayPage extends GoogleMapExampleAppPage { - const TileOverlayPage({Key? key}) - : super(const Icon(Icons.map), 'Tile overlay', key: key); + const TileOverlayPage() : super(const Icon(Icons.map), 'Tile overlay'); @override Widget build(BuildContext context) { @@ -23,7 +22,7 @@ class TileOverlayPage extends GoogleMapExampleAppPage { } class TileOverlayBody extends StatefulWidget { - const TileOverlayBody({Key? key}) : super(key: key); + const TileOverlayBody(); @override State createState() => TileOverlayBodyState(); @@ -91,16 +90,16 @@ class TileOverlayBodyState extends State { ), ), TextButton( - onPressed: _addTileOverlay, child: const Text('Add tile overlay'), + onPressed: _addTileOverlay, ), TextButton( - onPressed: _removeTileOverlay, child: const Text('Remove tile overlay'), + onPressed: _removeTileOverlay, ), TextButton( - onPressed: _clearTileCache, child: const Text('Clear tile cache'), + onPressed: _clearTileCache, ), ], ); 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 dfc6e579d560..088589e4a2ce 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 @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: library_private_types_in_public_api - part of google_maps_flutter; /// Controller for a single GoogleMap instance running on the host platform. diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index a294dd09981f..c10f9c679a17 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/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.1.5 +version: 2.1.4 environment: sdk: ">=2.14.0 <3.0.0" 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 f2fe971f4591..48908b984b0f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.3.2+2 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.3.2+1 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 d1ba571b5bd0..10415204570c 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 @@ -11,7 +11,7 @@ void main() { /// App for testing class MyApp extends StatefulWidget { @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { 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 271d87d21092..2780175d29e2 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/plugins/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.3.2+2 +version: 0.3.2+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 5b47536cd2e2..caab46de7c5b 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,8 +1,3 @@ -## 5.3.1 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 5.3.0 * Moves Android and iOS implementations to federated packages. diff --git a/packages/google_sign_in/google_sign_in/example/lib/main.dart b/packages/google_sign_in/google_sign_in/example/lib/main.dart index 4c27543f5b18..9840a1e0a9f6 100644 --- a/packages/google_sign_in/google_sign_in/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in/example/lib/main.dart @@ -22,7 +22,7 @@ GoogleSignIn _googleSignIn = GoogleSignIn( void main() { runApp( - const MaterialApp( + MaterialApp( title: 'Google Sign In', home: SignInDemo(), ), @@ -30,8 +30,6 @@ void main() { } class SignInDemo extends StatefulWidget { - const SignInDemo({Key? key}) : super(key: key); - @override State createState() => SignInDemoState(); } @@ -127,8 +125,8 @@ class SignInDemoState extends State { const Text('Signed in successfully.'), Text(_contactText), ElevatedButton( - onPressed: _handleSignOut, child: const Text('SIGN OUT'), + onPressed: _handleSignOut, ), ElevatedButton( child: const Text('REFRESH'), @@ -142,8 +140,8 @@ class SignInDemoState extends State { children: [ const Text('You are not currently signed in.'), ElevatedButton( - onPressed: _handleSignIn, child: const Text('SIGN IN'), + onPressed: _handleSignIn, ), ], ); diff --git a/packages/google_sign_in/google_sign_in/lib/widgets.dart b/packages/google_sign_in/google_sign_in/lib/widgets.dart index f7ae5f9a6e5f..61f89133fba4 100644 --- a/packages/google_sign_in/google_sign_in/lib/widgets.dart +++ b/packages/google_sign_in/google_sign_in/lib/widgets.dart @@ -22,13 +22,11 @@ class GoogleUserCircleAvatar extends StatelessWidget { /// in place of a profile photo, or a default profile photo if the user's /// identity does not specify a `displayName`. const GoogleUserCircleAvatar({ - Key? key, required this.identity, this.placeholderPhotoUrl, this.foregroundColor, this.backgroundColor, - }) : assert(identity != null), - super(key: key); + }) : assert(identity != null); /// A regular expression that matches against the "size directive" path /// segment of Google profile image URLs. diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index e58b27af08b7..760706f2e7bc 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/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 and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.3.1 +version: 5.3.0 environment: 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 90069d05f442..3ffa6b5b7d6b 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,8 +1,3 @@ -## 5.2.7 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 5.2.6 * Switches to an internal method channel, rather than the default. diff --git a/packages/google_sign_in/google_sign_in_android/example/lib/main.dart b/packages/google_sign_in/google_sign_in_android/example/lib/main.dart index 526cf8b69ccf..a750c330001d 100644 --- a/packages/google_sign_in/google_sign_in_android/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_android/example/lib/main.dart @@ -14,7 +14,7 @@ import 'package:http/http.dart' as http; void main() { runApp( - const MaterialApp( + MaterialApp( title: 'Google Sign In', home: SignInDemo(), ), @@ -22,8 +22,6 @@ void main() { } class SignInDemo extends StatefulWidget { - const SignInDemo({Key? key}) : super(key: key); - @override State createState() => SignInDemoState(); } @@ -144,8 +142,8 @@ class SignInDemoState extends State { const Text('Signed in successfully.'), Text(_contactText), ElevatedButton( - onPressed: _handleSignOut, child: const Text('SIGN OUT'), + onPressed: _handleSignOut, ), ElevatedButton( child: const Text('REFRESH'), @@ -159,8 +157,8 @@ class SignInDemoState extends State { children: [ const Text('You are not currently signed in.'), ElevatedButton( - onPressed: _handleSignIn, child: const Text('SIGN IN'), + onPressed: _handleSignIn, ), ], ); 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 d9b272320694..fa3dc1489b26 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/plugins/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: 5.2.7 +version: 5.2.6 environment: sdk: ">=2.14.0 <3.0.0" 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 90069d05f442..3ffa6b5b7d6b 100644 --- a/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_ios/CHANGELOG.md @@ -1,8 +1,3 @@ -## 5.2.7 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 5.2.6 * Switches to an internal method channel, rather than the default. diff --git a/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart b/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart index 526cf8b69ccf..a750c330001d 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart @@ -14,7 +14,7 @@ import 'package:http/http.dart' as http; void main() { runApp( - const MaterialApp( + MaterialApp( title: 'Google Sign In', home: SignInDemo(), ), @@ -22,8 +22,6 @@ void main() { } class SignInDemo extends StatefulWidget { - const SignInDemo({Key? key}) : super(key: key); - @override State createState() => SignInDemoState(); } @@ -144,8 +142,8 @@ class SignInDemoState extends State { const Text('Signed in successfully.'), Text(_contactText), ElevatedButton( - onPressed: _handleSignOut, child: const Text('SIGN OUT'), + onPressed: _handleSignOut, ), ElevatedButton( child: const Text('REFRESH'), @@ -159,8 +157,8 @@ class SignInDemoState extends State { children: [ const Text('You are not currently signed in.'), ElevatedButton( - onPressed: _handleSignIn, child: const Text('SIGN IN'), + onPressed: _handleSignIn, ), ], ); 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 b6f541b22ce1..e5ef3832ff0e 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: Android implementation of the google_sign_in plugin. repository: https://github.com/flutter/plugins/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.2.7 +version: 5.2.6 environment: sdk: ">=2.14.0 <3.0.0" 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 6e87bebf5d02..aab4acae0376 100644 --- a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md @@ -1,8 +1,3 @@ -## 0.10.1+1 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 0.10.1 * Updates minimum Flutter version to 2.8. diff --git a/packages/google_sign_in/google_sign_in_web/example/lib/main.dart b/packages/google_sign_in/google_sign_in_web/example/lib/main.dart index b23015c811e8..d381fb4af3ab 100644 --- a/packages/google_sign_in/google_sign_in_web/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_web/example/lib/main.dart @@ -5,16 +5,13 @@ import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// App for testing class MyApp extends StatefulWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { 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 3dcd0e8eef29..5a09453b8e86 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/plugins/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.10.1+1 +version: 0.10.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index a384a5272e05..f1bf54c5cd35 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,8 +1,3 @@ -## 0.8.5+1 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 0.8.5 * Moves Android and iOS implementations to federated packages. diff --git a/packages/image_picker/image_picker/example/lib/main.dart b/packages/image_picker/image_picker/example/lib/main.dart index a6f0e83c3abb..f3ad2375b8f2 100755 --- a/packages/image_picker/image_picker/example/lib/main.dart +++ b/packages/image_picker/image_picker/example/lib/main.dart @@ -13,12 +13,10 @@ import 'package:image_picker/image_picker.dart'; import 'package:video_player/video_player.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( @@ -34,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -179,22 +177,21 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - label: 'image_picker_example_picked_images', - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - // Why network for web? - // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform - return Semantics( - label: 'image_picker_example_picked_image', - child: kIsWeb - ? Image.network(_imageFileList![index].path) - : Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - ); + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + label: 'image_picker_example_picked_images'); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -420,7 +417,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); + const AspectRatioVideo(this.controller); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 818486d8e145..77a50916283e 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5+1 +version: 0.8.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index 0514fc33d420..3472ade28d5b 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,8 +1,3 @@ -## 0.8.4+12 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 0.8.4+11 * Splits from `image_picker` as a federated implementation. diff --git a/packages/image_picker/image_picker_android/example/lib/main.dart b/packages/image_picker/image_picker_android/example/lib/main.dart index d56aeb866195..48eee35445da 100755 --- a/packages/image_picker/image_picker_android/example/lib/main.dart +++ b/packages/image_picker/image_picker_android/example/lib/main.dart @@ -13,12 +13,10 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:video_player/video_player.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( @@ -34,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -179,22 +177,21 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - label: 'image_picker_example_picked_images', - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - // Why network for web? - // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform - return Semantics( - label: 'image_picker_example_picked_image', - child: kIsWeb - ? Image.network(_imageFileList![index].path) - : Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - ); + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + label: 'image_picker_example_picked_images'); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -420,7 +417,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); + const AspectRatioVideo(this.controller); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index 90d136c2c89b..dbeef9bed193 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/plugins/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.4+12 +version: 0.8.4+11 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index c33b3b9981de..dcf353fe19b1 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.1.7 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.1.6 * Internal code cleanup for stricter analysis options. diff --git a/packages/image_picker/image_picker_for_web/example/lib/main.dart b/packages/image_picker/image_picker_for_web/example/lib/main.dart index 87422953de6a..341913a18490 100644 --- a/packages/image_picker/image_picker_for_web/example/lib/main.dart +++ b/packages/image_picker/image_picker_for_web/example/lib/main.dart @@ -5,16 +5,13 @@ import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// App for testing class MyApp extends StatefulWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index b0c5deb0da7a..deccd2b50a1f 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_for_web description: Web platform implementation of image_picker repository: https://github.com/flutter/plugins/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: 2.1.7 +version: 2.1.6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index af391db02689..31a0795a4e30 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.8.5+1 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.8.5 diff --git a/packages/image_picker/image_picker_ios/example/lib/main.dart b/packages/image_picker/image_picker_ios/example/lib/main.dart index d56aeb866195..48eee35445da 100755 --- a/packages/image_picker/image_picker_ios/example/lib/main.dart +++ b/packages/image_picker/image_picker_ios/example/lib/main.dart @@ -13,12 +13,10 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:video_player/video_player.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( @@ -34,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -179,22 +177,21 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - label: 'image_picker_example_picked_images', - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - // Why network for web? - // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform - return Semantics( - label: 'image_picker_example_picked_image', - child: kIsWeb - ? Image.network(_imageFileList![index].path) - : Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - ); + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + label: 'image_picker_example_picked_images'); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -420,7 +417,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); + const AspectRatioVideo(this.controller); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index 76ca20614f18..a9cd052be56a 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 video_picker plugin. repository: https://github.com/flutter/plugins/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.5+1 +version: 0.8.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_windows/CHANGELOG.md b/packages/image_picker/image_picker_windows/CHANGELOG.md index e72ab244068f..d98656b849c8 100644 --- a/packages/image_picker/image_picker_windows/CHANGELOG.md +++ b/packages/image_picker/image_picker_windows/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.1.0+1 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.1.0 diff --git a/packages/image_picker/image_picker_windows/example/lib/main.dart b/packages/image_picker/image_picker_windows/example/lib/main.dart index b3ba3574522a..577d6dadf2d9 100644 --- a/packages/image_picker/image_picker_windows/example/lib/main.dart +++ b/packages/image_picker/image_picker_windows/example/lib/main.dart @@ -12,12 +12,10 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:video_player/video_player.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( @@ -33,7 +31,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -178,18 +176,17 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - label: 'image_picker_example_picked_images', - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - return Semantics( - label: 'image_picker_example_picked_image', - child: Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - ); + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + return Semantics( + label: 'image_picker_example_picked_image', + child: Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + label: 'image_picker_example_picked_images'); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -366,7 +363,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); + const AspectRatioVideo(this.controller); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker_windows/pubspec.yaml b/packages/image_picker/image_picker_windows/pubspec.yaml index afadf3e39148..eec41f7bfa0d 100644 --- a/packages/image_picker/image_picker_windows/pubspec.yaml +++ b/packages/image_picker/image_picker_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_windows description: Windows platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.1.0+1 +version: 0.1.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 8412c23ee8e8..24ef9eaffd1d 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,9 +1,7 @@ -## 3.0.3 +## NEXT * Removes unnecessary imports. * Adds OS version support information to README. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 3.0.2 diff --git a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart index 34346a0bd339..651652b40c3a 100644 --- a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart @@ -33,7 +33,7 @@ const List _kProductIds = [ class _MyApp extends StatefulWidget { @override - State<_MyApp> createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<_MyApp> { @@ -247,61 +247,60 @@ class _MyAppState extends State<_MyApp> { (ProductDetails productDetails) { final PurchaseDetails? previousPurchase = purchases[productDetails.id]; return ListTile( - title: Text( - productDetails.title, - ), - subtitle: Text( - productDetails.description, - ), - trailing: previousPurchase != null - ? IconButton( - onPressed: () => confirmPriceChange(context), - icon: const Icon(Icons.upgrade)) - : TextButton( - style: TextButton.styleFrom( - backgroundColor: Colors.green[800], - primary: Colors.white, - ), - onPressed: () { - late PurchaseParam purchaseParam; - - if (Platform.isAndroid) { - // NOTE: If you are making a subscription purchase/upgrade/downgrade, we recommend you to - // verify the latest status of you your subscription by using server side receipt validation - // and update the UI accordingly. The subscription purchase status shown - // inside the app may not be accurate. - final GooglePlayPurchaseDetails? oldSubscription = - _getOldSubscription(productDetails, purchases); - - purchaseParam = GooglePlayPurchaseParam( + title: Text( + productDetails.title, + ), + subtitle: Text( + productDetails.description, + ), + trailing: previousPurchase != null + ? IconButton( + onPressed: () => confirmPriceChange(context), + icon: const Icon(Icons.upgrade)) + : TextButton( + child: Text(productDetails.price), + style: TextButton.styleFrom( + backgroundColor: Colors.green[800], + primary: Colors.white, + ), + onPressed: () { + late PurchaseParam purchaseParam; + + if (Platform.isAndroid) { + // NOTE: If you are making a subscription purchase/upgrade/downgrade, we recommend you to + // verify the latest status of you your subscription by using server side receipt validation + // and update the UI accordingly. The subscription purchase status shown + // inside the app may not be accurate. + final GooglePlayPurchaseDetails? oldSubscription = + _getOldSubscription(productDetails, purchases); + + purchaseParam = GooglePlayPurchaseParam( + productDetails: productDetails, + applicationUserName: null, + changeSubscriptionParam: (oldSubscription != null) + ? ChangeSubscriptionParam( + oldPurchaseDetails: oldSubscription, + prorationMode: ProrationMode + .immediateWithTimeProration, + ) + : null); + } else { + purchaseParam = PurchaseParam( productDetails: productDetails, applicationUserName: null, - changeSubscriptionParam: (oldSubscription != null) - ? ChangeSubscriptionParam( - oldPurchaseDetails: oldSubscription, - prorationMode: - ProrationMode.immediateWithTimeProration, - ) - : null); - } else { - purchaseParam = PurchaseParam( - productDetails: productDetails, - applicationUserName: null, - ); - } - - if (productDetails.id == _kConsumableId) { - _inAppPurchase.buyConsumable( - purchaseParam: purchaseParam, - autoConsume: _kAutoConsume || Platform.isIOS); - } else { - _inAppPurchase.buyNonConsumable( - purchaseParam: purchaseParam); - } - }, - child: Text(productDetails.price), - ), - ); + ); + } + + if (productDetails.id == _kConsumableId) { + _inAppPurchase.buyConsumable( + purchaseParam: purchaseParam, + autoConsume: _kAutoConsume || Platform.isIOS); + } else { + _inAppPurchase.buyNonConsumable( + purchaseParam: purchaseParam); + } + }, + )); }, )); @@ -341,9 +340,9 @@ class _MyAppState extends State<_MyApp> { const Divider(), GridView.count( crossAxisCount: 5, + children: tokens, shrinkWrap: true, padding: const EdgeInsets.all(16.0), - children: tokens, ) ])); } @@ -360,12 +359,12 @@ class _MyAppState extends State<_MyApp> { mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( + child: const Text('Restore purchases'), style: TextButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, primary: Colors.white, ), onPressed: () => _inAppPurchase.restorePurchases(), - child: const Text('Restore purchases'), ), ], ), diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index d2f875293876..af8f5f3c2773 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 3.0.3 +version: 3.0.2 environment: sdk: ">=2.12.0 <3.0.0" 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 4b9e58c08d06..2657d504ac91 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.2.2+4 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.2.2+3 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 1da943535f70..939bc43bea63 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 @@ -37,7 +37,7 @@ const List _kProductIds = [ class _MyApp extends StatefulWidget { @override - State<_MyApp> createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<_MyApp> { @@ -264,6 +264,7 @@ class _MyAppState extends State<_MyApp> { }, icon: const Icon(Icons.upgrade)) : TextButton( + child: Text(productDetails.price), style: TextButton.styleFrom( backgroundColor: Colors.green[800], primary: Colors.white, @@ -296,7 +297,6 @@ class _MyAppState extends State<_MyApp> { purchaseParam: purchaseParam); } }, - child: Text(productDetails.price), )); }, )); @@ -337,9 +337,9 @@ class _MyAppState extends State<_MyApp> { const Divider(), GridView.count( crossAxisCount: 5, + children: tokens, shrinkWrap: true, padding: const EdgeInsets.all(16.0), - children: tokens, ) ])); } 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 7de778177c31..62888e6dfb73 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/plugins/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.2.2+4 +version: 0.2.2+3 environment: sdk: ">=2.14.0 <3.0.0" 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 7342077ab176..403ee32be2ae 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.3.0+6 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.3.0+5 diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart index f45a8c7f8741..2ee2deb7fe35 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart @@ -37,7 +37,7 @@ const List _kProductIds = [ class _MyApp extends StatefulWidget { @override - State<_MyApp> createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<_MyApp> { @@ -259,6 +259,7 @@ class _MyAppState extends State<_MyApp> { }, icon: const Icon(Icons.upgrade)) : TextButton( + child: Text(productDetails.price), style: TextButton.styleFrom( backgroundColor: Colors.green[800], primary: Colors.white, @@ -277,7 +278,6 @@ class _MyAppState extends State<_MyApp> { purchaseParam: purchaseParam); } }, - child: Text(productDetails.price), )); }, )); @@ -318,9 +318,9 @@ class _MyAppState extends State<_MyApp> { const Divider(), GridView.count( crossAxisCount: 5, + children: tokens, shrinkWrap: true, padding: const EdgeInsets.all(16.0), - children: tokens, ) ])); } @@ -337,12 +337,12 @@ class _MyAppState extends State<_MyApp> { mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( + child: const Text('Restore purchases'), style: TextButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, primary: Colors.white, ), onPressed: () => _iapStoreKitPlatform.restorePurchases(), - child: const Text('Restore purchases'), ), ], ), 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 24b693c98c36..9693c186119c 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 platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/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.0+6 +version: 0.3.0+5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index cf2632feaac7..0a3755afa7e7 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,8 +1,6 @@ -## 0.2.0+6 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.2.0+5 diff --git a/packages/ios_platform_images/example/lib/main.dart b/packages/ios_platform_images/example/lib/main.dart index 929814ecce00..ecdfeb2cba01 100644 --- a/packages/ios_platform_images/example/lib/main.dart +++ b/packages/ios_platform_images/example/lib/main.dart @@ -5,15 +5,12 @@ import 'package:flutter/material.dart'; import 'package:ios_platform_images/ios_platform_images.dart'; -void main() => runApp(const MyApp()); +void main() => runApp(MyApp()); /// Main widget for the example app. class MyApp extends StatefulWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/ios_platform_images/example/test/widget_test.dart b/packages/ios_platform_images/example/test/widget_test.dart index f3cd4c68b65b..18e9e657ddb9 100644 --- a/packages/ios_platform_images/example/test/widget_test.dart +++ b/packages/ios_platform_images/example/test/widget_test.dart @@ -12,7 +12,7 @@ import 'package:ios_platform_images_example/main.dart'; void main() { testWidgets('Verify loads image', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); + await tester.pumpWidget(MyApp()); expect( find.byWidgetPredicate( diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index 41a177560299..7f80714e4c1c 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -2,7 +2,7 @@ name: ios_platform_images description: A plugin to share images between Flutter and iOS in add-to-app setups. repository: https://github.com/flutter/plugins/tree/main/packages/ios_platform_images issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+ios_platform_images%22 -version: 0.2.0+6 +version: 0.2.0+5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 6765b497e506..570d8eef7b9b 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -4,8 +4,6 @@ * Updates README to match API changes in 2.0, and to improve clarity in general. * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.0.0 diff --git a/packages/local_auth/local_auth/example/lib/main.dart b/packages/local_auth/local_auth/example/lib/main.dart index cc687f562402..92ad7cf4fb3f 100644 --- a/packages/local_auth/local_auth/example/lib/main.dart +++ b/packages/local_auth/local_auth/example/lib/main.dart @@ -11,14 +11,12 @@ import 'package:flutter/services.dart'; import 'package:local_auth/local_auth.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { @@ -171,14 +169,14 @@ class _MyAppState extends State { const Divider(height: 100), Text('Can check biometrics: $_canCheckBiometrics\n'), ElevatedButton( - onPressed: _checkBiometrics, child: const Text('Check biometrics'), + onPressed: _checkBiometrics, ), const Divider(height: 100), Text('Available biometrics: $_availableBiometrics\n'), ElevatedButton( - onPressed: _getAvailableBiometrics, child: const Text('Get available biometrics'), + onPressed: _getAvailableBiometrics, ), const Divider(height: 100), Text('Current State: $_authorized\n'), @@ -197,7 +195,6 @@ class _MyAppState extends State { Column( children: [ ElevatedButton( - onPressed: _authenticate, child: Row( mainAxisSize: MainAxisSize.min, children: const [ @@ -205,9 +202,9 @@ class _MyAppState extends State { Icon(Icons.perm_device_information), ], ), + onPressed: _authenticate, ), ElevatedButton( - onPressed: _authenticateWithBiometrics, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -217,6 +214,7 @@ class _MyAppState extends State { const Icon(Icons.fingerprint), ], ), + onPressed: _authenticateWithBiometrics, ), ], ), diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 9f9bd7b535a9..6afcf1ffed07 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,8 +1,6 @@ -## 1.0.3 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 1.0.2 diff --git a/packages/local_auth/local_auth_android/example/lib/main.dart b/packages/local_auth/local_auth_android/example/lib/main.dart index 016d955f0a3f..29b1d66440eb 100644 --- a/packages/local_auth/local_auth_android/example/lib/main.dart +++ b/packages/local_auth/local_auth_android/example/lib/main.dart @@ -12,14 +12,12 @@ import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { @@ -176,14 +174,14 @@ class _MyAppState extends State { Text( 'Device supports biometrics: $_deviceSupportsBiometrics\n'), ElevatedButton( - onPressed: _checkBiometrics, child: const Text('Check biometrics'), + onPressed: _checkBiometrics, ), const Divider(height: 100), Text('Enrolled biometrics: $_enrolledBiometrics\n'), ElevatedButton( - onPressed: _getEnrolledBiometrics, child: const Text('Get enrolled biometrics'), + onPressed: _getEnrolledBiometrics, ), const Divider(height: 100), Text('Current State: $_authorized\n'), @@ -202,7 +200,6 @@ class _MyAppState extends State { Column( children: [ ElevatedButton( - onPressed: _authenticate, child: Row( mainAxisSize: MainAxisSize.min, children: const [ @@ -210,9 +207,9 @@ class _MyAppState extends State { Icon(Icons.perm_device_information), ], ), + onPressed: _authenticate, ), ElevatedButton( - onPressed: _authenticateWithBiometrics, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -222,6 +219,7 @@ class _MyAppState extends State { const Icon(Icons.fingerprint), ], ), + onPressed: _authenticateWithBiometrics, ), ], ), diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index cdd4e8225504..aad0b9b92e70 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.3 +version: 1.0.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index 2237cbe216f0..ca6ac43eb52f 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,8 +1,6 @@ -## 1.0.5 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 1.0.4 diff --git a/packages/local_auth/local_auth_ios/example/lib/main.dart b/packages/local_auth/local_auth_ios/example/lib/main.dart index 479a96ba809c..a8bf23b78a52 100644 --- a/packages/local_auth/local_auth_ios/example/lib/main.dart +++ b/packages/local_auth/local_auth_ios/example/lib/main.dart @@ -12,14 +12,12 @@ import 'package:local_auth_ios/local_auth_ios.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { @@ -175,14 +173,14 @@ class _MyAppState extends State { const Divider(height: 100), Text('Device supports biometrics: $_canCheckBiometrics\n'), ElevatedButton( - onPressed: _checkBiometrics, child: const Text('Check biometrics'), + onPressed: _checkBiometrics, ), const Divider(height: 100), Text('Enrolled biometrics: $_enrolledBiometrics\n'), ElevatedButton( - onPressed: _getEnrolledBiometrics, child: const Text('Get enrolled biometrics'), + onPressed: _getEnrolledBiometrics, ), const Divider(height: 100), Text('Current State: $_authorized\n'), @@ -201,7 +199,6 @@ class _MyAppState extends State { Column( children: [ ElevatedButton( - onPressed: _authenticate, child: Row( mainAxisSize: MainAxisSize.min, children: const [ @@ -209,9 +206,9 @@ class _MyAppState extends State { Icon(Icons.perm_device_information), ], ), + onPressed: _authenticate, ), ElevatedButton( - onPressed: _authenticateWithBiometrics, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -221,6 +218,7 @@ class _MyAppState extends State { const Icon(Icons.fingerprint), ], ), + onPressed: _authenticateWithBiometrics, ), ], ), diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index dded42b673f4..77ab74d383c8 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.5 +version: 1.0.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index 990021671801..a26a8901d1d7 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,9 +1,7 @@ -## 2.0.10 +## NEXT * Removes unnecessary imports. * Adds OS version support information to README. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.0.9 diff --git a/packages/path_provider/path_provider/example/lib/main.dart b/packages/path_provider/path_provider/example/lib/main.dart index cb9c2eb1798d..90c2ccb93154 100644 --- a/packages/path_provider/path_provider/example/lib/main.dart +++ b/packages/path_provider/path_provider/example/lib/main.dart @@ -10,12 +10,10 @@ import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -33,7 +31,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -140,10 +138,10 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestTempDirectory, child: const Text( 'Get Temporary Directory', ), + onPressed: _requestTempDirectory, ), ), FutureBuilder( @@ -157,10 +155,10 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestAppDocumentsDirectory, child: const Text( 'Get Application Documents Directory', ), + onPressed: _requestAppDocumentsDirectory, ), ), FutureBuilder( @@ -174,10 +172,10 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestAppSupportDirectory, child: const Text( 'Get Application Support Directory', ), + onPressed: _requestAppSupportDirectory, ), ), FutureBuilder( @@ -191,13 +189,13 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: - Platform.isAndroid ? null : _requestAppLibraryDirectory, child: Text( Platform.isAndroid ? 'Application Library Directory unavailable' : 'Get Application Library Directory', ), + onPressed: + Platform.isAndroid ? null : _requestAppLibraryDirectory, ), ), FutureBuilder( @@ -211,14 +209,14 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: !Platform.isAndroid - ? null - : _requestExternalStorageDirectory, child: Text( !Platform.isAndroid ? 'External storage is unavailable' : 'Get External Storage Directory', ), + onPressed: !Platform.isAndroid + ? null + : _requestExternalStorageDirectory, ), ), FutureBuilder( @@ -232,6 +230,11 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + child: Text( + !Platform.isAndroid + ? 'External directories are unavailable' + : 'Get External Storage Directories', + ), onPressed: !Platform.isAndroid ? null : () { @@ -239,11 +242,6 @@ class _MyHomePageState extends State { StorageDirectory.music, ); }, - child: Text( - !Platform.isAndroid - ? 'External directories are unavailable' - : 'Get External Storage Directories', - ), ), ), FutureBuilder?>( @@ -257,14 +255,14 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: !Platform.isAndroid - ? null - : _requestExternalCacheDirectories, child: Text( !Platform.isAndroid ? 'External directories are unavailable' : 'Get External Cache Directories', ), + onPressed: !Platform.isAndroid + ? null + : _requestExternalCacheDirectories, ), ), FutureBuilder?>( @@ -278,14 +276,14 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: Platform.isAndroid || Platform.isIOS - ? null - : _requestDownloadsDirectory, child: Text( Platform.isAndroid || Platform.isIOS ? 'Downloads directory is unavailable' : 'Get Downloads Directory', ), + onPressed: Platform.isAndroid || Platform.isIOS + ? null + : _requestDownloadsDirectory, ), ), FutureBuilder( diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml index 6ca8325843e4..30ddfdcfb119 100644 --- a/packages/path_provider/path_provider/pubspec.yaml +++ b/packages/path_provider/path_provider/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider description: Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.10 +version: 2.0.9 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index 4b15e2605038..31f8c81f8a65 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.0.14 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.0.13 * Fixes typing build warning. diff --git a/packages/path_provider/path_provider_android/example/lib/main.dart b/packages/path_provider/path_provider_android/example/lib/main.dart index fc9424a33542..6e04f865bfcf 100644 --- a/packages/path_provider/path_provider_android/example/lib/main.dart +++ b/packages/path_provider/path_provider_android/example/lib/main.dart @@ -8,12 +8,10 @@ import 'package:flutter/material.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -31,7 +29,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -123,8 +121,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestTempDirectory, child: const Text('Get Temporary Directory'), + onPressed: _requestTempDirectory, ), ), FutureBuilder( @@ -132,8 +130,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestAppDocumentsDirectory, child: const Text('Get Application Documents Directory'), + onPressed: _requestAppDocumentsDirectory, ), ), FutureBuilder( @@ -141,8 +139,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestAppSupportDirectory, child: const Text('Get Application Support Directory'), + onPressed: _requestAppSupportDirectory, ), ), FutureBuilder( @@ -150,8 +148,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestExternalStorageDirectory, child: const Text('Get External Storage Directory'), + onPressed: _requestExternalStorageDirectory, ), ), FutureBuilder( @@ -176,8 +174,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestExternalCacheDirectories, child: const Text('Get External Cache Directories'), + onPressed: _requestExternalCacheDirectories, ), ), ]), diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index f1dc92abdefc..93ed9848f75b 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.14 +version: 2.0.13 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_ios/CHANGELOG.md b/packages/path_provider/path_provider_ios/CHANGELOG.md index 1940f5c7888e..543af778d2e2 100644 --- a/packages/path_provider/path_provider_ios/CHANGELOG.md +++ b/packages/path_provider/path_provider_ios/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.0.9 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.0.8 * Switches to a package-internal implementation of the platform interface. diff --git a/packages/path_provider/path_provider_ios/example/lib/main.dart b/packages/path_provider/path_provider_ios/example/lib/main.dart index d7140b76a06b..8c8d5410f923 100644 --- a/packages/path_provider/path_provider_ios/example/lib/main.dart +++ b/packages/path_provider/path_provider_ios/example/lib/main.dart @@ -8,12 +8,10 @@ import 'package:flutter/material.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -31,7 +29,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -92,8 +90,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestTempDirectory, child: const Text('Get Temporary Directory'), + onPressed: _requestTempDirectory, ), ), FutureBuilder( @@ -101,8 +99,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestAppDocumentsDirectory, child: const Text('Get Application Documents Directory'), + onPressed: _requestAppDocumentsDirectory, ), ), FutureBuilder( @@ -110,8 +108,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestAppSupportDirectory, child: const Text('Get Application Support Directory'), + onPressed: _requestAppSupportDirectory, ), ), FutureBuilder( @@ -119,8 +117,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - onPressed: _requestAppLibraryDirectory, child: const Text('Get Application Library Directory'), + onPressed: _requestAppLibraryDirectory, ), ), FutureBuilder( diff --git a/packages/path_provider/path_provider_ios/pubspec.yaml b/packages/path_provider/path_provider_ios/pubspec.yaml index d6c7de108c97..282f8e4f0ebd 100644 --- a/packages/path_provider/path_provider_ios/pubspec.yaml +++ b/packages/path_provider/path_provider_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_ios description: iOS implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.9 +version: 2.0.8 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_linux/CHANGELOG.md b/packages/path_provider/path_provider_linux/CHANGELOG.md index c9c4bb3cc906..55235c3542f9 100644 --- a/packages/path_provider/path_provider_linux/CHANGELOG.md +++ b/packages/path_provider/path_provider_linux/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.1.6 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.1.5 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_linux/example/lib/main.dart b/packages/path_provider/path_provider_linux/example/lib/main.dart index 1c7c7e87397a..d365e6bdeab4 100644 --- a/packages/path_provider/path_provider_linux/example/lib/main.dart +++ b/packages/path_provider/path_provider_linux/example/lib/main.dart @@ -7,16 +7,13 @@ import 'package:flutter/services.dart'; import 'package:path_provider_linux/path_provider_linux.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// Sample app class MyApp extends StatefulWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index 16438a3870d1..91304fc0b268 100644 --- a/packages/path_provider/path_provider_linux/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_linux description: Linux implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.1.6 +version: 2.1.5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/path_provider/path_provider_macos/CHANGELOG.md b/packages/path_provider/path_provider_macos/CHANGELOG.md index c59ba971d461..047792f8bcc4 100644 --- a/packages/path_provider/path_provider_macos/CHANGELOG.md +++ b/packages/path_provider/path_provider_macos/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.0.6 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.0.5 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_macos/example/lib/main.dart b/packages/path_provider/path_provider_macos/example/lib/main.dart index 13a6fada5fef..67a0eb32eeda 100644 --- a/packages/path_provider/path_provider_macos/example/lib/main.dart +++ b/packages/path_provider/path_provider_macos/example/lib/main.dart @@ -8,15 +8,13 @@ import 'package:flutter/material.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// Sample app class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/path_provider/path_provider_macos/pubspec.yaml b/packages/path_provider/path_provider_macos/pubspec.yaml index 444165b86c3f..2451b6dedf80 100644 --- a/packages/path_provider/path_provider_macos/pubspec.yaml +++ b/packages/path_provider/path_provider_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_macos description: macOS implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.6 +version: 2.0.5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/path_provider/path_provider_windows/CHANGELOG.md b/packages/path_provider/path_provider_windows/CHANGELOG.md index 014b6b36da2b..cd33e85a851d 100644 --- a/packages/path_provider/path_provider_windows/CHANGELOG.md +++ b/packages/path_provider/path_provider_windows/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.0.6 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.0.5 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_windows/example/lib/main.dart b/packages/path_provider/path_provider_windows/example/lib/main.dart index 4c63d245a16a..509292bf7405 100644 --- a/packages/path_provider/path_provider_windows/example/lib/main.dart +++ b/packages/path_provider/path_provider_windows/example/lib/main.dart @@ -8,15 +8,13 @@ import 'package:flutter/material.dart'; import 'package:path_provider_windows/path_provider_windows.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// Sample app class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/path_provider/path_provider_windows/pubspec.yaml b/packages/path_provider/path_provider_windows/pubspec.yaml index 49afdd6293e7..873fa0a6861b 100644 --- a/packages/path_provider/path_provider_windows/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_windows description: Windows implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.6 +version: 2.0.5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index 73540a863364..c30d7052320e 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,10 +1,8 @@ -## 0.6.0+11 +## NEXT * Removes unnecessary imports. * Updates minimum Flutter version to 2.8. * Adds OS version support information to README. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.6.0+10 diff --git a/packages/quick_actions/quick_actions/example/lib/main.dart b/packages/quick_actions/quick_actions/example/lib/main.dart index cafbf0c351d9..1ce6f51d71de 100644 --- a/packages/quick_actions/quick_actions/example/lib/main.dart +++ b/packages/quick_actions/quick_actions/example/lib/main.dart @@ -8,12 +8,10 @@ import 'package:flutter/material.dart'; import 'package:quick_actions/quick_actions.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -30,7 +28,7 @@ class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/quick_actions/quick_actions/pubspec.yaml b/packages/quick_actions/quick_actions/pubspec.yaml index 37e8dbe5f3e3..8ef2d3ab4e02 100644 --- a/packages/quick_actions/quick_actions/pubspec.yaml +++ b/packages/quick_actions/quick_actions/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for creating shortcuts on home screen, also known as Quick Actions on iOS and App Shortcuts on Android. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%22 -version: 0.6.0+11 +version: 0.6.0+10 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions_android/CHANGELOG.md b/packages/quick_actions/quick_actions_android/CHANGELOG.md index 56accc9d044c..98e8cf5e333b 100644 --- a/packages/quick_actions/quick_actions_android/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_android/CHANGELOG.md @@ -1,8 +1,3 @@ -## 0.6.0+10 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 0.6.0+9 * Switches to a package-internal implementation of the platform interface. \ No newline at end of file diff --git a/packages/quick_actions/quick_actions_android/example/lib/main.dart b/packages/quick_actions/quick_actions_android/example/lib/main.dart index d8b7832bf9dc..06f141073b33 100644 --- a/packages/quick_actions/quick_actions_android/example/lib/main.dart +++ b/packages/quick_actions/quick_actions_android/example/lib/main.dart @@ -8,12 +8,10 @@ import 'package:flutter/material.dart'; import 'package:quick_actions_android/quick_actions_android.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -30,7 +28,7 @@ class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/quick_actions/quick_actions_android/pubspec.yaml b/packages/quick_actions/quick_actions_android/pubspec.yaml index 4ddbc79ee5e9..cf9971dca945 100644 --- a/packages/quick_actions/quick_actions_android/pubspec.yaml +++ b/packages/quick_actions/quick_actions_android/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_android description: An implementation for the Android platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.6.0+10 +version: 0.6.0+9 environment: sdk: ">=2.15.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions_ios/CHANGELOG.md b/packages/quick_actions/quick_actions_ios/CHANGELOG.md index 56accc9d044c..d48afbd8d13a 100644 --- a/packages/quick_actions/quick_actions_ios/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_ios/CHANGELOG.md @@ -1,7 +1,3 @@ -## 0.6.0+10 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.6.0+9 diff --git a/packages/quick_actions/quick_actions_ios/example/lib/main.dart b/packages/quick_actions/quick_actions_ios/example/lib/main.dart index 008917b724e0..5173d952d623 100644 --- a/packages/quick_actions/quick_actions_ios/example/lib/main.dart +++ b/packages/quick_actions/quick_actions_ios/example/lib/main.dart @@ -8,12 +8,10 @@ import 'package:flutter/material.dart'; import 'package:quick_actions_ios/quick_actions_ios.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -30,7 +28,7 @@ class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/quick_actions/quick_actions_ios/pubspec.yaml b/packages/quick_actions/quick_actions_ios/pubspec.yaml index 47748b9789ad..26644ba12fde 100644 --- a/packages/quick_actions/quick_actions_ios/pubspec.yaml +++ b/packages/quick_actions/quick_actions_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_ios description: An implementation for the iOS platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.6.0+10 +version: 0.6.0+9 environment: sdk: ">=2.15.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index 22c39aad98fd..84566e26e2c0 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.0.14 +## NEXT * Adds OS version support information to README. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.0.13 diff --git a/packages/shared_preferences/shared_preferences/example/lib/main.dart b/packages/shared_preferences/shared_preferences/example/lib/main.dart index a2e72b446925..43f3c78ad920 100644 --- a/packages/shared_preferences/shared_preferences/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences/example/lib/main.dart @@ -10,12 +10,10 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index 4218095c0efe..39b48ef51ed2 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.14 +version: 2.0.13 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index 51e99ec6d3d5..5321e869c497 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.0.12 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.0.11 * Switches to an in-package method channel implementation. diff --git a/packages/shared_preferences/shared_preferences_android/example/lib/main.dart b/packages/shared_preferences/shared_preferences_android/example/lib/main.dart index bb513b09f6d5..06ee9434ba84 100644 --- a/packages/shared_preferences/shared_preferences_android/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_android/example/lib/main.dart @@ -8,12 +8,10 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_android/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/pubspec.yaml index 2d8cc88d3703..7eb180f3ab48 100644 --- a/packages/shared_preferences/shared_preferences_android/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_android description: Android implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.12 +version: 2.0.11 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md index 29ade8d496a1..a5cc1d34e034 100644 --- a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.1.1 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.1.0 * Upgrades to using Pigeon. diff --git a/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart b/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart index bb513b09f6d5..06ee9434ba84 100644 --- a/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart @@ -8,12 +8,10 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml index a8bde2e9f87f..33bf5baffd18 100644 --- a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_ios description: iOS implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.1 +version: 2.1.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md index f0cb8322f385..34dd631746bb 100644 --- a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.1.1 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.1.0 diff --git a/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart b/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart index d51be33baeed..4b71c7ea3beb 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart @@ -10,12 +10,10 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_linux/shared_preferences_linux.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml index 8f3ce1723bc9..8ab692a613e2 100644 --- a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_linux description: Linux implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.1 +version: 2.1.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md index 8ba116a74fe0..0f194de44224 100644 --- a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.0.4 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.0.3 diff --git a/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart b/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart index e6bbe5931471..349e9c45405a 100644 --- a/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart @@ -10,12 +10,10 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml index 615d0b05ba99..0873696e6d4f 100644 --- a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_macos description: macOS implementation of the shared_preferences plugin. repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.4 +version: 2.0.3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md index 9ea249034105..2a6ffa20e37b 100644 --- a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.0.4 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.0.3 * Fixes newly enabled analyzer options. diff --git a/packages/shared_preferences/shared_preferences_web/example/lib/main.dart b/packages/shared_preferences/shared_preferences_web/example/lib/main.dart index 87422953de6a..341913a18490 100644 --- a/packages/shared_preferences/shared_preferences_web/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_web/example/lib/main.dart @@ -5,16 +5,13 @@ import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// App for testing class MyApp extends StatefulWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/shared_preferences/shared_preferences_web/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/pubspec.yaml index 9ff76d27714c..232c87c426fc 100644 --- a/packages/shared_preferences/shared_preferences_web/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_web description: Web platform implementation of shared_preferences repository: https://github.com/flutter/plugins/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.0.4 +version: 2.0.3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md index f79f9e3d5d39..6c96681ce5b7 100644 --- a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.1.1 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.1.0 * Deprecated `SharedPreferencesWindows.instance` in favor of `SharedPreferencesStorePlatform.instance`. diff --git a/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart b/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart index 74d5e4c68772..40a9159cee70 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart @@ -10,12 +10,10 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_windows/shared_preferences_windows.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml index 99326cb24f18..6dcb5997f131 100644 --- a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_windows description: Windows implementation of shared_preferences repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.1 +version: 2.1.0 environment: sdk: '>=2.12.0 <3.0.0' diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 493412c3e006..b25956fd5919 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,8 +1,6 @@ -## 6.1.1 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 6.1.0 diff --git a/packages/url_launcher/url_launcher/example/lib/main.dart b/packages/url_launcher/url_launcher/example/lib/main.dart index a538940f1a68..898e80661296 100644 --- a/packages/url_launcher/url_launcher/example/lib/main.dart +++ b/packages/url_launcher/url_launcher/example/lib/main.dart @@ -11,12 +11,10 @@ import 'package:url_launcher/link.dart'; import 'package:url_launcher/url_launcher.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -34,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher/lib/src/link.dart b/packages/url_launcher/url_launcher/lib/src/link.dart index 8c0c18e820e3..76cb97748003 100644 --- a/packages/url_launcher/url_launcher/lib/src/link.dart +++ b/packages/url_launcher/url_launcher/lib/src/link.dart @@ -86,7 +86,7 @@ class Link extends StatelessWidget implements LinkInfo { /// event channel messages to instruct the framework to push the route name. class DefaultLinkDelegate extends StatelessWidget { /// Creates a delegate for the given [link]. - const DefaultLinkDelegate(this.link, {Key? key}) : super(key: key); + const DefaultLinkDelegate(this.link); /// Given a [link], creates an instance of [DefaultLinkDelegate]. /// diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index c14b62a1e70a..6803d71032cb 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/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.1.1 +version: 6.1.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md index 887178c479e4..69b96156d849 100644 --- a/packages/url_launcher/url_launcher_android/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -1,8 +1,3 @@ -## 6.0.17 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 6.0.16 * Adds fallback querying for `canLaunch` with web URLs, to avoid false negatives diff --git a/packages/url_launcher/url_launcher_android/example/lib/main.dart b/packages/url_launcher/url_launcher_android/example/lib/main.dart index 672ae4a27665..7abc73430e8b 100644 --- a/packages/url_launcher/url_launcher_android/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_android/example/lib/main.dart @@ -10,12 +10,10 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -33,7 +31,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index 3c80170f1422..3230dfeffd2e 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_android description: Android implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.17 +version: 6.0.16 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_ios/CHANGELOG.md b/packages/url_launcher/url_launcher_ios/CHANGELOG.md index 5f6dd37142bb..6e0c8d6a20d7 100644 --- a/packages/url_launcher/url_launcher_ios/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_ios/CHANGELOG.md @@ -1,8 +1,3 @@ -## 6.0.16 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 6.0.15 * Switches to an in-package method channel implementation. diff --git a/packages/url_launcher/url_launcher_ios/example/lib/main.dart b/packages/url_launcher/url_launcher_ios/example/lib/main.dart index 7aa3a4b74e83..2f73622ebb41 100644 --- a/packages/url_launcher/url_launcher_ios/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_ios/example/lib/main.dart @@ -10,12 +10,10 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -33,7 +31,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_ios/pubspec.yaml b/packages/url_launcher/url_launcher_ios/pubspec.yaml index 0b21bad35204..8a5bfd20c8f4 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/plugins/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.0.16 +version: 6.0.15 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_linux/CHANGELOG.md b/packages/url_launcher/url_launcher_linux/CHANGELOG.md index 27c18a66805b..0fc373f2ebb1 100644 --- a/packages/url_launcher/url_launcher_linux/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_linux/CHANGELOG.md @@ -1,8 +1,3 @@ -## 3.0.1 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 3.0.0 * Changes the major version since, due to a typo in `default_package` in diff --git a/packages/url_launcher/url_launcher_linux/example/lib/main.dart b/packages/url_launcher/url_launcher_linux/example/lib/main.dart index 0b985e78ac0d..a9a5d22dadd5 100644 --- a/packages/url_launcher/url_launcher_linux/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_linux/example/lib/main.dart @@ -9,12 +9,10 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -32,7 +30,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_linux/pubspec.yaml b/packages/url_launcher/url_launcher_linux/pubspec.yaml index c9472045e499..cb9a0be0aa41 100644 --- a/packages/url_launcher/url_launcher_linux/pubspec.yaml +++ b/packages/url_launcher/url_launcher_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_linux description: Linux implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.1 +version: 3.0.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_macos/CHANGELOG.md b/packages/url_launcher/url_launcher_macos/CHANGELOG.md index 2fa5e918eadd..082bc45fc2e8 100644 --- a/packages/url_launcher/url_launcher_macos/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_macos/CHANGELOG.md @@ -1,8 +1,3 @@ -## 3.0.1 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 3.0.0 * Changes the major version since, due to a typo in `default_package` in diff --git a/packages/url_launcher/url_launcher_macos/example/lib/main.dart b/packages/url_launcher/url_launcher_macos/example/lib/main.dart index 0b985e78ac0d..a9a5d22dadd5 100644 --- a/packages/url_launcher/url_launcher_macos/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_macos/example/lib/main.dart @@ -9,12 +9,10 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -32,7 +30,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_macos/pubspec.yaml b/packages/url_launcher/url_launcher_macos/pubspec.yaml index edda6b67cfb3..8b5183b1914d 100644 --- a/packages/url_launcher/url_launcher_macos/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_macos description: macOS implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.1 +version: 3.0.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md index b53a92cee707..a434b7af70c2 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.0.10 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 2.0.9 - Fixes invalid routes when opening a `Link` in a new tab diff --git a/packages/url_launcher/url_launcher_web/example/lib/main.dart b/packages/url_launcher/url_launcher_web/example/lib/main.dart index 87422953de6a..341913a18490 100644 --- a/packages/url_launcher/url_launcher_web/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_web/example/lib/main.dart @@ -5,16 +5,13 @@ import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// App for testing class MyApp extends StatefulWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { 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 eccd9aef80e3..4498e74ea9ce 100644 --- a/packages/url_launcher/url_launcher_web/lib/src/link.dart +++ b/packages/url_launcher/url_launcher_web/lib/src/link.dart @@ -31,7 +31,7 @@ HtmlViewFactory get linkViewFactory => LinkViewController._viewFactory; /// It uses a platform view to render an anchor element in the DOM. class WebLinkDelegate extends StatefulWidget { /// Creates a delegate for the given [link]. - const WebLinkDelegate(this.link, {Key? key}) : super(key: key); + const WebLinkDelegate(this.link); /// Information about the link built by the app. final LinkInfo link; diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index cd8ed2d269c8..c45c062255ad 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_web description: Web platform implementation of url_launcher repository: https://github.com/flutter/plugins/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.0.10 +version: 2.0.9 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_windows/CHANGELOG.md b/packages/url_launcher/url_launcher_windows/CHANGELOG.md index 3ff14fd2f18a..e02f5a2288e1 100644 --- a/packages/url_launcher/url_launcher_windows/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_windows/CHANGELOG.md @@ -1,8 +1,3 @@ -## 3.0.1 - -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. - ## 3.0.0 * Changes the major version since, due to a typo in `default_package` in diff --git a/packages/url_launcher/url_launcher_windows/example/lib/main.dart b/packages/url_launcher/url_launcher_windows/example/lib/main.dart index 0b985e78ac0d..a9a5d22dadd5 100644 --- a/packages/url_launcher/url_launcher_windows/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_windows/example/lib/main.dart @@ -9,12 +9,10 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - @override Widget build(BuildContext context) { return MaterialApp( @@ -32,7 +30,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - State createState() => _MyHomePageState(); + _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml index c3f224e26adf..95f17ad9e921 100644 --- a/packages/url_launcher/url_launcher_windows/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_windows description: Windows implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.1 +version: 3.0.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index ede890162f86..af01c64fd554 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.4.1 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.4.0 diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index a6ec51015c33..39cd415dbb79 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -708,14 +708,14 @@ class _VideoAppLifeCycleObserver extends Object with WidgetsBindingObserver { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - const VideoPlayer(this.controller, {Key? key}) : super(key: key); + const VideoPlayer(this.controller); /// The [VideoPlayerController] responsible for the video being rendered in /// this widget. final VideoPlayerController controller; @override - State createState() => _VideoPlayerState(); + _VideoPlayerState createState() => _VideoPlayerState(); } class _VideoPlayerState extends State { @@ -883,11 +883,10 @@ class VideoProgressIndicator extends StatefulWidget { /// to `top: 5.0`. const VideoProgressIndicator( this.controller, { - Key? key, this.colors = const VideoProgressColors(), required this.allowScrubbing, this.padding = const EdgeInsets.only(top: 5.0), - }) : super(key: key); + }); /// The [VideoPlayerController] that actually associates a video with this /// widget. @@ -911,7 +910,7 @@ class VideoProgressIndicator extends StatefulWidget { final EdgeInsets padding; @override - State createState() => _VideoProgressIndicatorState(); + _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); } class _VideoProgressIndicatorState extends State { @@ -985,8 +984,8 @@ class _VideoProgressIndicatorState extends State { ); if (widget.allowScrubbing) { return _VideoScrubber( - controller: controller, child: paddedProgressIndicator, + controller: controller, ); } else { return paddedProgressIndicator; diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index b0ca56429271..0d654a4330a7 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/plugins/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.4.1 +version: 2.4.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 08acba895eba..16dd52ca6da0 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.3.3 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.3.2 diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart index 5bce3117d0d6..498dbffc9e84 100644 --- a/packages/video_player/video_player_android/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart @@ -351,14 +351,14 @@ class MiniController extends ValueNotifier { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - const VideoPlayer(this.controller, {Key? key}) : super(key: key); + const VideoPlayer(this.controller); /// The [MiniController] responsible for the video being rendered in /// this widget. final MiniController controller; @override - State createState() => _VideoPlayerState(); + _VideoPlayerState createState() => _VideoPlayerState(); } class _VideoPlayerState extends State { @@ -450,14 +450,14 @@ class _VideoScrubberState extends State<_VideoScrubber> { class VideoProgressIndicator extends StatefulWidget { /// Construct an instance that displays the play/buffering status of the video /// controlled by [controller]. - const VideoProgressIndicator(this.controller, {Key? key}) : super(key: key); + const VideoProgressIndicator(this.controller); /// The [MiniController] that actually associates a video with this /// widget. final MiniController controller; @override - State createState() => _VideoProgressIndicatorState(); + _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); } class _VideoProgressIndicatorState extends State { @@ -527,11 +527,11 @@ class _VideoProgressIndicatorState extends State { ); } return _VideoScrubber( - controller: controller, child: Padding( padding: const EdgeInsets.only(top: 5.0), child: progressIndicator, ), + controller: controller, ); } } diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index bc69fd41369a..aa288ed71eac 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.3 +version: 2.3.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 6ab5398f7013..d77c36c915b6 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.3.4 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.3.3 diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart index 5bce3117d0d6..498dbffc9e84 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart @@ -351,14 +351,14 @@ class MiniController extends ValueNotifier { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - const VideoPlayer(this.controller, {Key? key}) : super(key: key); + const VideoPlayer(this.controller); /// The [MiniController] responsible for the video being rendered in /// this widget. final MiniController controller; @override - State createState() => _VideoPlayerState(); + _VideoPlayerState createState() => _VideoPlayerState(); } class _VideoPlayerState extends State { @@ -450,14 +450,14 @@ class _VideoScrubberState extends State<_VideoScrubber> { class VideoProgressIndicator extends StatefulWidget { /// Construct an instance that displays the play/buffering status of the video /// controlled by [controller]. - const VideoProgressIndicator(this.controller, {Key? key}) : super(key: key); + const VideoProgressIndicator(this.controller); /// The [MiniController] that actually associates a video with this /// widget. final MiniController controller; @override - State createState() => _VideoProgressIndicatorState(); + _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); } class _VideoProgressIndicatorState extends State { @@ -527,11 +527,11 @@ class _VideoProgressIndicatorState extends State { ); } return _VideoScrubber( - controller: controller, child: Padding( padding: const EdgeInsets.only(top: 5.0), child: progressIndicator, ), + controller: controller, ); } } diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index 380d8343c024..b3cc69eca958 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.4 +version: 2.3.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 094ffda207c5..00788c4386fe 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.0.9 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.0.8 diff --git a/packages/video_player/video_player_web/example/lib/main.dart b/packages/video_player/video_player_web/example/lib/main.dart index 87422953de6a..341913a18490 100644 --- a/packages/video_player/video_player_web/example/lib/main.dart +++ b/packages/video_player/video_player_web/example/lib/main.dart @@ -5,16 +5,13 @@ import 'package:flutter/material.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } /// App for testing class MyApp extends StatefulWidget { - /// Default Constructor - const MyApp({Key? key}) : super(key: key); - @override - State createState() => _MyAppState(); + _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 7af0dc46dde8..064517e1f264 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/plugins/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.0.9 +version: 2.0.8 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index fa47a6cc3143..7a56f3f176d0 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,8 +1,6 @@ -## 3.0.3 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 3.0.2 @@ -13,7 +11,7 @@ * Removes a duplicate Android-specific integration test. * Fixes an integration test race condition. -* Fixes comments (accidentally mixed // with ///). +* Fixes comments (accidentially mixed // with ///). ## 3.0.0 diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index cc001f336849..ba321264ee1e 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -1306,8 +1306,7 @@ Future _runJavascriptReturningResult( class ResizableWebView extends StatefulWidget { const ResizableWebView( - {Key? key, required this.onResize, required this.onPageFinished}) - : super(key: key); + {required this.onResize, required this.onPageFinished}); final JavascriptMessageHandler onResize; final VoidCallback onPageFinished; diff --git a/packages/webview_flutter/webview_flutter/example/lib/main.dart b/packages/webview_flutter/webview_flutter/example/lib/main.dart index 3d8731127970..51080be01df0 100644 --- a/packages/webview_flutter/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter/example/lib/main.dart @@ -71,12 +71,12 @@ const String kTransparentBackgroundPage = ''' '''; class WebViewExample extends StatefulWidget { - const WebViewExample({Key? key, this.cookieManager}) : super(key: key); + const WebViewExample({this.cookieManager}); final CookieManager? cookieManager; @override - State createState() => _WebViewExampleState(); + _WebViewExampleState createState() => _WebViewExampleState(); } class _WebViewExampleState extends State { @@ -190,9 +190,8 @@ enum MenuOptions { } class SampleMenu extends StatelessWidget { - SampleMenu(this.controller, CookieManager? cookieManager, {Key? key}) - : cookieManager = cookieManager ?? CookieManager(), - super(key: key); + SampleMenu(this.controller, CookieManager? cookieManager) + : cookieManager = cookieManager ?? CookieManager(); final Future controller; late final CookieManager cookieManager; @@ -251,8 +250,8 @@ class SampleMenu extends StatelessWidget { itemBuilder: (BuildContext context) => >[ PopupMenuItem( value: MenuOptions.showUserAgent, - enabled: controller.hasData, child: const Text('Show user agent'), + enabled: controller.hasData, ), const PopupMenuItem( value: MenuOptions.listCookies, @@ -444,9 +443,8 @@ class SampleMenu extends StatelessWidget { } class NavigationControls extends StatelessWidget { - const NavigationControls(this._webViewControllerFuture, {Key? key}) - : assert(_webViewControllerFuture != null), - super(key: key); + const NavigationControls(this._webViewControllerFuture) + : assert(_webViewControllerFuture != null); final Future _webViewControllerFuture; diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index a48f6f912c2d..10350984ce9a 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 3.0.3 +version: 3.0.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 4a451442f6cc..edaa0883713f 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.8.7 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.8.6 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 4c06fa6b3c18..51e09912da23 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 @@ -1446,10 +1446,9 @@ Future _runJavaScriptReturningResult( class ResizableWebView extends StatefulWidget { const ResizableWebView({ - Key? key, required this.onResize, required this.onPageFinished, - }) : super(key: key); + }); final JavascriptMessageHandler onResize; final VoidCallback onPageFinished; 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 349a64916e8b..5d19ca71ac84 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -247,8 +247,8 @@ class _SampleMenu extends StatelessWidget { itemBuilder: (BuildContext context) => >[ PopupMenuItem<_MenuOptions>( value: _MenuOptions.showUserAgent, - enabled: controller.hasData, child: const Text('Show user agent'), + enabled: controller.hasData, ), const PopupMenuItem<_MenuOptions>( value: _MenuOptions.listCookies, diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart index 56745314d92b..91ea66376904 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart @@ -250,7 +250,7 @@ class WebView extends StatefulWidget { final Color? backgroundColor; @override - State createState() => _WebViewState(); + _WebViewState createState() => _WebViewState(); } class _WebViewState extends State { diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart index f1b130c7e365..28d169c9cb94 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart @@ -15,7 +15,6 @@ import 'src/android_webview.dart' as android_webview; class WebViewAndroidWidget extends StatefulWidget { /// Constructs a [WebViewAndroidWidget]. const WebViewAndroidWidget({ - Key? key, required this.creationParams, required this.useHybridComposition, required this.callbacksHandler, @@ -25,7 +24,7 @@ class WebViewAndroidWidget extends StatefulWidget { @visibleForTesting this.flutterAssetManager = const android_webview.FlutterAssetManager(), @visibleForTesting this.webStorage, - }) : super(key: key); + }); /// Initial parameters used to setup the WebView. final CreationParams creationParams; diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 407887f2ba95..9a7c48a4dcd8 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/plugins/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: 2.8.7 +version: 2.8.6 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index b7254e1a0a7a..9f7ebe368941 100644 --- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -1,9 +1,7 @@ -## 0.1.0+2 +## NEXT * Removes unnecessary imports. * Fixes unit tests to run on latest `master` version of Flutter. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 0.1.0+1 diff --git a/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart index ffd3367d33f4..8cd74f660f58 100644 --- a/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart @@ -46,7 +46,7 @@ class WebView extends StatefulWidget { final String? initialUrl; @override - State createState() => _WebViewState(); + _WebViewState createState() => _WebViewState(); } class _WebViewState extends State { diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml index 35a7b74a764c..bd154387097e 100644 --- a/packages/webview_flutter/webview_flutter_web/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_web description: A Flutter plugin that provides a WebView widget on web. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 0.1.0+2 +version: 0.1.0+1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 6db769b0d922..f042dd081475 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,8 +1,6 @@ -## 2.7.4 +## NEXT * Removes unnecessary imports. -* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors - lint warnings. ## 2.7.3 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 aa376f8358e9..ceff62e3d5e8 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 @@ -1181,8 +1181,7 @@ Future _getUserAgent(WebViewController controller) async { class ResizableWebView extends StatefulWidget { const ResizableWebView( - {Key? key, required this.onResize, required this.onPageFinished}) - : super(key: key); + {required this.onResize, required this.onPageFinished}); final JavascriptMessageHandler onResize; final VoidCallback onPageFinished; 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 7b30923e1e54..e8b86d9c5773 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -232,8 +232,8 @@ class _SampleMenu extends StatelessWidget { itemBuilder: (BuildContext context) => >[ PopupMenuItem<_MenuOptions>( value: _MenuOptions.showUserAgent, - enabled: controller.hasData, child: const Text('Show user agent'), + enabled: controller.hasData, ), const PopupMenuItem<_MenuOptions>( value: _MenuOptions.listCookies, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart index c44c4e743669..4d479f943d62 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart @@ -240,7 +240,7 @@ class WebView extends StatefulWidget { final Color? backgroundColor; @override - State createState() => _WebViewState(); + _WebViewState createState() => _WebViewState(); } class _WebViewState extends State { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 8c37112d7a24..012cd221599b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -18,14 +18,13 @@ import 'web_kit/web_kit.dart'; class WebKitWebViewWidget extends StatefulWidget { /// Constructs a [WebKitWebViewWidget]. const WebKitWebViewWidget({ - Key? key, required this.creationParams, required this.callbacksHandler, required this.javascriptChannelRegistry, required this.onBuildWidget, this.configuration, @visibleForTesting this.webViewProxy = const WebViewWidgetProxy(), - }) : super(key: key); + }); /// The initial parameters used to setup the WebView. final CreationParams creationParams; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 365e64720d4f..f4e72b8f14eb 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/plugins/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: 2.7.4 +version: 2.7.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 5faa7c8201d9..9ed2a9278653 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +- Fixes changelog validation when reverting to a `NEXT` state. + ## 0.8.5 - Updates `test` to inculde the Dart unit tests of examples, if any. @@ -322,7 +326,7 @@ and `firebase-test-lab`. ## v.0.0.36+2 -- Default to showing podspec lint warnings. +- Default to showing podspec lint warnings ## v.0.0.36+1 diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart index fb768c19b524..c0e67764360e 100644 --- a/script/tool/lib/src/version_check_command.dart +++ b/script/tool/lib/src/version_check_command.dart @@ -39,8 +39,11 @@ enum _CurrentVersionState { /// The version is unchanged. unchanged, - /// The version has changed, and the transition is valid. - validChange, + /// The version has increased, and the transition is valid. + validIncrease, + + /// The version has decrease, and the transition is a valid revert. + validRevert, /// The version has changed, and the transition is invalid. invalidChange, @@ -218,7 +221,8 @@ class VersionCheckCommand extends PackageLoopingCommand { case _CurrentVersionState.unchanged: versionChanged = false; break; - case _CurrentVersionState.validChange: + case _CurrentVersionState.validIncrease: + case _CurrentVersionState.validRevert: versionChanged = true; break; case _CurrentVersionState.invalidChange: @@ -232,7 +236,7 @@ class VersionCheckCommand extends PackageLoopingCommand { } if (!(await _validateChangelogVersion(package, - pubspec: pubspec, pubspecVersionChanged: versionChanged))) { + pubspec: pubspec, pubspecVersionState: versionState))) { errors.add('CHANGELOG.md failed validation.'); } @@ -322,7 +326,7 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} '${getBoolArg(_againstPubFlag) ? 'on pub server' : 'at git base'}.'); logWarning( '${indentation}If this plugin is not new, something has gone wrong.'); - return _CurrentVersionState.validChange; // Assume new, thus valid. + return _CurrentVersionState.validIncrease; // Assume new, thus valid. } if (previousVersion == currentVersion) { @@ -340,7 +344,7 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} if (possibleVersionsFromNewVersion.containsKey(previousVersion)) { logWarning('${indentation}New version is lower than previous version. ' 'This is assumed to be a revert.'); - return _CurrentVersionState.validChange; + return _CurrentVersionState.validRevert; } } @@ -367,7 +371,7 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} return _CurrentVersionState.invalidChange; } - return _CurrentVersionState.validChange; + return _CurrentVersionState.validIncrease; } /// Checks whether or not [package]'s CHANGELOG's versioning is correct, @@ -378,7 +382,7 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} Future _validateChangelogVersion( RepositoryPackage package, { required Pubspec pubspec, - required bool pubspecVersionChanged, + required _CurrentVersionState pubspecVersionState, }) async { // This method isn't called unless `version` is non-null. final Version fromPubspec = pubspec.version!; @@ -405,8 +409,9 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} // changes that don't warrant publishing on their own. final bool hasNextSection = versionString == 'NEXT'; if (hasNextSection) { - // NEXT should not be present in a commit that changes the version. - if (pubspecVersionChanged) { + // NEXT should not be present in a commit that increases the version. + if (pubspecVersionState == _CurrentVersionState.validIncrease || + pubspecVersionState == _CurrentVersionState.invalidChange) { printError(badNextErrorMessage); return false; } diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index b0a8990e1300..5c38bd5f7033 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -319,14 +319,14 @@ String _pluginPlatformSection( return entry; } -typedef ErrorHandler = void Function(Error error); +typedef _ErrorHandler = void Function(Error error); /// Run the command [runner] with the given [args] and return /// what was printed. /// A custom [errorHandler] can be used to handle the runner error as desired without throwing. Future> runCapturingPrint( CommandRunner runner, List args, - {ErrorHandler? errorHandler}) async { + {_ErrorHandler? errorHandler}) async { final List prints = []; final ZoneSpecification spec = ZoneSpecification( print: (_, __, ___, String message) { diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart index aeacd77635df..5b8ed97e20c5 100644 --- a/script/tool/test/version_check_command_test.dart +++ b/script/tool/test/version_check_command_test.dart @@ -44,7 +44,7 @@ class MockProcessResult extends Mock implements io.ProcessResult {} void main() { const String indentation = ' '; - group('$VersionCheckCommand', () { + group('VersionCheckCommand', () { late FileSystem fileSystem; late MockPlatform mockPlatform; late Directory packagesDir; @@ -602,7 +602,7 @@ This is necessary because of X, Y, and Z ); }); - test('Fail if the version changes without replacing NEXT', () async { + test('fails if the version increases without replacing NEXT', () async { final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.1'); @@ -632,6 +632,33 @@ This is necessary because of X, Y, and Z ); }); + test('allows NEXT for a revert', () async { + final RepositoryPackage plugin = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); + + const String changelog = ''' +## NEXT +* Some changes that should be listed as part of 1.0.1. +## 1.0.0 +* Some other changes. +'''; + createFakeCHANGELOG(plugin, changelog); + createFakeCHANGELOG(plugin, changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.1'), + ]; + + final List output = await runCapturingPrint( + runner, ['version-check', '--base-sha=main']); + expect( + output, + containsAllInOrder([ + contains('New version is lower than previous version. ' + 'This is assumed to be a revert.'), + ]), + ); + }); + test( 'fails gracefully if the version headers are not found due to using the wrong style', () async { From 5e50710c4ad2da9ee227cd52580a465364296b8f Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 10 May 2022 14:43:28 -0400 Subject: [PATCH 289/844] Re-land: Enable lints `library_private_types_in_public_api`, `sort_child_properties_last` and `use_key_in_widget_constructors` (#5692) Re-lands https://github.com/flutter/plugins/pull/5428 This is a revert of flutter/plugins#5691 (the revert of the above) with the following changes: - Excludes the repo tooling changes that had to be added to the revert, since we want those - Fixes local_auth: - Updates code for the new analysis failure - Fixes the bad version merge that dropped the version change - Reverts a version change in `file_selector_platform_interface`, which didn't otherwise change --- analysis_options.yaml | 5 +- packages/camera/camera/CHANGELOG.md | 4 +- packages/camera/camera/README.md | 14 ++- packages/camera/camera/example/lib/main.dart | 60 +++++----- .../example/lib/readme_full_example.dart | 16 +-- .../camera/camera/example/test/main_test.dart | 2 +- .../camera/camera/lib/src/camera_preview.dart | 3 +- packages/camera/camera/pubspec.yaml | 2 +- packages/camera/camera_web/CHANGELOG.md | 5 + .../camera/camera_web/example/lib/main.dart | 5 +- packages/camera/camera_web/pubspec.yaml | 2 +- packages/camera/camera_windows/CHANGELOG.md | 4 +- .../camera_windows/example/lib/main.dart | 5 +- packages/camera/camera_windows/pubspec.yaml | 2 +- packages/espresso/CHANGELOG.md | 7 +- packages/espresso/example/lib/main.dart | 7 +- packages/espresso/pubspec.yaml | 2 +- .../file_selector/file_selector/CHANGELOG.md | 4 +- .../example/lib/get_directory_page.dart | 5 +- .../file_selector/example/lib/home_page.dart | 3 + .../file_selector/example/lib/main.dart | 16 ++- .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 5 +- .../example/lib/open_text_page.dart | 6 +- .../example/lib/save_text_page.dart | 3 + .../file_selector/file_selector/pubspec.yaml | 2 +- .../file_selector_macos/CHANGELOG.md | 4 +- .../example/lib/get_directory_page.dart | 5 +- .../example/lib/home_page.dart | 3 + .../file_selector_macos/example/lib/main.dart | 16 ++- .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 5 +- .../example/lib/open_text_page.dart | 6 +- .../example/lib/save_text_page.dart | 5 +- .../file_selector_macos/pubspec.yaml | 2 +- .../file_selector_web/CHANGELOG.md | 5 + .../file_selector_web/example/lib/main.dart | 7 +- .../file_selector_web/pubspec.yaml | 2 +- .../file_selector_windows/CHANGELOG.md | 4 +- .../example/lib/get_directory_page.dart | 5 +- .../example/lib/home_page.dart | 3 + .../example/lib/main.dart | 16 ++- .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 5 +- .../example/lib/open_text_page.dart | 6 +- .../example/lib/save_text_page.dart | 5 +- .../file_selector_windows/pubspec.yaml | 2 +- .../CHANGELOG.md | 4 +- ...flutter_plugin_android_lifecycle_test.dart | 2 +- .../example/lib/main.dart | 8 +- .../pubspec.yaml | 2 +- .../google_maps_flutter/CHANGELOG.md | 6 +- .../example/lib/animate_camera.dart | 6 +- .../example/lib/lite_mode.dart | 3 +- .../google_maps_flutter/example/lib/main.dart | 8 +- .../example/lib/map_click.dart | 3 +- .../example/lib/map_coordinates.dart | 3 +- .../example/lib/map_ui.dart | 5 +- .../example/lib/marker_icons.dart | 5 +- .../example/lib/move_camera.dart | 5 +- .../example/lib/padding.dart | 5 +- .../google_maps_flutter/example/lib/page.dart | 3 +- .../example/lib/place_circle.dart | 18 +-- .../example/lib/place_marker.dart | 31 ++--- .../example/lib/place_polygon.dart | 24 ++-- .../example/lib/place_polyline.dart | 26 ++-- .../example/lib/scrolling_map.dart | 5 +- .../example/lib/snapshot.dart | 5 +- .../example/lib/tile_overlay.dart | 11 +- .../lib/src/controller.dart | 2 + .../google_maps_flutter/pubspec.yaml | 2 +- .../google_maps_flutter_web/CHANGELOG.md | 4 +- .../example/lib/main.dart | 2 +- .../google_maps_flutter_web/pubspec.yaml | 2 +- .../google_sign_in/CHANGELOG.md | 5 + .../google_sign_in/example/lib/main.dart | 8 +- .../google_sign_in/lib/widgets.dart | 4 +- .../google_sign_in/pubspec.yaml | 2 +- .../google_sign_in_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 8 +- .../google_sign_in_android/pubspec.yaml | 2 +- .../google_sign_in_ios/CHANGELOG.md | 5 + .../google_sign_in_ios/example/lib/main.dart | 8 +- .../google_sign_in_ios/pubspec.yaml | 2 +- .../google_sign_in_web/CHANGELOG.md | 5 + .../google_sign_in_web/example/lib/main.dart | 7 +- .../google_sign_in_web/pubspec.yaml | 2 +- .../image_picker/image_picker/CHANGELOG.md | 5 + .../image_picker/example/lib/main.dart | 39 +++--- .../image_picker/image_picker/pubspec.yaml | 2 +- .../image_picker_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 39 +++--- .../image_picker_android/pubspec.yaml | 2 +- .../image_picker_for_web/CHANGELOG.md | 5 + .../example/lib/main.dart | 7 +- .../image_picker_for_web/pubspec.yaml | 2 +- .../image_picker_ios/CHANGELOG.md | 4 +- .../image_picker_ios/example/lib/main.dart | 39 +++--- .../image_picker_ios/pubspec.yaml | 2 +- .../image_picker_windows/CHANGELOG.md | 4 +- .../example/lib/main.dart | 31 ++--- .../image_picker_windows/pubspec.yaml | 2 +- .../in_app_purchase/CHANGELOG.md | 4 +- .../in_app_purchase/example/lib/main.dart | 111 +++++++++--------- .../in_app_purchase/pubspec.yaml | 2 +- .../in_app_purchase_android/CHANGELOG.md | 4 +- .../example/lib/main.dart | 6 +- .../in_app_purchase_android/pubspec.yaml | 2 +- .../in_app_purchase_storekit/CHANGELOG.md | 4 +- .../example/lib/main.dart | 8 +- .../in_app_purchase_storekit/pubspec.yaml | 2 +- packages/ios_platform_images/CHANGELOG.md | 4 +- .../ios_platform_images/example/lib/main.dart | 7 +- .../example/test/widget_test.dart | 2 +- packages/ios_platform_images/pubspec.yaml | 2 +- packages/local_auth/local_auth/CHANGELOG.md | 5 + .../local_auth/example/lib/main.dart | 14 ++- .../example/lib/readme_excerpts.dart | 4 +- packages/local_auth/local_auth/pubspec.yaml | 2 +- .../local_auth_android/CHANGELOG.md | 4 +- .../local_auth_android/example/lib/main.dart | 14 ++- .../local_auth_android/pubspec.yaml | 2 +- .../local_auth/local_auth_ios/CHANGELOG.md | 4 +- .../local_auth_ios/example/lib/main.dart | 14 ++- .../local_auth/local_auth_ios/pubspec.yaml | 2 +- .../path_provider/path_provider/CHANGELOG.md | 4 +- .../path_provider/example/lib/main.dart | 44 +++---- .../path_provider/path_provider/pubspec.yaml | 2 +- .../path_provider_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 16 +-- .../path_provider_android/pubspec.yaml | 2 +- .../path_provider_ios/CHANGELOG.md | 5 + .../path_provider_ios/example/lib/main.dart | 14 ++- .../path_provider_ios/pubspec.yaml | 2 +- .../path_provider_linux/CHANGELOG.md | 5 + .../path_provider_linux/example/lib/main.dart | 7 +- .../path_provider_linux/pubspec.yaml | 2 +- .../path_provider_macos/CHANGELOG.md | 5 + .../path_provider_macos/example/lib/main.dart | 6 +- .../path_provider_macos/pubspec.yaml | 2 +- .../path_provider_windows/CHANGELOG.md | 5 + .../example/lib/main.dart | 6 +- .../path_provider_windows/pubspec.yaml | 2 +- .../quick_actions/quick_actions/CHANGELOG.md | 4 +- .../quick_actions/example/lib/main.dart | 6 +- .../quick_actions/quick_actions/pubspec.yaml | 2 +- .../quick_actions_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 6 +- .../quick_actions_android/pubspec.yaml | 2 +- .../quick_actions_ios/CHANGELOG.md | 4 + .../quick_actions_ios/example/lib/main.dart | 6 +- .../quick_actions_ios/pubspec.yaml | 2 +- .../shared_preferences/CHANGELOG.md | 4 +- .../shared_preferences/example/lib/main.dart | 4 +- .../shared_preferences/pubspec.yaml | 2 +- .../shared_preferences_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 4 +- .../shared_preferences_android/pubspec.yaml | 2 +- .../shared_preferences_ios/CHANGELOG.md | 5 + .../example/lib/main.dart | 4 +- .../shared_preferences_ios/pubspec.yaml | 2 +- .../shared_preferences_linux/CHANGELOG.md | 4 +- .../example/lib/main.dart | 4 +- .../shared_preferences_linux/pubspec.yaml | 2 +- .../shared_preferences_macos/CHANGELOG.md | 4 +- .../example/lib/main.dart | 4 +- .../shared_preferences_macos/pubspec.yaml | 2 +- .../shared_preferences_web/CHANGELOG.md | 5 + .../example/lib/main.dart | 7 +- .../shared_preferences_web/pubspec.yaml | 2 +- .../shared_preferences_windows/CHANGELOG.md | 5 + .../example/lib/main.dart | 4 +- .../shared_preferences_windows/pubspec.yaml | 2 +- .../url_launcher/url_launcher/CHANGELOG.md | 4 +- .../url_launcher/example/lib/main.dart | 6 +- .../url_launcher/lib/src/link.dart | 2 +- .../url_launcher/url_launcher/pubspec.yaml | 2 +- .../url_launcher_android/CHANGELOG.md | 5 + .../example/lib/main.dart | 6 +- .../url_launcher_android/pubspec.yaml | 2 +- .../url_launcher_ios/CHANGELOG.md | 5 + .../url_launcher_ios/example/lib/main.dart | 6 +- .../url_launcher_ios/pubspec.yaml | 2 +- .../url_launcher_linux/CHANGELOG.md | 5 + .../url_launcher_linux/example/lib/main.dart | 6 +- .../url_launcher_linux/pubspec.yaml | 2 +- .../url_launcher_macos/CHANGELOG.md | 5 + .../url_launcher_macos/example/lib/main.dart | 6 +- .../url_launcher_macos/pubspec.yaml | 2 +- .../url_launcher_web/CHANGELOG.md | 5 + .../url_launcher_web/example/lib/main.dart | 7 +- .../url_launcher_web/lib/src/link.dart | 2 +- .../url_launcher_web/pubspec.yaml | 2 +- .../url_launcher_windows/CHANGELOG.md | 5 + .../example/lib/main.dart | 6 +- .../url_launcher_windows/pubspec.yaml | 2 +- .../video_player/video_player/CHANGELOG.md | 4 +- .../video_player/lib/video_player.dart | 11 +- .../video_player/video_player/pubspec.yaml | 2 +- .../video_player_android/CHANGELOG.md | 4 +- .../example/lib/mini_controller.dart | 10 +- .../video_player_android/pubspec.yaml | 2 +- .../video_player_avfoundation/CHANGELOG.md | 4 +- .../example/lib/mini_controller.dart | 10 +- .../video_player_avfoundation/pubspec.yaml | 2 +- .../video_player_web/CHANGELOG.md | 4 +- .../video_player_web/example/lib/main.dart | 7 +- .../video_player_web/pubspec.yaml | 2 +- .../webview_flutter/CHANGELOG.md | 6 +- .../webview_flutter_test.dart | 3 +- .../webview_flutter/example/lib/main.dart | 16 +-- .../webview_flutter/pubspec.yaml | 2 +- .../webview_flutter_android/CHANGELOG.md | 4 +- .../webview_flutter_test.dart | 3 +- .../example/lib/main.dart | 2 +- .../example/lib/web_view.dart | 2 +- .../lib/webview_android_widget.dart | 3 +- .../webview_flutter_android/pubspec.yaml | 2 +- .../webview_flutter_web/CHANGELOG.md | 4 +- .../example/lib/web_view.dart | 2 +- .../webview_flutter_web/pubspec.yaml | 2 +- .../webview_flutter_wkwebview/CHANGELOG.md | 4 +- .../webview_flutter_test.dart | 3 +- .../example/lib/main.dart | 2 +- .../example/lib/web_view.dart | 2 +- .../lib/src/web_kit_webview_widget.dart | 3 +- .../webview_flutter_wkwebview/pubspec.yaml | 2 +- script/tool/test/util.dart | 4 +- 228 files changed, 938 insertions(+), 525 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index f6177cd9939a..ba5e0a9c4ced 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -134,6 +134,7 @@ linter: - leading_newlines_in_multiline_strings - library_names - library_prefixes + - library_private_types_in_public_api # - lines_longer_than_80_chars # not required by flutter style - list_remove_unrelated_type # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181 @@ -197,7 +198,7 @@ linter: - recursive_getters # - sized_box_for_whitespace # not yet tested - slash_for_doc_comments - # - sort_child_properties_last # not yet tested + - sort_child_properties_last - sort_constructors_first - sort_unnamed_constructors_first - test_types_in_equals @@ -229,7 +230,7 @@ linter: - use_full_hex_values_for_flutter_colors # - use_function_type_syntax_for_parameters # not yet tested - use_is_even_rather_than_modulo - # - use_key_in_widget_constructors # not yet tested + - use_key_in_widget_constructors - use_late_for_private_fields_and_variables - use_raw_strings - use_rethrow_when_possible diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 4d7e9bbeb218..bf0ccf86a82e 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.9.4+22 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.9.4+21 diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index 97b16d20f48a..0bcaeaeb3b7c 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -89,18 +89,22 @@ Here is a small example flutter app displaying a full screen camera preview. import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; -late List cameras; +late List _cameras; Future main() async { WidgetsFlutterBinding.ensureInitialized(); - cameras = await availableCameras(); - runApp(CameraApp()); + _cameras = await availableCameras(); + runApp(const CameraApp()); } +/// CameraApp is the Main Application. class CameraApp extends StatefulWidget { + /// Default Constructor + const CameraApp({Key? key}) : super(key: key); + @override - _CameraAppState createState() => _CameraAppState(); + State createState() => _CameraAppState(); } class _CameraAppState extends State { @@ -109,7 +113,7 @@ class _CameraAppState extends State { @override void initState() { super.initState(); - controller = CameraController(cameras[0], ResolutionPreset.max); + controller = CameraController(_cameras[0], ResolutionPreset.max); controller.initialize().then((_) { if (!mounted) { return; diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index aabbe249313d..a645326f2803 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: public_member_api_docs - import 'dart:async'; import 'dart:io'; @@ -13,9 +11,13 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:video_player/video_player.dart'; +/// Camera example home widget. class CameraExampleHome extends StatefulWidget { + /// Default Constructor + const CameraExampleHome({Key? key}) : super(key: key); + @override - _CameraExampleHomeState createState() { + State createState() { return _CameraExampleHomeState(); } } @@ -34,7 +36,7 @@ IconData getCameraLensIcon(CameraLensDirection direction) { } } -void logError(String code, String? message) { +void _logError(String code, String? message) { if (message != null) { print('Error: $code\nError Message: $message'); } else { @@ -134,12 +136,6 @@ class _CameraExampleHomeState extends State children: [ Expanded( child: Container( - child: Padding( - padding: const EdgeInsets.all(1.0), - child: Center( - child: _cameraPreviewWidget(), - ), - ), decoration: BoxDecoration( color: Colors.black, border: Border.all( @@ -150,6 +146,12 @@ class _CameraExampleHomeState extends State width: 3.0, ), ), + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Center( + child: _cameraPreviewWidget(), + ), + ), ), ), _captureControlRowWidget(), @@ -233,6 +235,8 @@ class _CameraExampleHomeState extends State Container() else SizedBox( + width: 64.0, + height: 64.0, child: (localVideoController == null) ? ( // The captured image on the web contains a network-accessible URL @@ -243,6 +247,8 @@ class _CameraExampleHomeState extends State ? Image.network(imageFile!.path) : Image.file(File(imageFile!.path))) : Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.pink)), child: Center( child: AspectRatio( aspectRatio: @@ -251,11 +257,7 @@ class _CameraExampleHomeState extends State : 1.0, child: VideoPlayer(localVideoController)), ), - decoration: BoxDecoration( - border: Border.all(color: Colors.pink)), ), - width: 64.0, - height: 64.0, ), ], ), @@ -394,7 +396,6 @@ class _CameraExampleHomeState extends State mainAxisSize: MainAxisSize.max, children: [ TextButton( - child: const Text('AUTO'), style: styleAuto, onPressed: controller != null ? () => @@ -406,21 +407,22 @@ class _CameraExampleHomeState extends State showInSnackBar('Resetting exposure point'); } }, + child: const Text('AUTO'), ), TextButton( - child: const Text('LOCKED'), style: styleLocked, onPressed: controller != null ? () => onSetExposureModeButtonPressed(ExposureMode.locked) : null, + child: const Text('LOCKED'), ), TextButton( - child: const Text('RESET OFFSET'), style: styleLocked, onPressed: controller != null ? () => controller!.setExposureOffset(0.0) : null, + child: const Text('RESET OFFSET'), ), ], ), @@ -479,7 +481,6 @@ class _CameraExampleHomeState extends State mainAxisSize: MainAxisSize.max, children: [ TextButton( - child: const Text('AUTO'), style: styleAuto, onPressed: controller != null ? () => onSetFocusModeButtonPressed(FocusMode.auto) @@ -490,13 +491,14 @@ class _CameraExampleHomeState extends State } showInSnackBar('Resetting focus point'); }, + child: const Text('AUTO'), ), TextButton( - child: const Text('LOCKED'), style: styleLocked, onPressed: controller != null ? () => onSetFocusModeButtonPressed(FocusMode.locked) : null, + child: const Text('LOCKED'), ), ], ), @@ -582,13 +584,13 @@ class _CameraExampleHomeState extends State onNewCameraSelected(description); }; - if (cameras.isEmpty) { + if (_cameras.isEmpty) { _ambiguate(SchedulerBinding.instance)?.addPostFrameCallback((_) async { showInSnackBar('No camera found.'); }); return const Text('None'); } else { - for (final CameraDescription cameraDescription in cameras) { + for (final CameraDescription cameraDescription in _cameras) { toggles.add( SizedBox( width: 90.0, @@ -1014,31 +1016,35 @@ class _CameraExampleHomeState extends State } void _showCameraException(CameraException e) { - logError(e.code, e.description); + _logError(e.code, e.description); showInSnackBar('Error: ${e.code}\n${e.description}'); } } +/// CameraApp is the Main Application. class CameraApp extends StatelessWidget { + /// Default Constructor + const CameraApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { - return MaterialApp( + return const MaterialApp( home: CameraExampleHome(), ); } } -List cameras = []; +List _cameras = []; Future main() async { // Fetch the available cameras before initializing the app. try { WidgetsFlutterBinding.ensureInitialized(); - cameras = await availableCameras(); + _cameras = await availableCameras(); } on CameraException catch (e) { - logError(e.code, e.description); + _logError(e.code, e.description); } - runApp(CameraApp()); + runApp(const CameraApp()); } /// This allows a value of type T or T? to be treated as a value of type T?. diff --git a/packages/camera/camera/example/lib/readme_full_example.dart b/packages/camera/camera/example/lib/readme_full_example.dart index b25e637a0c95..a310fd9daeb0 100644 --- a/packages/camera/camera/example/lib/readme_full_example.dart +++ b/packages/camera/camera/example/lib/readme_full_example.dart @@ -2,24 +2,26 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: public_member_api_docs - // #docregion FullAppExample import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; -late List cameras; +late List _cameras; Future main() async { WidgetsFlutterBinding.ensureInitialized(); - cameras = await availableCameras(); - runApp(CameraApp()); + _cameras = await availableCameras(); + runApp(const CameraApp()); } +/// CameraApp is the Main Application. class CameraApp extends StatefulWidget { + /// Default Constructor + const CameraApp({Key? key}) : super(key: key); + @override - _CameraAppState createState() => _CameraAppState(); + State createState() => _CameraAppState(); } class _CameraAppState extends State { @@ -28,7 +30,7 @@ class _CameraAppState extends State { @override void initState() { super.initState(); - controller = CameraController(cameras[0], ResolutionPreset.max); + controller = CameraController(_cameras[0], ResolutionPreset.max); controller.initialize().then((_) { if (!mounted) { return; diff --git a/packages/camera/camera/example/test/main_test.dart b/packages/camera/camera/example/test/main_test.dart index 9a5fcdf2d5ea..6e909efcfc62 100644 --- a/packages/camera/camera/example/test/main_test.dart +++ b/packages/camera/camera/example/test/main_test.dart @@ -9,7 +9,7 @@ import 'package:flutter_test/flutter_test.dart'; void main() { testWidgets('Test snackbar', (WidgetTester tester) async { WidgetsFlutterBinding.ensureInitialized(); - await tester.pumpWidget(CameraApp()); + await tester.pumpWidget(const CameraApp()); await tester.pumpAndSettle(); expect(find.byType(SnackBar), findsOneWidget); }); diff --git a/packages/camera/camera/lib/src/camera_preview.dart b/packages/camera/camera/lib/src/camera_preview.dart index a9b3f2143b49..94ffca649fa6 100644 --- a/packages/camera/camera/lib/src/camera_preview.dart +++ b/packages/camera/camera/lib/src/camera_preview.dart @@ -10,7 +10,8 @@ import 'package:flutter/services.dart'; /// A widget showing a live camera preview. class CameraPreview extends StatelessWidget { /// Creates a preview widget for the given camera controller. - const CameraPreview(this.controller, {this.child}); + const CameraPreview(this.controller, {Key? key, this.child}) + : super(key: key); /// The controller for the camera that the preview is shown for. final CameraController controller; diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index f62777044617..fde6663844c2 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+21 +version: 0.9.4+22 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 852e4a03fd43..7a24e12e5029 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.2.1+5 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.2.1+4 * Migrates from `ui.hash*` to `Object.hash*`. diff --git a/packages/camera/camera_web/example/lib/main.dart b/packages/camera/camera_web/example/lib/main.dart index ab04ce2ca2c7..670891fa5009 100644 --- a/packages/camera/camera_web/example/lib/main.dart +++ b/packages/camera/camera_web/example/lib/main.dart @@ -4,10 +4,13 @@ import 'package:flutter/material.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); /// App for testing class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const Directionality( diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 2d1a4508eb73..8bef974190b2 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.2.1+4 +version: 0.2.1+5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index b1383dc54993..0f3bf441b05a 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.1.0+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.1.0 diff --git a/packages/camera/camera_windows/example/lib/main.dart b/packages/camera/camera_windows/example/lib/main.dart index b73e00cac52b..5758b0f1e397 100644 --- a/packages/camera/camera_windows/example/lib/main.dart +++ b/packages/camera/camera_windows/example/lib/main.dart @@ -9,11 +9,14 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// Example app for Camera Windows plugin. class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override State createState() => _MyAppState(); } diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index 1081c3dfc01f..fe655b04e8c8 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -1,8 +1,8 @@ name: camera_windows description: A Flutter plugin for getting information about and controlling the camera on Windows. -version: 0.1.0 repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 +version: 0.1.0+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/espresso/CHANGELOG.md b/packages/espresso/CHANGELOG.md index eb1f267ca1d3..dad0a912e174 100644 --- a/packages/espresso/CHANGELOG.md +++ b/packages/espresso/CHANGELOG.md @@ -1,8 +1,13 @@ +## 0.2.0+2 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.2.0+1 * Adds OS version support information to README. * Updates `androidx.test.ext:junit` and `androidx.test.ext:truth` for - compatibilty with updated Flutter template. + compatibility with updated Flutter template. ## 0.2.0 diff --git a/packages/espresso/example/lib/main.dart b/packages/espresso/example/lib/main.dart index 14f94abb28c8..741cd9cf9fa2 100644 --- a/packages/espresso/example/lib/main.dart +++ b/packages/espresso/example/lib/main.dart @@ -4,10 +4,13 @@ import 'package:flutter/material.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); /// Example app for Espresso plugin. class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + // This widget is the root of your application. @override Widget build(BuildContext context) { @@ -45,7 +48,7 @@ class _MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State<_MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<_MyHomePage> { diff --git a/packages/espresso/pubspec.yaml b/packages/espresso/pubspec.yaml index 7737fc46d4b6..ac0199cf045f 100644 --- a/packages/espresso/pubspec.yaml +++ b/packages/espresso/pubspec.yaml @@ -3,7 +3,7 @@ description: Java classes for testing Flutter apps using Espresso. Allows driving Flutter widgets from a native Espresso test. repository: https://github.com/flutter/plugins/tree/main/packages/espresso issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+espresso%22 -version: 0.2.0+1 +version: 0.2.0+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index c0821fed7446..17baf9f12469 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,7 +1,9 @@ -## NEXT +## 0.8.4+2 * Removes unnecessary imports. * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.8.4+1 diff --git a/packages/file_selector/file_selector/example/lib/get_directory_page.dart b/packages/file_selector/file_selector/example/lib/get_directory_page.dart index b3ed9d0eeaca..506134d66bd8 100644 --- a/packages/file_selector/file_selector/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector/example/lib/get_directory_page.dart @@ -7,6 +7,9 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of getDirectoryPath class GetDirectoryPage extends StatelessWidget { + /// Default Constructor + const GetDirectoryPage({Key? key}) : super(key: key); + Future _getDirectoryPath(BuildContext context) async { const String confirmButtonText = 'Choose'; final String? directoryPath = await getDirectoryPath( @@ -50,7 +53,7 @@ class GetDirectoryPage extends StatelessWidget { /// Widget that displays a text file in a dialog class TextDisplay extends StatelessWidget { /// Default Constructor - const TextDisplay(this.directoryPath); + const TextDisplay(this.directoryPath, {Key? key}) : super(key: key); /// Directory path final String directoryPath; diff --git a/packages/file_selector/file_selector/example/lib/home_page.dart b/packages/file_selector/file_selector/example/lib/home_page.dart index c598cbdf2611..9a0733b56b93 100644 --- a/packages/file_selector/file_selector/example/lib/home_page.dart +++ b/packages/file_selector/file_selector/example/lib/home_page.dart @@ -6,6 +6,9 @@ import 'package:flutter/material.dart'; /// Home Page of the application class HomePage extends StatelessWidget { + /// Default Constructor + const HomePage({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 14ce3f593f33..34f5857ab0bc 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -11,11 +11,14 @@ import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// MyApp is the Main Application class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -24,13 +27,14 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: HomePage(), + home: const HomePage(), routes: { - '/open/image': (BuildContext context) => OpenImagePage(), - '/open/images': (BuildContext context) => OpenMultipleImagesPage(), - '/open/text': (BuildContext context) => OpenTextPage(), + '/open/image': (BuildContext context) => const OpenImagePage(), + '/open/images': (BuildContext context) => + const OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => const OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), - '/directory': (BuildContext context) => GetDirectoryPage(), + '/directory': (BuildContext context) => const GetDirectoryPage(), }, ); } diff --git a/packages/file_selector/file_selector/example/lib/open_image_page.dart b/packages/file_selector/file_selector/example/lib/open_image_page.dart index 0abdba6eb72d..e520ffb402aa 100644 --- a/packages/file_selector/file_selector/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_image_page.dart @@ -10,6 +10,9 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of openFiles class OpenImagePage extends StatelessWidget { + /// Default Constructor + const OpenImagePage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'images', @@ -59,7 +62,8 @@ class OpenImagePage extends StatelessWidget { /// Widget that displays a text file in a dialog class ImageDisplay extends StatelessWidget { /// Default Constructor - const ImageDisplay(this.fileName, this.filePath); + const ImageDisplay(this.fileName, this.filePath, {Key? key}) + : super(key: key); /// Image's name final String fileName; diff --git a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart index 9a1101214aaa..e2d21c7f04d1 100644 --- a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart @@ -10,6 +10,9 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of openFiles class OpenMultipleImagesPage extends StatelessWidget { + /// Default Constructor + const OpenMultipleImagesPage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', @@ -61,7 +64,7 @@ class OpenMultipleImagesPage extends StatelessWidget { /// Widget that displays a text file in a dialog class MultipleImagesDisplay extends StatelessWidget { /// Default Constructor - const MultipleImagesDisplay(this.files); + const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); /// The files containing the images final List files; diff --git a/packages/file_selector/file_selector/example/lib/open_text_page.dart b/packages/file_selector/file_selector/example/lib/open_text_page.dart index 652e8596cf81..be48a434282b 100644 --- a/packages/file_selector/file_selector/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_text_page.dart @@ -7,6 +7,9 @@ import 'package:flutter/material.dart'; /// Screen that shows an example of openFile class OpenTextPage extends StatelessWidget { + /// Default Constructor + const OpenTextPage({Key? key}) : super(key: key); + Future _openTextFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'text', @@ -55,7 +58,8 @@ class OpenTextPage extends StatelessWidget { /// Widget that displays a text file in a dialog class TextDisplay extends StatelessWidget { /// Default Constructor - const TextDisplay(this.fileName, this.fileContent); + const TextDisplay(this.fileName, this.fileContent, {Key? key}) + : super(key: key); /// File's name final String fileName; diff --git a/packages/file_selector/file_selector/example/lib/save_text_page.dart b/packages/file_selector/file_selector/example/lib/save_text_page.dart index 108ef89b0248..b0317844ec36 100644 --- a/packages/file_selector/file_selector/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/save_text_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Page for showing an example of saving with file_selector class SaveTextPage extends StatelessWidget { + /// Default Constructor + SaveTextPage({Key? key}) : super(key: key); + final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index c05900f650cf..7026f7f32287 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.4+1 +version: 0.8.4+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_macos/CHANGELOG.md b/packages/file_selector/file_selector_macos/CHANGELOG.md index b46a174bd323..19724a513a9e 100644 --- a/packages/file_selector/file_selector_macos/CHANGELOG.md +++ b/packages/file_selector/file_selector_macos/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.8.2+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.8.2 diff --git a/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart b/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart index 0e55df8ce622..a27ab2b40aba 100644 --- a/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a directory using `getDirectoryPath`, /// then displays the selected directory in a dialog. class GetDirectoryPage extends StatelessWidget { + /// Default Constructor + const GetDirectoryPage({Key? key}) : super(key: key); + Future _getDirectoryPath(BuildContext context) async { const String confirmButtonText = 'Choose'; final String? directoryPath = @@ -52,7 +55,7 @@ class GetDirectoryPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Creates a `TextDisplay`. - const TextDisplay(this.directoryPath); + const TextDisplay(this.directoryPath, {Key? key}) : super(key: key); /// The path selected in the dialog. final String directoryPath; diff --git a/packages/file_selector/file_selector_macos/example/lib/home_page.dart b/packages/file_selector/file_selector_macos/example/lib/home_page.dart index 958680be0e3b..4d6ca7e6e6e3 100644 --- a/packages/file_selector/file_selector_macos/example/lib/home_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/home_page.dart @@ -6,6 +6,9 @@ import 'package:flutter/material.dart'; /// Home Page of the application. class HomePage extends StatelessWidget { + /// Default Constructor + const HomePage({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( diff --git a/packages/file_selector/file_selector_macos/example/lib/main.dart b/packages/file_selector/file_selector_macos/example/lib/main.dart index a49ebac1aea5..cbe268e1c7ab 100644 --- a/packages/file_selector/file_selector_macos/example/lib/main.dart +++ b/packages/file_selector/file_selector_macos/example/lib/main.dart @@ -11,11 +11,14 @@ import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// MyApp is the Main Application. class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -24,13 +27,14 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: HomePage(), + home: const HomePage(), routes: { - '/open/image': (BuildContext context) => OpenImagePage(), - '/open/images': (BuildContext context) => OpenMultipleImagesPage(), - '/open/text': (BuildContext context) => OpenTextPage(), + '/open/image': (BuildContext context) => const OpenImagePage(), + '/open/images': (BuildContext context) => + const OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => const OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), - '/directory': (BuildContext context) => GetDirectoryPage(), + '/directory': (BuildContext context) => const GetDirectoryPage(), }, ); } diff --git a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart index aaf083603e72..1a05343b27ab 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart @@ -11,6 +11,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select an image file using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenImagePage extends StatelessWidget { + /// Default Constructor + const OpenImagePage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'images', @@ -59,7 +62,8 @@ class OpenImagePage extends StatelessWidget { /// Widget that displays an image in a dialog. class ImageDisplay extends StatelessWidget { /// Default Constructor. - const ImageDisplay(this.fileName, this.filePath); + const ImageDisplay(this.fileName, this.filePath, {Key? key}) + : super(key: key); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart index a030b8b4b10b..9c3c8e369f5b 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart @@ -11,6 +11,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select multiple image files using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenMultipleImagesPage extends StatelessWidget { + /// Default Constructor + const OpenMultipleImagesPage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', @@ -63,7 +66,7 @@ class OpenMultipleImagesPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class MultipleImagesDisplay extends StatelessWidget { /// Default Constructor. - const MultipleImagesDisplay(this.files); + const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); /// The files containing the images. final List files; diff --git a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart index fa281a0020d5..9adde400b5e1 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a text file using `openFile`, then /// displays its contents in a dialog. class OpenTextPage extends StatelessWidget { + /// Default Constructor + const OpenTextPage({Key? key}) : super(key: key); + Future _openTextFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'text', @@ -56,7 +59,8 @@ class OpenTextPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Default Constructor. - const TextDisplay(this.fileName, this.fileContent); + const TextDisplay(this.fileName, this.fileContent, {Key? key}) + : super(key: key); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart index 3989c62b7442..a44a387c019b 100644 --- a/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart @@ -9,6 +9,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a save location using `getSavePath`, /// then writes text to a file at that location. class SaveTextPage extends StatelessWidget { + /// Default Constructor + SaveTextPage({Key? key}) : super(key: key); + final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); @@ -67,8 +70,8 @@ class SaveTextPage extends StatelessWidget { primary: Colors.blue, onPrimary: Colors.white, ), - child: const Text('Press to save a text file'), onPressed: _saveFile, + child: const Text('Press to save a text file'), ), ], ), diff --git a/packages/file_selector/file_selector_macos/pubspec.yaml b/packages/file_selector/file_selector_macos/pubspec.yaml index 071d261c4bf8..41077c1c04e6 100644 --- a/packages/file_selector/file_selector_macos/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_macos description: macOS implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.2 +version: 0.8.2+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md index 5927239ef9e3..ce9d5590f9a9 100644 --- a/packages/file_selector/file_selector_web/CHANGELOG.md +++ b/packages/file_selector/file_selector_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.1+4 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.8.1+3 * Minor code cleanup for new analysis rules. diff --git a/packages/file_selector/file_selector_web/example/lib/main.dart b/packages/file_selector/file_selector_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/file_selector/file_selector_web/example/lib/main.dart +++ b/packages/file_selector/file_selector_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index 74d0412b440f..2e12b6d175a3 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/plugins/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.8.1+3 +version: 0.8.1+4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_windows/CHANGELOG.md b/packages/file_selector/file_selector_windows/CHANGELOG.md index ae3cd13342b1..c242717c3267 100644 --- a/packages/file_selector/file_selector_windows/CHANGELOG.md +++ b/packages/file_selector/file_selector_windows/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.8.2+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.8.2 diff --git a/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart b/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart index b282b9030d67..8fc1a9001465 100644 --- a/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a directory using `getDirectoryPath`, /// then displays the selected directory in a dialog. class GetDirectoryPage extends StatelessWidget { + /// Default Constructor + const GetDirectoryPage({Key? key}) : super(key: key); + Future _getDirectoryPath(BuildContext context) async { const String confirmButtonText = 'Choose'; final String? directoryPath = @@ -52,7 +55,7 @@ class GetDirectoryPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Creates a `TextDisplay`. - const TextDisplay(this.directoryPath); + const TextDisplay(this.directoryPath, {Key? key}) : super(key: key); /// The path selected in the dialog. final String directoryPath; diff --git a/packages/file_selector/file_selector_windows/example/lib/home_page.dart b/packages/file_selector/file_selector_windows/example/lib/home_page.dart index 958680be0e3b..4d6ca7e6e6e3 100644 --- a/packages/file_selector/file_selector_windows/example/lib/home_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/home_page.dart @@ -6,6 +6,9 @@ import 'package:flutter/material.dart'; /// Home Page of the application. class HomePage extends StatelessWidget { + /// Default Constructor + const HomePage({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( diff --git a/packages/file_selector/file_selector_windows/example/lib/main.dart b/packages/file_selector/file_selector_windows/example/lib/main.dart index a49ebac1aea5..cbe268e1c7ab 100644 --- a/packages/file_selector/file_selector_windows/example/lib/main.dart +++ b/packages/file_selector/file_selector_windows/example/lib/main.dart @@ -11,11 +11,14 @@ import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// MyApp is the Main Application. class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -24,13 +27,14 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: HomePage(), + home: const HomePage(), routes: { - '/open/image': (BuildContext context) => OpenImagePage(), - '/open/images': (BuildContext context) => OpenMultipleImagesPage(), - '/open/text': (BuildContext context) => OpenTextPage(), + '/open/image': (BuildContext context) => const OpenImagePage(), + '/open/images': (BuildContext context) => + const OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => const OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), - '/directory': (BuildContext context) => GetDirectoryPage(), + '/directory': (BuildContext context) => const GetDirectoryPage(), }, ); } diff --git a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart index aaf083603e72..1a05343b27ab 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart @@ -11,6 +11,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select an image file using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenImagePage extends StatelessWidget { + /// Default Constructor + const OpenImagePage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'images', @@ -59,7 +62,8 @@ class OpenImagePage extends StatelessWidget { /// Widget that displays an image in a dialog. class ImageDisplay extends StatelessWidget { /// Default Constructor. - const ImageDisplay(this.fileName, this.filePath); + const ImageDisplay(this.fileName, this.filePath, {Key? key}) + : super(key: key); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart index a030b8b4b10b..9c3c8e369f5b 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart @@ -11,6 +11,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select multiple image files using /// `openFiles`, then displays the selected images in a gallery dialog. class OpenMultipleImagesPage extends StatelessWidget { + /// Default Constructor + const OpenMultipleImagesPage({Key? key}) : super(key: key); + Future _openImageFile(BuildContext context) async { final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', @@ -63,7 +66,7 @@ class OpenMultipleImagesPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class MultipleImagesDisplay extends StatelessWidget { /// Default Constructor. - const MultipleImagesDisplay(this.files); + const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); /// The files containing the images. final List files; diff --git a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart index fa281a0020d5..9adde400b5e1 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart @@ -8,6 +8,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a text file using `openFile`, then /// displays its contents in a dialog. class OpenTextPage extends StatelessWidget { + /// Default Constructor + const OpenTextPage({Key? key}) : super(key: key); + Future _openTextFile(BuildContext context) async { final XTypeGroup typeGroup = XTypeGroup( label: 'text', @@ -56,7 +59,8 @@ class OpenTextPage extends StatelessWidget { /// Widget that displays a text file in a dialog. class TextDisplay extends StatelessWidget { /// Default Constructor. - const TextDisplay(this.fileName, this.fileContent); + const TextDisplay(this.fileName, this.fileContent, {Key? key}) + : super(key: key); /// The name of the selected file. final String fileName; diff --git a/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart index b87a51c3877d..961e0fb2fbc3 100644 --- a/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart @@ -9,6 +9,9 @@ import 'package:flutter/material.dart'; /// Screen that allows the user to select a save location using `getSavePath`, /// then writes text to a file at that location. class SaveTextPage extends StatelessWidget { + /// Default Constructor + SaveTextPage({Key? key}) : super(key: key); + final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); @@ -67,8 +70,8 @@ class SaveTextPage extends StatelessWidget { primary: Colors.blue, onPrimary: Colors.white, ), - child: const Text('Press to save a text file'), onPressed: _saveFile, + child: const Text('Press to save a text file'), ), ], ), diff --git a/packages/file_selector/file_selector_windows/pubspec.yaml b/packages/file_selector/file_selector_windows/pubspec.yaml index 7b035e974293..152b63ef4a3f 100644 --- a/packages/file_selector/file_selector_windows/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_windows description: Windows implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.2 +version: 0.8.2+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md index 8fdfc39f3bf9..a0a3f67bed46 100644 --- a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md +++ b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.0.6 * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.5 diff --git a/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart b/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart index 1d329a02f93b..1198c6f01806 100644 --- a/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart +++ b/packages/flutter_plugin_android_lifecycle/example/integration_test/flutter_plugin_android_lifecycle_test.dart @@ -10,6 +10,6 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('loads', (WidgetTester tester) async { - await tester.pumpWidget(MyApp()); + await tester.pumpWidget(const MyApp()); }); } diff --git a/packages/flutter_plugin_android_lifecycle/example/lib/main.dart b/packages/flutter_plugin_android_lifecycle/example/lib/main.dart index 3ef6794dfad2..c465b3b687f2 100644 --- a/packages/flutter_plugin_android_lifecycle/example/lib/main.dart +++ b/packages/flutter_plugin_android_lifecycle/example/lib/main.dart @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: public_member_api_docs - import 'package:flutter/material.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); +/// MyApp is the Main Application. class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( diff --git a/packages/flutter_plugin_android_lifecycle/pubspec.yaml b/packages/flutter_plugin_android_lifecycle/pubspec.yaml index b359cc55c351..c109d0936589 100644 --- a/packages/flutter_plugin_android_lifecycle/pubspec.yaml +++ b/packages/flutter_plugin_android_lifecycle/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_plugin_android_lifecycle description: Flutter plugin for accessing an Android Lifecycle within other plugins. repository: https://github.com/flutter/plugins/tree/main/packages/flutter_plugin_android_lifecycle issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_plugin_android_lifecycle%22 -version: 2.0.5 +version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index b0662b89d9cc..d1f2f926e2ab 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.1.5 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.1.4 @@ -223,7 +225,7 @@ GoogleMapController is now uniformly driven by implementing `DefaultLifecycleObs ## 0.5.26+1 -* Removes a errorneously added method from the GoogleMapController.h header file. +* Removes an erroneously added method from the GoogleMapController.h header file. ## 0.5.26 diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart index f8072eee7c85..09df2b98b146 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart @@ -10,8 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class AnimateCameraPage extends GoogleMapExampleAppPage { - const AnimateCameraPage() - : super(const Icon(Icons.map), 'Camera control, animated'); + const AnimateCameraPage({Key? key}) + : super(const Icon(Icons.map), 'Camera control, animated', key: key); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class AnimateCameraPage extends GoogleMapExampleAppPage { } class AnimateCamera extends StatefulWidget { - const AnimateCamera(); + const AnimateCamera({Key? key}) : super(key: key); @override State createState() => AnimateCameraState(); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart index b1b58fdc91bf..fd95cf864a7c 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/lite_mode.dart @@ -12,7 +12,8 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class LiteModePage extends GoogleMapExampleAppPage { - const LiteModePage() : super(const Icon(Icons.map), 'Lite mode'); + const LiteModePage({Key? key}) + : super(const Icon(Icons.map), 'Lite mode', key: key); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart index f4d420a72f7c..8932705bc6d5 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// ignore_for_file: public_member_api_docs - import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -43,7 +41,11 @@ final List _allPages = [ const TileOverlayPage(), ]; +/// MapsDemo is the Main Application. class MapsDemo extends StatelessWidget { + /// Default Constructor + const MapsDemo({Key? key}) : super(key: key); + void _pushPage(BuildContext context, GoogleMapExampleAppPage page) { Navigator.of(context).push(MaterialPageRoute( builder: (_) => Scaffold( @@ -72,5 +74,5 @@ void main() { if (defaultTargetPlatform == TargetPlatform.android) { AndroidGoogleMapsFlutter.useAndroidViewSurface = true; } - runApp(MaterialApp(home: MapsDemo())); + runApp(const MaterialApp(home: MapsDemo())); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart index bbe2372dce9a..9c96f25d5fa7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart @@ -12,7 +12,8 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class MapClickPage extends GoogleMapExampleAppPage { - const MapClickPage() : super(const Icon(Icons.mouse), 'Map click'); + const MapClickPage({Key? key}) + : super(const Icon(Icons.mouse), 'Map click', key: key); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart index 8e4853c040ed..1179acd46ffa 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart @@ -12,7 +12,8 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class MapCoordinatesPage extends GoogleMapExampleAppPage { - const MapCoordinatesPage() : super(const Icon(Icons.map), 'Map coordinates'); + const MapCoordinatesPage({Key? key}) + : super(const Icon(Icons.map), 'Map coordinates', key: key); @override Widget build(BuildContext context) { 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 48ef1f570e02..fbfeda56a968 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 @@ -16,7 +16,8 @@ final LatLngBounds sydneyBounds = LatLngBounds( ); class MapUiPage extends GoogleMapExampleAppPage { - const MapUiPage() : super(const Icon(Icons.map), 'User interface'); + const MapUiPage({Key? key}) + : super(const Icon(Icons.map), 'User interface', key: key); @override Widget build(BuildContext context) { @@ -25,7 +26,7 @@ class MapUiPage extends GoogleMapExampleAppPage { } class MapUiBody extends StatefulWidget { - const MapUiBody(); + const MapUiBody({Key? key}) : super(key: key); @override State createState() => MapUiBodyState(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart index 95ace9d7c482..58d266c95d1d 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/marker_icons.dart @@ -11,7 +11,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class MarkerIconsPage extends GoogleMapExampleAppPage { - const MarkerIconsPage() : super(const Icon(Icons.image), 'Marker icons'); + const MarkerIconsPage({Key? key}) + : super(const Icon(Icons.image), 'Marker icons', key: key); @override Widget build(BuildContext context) { @@ -20,7 +21,7 @@ class MarkerIconsPage extends GoogleMapExampleAppPage { } class MarkerIconsBody extends StatefulWidget { - const MarkerIconsBody(); + const MarkerIconsBody({Key? key}) : super(key: key); @override State createState() => MarkerIconsBodyState(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart index 33da90f32c1b..a6bae3009f0b 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart @@ -10,7 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class MoveCameraPage extends GoogleMapExampleAppPage { - const MoveCameraPage() : super(const Icon(Icons.map), 'Camera control'); + const MoveCameraPage({Key? key}) + : super(const Icon(Icons.map), 'Camera control', key: key); @override Widget build(BuildContext context) { @@ -19,7 +20,7 @@ class MoveCameraPage extends GoogleMapExampleAppPage { } class MoveCamera extends StatefulWidget { - const MoveCamera(); + const MoveCamera({Key? key}) : super(key: key); @override State createState() => MoveCameraState(); } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart index 77091909e970..a4bfa88d559f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart @@ -9,7 +9,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PaddingPage extends GoogleMapExampleAppPage { - const PaddingPage() : super(const Icon(Icons.map), 'Add padding to the map'); + const PaddingPage({Key? key}) + : super(const Icon(Icons.map), 'Add padding to the map', key: key); @override Widget build(BuildContext context) { @@ -18,7 +19,7 @@ class PaddingPage extends GoogleMapExampleAppPage { } class MarkerIconsBody extends StatefulWidget { - const MarkerIconsBody(); + const MarkerIconsBody({Key? key}) : super(key: key); @override State createState() => MarkerIconsBodyState(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart index fb6eb3260f6d..eb01ab07a6f3 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/page.dart @@ -7,7 +7,8 @@ import 'package:flutter/material.dart'; abstract class GoogleMapExampleAppPage extends StatelessWidget { - const GoogleMapExampleAppPage(this.leading, this.title); + const GoogleMapExampleAppPage(this.leading, this.title, {Key? key}) + : super(key: key); final Widget leading; final String title; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart index c6f1509af69f..ef5033cfa1ee 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart @@ -10,8 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlaceCirclePage extends GoogleMapExampleAppPage { - const PlaceCirclePage() - : super(const Icon(Icons.linear_scale), 'Place circle'); + const PlaceCirclePage({Key? key}) + : super(const Icon(Icons.linear_scale), 'Place circle', key: key); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class PlaceCirclePage extends GoogleMapExampleAppPage { } class PlaceCircleBody extends StatefulWidget { - const PlaceCircleBody(); + const PlaceCircleBody({Key? key}) : super(key: key); @override State createState() => PlaceCircleBodyState(); @@ -170,42 +170,42 @@ class PlaceCircleBodyState extends State { Column( children: [ TextButton( - child: const Text('add'), onPressed: _add, + child: const Text('add'), ), TextButton( - child: const Text('remove'), onPressed: (selectedId == null) ? null : () => _remove(selectedId), + child: const Text('remove'), ), TextButton( - child: const Text('toggle visible'), onPressed: (selectedId == null) ? null : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), ), ], ), Column( children: [ TextButton( - child: const Text('change stroke width'), onPressed: (selectedId == null) ? null : () => _changeStrokeWidth(selectedId), + child: const Text('change stroke width'), ), TextButton( - child: const Text('change stroke color'), onPressed: (selectedId == null) ? null : () => _changeStrokeColor(selectedId), + child: const Text('change stroke color'), ), TextButton( - child: const Text('change fill color'), onPressed: (selectedId == null) ? null : () => _changeFillColor(selectedId), + child: const Text('change fill color'), ), ], ) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart index 4291cac6841e..1238d61547b8 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart @@ -15,7 +15,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlaceMarkerPage extends GoogleMapExampleAppPage { - const PlaceMarkerPage() : super(const Icon(Icons.place), 'Place marker'); + const PlaceMarkerPage({Key? key}) + : super(const Icon(Icons.place), 'Place marker', key: key); @override Widget build(BuildContext context) { @@ -24,7 +25,7 @@ class PlaceMarkerPage extends GoogleMapExampleAppPage { } class PlaceMarkerBody extends StatefulWidget { - const PlaceMarkerBody(); + const PlaceMarkerBody({Key? key}) : super(key: key); @override State createState() => PlaceMarkerBodyState(); @@ -308,13 +309,13 @@ class PlaceMarkerBodyState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ TextButton( - child: const Text('Add'), onPressed: _add, + child: const Text('Add'), ), TextButton( - child: const Text('Remove'), onPressed: selectedId == null ? null : () => _remove(selectedId), + child: const Text('Remove'), ), ], ), @@ -322,62 +323,61 @@ class PlaceMarkerBodyState extends State { alignment: WrapAlignment.spaceEvenly, children: [ TextButton( - child: const Text('change info'), onPressed: selectedId == null ? null : () => _changeInfo(selectedId), + child: const Text('change info'), ), TextButton( - child: const Text('change info anchor'), onPressed: selectedId == null ? null : () => _changeInfoAnchor(selectedId), + child: const Text('change info anchor'), ), TextButton( - child: const Text('change alpha'), onPressed: selectedId == null ? null : () => _changeAlpha(selectedId), + child: const Text('change alpha'), ), TextButton( - child: const Text('change anchor'), onPressed: selectedId == null ? null : () => _changeAnchor(selectedId), + child: const Text('change anchor'), ), TextButton( - child: const Text('toggle draggable'), onPressed: selectedId == null ? null : () => _toggleDraggable(selectedId), + child: const Text('toggle draggable'), ), TextButton( - child: const Text('toggle flat'), onPressed: selectedId == null ? null : () => _toggleFlat(selectedId), + child: const Text('toggle flat'), ), TextButton( - child: const Text('change position'), onPressed: selectedId == null ? null : () => _changePosition(selectedId), + child: const Text('change position'), ), TextButton( - child: const Text('change rotation'), onPressed: selectedId == null ? null : () => _changeRotation(selectedId), + child: const Text('change rotation'), ), TextButton( - child: const Text('toggle visible'), onPressed: selectedId == null ? null : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), ), TextButton( - child: const Text('change zIndex'), onPressed: selectedId == null ? null : () => _changeZIndex(selectedId), + child: const Text('change zIndex'), ), TextButton( - child: const Text('set marker icon'), onPressed: selectedId == null ? null : () { @@ -387,6 +387,7 @@ class PlaceMarkerBodyState extends State { }, ); }, + child: const Text('set marker icon'), ), ], ), diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart index 8d4fa3e07c36..f1932141b8ab 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart @@ -10,8 +10,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlacePolygonPage extends GoogleMapExampleAppPage { - const PlacePolygonPage() - : super(const Icon(Icons.linear_scale), 'Place polygon'); + const PlacePolygonPage({Key? key}) + : super(const Icon(Icons.linear_scale), 'Place polygon', key: key); @override Widget build(BuildContext context) { @@ -20,7 +20,7 @@ class PlacePolygonPage extends GoogleMapExampleAppPage { } class PlacePolygonBody extends StatefulWidget { - const PlacePolygonBody(); + const PlacePolygonBody({Key? key}) : super(key: key); @override State createState() => PlacePolygonBodyState(); @@ -196,64 +196,64 @@ class PlacePolygonBodyState extends State { Column( children: [ TextButton( - child: const Text('add'), onPressed: _add, + child: const Text('add'), ), TextButton( - child: const Text('remove'), onPressed: (selectedId == null) ? null : () => _remove(selectedId), + child: const Text('remove'), ), TextButton( - child: const Text('toggle visible'), onPressed: (selectedId == null) ? null : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), ), TextButton( - child: const Text('toggle geodesic'), onPressed: (selectedId == null) ? null : () => _toggleGeodesic(selectedId), + child: const Text('toggle geodesic'), ), ], ), Column( children: [ TextButton( - child: const Text('add holes'), onPressed: (selectedId == null) ? null : ((polygons[selectedId]!.holes.isNotEmpty) ? null : () => _addHoles(selectedId)), + child: const Text('add holes'), ), TextButton( - child: const Text('remove holes'), onPressed: (selectedId == null) ? null : ((polygons[selectedId]!.holes.isEmpty) ? null : () => _removeHoles(selectedId)), + child: const Text('remove holes'), ), TextButton( - child: const Text('change stroke width'), onPressed: (selectedId == null) ? null : () => _changeWidth(selectedId), + child: const Text('change stroke width'), ), TextButton( - child: const Text('change stroke color'), onPressed: (selectedId == null) ? null : () => _changeStrokeColor(selectedId), + child: const Text('change stroke color'), ), TextButton( - child: const Text('change fill color'), onPressed: (selectedId == null) ? null : () => _changeFillColor(selectedId), + child: const Text('change fill color'), ), ], ) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart index 434920d293be..b3a637ce7d15 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart @@ -11,8 +11,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class PlacePolylinePage extends GoogleMapExampleAppPage { - const PlacePolylinePage() - : super(const Icon(Icons.linear_scale), 'Place polyline'); + const PlacePolylinePage({Key? key}) + : super(const Icon(Icons.linear_scale), 'Place polyline', key: key); @override Widget build(BuildContext context) { @@ -21,7 +21,7 @@ class PlacePolylinePage extends GoogleMapExampleAppPage { } class PlacePolylineBody extends StatefulWidget { - const PlacePolylineBody(); + const PlacePolylineBody({Key? key}) : super(key: key); @override State createState() => PlacePolylineBodyState(); @@ -234,66 +234,66 @@ class PlacePolylineBodyState extends State { Column( children: [ TextButton( - child: const Text('add'), onPressed: _add, + child: const Text('add'), ), TextButton( - child: const Text('remove'), onPressed: (selectedId == null) ? null : () => _remove(selectedId), + child: const Text('remove'), ), TextButton( - child: const Text('toggle visible'), onPressed: (selectedId == null) ? null : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), ), TextButton( - child: const Text('toggle geodesic'), onPressed: (selectedId == null) ? null : () => _toggleGeodesic(selectedId), + child: const Text('toggle geodesic'), ), ], ), Column( children: [ TextButton( - child: const Text('change width'), onPressed: (selectedId == null) ? null : () => _changeWidth(selectedId), + child: const Text('change width'), ), TextButton( - child: const Text('change color'), onPressed: (selectedId == null) ? null : () => _changeColor(selectedId), + child: const Text('change color'), ), TextButton( - child: const Text('change start cap [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changeStartCap(selectedId), + child: const Text('change start cap [Android only]'), ), TextButton( - child: const Text('change end cap [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changeEndCap(selectedId), + child: const Text('change end cap [Android only]'), ), TextButton( - child: const Text('change joint type [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changeJointType(selectedId), + child: const Text('change joint type [Android only]'), ), TextButton( - child: const Text('change pattern [Android only]'), onPressed: isIOS || (selectedId == null) ? null : () => _changePattern(selectedId), + child: const Text('change pattern [Android only]'), ), ], ) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart index 04769315e685..ca9d3962ddd7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart @@ -14,7 +14,8 @@ import 'page.dart'; const LatLng _center = LatLng(32.080664, 34.9563837); class ScrollingMapPage extends GoogleMapExampleAppPage { - const ScrollingMapPage() : super(const Icon(Icons.map), 'Scrolling map'); + const ScrollingMapPage({Key? key}) + : super(const Icon(Icons.map), 'Scrolling map', key: key); @override Widget build(BuildContext context) { @@ -23,7 +24,7 @@ class ScrollingMapPage extends GoogleMapExampleAppPage { } class ScrollingMapBody extends StatelessWidget { - const ScrollingMapBody(); + const ScrollingMapBody({Key? key}) : super(key: key); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart index 9afc28869490..849a9f469938 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart @@ -15,8 +15,9 @@ const CameraPosition _kInitialPosition = CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); class SnapshotPage extends GoogleMapExampleAppPage { - const SnapshotPage() - : super(const Icon(Icons.camera_alt), 'Take a snapshot of the map'); + const SnapshotPage({Key? key}) + : super(const Icon(Icons.camera_alt), 'Take a snapshot of the map', + key: key); @override Widget build(BuildContext context) { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart index dc7f7d1d6f33..81dfc2815866 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart @@ -13,7 +13,8 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'page.dart'; class TileOverlayPage extends GoogleMapExampleAppPage { - const TileOverlayPage() : super(const Icon(Icons.map), 'Tile overlay'); + const TileOverlayPage({Key? key}) + : super(const Icon(Icons.map), 'Tile overlay', key: key); @override Widget build(BuildContext context) { @@ -22,7 +23,7 @@ class TileOverlayPage extends GoogleMapExampleAppPage { } class TileOverlayBody extends StatefulWidget { - const TileOverlayBody(); + const TileOverlayBody({Key? key}) : super(key: key); @override State createState() => TileOverlayBodyState(); @@ -90,16 +91,16 @@ class TileOverlayBodyState extends State { ), ), TextButton( - child: const Text('Add tile overlay'), onPressed: _addTileOverlay, + child: const Text('Add tile overlay'), ), TextButton( - child: const Text('Remove tile overlay'), onPressed: _removeTileOverlay, + child: const Text('Remove tile overlay'), ), TextButton( - child: const Text('Clear tile cache'), onPressed: _clearTileCache, + child: const Text('Clear tile cache'), ), ], ); 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 088589e4a2ce..dfc6e579d560 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 @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: library_private_types_in_public_api + part of google_maps_flutter; /// Controller for a single GoogleMap instance running on the host platform. diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index c10f9c679a17..a294dd09981f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/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.1.4 +version: 2.1.5 environment: sdk: ">=2.14.0 <3.0.0" 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 48908b984b0f..f2fe971f4591 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,8 @@ -## NEXT +## 0.3.2+2 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.3.2+1 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 10415204570c..d1ba571b5bd0 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 @@ -11,7 +11,7 @@ void main() { /// App for testing class MyApp extends StatefulWidget { @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { 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 2780175d29e2..271d87d21092 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/plugins/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.3.2+1 +version: 0.3.2+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index caab46de7c5b..5b47536cd2e2 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,3 +1,8 @@ +## 5.3.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 5.3.0 * Moves Android and iOS implementations to federated packages. diff --git a/packages/google_sign_in/google_sign_in/example/lib/main.dart b/packages/google_sign_in/google_sign_in/example/lib/main.dart index 9840a1e0a9f6..4c27543f5b18 100644 --- a/packages/google_sign_in/google_sign_in/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in/example/lib/main.dart @@ -22,7 +22,7 @@ GoogleSignIn _googleSignIn = GoogleSignIn( void main() { runApp( - MaterialApp( + const MaterialApp( title: 'Google Sign In', home: SignInDemo(), ), @@ -30,6 +30,8 @@ void main() { } class SignInDemo extends StatefulWidget { + const SignInDemo({Key? key}) : super(key: key); + @override State createState() => SignInDemoState(); } @@ -125,8 +127,8 @@ class SignInDemoState extends State { const Text('Signed in successfully.'), Text(_contactText), ElevatedButton( - child: const Text('SIGN OUT'), onPressed: _handleSignOut, + child: const Text('SIGN OUT'), ), ElevatedButton( child: const Text('REFRESH'), @@ -140,8 +142,8 @@ class SignInDemoState extends State { children: [ const Text('You are not currently signed in.'), ElevatedButton( - child: const Text('SIGN IN'), onPressed: _handleSignIn, + child: const Text('SIGN IN'), ), ], ); diff --git a/packages/google_sign_in/google_sign_in/lib/widgets.dart b/packages/google_sign_in/google_sign_in/lib/widgets.dart index 61f89133fba4..f7ae5f9a6e5f 100644 --- a/packages/google_sign_in/google_sign_in/lib/widgets.dart +++ b/packages/google_sign_in/google_sign_in/lib/widgets.dart @@ -22,11 +22,13 @@ class GoogleUserCircleAvatar extends StatelessWidget { /// in place of a profile photo, or a default profile photo if the user's /// identity does not specify a `displayName`. const GoogleUserCircleAvatar({ + Key? key, required this.identity, this.placeholderPhotoUrl, this.foregroundColor, this.backgroundColor, - }) : assert(identity != null); + }) : assert(identity != null), + super(key: key); /// A regular expression that matches against the "size directive" path /// segment of Google profile image URLs. diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 760706f2e7bc..e58b27af08b7 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/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 and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.3.0 +version: 5.3.1 environment: 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 3ffa6b5b7d6b..90069d05f442 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 5.2.7 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 5.2.6 * Switches to an internal method channel, rather than the default. diff --git a/packages/google_sign_in/google_sign_in_android/example/lib/main.dart b/packages/google_sign_in/google_sign_in_android/example/lib/main.dart index a750c330001d..526cf8b69ccf 100644 --- a/packages/google_sign_in/google_sign_in_android/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_android/example/lib/main.dart @@ -14,7 +14,7 @@ import 'package:http/http.dart' as http; void main() { runApp( - MaterialApp( + const MaterialApp( title: 'Google Sign In', home: SignInDemo(), ), @@ -22,6 +22,8 @@ void main() { } class SignInDemo extends StatefulWidget { + const SignInDemo({Key? key}) : super(key: key); + @override State createState() => SignInDemoState(); } @@ -142,8 +144,8 @@ class SignInDemoState extends State { const Text('Signed in successfully.'), Text(_contactText), ElevatedButton( - child: const Text('SIGN OUT'), onPressed: _handleSignOut, + child: const Text('SIGN OUT'), ), ElevatedButton( child: const Text('REFRESH'), @@ -157,8 +159,8 @@ class SignInDemoState extends State { children: [ const Text('You are not currently signed in.'), ElevatedButton( - child: const Text('SIGN IN'), onPressed: _handleSignIn, + child: const Text('SIGN IN'), ), ], ); 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 fa3dc1489b26..d9b272320694 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/plugins/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: 5.2.6 +version: 5.2.7 environment: sdk: ">=2.14.0 <3.0.0" 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 3ffa6b5b7d6b..90069d05f442 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,8 @@ +## 5.2.7 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 5.2.6 * Switches to an internal method channel, rather than the default. diff --git a/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart b/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart index a750c330001d..526cf8b69ccf 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart @@ -14,7 +14,7 @@ import 'package:http/http.dart' as http; void main() { runApp( - MaterialApp( + const MaterialApp( title: 'Google Sign In', home: SignInDemo(), ), @@ -22,6 +22,8 @@ void main() { } class SignInDemo extends StatefulWidget { + const SignInDemo({Key? key}) : super(key: key); + @override State createState() => SignInDemoState(); } @@ -142,8 +144,8 @@ class SignInDemoState extends State { const Text('Signed in successfully.'), Text(_contactText), ElevatedButton( - child: const Text('SIGN OUT'), onPressed: _handleSignOut, + child: const Text('SIGN OUT'), ), ElevatedButton( child: const Text('REFRESH'), @@ -157,8 +159,8 @@ class SignInDemoState extends State { children: [ const Text('You are not currently signed in.'), ElevatedButton( - child: const Text('SIGN IN'), onPressed: _handleSignIn, + child: const Text('SIGN IN'), ), ], ); 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 e5ef3832ff0e..b6f541b22ce1 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: Android implementation of the google_sign_in plugin. repository: https://github.com/flutter/plugins/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.2.6 +version: 5.2.7 environment: sdk: ">=2.14.0 <3.0.0" 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 aab4acae0376..6e87bebf5d02 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.10.1+1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.10.1 * Updates minimum Flutter version to 2.8. diff --git a/packages/google_sign_in/google_sign_in_web/example/lib/main.dart b/packages/google_sign_in/google_sign_in_web/example/lib/main.dart index d381fb4af3ab..b23015c811e8 100644 --- a/packages/google_sign_in/google_sign_in_web/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { 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 5a09453b8e86..3dcd0e8eef29 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/plugins/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.10.1 +version: 0.10.1+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index f1bf54c5cd35..a384a5272e05 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.5+1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.8.5 * Moves Android and iOS implementations to federated packages. diff --git a/packages/image_picker/image_picker/example/lib/main.dart b/packages/image_picker/image_picker/example/lib/main.dart index f3ad2375b8f2..a6f0e83c3abb 100755 --- a/packages/image_picker/image_picker/example/lib/main.dart +++ b/packages/image_picker/image_picker/example/lib/main.dart @@ -13,10 +13,12 @@ import 'package:image_picker/image_picker.dart'; import 'package:video_player/video_player.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( @@ -32,7 +34,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -177,21 +179,22 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - // Why network for web? - // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform - return Semantics( - label: 'image_picker_example_picked_image', - child: kIsWeb - ? Image.network(_imageFileList![index].path) - : Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - label: 'image_picker_example_picked_images'); + label: 'image_picker_example_picked_images', + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + ); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -417,7 +420,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller); + const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 77a50916283e..818486d8e145 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5 +version: 0.8.5+1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index 3472ade28d5b..0514fc33d420 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.4+12 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.8.4+11 * Splits from `image_picker` as a federated implementation. diff --git a/packages/image_picker/image_picker_android/example/lib/main.dart b/packages/image_picker/image_picker_android/example/lib/main.dart index 48eee35445da..d56aeb866195 100755 --- a/packages/image_picker/image_picker_android/example/lib/main.dart +++ b/packages/image_picker/image_picker_android/example/lib/main.dart @@ -13,10 +13,12 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:video_player/video_player.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( @@ -32,7 +34,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -177,21 +179,22 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - // Why network for web? - // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform - return Semantics( - label: 'image_picker_example_picked_image', - child: kIsWeb - ? Image.network(_imageFileList![index].path) - : Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - label: 'image_picker_example_picked_images'); + label: 'image_picker_example_picked_images', + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + ); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -417,7 +420,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller); + const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index dbeef9bed193..90d136c2c89b 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/plugins/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.4+11 +version: 0.8.4+12 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index dcf353fe19b1..c33b3b9981de 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.7 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.1.6 * Internal code cleanup for stricter analysis options. diff --git a/packages/image_picker/image_picker_for_web/example/lib/main.dart b/packages/image_picker/image_picker_for_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/image_picker/image_picker_for_web/example/lib/main.dart +++ b/packages/image_picker/image_picker_for_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index deccd2b50a1f..b0c5deb0da7a 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_for_web description: Web platform implementation of image_picker repository: https://github.com/flutter/plugins/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: 2.1.6 +version: 2.1.7 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index 31a0795a4e30..af391db02689 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.8.5+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.8.5 diff --git a/packages/image_picker/image_picker_ios/example/lib/main.dart b/packages/image_picker/image_picker_ios/example/lib/main.dart index 48eee35445da..d56aeb866195 100755 --- a/packages/image_picker/image_picker_ios/example/lib/main.dart +++ b/packages/image_picker/image_picker_ios/example/lib/main.dart @@ -13,10 +13,12 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:video_player/video_player.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( @@ -32,7 +34,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -177,21 +179,22 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - // Why network for web? - // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform - return Semantics( - label: 'image_picker_example_picked_image', - child: kIsWeb - ? Image.network(_imageFileList![index].path) - : Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - label: 'image_picker_example_picked_images'); + label: 'image_picker_example_picked_images', + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + // Why network for web? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_imageFileList![index].path) + : Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + ); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -417,7 +420,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller); + const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index a9cd052be56a..76ca20614f18 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 video_picker plugin. repository: https://github.com/flutter/plugins/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.5 +version: 0.8.5+1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_windows/CHANGELOG.md b/packages/image_picker/image_picker_windows/CHANGELOG.md index d98656b849c8..e72ab244068f 100644 --- a/packages/image_picker/image_picker_windows/CHANGELOG.md +++ b/packages/image_picker/image_picker_windows/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.1.0+1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.1.0 diff --git a/packages/image_picker/image_picker_windows/example/lib/main.dart b/packages/image_picker/image_picker_windows/example/lib/main.dart index 577d6dadf2d9..b3ba3574522a 100644 --- a/packages/image_picker/image_picker_windows/example/lib/main.dart +++ b/packages/image_picker/image_picker_windows/example/lib/main.dart @@ -12,10 +12,12 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:video_player/video_player.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( @@ -31,7 +33,7 @@ class MyHomePage extends StatefulWidget { final String? title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -176,17 +178,18 @@ class _MyHomePageState extends State { } if (_imageFileList != null) { return Semantics( - child: ListView.builder( - key: UniqueKey(), - itemBuilder: (BuildContext context, int index) { - return Semantics( - label: 'image_picker_example_picked_image', - child: Image.file(File(_imageFileList![index].path)), - ); - }, - itemCount: _imageFileList!.length, - ), - label: 'image_picker_example_picked_images'); + label: 'image_picker_example_picked_images', + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + return Semantics( + label: 'image_picker_example_picked_image', + child: Image.file(File(_imageFileList![index].path)), + ); + }, + itemCount: _imageFileList!.length, + ), + ); } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -363,7 +366,7 @@ typedef OnPickImageCallback = void Function( double? maxWidth, double? maxHeight, int? quality); class AspectRatioVideo extends StatefulWidget { - const AspectRatioVideo(this.controller); + const AspectRatioVideo(this.controller, {Key? key}) : super(key: key); final VideoPlayerController? controller; diff --git a/packages/image_picker/image_picker_windows/pubspec.yaml b/packages/image_picker/image_picker_windows/pubspec.yaml index eec41f7bfa0d..afadf3e39148 100644 --- a/packages/image_picker/image_picker_windows/pubspec.yaml +++ b/packages/image_picker/image_picker_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_windows description: Windows platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.1.0 +version: 0.1.0+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 24ef9eaffd1d..8412c23ee8e8 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,7 +1,9 @@ -## NEXT +## 3.0.3 * Removes unnecessary imports. * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 3.0.2 diff --git a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart index 651652b40c3a..34346a0bd339 100644 --- a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart @@ -33,7 +33,7 @@ const List _kProductIds = [ class _MyApp extends StatefulWidget { @override - _MyAppState createState() => _MyAppState(); + State<_MyApp> createState() => _MyAppState(); } class _MyAppState extends State<_MyApp> { @@ -247,60 +247,61 @@ class _MyAppState extends State<_MyApp> { (ProductDetails productDetails) { final PurchaseDetails? previousPurchase = purchases[productDetails.id]; return ListTile( - title: Text( - productDetails.title, - ), - subtitle: Text( - productDetails.description, - ), - trailing: previousPurchase != null - ? IconButton( - onPressed: () => confirmPriceChange(context), - icon: const Icon(Icons.upgrade)) - : TextButton( - child: Text(productDetails.price), - style: TextButton.styleFrom( - backgroundColor: Colors.green[800], - primary: Colors.white, - ), - onPressed: () { - late PurchaseParam purchaseParam; - - if (Platform.isAndroid) { - // NOTE: If you are making a subscription purchase/upgrade/downgrade, we recommend you to - // verify the latest status of you your subscription by using server side receipt validation - // and update the UI accordingly. The subscription purchase status shown - // inside the app may not be accurate. - final GooglePlayPurchaseDetails? oldSubscription = - _getOldSubscription(productDetails, purchases); - - purchaseParam = GooglePlayPurchaseParam( - productDetails: productDetails, - applicationUserName: null, - changeSubscriptionParam: (oldSubscription != null) - ? ChangeSubscriptionParam( - oldPurchaseDetails: oldSubscription, - prorationMode: ProrationMode - .immediateWithTimeProration, - ) - : null); - } else { - purchaseParam = PurchaseParam( + title: Text( + productDetails.title, + ), + subtitle: Text( + productDetails.description, + ), + trailing: previousPurchase != null + ? IconButton( + onPressed: () => confirmPriceChange(context), + icon: const Icon(Icons.upgrade)) + : TextButton( + style: TextButton.styleFrom( + backgroundColor: Colors.green[800], + primary: Colors.white, + ), + onPressed: () { + late PurchaseParam purchaseParam; + + if (Platform.isAndroid) { + // NOTE: If you are making a subscription purchase/upgrade/downgrade, we recommend you to + // verify the latest status of you your subscription by using server side receipt validation + // and update the UI accordingly. The subscription purchase status shown + // inside the app may not be accurate. + final GooglePlayPurchaseDetails? oldSubscription = + _getOldSubscription(productDetails, purchases); + + purchaseParam = GooglePlayPurchaseParam( productDetails: productDetails, applicationUserName: null, - ); - } - - if (productDetails.id == _kConsumableId) { - _inAppPurchase.buyConsumable( - purchaseParam: purchaseParam, - autoConsume: _kAutoConsume || Platform.isIOS); - } else { - _inAppPurchase.buyNonConsumable( - purchaseParam: purchaseParam); - } - }, - )); + changeSubscriptionParam: (oldSubscription != null) + ? ChangeSubscriptionParam( + oldPurchaseDetails: oldSubscription, + prorationMode: + ProrationMode.immediateWithTimeProration, + ) + : null); + } else { + purchaseParam = PurchaseParam( + productDetails: productDetails, + applicationUserName: null, + ); + } + + if (productDetails.id == _kConsumableId) { + _inAppPurchase.buyConsumable( + purchaseParam: purchaseParam, + autoConsume: _kAutoConsume || Platform.isIOS); + } else { + _inAppPurchase.buyNonConsumable( + purchaseParam: purchaseParam); + } + }, + child: Text(productDetails.price), + ), + ); }, )); @@ -340,9 +341,9 @@ class _MyAppState extends State<_MyApp> { const Divider(), GridView.count( crossAxisCount: 5, - children: tokens, shrinkWrap: true, padding: const EdgeInsets.all(16.0), + children: tokens, ) ])); } @@ -359,12 +360,12 @@ class _MyAppState extends State<_MyApp> { mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( - child: const Text('Restore purchases'), style: TextButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, primary: Colors.white, ), onPressed: () => _inAppPurchase.restorePurchases(), + child: const Text('Restore purchases'), ), ], ), diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index af8f5f3c2773..d2f875293876 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 3.0.2 +version: 3.0.3 environment: sdk: ">=2.12.0 <3.0.0" 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 2657d504ac91..4b9e58c08d06 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.2.2+4 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.2.2+3 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 939bc43bea63..1da943535f70 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 @@ -37,7 +37,7 @@ const List _kProductIds = [ class _MyApp extends StatefulWidget { @override - _MyAppState createState() => _MyAppState(); + State<_MyApp> createState() => _MyAppState(); } class _MyAppState extends State<_MyApp> { @@ -264,7 +264,6 @@ class _MyAppState extends State<_MyApp> { }, icon: const Icon(Icons.upgrade)) : TextButton( - child: Text(productDetails.price), style: TextButton.styleFrom( backgroundColor: Colors.green[800], primary: Colors.white, @@ -297,6 +296,7 @@ class _MyAppState extends State<_MyApp> { purchaseParam: purchaseParam); } }, + child: Text(productDetails.price), )); }, )); @@ -337,9 +337,9 @@ class _MyAppState extends State<_MyApp> { const Divider(), GridView.count( crossAxisCount: 5, - children: tokens, shrinkWrap: true, padding: const EdgeInsets.all(16.0), + children: tokens, ) ])); } 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 62888e6dfb73..7de778177c31 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/plugins/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.2.2+3 +version: 0.2.2+4 environment: sdk: ">=2.14.0 <3.0.0" 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 403ee32be2ae..7342077ab176 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.3.0+6 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.3.0+5 diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart index 2ee2deb7fe35..f45a8c7f8741 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart @@ -37,7 +37,7 @@ const List _kProductIds = [ class _MyApp extends StatefulWidget { @override - _MyAppState createState() => _MyAppState(); + State<_MyApp> createState() => _MyAppState(); } class _MyAppState extends State<_MyApp> { @@ -259,7 +259,6 @@ class _MyAppState extends State<_MyApp> { }, icon: const Icon(Icons.upgrade)) : TextButton( - child: Text(productDetails.price), style: TextButton.styleFrom( backgroundColor: Colors.green[800], primary: Colors.white, @@ -278,6 +277,7 @@ class _MyAppState extends State<_MyApp> { purchaseParam: purchaseParam); } }, + child: Text(productDetails.price), )); }, )); @@ -318,9 +318,9 @@ class _MyAppState extends State<_MyApp> { const Divider(), GridView.count( crossAxisCount: 5, - children: tokens, shrinkWrap: true, padding: const EdgeInsets.all(16.0), + children: tokens, ) ])); } @@ -337,12 +337,12 @@ class _MyAppState extends State<_MyApp> { mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( - child: const Text('Restore purchases'), style: TextButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, primary: Colors.white, ), onPressed: () => _iapStoreKitPlatform.restorePurchases(), + child: const Text('Restore purchases'), ), ], ), 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 9693c186119c..24b693c98c36 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 platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/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.0+5 +version: 0.3.0+6 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index 0a3755afa7e7..cf2632feaac7 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 0.2.0+6 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.2.0+5 diff --git a/packages/ios_platform_images/example/lib/main.dart b/packages/ios_platform_images/example/lib/main.dart index ecdfeb2cba01..929814ecce00 100644 --- a/packages/ios_platform_images/example/lib/main.dart +++ b/packages/ios_platform_images/example/lib/main.dart @@ -5,12 +5,15 @@ import 'package:flutter/material.dart'; import 'package:ios_platform_images/ios_platform_images.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); /// Main widget for the example app. class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/ios_platform_images/example/test/widget_test.dart b/packages/ios_platform_images/example/test/widget_test.dart index 18e9e657ddb9..f3cd4c68b65b 100644 --- a/packages/ios_platform_images/example/test/widget_test.dart +++ b/packages/ios_platform_images/example/test/widget_test.dart @@ -12,7 +12,7 @@ import 'package:ios_platform_images_example/main.dart'; void main() { testWidgets('Verify loads image', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); + await tester.pumpWidget(const MyApp()); expect( find.byWidgetPredicate( diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index 7f80714e4c1c..41a177560299 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -2,7 +2,7 @@ name: ios_platform_images description: A plugin to share images between Flutter and iOS in add-to-app setups. repository: https://github.com/flutter/plugins/tree/main/packages/ios_platform_images issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+ios_platform_images%22 -version: 0.2.0+5 +version: 0.2.0+6 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 570d8eef7b9b..8a2743e6140b 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.2 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.1 * Restores the ability to import `error_codes.dart`. diff --git a/packages/local_auth/local_auth/example/lib/main.dart b/packages/local_auth/local_auth/example/lib/main.dart index 92ad7cf4fb3f..cc687f562402 100644 --- a/packages/local_auth/local_auth/example/lib/main.dart +++ b/packages/local_auth/local_auth/example/lib/main.dart @@ -11,12 +11,14 @@ import 'package:flutter/services.dart'; import 'package:local_auth/local_auth.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { @@ -169,14 +171,14 @@ class _MyAppState extends State { const Divider(height: 100), Text('Can check biometrics: $_canCheckBiometrics\n'), ElevatedButton( - child: const Text('Check biometrics'), onPressed: _checkBiometrics, + child: const Text('Check biometrics'), ), const Divider(height: 100), Text('Available biometrics: $_availableBiometrics\n'), ElevatedButton( - child: const Text('Get available biometrics'), onPressed: _getAvailableBiometrics, + child: const Text('Get available biometrics'), ), const Divider(height: 100), Text('Current State: $_authorized\n'), @@ -195,6 +197,7 @@ class _MyAppState extends State { Column( children: [ ElevatedButton( + onPressed: _authenticate, child: Row( mainAxisSize: MainAxisSize.min, children: const [ @@ -202,9 +205,9 @@ class _MyAppState extends State { Icon(Icons.perm_device_information), ], ), - onPressed: _authenticate, ), ElevatedButton( + onPressed: _authenticateWithBiometrics, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -214,7 +217,6 @@ class _MyAppState extends State { const Icon(Icons.fingerprint), ], ), - onPressed: _authenticateWithBiometrics, ), ], ), 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 25ddfe0fa62f..340aaef28f84 100644 --- a/packages/local_auth/local_auth/example/lib/readme_excerpts.dart +++ b/packages/local_auth/local_auth/example/lib/readme_excerpts.dart @@ -24,10 +24,12 @@ import 'package:local_auth_ios/local_auth_ios.dart'; // #enddocregion CustomMessages void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override State createState() => _MyAppState(); } diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index 087b84920bb9..119b8d778cbc 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -3,7 +3,7 @@ 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/plugins/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.0.1 +version: 2.0.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 6afcf1ffed07..9f9bd7b535a9 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 1.0.3 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 1.0.2 diff --git a/packages/local_auth/local_auth_android/example/lib/main.dart b/packages/local_auth/local_auth_android/example/lib/main.dart index 29b1d66440eb..016d955f0a3f 100644 --- a/packages/local_auth/local_auth_android/example/lib/main.dart +++ b/packages/local_auth/local_auth_android/example/lib/main.dart @@ -12,12 +12,14 @@ import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { @@ -174,14 +176,14 @@ class _MyAppState extends State { Text( 'Device supports biometrics: $_deviceSupportsBiometrics\n'), ElevatedButton( - child: const Text('Check biometrics'), onPressed: _checkBiometrics, + child: const Text('Check biometrics'), ), const Divider(height: 100), Text('Enrolled biometrics: $_enrolledBiometrics\n'), ElevatedButton( - child: const Text('Get enrolled biometrics'), onPressed: _getEnrolledBiometrics, + child: const Text('Get enrolled biometrics'), ), const Divider(height: 100), Text('Current State: $_authorized\n'), @@ -200,6 +202,7 @@ class _MyAppState extends State { Column( children: [ ElevatedButton( + onPressed: _authenticate, child: Row( mainAxisSize: MainAxisSize.min, children: const [ @@ -207,9 +210,9 @@ class _MyAppState extends State { Icon(Icons.perm_device_information), ], ), - onPressed: _authenticate, ), ElevatedButton( + onPressed: _authenticateWithBiometrics, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -219,7 +222,6 @@ class _MyAppState extends State { const Icon(Icons.fingerprint), ], ), - onPressed: _authenticateWithBiometrics, ), ], ), diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index aad0b9b92e70..cdd4e8225504 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.2 +version: 1.0.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index ca6ac43eb52f..2237cbe216f0 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 1.0.5 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 1.0.4 diff --git a/packages/local_auth/local_auth_ios/example/lib/main.dart b/packages/local_auth/local_auth_ios/example/lib/main.dart index a8bf23b78a52..479a96ba809c 100644 --- a/packages/local_auth/local_auth_ios/example/lib/main.dart +++ b/packages/local_auth/local_auth_ios/example/lib/main.dart @@ -12,12 +12,14 @@ import 'package:local_auth_ios/local_auth_ios.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { @@ -173,14 +175,14 @@ class _MyAppState extends State { const Divider(height: 100), Text('Device supports biometrics: $_canCheckBiometrics\n'), ElevatedButton( - child: const Text('Check biometrics'), onPressed: _checkBiometrics, + child: const Text('Check biometrics'), ), const Divider(height: 100), Text('Enrolled biometrics: $_enrolledBiometrics\n'), ElevatedButton( - child: const Text('Get enrolled biometrics'), onPressed: _getEnrolledBiometrics, + child: const Text('Get enrolled biometrics'), ), const Divider(height: 100), Text('Current State: $_authorized\n'), @@ -199,6 +201,7 @@ class _MyAppState extends State { Column( children: [ ElevatedButton( + onPressed: _authenticate, child: Row( mainAxisSize: MainAxisSize.min, children: const [ @@ -206,9 +209,9 @@ class _MyAppState extends State { Icon(Icons.perm_device_information), ], ), - onPressed: _authenticate, ), ElevatedButton( + onPressed: _authenticateWithBiometrics, child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -218,7 +221,6 @@ class _MyAppState extends State { const Icon(Icons.fingerprint), ], ), - onPressed: _authenticateWithBiometrics, ), ], ), diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index 77ab74d383c8..dded42b673f4 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.4 +version: 1.0.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index a26a8901d1d7..990021671801 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,7 +1,9 @@ -## NEXT +## 2.0.10 * Removes unnecessary imports. * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.9 diff --git a/packages/path_provider/path_provider/example/lib/main.dart b/packages/path_provider/path_provider/example/lib/main.dart index 90c2ccb93154..cb9c2eb1798d 100644 --- a/packages/path_provider/path_provider/example/lib/main.dart +++ b/packages/path_provider/path_provider/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -31,7 +33,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -138,10 +140,10 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: _requestTempDirectory, child: const Text( 'Get Temporary Directory', ), - onPressed: _requestTempDirectory, ), ), FutureBuilder( @@ -155,10 +157,10 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: _requestAppDocumentsDirectory, child: const Text( 'Get Application Documents Directory', ), - onPressed: _requestAppDocumentsDirectory, ), ), FutureBuilder( @@ -172,10 +174,10 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: _requestAppSupportDirectory, child: const Text( 'Get Application Support Directory', ), - onPressed: _requestAppSupportDirectory, ), ), FutureBuilder( @@ -189,13 +191,13 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: + Platform.isAndroid ? null : _requestAppLibraryDirectory, child: Text( Platform.isAndroid ? 'Application Library Directory unavailable' : 'Get Application Library Directory', ), - onPressed: - Platform.isAndroid ? null : _requestAppLibraryDirectory, ), ), FutureBuilder( @@ -209,14 +211,14 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: !Platform.isAndroid + ? null + : _requestExternalStorageDirectory, child: Text( !Platform.isAndroid ? 'External storage is unavailable' : 'Get External Storage Directory', ), - onPressed: !Platform.isAndroid - ? null - : _requestExternalStorageDirectory, ), ), FutureBuilder( @@ -230,11 +232,6 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: Text( - !Platform.isAndroid - ? 'External directories are unavailable' - : 'Get External Storage Directories', - ), onPressed: !Platform.isAndroid ? null : () { @@ -242,6 +239,11 @@ class _MyHomePageState extends State { StorageDirectory.music, ); }, + child: Text( + !Platform.isAndroid + ? 'External directories are unavailable' + : 'Get External Storage Directories', + ), ), ), FutureBuilder?>( @@ -255,14 +257,14 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: !Platform.isAndroid + ? null + : _requestExternalCacheDirectories, child: Text( !Platform.isAndroid ? 'External directories are unavailable' : 'Get External Cache Directories', ), - onPressed: !Platform.isAndroid - ? null - : _requestExternalCacheDirectories, ), ), FutureBuilder?>( @@ -276,14 +278,14 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( + onPressed: Platform.isAndroid || Platform.isIOS + ? null + : _requestDownloadsDirectory, child: Text( Platform.isAndroid || Platform.isIOS ? 'Downloads directory is unavailable' : 'Get Downloads Directory', ), - onPressed: Platform.isAndroid || Platform.isIOS - ? null - : _requestDownloadsDirectory, ), ), FutureBuilder( diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml index 30ddfdcfb119..6ca8325843e4 100644 --- a/packages/path_provider/path_provider/pubspec.yaml +++ b/packages/path_provider/path_provider/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider description: Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.9 +version: 2.0.10 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index 31f8c81f8a65..4b15e2605038 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.14 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.13 * Fixes typing build warning. diff --git a/packages/path_provider/path_provider_android/example/lib/main.dart b/packages/path_provider/path_provider_android/example/lib/main.dart index 6e04f865bfcf..fc9424a33542 100644 --- a/packages/path_provider/path_provider_android/example/lib/main.dart +++ b/packages/path_provider/path_provider_android/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -29,7 +31,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -121,8 +123,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Temporary Directory'), onPressed: _requestTempDirectory, + child: const Text('Get Temporary Directory'), ), ), FutureBuilder( @@ -130,8 +132,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Documents Directory'), onPressed: _requestAppDocumentsDirectory, + child: const Text('Get Application Documents Directory'), ), ), FutureBuilder( @@ -139,8 +141,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Support Directory'), onPressed: _requestAppSupportDirectory, + child: const Text('Get Application Support Directory'), ), ), FutureBuilder( @@ -148,8 +150,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get External Storage Directory'), onPressed: _requestExternalStorageDirectory, + child: const Text('Get External Storage Directory'), ), ), FutureBuilder( @@ -174,8 +176,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get External Cache Directories'), onPressed: _requestExternalCacheDirectories, + child: const Text('Get External Cache Directories'), ), ), ]), diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index 93ed9848f75b..f1dc92abdefc 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.13 +version: 2.0.14 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_ios/CHANGELOG.md b/packages/path_provider/path_provider_ios/CHANGELOG.md index 543af778d2e2..1940f5c7888e 100644 --- a/packages/path_provider/path_provider_ios/CHANGELOG.md +++ b/packages/path_provider/path_provider_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.9 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.8 * Switches to a package-internal implementation of the platform interface. diff --git a/packages/path_provider/path_provider_ios/example/lib/main.dart b/packages/path_provider/path_provider_ios/example/lib/main.dart index 8c8d5410f923..d7140b76a06b 100644 --- a/packages/path_provider/path_provider_ios/example/lib/main.dart +++ b/packages/path_provider/path_provider_ios/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -29,7 +31,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { @@ -90,8 +92,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Temporary Directory'), onPressed: _requestTempDirectory, + child: const Text('Get Temporary Directory'), ), ), FutureBuilder( @@ -99,8 +101,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Documents Directory'), onPressed: _requestAppDocumentsDirectory, + child: const Text('Get Application Documents Directory'), ), ), FutureBuilder( @@ -108,8 +110,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Support Directory'), onPressed: _requestAppSupportDirectory, + child: const Text('Get Application Support Directory'), ), ), FutureBuilder( @@ -117,8 +119,8 @@ class _MyHomePageState extends State { Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( - child: const Text('Get Application Library Directory'), onPressed: _requestAppLibraryDirectory, + child: const Text('Get Application Library Directory'), ), ), FutureBuilder( diff --git a/packages/path_provider/path_provider_ios/pubspec.yaml b/packages/path_provider/path_provider_ios/pubspec.yaml index 282f8e4f0ebd..d6c7de108c97 100644 --- a/packages/path_provider/path_provider_ios/pubspec.yaml +++ b/packages/path_provider/path_provider_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_ios description: iOS implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.8 +version: 2.0.9 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_linux/CHANGELOG.md b/packages/path_provider/path_provider_linux/CHANGELOG.md index 55235c3542f9..c9c4bb3cc906 100644 --- a/packages/path_provider/path_provider_linux/CHANGELOG.md +++ b/packages/path_provider/path_provider_linux/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.6 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.1.5 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_linux/example/lib/main.dart b/packages/path_provider/path_provider_linux/example/lib/main.dart index d365e6bdeab4..1c7c7e87397a 100644 --- a/packages/path_provider/path_provider_linux/example/lib/main.dart +++ b/packages/path_provider/path_provider_linux/example/lib/main.dart @@ -7,13 +7,16 @@ import 'package:flutter/services.dart'; import 'package:path_provider_linux/path_provider_linux.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// Sample app class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index 91304fc0b268..16438a3870d1 100644 --- a/packages/path_provider/path_provider_linux/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_linux description: Linux implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.1.5 +version: 2.1.6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/path_provider/path_provider_macos/CHANGELOG.md b/packages/path_provider/path_provider_macos/CHANGELOG.md index 047792f8bcc4..c59ba971d461 100644 --- a/packages/path_provider/path_provider_macos/CHANGELOG.md +++ b/packages/path_provider/path_provider_macos/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.6 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.5 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_macos/example/lib/main.dart b/packages/path_provider/path_provider_macos/example/lib/main.dart index 67a0eb32eeda..13a6fada5fef 100644 --- a/packages/path_provider/path_provider_macos/example/lib/main.dart +++ b/packages/path_provider/path_provider_macos/example/lib/main.dart @@ -8,13 +8,15 @@ import 'package:flutter/material.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// Sample app class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/path_provider/path_provider_macos/pubspec.yaml b/packages/path_provider/path_provider_macos/pubspec.yaml index 2451b6dedf80..444165b86c3f 100644 --- a/packages/path_provider/path_provider_macos/pubspec.yaml +++ b/packages/path_provider/path_provider_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_macos description: macOS implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.5 +version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/path_provider/path_provider_windows/CHANGELOG.md b/packages/path_provider/path_provider_windows/CHANGELOG.md index cd33e85a851d..014b6b36da2b 100644 --- a/packages/path_provider/path_provider_windows/CHANGELOG.md +++ b/packages/path_provider/path_provider_windows/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.6 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.5 * Removes dependency on `meta`. diff --git a/packages/path_provider/path_provider_windows/example/lib/main.dart b/packages/path_provider/path_provider_windows/example/lib/main.dart index 509292bf7405..4c63d245a16a 100644 --- a/packages/path_provider/path_provider_windows/example/lib/main.dart +++ b/packages/path_provider/path_provider_windows/example/lib/main.dart @@ -8,13 +8,15 @@ import 'package:flutter/material.dart'; import 'package:path_provider_windows/path_provider_windows.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// Sample app class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/path_provider/path_provider_windows/pubspec.yaml b/packages/path_provider/path_provider_windows/pubspec.yaml index 873fa0a6861b..49afdd6293e7 100644 --- a/packages/path_provider/path_provider_windows/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_windows description: Windows implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.5 +version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index c30d7052320e..73540a863364 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,8 +1,10 @@ -## NEXT +## 0.6.0+11 * Removes unnecessary imports. * Updates minimum Flutter version to 2.8. * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.6.0+10 diff --git a/packages/quick_actions/quick_actions/example/lib/main.dart b/packages/quick_actions/quick_actions/example/lib/main.dart index 1ce6f51d71de..cafbf0c351d9 100644 --- a/packages/quick_actions/quick_actions/example/lib/main.dart +++ b/packages/quick_actions/quick_actions/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:quick_actions/quick_actions.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -28,7 +30,7 @@ class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/quick_actions/quick_actions/pubspec.yaml b/packages/quick_actions/quick_actions/pubspec.yaml index 8ef2d3ab4e02..37e8dbe5f3e3 100644 --- a/packages/quick_actions/quick_actions/pubspec.yaml +++ b/packages/quick_actions/quick_actions/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for creating shortcuts on home screen, also known as Quick Actions on iOS and App Shortcuts on Android. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%22 -version: 0.6.0+10 +version: 0.6.0+11 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions_android/CHANGELOG.md b/packages/quick_actions/quick_actions_android/CHANGELOG.md index 98e8cf5e333b..56accc9d044c 100644 --- a/packages/quick_actions/quick_actions_android/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.6.0+10 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 0.6.0+9 * Switches to a package-internal implementation of the platform interface. \ No newline at end of file diff --git a/packages/quick_actions/quick_actions_android/example/lib/main.dart b/packages/quick_actions/quick_actions_android/example/lib/main.dart index 06f141073b33..d8b7832bf9dc 100644 --- a/packages/quick_actions/quick_actions_android/example/lib/main.dart +++ b/packages/quick_actions/quick_actions_android/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:quick_actions_android/quick_actions_android.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -28,7 +30,7 @@ class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/quick_actions/quick_actions_android/pubspec.yaml b/packages/quick_actions/quick_actions_android/pubspec.yaml index cf9971dca945..4ddbc79ee5e9 100644 --- a/packages/quick_actions/quick_actions_android/pubspec.yaml +++ b/packages/quick_actions/quick_actions_android/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_android description: An implementation for the Android platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.6.0+9 +version: 0.6.0+10 environment: sdk: ">=2.15.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions_ios/CHANGELOG.md b/packages/quick_actions/quick_actions_ios/CHANGELOG.md index d48afbd8d13a..56accc9d044c 100644 --- a/packages/quick_actions/quick_actions_ios/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.0+10 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.6.0+9 diff --git a/packages/quick_actions/quick_actions_ios/example/lib/main.dart b/packages/quick_actions/quick_actions_ios/example/lib/main.dart index 5173d952d623..008917b724e0 100644 --- a/packages/quick_actions/quick_actions_ios/example/lib/main.dart +++ b/packages/quick_actions/quick_actions_ios/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:quick_actions_ios/quick_actions_ios.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -28,7 +30,7 @@ class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/quick_actions/quick_actions_ios/pubspec.yaml b/packages/quick_actions/quick_actions_ios/pubspec.yaml index 26644ba12fde..47748b9789ad 100644 --- a/packages/quick_actions/quick_actions_ios/pubspec.yaml +++ b/packages/quick_actions/quick_actions_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_ios description: An implementation for the iOS platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.6.0+9 +version: 0.6.0+10 environment: sdk: ">=2.15.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index 84566e26e2c0..22c39aad98fd 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.0.14 * Adds OS version support information to README. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.13 diff --git a/packages/shared_preferences/shared_preferences/example/lib/main.dart b/packages/shared_preferences/shared_preferences/example/lib/main.dart index 43f3c78ad920..a2e72b446925 100644 --- a/packages/shared_preferences/shared_preferences/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index 39b48ef51ed2..4218095c0efe 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.13 +version: 2.0.14 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index 5321e869c497..51e99ec6d3d5 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.12 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.11 * Switches to an in-package method channel implementation. diff --git a/packages/shared_preferences/shared_preferences_android/example/lib/main.dart b/packages/shared_preferences/shared_preferences_android/example/lib/main.dart index 06ee9434ba84..bb513b09f6d5 100644 --- a/packages/shared_preferences/shared_preferences_android/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_android/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_android/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/pubspec.yaml index 7eb180f3ab48..2d8cc88d3703 100644 --- a/packages/shared_preferences/shared_preferences_android/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_android description: Android implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.11 +version: 2.0.12 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md index a5cc1d34e034..29ade8d496a1 100644 --- a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.1.0 * Upgrades to using Pigeon. diff --git a/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart b/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart index 06ee9434ba84..bb513b09f6d5 100644 --- a/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_ios/example/lib/main.dart @@ -8,10 +8,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml index 33bf5baffd18..a8bde2e9f87f 100644 --- a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_ios description: iOS implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.0 +version: 2.1.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md index 34dd631746bb..f0cb8322f385 100644 --- a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.1.1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.1.0 diff --git a/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart b/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart index 4b71c7ea3beb..d51be33baeed 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_linux/shared_preferences_linux.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml index 8ab692a613e2..8f3ce1723bc9 100644 --- a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_linux description: Linux implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.0 +version: 2.1.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md index 0f194de44224..8ba116a74fe0 100644 --- a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.0.4 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.3 diff --git a/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart b/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart index 349e9c45405a..e6bbe5931471 100644 --- a/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_macos/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml index 0873696e6d4f..615d0b05ba99 100644 --- a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_macos description: macOS implementation of the shared_preferences plugin. repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.3 +version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md index 2a6ffa20e37b..9ea249034105 100644 --- a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.4 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.3 * Fixes newly enabled analyzer options. diff --git a/packages/shared_preferences/shared_preferences_web/example/lib/main.dart b/packages/shared_preferences/shared_preferences_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/shared_preferences/shared_preferences_web/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/shared_preferences/shared_preferences_web/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/pubspec.yaml index 232c87c426fc..9ff76d27714c 100644 --- a/packages/shared_preferences/shared_preferences_web/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_web description: Web platform implementation of shared_preferences repository: https://github.com/flutter/plugins/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.0.3 +version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md index 6c96681ce5b7..f79f9e3d5d39 100644 --- a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.1.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.1.0 * Deprecated `SharedPreferencesWindows.instance` in favor of `SharedPreferencesStorePlatform.instance`. diff --git a/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart b/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart index 40a9159cee70..74d5e4c68772 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences_windows/shared_preferences_windows.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return const MaterialApp( diff --git a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml index 6dcb5997f131..99326cb24f18 100644 --- a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_windows description: Windows implementation of shared_preferences repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.0 +version: 2.1.1 environment: sdk: '>=2.12.0 <3.0.0' diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index b25956fd5919..493412c3e006 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 6.1.1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 6.1.0 diff --git a/packages/url_launcher/url_launcher/example/lib/main.dart b/packages/url_launcher/url_launcher/example/lib/main.dart index 898e80661296..a538940f1a68 100644 --- a/packages/url_launcher/url_launcher/example/lib/main.dart +++ b/packages/url_launcher/url_launcher/example/lib/main.dart @@ -11,10 +11,12 @@ import 'package:url_launcher/link.dart'; import 'package:url_launcher/url_launcher.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -32,7 +34,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher/lib/src/link.dart b/packages/url_launcher/url_launcher/lib/src/link.dart index 76cb97748003..8c0c18e820e3 100644 --- a/packages/url_launcher/url_launcher/lib/src/link.dart +++ b/packages/url_launcher/url_launcher/lib/src/link.dart @@ -86,7 +86,7 @@ class Link extends StatelessWidget implements LinkInfo { /// event channel messages to instruct the framework to push the route name. class DefaultLinkDelegate extends StatelessWidget { /// Creates a delegate for the given [link]. - const DefaultLinkDelegate(this.link); + const DefaultLinkDelegate(this.link, {Key? key}) : super(key: key); /// Given a [link], creates an instance of [DefaultLinkDelegate]. /// diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 6803d71032cb..c14b62a1e70a 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/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.1.0 +version: 6.1.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md index 69b96156d849..887178c479e4 100644 --- a/packages/url_launcher/url_launcher_android/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.0.17 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 6.0.16 * Adds fallback querying for `canLaunch` with web URLs, to avoid false negatives diff --git a/packages/url_launcher/url_launcher_android/example/lib/main.dart b/packages/url_launcher/url_launcher_android/example/lib/main.dart index 7abc73430e8b..672ae4a27665 100644 --- a/packages/url_launcher/url_launcher_android/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_android/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -31,7 +33,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index 3230dfeffd2e..3c80170f1422 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_android description: Android implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.16 +version: 6.0.17 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_ios/CHANGELOG.md b/packages/url_launcher/url_launcher_ios/CHANGELOG.md index 6e0c8d6a20d7..5f6dd37142bb 100644 --- a/packages/url_launcher/url_launcher_ios/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.0.16 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 6.0.15 * Switches to an in-package method channel implementation. diff --git a/packages/url_launcher/url_launcher_ios/example/lib/main.dart b/packages/url_launcher/url_launcher_ios/example/lib/main.dart index 2f73622ebb41..7aa3a4b74e83 100644 --- a/packages/url_launcher/url_launcher_ios/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_ios/example/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -31,7 +33,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_ios/pubspec.yaml b/packages/url_launcher/url_launcher_ios/pubspec.yaml index 8a5bfd20c8f4..0b21bad35204 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/plugins/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.0.15 +version: 6.0.16 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_linux/CHANGELOG.md b/packages/url_launcher/url_launcher_linux/CHANGELOG.md index 0fc373f2ebb1..27c18a66805b 100644 --- a/packages/url_launcher/url_launcher_linux/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_linux/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.0.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 3.0.0 * Changes the major version since, due to a typo in `default_package` in diff --git a/packages/url_launcher/url_launcher_linux/example/lib/main.dart b/packages/url_launcher/url_launcher_linux/example/lib/main.dart index a9a5d22dadd5..0b985e78ac0d 100644 --- a/packages/url_launcher/url_launcher_linux/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_linux/example/lib/main.dart @@ -9,10 +9,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -30,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_linux/pubspec.yaml b/packages/url_launcher/url_launcher_linux/pubspec.yaml index cb9a0be0aa41..c9472045e499 100644 --- a/packages/url_launcher/url_launcher_linux/pubspec.yaml +++ b/packages/url_launcher/url_launcher_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_linux description: Linux implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.0 +version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_macos/CHANGELOG.md b/packages/url_launcher/url_launcher_macos/CHANGELOG.md index 082bc45fc2e8..2fa5e918eadd 100644 --- a/packages/url_launcher/url_launcher_macos/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_macos/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.0.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 3.0.0 * Changes the major version since, due to a typo in `default_package` in diff --git a/packages/url_launcher/url_launcher_macos/example/lib/main.dart b/packages/url_launcher/url_launcher_macos/example/lib/main.dart index a9a5d22dadd5..0b985e78ac0d 100644 --- a/packages/url_launcher/url_launcher_macos/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_macos/example/lib/main.dart @@ -9,10 +9,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -30,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_macos/pubspec.yaml b/packages/url_launcher/url_launcher_macos/pubspec.yaml index 8b5183b1914d..edda6b67cfb3 100644 --- a/packages/url_launcher/url_launcher_macos/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_macos description: macOS implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.0 +version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md index a434b7af70c2..b53a92cee707 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.10 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 2.0.9 - Fixes invalid routes when opening a `Link` in a new tab diff --git a/packages/url_launcher/url_launcher_web/example/lib/main.dart b/packages/url_launcher/url_launcher_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/url_launcher/url_launcher_web/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { 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 4498e74ea9ce..eccd9aef80e3 100644 --- a/packages/url_launcher/url_launcher_web/lib/src/link.dart +++ b/packages/url_launcher/url_launcher_web/lib/src/link.dart @@ -31,7 +31,7 @@ HtmlViewFactory get linkViewFactory => LinkViewController._viewFactory; /// It uses a platform view to render an anchor element in the DOM. class WebLinkDelegate extends StatefulWidget { /// Creates a delegate for the given [link]. - const WebLinkDelegate(this.link); + const WebLinkDelegate(this.link, {Key? key}) : super(key: key); /// Information about the link built by the app. final LinkInfo link; diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index c45c062255ad..cd8ed2d269c8 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_web description: Web platform implementation of url_launcher repository: https://github.com/flutter/plugins/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.0.9 +version: 2.0.10 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_windows/CHANGELOG.md b/packages/url_launcher/url_launcher_windows/CHANGELOG.md index e02f5a2288e1..3ff14fd2f18a 100644 --- a/packages/url_launcher/url_launcher_windows/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_windows/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.0.1 + +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. + ## 3.0.0 * Changes the major version since, due to a typo in `default_package` in diff --git a/packages/url_launcher/url_launcher_windows/example/lib/main.dart b/packages/url_launcher/url_launcher_windows/example/lib/main.dart index a9a5d22dadd5..0b985e78ac0d 100644 --- a/packages/url_launcher/url_launcher_windows/example/lib/main.dart +++ b/packages/url_launcher/url_launcher_windows/example/lib/main.dart @@ -9,10 +9,12 @@ import 'package:flutter/material.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return MaterialApp( @@ -30,7 +32,7 @@ class MyHomePage extends StatefulWidget { final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + State createState() => _MyHomePageState(); } class _MyHomePageState extends State { diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml index 95f17ad9e921..c3f224e26adf 100644 --- a/packages/url_launcher/url_launcher_windows/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_windows description: Windows implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.0 +version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index af01c64fd554..ede890162f86 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.4.1 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.4.0 diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 39cd415dbb79..a6ec51015c33 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -708,14 +708,14 @@ class _VideoAppLifeCycleObserver extends Object with WidgetsBindingObserver { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - const VideoPlayer(this.controller); + const VideoPlayer(this.controller, {Key? key}) : super(key: key); /// The [VideoPlayerController] responsible for the video being rendered in /// this widget. final VideoPlayerController controller; @override - _VideoPlayerState createState() => _VideoPlayerState(); + State createState() => _VideoPlayerState(); } class _VideoPlayerState extends State { @@ -883,10 +883,11 @@ class VideoProgressIndicator extends StatefulWidget { /// to `top: 5.0`. const VideoProgressIndicator( this.controller, { + Key? key, this.colors = const VideoProgressColors(), required this.allowScrubbing, this.padding = const EdgeInsets.only(top: 5.0), - }); + }) : super(key: key); /// The [VideoPlayerController] that actually associates a video with this /// widget. @@ -910,7 +911,7 @@ class VideoProgressIndicator extends StatefulWidget { final EdgeInsets padding; @override - _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); + State createState() => _VideoProgressIndicatorState(); } class _VideoProgressIndicatorState extends State { @@ -984,8 +985,8 @@ class _VideoProgressIndicatorState extends State { ); if (widget.allowScrubbing) { return _VideoScrubber( - child: paddedProgressIndicator, controller: controller, + child: paddedProgressIndicator, ); } else { return paddedProgressIndicator; diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 0d654a4330a7..b0ca56429271 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/plugins/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.4.0 +version: 2.4.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 16dd52ca6da0..08acba895eba 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.3.3 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.3.2 diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart index 498dbffc9e84..5bce3117d0d6 100644 --- a/packages/video_player/video_player_android/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart @@ -351,14 +351,14 @@ class MiniController extends ValueNotifier { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - const VideoPlayer(this.controller); + const VideoPlayer(this.controller, {Key? key}) : super(key: key); /// The [MiniController] responsible for the video being rendered in /// this widget. final MiniController controller; @override - _VideoPlayerState createState() => _VideoPlayerState(); + State createState() => _VideoPlayerState(); } class _VideoPlayerState extends State { @@ -450,14 +450,14 @@ class _VideoScrubberState extends State<_VideoScrubber> { class VideoProgressIndicator extends StatefulWidget { /// Construct an instance that displays the play/buffering status of the video /// controlled by [controller]. - const VideoProgressIndicator(this.controller); + const VideoProgressIndicator(this.controller, {Key? key}) : super(key: key); /// The [MiniController] that actually associates a video with this /// widget. final MiniController controller; @override - _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); + State createState() => _VideoProgressIndicatorState(); } class _VideoProgressIndicatorState extends State { @@ -527,11 +527,11 @@ class _VideoProgressIndicatorState extends State { ); } return _VideoScrubber( + controller: controller, child: Padding( padding: const EdgeInsets.only(top: 5.0), child: progressIndicator, ), - controller: controller, ); } } diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index aa288ed71eac..bc69fd41369a 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.2 +version: 2.3.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index d77c36c915b6..6ab5398f7013 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.3.4 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.3.3 diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart index 498dbffc9e84..5bce3117d0d6 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart @@ -351,14 +351,14 @@ class MiniController extends ValueNotifier { /// Widget that displays the video controlled by [controller]. class VideoPlayer extends StatefulWidget { /// Uses the given [controller] for all video rendered in this widget. - const VideoPlayer(this.controller); + const VideoPlayer(this.controller, {Key? key}) : super(key: key); /// The [MiniController] responsible for the video being rendered in /// this widget. final MiniController controller; @override - _VideoPlayerState createState() => _VideoPlayerState(); + State createState() => _VideoPlayerState(); } class _VideoPlayerState extends State { @@ -450,14 +450,14 @@ class _VideoScrubberState extends State<_VideoScrubber> { class VideoProgressIndicator extends StatefulWidget { /// Construct an instance that displays the play/buffering status of the video /// controlled by [controller]. - const VideoProgressIndicator(this.controller); + const VideoProgressIndicator(this.controller, {Key? key}) : super(key: key); /// The [MiniController] that actually associates a video with this /// widget. final MiniController controller; @override - _VideoProgressIndicatorState createState() => _VideoProgressIndicatorState(); + State createState() => _VideoProgressIndicatorState(); } class _VideoProgressIndicatorState extends State { @@ -527,11 +527,11 @@ class _VideoProgressIndicatorState extends State { ); } return _VideoScrubber( + controller: controller, child: Padding( padding: const EdgeInsets.only(top: 5.0), child: progressIndicator, ), - controller: controller, ); } } diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index b3cc69eca958..380d8343c024 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.3 +version: 2.3.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 00788c4386fe..094ffda207c5 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.0.9 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.0.8 diff --git a/packages/video_player/video_player_web/example/lib/main.dart b/packages/video_player/video_player_web/example/lib/main.dart index 341913a18490..87422953de6a 100644 --- a/packages/video_player/video_player_web/example/lib/main.dart +++ b/packages/video_player/video_player_web/example/lib/main.dart @@ -5,13 +5,16 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 064517e1f264..7af0dc46dde8 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/plugins/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.0.8 +version: 2.0.9 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 7a56f3f176d0..fa47a6cc3143 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 3.0.3 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 3.0.2 @@ -11,7 +13,7 @@ * Removes a duplicate Android-specific integration test. * Fixes an integration test race condition. -* Fixes comments (accidentially mixed // with ///). +* Fixes comments (accidentally mixed // with ///). ## 3.0.0 diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index ba321264ee1e..cc001f336849 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -1306,7 +1306,8 @@ Future _runJavascriptReturningResult( class ResizableWebView extends StatefulWidget { const ResizableWebView( - {required this.onResize, required this.onPageFinished}); + {Key? key, required this.onResize, required this.onPageFinished}) + : super(key: key); final JavascriptMessageHandler onResize; final VoidCallback onPageFinished; diff --git a/packages/webview_flutter/webview_flutter/example/lib/main.dart b/packages/webview_flutter/webview_flutter/example/lib/main.dart index 51080be01df0..3d8731127970 100644 --- a/packages/webview_flutter/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter/example/lib/main.dart @@ -71,12 +71,12 @@ const String kTransparentBackgroundPage = ''' '''; class WebViewExample extends StatefulWidget { - const WebViewExample({this.cookieManager}); + const WebViewExample({Key? key, this.cookieManager}) : super(key: key); final CookieManager? cookieManager; @override - _WebViewExampleState createState() => _WebViewExampleState(); + State createState() => _WebViewExampleState(); } class _WebViewExampleState extends State { @@ -190,8 +190,9 @@ enum MenuOptions { } class SampleMenu extends StatelessWidget { - SampleMenu(this.controller, CookieManager? cookieManager) - : cookieManager = cookieManager ?? CookieManager(); + SampleMenu(this.controller, CookieManager? cookieManager, {Key? key}) + : cookieManager = cookieManager ?? CookieManager(), + super(key: key); final Future controller; late final CookieManager cookieManager; @@ -250,8 +251,8 @@ class SampleMenu extends StatelessWidget { itemBuilder: (BuildContext context) => >[ PopupMenuItem( value: MenuOptions.showUserAgent, - child: const Text('Show user agent'), enabled: controller.hasData, + child: const Text('Show user agent'), ), const PopupMenuItem( value: MenuOptions.listCookies, @@ -443,8 +444,9 @@ class SampleMenu extends StatelessWidget { } class NavigationControls extends StatelessWidget { - const NavigationControls(this._webViewControllerFuture) - : assert(_webViewControllerFuture != null); + const NavigationControls(this._webViewControllerFuture, {Key? key}) + : assert(_webViewControllerFuture != null), + super(key: key); final Future _webViewControllerFuture; diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 10350984ce9a..a48f6f912c2d 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 3.0.2 +version: 3.0.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index edaa0883713f..4a451442f6cc 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.8.7 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.8.6 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 51e09912da23..4c06fa6b3c18 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 @@ -1446,9 +1446,10 @@ Future _runJavaScriptReturningResult( class ResizableWebView extends StatefulWidget { const ResizableWebView({ + Key? key, required this.onResize, required this.onPageFinished, - }); + }) : super(key: key); final JavascriptMessageHandler onResize; final VoidCallback onPageFinished; 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 5d19ca71ac84..349a64916e8b 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -247,8 +247,8 @@ class _SampleMenu extends StatelessWidget { itemBuilder: (BuildContext context) => >[ PopupMenuItem<_MenuOptions>( value: _MenuOptions.showUserAgent, - child: const Text('Show user agent'), enabled: controller.hasData, + child: const Text('Show user agent'), ), const PopupMenuItem<_MenuOptions>( value: _MenuOptions.listCookies, diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart index 91ea66376904..56745314d92b 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart @@ -250,7 +250,7 @@ class WebView extends StatefulWidget { final Color? backgroundColor; @override - _WebViewState createState() => _WebViewState(); + State createState() => _WebViewState(); } class _WebViewState extends State { diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart index 28d169c9cb94..f1b130c7e365 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart @@ -15,6 +15,7 @@ import 'src/android_webview.dart' as android_webview; class WebViewAndroidWidget extends StatefulWidget { /// Constructs a [WebViewAndroidWidget]. const WebViewAndroidWidget({ + Key? key, required this.creationParams, required this.useHybridComposition, required this.callbacksHandler, @@ -24,7 +25,7 @@ class WebViewAndroidWidget extends StatefulWidget { @visibleForTesting this.flutterAssetManager = const android_webview.FlutterAssetManager(), @visibleForTesting this.webStorage, - }); + }) : super(key: key); /// Initial parameters used to setup the WebView. final CreationParams creationParams; diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 9a7c48a4dcd8..407887f2ba95 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/plugins/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: 2.8.6 +version: 2.8.7 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index 9f7ebe368941..b7254e1a0a7a 100644 --- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -1,7 +1,9 @@ -## NEXT +## 0.1.0+2 * Removes unnecessary imports. * Fixes unit tests to run on latest `master` version of Flutter. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 0.1.0+1 diff --git a/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart index 8cd74f660f58..ffd3367d33f4 100644 --- a/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_web/example/lib/web_view.dart @@ -46,7 +46,7 @@ class WebView extends StatefulWidget { final String? initialUrl; @override - _WebViewState createState() => _WebViewState(); + State createState() => _WebViewState(); } class _WebViewState extends State { diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml index bd154387097e..35a7b74a764c 100644 --- a/packages/webview_flutter/webview_flutter_web/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_web description: A Flutter plugin that provides a WebView widget on web. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 0.1.0+1 +version: 0.1.0+2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index f042dd081475..6db769b0d922 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 2.7.4 * Removes unnecessary imports. +* Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors + lint warnings. ## 2.7.3 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 ceff62e3d5e8..aa376f8358e9 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 @@ -1181,7 +1181,8 @@ Future _getUserAgent(WebViewController controller) async { class ResizableWebView extends StatefulWidget { const ResizableWebView( - {required this.onResize, required this.onPageFinished}); + {Key? key, required this.onResize, required this.onPageFinished}) + : super(key: key); final JavascriptMessageHandler onResize; final VoidCallback onPageFinished; 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 e8b86d9c5773..7b30923e1e54 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -232,8 +232,8 @@ class _SampleMenu extends StatelessWidget { itemBuilder: (BuildContext context) => >[ PopupMenuItem<_MenuOptions>( value: _MenuOptions.showUserAgent, - child: const Text('Show user agent'), enabled: controller.hasData, + child: const Text('Show user agent'), ), const PopupMenuItem<_MenuOptions>( value: _MenuOptions.listCookies, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart index 4d479f943d62..c44c4e743669 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart @@ -240,7 +240,7 @@ class WebView extends StatefulWidget { final Color? backgroundColor; @override - _WebViewState createState() => _WebViewState(); + State createState() => _WebViewState(); } class _WebViewState extends State { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 012cd221599b..8c37112d7a24 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -18,13 +18,14 @@ import 'web_kit/web_kit.dart'; class WebKitWebViewWidget extends StatefulWidget { /// Constructs a [WebKitWebViewWidget]. const WebKitWebViewWidget({ + Key? key, required this.creationParams, required this.callbacksHandler, required this.javascriptChannelRegistry, required this.onBuildWidget, this.configuration, @visibleForTesting this.webViewProxy = const WebViewWidgetProxy(), - }); + }) : super(key: key); /// The initial parameters used to setup the WebView. final CreationParams creationParams; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index f4e72b8f14eb..365e64720d4f 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/plugins/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: 2.7.3 +version: 2.7.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index 5c38bd5f7033..b0a8990e1300 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -319,14 +319,14 @@ String _pluginPlatformSection( return entry; } -typedef _ErrorHandler = void Function(Error error); +typedef ErrorHandler = void Function(Error error); /// Run the command [runner] with the given [args] and return /// what was printed. /// A custom [errorHandler] can be used to handle the runner error as desired without throwing. Future> runCapturingPrint( CommandRunner runner, List args, - {_ErrorHandler? errorHandler}) async { + {ErrorHandler? errorHandler}) async { final List prints = []; final ZoneSpecification spec = ZoneSpecification( print: (_, __, ___, String message) { From 329f5efe63bfdd8061f9f0580008d3e79351ad5d Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 10 May 2022 13:12:14 -0700 Subject: [PATCH 290/844] [webview_flutter_wkwebview] The rest of the Objective-C HostApi methods (#5604) --- .../ios/Runner.xcodeproj/project.pbxproj | 46 +++++- .../ios/RunnerTests/FWFDataConvertersTests.m | 23 ++- .../FWFHTTPCookieStoreHostApiTests.m | 55 +++++++ .../FWFNavigationDelegateHostApiTests.m | 28 ++++ .../ios/RunnerTests/FWFObjectHostApiTests.m | 82 +++++++++++ .../RunnerTests/FWFPreferencesHostApiTests.m | 43 ++++++ .../FWFScriptMessageHandlerHostApiTests.m | 29 ++++ .../RunnerTests/FWFScrollViewHostApiTests.m | 64 ++++++++ .../RunnerTests/FWFUIDelegateHostApiTests.m | 27 ++++ .../ios/RunnerTests/FWFUIViewHostApiTests.m | 49 +++++++ .../FWFUserContentControllerHostApiTests.m | 127 ++++++++++++++++ .../FWFWebViewConfigurationHostApiTests.m | 88 +++++++++++ .../ios/RunnerTests/FWFWebViewHostApiTests.m | 14 ++ .../FWFWebsiteDataStoreHostApiTests.m | 75 ++++++++++ .../ios/Classes/FWFDataConverters.h | 73 ++++++++- .../ios/Classes/FWFDataConverters.m | 130 +++++++++++++++++ .../ios/Classes/FWFGeneratedWebKitApis.h | 70 +++++---- .../ios/Classes/FWFGeneratedWebKitApis.m | 138 ++++++++---------- .../ios/Classes/FWFHTTPCookieStoreHostApi.h | 22 +++ .../ios/Classes/FWFHTTPCookieStoreHostApi.m | 60 ++++++++ .../Classes/FWFNavigationDelegateHostApi.h | 28 ++++ .../Classes/FWFNavigationDelegateHostApi.m | 40 +++++ .../ios/Classes/FWFObjectHostApi.h | 21 +++ .../ios/Classes/FWFObjectHostApi.m | 54 +++++++ .../ios/Classes/FWFPreferencesHostApi.h | 22 +++ .../ios/Classes/FWFPreferencesHostApi.m | 44 ++++++ .../Classes/FWFScriptMessageHandlerHostApi.h | 28 ++++ .../Classes/FWFScriptMessageHandlerHostApi.m | 37 +++++ .../ios/Classes/FWFScrollViewHostApi.h | 22 +++ .../ios/Classes/FWFScrollViewHostApi.m | 57 ++++++++ .../ios/Classes/FWFUIDelegateHostApi.h | 28 ++++ .../ios/Classes/FWFUIDelegateHostApi.m | 33 +++++ .../ios/Classes/FWFUIViewHostApi.h | 21 +++ .../ios/Classes/FWFUIViewHostApi.m | 43 ++++++ .../Classes/FWFUserContentControllerHostApi.h | 22 +++ .../Classes/FWFUserContentControllerHostApi.m | 80 ++++++++++ .../Classes/FWFWebViewConfigurationHostApi.h | 22 +++ .../Classes/FWFWebViewConfigurationHostApi.m | 87 +++++++++++ .../ios/Classes/FWFWebsiteDataStoreHostApi.h | 22 +++ .../ios/Classes/FWFWebsiteDataStoreHostApi.m | 67 +++++++++ .../ios/Classes/webview-umbrella.h | 11 ++ .../lib/src/common/web_kit.pigeon.dart | 122 ++++++---------- .../lib/src/foundation/foundation.dart | 3 + .../lib/src/web_kit/web_kit.dart | 14 +- .../lib/src/web_kit/web_kit_api_impls.dart | 61 ++++---- .../lib/src/web_kit_cookie_manager.dart | 2 +- .../lib/src/web_kit_webview_widget.dart | 18 ++- .../pigeons/web_kit.dart | 70 +++++---- .../test/src/common/test_web_kit.pigeon.dart | 51 ++----- .../test/src/ui_kit/ui_kit_test.mocks.dart | 4 - .../test/src/web_kit/web_kit_test.dart | 14 +- .../test/src/web_kit/web_kit_test.mocks.dart | 27 ++-- .../test/src/web_kit_cookie_manager_test.dart | 4 +- .../web_kit_cookie_manager_test.mocks.dart | 2 +- .../test/src/web_kit_webview_widget_test.dart | 20 +-- .../web_kit_webview_widget_test.mocks.dart | 2 +- 56 files changed, 2115 insertions(+), 331 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFHTTPCookieStoreHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFNavigationDelegateHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFPreferencesHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScrollViewHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIViewHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUserContentControllerHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewConfigurationHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebsiteDataStoreHostApiTests.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.h create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj index c48b313daec2..e7519af18e7c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -14,6 +14,17 @@ 8FA6A87928062CD000A4B183 /* FWFInstanceManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */; }; 8FB79B5328134C3100C101D3 /* FWFWebViewHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */; }; 8FB79B55281B24F600C101D3 /* FWFDataConvertersTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B54281B24F600C101D3 /* FWFDataConvertersTests.m */; }; + 8FB79B672820453400C101D3 /* FWFHTTPCookieStoreHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B662820453400C101D3 /* FWFHTTPCookieStoreHostApiTests.m */; }; + 8FB79B6928204E8700C101D3 /* FWFPreferencesHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B6828204E8700C101D3 /* FWFPreferencesHostApiTests.m */; }; + 8FB79B6B28204EE500C101D3 /* FWFWebsiteDataStoreHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B6A28204EE500C101D3 /* FWFWebsiteDataStoreHostApiTests.m */; }; + 8FB79B6D2820533B00C101D3 /* FWFWebViewConfigurationHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B6C2820533B00C101D3 /* FWFWebViewConfigurationHostApiTests.m */; }; + 8FB79B73282096B500C101D3 /* FWFScriptMessageHandlerHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B72282096B500C101D3 /* FWFScriptMessageHandlerHostApiTests.m */; }; + 8FB79B7928209D1300C101D3 /* FWFUserContentControllerHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B7828209D1300C101D3 /* FWFUserContentControllerHostApiTests.m */; }; + 8FB79B832820A39300C101D3 /* FWFNavigationDelegateHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B822820A39300C101D3 /* FWFNavigationDelegateHostApiTests.m */; }; + 8FB79B852820A3A400C101D3 /* FWFUIDelegateHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B842820A3A400C101D3 /* FWFUIDelegateHostApiTests.m */; }; + 8FB79B8F2820BAB300C101D3 /* FWFScrollViewHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B8E2820BAB300C101D3 /* FWFScrollViewHostApiTests.m */; }; + 8FB79B912820BAC700C101D3 /* FWFUIViewHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B902820BAC700C101D3 /* FWFUIViewHostApiTests.m */; }; + 8FB79B972821985200C101D3 /* FWFObjectHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B962821985200C101D3 /* FWFObjectHostApiTests.m */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -73,6 +84,17 @@ 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFInstanceManagerTests.m; sourceTree = ""; }; 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFWebViewHostApiTests.m; sourceTree = ""; }; 8FB79B54281B24F600C101D3 /* FWFDataConvertersTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFDataConvertersTests.m; sourceTree = ""; }; + 8FB79B662820453400C101D3 /* FWFHTTPCookieStoreHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFHTTPCookieStoreHostApiTests.m; sourceTree = ""; }; + 8FB79B6828204E8700C101D3 /* FWFPreferencesHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFPreferencesHostApiTests.m; sourceTree = ""; }; + 8FB79B6A28204EE500C101D3 /* FWFWebsiteDataStoreHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFWebsiteDataStoreHostApiTests.m; sourceTree = ""; }; + 8FB79B6C2820533B00C101D3 /* FWFWebViewConfigurationHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFWebViewConfigurationHostApiTests.m; sourceTree = ""; }; + 8FB79B72282096B500C101D3 /* FWFScriptMessageHandlerHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFScriptMessageHandlerHostApiTests.m; sourceTree = ""; }; + 8FB79B7828209D1300C101D3 /* FWFUserContentControllerHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFUserContentControllerHostApiTests.m; sourceTree = ""; }; + 8FB79B822820A39300C101D3 /* FWFNavigationDelegateHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFNavigationDelegateHostApiTests.m; sourceTree = ""; }; + 8FB79B842820A3A400C101D3 /* FWFUIDelegateHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFUIDelegateHostApiTests.m; sourceTree = ""; }; + 8FB79B8E2820BAB300C101D3 /* FWFScrollViewHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFScrollViewHostApiTests.m; sourceTree = ""; }; + 8FB79B902820BAC700C101D3 /* FWFUIViewHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFUIViewHostApiTests.m; sourceTree = ""; }; + 8FB79B962821985200C101D3 /* FWFObjectHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFObjectHostApiTests.m; 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; }; @@ -135,6 +157,17 @@ 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */, 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */, 8FB79B54281B24F600C101D3 /* FWFDataConvertersTests.m */, + 8FB79B662820453400C101D3 /* FWFHTTPCookieStoreHostApiTests.m */, + 8FB79B6828204E8700C101D3 /* FWFPreferencesHostApiTests.m */, + 8FB79B6A28204EE500C101D3 /* FWFWebsiteDataStoreHostApiTests.m */, + 8FB79B6C2820533B00C101D3 /* FWFWebViewConfigurationHostApiTests.m */, + 8FB79B72282096B500C101D3 /* FWFScriptMessageHandlerHostApiTests.m */, + 8FB79B7828209D1300C101D3 /* FWFUserContentControllerHostApiTests.m */, + 8FB79B822820A39300C101D3 /* FWFNavigationDelegateHostApiTests.m */, + 8FB79B842820A3A400C101D3 /* FWFUIDelegateHostApiTests.m */, + 8FB79B8E2820BAB300C101D3 /* FWFScrollViewHostApiTests.m */, + 8FB79B902820BAC700C101D3 /* FWFUIViewHostApiTests.m */, + 8FB79B962821985200C101D3 /* FWFObjectHostApiTests.m */, ); path = RunnerTests; sourceTree = ""; @@ -433,11 +466,22 @@ buildActionMask = 2147483647; files = ( 8FA6A87928062CD000A4B183 /* FWFInstanceManagerTests.m in Sources */, + 8FB79B852820A3A400C101D3 /* FWFUIDelegateHostApiTests.m in Sources */, + 8FB79B972821985200C101D3 /* FWFObjectHostApiTests.m in Sources */, + 8FB79B672820453400C101D3 /* FWFHTTPCookieStoreHostApiTests.m in Sources */, 8FB79B5328134C3100C101D3 /* FWFWebViewHostApiTests.m in Sources */, + 8FB79B73282096B500C101D3 /* FWFScriptMessageHandlerHostApiTests.m in Sources */, 334734012669319100DCC49E /* FLTWebViewTests.m in Sources */, + 8FB79B7928209D1300C101D3 /* FWFUserContentControllerHostApiTests.m in Sources */, + 8FB79B6B28204EE500C101D3 /* FWFWebsiteDataStoreHostApiTests.m in Sources */, + 8FB79B8F2820BAB300C101D3 /* FWFScrollViewHostApiTests.m in Sources */, + 8FB79B912820BAC700C101D3 /* FWFUIViewHostApiTests.m in Sources */, 334734022669319400DCC49E /* FLTWKNavigationDelegateTests.m in Sources */, 8FB79B55281B24F600C101D3 /* FWFDataConvertersTests.m in Sources */, + 8FB79B6D2820533B00C101D3 /* FWFWebViewConfigurationHostApiTests.m in Sources */, + 8FB79B832820A39300C101D3 /* FWFNavigationDelegateHostApiTests.m in Sources */, E43693B527512C0F00382F85 /* FLTCookieManagerTests.m in Sources */, + 8FB79B6928204E8700C101D3 /* FWFPreferencesHostApiTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 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 55952373998d..57d90f6c6814 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 @@ -10,7 +10,7 @@ @interface FWFDataConvertersTests : XCTestCase @end @implementation FWFDataConvertersTests -- (void)testFNSURLRequestFromRequestData { +- (void)testFWFNSURLRequestFromRequestData { NSURLRequest *request = FWFNSURLRequestFromRequestData([FWFNSUrlRequestData makeWithUrl:@"https://flutter.dev" httpMethod:@"post" @@ -22,4 +22,25 @@ - (void)testFNSURLRequestFromRequestData { XCTAssertEqualObjects(request.HTTPBody, [NSData data]); XCTAssertEqualObjects(request.allHTTPHeaderFields, @{@"a" : @"header"}); } + +- (void)testFWFNSHTTPCookieFromCookieData { + NSHTTPCookie *cookie = FWFNSHTTPCookieFromCookieData([FWFNSHttpCookieData + makeWithPropertyKeys:@[ [FWFNSHttpCookiePropertyKeyEnumData + makeWithValue:FWFNSHttpCookiePropertyKeyEnumName] ] + propertyValues:@[ @"cookieName" ]]); + XCTAssertEqualObjects(cookie, + [NSHTTPCookie cookieWithProperties:@{NSHTTPCookieName : @"cookieName"}]); +} + +- (void)testFWFWKUserScriptFromScriptData { + WKUserScript *userScript = FWFWKUserScriptFromScriptData([FWFWKUserScriptData + makeWithSource:@"mySource" + injectionTime:[FWFWKUserScriptInjectionTimeEnumData + makeWithValue:FWFWKUserScriptInjectionTimeEnumAtDocumentStart] + isMainFrameOnly:@NO]); + + XCTAssertEqualObjects(userScript.source, @"mySource"); + XCTAssertEqual(userScript.injectionTime, WKUserScriptInjectionTimeAtDocumentStart); + XCTAssertEqual(userScript.isForMainFrameOnly, NO); +} @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFHTTPCookieStoreHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFHTTPCookieStoreHostApiTests.m new file mode 100644 index 000000000000..315640a99247 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFHTTPCookieStoreHostApiTests.m @@ -0,0 +1,55 @@ +// 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 XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFHTTPCookieStoreHostApiTests : XCTestCase +@end + +@implementation FWFHTTPCookieStoreHostApiTests +- (void)testCreateFromWebsiteDataStoreWithIdentifier API_AVAILABLE(ios(11.0)) { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFHTTPCookieStoreHostApiImpl *hostApi = + [[FWFHTTPCookieStoreHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + WKWebsiteDataStore *mockDataStore = OCMClassMock([WKWebsiteDataStore class]); + OCMStub([mockDataStore httpCookieStore]).andReturn(OCMClassMock([WKHTTPCookieStore class])); + [instanceManager addInstance:mockDataStore withIdentifier:0]; + + FlutterError *error; + [hostApi createFromWebsiteDataStoreWithIdentifier:@1 dataStoreIdentifier:@0 error:&error]; + WKHTTPCookieStore *cookieStore = (WKHTTPCookieStore *)[instanceManager instanceForIdentifier:1]; + XCTAssertTrue([cookieStore isKindOfClass:[WKHTTPCookieStore class]]); + XCTAssertNil(error); +} + +- (void)testSetCookie API_AVAILABLE(ios(11.0)) { + WKHTTPCookieStore *mockHttpCookieStore = OCMClassMock([WKHTTPCookieStore class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockHttpCookieStore withIdentifier:0]; + + FWFHTTPCookieStoreHostApiImpl *hostApi = + [[FWFHTTPCookieStoreHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FWFNSHttpCookieData *cookieData = [FWFNSHttpCookieData + makeWithPropertyKeys:@[ [FWFNSHttpCookiePropertyKeyEnumData + makeWithValue:FWFNSHttpCookiePropertyKeyEnumName] ] + propertyValues:@[ @"hello" ]]; + FlutterError *__block blockError; + [hostApi setCookieForStoreWithIdentifier:@0 + cookie:cookieData + completion:^(FlutterError *error) { + blockError = error; + }]; + OCMVerify([mockHttpCookieStore + setCookie:[NSHTTPCookie cookieWithProperties:@{NSHTTPCookieName : @"hello"}] + completionHandler:OCMOCK_ANY]); + XCTAssertNil(blockError); +} +@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 new file mode 100644 index 000000000000..02e473f8b795 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFNavigationDelegateHostApiTests.m @@ -0,0 +1,28 @@ +// 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 XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFNavigationDelegateHostApiTests : XCTestCase +@end + +@implementation FWFNavigationDelegateHostApiTests +- (void)testCreateWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFNavigationDelegateHostApiImpl *hostApi = + [[FWFNavigationDelegateHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi createWithIdentifier:@0 error:&error]; + FWFNavigationDelegate *navigationDelegate = + (FWFNavigationDelegate *)[instanceManager instanceForIdentifier:0]; + + XCTAssertTrue([navigationDelegate conformsToProtocol:@protocol(WKNavigationDelegate)]); + XCTAssertNil(error); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m new file mode 100644 index 000000000000..6886c2600e13 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m @@ -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 Flutter; +@import XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFObjectHostApiTests : XCTestCase +@end + +@implementation FWFObjectHostApiTests +- (void)testAddObserver { + NSObject *mockObject = OCMClassMock([NSObject class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockObject withIdentifier:0]; + + FWFObjectHostApiImpl *hostApi = + [[FWFObjectHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + NSObject *observerObject = [[NSObject alloc] init]; + [instanceManager addInstance:observerObject withIdentifier:1]; + + FlutterError *error; + [hostApi + addObserverForObjectWithIdentifier:@0 + observerIdentifier:@1 + keyPath:@"myKey" + options:@[ + [FWFNSKeyValueObservingOptionsEnumData + makeWithValue:FWFNSKeyValueObservingOptionsEnumOldValue], + [FWFNSKeyValueObservingOptionsEnumData + makeWithValue:FWFNSKeyValueObservingOptionsEnumNewValue] + ] + error:&error]; + + OCMVerify([mockObject addObserver:observerObject + forKeyPath:@"myKey" + options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) + context:nil]); + XCTAssertNil(error); +} + +- (void)testRemoveObserver { + NSObject *mockObject = OCMClassMock([NSObject class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockObject withIdentifier:0]; + + FWFObjectHostApiImpl *hostApi = + [[FWFObjectHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + NSObject *observerObject = [[NSObject alloc] init]; + [instanceManager addInstance:observerObject withIdentifier:1]; + + FlutterError *error; + [hostApi removeObserverForObjectWithIdentifier:@0 + observerIdentifier:@1 + keyPath:@"myKey" + error:&error]; + OCMVerify([mockObject removeObserver:observerObject forKeyPath:@"myKey"]); + XCTAssertNil(error); +} + +- (void)testDispose { + NSObject *object = [[NSObject alloc] init]; + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:object withIdentifier:0]; + + FWFObjectHostApiImpl *hostApi = + [[FWFObjectHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi disposeObjectWithIdentifier:@0 error:&error]; + XCTAssertEqual([instanceManager identifierForInstance:object], NSNotFound); + XCTAssertNil(error); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFPreferencesHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFPreferencesHostApiTests.m new file mode 100644 index 000000000000..1837a9373930 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFPreferencesHostApiTests.m @@ -0,0 +1,43 @@ +// 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 XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFPreferencesHostApiTests : XCTestCase +@end + +@implementation FWFPreferencesHostApiTests +- (void)testCreateFromWebViewConfigurationWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFPreferencesHostApiImpl *hostApi = + [[FWFPreferencesHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + [instanceManager addInstance:[[WKWebViewConfiguration alloc] init] withIdentifier:0]; + + FlutterError *error; + [hostApi createFromWebViewConfigurationWithIdentifier:@1 configurationIdentifier:@0 error:&error]; + WKPreferences *preferences = (WKPreferences *)[instanceManager instanceForIdentifier:1]; + XCTAssertTrue([preferences isKindOfClass:[WKPreferences class]]); + XCTAssertNil(error); +} + +- (void)testSetJavaScriptEnabled { + WKPreferences *mockPreferences = OCMClassMock([WKPreferences class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockPreferences withIdentifier:0]; + + FWFPreferencesHostApiImpl *hostApi = + [[FWFPreferencesHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi setJavaScriptEnabledForPreferencesWithIdentifier:@0 isEnabled:@YES error:&error]; + OCMVerify([mockPreferences setJavaScriptEnabled:YES]); + XCTAssertNil(error); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m new file mode 100644 index 000000000000..cb8348fc4702 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m @@ -0,0 +1,29 @@ +// 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 XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFScriptMessageHandlerHostApiTests : XCTestCase +@end + +@implementation FWFScriptMessageHandlerHostApiTests +- (void)testCreateWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFScriptMessageHandlerHostApiImpl *hostApi = + [[FWFScriptMessageHandlerHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi createWithIdentifier:@0 error:&error]; + + FWFScriptMessageHandler *scriptMessageHandler = + (FWFScriptMessageHandler *)[instanceManager instanceForIdentifier:0]; + + XCTAssertTrue([scriptMessageHandler conformsToProtocol:@protocol(WKScriptMessageHandler)]); + XCTAssertNil(error); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScrollViewHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScrollViewHostApiTests.m new file mode 100644 index 000000000000..87d17119e5e7 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScrollViewHostApiTests.m @@ -0,0 +1,64 @@ +// 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 XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFScrollViewHostApiTests : XCTestCase +@end + +@implementation FWFScrollViewHostApiTests +- (void)testGetContentOffset { + UIScrollView *mockScrollView = OCMClassMock([UIScrollView class]); + OCMStub([mockScrollView contentOffset]).andReturn(CGPointMake(1.0, 2.0)); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockScrollView withIdentifier:0]; + + FWFScrollViewHostApiImpl *hostApi = + [[FWFScrollViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + NSArray *expectedValue = @[ @1.0, @2.0 ]; + XCTAssertEqualObjects([hostApi contentOffsetForScrollViewWithIdentifier:@0 error:&error], + expectedValue); + XCTAssertNil(error); +} + +- (void)testScrollBy { + UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + scrollView.contentOffset = CGPointMake(1, 2); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:scrollView withIdentifier:0]; + + FWFScrollViewHostApiImpl *hostApi = + [[FWFScrollViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi scrollByForScrollViewWithIdentifier:@0 x:@1 y:@2 error:&error]; + XCTAssertEqual(scrollView.contentOffset.x, 2); + XCTAssertEqual(scrollView.contentOffset.y, 4); + XCTAssertNil(error); +} + +- (void)testSetContentOffset { + UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:scrollView withIdentifier:0]; + + FWFScrollViewHostApiImpl *hostApi = + [[FWFScrollViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi setContentOffsetForScrollViewWithIdentifier:@0 toX:@1 y:@2 error:&error]; + XCTAssertEqual(scrollView.contentOffset.x, 1); + XCTAssertEqual(scrollView.contentOffset.y, 2); + XCTAssertNil(error); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m new file mode 100644 index 000000000000..2f7838be5aa8 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m @@ -0,0 +1,27 @@ +// 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 XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFUIDelegateHostApiTests : XCTestCase +@end + +@implementation FWFUIDelegateHostApiTests +- (void)testCreateWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFUIDelegateHostApiImpl *hostApi = + [[FWFUIDelegateHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi createWithIdentifier:@0 error:&error]; + FWFUIDelegate *delegate = (FWFUIDelegate *)[instanceManager instanceForIdentifier:0]; + + XCTAssertTrue([delegate conformsToProtocol:@protocol(WKUIDelegate)]); + XCTAssertNil(error); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIViewHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIViewHostApiTests.m new file mode 100644 index 000000000000..8c7c9c9b45b1 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIViewHostApiTests.m @@ -0,0 +1,49 @@ +// 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 XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFUIViewHostApiTests : XCTestCase +@end + +@implementation FWFUIViewHostApiTests +- (void)testSetBackgroundColor { + UIView *mockUIView = OCMClassMock([UIView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockUIView withIdentifier:0]; + + FWFUIViewHostApiImpl *hostApi = + [[FWFUIViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi setBackgroundColorForViewWithIdentifier:@0 toValue:@123 error:&error]; + + OCMVerify([mockUIView setBackgroundColor:[UIColor colorWithRed:(123 >> 16 & 0xff) / 255.0 + green:(123 >> 8 & 0xff) / 255.0 + blue:(123 & 0xff) / 255.0 + alpha:(123 >> 24 & 0xff) / 255.0]]); + XCTAssertNil(error); +} + +- (void)testSetOpaque { + UIView *mockUIView = OCMClassMock([UIView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockUIView withIdentifier:0]; + + FWFUIViewHostApiImpl *hostApi = + [[FWFUIViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi setOpaqueForViewWithIdentifier:@0 isOpaque:@YES error:&error]; + OCMVerify([mockUIView setOpaque:YES]); + XCTAssertNil(error); +} + +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUserContentControllerHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUserContentControllerHostApiTests.m new file mode 100644 index 000000000000..d70341e87890 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUserContentControllerHostApiTests.m @@ -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 Flutter; +@import XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFUserContentControllerHostApiTests : XCTestCase +@end + +@implementation FWFUserContentControllerHostApiTests +- (void)testCreateFromWebViewConfigurationWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFUserContentControllerHostApiImpl *hostApi = + [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + [instanceManager addInstance:[[WKWebViewConfiguration alloc] init] withIdentifier:0]; + + FlutterError *error; + [hostApi createFromWebViewConfigurationWithIdentifier:@1 configurationIdentifier:@0 error:&error]; + WKUserContentController *userContentController = + (WKUserContentController *)[instanceManager instanceForIdentifier:1]; + XCTAssertTrue([userContentController isKindOfClass:[WKUserContentController class]]); + XCTAssertNil(error); +} + +- (void)testAddScriptMessageHandler { + WKUserContentController *mockUserContentController = + OCMClassMock([WKUserContentController class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockUserContentController withIdentifier:0]; + + FWFUserContentControllerHostApiImpl *hostApi = + [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + id mockMessageHandler = + OCMProtocolMock(@protocol(WKScriptMessageHandler)); + [instanceManager addInstance:mockMessageHandler withIdentifier:1]; + + FlutterError *error; + [hostApi addScriptMessageHandlerForControllerWithIdentifier:@0 + handlerIdentifier:@1 + ofName:@"apple" + error:&error]; + OCMVerify([mockUserContentController addScriptMessageHandler:mockMessageHandler name:@"apple"]); + XCTAssertNil(error); +} + +- (void)testRemoveScriptMessageHandler { + WKUserContentController *mockUserContentController = + OCMClassMock([WKUserContentController class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockUserContentController withIdentifier:0]; + + FWFUserContentControllerHostApiImpl *hostApi = + [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi removeScriptMessageHandlerForControllerWithIdentifier:@0 name:@"apple" error:&error]; + OCMVerify([mockUserContentController removeScriptMessageHandlerForName:@"apple"]); + XCTAssertNil(error); +} + +- (void)testRemoveAllScriptMessageHandlers API_AVAILABLE(ios(14.0)) { + WKUserContentController *mockUserContentController = + OCMClassMock([WKUserContentController class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockUserContentController withIdentifier:0]; + + FWFUserContentControllerHostApiImpl *hostApi = + [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi removeAllScriptMessageHandlersForControllerWithIdentifier:@0 error:&error]; + OCMVerify([mockUserContentController removeAllScriptMessageHandlers]); + XCTAssertNil(error); +} + +- (void)testAddUserScript { + WKUserContentController *mockUserContentController = + OCMClassMock([WKUserContentController class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockUserContentController withIdentifier:0]; + + FWFUserContentControllerHostApiImpl *hostApi = + [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi + addUserScriptForControllerWithIdentifier:@0 + userScript: + [FWFWKUserScriptData + makeWithSource:@"runAScript" + injectionTime: + [FWFWKUserScriptInjectionTimeEnumData + makeWithValue: + FWFWKUserScriptInjectionTimeEnumAtDocumentEnd] + isMainFrameOnly:@YES] + error:&error]; + + OCMVerify([mockUserContentController addUserScript:[OCMArg isKindOfClass:[WKUserScript class]]]); + XCTAssertNil(error); +} + +- (void)testRemoveAllUserScripts { + WKUserContentController *mockUserContentController = + OCMClassMock([WKUserContentController class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockUserContentController withIdentifier:0]; + + FWFUserContentControllerHostApiImpl *hostApi = + [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi removeAllUserScriptsForControllerWithIdentifier:@0 error:&error]; + OCMVerify([mockUserContentController removeAllUserScripts]); + XCTAssertNil(error); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewConfigurationHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewConfigurationHostApiTests.m new file mode 100644 index 000000000000..dab10799891b --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewConfigurationHostApiTests.m @@ -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 Flutter; +@import XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFWebViewConfigurationHostApiTests : XCTestCase +@end + +@implementation FWFWebViewConfigurationHostApiTests +- (void)testCreateWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFWebViewConfigurationHostApiImpl *hostApi = + [[FWFWebViewConfigurationHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi createWithIdentifier:@0 error:&error]; + WKWebViewConfiguration *configuration = + (WKWebViewConfiguration *)[instanceManager instanceForIdentifier:0]; + XCTAssertTrue([configuration isKindOfClass:[WKWebViewConfiguration class]]); + XCTAssertNil(error); +} + +- (void)testCreateFromWebViewWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFWebViewConfigurationHostApiImpl *hostApi = + [[FWFWebViewConfigurationHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + WKWebView *mockWebView = OCMClassMock([WKWebView class]); + OCMStub([mockWebView configuration]).andReturn(OCMClassMock([WKWebViewConfiguration class])); + [instanceManager addInstance:mockWebView withIdentifier:0]; + + FlutterError *error; + [hostApi createFromWebViewWithIdentifier:@1 webViewIdentifier:@0 error:&error]; + WKWebViewConfiguration *configuration = + (WKWebViewConfiguration *)[instanceManager instanceForIdentifier:1]; + XCTAssertTrue([configuration isKindOfClass:[WKWebViewConfiguration class]]); + XCTAssertNil(error); +} + +- (void)testSetAllowsInlineMediaPlayback { + WKWebViewConfiguration *mockWebViewConfiguration = OCMClassMock([WKWebViewConfiguration class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebViewConfiguration withIdentifier:0]; + + FWFWebViewConfigurationHostApiImpl *hostApi = + [[FWFWebViewConfigurationHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:@0 + isAllowed:@NO + error:&error]; + OCMVerify([mockWebViewConfiguration setAllowsInlineMediaPlayback:NO]); + XCTAssertNil(error); +} + +- (void)testSetMediaTypesRequiringUserActionForPlayback { + WKWebViewConfiguration *mockWebViewConfiguration = OCMClassMock([WKWebViewConfiguration class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebViewConfiguration withIdentifier:0]; + + FWFWebViewConfigurationHostApiImpl *hostApi = + [[FWFWebViewConfigurationHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi + setMediaTypesRequiresUserActionForConfigurationWithIdentifier:@0 + forTypes:@[ + [FWFWKAudiovisualMediaTypeEnumData + makeWithValue: + FWFWKAudiovisualMediaTypeEnumAudio], + [FWFWKAudiovisualMediaTypeEnumData + makeWithValue: + FWFWKAudiovisualMediaTypeEnumVideo] + ] + error:&error]; + OCMVerify([mockWebViewConfiguration + setMediaTypesRequiringUserActionForPlayback:(WKAudiovisualMediaTypeAudio | + WKAudiovisualMediaTypeVideo)]); + XCTAssertNil(error); +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m index dc0c1ce593d2..87960c07ee5e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m @@ -12,6 +12,20 @@ @interface FWFWebViewHostApiTests : XCTestCase @end @implementation FWFWebViewHostApiTests +- (void)testCreateWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFWebViewHostApiImpl *hostApi = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + [instanceManager addInstance:[[WKWebViewConfiguration alloc] init] withIdentifier:0]; + + FlutterError *error; + [hostApi createWithIdentifier:@1 configurationIdentifier:@0 error:&error]; + WKWebView *webView = (WKWebView *)[instanceManager instanceForIdentifier:1]; + XCTAssertTrue([webView isKindOfClass:[WKWebView class]]); + XCTAssertNil(error); +} + - (void)testLoadRequest { FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebsiteDataStoreHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebsiteDataStoreHostApiTests.m new file mode 100644 index 000000000000..c754f78551b9 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebsiteDataStoreHostApiTests.m @@ -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. + +@import Flutter; +@import XCTest; +@import webview_flutter_wkwebview; + +#import + +@interface FWFWebsiteDataStoreHostApiTests : XCTestCase +@end + +@implementation FWFWebsiteDataStoreHostApiTests +- (void)testCreateFromWebViewConfigurationWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFWebsiteDataStoreHostApiImpl *hostApi = + [[FWFWebsiteDataStoreHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + [instanceManager addInstance:[[WKWebViewConfiguration alloc] init] withIdentifier:0]; + + FlutterError *error; + [hostApi createFromWebViewConfigurationWithIdentifier:@1 configurationIdentifier:@0 error:&error]; + WKWebsiteDataStore *dataStore = (WKWebsiteDataStore *)[instanceManager instanceForIdentifier:1]; + XCTAssertTrue([dataStore isKindOfClass:[WKWebsiteDataStore class]]); + XCTAssertNil(error); +} + +- (void)testCreateDefaultDataStoreWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFWebsiteDataStoreHostApiImpl *hostApi = + [[FWFWebsiteDataStoreHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + FlutterError *error; + [hostApi createDefaultDataStoreWithIdentifier:@0 error:&error]; + WKWebsiteDataStore *dataStore = (WKWebsiteDataStore *)[instanceManager instanceForIdentifier:0]; + XCTAssertEqualObjects(dataStore, [WKWebsiteDataStore defaultDataStore]); + XCTAssertNil(error); +} + +- (void)testRemoveDataOfTypes { + WKWebsiteDataStore *mockWebsiteDataStore = OCMClassMock([WKWebsiteDataStore class]); + + WKWebsiteDataRecord *mockDataRecord = OCMClassMock([WKWebsiteDataRecord class]); + OCMStub([mockWebsiteDataStore + fetchDataRecordsOfTypes:[NSSet setWithObject:WKWebsiteDataTypeLocalStorage] + completionHandler:([OCMArg invokeBlockWithArgs:@[ mockDataRecord ], nil])]); + + OCMStub([mockWebsiteDataStore + removeDataOfTypes:[NSSet setWithObject:WKWebsiteDataTypeLocalStorage] + modifiedSince:[NSDate dateWithTimeIntervalSince1970:45.0] + completionHandler:([OCMArg invokeBlock])]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addInstance:mockWebsiteDataStore withIdentifier:0]; + + FWFWebsiteDataStoreHostApiImpl *hostApi = + [[FWFWebsiteDataStoreHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + NSNumber __block *returnValue; + FlutterError *__block blockError; + [hostApi removeDataFromDataStoreWithIdentifier:@0 + ofTypes:@[ + [FWFWKWebsiteDataTypeEnumData + makeWithValue:FWFWKWebsiteDataTypeEnumLocalStorage] + ] + modifiedSince:@45.0 + completion:^(NSNumber *result, FlutterError *error) { + returnValue = result; + blockError = error; + }]; + XCTAssertEqualObjects(returnValue, @YES); + XCTAssertNil(blockError); +} +@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 e9dbf2d9efa7..4cf24c67ac95 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h @@ -4,6 +4,8 @@ #import "FWFGeneratedWebKitApis.h" +#import + NS_ASSUME_NONNULL_BEGIN /** @@ -13,7 +15,74 @@ NS_ASSUME_NONNULL_BEGIN * * @return An NSURLRequest or nil if data could not be converted. */ -extern NSURLRequest* _Nullable FWFNSURLRequestFromRequestData( - FWFNSUrlRequestData* data); +extern NSURLRequest *_Nullable FWFNSURLRequestFromRequestData(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. + */ +extern NSHTTPCookie *_Nullable FWFNSHTTPCookieFromCookieData(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. + */ +extern NSKeyValueObservingOptions FWFNSKeyValueObservingOptionsFromEnumData( + 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. + */ +extern NSHTTPCookiePropertyKey _Nullable FWFNSHTTPCookiePropertyKeyFromEnumData( + 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. + */ +extern WKUserScript *FWFWKUserScriptFromScriptData(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. + */ +extern WKUserScriptInjectionTime FWFWKUserScriptInjectionTimeFromEnumData( + 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. + */ +API_AVAILABLE(ios(10.0)) +extern WKAudiovisualMediaTypes FWFWKAudiovisualMediaTypeFromEnumData( + 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. + */ +extern NSString *_Nullable FWFWKWebsiteDataTypeFromEnumData(FWFWKWebsiteDataTypeEnumData *data); NS_ASSUME_NONNULL_END 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 1945bc3354f3..a06b3d79b38c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m @@ -23,3 +23,133 @@ return request; } + +extern NSHTTPCookie *_Nullable FWFNSHTTPCookieFromCookieData(FWFNSHttpCookieData *data) { + NSMutableDictionary *properties = [NSMutableDictionary dictionary]; + for (int i = 0; i < data.propertyKeys.count; i++) { + NSHTTPCookiePropertyKey cookieKey = + FWFNSHTTPCookiePropertyKeyFromEnumData(data.propertyKeys[i]); + if (!cookieKey) { + // Some keys aren't supported on all versions, so this ignores keys + // that require a higher version or are unsupported. + continue; + } + [properties setObject:data.propertyValues[i] forKey:cookieKey]; + } + return [NSHTTPCookie cookieWithProperties:properties]; +} + +NSKeyValueObservingOptions FWFNSKeyValueObservingOptionsFromEnumData( + FWFNSKeyValueObservingOptionsEnumData *data) { + switch (data.value) { + case FWFNSKeyValueObservingOptionsEnumNewValue: + return NSKeyValueObservingOptionNew; + case FWFNSKeyValueObservingOptionsEnumOldValue: + return NSKeyValueObservingOptionOld; + case FWFNSKeyValueObservingOptionsEnumInitialValue: + return NSKeyValueObservingOptionInitial; + case FWFNSKeyValueObservingOptionsEnumPriorNotification: + return NSKeyValueObservingOptionPrior; + } + + return -1; +} + +NSHTTPCookiePropertyKey _Nullable FWFNSHTTPCookiePropertyKeyFromEnumData( + FWFNSHttpCookiePropertyKeyEnumData *data) { + switch (data.value) { + case FWFNSHttpCookiePropertyKeyEnumComment: + return NSHTTPCookieComment; + case FWFNSHttpCookiePropertyKeyEnumCommentUrl: + return NSHTTPCookieCommentURL; + case FWFNSHttpCookiePropertyKeyEnumDiscard: + return NSHTTPCookieDiscard; + case FWFNSHttpCookiePropertyKeyEnumDomain: + return NSHTTPCookieDomain; + case FWFNSHttpCookiePropertyKeyEnumExpires: + return NSHTTPCookieExpires; + case FWFNSHttpCookiePropertyKeyEnumMaximumAge: + return NSHTTPCookieMaximumAge; + case FWFNSHttpCookiePropertyKeyEnumName: + return NSHTTPCookieName; + case FWFNSHttpCookiePropertyKeyEnumOriginUrl: + return NSHTTPCookieOriginURL; + case FWFNSHttpCookiePropertyKeyEnumPath: + return NSHTTPCookiePath; + case FWFNSHttpCookiePropertyKeyEnumPort: + return NSHTTPCookiePort; + case FWFNSHttpCookiePropertyKeyEnumSameSitePolicy: + if (@available(iOS 13.0, *)) { + return NSHTTPCookieSameSitePolicy; + } else { + return nil; + } + case FWFNSHttpCookiePropertyKeyEnumSecure: + return NSHTTPCookieSecure; + case FWFNSHttpCookiePropertyKeyEnumValue: + return NSHTTPCookieValue; + case FWFNSHttpCookiePropertyKeyEnumVersion: + return NSHTTPCookieVersion; + } + + return nil; +} + +extern WKUserScript *FWFWKUserScriptFromScriptData(FWFWKUserScriptData *data) { + return [[WKUserScript alloc] + initWithSource:data.source + injectionTime:FWFWKUserScriptInjectionTimeFromEnumData(data.injectionTime) + forMainFrameOnly:data.isMainFrameOnly.boolValue]; +} + +WKUserScriptInjectionTime FWFWKUserScriptInjectionTimeFromEnumData( + FWFWKUserScriptInjectionTimeEnumData *data) { + switch (data.value) { + case FWFWKUserScriptInjectionTimeEnumAtDocumentStart: + return WKUserScriptInjectionTimeAtDocumentStart; + case FWFWKUserScriptInjectionTimeEnumAtDocumentEnd: + return WKUserScriptInjectionTimeAtDocumentEnd; + } + + return -1; +} + +API_AVAILABLE(ios(10.0)) +WKAudiovisualMediaTypes FWFWKAudiovisualMediaTypeFromEnumData( + FWFWKAudiovisualMediaTypeEnumData *data) { + switch (data.value) { + case FWFWKAudiovisualMediaTypeEnumNone: + return WKAudiovisualMediaTypeNone; + case FWFWKAudiovisualMediaTypeEnumAudio: + return WKAudiovisualMediaTypeAudio; + case FWFWKAudiovisualMediaTypeEnumVideo: + return WKAudiovisualMediaTypeVideo; + case FWFWKAudiovisualMediaTypeEnumAll: + return WKAudiovisualMediaTypeAll; + } + + return -1; +} + +NSString *_Nullable FWFWKWebsiteDataTypeFromEnumData(FWFWKWebsiteDataTypeEnumData *data) { + switch (data.value) { + case FWFWKWebsiteDataTypeEnumCookies: + return WKWebsiteDataTypeCookies; + case FWFWKWebsiteDataTypeEnumMemoryCache: + return WKWebsiteDataTypeMemoryCache; + case FWFWKWebsiteDataTypeEnumDiskCache: + return WKWebsiteDataTypeDiskCache; + case FWFWKWebsiteDataTypeEnumOfflineWebApplicationCache: + return WKWebsiteDataTypeOfflineWebApplicationCache; + case FWFWKWebsiteDataTypeEnumLocalStorage: + return WKWebsiteDataTypeLocalStorage; + case FWFWKWebsiteDataTypeEnumSessionStorage: + return WKWebsiteDataTypeSessionStorage; + case FWFWKWebsiteDataTypeEnumWebSQLDatabases: + return WKWebsiteDataTypeWebSQLDatabases; + case FWFWKWebsiteDataTypeEnumIndexedDBDatabases: + return WKWebsiteDataTypeIndexedDBDatabases; + } + + return nil; +} 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 5306b300fc03..7d2bd44050ea 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h @@ -45,15 +45,15 @@ typedef NS_ENUM(NSUInteger, FWFWKAudiovisualMediaTypeEnum) { FWFWKAudiovisualMediaTypeEnumAll = 3, }; -typedef NS_ENUM(NSUInteger, FWFWKWebsiteDataTypesEnum) { - FWFWKWebsiteDataTypesEnumCookies = 0, - FWFWKWebsiteDataTypesEnumMemoryCache = 1, - FWFWKWebsiteDataTypesEnumDiskCache = 2, - FWFWKWebsiteDataTypesEnumOfflineWebApplicationCache = 3, - FWFWKWebsiteDataTypesEnumLocalStroage = 4, - FWFWKWebsiteDataTypesEnumSessionStorage = 5, - FWFWKWebsiteDataTypesEnumSqlDatabases = 6, - FWFWKWebsiteDataTypesEnumIndexedDBDatabases = 7, +typedef NS_ENUM(NSUInteger, FWFWKWebsiteDataTypeEnum) { + FWFWKWebsiteDataTypeEnumCookies = 0, + FWFWKWebsiteDataTypeEnumMemoryCache = 1, + FWFWKWebsiteDataTypeEnumDiskCache = 2, + FWFWKWebsiteDataTypeEnumOfflineWebApplicationCache = 3, + FWFWKWebsiteDataTypeEnumLocalStorage = 4, + FWFWKWebsiteDataTypeEnumSessionStorage = 5, + FWFWKWebsiteDataTypeEnumWebSQLDatabases = 6, + FWFWKWebsiteDataTypeEnumIndexedDBDatabases = 7, }; typedef NS_ENUM(NSUInteger, FWFWKNavigationActionPolicyEnum) { @@ -81,33 +81,43 @@ typedef NS_ENUM(NSUInteger, FWFNSHttpCookiePropertyKeyEnum) { @class FWFNSKeyValueObservingOptionsEnumData; @class FWFWKUserScriptInjectionTimeEnumData; @class FWFWKAudiovisualMediaTypeEnumData; -@class FWFWKWebsiteDataTypesEnumData; +@class FWFWKWebsiteDataTypeEnumData; @class FWFNSHttpCookiePropertyKeyEnumData; @class FWFNSUrlRequestData; @class FWFWKUserScriptData; @class FWFNSHttpCookieData; @interface FWFNSKeyValueObservingOptionsEnumData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithValue:(FWFNSKeyValueObservingOptionsEnum)value; @property(nonatomic, assign) FWFNSKeyValueObservingOptionsEnum value; @end @interface FWFWKUserScriptInjectionTimeEnumData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithValue:(FWFWKUserScriptInjectionTimeEnum)value; @property(nonatomic, assign) FWFWKUserScriptInjectionTimeEnum value; @end @interface FWFWKAudiovisualMediaTypeEnumData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithValue:(FWFWKAudiovisualMediaTypeEnum)value; @property(nonatomic, assign) FWFWKAudiovisualMediaTypeEnum value; @end -@interface FWFWKWebsiteDataTypesEnumData : NSObject -+ (instancetype)makeWithValue:(FWFWKWebsiteDataTypesEnum)value; -@property(nonatomic, assign) FWFWKWebsiteDataTypesEnum value; +@interface FWFWKWebsiteDataTypeEnumData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithValue:(FWFWKWebsiteDataTypeEnum)value; +@property(nonatomic, assign) FWFWKWebsiteDataTypeEnum value; @end @interface FWFNSHttpCookiePropertyKeyEnumData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; + (instancetype)makeWithValue:(FWFNSHttpCookiePropertyKeyEnum)value; @property(nonatomic, assign) FWFNSHttpCookiePropertyKeyEnum value; @end @@ -139,24 +149,24 @@ typedef NS_ENUM(NSUInteger, FWFNSHttpCookiePropertyKeyEnum) { @interface FWFNSHttpCookieData : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithProperties: - (NSDictionary *)properties; -@property(nonatomic, strong) - NSDictionary *properties; ++ (instancetype)makeWithPropertyKeys:(NSArray *)propertyKeys + propertyValues:(NSArray *)propertyValues; +@property(nonatomic, strong) NSArray *propertyKeys; +@property(nonatomic, strong) NSArray *propertyValues; @end /// The codec used by FWFWKWebsiteDataStoreHostApi. NSObject *FWFWKWebsiteDataStoreHostApiGetCodec(void); @protocol FWFWKWebsiteDataStoreHostApi -- (void)createDataStoreFromConfigurationWithIdentifier:(NSNumber *)instanceId - configurationIdentifier:(NSNumber *)configurationInstanceId - error:(FlutterError *_Nullable *_Nonnull)error; +- (void)createFromWebViewConfigurationWithIdentifier:(NSNumber *)instanceId + configurationIdentifier:(NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; - (void)createDefaultDataStoreWithIdentifier:(NSNumber *)instanceId error:(FlutterError *_Nullable *_Nonnull)error; - (void)removeDataFromDataStoreWithIdentifier:(NSNumber *)instanceId - ofTypes:(NSArray *)dataTypes - secondsModifiedSinceEpoch:(NSNumber *)secondsModifiedSinceEpoch + ofTypes:(NSArray *)dataTypes + modifiedSince:(NSNumber *)modificationTimeInSecondsSinceEpoch completion:(void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion; @end @@ -169,10 +179,6 @@ extern void FWFWKWebsiteDataStoreHostApiSetup( NSObject *FWFUIViewHostApiGetCodec(void); @protocol FWFUIViewHostApi -/// @return `nil` only when `error != nil`. -- (nullable NSArray *) - contentOffsetForViewWithIdentifier:(NSNumber *)instanceId - error:(FlutterError *_Nullable *_Nonnull)error; - (void)setBackgroundColorForViewWithIdentifier:(NSNumber *)instanceId toValue:(nullable NSNumber *)value error:(FlutterError *_Nullable *_Nonnull)error; @@ -196,7 +202,7 @@ NSObject *FWFUIScrollViewHostApiGetCodec(void); contentOffsetForScrollViewWithIdentifier:(NSNumber *)instanceId error:(FlutterError *_Nullable *_Nonnull)error; - (void)scrollByForScrollViewWithIdentifier:(NSNumber *)instanceId - toX:(NSNumber *)x + x:(NSNumber *)x y:(NSNumber *)y error:(FlutterError *_Nullable *_Nonnull)error; - (void)setContentOffsetForScrollViewWithIdentifier:(NSNumber *)instanceId @@ -217,7 +223,7 @@ NSObject *FWFWKWebViewConfigurationHostApiGetCodec(void); webViewIdentifier:(NSNumber *)webViewInstanceId error:(FlutterError *_Nullable *_Nonnull)error; - (void)setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:(NSNumber *)instanceId - isAlowed:(NSNumber *)allow + isAllowed:(NSNumber *)allow error: (FlutterError *_Nullable *_Nonnull) error; @@ -270,9 +276,9 @@ extern void FWFWKUserContentControllerHostApiSetup( NSObject *FWFWKPreferencesHostApiGetCodec(void); @protocol FWFWKPreferencesHostApi -- (void)createFromWebViewConfiguration:(NSNumber *)instanceId - configurationIdentifier:(NSNumber *)configurationInstanceId - error:(FlutterError *_Nullable *_Nonnull)error; +- (void)createFromWebViewConfigurationWithIdentifier:(NSNumber *)instanceId + configurationIdentifier:(NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable *_Nonnull)error; - (void)setJavaScriptEnabledForPreferencesWithIdentifier:(NSNumber *)instanceId isEnabled:(NSNumber *)enabled error:(FlutterError *_Nullable *_Nonnull)error; @@ -428,7 +434,7 @@ NSObject *FWFWKHttpCookieStoreHostApiGetCodec(void); error:(FlutterError *_Nullable *_Nonnull)error; - (void)setCookieForStoreWithIdentifier:(NSNumber *)instanceId cookie:(FWFNSHttpCookieData *)cookie - error:(FlutterError *_Nullable *_Nonnull)error; + completion:(void (^)(FlutterError *_Nullable))completion; @end extern void FWFWKHttpCookieStoreHostApiSetup(id binaryMessenger, 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 070b3c5cc033..aaeccc5cc1b0 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m @@ -45,8 +45,8 @@ @interface FWFWKAudiovisualMediaTypeEnumData () + (FWFWKAudiovisualMediaTypeEnumData *)fromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end -@interface FWFWKWebsiteDataTypesEnumData () -+ (FWFWKWebsiteDataTypesEnumData *)fromMap:(NSDictionary *)dict; +@interface FWFWKWebsiteDataTypeEnumData () ++ (FWFWKWebsiteDataTypeEnumData *)fromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFNSHttpCookiePropertyKeyEnumData () @@ -120,14 +120,14 @@ - (NSDictionary *)toMap { } @end -@implementation FWFWKWebsiteDataTypesEnumData -+ (instancetype)makeWithValue:(FWFWKWebsiteDataTypesEnum)value { - FWFWKWebsiteDataTypesEnumData *pigeonResult = [[FWFWKWebsiteDataTypesEnumData alloc] init]; +@implementation FWFWKWebsiteDataTypeEnumData ++ (instancetype)makeWithValue:(FWFWKWebsiteDataTypeEnum)value { + FWFWKWebsiteDataTypeEnumData *pigeonResult = [[FWFWKWebsiteDataTypeEnumData alloc] init]; pigeonResult.value = value; return pigeonResult; } -+ (FWFWKWebsiteDataTypesEnumData *)fromMap:(NSDictionary *)dict { - FWFWKWebsiteDataTypesEnumData *pigeonResult = [[FWFWKWebsiteDataTypesEnumData alloc] init]; ++ (FWFWKWebsiteDataTypeEnumData *)fromMap:(NSDictionary *)dict { + FWFWKWebsiteDataTypeEnumData *pigeonResult = [[FWFWKWebsiteDataTypeEnumData alloc] init]; pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; return pigeonResult; } @@ -220,22 +220,27 @@ - (NSDictionary *)toMap { @end @implementation FWFNSHttpCookieData -+ (instancetype)makeWithProperties: - (NSDictionary *)properties { ++ (instancetype)makeWithPropertyKeys:(NSArray *)propertyKeys + propertyValues:(NSArray *)propertyValues { FWFNSHttpCookieData *pigeonResult = [[FWFNSHttpCookieData alloc] init]; - pigeonResult.properties = properties; + pigeonResult.propertyKeys = propertyKeys; + pigeonResult.propertyValues = propertyValues; return pigeonResult; } + (FWFNSHttpCookieData *)fromMap:(NSDictionary *)dict { FWFNSHttpCookieData *pigeonResult = [[FWFNSHttpCookieData alloc] init]; - pigeonResult.properties = GetNullableObject(dict, @"properties"); - NSAssert(pigeonResult.properties != nil, @""); + pigeonResult.propertyKeys = GetNullableObject(dict, @"propertyKeys"); + NSAssert(pigeonResult.propertyKeys != nil, @""); + pigeonResult.propertyValues = GetNullableObject(dict, @"propertyValues"); + NSAssert(pigeonResult.propertyValues != nil, @""); return pigeonResult; } - (NSDictionary *)toMap { return [NSDictionary - dictionaryWithObjectsAndKeys:(self.properties ? self.properties : [NSNull null]), - @"properties", nil]; + dictionaryWithObjectsAndKeys:(self.propertyKeys ? self.propertyKeys : [NSNull null]), + @"propertyKeys", + (self.propertyValues ? self.propertyValues : [NSNull null]), + @"propertyValues", nil]; } @end @@ -245,7 +250,7 @@ @implementation FWFWKWebsiteDataStoreHostApiCodecReader - (nullable id)readValueOfType:(UInt8)type { switch (type) { case 128: - return [FWFWKWebsiteDataTypesEnumData fromMap:[self readValue]]; + return [FWFWKWebsiteDataTypeEnumData fromMap:[self readValue]]; default: return [super readValueOfType:type]; @@ -257,7 +262,7 @@ @interface FWFWKWebsiteDataStoreHostApiCodecWriter : FlutterStandardWriter @end @implementation FWFWKWebsiteDataStoreHostApiCodecWriter - (void)writeValue:(id)value { - if ([value isKindOfClass:[FWFWKWebsiteDataTypesEnumData class]]) { + if ([value isKindOfClass:[FWFWKWebsiteDataTypeEnumData class]]) { [self writeByte:128]; [self writeValue:[value toMap]]; } else { @@ -298,20 +303,19 @@ void FWFWKWebsiteDataStoreHostApiSetup(id binaryMessenge codec:FWFWKWebsiteDataStoreHostApiGetCodec()]; if (api) { NSCAssert( - [api respondsToSelector:@selector(createDataStoreFromConfigurationWithIdentifier: - configurationIdentifier:error:)], + [api respondsToSelector:@selector(createFromWebViewConfigurationWithIdentifier: + configurationIdentifier:error:)], @"FWFWKWebsiteDataStoreHostApi api (%@) doesn't respond to " - @"@selector(createDataStoreFromConfigurationWithIdentifier:configurationIdentifier:error:" - @")", + @"@selector(createFromWebViewConfigurationWithIdentifier:configurationIdentifier:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); NSNumber *arg_configurationInstanceId = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api createDataStoreFromConfigurationWithIdentifier:arg_instanceId - configurationIdentifier:arg_configurationInstanceId - error:&error]; + [api createFromWebViewConfigurationWithIdentifier:arg_instanceId + configurationIdentifier:arg_configurationInstanceId + error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -345,21 +349,20 @@ void FWFWKWebsiteDataStoreHostApiSetup(id binaryMessenge binaryMessenger:binaryMessenger codec:FWFWKWebsiteDataStoreHostApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector - (removeDataFromDataStoreWithIdentifier: - ofTypes:secondsModifiedSinceEpoch:completion:)], - @"FWFWKWebsiteDataStoreHostApi api (%@) doesn't respond to " - @"@selector(removeDataFromDataStoreWithIdentifier:ofTypes:" - @"secondsModifiedSinceEpoch:completion:)", - api); + NSCAssert( + [api respondsToSelector:@selector + (removeDataFromDataStoreWithIdentifier:ofTypes:modifiedSince:completion:)], + @"FWFWKWebsiteDataStoreHostApi api (%@) doesn't respond to " + @"@selector(removeDataFromDataStoreWithIdentifier:ofTypes:modifiedSince:completion:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); - NSArray *arg_dataTypes = GetNullableObjectAtIndex(args, 1); - NSNumber *arg_secondsModifiedSinceEpoch = GetNullableObjectAtIndex(args, 2); + NSArray *arg_dataTypes = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_modificationTimeInSecondsSinceEpoch = GetNullableObjectAtIndex(args, 2); [api removeDataFromDataStoreWithIdentifier:arg_instanceId ofTypes:arg_dataTypes - secondsModifiedSinceEpoch:arg_secondsModifiedSinceEpoch + modifiedSince:arg_modificationTimeInSecondsSinceEpoch completion:^(NSNumber *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); @@ -404,28 +407,6 @@ - (FlutterStandardReader *)readerWithData:(NSData *)data { void FWFUIViewHostApiSetup(id binaryMessenger, NSObject *api) { - { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.UIViewHostApi.getContentOffset" - binaryMessenger:binaryMessenger - codec:FWFUIViewHostApiGetCodec()]; - if (api) { - NSCAssert([api respondsToSelector:@selector(contentOffsetForViewWithIdentifier:error:)], - @"FWFUIViewHostApi api (%@) doesn't respond to " - @"@selector(contentOffsetForViewWithIdentifier:error:)", - api); - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); - FlutterError *error; - NSArray *output = [api contentOffsetForViewWithIdentifier:arg_instanceId - error:&error]; - callback(wrapResult(output, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] initWithName:@"dev.flutter.pigeon.UIViewHostApi.setBackgroundColor" @@ -559,10 +540,9 @@ void FWFUIScrollViewHostApiSetup(id binaryMessenger, binaryMessenger:binaryMessenger codec:FWFUIScrollViewHostApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(scrollByForScrollViewWithIdentifier: - toX:y:error:)], + NSCAssert([api respondsToSelector:@selector(scrollByForScrollViewWithIdentifier:x:y:error:)], @"FWFUIScrollViewHostApi api (%@) doesn't respond to " - @"@selector(scrollByForScrollViewWithIdentifier:toX:y:error:)", + @"@selector(scrollByForScrollViewWithIdentifier:x:y:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; @@ -570,7 +550,7 @@ void FWFUIScrollViewHostApiSetup(id binaryMessenger, NSNumber *arg_x = GetNullableObjectAtIndex(args, 1); NSNumber *arg_y = GetNullableObjectAtIndex(args, 2); FlutterError *error; - [api scrollByForScrollViewWithIdentifier:arg_instanceId toX:arg_x y:arg_y error:&error]; + [api scrollByForScrollViewWithIdentifier:arg_instanceId x:arg_x y:arg_y error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -711,9 +691,9 @@ void FWFWKWebViewConfigurationHostApiSetup(id binaryMess if (api) { NSCAssert( [api respondsToSelector:@selector - (setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:isAlowed:error:)], + (setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:isAllowed:error:)], @"FWFWKWebViewConfigurationHostApi api (%@) doesn't respond to " - @"@selector(setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:isAlowed:error:)", + @"@selector(setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:isAllowed:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; @@ -721,7 +701,7 @@ void FWFWKWebViewConfigurationHostApiSetup(id binaryMess NSNumber *arg_allow = GetNullableObjectAtIndex(args, 1); FlutterError *error; [api setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:arg_instanceId - isAlowed:arg_allow + isAllowed:arg_allow error:&error]; callback(wrapResult(nil, error)); }]; @@ -1008,19 +988,20 @@ void FWFWKPreferencesHostApiSetup(id binaryMessenger, binaryMessenger:binaryMessenger codec:FWFWKPreferencesHostApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(createFromWebViewConfiguration: - configurationIdentifier:error:)], - @"FWFWKPreferencesHostApi api (%@) doesn't respond to " - @"@selector(createFromWebViewConfiguration:configurationIdentifier:error:)", - api); + NSCAssert( + [api respondsToSelector:@selector(createFromWebViewConfigurationWithIdentifier: + configurationIdentifier:error:)], + @"FWFWKPreferencesHostApi api (%@) doesn't respond to " + @"@selector(createFromWebViewConfigurationWithIdentifier:configurationIdentifier:error:)", + api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); NSNumber *arg_configurationInstanceId = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api createFromWebViewConfiguration:arg_instanceId - configurationIdentifier:arg_configurationInstanceId - error:&error]; + [api createFromWebViewConfigurationWithIdentifier:arg_instanceId + configurationIdentifier:arg_configurationInstanceId + error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -1472,7 +1453,7 @@ - (nullable id)readValueOfType:(UInt8)type { return [FWFWKUserScriptInjectionTimeEnumData fromMap:[self readValue]]; case 135: - return [FWFWKWebsiteDataTypesEnumData fromMap:[self readValue]]; + return [FWFWKWebsiteDataTypeEnumData fromMap:[self readValue]]; default: return [super readValueOfType:type]; @@ -1505,7 +1486,7 @@ - (void)writeValue:(id)value { } else if ([value isKindOfClass:[FWFWKUserScriptInjectionTimeEnumData class]]) { [self writeByte:134]; [self writeValue:[value toMap]]; - } else if ([value isKindOfClass:[FWFWKWebsiteDataTypesEnumData class]]) { + } else if ([value isKindOfClass:[FWFWKWebsiteDataTypeEnumData class]]) { [self writeByte:135]; [self writeValue:[value toMap]]; } else { @@ -2107,17 +2088,20 @@ void FWFWKHttpCookieStoreHostApiSetup(id binaryMessenger binaryMessenger:binaryMessenger codec:FWFWKHttpCookieStoreHostApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(setCookieForStoreWithIdentifier:cookie:error:)], + NSCAssert([api respondsToSelector:@selector(setCookieForStoreWithIdentifier: + cookie:completion:)], @"FWFWKHttpCookieStoreHostApi api (%@) doesn't respond to " - @"@selector(setCookieForStoreWithIdentifier:cookie:error:)", + @"@selector(setCookieForStoreWithIdentifier:cookie:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); FWFNSHttpCookieData *arg_cookie = GetNullableObjectAtIndex(args, 1); - FlutterError *error; - [api setCookieForStoreWithIdentifier:arg_instanceId cookie:arg_cookie error:&error]; - callback(wrapResult(nil, error)); + [api setCookieForStoreWithIdentifier:arg_instanceId + cookie:arg_cookie + completion:^(FlutterError *_Nullable error) { + callback(wrapResult(nil, error)); + }]; }]; } else { [channel setMessageHandler:nil]; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.h new file mode 100644 index 000000000000..887c9f1b3d8b --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.h @@ -0,0 +1,22 @@ +// 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 + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Host api implementation for WKHTTPCookieStore. + * + * Handles creating WKHTTPCookieStore that intercommunicate with a paired Dart object. + */ +@interface FWFHTTPCookieStoreHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.m new file mode 100644 index 000000000000..6ebd60a4ac40 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.m @@ -0,0 +1,60 @@ +// 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 "FWFHTTPCookieStoreHostApi.h" +#import "FWFDataConverters.h" +#import "FWFWebsiteDataStoreHostApi.h" + +@interface FWFHTTPCookieStoreHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFHTTPCookieStoreHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (WKHTTPCookieStore *)HTTPCookieStoreForIdentifier:(NSNumber *)instanceId + API_AVAILABLE(ios(11.0)) { + return (WKHTTPCookieStore *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +} + +- (void)createFromWebsiteDataStoreWithIdentifier:(nonnull NSNumber *)instanceId + dataStoreIdentifier:(nonnull NSNumber *)websiteDataStoreInstanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + if (@available(iOS 11.0, *)) { + WKWebsiteDataStore *dataStore = (WKWebsiteDataStore *)[self.instanceManager + instanceForIdentifier:websiteDataStoreInstanceId.longValue]; + [self.instanceManager addInstance:dataStore.httpCookieStore + withIdentifier:instanceId.longValue]; + } else { + *error = [FlutterError + errorWithCode:@"FWFUnsupportedVersionError" + message:@"WKWebsiteDataStore.httpCookieStore is only supported on versions 11+." + details:nil]; + } +} + +- (void)setCookieForStoreWithIdentifier:(nonnull NSNumber *)instanceId + cookie:(nonnull FWFNSHttpCookieData *)cookie + completion:(nonnull void (^)(FlutterError *_Nullable))completion { + NSHTTPCookie *nsCookie = FWFNSHTTPCookieFromCookieData(cookie); + + if (@available(iOS 11.0, *)) { + [[self HTTPCookieStoreForIdentifier:instanceId] setCookie:nsCookie + completionHandler:^{ + completion(nil); + }]; + } else { + completion([FlutterError errorWithCode:@"FWFUnsupportedVersionError" + message:@"setCookie is only supported on versions 11+." + details:nil]); + } +} +@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 new file mode 100644 index 000000000000..fca1e83c86e0 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.h @@ -0,0 +1,28 @@ +// 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 + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Implementation of WKNavigationDelegate for FWFNavigationDelegateHostApiImpl. + */ +@interface FWFNavigationDelegate : NSObject +@end + +/** + * Host api implementation for WKNavigationDelegate. + * + * Handles creating WKNavigationDelegate that intercommunicate with a paired Dart object. + */ +@interface FWFNavigationDelegateHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_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 new file mode 100644 index 000000000000..188d83ff81b6 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m @@ -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 "FWFNavigationDelegateHostApi.h" +#import "FWFWebViewConfigurationHostApi.h" + +@implementation FWFNavigationDelegate +@end + +@interface FWFNavigationDelegateHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFNavigationDelegateHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (FWFNavigationDelegate *)navigationDelegateForIdentifier:(NSNumber *)instanceId { + return (FWFNavigationDelegate *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +} + +- (void)createWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error { + FWFNavigationDelegate *navigationDelegate = [[FWFNavigationDelegate alloc] init]; + [self.instanceManager addInstance:navigationDelegate withIdentifier:instanceId.longValue]; +} + +- (void)setDidFinishNavigationForDelegateWithIdentifier:(nonnull NSNumber *)instanceId + functionIdentifier:(nullable NSNumber *)functionInstanceId + error:(FlutterError *_Nullable __autoreleasing + *_Nonnull)error { + // TODO(bparrishMines): Implement when callback method design is finalized. +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.h new file mode 100644 index 000000000000..23c38fbe482d --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.h @@ -0,0 +1,21 @@ +// 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 "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Host api implementation for NSObject. + * + * Handles creating NSObject that intercommunicate with a paired Dart object. + */ +@interface FWFObjectHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m new file mode 100644 index 000000000000..b229c2e819a9 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m @@ -0,0 +1,54 @@ +// 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 "FWFObjectHostApi.h" +#import "FWFDataConverters.h" + +@interface FWFObjectHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFObjectHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (NSObject *)objectForIdentifier:(NSNumber *)instanceId { + return (NSObject *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +} + +- (void)addObserverForObjectWithIdentifier:(nonnull NSNumber *)instanceId + observerIdentifier:(nonnull NSNumber *)observer + keyPath:(nonnull NSString *)keyPath + options: + (nonnull NSArray *) + options + error:(FlutterError *_Nullable *_Nonnull)error { + NSKeyValueObservingOptions optionsInt = 0; + for (FWFNSKeyValueObservingOptionsEnumData *data in options) { + optionsInt |= FWFNSKeyValueObservingOptionsFromEnumData(data); + } + [[self objectForIdentifier:instanceId] addObserver:[self objectForIdentifier:observer] + forKeyPath:keyPath + options:optionsInt + context:nil]; +} + +- (void)removeObserverForObjectWithIdentifier:(nonnull NSNumber *)instanceId + observerIdentifier:(nonnull NSNumber *)observer + keyPath:(nonnull NSString *)keyPath + error:(FlutterError *_Nullable *_Nonnull)error { + [[self objectForIdentifier:instanceId] removeObserver:[self objectForIdentifier:observer] + forKeyPath:keyPath]; +} + +- (void)disposeObjectWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error { + [self.instanceManager removeInstanceWithIdentifier:instanceId.longValue]; +} +@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 new file mode 100644 index 000000000000..de2d26491a58 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.h @@ -0,0 +1,22 @@ +// 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 + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Host api implementation for WKPreferences. + * + * Handles creating WKPreferences that intercommunicate with a paired Dart object. + */ +@interface FWFPreferencesHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.m new file mode 100644 index 000000000000..f907d211f08e --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.m @@ -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 "FWFPreferencesHostApi.h" +#import "FWFWebViewConfigurationHostApi.h" + +@interface FWFPreferencesHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFPreferencesHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (WKPreferences *)preferencesForIdentifier:(NSNumber *)instanceId { + return (WKPreferences *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +} + +- (void)createWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error { + WKPreferences *preferences = [[WKPreferences alloc] init]; + [self.instanceManager addInstance:preferences withIdentifier:instanceId.longValue]; +} + +- (void)createFromWebViewConfigurationWithIdentifier:(nonnull NSNumber *)instanceId + configurationIdentifier:(nonnull NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable *_Nonnull)error { + WKWebViewConfiguration *configuration = (WKWebViewConfiguration *)[self.instanceManager + instanceForIdentifier:configurationInstanceId.longValue]; + [self.instanceManager addInstance:configuration.preferences withIdentifier:instanceId.longValue]; +} + +- (void)setJavaScriptEnabledForPreferencesWithIdentifier:(nonnull NSNumber *)instanceId + isEnabled:(nonnull NSNumber *)enabled + error:(FlutterError *_Nullable *_Nonnull)error { + [[self preferencesForIdentifier:instanceId] setJavaScriptEnabled:enabled.boolValue]; +} +@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 new file mode 100644 index 000000000000..7440862ce4e4 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.h @@ -0,0 +1,28 @@ +// 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 + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Implementation of WKScriptMessageHandler for FWFScriptMessageHandlerHostApiImpl. + */ +@interface FWFScriptMessageHandler : NSObject +@end + +/** + * Host api implementation for WKScriptMessageHandler. + * + * Handles creating WKScriptMessageHandler that intercommunicate with a paired Dart object. + */ +@interface FWFScriptMessageHandlerHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m new file mode 100644 index 000000000000..37662b5fc9f4 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m @@ -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. + +#import "FWFScriptMessageHandlerHostApi.h" +#import "FWFDataConverters.h" + +@implementation FWFScriptMessageHandler +- (void)userContentController:(nonnull WKUserContentController *)userContentController + didReceiveScriptMessage:(nonnull WKScriptMessage *)message { +} +@end + +@interface FWFScriptMessageHandlerHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFScriptMessageHandlerHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (FWFScriptMessageHandler *)scriptMessageHandlerForIdentifier:(NSNumber *)instanceId { + return (FWFScriptMessageHandler *)[self.instanceManager + instanceForIdentifier:instanceId.longValue]; +} + +- (void)createWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error { + FWFScriptMessageHandler *scriptMessageHandler = [[FWFScriptMessageHandler alloc] init]; + [self.instanceManager addInstance:scriptMessageHandler withIdentifier:instanceId.longValue]; +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.h new file mode 100644 index 000000000000..25f373f374e3 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.h @@ -0,0 +1,22 @@ +// 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 + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Host api implementation for UIScrollView. + * + * Handles creating UIScrollView that intercommunicate with a paired Dart object. + */ +@interface FWFScrollViewHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.m new file mode 100644 index 000000000000..a7522995e0e1 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.m @@ -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 "FWFScrollViewHostApi.h" +#import "FWFWebViewHostApi.h" + +@interface FWFScrollViewHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFScrollViewHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (UIScrollView *)scrollViewForIdentifier:(NSNumber *)instanceId { + return (UIScrollView *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +} + +- (void)createFromWebViewWithIdentifier:(nonnull NSNumber *)instanceId + webViewIdentifier:(nonnull NSNumber *)webViewInstanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + WKWebView *webView = + (WKWebView *)[self.instanceManager instanceForIdentifier:webViewInstanceId.longValue]; + [self.instanceManager addInstance:webView.scrollView withIdentifier:instanceId.longValue]; +} + +- (NSArray *) + contentOffsetForScrollViewWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error { + CGPoint point = [[self scrollViewForIdentifier:instanceId] contentOffset]; + return @[ @(point.x), @(point.y) ]; +} + +- (void)scrollByForScrollViewWithIdentifier:(nonnull NSNumber *)instanceId + x:(nonnull NSNumber *)x + y:(nonnull NSNumber *)y + error:(FlutterError *_Nullable *_Nonnull)error { + UIScrollView *scrollView = [self scrollViewForIdentifier:instanceId]; + CGPoint contentOffset = scrollView.contentOffset; + [scrollView setContentOffset:CGPointMake(contentOffset.x + x.doubleValue, + contentOffset.y + y.doubleValue)]; +} + +- (void)setContentOffsetForScrollViewWithIdentifier:(nonnull NSNumber *)instanceId + toX:(nonnull NSNumber *)x + y:(nonnull NSNumber *)y + error:(FlutterError *_Nullable *_Nonnull)error { + [[self scrollViewForIdentifier:instanceId] + setContentOffset:CGPointMake(x.doubleValue, y.doubleValue)]; +} +@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 new file mode 100644 index 000000000000..8795b00e9001 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h @@ -0,0 +1,28 @@ +// 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 + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Implementation of WKUIDelegate for FWFUIDelegateHostApiImpl. + */ +@interface FWFUIDelegate : NSObject +@end + +/** + * Host api implementation for WKUIDelegate. + * + * Handles creating WKUIDelegate that intercommunicate with a paired Dart object. + */ +@interface FWFUIDelegateHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m new file mode 100644 index 000000000000..2621c5dde287 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m @@ -0,0 +1,33 @@ +// 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 "FWFUIDelegateHostApi.h" +#import "FWFWebViewConfigurationHostApi.h" + +@implementation FWFUIDelegate +@end + +@interface FWFUIDelegateHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFUIDelegateHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (FWFUIDelegate *)delegateForIdentifier:(NSNumber *)instanceId { + return (FWFUIDelegate *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +} + +- (void)createWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error { + FWFUIDelegate *uIDelegate = [[FWFUIDelegate alloc] init]; + [self.instanceManager addInstance:uIDelegate withIdentifier:instanceId.longValue]; +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.h new file mode 100644 index 000000000000..82edd6b742ca --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.h @@ -0,0 +1,21 @@ +// 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 "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Host api implementation for UIView. + * + * Handles creating UIView that intercommunicate with a paired Dart object. + */ +@interface FWFUIViewHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.m new file mode 100644 index 000000000000..b2c1f639d1f6 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.m @@ -0,0 +1,43 @@ +// 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 "FWFUIViewHostApi.h" + +@interface FWFUIViewHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFUIViewHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (UIView *)viewForIdentifier:(NSNumber *)instanceId { + return (UIView *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +} + +- (void)setBackgroundColorForViewWithIdentifier:(nonnull NSNumber *)instanceId + toValue:(nullable NSNumber *)color + error:(FlutterError *_Nullable *_Nonnull)error { + if (!color) { + [[self viewForIdentifier:instanceId] setBackgroundColor:nil]; + } + int colorInt = color.intValue; + UIColor *colorObject = [UIColor colorWithRed:(colorInt >> 16 & 0xff) / 255.0 + green:(colorInt >> 8 & 0xff) / 255.0 + blue:(colorInt & 0xff) / 255.0 + alpha:(colorInt >> 24 & 0xff) / 255.0]; + [[self viewForIdentifier:instanceId] setBackgroundColor:colorObject]; +} + +- (void)setOpaqueForViewWithIdentifier:(nonnull NSNumber *)instanceId + isOpaque:(nonnull NSNumber *)opaque + error:(FlutterError *_Nullable *_Nonnull)error { + [[self viewForIdentifier:instanceId] setOpaque:opaque.boolValue]; +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.h new file mode 100644 index 000000000000..f0e5a1383ac3 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.h @@ -0,0 +1,22 @@ +// 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 + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Host api implementation for WKUserContentController. + * + * Handles creating WKUserContentController that intercommunicate with a paired Dart object. + */ +@interface FWFUserContentControllerHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.m new file mode 100644 index 000000000000..2db7cff0b68e --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.m @@ -0,0 +1,80 @@ +// 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 "FWFUserContentControllerHostApi.h" +#import "FWFDataConverters.h" +#import "FWFWebViewConfigurationHostApi.h" + +@interface FWFUserContentControllerHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFUserContentControllerHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (WKUserContentController *)userContentControllerForIdentifier:(NSNumber *)instanceId { + return (WKUserContentController *)[self.instanceManager + instanceForIdentifier:instanceId.longValue]; +} + +- (void)createFromWebViewConfigurationWithIdentifier:(nonnull NSNumber *)instanceId + configurationIdentifier:(nonnull NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable *_Nonnull)error { + WKWebViewConfiguration *configuration = (WKWebViewConfiguration *)[self.instanceManager + instanceForIdentifier:configurationInstanceId.longValue]; + [self.instanceManager addInstance:configuration.userContentController + withIdentifier:instanceId.longValue]; +} + +- (void)addScriptMessageHandlerForControllerWithIdentifier:(nonnull NSNumber *)instanceId + handlerIdentifier:(nonnull NSNumber *)handler + ofName:(nonnull NSString *)name + error: + (FlutterError *_Nullable *_Nonnull)error { + [[self userContentControllerForIdentifier:instanceId] + addScriptMessageHandler:(id)[self.instanceManager + instanceForIdentifier:handler.longValue] + name:name]; +} + +- (void)removeScriptMessageHandlerForControllerWithIdentifier:(nonnull NSNumber *)instanceId + name:(nonnull NSString *)name + error:(FlutterError *_Nullable *_Nonnull) + error { + [[self userContentControllerForIdentifier:instanceId] removeScriptMessageHandlerForName:name]; +} + +- (void)removeAllScriptMessageHandlersForControllerWithIdentifier:(nonnull NSNumber *)instanceId + error: + (FlutterError *_Nullable *_Nonnull) + error { + if (@available(iOS 14.0, *)) { + [[self userContentControllerForIdentifier:instanceId] removeAllScriptMessageHandlers]; + } else { + *error = [FlutterError + errorWithCode:@"FWFUnsupportedVersionError" + message:@"removeAllScriptMessageHandlers is only supported on versions 14+." + details:nil]; + } +} + +- (void)addUserScriptForControllerWithIdentifier:(nonnull NSNumber *)instanceId + userScript:(nonnull FWFWKUserScriptData *)userScript + error:(FlutterError *_Nullable *_Nonnull)error { + [[self userContentControllerForIdentifier:instanceId] + addUserScript:FWFWKUserScriptFromScriptData(userScript)]; +} + +- (void)removeAllUserScriptsForControllerWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error { + [[self userContentControllerForIdentifier:instanceId] removeAllUserScripts]; +} + +@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 new file mode 100644 index 000000000000..4c01d9354107 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.h @@ -0,0 +1,22 @@ +// 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 + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Host api implementation for WKWebViewConfiguration. + * + * Handles creating WKWebViewConfiguration that intercommunicate with a paired Dart object. + */ +@interface FWFWebViewConfigurationHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m new file mode 100644 index 000000000000..0b239daebf99 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m @@ -0,0 +1,87 @@ +// 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 "FWFWebViewConfigurationHostApi.h" +#import "FWFDataConverters.h" +#import "FWFWebViewConfigurationHostApi.h" + +@interface FWFWebViewConfigurationHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFWebViewConfigurationHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (WKWebViewConfiguration *)webViewConfigurationForIdentifier:(NSNumber *)instanceId { + return (WKWebViewConfiguration *)[self.instanceManager + instanceForIdentifier:instanceId.longValue]; +} + +- (void)createWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable *_Nonnull)error { + WKWebViewConfiguration *webViewConfiguration = [[WKWebViewConfiguration alloc] init]; + [self.instanceManager addInstance:webViewConfiguration withIdentifier:instanceId.longValue]; +} + +- (void)createFromWebViewWithIdentifier:(nonnull NSNumber *)instanceId + webViewIdentifier:(nonnull NSNumber *)webViewInstanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + WKWebView *webView = + (WKWebView *)[self.instanceManager instanceForIdentifier:webViewInstanceId.longValue]; + [self.instanceManager addInstance:webView.configuration withIdentifier:instanceId.longValue]; +} + +- (void)setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:(nonnull NSNumber *)instanceId + isAllowed:(nonnull NSNumber *)allow + error: + (FlutterError *_Nullable *_Nonnull) + error { + [[self webViewConfigurationForIdentifier:instanceId] + setAllowsInlineMediaPlayback:allow.boolValue]; +} + +- (void) + setMediaTypesRequiresUserActionForConfigurationWithIdentifier:(nonnull NSNumber *)instanceId + forTypes: + (nonnull NSArray< + FWFWKAudiovisualMediaTypeEnumData + *> *)types + error: + (FlutterError *_Nullable *_Nonnull) + error { + NSAssert(types.count, @"Types must not be empty."); + + WKWebViewConfiguration *configuration = + (WKWebViewConfiguration *)[self webViewConfigurationForIdentifier:instanceId]; + if (@available(iOS 10.0, *)) { + WKAudiovisualMediaTypes typesInt = 0; + for (FWFWKAudiovisualMediaTypeEnumData *data in types) { + typesInt |= FWFWKAudiovisualMediaTypeFromEnumData(data); + } + [configuration setMediaTypesRequiringUserActionForPlayback:typesInt]; + } else { + for (FWFWKAudiovisualMediaTypeEnumData *data in types) { + switch (data.value) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + case FWFWKAudiovisualMediaTypeEnumNone: + configuration.requiresUserActionForMediaPlayback = false; + break; + case FWFWKAudiovisualMediaTypeEnumAudio: + case FWFWKAudiovisualMediaTypeEnumVideo: + case FWFWKAudiovisualMediaTypeEnumAll: + configuration.requiresUserActionForMediaPlayback = true; + break; +#pragma clang diagnostic pop + } + } + } +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.h new file mode 100644 index 000000000000..72f00e032ee4 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.h @@ -0,0 +1,22 @@ +// 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 + +#import "FWFGeneratedWebKitApis.h" +#import "FWFInstanceManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * Host api implementation for WKWebsiteDataStore. + * + * Handles creating WKWebsiteDataStore that intercommunicate with a paired Dart object. + */ +@interface FWFWebsiteDataStoreHostApiImpl : NSObject +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m new file mode 100644 index 000000000000..e052ae03543c --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m @@ -0,0 +1,67 @@ +// 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 "FWFWebsiteDataStoreHostApi.h" +#import "FWFDataConverters.h" +#import "FWFWebViewConfigurationHostApi.h" + +@interface FWFWebsiteDataStoreHostApiImpl () +@property(nonatomic) FWFInstanceManager *instanceManager; +@end + +@implementation FWFWebsiteDataStoreHostApiImpl +- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (WKWebsiteDataStore *)websiteDataStoreForIdentifier:(NSNumber *)instanceId { + return (WKWebsiteDataStore *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +} + +- (void)createFromWebViewConfigurationWithIdentifier:(nonnull NSNumber *)instanceId + configurationIdentifier:(nonnull NSNumber *)configurationInstanceId + error:(FlutterError *_Nullable *_Nonnull)error { + WKWebViewConfiguration *configuration = (WKWebViewConfiguration *)[self.instanceManager + instanceForIdentifier:configurationInstanceId.longValue]; + [self.instanceManager addInstance:configuration.websiteDataStore + withIdentifier:instanceId.longValue]; +} + +- (void)createDefaultDataStoreWithIdentifier:(nonnull NSNumber *)instanceId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + [self.instanceManager addInstance:[WKWebsiteDataStore defaultDataStore] + withIdentifier:instanceId.longValue]; +} + +- (void) + removeDataFromDataStoreWithIdentifier:(nonnull NSNumber *)instanceId + ofTypes: + (nonnull NSArray *)dataTypes + modifiedSince:(nonnull NSNumber *)modificationTimeInSecondsSinceEpoch + completion:(nonnull void (^)(NSNumber *_Nullable, + FlutterError *_Nullable))completion { + NSMutableSet *stringDataTypes = [NSMutableSet set]; + for (FWFWKWebsiteDataTypeEnumData *type in dataTypes) { + [stringDataTypes addObject:FWFWKWebsiteDataTypeFromEnumData(type)]; + } + + WKWebsiteDataStore *dataStore = [self websiteDataStoreForIdentifier:instanceId]; + [dataStore + fetchDataRecordsOfTypes:stringDataTypes + completionHandler:^(NSArray *records) { + [dataStore + removeDataOfTypes:stringDataTypes + modifiedSince:[NSDate dateWithTimeIntervalSince1970: + modificationTimeInSecondsSinceEpoch.doubleValue] + completionHandler:^{ + completion(@(records.count > 0), nil); + }]; + }]; +} +@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h index 9a7fd1b5ef3f..0aa409e4b31f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h @@ -9,7 +9,18 @@ #import #import #import +#import #import +#import +#import +#import +#import +#import +#import +#import +#import +#import #import +#import #import #import diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart index fa52b43c396e..f84ba68bfbc1 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart @@ -45,14 +45,14 @@ enum WKAudiovisualMediaTypeEnum { all, } -enum WKWebsiteDataTypesEnum { +enum WKWebsiteDataTypeEnum { cookies, memoryCache, diskCache, offlineWebApplicationCache, - localStroage, + localStorage, sessionStorage, - sqlDatabases, + webSQLDatabases, indexedDBDatabases, } @@ -80,115 +80,105 @@ enum NSHttpCookiePropertyKeyEnum { class NSKeyValueObservingOptionsEnumData { NSKeyValueObservingOptionsEnumData({ - this.value, + required this.value, }); - NSKeyValueObservingOptionsEnum? value; + NSKeyValueObservingOptionsEnum value; Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value?.index; + pigeonMap['value'] = value.index; return pigeonMap; } static NSKeyValueObservingOptionsEnumData decode(Object message) { final Map pigeonMap = message as Map; return NSKeyValueObservingOptionsEnumData( - value: pigeonMap['value'] != null - ? NSKeyValueObservingOptionsEnum.values[pigeonMap['value']! as int] - : null, + value: NSKeyValueObservingOptionsEnum.values[pigeonMap['value']! as int], ); } } class WKUserScriptInjectionTimeEnumData { WKUserScriptInjectionTimeEnumData({ - this.value, + required this.value, }); - WKUserScriptInjectionTimeEnum? value; + WKUserScriptInjectionTimeEnum value; Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value?.index; + pigeonMap['value'] = value.index; return pigeonMap; } static WKUserScriptInjectionTimeEnumData decode(Object message) { final Map pigeonMap = message as Map; return WKUserScriptInjectionTimeEnumData( - value: pigeonMap['value'] != null - ? WKUserScriptInjectionTimeEnum.values[pigeonMap['value']! as int] - : null, + value: WKUserScriptInjectionTimeEnum.values[pigeonMap['value']! as int], ); } } class WKAudiovisualMediaTypeEnumData { WKAudiovisualMediaTypeEnumData({ - this.value, + required this.value, }); - WKAudiovisualMediaTypeEnum? value; + WKAudiovisualMediaTypeEnum value; Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value?.index; + pigeonMap['value'] = value.index; return pigeonMap; } static WKAudiovisualMediaTypeEnumData decode(Object message) { final Map pigeonMap = message as Map; return WKAudiovisualMediaTypeEnumData( - value: pigeonMap['value'] != null - ? WKAudiovisualMediaTypeEnum.values[pigeonMap['value']! as int] - : null, + value: WKAudiovisualMediaTypeEnum.values[pigeonMap['value']! as int], ); } } -class WKWebsiteDataTypesEnumData { - WKWebsiteDataTypesEnumData({ - this.value, +class WKWebsiteDataTypeEnumData { + WKWebsiteDataTypeEnumData({ + required this.value, }); - WKWebsiteDataTypesEnum? value; + WKWebsiteDataTypeEnum value; Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value?.index; + pigeonMap['value'] = value.index; return pigeonMap; } - static WKWebsiteDataTypesEnumData decode(Object message) { + static WKWebsiteDataTypeEnumData decode(Object message) { final Map pigeonMap = message as Map; - return WKWebsiteDataTypesEnumData( - value: pigeonMap['value'] != null - ? WKWebsiteDataTypesEnum.values[pigeonMap['value']! as int] - : null, + return WKWebsiteDataTypeEnumData( + value: WKWebsiteDataTypeEnum.values[pigeonMap['value']! as int], ); } } class NSHttpCookiePropertyKeyEnumData { NSHttpCookiePropertyKeyEnumData({ - this.value, + required this.value, }); - NSHttpCookiePropertyKeyEnum? value; + NSHttpCookiePropertyKeyEnum value; Object encode() { final Map pigeonMap = {}; - pigeonMap['value'] = value?.index; + pigeonMap['value'] = value.index; return pigeonMap; } static NSHttpCookiePropertyKeyEnumData decode(Object message) { final Map pigeonMap = message as Map; return NSHttpCookiePropertyKeyEnumData( - value: pigeonMap['value'] != null - ? NSHttpCookiePropertyKeyEnum.values[pigeonMap['value']! as int] - : null, + value: NSHttpCookiePropertyKeyEnum.values[pigeonMap['value']! as int], ); } } @@ -262,22 +252,27 @@ class WKUserScriptData { class NSHttpCookieData { NSHttpCookieData({ - required this.properties, + required this.propertyKeys, + required this.propertyValues, }); - Map properties; + List propertyKeys; + List propertyValues; Object encode() { final Map pigeonMap = {}; - pigeonMap['properties'] = properties; + pigeonMap['propertyKeys'] = propertyKeys; + pigeonMap['propertyValues'] = propertyValues; return pigeonMap; } static NSHttpCookieData decode(Object message) { final Map pigeonMap = message as Map; return NSHttpCookieData( - properties: (pigeonMap['properties'] as Map?)! - .cast(), + propertyKeys: (pigeonMap['propertyKeys'] as List?)! + .cast(), + propertyValues: + (pigeonMap['propertyValues'] as List?)!.cast(), ); } } @@ -286,7 +281,7 @@ class _WKWebsiteDataStoreHostApiCodec extends StandardMessageCodec { const _WKWebsiteDataStoreHostApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is WKWebsiteDataTypesEnumData) { + if (value is WKWebsiteDataTypeEnumData) { buffer.putUint8(128); writeValue(buffer, value.encode()); } else { @@ -298,7 +293,7 @@ class _WKWebsiteDataStoreHostApiCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 128: - return WKWebsiteDataTypesEnumData.decode(readValue(buffer)!); + return WKWebsiteDataTypeEnumData.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -371,15 +366,15 @@ class WKWebsiteDataStoreHostApi { Future removeDataOfTypes( int arg_instanceId, - List arg_dataTypes, - double arg_secondsModifiedSinceEpoch) async { + List arg_dataTypes, + double arg_modificationTimeInSecondsSinceEpoch) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([ arg_instanceId, arg_dataTypes, - arg_secondsModifiedSinceEpoch + arg_modificationTimeInSecondsSinceEpoch ]) as Map?; if (replyMap == null) { throw PlatformException( @@ -420,35 +415,6 @@ class UIViewHostApi { static const MessageCodec codec = _UIViewHostApiCodec(); - Future> getContentOffset(int arg_instanceId) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.UIViewHostApi.getContentOffset', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; - throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], - ); - } else if (replyMap['result'] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyMap['result'] as List?)!.cast(); - } - } - Future setBackgroundColor(int arg_instanceId, int? arg_value) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.UIViewHostApi.setBackgroundColor', codec, @@ -1352,7 +1318,7 @@ class _WKWebViewHostApiCodec extends StandardMessageCodec { } else if (value is WKUserScriptInjectionTimeEnumData) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is WKWebsiteDataTypesEnumData) { + } else if (value is WKWebsiteDataTypeEnumData) { buffer.putUint8(135); writeValue(buffer, value.encode()); } else { @@ -1385,7 +1351,7 @@ class _WKWebViewHostApiCodec extends StandardMessageCodec { return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); case 135: - return WKWebsiteDataTypesEnumData.decode(readValue(buffer)!); + return WKWebsiteDataTypeEnumData.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); 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 ec7bb5377de0..5c127c5654c6 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 @@ -146,6 +146,9 @@ enum NSHttpCookiePropertyKey { /// A String indicating the same-site policy for the cookie. /// + /// This is only supported on iOS version 13+. This value will be ignored on + /// versions < 13. + /// /// See https://developer.apple.com/documentation/foundation/nshttpcookiesamesitepolicy. sameSitePolicy, 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 ab63db67bba1..88f763fe6ba3 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 @@ -53,7 +53,7 @@ enum WKAudiovisualMediaType { /// Types of data that websites store. /// /// See https://developer.apple.com/documentation/webkit/wkwebsitedatarecord/data_store_record_types?language=objc. -enum WKWebsiteDataTypes { +enum WKWebsiteDataType { /// Cookies. cookies, @@ -67,13 +67,13 @@ enum WKWebsiteDataTypes { offlineWebApplicationCache, /// HTML local storage. - localStroage, + localStorage, /// HTML session storage. sessionStorage, /// WebSQL databases. - sqlDatabases, + webSQLDatabases, /// IndexedDB databases. indexedDBDatabases, @@ -291,7 +291,7 @@ class WKWebsiteDataStore { /// /// Returns whether any data was removed. Future removeDataOfTypes( - Set dataTypes, + Set dataTypes, DateTime since, ) { return _websiteDataStoreApi.removeDataOfTypesForInstances( @@ -440,7 +440,10 @@ class WKUserContentController { ); } - /// Uninstalls all custom message handlers associated with the user content controller. + /// Uninstalls all custom message handlers associated with the user content + /// controller. + /// + /// Only supported on iOS version 14+. Future removeAllScriptMessageHandlers() { return _userContentControllerApi.removeAllScriptMessageHandlersForInstances( this, @@ -550,6 +553,7 @@ class WKWebViewConfiguration { Future setMediaTypesRequiringUserActionForPlayback( Set types, ) { + assert(types.isNotEmpty); return _webViewConfigurationApi .setMediaTypesRequiringUserActionForPlaybackForInstances( this, 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 b970ab26bcc7..8bf29308371e 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 @@ -10,52 +10,53 @@ import '../common/web_kit.pigeon.dart'; import '../foundation/foundation.dart'; import 'web_kit.dart'; -Iterable _toWKWebsiteDataTypesEnumData( - Iterable types) { - return types.map((WKWebsiteDataTypes type) { - late final WKWebsiteDataTypesEnum value; +Iterable _toWKWebsiteDataTypeEnumData( + Iterable types) { + return types.map((WKWebsiteDataType type) { + late final WKWebsiteDataTypeEnum value; switch (type) { - case WKWebsiteDataTypes.cookies: - value = WKWebsiteDataTypesEnum.cookies; + case WKWebsiteDataType.cookies: + value = WKWebsiteDataTypeEnum.cookies; break; - case WKWebsiteDataTypes.memoryCache: - value = WKWebsiteDataTypesEnum.memoryCache; + case WKWebsiteDataType.memoryCache: + value = WKWebsiteDataTypeEnum.memoryCache; break; - case WKWebsiteDataTypes.diskCache: - value = WKWebsiteDataTypesEnum.diskCache; + case WKWebsiteDataType.diskCache: + value = WKWebsiteDataTypeEnum.diskCache; break; - case WKWebsiteDataTypes.offlineWebApplicationCache: - value = WKWebsiteDataTypesEnum.offlineWebApplicationCache; + case WKWebsiteDataType.offlineWebApplicationCache: + value = WKWebsiteDataTypeEnum.offlineWebApplicationCache; break; - case WKWebsiteDataTypes.localStroage: - value = WKWebsiteDataTypesEnum.localStroage; + case WKWebsiteDataType.localStorage: + value = WKWebsiteDataTypeEnum.localStorage; break; - case WKWebsiteDataTypes.sessionStorage: - value = WKWebsiteDataTypesEnum.sessionStorage; + case WKWebsiteDataType.sessionStorage: + value = WKWebsiteDataTypeEnum.sessionStorage; break; - case WKWebsiteDataTypes.sqlDatabases: - value = WKWebsiteDataTypesEnum.sqlDatabases; + case WKWebsiteDataType.webSQLDatabases: + value = WKWebsiteDataTypeEnum.webSQLDatabases; break; - case WKWebsiteDataTypes.indexedDBDatabases: - value = WKWebsiteDataTypesEnum.indexedDBDatabases; + case WKWebsiteDataType.indexedDBDatabases: + value = WKWebsiteDataTypeEnum.indexedDBDatabases; break; } - return WKWebsiteDataTypesEnumData(value: value); + return WKWebsiteDataTypeEnumData(value: value); }); } extension _NSHttpCookieConverter on NSHttpCookie { NSHttpCookieData toNSHttpCookieData() { + final Iterable keys = properties.keys; return NSHttpCookieData( - properties: properties.map( - (NSHttpCookiePropertyKey key, Object value) { - return MapEntry( - key.toNSHttpCookiePropertyKeyEnumData(), - value.toString(), - ); + propertyKeys: keys.map( + (NSHttpCookiePropertyKey key) { + return key.toNSHttpCookiePropertyKeyEnumData(); }, - ), + ).toList(), + propertyValues: keys + .map((NSHttpCookiePropertyKey key) => properties[key]!) + .toList(), ); } } @@ -258,12 +259,12 @@ class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi { /// Calls [removeDataOfTypes] with the ids of the provided object instances. Future removeDataOfTypesForInstances( WKWebsiteDataStore instance, - Set dataTypes, { + Set dataTypes, { required double secondsModifiedSinceEpoch, }) { return removeDataOfTypes( instanceManager.getInstanceId(instance)!, - _toWKWebsiteDataTypesEnumData(dataTypes).toList(), + _toWKWebsiteDataTypeEnumData(dataTypes).toList(), secondsModifiedSinceEpoch, ); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart index c73111d73fd0..46322319ae89 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart @@ -19,7 +19,7 @@ class WebKitCookieManager extends WebViewCookieManagerPlatform { @override Future clearCookies() async { return websiteDataStore.removeDataOfTypes( - {WKWebsiteDataTypes.cookies}, + {WKWebsiteDataType.cookies}, DateTime.fromMillisecondsSinceEpoch(0), ); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 8c37112d7a24..0a177d854bc2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -227,11 +227,11 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { @override Future clearCache() { return webView.configuration.websiteDataStore.removeDataOfTypes( - { - WKWebsiteDataTypes.memoryCache, - WKWebsiteDataTypes.diskCache, - WKWebsiteDataTypes.offlineWebApplicationCache, - WKWebsiteDataTypes.localStroage, + { + WKWebsiteDataType.memoryCache, + WKWebsiteDataType.diskCache, + WKWebsiteDataType.offlineWebApplicationCache, + WKWebsiteDataType.localStorage, }, DateTime.fromMillisecondsSinceEpoch(0), ); @@ -517,8 +517,12 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { Set removedJavaScriptChannels = const {}, }) async { webView.configuration.userContentController.removeAllUserScripts(); - webView.configuration.userContentController - .removeAllScriptMessageHandlers(); + // TODO(bparrishMines): This can be replaced with + // `removeAllScriptMessageHandlers` once Dart supports runtime version + // checking. (e.g. The equivalent to @availability in Objective-C.) + _scriptMessageHandlers.keys.forEach( + webView.configuration.userContentController.removeScriptMessageHandler, + ); removedJavaScriptChannels.forEach(_scriptMessageHandlers.remove); final Set remainingNames = _scriptMessageHandlers.keys.toSet(); 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 509fa8e1f7dd..6844a5fa67ad 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -37,10 +37,10 @@ enum NSKeyValueObservingOptionsEnum { priorNotification, } +// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class NSKeyValueObservingOptionsEnumData { - // TODO(bparrishMines): Generated code fails when enums are marked as nonnull. - // Change to nonnull once this is fixed: https://github.com/flutter/flutter/issues/100594 - late NSKeyValueObservingOptionsEnum? value; + late NSKeyValueObservingOptionsEnum value; } /// Mirror of NSKeyValueChange. @@ -53,8 +53,10 @@ enum NSKeyValueChangeEnum { replacement, } +// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class NSKeyValueChangeEnumData { - late NSKeyValueChangeEnum? value; + late NSKeyValueChangeEnum value; } /// Mirror of NSKeyValueChangeKey. @@ -68,8 +70,10 @@ enum NSKeyValueChangeKeyEnum { oldValue, } +// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class NSKeyValueChangeKeyEnumData { - late NSKeyValueChangeKeyEnum? value; + late NSKeyValueChangeKeyEnum value; } /// Mirror of WKUserScriptInjectionTime. @@ -80,8 +84,10 @@ enum WKUserScriptInjectionTimeEnum { atDocumentEnd, } +// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class WKUserScriptInjectionTimeEnumData { - late WKUserScriptInjectionTimeEnum? value; + late WKUserScriptInjectionTimeEnum value; } /// Mirror of WKAudiovisualMediaTypes. @@ -94,26 +100,30 @@ enum WKAudiovisualMediaTypeEnum { all, } +// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class WKAudiovisualMediaTypeEnumData { - late WKAudiovisualMediaTypeEnum? value; + late WKAudiovisualMediaTypeEnum value; } /// Mirror of WKWebsiteDataTypes. /// /// See https://developer.apple.com/documentation/webkit/wkwebsitedatarecord/data_store_record_types?language=objc. -enum WKWebsiteDataTypesEnum { +enum WKWebsiteDataTypeEnum { cookies, memoryCache, diskCache, offlineWebApplicationCache, - localStroage, + localStorage, sessionStorage, - sqlDatabases, + webSQLDatabases, indexedDBDatabases, } -class WKWebsiteDataTypesEnumData { - late WKWebsiteDataTypesEnum? value; +// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 +class WKWebsiteDataTypeEnumData { + late WKWebsiteDataTypeEnum value; } /// Mirror of WKNavigationActionPolicy. @@ -124,8 +134,10 @@ enum WKNavigationActionPolicyEnum { cancel, } +// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class WKNavigationActionPolicyEnumData { - late WKNavigationActionPolicyEnum? value; + late WKNavigationActionPolicyEnum value; } /// Mirror of NSHTTPCookiePropertyKey. @@ -148,8 +160,10 @@ enum NSHttpCookiePropertyKeyEnum { version, } +// TODO(bparrishMines): Enums need be wrapped in a data class because thay can't +// be used as primitive arguments. See https://github.com/flutter/flutter/issues/87307 class NSHttpCookiePropertyKeyEnumData { - late NSHttpCookiePropertyKeyEnum? value; + late NSHttpCookiePropertyKeyEnum value; } /// Mirror of NSURLRequest. @@ -207,7 +221,13 @@ class WKScriptMessageData { /// /// See https://developer.apple.com/documentation/foundation/nshttpcookie?language=objc. class NSHttpCookieData { - late Map properties; + // TODO(bparrishMines): Change to a map when Objective-C data classes conform + // to `NSCopying`. See https://github.com/flutter/flutter/issues/103383. + // `NSDictionary`s are unable to use data classes as keys because they don't + // conform to `NSCopying`. This splits the map of properties into a list of + // keys and values with the ordered maintained. + late List propertyKeys; + late List propertyValues; } /// Mirror of WKWebsiteDataStore. @@ -216,7 +236,7 @@ class NSHttpCookieData { @HostApi(dartHostTestHandler: 'TestWKWebsiteDataStoreHostApi') abstract class WKWebsiteDataStoreHostApi { @ObjCSelector( - 'createDataStoreFromConfigurationWithIdentifier:configurationIdentifier:', + 'createFromWebViewConfigurationWithIdentifier:configurationIdentifier:', ) void createFromWebViewConfiguration( int instanceId, @@ -227,13 +247,13 @@ abstract class WKWebsiteDataStoreHostApi { void createDefaultDataStore(int instanceId); @ObjCSelector( - 'removeDataFromDataStoreWithIdentifier:ofTypes:secondsModifiedSinceEpoch:', + 'removeDataFromDataStoreWithIdentifier:ofTypes:modifiedSince:', ) @async bool removeDataOfTypes( int instanceId, - List dataTypes, - double secondsModifiedSinceEpoch, + List dataTypes, + double modificationTimeInSecondsSinceEpoch, ); } @@ -242,9 +262,6 @@ abstract class WKWebsiteDataStoreHostApi { /// See https://developer.apple.com/documentation/uikit/uiview?language=objc. @HostApi(dartHostTestHandler: 'TestUIViewHostApi') abstract class UIViewHostApi { - @ObjCSelector('contentOffsetForViewWithIdentifier:') - List getContentOffset(int instanceId); - @ObjCSelector('setBackgroundColorForViewWithIdentifier:toValue:') void setBackgroundColor(int instanceId, int? value); @@ -263,7 +280,7 @@ abstract class UIScrollViewHostApi { @ObjCSelector('contentOffsetForScrollViewWithIdentifier:') List getContentOffset(int instanceId); - @ObjCSelector('scrollByForScrollViewWithIdentifier:toX:y:') + @ObjCSelector('scrollByForScrollViewWithIdentifier:x:y:') void scrollBy(int instanceId, double x, double y); @ObjCSelector('setContentOffsetForScrollViewWithIdentifier:toX:y:') @@ -282,7 +299,7 @@ abstract class WKWebViewConfigurationHostApi { void createFromWebView(int instanceId, int webViewInstanceId); @ObjCSelector( - 'setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:isAlowed:', + 'setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:isAllowed:', ) void setAllowsInlineMediaPlayback(int instanceId, bool allow); @@ -335,7 +352,9 @@ abstract class WKUserContentControllerHostApi { /// See https://developer.apple.com/documentation/webkit/wkpreferences?language=objc. @HostApi(dartHostTestHandler: 'TestWKPreferencesHostApi') abstract class WKPreferencesHostApi { - @ObjCSelector('createFromWebViewConfiguration:configurationIdentifier:') + @ObjCSelector( + 'createFromWebViewConfigurationWithIdentifier:configurationIdentifier:', + ) void createFromWebViewConfiguration( int instanceId, int configurationInstanceId, @@ -498,5 +517,6 @@ abstract class WKHttpCookieStoreHostApi { ); @ObjCSelector('setCookieForStoreWithIdentifier:cookie:') + @async void setCookie(int instanceId, NSHttpCookieData cookie); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart index 042ddedbd769..1f963b4d9bc3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart @@ -18,7 +18,7 @@ class _TestWKWebsiteDataStoreHostApiCodec extends StandardMessageCodec { const _TestWKWebsiteDataStoreHostApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is WKWebsiteDataTypesEnumData) { + if (value is WKWebsiteDataTypeEnumData) { buffer.putUint8(128); writeValue(buffer, value.encode()); } else { @@ -30,7 +30,7 @@ class _TestWKWebsiteDataStoreHostApiCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 128: - return WKWebsiteDataTypesEnumData.decode(readValue(buffer)!); + return WKWebsiteDataTypeEnumData.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -47,8 +47,8 @@ abstract class TestWKWebsiteDataStoreHostApi { void createDefaultDataStore(int instanceId); Future removeDataOfTypes( int instanceId, - List dataTypes, - double secondsModifiedSinceEpoch); + List dataTypes, + double modificationTimeInSecondsSinceEpoch); static void setup(TestWKWebsiteDataStoreHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -110,15 +110,16 @@ abstract class TestWKWebsiteDataStoreHostApi { final int? arg_instanceId = (args[0] as int?); assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes was null, expected non-null int.'); - final List? arg_dataTypes = - (args[1] as List?)?.cast(); + final List? arg_dataTypes = + (args[1] as List?)?.cast(); assert(arg_dataTypes != null, - 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes was null, expected non-null List.'); - final double? arg_secondsModifiedSinceEpoch = (args[2] as double?); - assert(arg_secondsModifiedSinceEpoch != null, + 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes was null, expected non-null List.'); + final double? arg_modificationTimeInSecondsSinceEpoch = + (args[2] as double?); + assert(arg_modificationTimeInSecondsSinceEpoch != null, 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes was null, expected non-null double.'); - final bool output = await api.removeDataOfTypes( - arg_instanceId!, arg_dataTypes!, arg_secondsModifiedSinceEpoch!); + final bool output = await api.removeDataOfTypes(arg_instanceId!, + arg_dataTypes!, arg_modificationTimeInSecondsSinceEpoch!); return {'result': output}; }); } @@ -133,30 +134,10 @@ class _TestUIViewHostApiCodec extends StandardMessageCodec { abstract class TestUIViewHostApi { static const MessageCodec codec = _TestUIViewHostApiCodec(); - List getContentOffset(int instanceId); void setBackgroundColor(int instanceId, int? value); void setOpaque(int instanceId, bool opaque); static void setup(TestUIViewHostApi? api, {BinaryMessenger? binaryMessenger}) { - { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.UIViewHostApi.getContentOffset', codec, - binaryMessenger: binaryMessenger); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.UIViewHostApi.getContentOffset 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.UIViewHostApi.getContentOffset was null, expected non-null int.'); - final List output = api.getContentOffset(arg_instanceId!); - return {'result': output}; - }); - } - } { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.UIViewHostApi.setBackgroundColor', codec, @@ -909,7 +890,7 @@ class _TestWKWebViewHostApiCodec extends StandardMessageCodec { } else if (value is WKUserScriptInjectionTimeEnumData) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is WKWebsiteDataTypesEnumData) { + } else if (value is WKWebsiteDataTypeEnumData) { buffer.putUint8(135); writeValue(buffer, value.encode()); } else { @@ -942,7 +923,7 @@ class _TestWKWebViewHostApiCodec extends StandardMessageCodec { return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); case 135: - return WKWebsiteDataTypesEnumData.decode(readValue(buffer)!); + return WKWebsiteDataTypeEnumData.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -1418,7 +1399,7 @@ abstract class TestWKHttpCookieStoreHostApi { void createFromWebsiteDataStore( int instanceId, int websiteDataStoreInstanceId); - void setCookie(int instanceId, NSHttpCookieData cookie); + Future setCookie(int instanceId, NSHttpCookieData cookie); static void setup(TestWKHttpCookieStoreHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1462,7 +1443,7 @@ abstract class TestWKHttpCookieStoreHostApi { final NSHttpCookieData? arg_cookie = (args[1] as NSHttpCookieData?); assert(arg_cookie != null, 'Argument for dev.flutter.pigeon.WKHttpCookieStoreHostApi.setCookie was null, expected non-null NSHttpCookieData.'); - api.setCookie(arg_instanceId!, arg_cookie!); + await api.setCookie(arg_instanceId!, arg_cookie!); return {}; }); } 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 908709c90134..a9f9b2c322c7 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 @@ -185,10 +185,6 @@ class MockTestUIViewHostApi extends _i1.Mock implements _i2.TestUIViewHostApi { _i1.throwOnMissingStub(this); } - @override - List getContentOffset(int? instanceId) => - (super.noSuchMethod(Invocation.method(#getContentOffset, [instanceId]), - returnValue: []) as List); @override void setBackgroundColor(int? instanceId, int? value) => super.noSuchMethod( Invocation.method(#setBackgroundColor, [instanceId, value]), 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 8564bf889c2f..ae13ca9e6b6d 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 @@ -95,21 +95,21 @@ void main() { expect( websiteDataStore.removeDataOfTypes( - {WKWebsiteDataTypes.cookies}, + {WKWebsiteDataType.cookies}, DateTime.fromMillisecondsSinceEpoch(5000), ), completion(true), ); - final List typeData = + final List typeData = verify(mockPlatformHostApi.removeDataOfTypes( instanceManager.getInstanceId(websiteDataStore), captureAny, 5.0, - )).captured.single.cast() - as List; + )).captured.single.cast() + as List; - expect(typeData.single.value, WKWebsiteDataTypesEnum.cookies); + expect(typeData.single.value, WKWebsiteDataTypeEnum.cookies); }); }); @@ -169,10 +169,10 @@ void main() { ).captured.single as NSHttpCookieData; expect( - cookie.properties.entries.single.key!.value, + cookie.propertyKeys.single!.value, NSHttpCookiePropertyKeyEnum.comment, ); - expect(cookie.properties.entries.single.value, 'aComment'); + expect(cookie.propertyValues.single, 'aComment'); }); }); 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 d4f1fefc190c..4ffb7d4c19d3 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 @@ -2,11 +2,11 @@ // in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. // Do not manually edit this file. -import 'dart:async' as _i4; +import 'dart:async' as _i3; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart' - as _i3; + as _i4; import '../common/test_web_kit.pigeon.dart' as _i2; @@ -37,9 +37,10 @@ class MockTestWKHttpCookieStoreHostApi extends _i1.Mock [instanceId, websiteDataStoreInstanceId]), returnValueForMissingStub: null); @override - void setCookie(int? instanceId, _i3.NSHttpCookieData? cookie) => - super.noSuchMethod(Invocation.method(#setCookie, [instanceId, cookie]), - returnValueForMissingStub: null); + _i3.Future setCookie(int? instanceId, _i4.NSHttpCookieData? cookie) => + (super.noSuchMethod(Invocation.method(#setCookie, [instanceId, cookie]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); } /// A class which mocks [TestWKNavigationDelegateHostApi]. @@ -149,7 +150,7 @@ class MockTestWKUserContentControllerHostApi extends _i1.Mock Invocation.method(#removeAllScriptMessageHandlers, [instanceId]), returnValueForMissingStub: null); @override - void addUserScript(int? instanceId, _i3.WKUserScriptData? userScript) => super + void addUserScript(int? instanceId, _i4.WKUserScriptData? userScript) => super .noSuchMethod(Invocation.method(#addUserScript, [instanceId, userScript]), returnValueForMissingStub: null); @override @@ -184,7 +185,7 @@ class MockTestWKWebViewConfigurationHostApi extends _i1.Mock returnValueForMissingStub: null); @override void setMediaTypesRequiringUserActionForPlayback( - int? instanceId, List<_i3.WKAudiovisualMediaTypeEnumData?>? types) => + int? instanceId, List<_i4.WKAudiovisualMediaTypeEnumData?>? types) => super.noSuchMethod( Invocation.method(#setMediaTypesRequiringUserActionForPlayback, [instanceId, types]), @@ -225,7 +226,7 @@ class MockTestWKWebViewHostApi extends _i1.Mock Invocation.method(#getEstimatedProgress, [instanceId]), returnValue: 0.0) as double); @override - void loadRequest(int? instanceId, _i3.NSUrlRequestData? request) => + void loadRequest(int? instanceId, _i4.NSUrlRequestData? request) => super.noSuchMethod(Invocation.method(#loadRequest, [instanceId, request]), returnValueForMissingStub: null); @override @@ -278,12 +279,12 @@ class MockTestWKWebViewHostApi extends _i1.Mock Invocation.method(#setCustomUserAgent, [instanceId, userAgent]), returnValueForMissingStub: null); @override - _i4.Future evaluateJavaScript( + _i3.Future evaluateJavaScript( int? instanceId, String? javaScriptString) => (super.noSuchMethod( Invocation.method( #evaluateJavaScript, [instanceId, javaScriptString]), - returnValue: Future.value()) as _i4.Future); + returnValue: Future.value()) as _i3.Future); } /// A class which mocks [TestWKWebsiteDataStoreHostApi]. @@ -307,12 +308,12 @@ class MockTestWKWebsiteDataStoreHostApi extends _i1.Mock Invocation.method(#createDefaultDataStore, [instanceId]), returnValueForMissingStub: null); @override - _i4.Future removeDataOfTypes( + _i3.Future removeDataOfTypes( int? instanceId, - List<_i3.WKWebsiteDataTypesEnumData?>? dataTypes, + List<_i4.WKWebsiteDataTypeEnumData?>? dataTypes, double? secondsModifiedSinceEpoch) => (super.noSuchMethod( Invocation.method(#removeDataOfTypes, [instanceId, dataTypes, secondsModifiedSinceEpoch]), - returnValue: Future.value(false)) as _i4.Future); + returnValue: Future.value(false)) as _i3.Future); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart index 8f23ff6b4a3d..6baff12eda17 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart @@ -37,12 +37,12 @@ void main() { test('clearCookies', () async { when(mockWebsiteDataStore.removeDataOfTypes( - {WKWebsiteDataTypes.cookies}, any)) + {WKWebsiteDataType.cookies}, any)) .thenAnswer((_) => Future.value(true)); expect(cookieManager.clearCookies(), completion(true)); when(mockWebsiteDataStore.removeDataOfTypes( - {WKWebsiteDataTypes.cookies}, any)) + {WKWebsiteDataType.cookies}, any)) .thenAnswer((_) => Future.value(false)); expect(cookieManager.clearCookies(), completion(false)); }); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart index a85c57f7bdb3..8d7751da785f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart @@ -79,7 +79,7 @@ class MockWKWebsiteDataStore extends _i1.Mock returnValue: _FakeWKHttpCookieStore_0()) as _i2.WKHttpCookieStore); @override _i3.Future removeDataOfTypes( - Set<_i2.WKWebsiteDataTypes>? dataTypes, DateTime? since) => + Set<_i2.WKWebsiteDataType>? dataTypes, DateTime? since) => (super.noSuchMethod( Invocation.method(#removeDataOfTypes, [dataTypes, since]), returnValue: Future.value(false)) as _i3.Future); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index e5042dab78e4..d77feee22dd1 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -330,7 +330,7 @@ void main() { final List javaScriptChannels = verifyInOrder([ mockUserContentController.removeAllUserScripts(), - mockUserContentController.removeAllScriptMessageHandlers(), + mockUserContentController.removeScriptMessageHandler('myChannel'), mockUserContentController.addScriptMessageHandler( captureAny, captureAny, @@ -372,7 +372,6 @@ void main() { )); verify(mockUserContentController.removeAllUserScripts()); - verify(mockUserContentController.removeAllScriptMessageHandlers()); verifyNever(mockUserContentController.addScriptMessageHandler( any, any, @@ -769,11 +768,11 @@ void main() { await buildWidget(tester); when( mockWebsiteDataStore.removeDataOfTypes( - { - WKWebsiteDataTypes.memoryCache, - WKWebsiteDataTypes.diskCache, - WKWebsiteDataTypes.offlineWebApplicationCache, - WKWebsiteDataTypes.localStroage, + { + WKWebsiteDataType.memoryCache, + WKWebsiteDataType.diskCache, + WKWebsiteDataType.offlineWebApplicationCache, + WKWebsiteDataType.localStorage, }, DateTime.fromMillisecondsSinceEpoch(0), ), @@ -835,12 +834,15 @@ void main() { await testController.removeJavascriptChannels({'c'}); - verify(mockUserContentController.removeAllScriptMessageHandlers()); verify(mockUserContentController.removeAllUserScripts()); + verify(mockUserContentController.removeScriptMessageHandler('c')); + verify(mockUserContentController.removeScriptMessageHandler('d')); final List javaScriptChannels = verify( mockUserContentController.addScriptMessageHandler( - captureAny, captureAny), + captureAny, + captureAny, + ), ).captured; expect( javaScriptChannels[0], diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index 1138d13f0c25..066f33a9774e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -434,7 +434,7 @@ class MockWKWebsiteDataStore extends _i1.Mock returnValue: _FakeWKHttpCookieStore_6()) as _i3.WKHttpCookieStore); @override _i5.Future removeDataOfTypes( - Set<_i3.WKWebsiteDataTypes>? dataTypes, DateTime? since) => + Set<_i3.WKWebsiteDataType>? dataTypes, DateTime? since) => (super.noSuchMethod( Invocation.method(#removeDataOfTypes, [dataTypes, since]), returnValue: Future.value(false)) as _i5.Future); From 2ca342c22777a5ecb63999bcb969382af00f9f43 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 10 May 2022 19:19:10 -0400 Subject: [PATCH 291/844] Roll Flutter from 4bed76757db7 to df7111a848cb (42 revisions) (#5696) --- .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 40188c21bd43..bca64d8293a3 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -4bed76757db7a4eea74d77d5a63804ef52198f37 +df7111a848cb0cc076a9c89d48332f0ec2700c45 From 3993fa7126376c2362bf078a86def2cf959091dd Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 11 May 2022 11:48:47 -0400 Subject: [PATCH 292/844] Re-sync analysis_options.yaml with flutter/flutter (#5695) The analysis options have gotten behind; this re-syncs to the current state of flutter/flutter. For options that are non-trivial to enable, either because they are non-trivial to fix, or touch a very large number of files, they are locally disabled with clear "LOCAL CHANGE" markers so that it's obvious where we are out of sync. For options that are simple to resolve, they are enabled in the PR. Part of https://github.com/flutter/flutter/issues/76229 --- analysis_options.yaml | 135 ++++++++++-------- packages/camera/camera/CHANGELOG.md | 4 + packages/camera/camera/example/lib/main.dart | 5 +- packages/camera/camera/pubspec.yaml | 2 +- packages/camera/camera_web/CHANGELOG.md | 4 + .../example/integration_test/camera_test.dart | 2 +- .../integration_test/camera_web_test.dart | 6 +- .../integration_test/helpers/mocks.dart | 4 +- .../camera/camera_web/lib/src/camera.dart | 3 + .../camera_web/lib/src/camera_service.dart | 2 +- .../camera/camera_web/lib/src/camera_web.dart | 4 +- packages/camera/camera_web/pubspec.yaml | 2 +- .../file_selector/file_selector/CHANGELOG.md | 4 + .../test/file_selector_test.dart | 2 + .../file_selector_web/CHANGELOG.md | 4 + .../file_selector_web_test.dart | 42 +++--- .../file_selector_web/lib/src/utils.dart | 2 +- .../file_selector_web/pubspec.yaml | 2 +- .../google_maps_flutter/CHANGELOG.md | 4 + .../example/lib/animate_camera.dart | 1 + .../example/lib/move_camera.dart | 1 + .../example/lib/place_circle.dart | 1 + .../example/lib/place_marker.dart | 3 +- .../example/lib/place_polygon.dart | 1 + .../example/lib/place_polyline.dart | 1 + .../example/lib/scrolling_map.dart | 2 +- .../example/lib/snapshot.dart | 1 + .../example/lib/tile_overlay.dart | 1 + .../google_sign_in_web/CHANGELOG.md | 4 + .../example/integration_test/auth2_test.dart | 2 +- .../integration_test/gapi_utils_test.dart | 2 + .../integration_test/src/test_utils.dart | 2 +- .../lib/google_sign_in_web.dart | 2 +- .../lib/src/generated/gapiauth2.dart | 19 +-- .../google_sign_in_web/lib/src/utils.dart | 1 + .../google_sign_in_web/pubspec.yaml | 2 +- .../image_picker/image_picker/CHANGELOG.md | 4 + .../image_picker/example/lib/main.dart | 11 +- .../image_picker/image_picker/pubspec.yaml | 2 +- .../image_picker_android/CHANGELOG.md | 4 + .../example/lib/main.dart | 11 +- .../image_picker_android/pubspec.yaml | 2 +- .../image_picker_for_web/CHANGELOG.md | 4 + .../integration_test/image_resizer_test.dart | 4 +- .../lib/src/image_resizer.dart | 3 +- .../image_picker_for_web/pubspec.yaml | 2 +- .../image_picker_ios/CHANGELOG.md | 4 + .../image_picker_ios/example/lib/main.dart | 55 +------ .../image_picker_ios/pubspec.yaml | 2 +- .../CHANGELOG.md | 4 + .../test/picked_file_io_test.dart | 2 +- .../image_picker_windows/CHANGELOG.md | 4 + .../example/lib/main.dart | 4 +- .../lib/image_picker_windows.dart | 2 +- .../image_picker_windows/pubspec.yaml | 2 +- .../in_app_purchase/CHANGELOG.md | 4 + .../in_app_purchase/example/lib/main.dart | 4 +- .../in_app_purchase/pubspec.yaml | 2 +- .../in_app_purchase_android/CHANGELOG.md | 4 + .../example/lib/main.dart | 4 +- .../billing_client_wrapper.dart | 4 +- ...pp_purchase_android_platform_addition.dart | 8 +- .../types/google_play_product_details.dart | 2 +- .../in_app_purchase_android/pubspec.yaml | 2 +- .../in_app_purchase_storekit/CHANGELOG.md | 4 + .../example/lib/main.dart | 4 +- .../in_app_purchase_storekit/pubspec.yaml | 2 +- .../store_kit_wrappers/sk_product_test.dart | 5 +- .../local_auth_android/CHANGELOG.md | 4 + .../lib/types/auth_messages_android.dart | 2 +- .../local_auth_android/pubspec.yaml | 2 +- .../CHANGELOG.md | 3 +- .../lib/src/method_channel_path_provider.dart | 1 + .../pubspec.yaml | 2 +- .../path_provider_windows/CHANGELOG.md | 4 + .../test/path_provider_windows_test.dart | 5 +- .../plugin_platform_interface/CHANGELOG.md | 1 + .../test/plugin_platform_interface_test.dart | 4 + .../quick_actions/quick_actions/CHANGELOG.md | 4 + .../test/quick_actions_test.dart | 6 +- .../shared_preferences/CHANGELOG.md | 4 + .../lib/shared_preferences.dart | 2 +- .../shared_preferences/pubspec.yaml | 2 +- .../test/shared_preferences_test.dart | 2 +- .../url_launcher/url_launcher/CHANGELOG.md | 4 + .../url_launcher/lib/src/legacy_api.dart | 4 +- .../url_launcher/url_launcher/pubspec.yaml | 2 +- .../mocks/mock_url_launcher_platform.dart | 2 + .../url_launcher_web/CHANGELOG.md | 4 + .../lib/url_launcher_web.dart | 1 + .../url_launcher_web/pubspec.yaml | 2 +- .../video_player/video_player/CHANGELOG.md | 4 + .../integration_test/video_player_test.dart | 2 +- .../video_player/example/lib/main.dart | 2 +- .../video_player/video_player/pubspec.yaml | 2 +- .../test/closed_caption_file_test.dart | 3 +- .../video_player/test/sub_rip_file_test.dart | 2 +- .../video_player_web/CHANGELOG.md | 4 + .../lib/src/video_player.dart | 1 + .../video_player_web/pubspec.yaml | 2 +- .../webview_flutter/CHANGELOG.md | 4 + .../webview_flutter_test.dart | 4 +- .../webview_flutter/example/lib/main.dart | 7 +- .../webview_flutter/pubspec.yaml | 2 +- .../test/webview_flutter_test.dart | 2 +- .../webview_flutter_android/CHANGELOG.md | 4 + .../webview_flutter_test.dart | 4 +- .../example/lib/main.dart | 5 +- .../webview_flutter_android/pubspec.yaml | 2 +- .../webview_flutter_web/CHANGELOG.md | 4 + .../lib/webview_flutter_web.dart | 11 +- .../webview_flutter_web/pubspec.yaml | 2 +- .../test/webview_flutter_web_test.dart | 4 +- .../webview_flutter_wkwebview/CHANGELOG.md | 4 + .../webview_flutter_test.dart | 4 +- .../example/lib/main.dart | 1 + .../lib/src/web_kit_webview_widget.dart | 12 +- .../webview_flutter_wkwebview/pubspec.yaml | 2 +- .../src/common/function_flutter_api_test.dart | 2 +- .../test/src/web_kit/web_kit_test.dart | 6 +- .../test/src/web_kit_webview_widget_test.dart | 16 +-- .../src/create_all_plugins_app_command.dart | 2 +- script/tool/lib/src/custom_test_command.dart | 2 +- script/tool/lib/src/format_command.dart | 5 +- .../tool/lib/src/license_check_command.dart | 3 +- .../lib/src/make_deps_path_based_command.dart | 4 +- .../tool/lib/src/pubspec_check_command.dart | 8 +- .../tool/lib/src/version_check_command.dart | 6 +- script/tool/test/analyze_command_test.dart | 2 +- .../tool/test/common/plugin_command_test.dart | 2 +- script/tool/test/format_command_test.dart | 5 +- .../test/publish_plugin_command_test.dart | 8 +- .../tool/test/pubspec_check_command_test.dart | 4 +- script/tool/test/util.dart | 2 +- .../tool/test/version_check_command_test.dart | 6 +- 135 files changed, 412 insertions(+), 292 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index ba5e0a9c4ced..87f7d6f9840b 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -9,23 +9,21 @@ # Specify analysis options. # -# Until there are meta linter rules, each desired lint must be explicitly enabled. -# See: https://github.com/dart-lang/linter/issues/288 -# # For a list of lints, see: http://dart-lang.github.io/linter/lints/ # See the configuration guide for more -# https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer +# https://github.com/dart-lang/sdk/tree/main/pkg/analyzer#configuring-the-analyzer # # There are other similar analysis options files in the flutter repos, # which should be kept in sync with this file: # # - analysis_options.yaml (this file) # - packages/flutter/lib/analysis_options_user.yaml -# - https://github.com/flutter/plugins/blob/master/analysis_options.yaml -# - https://github.com/flutter/engine/blob/master/analysis_options.yaml +# - https://github.com/flutter/flutter/blob/master/analysis_options.yaml +# - https://github.com/flutter/engine/blob/main/analysis_options.yaml +# - https://github.com/flutter/packages/blob/main/analysis_options.yaml # -# This file contains the analysis options used by Flutter tools, such as IntelliJ, -# Android Studio, and the `flutter analyze` command. +# This file contains the analysis options used for code in the flutter/plugins +# repository. analyzer: strong-mode: @@ -36,7 +34,7 @@ analyzer: missing_required_param: warning # treat missing returns as a warning (not a hint) missing_return: warning - # allow having TODOs in the code + # allow having TODO comments in the code todo: ignore # allow self-reference to deprecated members (we do this because otherwise we have # to annotate every member in every test, assert, etc, when we deprecate something) @@ -45,9 +43,10 @@ analyzer: # Stream and not importing dart:async # Please see https://github.com/flutter/flutter/pull/24528 for details. sdk_version_async_exported_from_core: ignore + # Turned off until null-safe rollout is complete. + unnecessary_null_comparison: ignore ### Local flutter/plugins changes ### # Allow null checks for as long as mixed mode is officially supported. - unnecessary_null_comparison: false always_require_non_null_named_parameters: false # not needed with nnbd exclude: # Ignore generated files @@ -58,8 +57,7 @@ analyzer: linter: rules: - # these rules are documented on and in the same order as - # the Dart Lint rules page to make maintenance easier + # This list is derived from the list of all available lints located at # https://github.com/dart-lang/linter/blob/master/example/all.yaml - always_declare_return_types - always_put_control_body_on_new_line @@ -69,62 +67,68 @@ linter: # - always_use_package_imports # we do this commonly - annotate_overrides # - avoid_annotating_with_dynamic # conflicts with always_specify_types - # - avoid_as # required for implicit-casts: true - avoid_bool_literals_in_conditional_expressions - # - avoid_catches_without_on_clauses # we do this commonly - # - avoid_catching_errors # we do this commonly + # - avoid_catches_without_on_clauses # blocked on https://github.com/dart-lang/linter/issues/3023 + # - avoid_catching_errors # blocked on https://github.com/dart-lang/linter/issues/3023 - avoid_classes_with_only_static_members - # - avoid_double_and_int_checks # only useful when targeting JS runtime + - avoid_double_and_int_checks + # - avoid_dynamic_calls # LOCAL CHANGE - Needs to be enabled and violations fixed. - avoid_empty_else - avoid_equals_and_hash_code_on_mutable_classes - # - avoid_escaping_inner_quotes # not yet tested + - avoid_escaping_inner_quotes - avoid_field_initializers_in_const_classes + # - avoid_final_parameters # incompatible with prefer_final_parameters - avoid_function_literals_in_foreach_calls - # - avoid_implementing_value_types # not yet tested + # - avoid_implementing_value_types # LOCAL CHANGE - Needs to be enabled and violations fixed. - avoid_init_to_null - # - avoid_js_rounded_ints # only useful when targeting JS runtime + - avoid_js_rounded_ints + # - avoid_multiple_declarations_per_line # seems to be a stylistic choice we don't subscribe to - avoid_null_checks_in_equality_operators - # - avoid_positional_boolean_parameters # not yet tested - # - avoid_print # not yet tested + # - avoid_positional_boolean_parameters # would have been nice to enable this but by now there's too many places that break it + # - avoid_print # LOCAL CHANGE - Needs to be enabled and violations fixed. # - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356) - # - avoid_redundant_argument_values # not yet tested + # - avoid_redundant_argument_values # LOCAL CHANGE - Needs to be enabled and violations fixed. - avoid_relative_lib_imports - avoid_renaming_method_parameters - avoid_return_types_on_setters - # - avoid_returning_null # there are plenty of valid reasons to return null - # - avoid_returning_null_for_future # not yet tested + # - avoid_returning_null # still violated by some pre-nnbd code that we haven't yet migrated + - avoid_returning_null_for_future - avoid_returning_null_for_void - # - avoid_returning_this # there are plenty of valid reasons to return this - # - avoid_setters_without_getters # not yet tested + # - avoid_returning_this # there are enough valid reasons to return `this` that this lint ends up with too many false positives + - avoid_setters_without_getters - avoid_shadowing_type_parameters - avoid_single_cascade_in_expression_statements - avoid_slow_async_io - # - avoid_type_to_string # we do this commonly + - avoid_type_to_string - avoid_types_as_parameter_names # - avoid_types_on_closure_parameters # conflicts with always_specify_types - # - avoid_unnecessary_containers # not yet tested + - avoid_unnecessary_containers - avoid_unused_constructor_parameters - avoid_void_async - # - avoid_web_libraries_in_flutter # not yet tested + # - avoid_web_libraries_in_flutter # we use web libraries in web-specific code, and our tests prevent us from using them elsewhere - await_only_futures - camel_case_extensions - camel_case_types - cancel_subscriptions - # - cascade_invocations # not yet tested + # - cascade_invocations # doesn't match the typical style of this repo - cast_nullable_to_non_nullable # - close_sinks # not reliable enough - # - comment_references # blocked on https://github.com/flutter/flutter/issues/20765 + # - comment_references # blocked on https://github.com/dart-lang/linter/issues/1142 + # - conditional_uri_does_not_exist # not yet tested # - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204 - control_flow_in_finally # - curly_braces_in_flow_control_structures # not required by flutter style - # - diagnostic_describe_all_properties # not yet tested + # - depend_on_referenced_packages # LOCAL CHANGE - Needs to be enabled and violations fixed. + - deprecated_consistency + # - diagnostic_describe_all_properties # enabled only at the framework level (packages/flutter/lib) - directives_ordering - # - do_not_use_environment # we do this commonly + # - do_not_use_environment # there are appropriate times to use the environment, especially in our tests and build logic - empty_catches - empty_constructor_bodies - empty_statements + - eol_at_end_of_file - exhaustive_cases - # - file_names # not yet tested + - file_names - flutter_style_todos - hash_and_equals - implementation_imports @@ -137,22 +141,25 @@ linter: - library_private_types_in_public_api # - lines_longer_than_80_chars # not required by flutter style - list_remove_unrelated_type - # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181 - # - missing_whitespace_between_adjacent_strings # not yet tested + # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/linter/issues/453 + - missing_whitespace_between_adjacent_strings - no_adjacent_strings_in_list - # - no_default_cases # too many false positives + # - no_default_cases # LOCAL CHANGE - Needs to be enabled and violations fixed. - no_duplicate_case_values + - no_leading_underscores_for_library_prefixes + # - no_leading_underscores_for_local_identifiers # LOCAL CHANGE - Needs to be enabled and violations fixed. - no_logic_in_create_state # - no_runtimeType_toString # ok in tests; we enable this only in packages/ - non_constant_identifier_names + - noop_primitive_operations - null_check_on_nullable_type_parameter - # - null_closures # not required by flutter style + - null_closures # - omit_local_variable_types # opposite of always_specify_types # - one_member_abstracts # too many false positives - # - only_throw_errors # https://github.com/flutter/flutter/issues/5792 + # - only_throw_errors # this does get disabled in a few places where we have legacy code that uses strings et al # LOCAL CHANGE - Needs to be enabled and violations fixed. - overridden_fields - package_api_docs - # - package_names # non conforming packages in sdk + - package_names - package_prefixed_library_names # - parameter_assignments # we do this commonly - prefer_adjacent_string_concatenation @@ -172,74 +179,90 @@ linter: - prefer_final_fields - prefer_final_in_for_each - prefer_final_locals + # - prefer_final_parameters # we should enable this one day when it can be auto-fixed (https://github.com/dart-lang/linter/issues/3104), see also parameter_assignments - prefer_for_elements_to_map_fromIterable - prefer_foreach - # - prefer_function_declarations_over_variables # not yet tested + - prefer_function_declarations_over_variables - prefer_generic_function_type_aliases - prefer_if_elements_to_conditional_expressions - prefer_if_null_operators - prefer_initializing_formals - prefer_inlined_adds - # - prefer_int_literals # not yet tested - # - prefer_interpolation_to_compose_strings # not yet tested + # - prefer_int_literals # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#use-double-literals-for-double-constants + - prefer_interpolation_to_compose_strings - prefer_is_empty - prefer_is_not_empty - prefer_is_not_operator - prefer_iterable_whereType - # - prefer_mixin # https://github.com/dart-lang/language/issues/32 - # - prefer_null_aware_operators # disable until NNBD, see https://github.com/flutter/flutter/pull/32711#issuecomment-492930932 - # - prefer_relative_imports # not yet tested + # - prefer_mixin # Has false positives, see https://github.com/dart-lang/linter/issues/3018 + # - prefer_null_aware_method_calls # "call()" is confusing to people new to the language since it's not documented anywhere + - prefer_null_aware_operators + # - prefer_relative_imports # LOCAL CHANGE - Needs to be enabled and violations fixed. - prefer_single_quotes - prefer_spread_collections - prefer_typing_uninitialized_variables - prefer_void_to_null - # - provide_deprecation_message # not yet tested + - provide_deprecation_message # - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml - recursive_getters - # - sized_box_for_whitespace # not yet tested + # - require_trailing_commas # blocked on https://github.com/dart-lang/sdk/issues/47441 + - secure_pubspec_urls + # - sized_box_for_whitespace # LOCAL CHANGE - Needs to be enabled and violations fixed. + # - sized_box_shrink_expand # not yet tested - slash_for_doc_comments - sort_child_properties_last - sort_constructors_first + # - sort_pub_dependencies # prevents separating pinned transitive dependencies - sort_unnamed_constructors_first - test_types_in_equals - throw_in_finally - tighten_type_of_initializing_formals # - type_annotate_public_apis # subset of always_specify_types - type_init_formals - # - unawaited_futures # too many false positives - # - unnecessary_await_in_return # not yet tested + # - unawaited_futures # too many false positives, especially with the way AnimationController works + # - unnecessary_await_in_return # LOCAL CHANGE - Needs to be enabled and violations fixed. - unnecessary_brace_in_string_interps - unnecessary_const + - unnecessary_constructor_name # - unnecessary_final # conflicts with prefer_final_locals - unnecessary_getters_setters # - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498 + - unnecessary_late - unnecessary_new - unnecessary_null_aware_assignments - # - unnecessary_null_checks # not yet tested + - unnecessary_null_checks - unnecessary_null_in_if_null_operators - unnecessary_nullable_for_final_variable_declarations - unnecessary_overrides - unnecessary_parenthesis - # - unnecessary_raw_strings # not yet tested + # - unnecessary_raw_strings # what's "necessary" is a matter of opinion; consistency across strings can help readability more than this lint - unnecessary_statements - unnecessary_string_escapes - unnecessary_string_interpolations - unnecessary_this - unrelated_type_equality_checks - # - unsafe_html # not yet tested + - unsafe_html + # - use_build_context_synchronously # LOCAL CHANGE - Needs to be enabled and violations fixed. + # - use_colored_box # not yet tested + # - use_decorated_box # not yet tested + # - use_enums # not yet tested - use_full_hex_values_for_flutter_colors - # - use_function_type_syntax_for_parameters # not yet tested + - use_function_type_syntax_for_parameters + - use_if_null_to_convert_nulls_to_bools - use_is_even_rather_than_modulo - use_key_in_widget_constructors - use_late_for_private_fields_and_variables + # - use_named_constants # LOCAL CHANGE - Needs to be enabled and violations fixed. - use_raw_strings - use_rethrow_when_possible - # - use_setters_to_change_properties # not yet tested + - use_setters_to_change_properties # - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182 + - use_super_parameters + - use_test_throws_matchers # - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review - valid_regexps - void_checks - ### Local flutter/plugins changes ### + ### Local flutter/plugins additions ### # These are from flutter/flutter/packages, so will need to be preserved # separately when moving to a shared file. - no_runtimeType_toString # use objectRuntimeType from package:foundation diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index bf0ccf86a82e..4715644a0c13 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+23 + +* Minor fixes for new analysis options. + ## 0.9.4+22 * Removes unnecessary imports. diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index a645326f2803..10a8a6f75e16 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -575,14 +575,13 @@ class _CameraExampleHomeState extends State Widget _cameraTogglesRowWidget() { final List toggles = []; - final Null Function(CameraDescription? description) onChanged = - (CameraDescription? description) { + void onChanged(CameraDescription? description) { if (description == null) { return; } onNewCameraSelected(description); - }; + } if (_cameras.isEmpty) { _ambiguate(SchedulerBinding.instance)?.addPostFrameCallback((_) async { diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index fde6663844c2..9d56f1b6e4da 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+22 +version: 0.9.4+23 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 7a24e12e5029..5a9fb6608323 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.1+6 + +* Minor fixes for new analysis options. + ## 0.2.1+5 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/camera/camera_web/example/integration_test/camera_test.dart b/packages/camera/camera_web/example/integration_test/camera_test.dart index 20d1cbaf8e36..50451b9778af 100644 --- a/packages/camera/camera_web/example/integration_test/camera_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_test.dart @@ -1477,7 +1477,7 @@ void main() { }); group('dispose', () { - testWidgets('resets the video element\'s source', + testWidgets("resets the video element's source", (WidgetTester tester) async { final Camera camera = Camera( textureId: textureId, 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 5a564dd66021..143783f5225b 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 @@ -498,7 +498,7 @@ void main() { isA().having( (CameraException e) => e.code, 'code', - exception.code.toString(), + exception.code, ), ), ); @@ -759,7 +759,7 @@ void main() { isA().having( (PlatformException e) => e.code, 'code', - exception.name.toString(), + exception.name, ), ), ); @@ -2495,7 +2495,7 @@ void main() { equals( CameraErrorEvent( cameraId, - 'Error code: ${CameraErrorCode.abort}, error message: The video element\'s source has not fully loaded.', + "Error code: ${CameraErrorCode.abort}, error message: The video element's source has not fully loaded.", ), ), ); diff --git a/packages/camera/camera_web/example/integration_test/helpers/mocks.dart b/packages/camera/camera_web/example/integration_test/helpers/mocks.dart index 3d9550fb7ab8..521c4bf5a18d 100644 --- a/packages/camera/camera_web/example/integration_test/helpers/mocks.dart +++ b/packages/camera/camera_web/example/integration_test/helpers/mocks.dart @@ -113,8 +113,8 @@ class FakeElementStream extends Fake final Stream _stream; @override - StreamSubscription listen(void onData(T event)?, - {Function? onError, void onDone()?, bool? cancelOnError}) { + StreamSubscription listen(void Function(T event)? onData, + {Function? onError, void Function()? onDone, bool? cancelOnError}) { return _stream.listen( onData, onError: onError, diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index 71368c65e99d..210a0df59eec 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -138,6 +138,9 @@ class Camera { /// A builder to merge a list of blobs into a single blob. @visibleForTesting + // TODO(stuartmorgan): Remove this 'ignore' once we don't analyze using 2.10 + // any more. It's a false positive that is fixed in later versions. + // ignore: prefer_function_declarations_over_variables html.Blob Function(List blobs, String type) blobBuilder = (List blobs, String type) => html.Blob(blobs, type); diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index f15845cf823b..b118169f0618 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -82,7 +82,7 @@ class CameraService { throw CameraWebException( cameraId, CameraErrorCode.type, - 'The camera options are incorrect or attempted' + 'The camera options are incorrect or attempted ' 'to access the media input from an insecure context.', ); case 'AbortError': diff --git a/packages/camera/camera_web/lib/src/camera_web.dart b/packages/camera/camera_web/lib/src/camera_web.dart index 6f9f10d68f84..26f965d49e16 100644 --- a/packages/camera/camera_web/lib/src/camera_web.dart +++ b/packages/camera/camera_web/lib/src/camera_web.dart @@ -290,7 +290,7 @@ class CameraPlugin extends CameraPlatform { cameraEventStreamController.add( CameraErrorEvent( cameraId, - 'Error code: ${CameraErrorCode.abort}, error message: The video element\'s source has not fully loaded.', + "Error code: ${CameraErrorCode.abort}, error message: The video element's source has not fully loaded.", ), ); }); @@ -400,7 +400,7 @@ class CameraPlugin extends CameraPlatform { // This wrapper allows use of both the old and new APIs. dynamic fullScreen() => documentElement.requestFullscreen(); await fullScreen(); - await screenOrientation.lock(orientationType.toString()); + await screenOrientation.lock(orientationType); } else { throw PlatformException( code: CameraErrorCode.orientationNotSupported.toString(), diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 8bef974190b2..90d119549e86 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.2.1+5 +version: 0.2.1+6 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 17baf9f12469..639783361852 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Minor fixes for new analysis options. + ## 0.8.4+2 * Removes unnecessary imports. diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index fc3e668f9d9e..887ab64c3c0c 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -284,10 +284,12 @@ class FakeFileSelector extends Fake this.confirmButtonText = confirmButtonText; } + // ignore: use_setters_to_change_properties void setFileResponse(List files) { this.files = files; } + // ignore: use_setters_to_change_properties void setPathResponse(String path) { this.path = path; } diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md index ce9d5590f9a9..3963601e2ac5 100644 --- a/packages/file_selector/file_selector_web/CHANGELOG.md +++ b/packages/file_selector/file_selector_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.1+5 + +* Minor fixes for new analysis options. + ## 0.8.1+4 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors 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 fe57d1d1e15d..43c88a2a4241 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 @@ -19,10 +19,10 @@ void main() { testWidgets('works', (WidgetTester _) async { final XFile mockFile = createXFile('1001', 'identity.png'); - final MockDomHelper mockDomHelper = MockDomHelper() - ..setFiles([mockFile]) - ..expectAccept('.jpg,.jpeg,image/png,image/*') - ..expectMultiple(false); + final MockDomHelper mockDomHelper = MockDomHelper( + files: [mockFile], + expectAccept: '.jpg,.jpeg,image/png,image/*', + expectMultiple: false); final FileSelectorWeb plugin = FileSelectorWeb(domHelper: mockDomHelper); @@ -49,10 +49,10 @@ void main() { final XFile mockFile1 = createXFile('123456', 'file1.txt'); final XFile mockFile2 = createXFile('', 'file2.txt'); - final MockDomHelper mockDomHelper = MockDomHelper() - ..setFiles([mockFile1, mockFile2]) - ..expectAccept('.txt') - ..expectMultiple(true); + final MockDomHelper mockDomHelper = MockDomHelper( + files: [mockFile1, mockFile2], + expectAccept: '.txt', + expectMultiple: true); final FileSelectorWeb plugin = FileSelectorWeb(domHelper: mockDomHelper); @@ -90,9 +90,17 @@ void main() { } class MockDomHelper implements DomHelper { - List _files = []; - String _expectedAccept = ''; - bool _expectedMultiple = false; + MockDomHelper({ + List files = const [], + String expectAccept = '', + bool expectMultiple = false, + }) : _files = files, + _expectedAccept = expectAccept, + _expectedMultiple = expectMultiple; + + final List _files; + final String _expectedAccept; + final bool _expectedMultiple; @override Future> getFiles({ @@ -106,18 +114,6 @@ class MockDomHelper implements DomHelper { reason: 'Expected "multiple" value does not match.'); return Future>.value(_files); } - - void setFiles(List files) { - _files = files; - } - - void expectAccept(String accept) { - _expectedAccept = accept; - } - - void expectMultiple(bool multiple) { - _expectedMultiple = multiple; - } } XFile createXFile(String content, String name) { diff --git a/packages/file_selector/file_selector_web/lib/src/utils.dart b/packages/file_selector/file_selector_web/lib/src/utils.dart index 6a534645fda6..fe8d1f433647 100644 --- a/packages/file_selector/file_selector_web/lib/src/utils.dart +++ b/packages/file_selector/file_selector_web/lib/src/utils.dart @@ -36,5 +36,5 @@ void _assertTypeGroupIsValid(XTypeGroup group) { /// Append a dot at the beggining if it is not there png -> .png String _normalizeExtension(String ext) { - return ext.isNotEmpty && ext[0] != '.' ? '.' + ext : ext; + return ext.isNotEmpty && ext[0] != '.' ? '.$ext' : ext; } diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index 2e12b6d175a3..488031995e55 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/plugins/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.8.1+4 +version: 0.8.1+5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index d1f2f926e2ab..07ffbfdb4e5d 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Minor fixes for new analysis options. + ## 2.1.5 * Removes unnecessary imports. diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart index 09df2b98b146..3975d64449b8 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/animate_camera.dart @@ -28,6 +28,7 @@ class AnimateCamera extends StatefulWidget { class AnimateCameraState extends State { GoogleMapController? mapController; + // ignore: use_setters_to_change_properties void _onMapCreated(GoogleMapController controller) { mapController = controller; } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart index a6bae3009f0b..7fa8a0354eb2 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/move_camera.dart @@ -28,6 +28,7 @@ class MoveCamera extends StatefulWidget { class MoveCameraState extends State { GoogleMapController? mapController; + // ignore: use_setters_to_change_properties void _onMapCreated(GoogleMapController controller) { mapController = controller; } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart index ef5033cfa1ee..7cbb63ac4e99 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_circle.dart @@ -48,6 +48,7 @@ class PlaceCircleBodyState extends State { int widthsIndex = 0; List widths = [10, 20, 5]; + // ignore: use_setters_to_change_properties void _onMapCreated(GoogleMapController controller) { this.controller = controller; } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart index 1238d61547b8..b8efc4e52562 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart @@ -43,6 +43,7 @@ class PlaceMarkerBodyState extends State { int _markerIdCounter = 1; LatLng? markerPosition; + // ignore: use_setters_to_change_properties void _onMapCreated(GoogleMapController controller) { this.controller = controller; } @@ -207,7 +208,7 @@ class PlaceMarkerBodyState extends State { Future _changeInfo(MarkerId markerId) async { final Marker marker = markers[markerId]!; - final String newSnippet = marker.infoWindow.snippet! + '*'; + final String newSnippet = '${marker.infoWindow.snippet!}*'; setState(() { markers[markerId] = marker.copyWith( infoWindowParam: marker.infoWindow.copyWith( diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart index f1932141b8ab..cb0cc56d4754 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polygon.dart @@ -49,6 +49,7 @@ class PlacePolygonBodyState extends State { int widthsIndex = 0; List widths = [10, 20, 5]; + // ignore: use_setters_to_change_properties void _onMapCreated(GoogleMapController controller) { this.controller = controller; } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart index b3a637ce7d15..7a7c5d2f4a16 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_polyline.dart @@ -77,6 +77,7 @@ class PlacePolylineBodyState extends State { [PatternItem.dot, PatternItem.gap(10.0)], ]; + // ignore: use_setters_to_change_properties void _onMapCreated(GoogleMapController controller) { this.controller = controller; } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart index ca9d3962ddd7..3d676e0713fd 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/scrolling_map.dart @@ -66,7 +66,7 @@ class ScrollingMapBody extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 30.0), child: Column( children: [ - const Text('This map doesn\'t consume the vertical drags.'), + const Text("This map doesn't consume the vertical drags."), const Padding( padding: EdgeInsets.only(bottom: 12.0), child: diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart index 849a9f469938..fbc7ae2a3e24 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/snapshot.dart @@ -68,6 +68,7 @@ class _SnapshotBodyState extends State<_SnapshotBody> { ); } + // ignore: use_setters_to_change_properties void onMapCreated(GoogleMapController controller) { _mapController = controller; } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart index 81dfc2815866..d88e09988dc7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart @@ -35,6 +35,7 @@ class TileOverlayBodyState extends State { GoogleMapController? controller; TileOverlay? _tileOverlay; + // ignore: use_setters_to_change_properties void _onMapCreated(GoogleMapController controller) { this.controller = controller; } 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 6e87bebf5d02..9a6b07650c64 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.10.1+2 + +* Minor fixes for new analysis options. + ## 0.10.1+1 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart index 9db024361580..d8c7655a11c4 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart @@ -117,7 +117,7 @@ void main() { expect(plugin.init(hostedDomain: ''), throwsAssertionError); }); - testWidgets('Init doesn\'t accept spaces in scopes', + testWidgets("Init doesn't accept spaces in scopes", (WidgetTester tester) async { expect( plugin.init( diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_utils_test.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_utils_test.dart index 1447093d4115..b341d1d6b96d 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_utils_test.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_utils_test.dart @@ -51,10 +51,12 @@ class FakeGoogleUser extends Fake implements gapi.GoogleUser { @override gapi.BasicProfile? getBasicProfile() => _basicProfile; + // ignore: use_setters_to_change_properties void setIsSignedIn(bool isSignedIn) { _isSignedIn = isSignedIn; } + // ignore: use_setters_to_change_properties void setBasicProfile(gapi.BasicProfile basicProfile) { _basicProfile = basicProfile; } diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/src/test_utils.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/src/test_utils.dart index 89f9b55f3ddf..56aa61df136e 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/src/test_utils.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/src/test_utils.dart @@ -6,5 +6,5 @@ import 'dart:convert'; String toBase64Url(String contents) { // Open the file - return 'data:text/javascript;base64,' + base64.encode(utf8.encode(contents)); + return 'data:text/javascript;base64,${base64.encode(utf8.encode(contents))}'; } diff --git a/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart b/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart index 533c353df310..ae6180d34acb 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart @@ -81,7 +81,7 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { assert( !scopes.any((String scope) => scope.contains(' ')), - 'OAuth 2.0 Scopes for Google APIs can\'t contain spaces.' + "OAuth 2.0 Scopes for Google APIs can't contain spaces. " 'Check https://developers.google.com/identity/protocols/googlescopes ' 'for a list of valid OAuth 2.0 scopes.'); diff --git a/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart b/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart index 8e23713c90e9..e1721668f41f 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart @@ -57,8 +57,8 @@ class GoogleAuth { /// Calls the onInit function when the GoogleAuth object is fully initialized, or calls the onFailure function if /// initialization fails. - external dynamic then(dynamic onInit(GoogleAuth googleAuth), - [dynamic onFailure(GoogleAuthInitFailureError reason)]); + external dynamic then(dynamic Function(GoogleAuth googleAuth) onInit, + [dynamic Function(GoogleAuthInitFailureError reason) onFailure]); /// Signs out all accounts from the application. external dynamic signOut(); @@ -70,8 +70,8 @@ class GoogleAuth { external dynamic attachClickHandler( dynamic container, SigninOptions options, - dynamic onsuccess(GoogleUser googleUser), - dynamic onfailure(String reason)); + dynamic Function(GoogleUser googleUser) onsuccess, + dynamic Function(String reason) onfailure); } @anonymous @@ -104,7 +104,7 @@ abstract class IsSignedIn { external bool get(); /// Listen for changes in the current user's sign-in state. - external void listen(dynamic listener(bool signedIn)); + external void listen(dynamic Function(bool signedIn) listener); } @anonymous @@ -116,7 +116,7 @@ abstract class CurrentUser { external GoogleUser get(); /// Listen for changes in currentUser. - external void listen(dynamic listener(GoogleUser user)); + external void listen(dynamic Function(GoogleUser user) listener); } @anonymous @@ -440,7 +440,7 @@ external GoogleAuth? getAuthInstance(); /// Reference: https://developers.google.com/api-client-library/javascript/reference/referencedocs#gapiauth2authorizeparams-callback @JS('gapi.auth2.authorize') external void authorize( - AuthorizeConfig params, void callback(AuthorizeResponse response)); + AuthorizeConfig params, void Function(AuthorizeResponse response) callback); // End module gapi.auth2 // Module gapi.signin2 @@ -497,6 +497,7 @@ external void render( @JS() abstract class Promise { external factory Promise( - void executor(void resolve(T result), Function reject)); - external Promise then(void onFulfilled(T result), [Function onRejected]); + void Function(void Function(T result) resolve, Function reject) executor); + external Promise then(void Function(T result) onFulfilled, + [Function onRejected]); } diff --git a/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart b/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart index cae20d28db44..72424d8ea15b 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/src/utils.dart @@ -29,6 +29,7 @@ Future injectJSLibraries( final html.ScriptElement script = html.ScriptElement() ..async = true ..defer = true + // ignore: unsafe_html ..src = library; // TODO(ditman): add a timeout race to fail this future loading.add(script.onLoad.first); 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 3dcd0e8eef29..a270af985ea7 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/plugins/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.10.1+1 +version: 0.10.1+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index a384a5272e05..156b9a3a6d23 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.5+2 + +* Minor fixes for new analysis options. + ## 0.8.5+1 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/image_picker/image_picker/example/lib/main.dart b/packages/image_picker/image_picker/example/lib/main.dart index a6f0e83c3abb..4eecc5fa2a1a 100755 --- a/packages/image_picker/image_picker/example/lib/main.dart +++ b/packages/image_picker/image_picker/example/lib/main.dart @@ -40,7 +40,7 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { List? _imageFileList; - set _imageFile(XFile? value) { + void _setImageFileListFromFile(XFile? value) { _imageFileList = value == null ? null : [value]; } @@ -118,7 +118,7 @@ class _MyHomePageState extends State { imageQuality: quality, ); setState(() { - _imageFile = pickedFile; + _setImageFileListFromFile(pickedFile); }); } catch (e) { setState(() { @@ -228,8 +228,11 @@ class _MyHomePageState extends State { } else { isVideo = false; setState(() { - _imageFile = response.file; - _imageFileList = response.files; + if (response.files == null) { + _setImageFileListFromFile(response.file); + } else { + _imageFileList = response.files; + } }); } } else { diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 818486d8e145..cc34d8ab33f5 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5+1 +version: 0.8.5+2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index 0514fc33d420..eede63435026 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.4+13 + +* Minor fixes for new analysis options. + ## 0.8.4+12 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/image_picker/image_picker_android/example/lib/main.dart b/packages/image_picker/image_picker_android/example/lib/main.dart index d56aeb866195..212e064cc6e5 100755 --- a/packages/image_picker/image_picker_android/example/lib/main.dart +++ b/packages/image_picker/image_picker_android/example/lib/main.dart @@ -40,7 +40,7 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { List? _imageFileList; - set _imageFile(XFile? value) { + void _setImageFileListFromFile(XFile? value) { _imageFileList = value == null ? null : [value]; } @@ -118,7 +118,7 @@ class _MyHomePageState extends State { imageQuality: quality, ); setState(() { - _imageFile = pickedFile; + _setImageFileListFromFile(pickedFile); }); } catch (e) { setState(() { @@ -228,8 +228,11 @@ class _MyHomePageState extends State { } else { isVideo = false; setState(() { - _imageFile = response.file; - _imageFileList = response.files; + if (response.files == null) { + _setImageFileListFromFile(response.file); + } else { + _imageFileList = response.files; + } }); } } else { diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index 90d136c2c89b..095534654ac5 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/plugins/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.4+12 +version: 0.8.4+13 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index c33b3b9981de..b69ba597aca3 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.8 + +* Minor fixes for new analysis options. + ## 2.1.7 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors 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 91794a7d5e78..1efd7b29a810 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 @@ -33,8 +33,8 @@ void main() { testWidgets('image is loaded correctly ', (WidgetTester tester) async { final html.ImageElement imageElement = await imageResizer.loadImage(pngFile.path); - expect(imageElement.width!, 10); - expect(imageElement.height!, 10); + expect(imageElement.width, 10); + expect(imageElement.height, 10); }); testWidgets( 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 e063099e3319..ba794acae3be 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 @@ -39,6 +39,7 @@ class ImageResizer { final Completer imageLoadCompleter = Completer(); final html.ImageElement imageElement = html.ImageElement(); + // ignore: unsafe_html imageElement.src = blobUrl; imageElement.onLoad.listen((html.Event event) { @@ -81,7 +82,7 @@ class ImageResizer { await canvas.toBlob(originalFile.mimeType, calculatedImageQuality); return XFile(html.Url.createObjectUrlFromBlob(blob), mimeType: originalFile.mimeType, - name: 'scaled_' + originalFile.name, + name: 'scaled_${originalFile.name}', lastModified: DateTime.now(), length: blob.size); } diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index b0c5deb0da7a..0b2c6d2fc0ff 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_for_web description: Web platform implementation of image_picker repository: https://github.com/flutter/plugins/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: 2.1.7 +version: 2.1.8 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index af391db02689..96b1c7f0d0a4 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.5+2 + +* Minor fixes for new analysis options. + ## 0.8.5+1 * Removes unnecessary imports. diff --git a/packages/image_picker/image_picker_ios/example/lib/main.dart b/packages/image_picker/image_picker_ios/example/lib/main.dart index d56aeb866195..c5372b8e7ad8 100755 --- a/packages/image_picker/image_picker_ios/example/lib/main.dart +++ b/packages/image_picker/image_picker_ios/example/lib/main.dart @@ -40,7 +40,7 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { List? _imageFileList; - set _imageFile(XFile? value) { + void _setImageFileListFromFile(XFile? value) { _imageFileList = value == null ? null : [value]; } @@ -118,7 +118,7 @@ class _MyHomePageState extends State { imageQuality: quality, ); setState(() { - _imageFile = pickedFile; + _setImageFileListFromFile(pickedFile); }); } catch (e) { setState(() { @@ -216,27 +216,6 @@ class _MyHomePageState extends State { } } - Future retrieveLostData() async { - final LostDataResponse response = await _picker.getLostData(); - if (response.isEmpty) { - return; - } - if (response.file != null) { - if (response.type == RetrieveType.video) { - isVideo = true; - await _playVideo(response.file); - } else { - isVideo = false; - setState(() { - _imageFile = response.file; - _imageFileList = response.files; - }); - } - } else { - _retrieveDataError = response.exception!.code; - } - } - @override Widget build(BuildContext context) { return Scaffold( @@ -244,35 +223,7 @@ class _MyHomePageState extends State { title: Text(widget.title!), ), body: Center( - child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android - ? FutureBuilder( - future: retrieveLostData(), - builder: (BuildContext context, AsyncSnapshot snapshot) { - switch (snapshot.connectionState) { - case ConnectionState.none: - case ConnectionState.waiting: - return const Text( - 'You have not yet picked an image.', - textAlign: TextAlign.center, - ); - case ConnectionState.done: - return _handlePreview(); - default: - if (snapshot.hasError) { - return Text( - 'Pick image/video error: ${snapshot.error}}', - textAlign: TextAlign.center, - ); - } else { - return const Text( - 'You have not yet picked an image.', - textAlign: TextAlign.center, - ); - } - } - }, - ) - : _handlePreview(), + child: _handlePreview(), ), floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index 76ca20614f18..d1de0a14ea69 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 video_picker plugin. repository: https://github.com/flutter/plugins/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.5+1 +version: 0.8.5+2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md index 9f6d1749c671..2defdd2d84cc 100644 --- a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md +++ b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Minor fixes for new analysis options. + ## 2.4.4 * Internal code cleanup for stricter analysis options. diff --git a/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart b/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart index 3201d3adea41..3e6cd0e01ca6 100644 --- a/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/picked_file_io_test.dart @@ -13,7 +13,7 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. final String pathPrefix = Directory.current.path.endsWith('test') ? './assets/' : './test/assets/'; -final String path = pathPrefix + 'hello.txt'; +final String path = '${pathPrefix}hello.txt'; const String expectedStringContents = 'Hello, world!'; final Uint8List bytes = Uint8List.fromList(utf8.encode(expectedStringContents)); final File textFile = File(path); diff --git a/packages/image_picker/image_picker_windows/CHANGELOG.md b/packages/image_picker/image_picker_windows/CHANGELOG.md index e72ab244068f..b8a265568633 100644 --- a/packages/image_picker/image_picker_windows/CHANGELOG.md +++ b/packages/image_picker/image_picker_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.0+2 + +* Minor fixes for new analysis options. + ## 0.1.0+1 * Removes unnecessary imports. diff --git a/packages/image_picker/image_picker_windows/example/lib/main.dart b/packages/image_picker/image_picker_windows/example/lib/main.dart index b3ba3574522a..e340a185bf3d 100644 --- a/packages/image_picker/image_picker_windows/example/lib/main.dart +++ b/packages/image_picker/image_picker_windows/example/lib/main.dart @@ -40,7 +40,7 @@ class _MyHomePageState extends State { List? _imageFileList; // This must be called from within a setState() callback - set _imageFile(PickedFile? value) { + void _setImageFileListFromFile(PickedFile? value) { _imageFileList = value == null ? null : [value]; } @@ -102,7 +102,7 @@ class _MyHomePageState extends State { imageQuality: quality, ); setState(() { - _imageFile = pickedFile; + _setImageFileListFromFile(pickedFile); }); } catch (e) { setState(() { diff --git a/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart b/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart index 9bd26c471b4e..0c6d6fbd6d66 100644 --- a/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart +++ b/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart @@ -46,7 +46,7 @@ class ImagePickerWindows extends ImagePickerPlatform { /// The file selector used to prompt the user to select images or videos. @visibleForTesting - static late FileSelectorPlatform fileSelector = FileSelectorWindows(); + static FileSelectorPlatform fileSelector = FileSelectorWindows(); /// Registers this class as the default instance of [ImagePickerPlatform]. static void registerWith() { diff --git a/packages/image_picker/image_picker_windows/pubspec.yaml b/packages/image_picker/image_picker_windows/pubspec.yaml index afadf3e39148..af96030debdf 100644 --- a/packages/image_picker/image_picker_windows/pubspec.yaml +++ b/packages/image_picker/image_picker_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_windows description: Windows platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.1.0+1 +version: 0.1.0+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 8412c23ee8e8..6f0d4877f8a3 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.0.4 + +* Minor fixes for new analysis options. + ## 3.0.3 * Removes unnecessary imports. diff --git a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart index 34346a0bd339..5dbdd8c14b29 100644 --- a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart @@ -193,8 +193,8 @@ class _MyAppState extends State<_MyApp> { final Widget storeHeader = ListTile( leading: Icon(_isAvailable ? Icons.check : Icons.block, color: _isAvailable ? Colors.green : ThemeData.light().errorColor), - title: Text( - 'The store is ' + (_isAvailable ? 'available' : 'unavailable') + '.'), + title: + Text('The store is ${_isAvailable ? 'available' : 'unavailable'}.'), ); final List children = [storeHeader]; diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index d2f875293876..4b9b9b7d64ff 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 3.0.3 +version: 3.0.4 environment: sdk: ">=2.12.0 <3.0.0" 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 4b9e58c08d06..18284f29a2d9 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.2+5 + +* Minor fixes for new analysis options. + ## 0.2.2+4 * Removes unnecessary imports. 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 1da943535f70..b1d90d40b97c 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 @@ -187,8 +187,8 @@ class _MyAppState extends State<_MyApp> { final Widget storeHeader = ListTile( leading: Icon(_isAvailable ? Icons.check : Icons.block, color: _isAvailable ? Colors.green : ThemeData.light().errorColor), - title: Text( - 'The store is ' + (_isAvailable ? 'available' : 'unavailable') + '.'), + title: + Text('The store is ${_isAvailable ? 'available' : 'unavailable'}.'), ); final List children = [storeHeader]; 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 7378aeb84cfc..70343fcfff0b 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 @@ -84,7 +84,9 @@ class BillingClient { /// **Deprecation warning:** it is no longer required to call /// [enablePendingPurchases] when initializing your application. @Deprecated( - 'The requirement to call `enablePendingPurchases()` has become obsolete since Google Play no longer accepts app submissions that don\'t support pending purchases.') + 'The requirement to call `enablePendingPurchases()` has become obsolete ' + "since Google Play no longer accepts app submissions that don't support " + 'pending purchases.') void enablePendingPurchases() { // No-op, until it is time to completely remove this method from the API. } 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 dd629164866f..db53ff4077d2 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 @@ -25,7 +25,9 @@ class InAppPurchaseAndroidPlatformAddition // ignore: deprecated_member_use_from_same_package /// See also [enablePendingPurchases] for more on pending purchases. @Deprecated( - 'The requirement to call `enablePendingPurchases()` has become obsolete since Google Play no longer accepts app submissions that don\'t support pending purchases.') + 'The requirement to call `enablePendingPurchases()` has become obsolete ' + "since Google Play no longer accepts app submissions that don't support " + 'pending purchases.') static bool get enablePendingPurchase => true; /// Enable the [InAppPurchaseConnection] to handle pending purchases. @@ -33,7 +35,9 @@ class InAppPurchaseAndroidPlatformAddition /// **Deprecation warning:** it is no longer required to call /// [enablePendingPurchases] when initializing your application. @Deprecated( - 'The requirement to call `enablePendingPurchases()` has become obsolete since Google Play no longer accepts app submissions that don\'t support pending purchases.') + 'The requirement to call `enablePendingPurchases()` has become obsolete ' + "since Google Play no longer accepts app submissions that don't support " + 'pending purchases.') static void enablePendingPurchases() { // No-op, until it is time to completely remove this method from the API. } diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_product_details.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_product_details.dart index 58fd34e0ad55..15ed16c7e2ec 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_product_details.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_product_details.dart @@ -39,7 +39,7 @@ class GooglePlayProductDetails extends ProductDetails { title: skuDetails.title, description: skuDetails.description, price: skuDetails.price, - rawPrice: ((skuDetails.priceAmountMicros) / 1000000.0).toDouble(), + rawPrice: skuDetails.priceAmountMicros / 1000000.0, currencyCode: skuDetails.priceCurrencyCode, currencySymbol: skuDetails.priceCurrencySymbol, skuDetails: skuDetails, 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 7de778177c31..103251909f14 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/plugins/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.2.2+4 +version: 0.2.2+5 environment: sdk: ">=2.14.0 <3.0.0" 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 7342077ab176..aba1d6ed3555 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.0+7 + +* Minor fixes for new analysis options. + ## 0.3.0+6 * Removes unnecessary imports. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart index f45a8c7f8741..5ebf1b051942 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart @@ -192,8 +192,8 @@ class _MyAppState extends State<_MyApp> { final Widget storeHeader = ListTile( leading: Icon(_isAvailable ? Icons.check : Icons.block, color: _isAvailable ? Colors.green : ThemeData.light().errorColor), - title: Text( - 'The store is ' + (_isAvailable ? 'available' : 'unavailable') + '.'), + title: + Text('The store is ${_isAvailable ? 'available' : 'unavailable'}.'), ); final List children = [storeHeader]; 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 24b693c98c36..235d491fbff0 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 platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/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.0+6 +version: 0.3.0+7 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart index 41329335dcf4..12fb21436ace 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart @@ -16,7 +16,7 @@ void main() { () { final SKProductSubscriptionPeriodWrapper wrapper = SKProductSubscriptionPeriodWrapper.fromJson( - buildSubscriptionPeriodMap(dummySubscription)!); + buildSubscriptionPeriodMap(dummySubscription)); expect(wrapper, equals(dummySubscription)); }); @@ -95,8 +95,7 @@ void main() { expect(product.title, wrapper.localizedTitle); expect(product.description, wrapper.localizedDescription); expect(product.id, wrapper.productIdentifier); - expect(product.price, - wrapper.priceLocale.currencySymbol + wrapper.price.toString()); + expect(product.price, wrapper.priceLocale.currencySymbol + wrapper.price); expect(product.skProduct, wrapper); }); diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 9f9bd7b535a9..f18e76bf1156 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.4 + +* Minor fixes for new analysis options. + ## 1.0.3 * Removes unnecessary imports. diff --git a/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart b/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart index ad901248e63c..c82f6820055c 100644 --- a/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart +++ b/packages/local_auth/local_auth_android/lib/types/auth_messages_android.dart @@ -187,6 +187,6 @@ String get androidDeviceCredentialsSetupDescription => /// biometric on their device. String get androidGoToSettingsDescription => Intl.message( 'Biometric authentication is not set up on your device. Go to ' - '\'Settings > Security\' to add biometric authentication.', + "'Settings > Security' to add biometric authentication.", desc: 'Message advising the user to go to the settings and configure ' 'biometric on their device.'); diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index cdd4e8225504..0feea23256a2 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.3 +version: 1.0.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_platform_interface/CHANGELOG.md b/packages/path_provider/path_provider_platform_interface/CHANGELOG.md index 4ed22f09a893..4eea4b36ba8a 100644 --- a/packages/path_provider/path_provider_platform_interface/CHANGELOG.md +++ b/packages/path_provider/path_provider_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.0.4 +* Minor fixes for new analysis options. * Removes unnecessary imports. ## 2.0.3 diff --git a/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart b/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart index 73e6ab48a585..fe632743b098 100644 --- a/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart +++ b/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart @@ -22,6 +22,7 @@ class MethodChannelPathProvider extends PathProviderPlatform { /// This API is only exposed for the unit tests. It should not be used by /// any code outside of the plugin itself. @visibleForTesting + // ignore: use_setters_to_change_properties void setMockPathProviderPlatform(Platform platform) { _platform = platform; } diff --git a/packages/path_provider/path_provider_platform_interface/pubspec.yaml b/packages/path_provider/path_provider_platform_interface/pubspec.yaml index d1b0b3821e21..90b40ac7a3d4 100644 --- a/packages/path_provider/path_provider_platform_interface/pubspec.yaml +++ b/packages/path_provider/path_provider_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%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.0.3 +version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/path_provider/path_provider_windows/CHANGELOG.md b/packages/path_provider/path_provider_windows/CHANGELOG.md index 014b6b36da2b..d933b0d51da6 100644 --- a/packages/path_provider/path_provider_windows/CHANGELOG.md +++ b/packages/path_provider/path_provider_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Minor fixes for new analysis options. + ## 2.0.6 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart b/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart index e977e07d99e6..7e4118c1ccc6 100644 --- a/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart +++ b/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart @@ -78,9 +78,8 @@ void main() { if (path != null) { expect( path, - endsWith(r'AppData\Roaming\' - r'A _Bad_ Company_ Name\' - r'A__Terrible__App__Name')); + endsWith( + r'AppData\Roaming\A _Bad_ Company_ Name\A__Terrible__App__Name')); expect(Directory(path).existsSync(), isTrue); } }, skip: !Platform.isWindows); diff --git a/packages/plugin_platform_interface/CHANGELOG.md b/packages/plugin_platform_interface/CHANGELOG.md index 72229cb63410..0e9b701444fd 100644 --- a/packages/plugin_platform_interface/CHANGELOG.md +++ b/packages/plugin_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Minor fixes for new analysis options. * Adds additional tests for `PlatformInterface` and `MockPlatformInterfaceMixin`. ## 2.1.2 diff --git a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart b/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart index 9e1ddc09e92b..329cecb16091 100644 --- a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart +++ b/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart @@ -11,6 +11,7 @@ class SamplePluginPlatform extends PlatformInterface { static final Object _token = Object(); + // ignore: avoid_setters_without_getters static set instance(SamplePluginPlatform instance) { PlatformInterface.verify(instance, _token); // A real implementation would set a static instance field here. @@ -35,6 +36,7 @@ class ConstTokenPluginPlatform extends PlatformInterface { static const Object _token = Object(); // invalid + // ignore: avoid_setters_without_getters static set instance(ConstTokenPluginPlatform instance) { PlatformInterface.verify(instance, _token); } @@ -47,6 +49,7 @@ class VerifyTokenPluginPlatform extends PlatformInterface { static final Object _token = Object(); + // ignore: avoid_setters_without_getters static set instance(VerifyTokenPluginPlatform instance) { PlatformInterface.verifyToken(instance, _token); // A real implementation would set a static instance field here. @@ -68,6 +71,7 @@ class ConstVerifyTokenPluginPlatform extends PlatformInterface { static const Object _token = Object(); // invalid + // ignore: avoid_setters_without_getters static set instance(ConstVerifyTokenPluginPlatform instance) { PlatformInterface.verifyToken(instance, _token); } diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index 73540a863364..d7703a85a548 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Minor fixes for new analysis options. + ## 0.6.0+11 * Removes unnecessary imports. diff --git a/packages/quick_actions/quick_actions/test/quick_actions_test.dart b/packages/quick_actions/quick_actions/test/quick_actions_test.dart index 2747818ae302..be9fd5e7720a 100644 --- a/packages/quick_actions/quick_actions/test/quick_actions_test.dart +++ b/packages/quick_actions/quick_actions/test/quick_actions_test.dart @@ -21,7 +21,7 @@ void main() { test('initialize() PlatformInterface', () async { const QuickActions quickActions = QuickActions(); - final QuickActionHandler handler = (String type) {}; + void handler(String type) {} await quickActions.initialize(handler); verify(QuickActionsPlatform.instance.initialize(handler)).called(1); @@ -29,7 +29,7 @@ void main() { test('setShortcutItems() PlatformInterface', () { const QuickActions quickActions = QuickActions(); - final QuickActionHandler handler = (String type) {}; + void handler(String type) {} quickActions.initialize(handler); quickActions.setShortcutItems([]); @@ -40,7 +40,7 @@ void main() { test('clearShortcutItems() PlatformInterface', () { const QuickActions quickActions = QuickActions(); - final QuickActionHandler handler = (String type) {}; + void handler(String type) {} quickActions.initialize(handler); quickActions.clearShortcutItems(); diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index 22c39aad98fd..4bf0f6a6b144 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.15 + +* Minor fixes for new analysis options. + ## 2.0.14 * Adds OS version support information to README. diff --git a/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart b/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart index 5e2a65889bee..77f04800a5bb 100644 --- a/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart +++ b/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart @@ -140,7 +140,7 @@ class SharedPreferences { /// Always returns true. /// On iOS, synchronize is marked deprecated. On Android, we commit every set. - @deprecated + @Deprecated('This method is now a no-op, and should no longer be called.') Future commit() async => true; /// Completes with true once the user preferences for the app has been cleared. diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index 4218095c0efe..14b56fe69889 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.14 +version: 2.0.15 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart index 11498cfa5dcb..0a02c46404fc 100755 --- a/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart @@ -172,7 +172,7 @@ void main() { group('mocking', () { const String _key = 'dummy'; - const String _prefixedKey = 'flutter.' + _key; + const String _prefixedKey = 'flutter.$_key'; test('test 1', () async { SharedPreferences.setMockInitialValues( diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 493412c3e006..043edeb3d8e2 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.1.2 + +* Minor fixes for new analysis options. + ## 6.1.1 * Removes unnecessary imports. 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 a61b200003a0..f6faf3fa3d0e 100644 --- a/packages/url_launcher/url_launcher/lib/src/legacy_api.dart +++ b/packages/url_launcher/url_launcher/lib/src/legacy_api.dart @@ -76,10 +76,10 @@ Future launch( final bool isWebURL = url != null && (url.scheme == 'http' || url.scheme == 'https'); - if ((forceSafariVC == true || forceWebView == true) && !isWebURL) { + if ((forceSafariVC ?? false || forceWebView) && !isWebURL) { throw PlatformException( code: 'NOT_A_WEB_SCHEME', - message: 'To use webview or safariVC, you need to pass' + message: 'To use webview or safariVC, you need to pass ' 'in a web URL. This $urlString is not a web URL.'); } diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index c14b62a1e70a..2cf75df6b0ef 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/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.1.1 +version: 6.1.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher/test/mocks/mock_url_launcher_platform.dart b/packages/url_launcher/url_launcher/test/mocks/mock_url_launcher_platform.dart index 789c1435df80..5c53257f7630 100644 --- a/packages/url_launcher/url_launcher/test/mocks/mock_url_launcher_platform.dart +++ b/packages/url_launcher/url_launcher/test/mocks/mock_url_launcher_platform.dart @@ -25,6 +25,7 @@ class MockUrlLauncher extends Fake bool canLaunchCalled = false; bool launchCalled = false; + // ignore: use_setters_to_change_properties void setCanLaunchExpectations(String url) { this.url = url; } @@ -49,6 +50,7 @@ class MockUrlLauncher extends Fake this.webOnlyWindowName = webOnlyWindowName; } + // ignore: use_setters_to_change_properties void setResponse(bool response) { this.response = response; } diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md index b53a92cee707..068650be6d53 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.11 + +* Minor fixes for new analysis options. + ## 2.0.10 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart b/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart index 72540c3c3b80..636cd8c513a3 100644 --- a/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart +++ b/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart @@ -64,6 +64,7 @@ class UrlLauncherPlugin extends UrlLauncherPlatform { // See https://github.com/flutter/flutter/issues/51461 for reference. final String target = webOnlyWindowName ?? ((_isSafari && _isSafariTargetTopScheme(url)) ? '_top' : ''); + // ignore: unsafe_html return _window.open(url, target); } diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index cd8ed2d269c8..cef323035379 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_web description: Web platform implementation of url_launcher repository: https://github.com/flutter/plugins/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.0.10 +version: 2.0.11 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index ede890162f86..1dbc4f73e9c2 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.4.2 + +* Minor fixes for new analysis options. + ## 2.4.1 * Removes unnecessary imports. diff --git a/packages/video_player/video_player/example/integration_test/video_player_test.dart b/packages/video_player/video_player/example/integration_test/video_player_test.dart index 151eb93149ee..633d636c7c69 100644 --- a/packages/video_player/video_player/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player/example/integration_test/video_player_test.dart @@ -182,7 +182,7 @@ void main() { child: FutureBuilder( future: started(), builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.data == true) { + if (snapshot.data ?? false) { return AspectRatio( aspectRatio: _controller.value.aspectRatio, child: VideoPlayer(_controller), diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index f5875975cea5..63afc4a28bc8 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -424,7 +424,7 @@ class _PlayerVideoAndPopPageState extends State<_PlayerVideoAndPopPage> { child: FutureBuilder( future: started(), builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.data == true) { + if (snapshot.data ?? false) { return AspectRatio( aspectRatio: _videoPlayerController.value.aspectRatio, child: VideoPlayer(_videoPlayerController), diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index b0ca56429271..05cfcf154f88 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/plugins/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.4.1 +version: 2.4.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player/test/closed_caption_file_test.dart b/packages/video_player/video_player/test/closed_caption_file_test.dart index b5c0a8e1db12..a20f9479dc45 100644 --- a/packages/video_player/video_player/test/closed_caption_file_test.dart +++ b/packages/video_player/video_player/test/closed_caption_file_test.dart @@ -21,8 +21,7 @@ void main() { 'number: 1, ' 'start: 0:00:01.000000, ' 'end: 0:00:02.000000, ' - 'text: caption' - ')'); + 'text: caption)'); }); }); } diff --git a/packages/video_player/video_player/test/sub_rip_file_test.dart b/packages/video_player/video_player/test/sub_rip_file_test.dart index ea3bfda036ec..82fe6ce033ab 100644 --- a/packages/video_player/video_player/test/sub_rip_file_test.dart +++ b/packages/video_player/video_player/test/sub_rip_file_test.dart @@ -57,7 +57,7 @@ void main() { ); expect( fourthCaption.text, - '- [ Machinery Beeping ]\n- I\'m not sure what that was,', + "- [ Machinery Beeping ]\n- I'm not sure what that was,", ); }); diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 094ffda207c5..e36d044901a4 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.10 + +* Minor fixes for new analysis options. + ## 2.0.9 * Removes unnecessary imports. 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 45d90d675b83..076167383ce7 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 @@ -134,6 +134,7 @@ class VideoPlayer { } /// Controls whether the video should start again after it finishes. + // ignore: use_setters_to_change_properties void setLooping(bool value) { _videoElement.loop = value; } diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 7af0dc46dde8..04fba273a2b6 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/plugins/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.0.9 +version: 2.0.10 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index fa47a6cc3143..31c16da8807b 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.0.4 + +* Minor fixes for new analysis options. + ## 3.0.3 * Removes unnecessary imports. diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index cc001f336849..066ac030595f 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -912,8 +912,8 @@ Future main() async { group('NavigationDelegate', () { const String blankPage = ''; - final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' + - base64Encode(const Utf8Encoder().convert(blankPage)); + final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' + '${base64Encode(const Utf8Encoder().convert(blankPage))}'; testWidgets('can allow requests', (WidgetTester tester) async { final Completer controllerCompleter = diff --git a/packages/webview_flutter/webview_flutter/example/lib/main.dart b/packages/webview_flutter/webview_flutter/example/lib/main.dart index 3d8731127970..79197b02315c 100644 --- a/packages/webview_flutter/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter/example/lib/main.dart @@ -40,8 +40,8 @@ const String kLocalExamplePage = '''

Local demo page

- This is an example page used to demonstrate how to load a local file or HTML - string using the Flutter + This is an example page used to demonstrate how to load a local file or HTML + string using the Flutter webview plugin.

@@ -155,7 +155,7 @@ class _WebViewExampleState extends State { onPressed: () async { String? url; if (controller.hasData) { - url = (await controller.data!.currentUrl())!; + url = await controller.data!.currentUrl(); } ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -345,6 +345,7 @@ class SampleMenu extends StatelessWidget { Future _onListCache( WebViewController controller, BuildContext context) async { await controller.runJavascript('caches.keys()' + // ignore: missing_whitespace_between_adjacent_strings '.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))' '.then((caches) => Toaster.postMessage(caches))'); } diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index a48f6f912c2d..9639b6abe76e 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 3.0.3 +version: 3.0.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart index ad25cadf1dc4..d7189917c221 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart @@ -588,7 +588,7 @@ void main() { }); test('Only valid JavaScript channel names are allowed', () { - final JavascriptMessageHandler noOp = (JavascriptMessage msg) {}; + void noOp(JavascriptMessage msg) {} JavascriptChannel(name: 'Tts1', onMessageReceived: noOp); JavascriptChannel(name: '_Alarm', onMessageReceived: noOp); JavascriptChannel(name: 'foo_bar_', onMessageReceived: noOp); diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 4a451442f6cc..41a6fa273149 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.8 + +* Minor fixes for new analysis options. + ## 2.8.7 * Removes unnecessary imports. 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 4c06fa6b3c18..383fe4508081 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 @@ -1009,8 +1009,8 @@ Future main() async { group('NavigationDelegate', () { const String blankPage = ''; - final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' + - base64Encode(const Utf8Encoder().convert(blankPage)); + final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' + '${base64Encode(const Utf8Encoder().convert(blankPage))}'; testWidgets('can allow requests', (WidgetTester tester) async { final Completer controllerCompleter = 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 349a64916e8b..4492e6e6e26f 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -56,8 +56,8 @@ const String kExamplePage = '''

Local demo page

- This is an example page used to demonstrate how to load a local file or HTML - string using the Flutter + This is an example page used to demonstrate how to load a local file or HTML + string using the Flutter webview plugin.

@@ -341,6 +341,7 @@ class _SampleMenu extends StatelessWidget { Future _onListCache( WebViewController controller, BuildContext context) async { await controller.runJavascript('caches.keys()' + // ignore: missing_whitespace_between_adjacent_strings '.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))' '.then((caches) => Snackbar.postMessage(caches))'); } diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 407887f2ba95..d6cf2b2a1c17 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/plugins/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: 2.8.7 +version: 2.8.8 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index b7254e1a0a7a..8ab70f9a78d3 100644 --- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.0+3 + +* Minor fixes for new analysis options. + ## 0.1.0+2 * Removes unnecessary imports. diff --git a/packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart b/packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart index aa427eb6874a..637c24926275 100644 --- a/packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart +++ b/packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart @@ -44,6 +44,7 @@ class WebWebViewPlatform implements WebViewPlatform { final IFrameElement element = document.getElementById('webview-$viewId')! as IFrameElement; if (creationParams.initialUrl != null) { + // ignore: unsafe_html element.src = creationParams.initialUrl; } onWebViewPlatformCreated(WebWebViewPlatformController( @@ -70,6 +71,7 @@ class WebWebViewPlatformController implements WebViewPlatformController { /// Setter for setting the HttpRequestFactory, for testing purposes. @visibleForTesting + // ignore: avoid_setters_without_getters set httpRequestFactory(HttpRequestFactory factory) { _httpRequestFactory = factory; } @@ -131,6 +133,7 @@ class WebWebViewPlatformController implements WebViewPlatformController { @override Future loadUrl(String url, Map? headers) async { + // ignore: unsafe_html _element.src = url; } @@ -179,7 +182,8 @@ class WebWebViewPlatformController implements WebViewPlatformController { String html, { String? baseUrl, }) async { - _element.src = 'data:text/html,' + Uri.encodeFull(html); + // ignore: unsafe_html + _element.src = 'data:text/html,${Uri.encodeFull(html)}'; } @override @@ -194,8 +198,9 @@ class WebWebViewPlatformController implements WebViewPlatformController { sendData: request.body); final String contentType = httpReq.getResponseHeader('content-type') ?? 'text/html'; + // ignore: unsafe_html _element.src = - 'data:$contentType,' + Uri.encodeFull(httpReq.responseText ?? ''); + 'data:$contentType,${Uri.encodeFull(httpReq.responseText ?? '')}'; } @override @@ -265,7 +270,7 @@ class HttpRequestFactory { String? mimeType, Map? requestHeaders, dynamic sendData, - void onProgress(ProgressEvent e)?}) { + void Function(ProgressEvent e)? onProgress}) { return HttpRequest.request(url, method: method, withCredentials: withCredentials, diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml index 35a7b74a764c..a834c9b77d51 100644 --- a/packages/webview_flutter/webview_flutter_web/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_web description: A Flutter plugin that provides a WebView widget on web. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 0.1.0+2 +version: 0.1.0+3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.dart b/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.dart index 90e2ea465782..6058dcf07272 100644 --- a/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.dart +++ b/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.dart @@ -64,7 +64,7 @@ void main() { // Run controller.loadHtmlString('test html'); // Verify - verify(mockElement.src = 'data:text/html,' + Uri.encodeFull('test html')); + verify(mockElement.src = 'data:text/html,${Uri.encodeFull('test html')}'); }); group('loadRequest', () { @@ -123,7 +123,7 @@ void main() { sendData: Uint8List.fromList('test body'.codeUnits), )); verify( - mockElement.src = 'data:text/plain,' + Uri.encodeFull('test data')); + mockElement.src = 'data:text/plain,${Uri.encodeFull('test data')}'); }); }); }); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 6db769b0d922..00aa7293c9ad 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.7.5 + +* Minor fixes for new analysis options. + ## 2.7.4 * Removes unnecessary imports. 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 aa376f8358e9..40018eee8d98 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 @@ -861,8 +861,8 @@ Future main() async { group('NavigationDelegate', () { const String blankPage = ''; - final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' + - base64Encode(const Utf8Encoder().convert(blankPage)); + final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,' + '${base64Encode(const Utf8Encoder().convert(blankPage))}'; testWidgets('can allow requests', (WidgetTester tester) async { final Completer controllerCompleter = 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 7b30923e1e54..3f61ebfdd6f8 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -326,6 +326,7 @@ class _SampleMenu extends StatelessWidget { Future _onListCache( WebViewController controller, BuildContext context) async { await controller.runJavascript('caches.keys()' + // ignore: missing_whitespace_between_adjacent_strings '.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))' '.then((caches) => Snackbar.postMessage(caches))'); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 0a177d854bc2..19051af6ae1a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -167,7 +167,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { webView.setNavigationDelegate(navigationDelegate); if (params.userAgent != null) { - webView.setCustomUserAgent(params.userAgent!); + webView.setCustomUserAgent(params.userAgent); } if (params.webSettings != null) { @@ -177,7 +177,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { if (params.backgroundColor != null) { webView.setOpaque(false); webView.setBackgroundColor(Colors.transparent); - webView.scrollView.setBackgroundColor(params.backgroundColor!); + webView.scrollView.setBackgroundColor(params.backgroundColor); } if (params.initialUrl != null) { @@ -496,10 +496,10 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { Future _disableZoom() { const WKUserScript userScript = WKUserScript( - "var meta = document.createElement('meta');" - "meta.name = 'viewport';" - "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0," - "user-scalable=no';" + "var meta = document.createElement('meta');\n" + "meta.name = 'viewport';\n" + "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, " + "user-scalable=no';\n" "var head = document.getElementsByTagName('head')[0];head.appendChild(meta);", WKUserScriptInjectionTime.atDocumentEnd, isMainFrameOnly: true, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 365e64720d4f..d85bf329a58e 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/plugins/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: 2.7.4 +version: 2.7.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart index 63e59386ceaf..6af9510b4f03 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart @@ -18,7 +18,7 @@ void main() { }); test('dispose', () { - final Function function = () {}; + void function() {} final int functionInstanceId = instanceManager.tryAddInstance(function)!; FoundationFlutterApis.instance = FoundationFlutterApis( 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 ae13ca9e6b6d..b09f3461c397 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 @@ -163,7 +163,7 @@ void main() { final NSHttpCookieData cookie = verify( mockPlatformHostApi.setCookie( - instanceManager.getInstanceId(httpCookieStore)!, + instanceManager.getInstanceId(httpCookieStore), captureAny, ), ).captured.single as NSHttpCookieData; @@ -373,8 +373,8 @@ void main() { instanceManager: instanceManager, ); verify(mockPlatformHostApi.createFromWebView( - instanceManager.getInstanceId(configurationFromWebView)!, - instanceManager.getInstanceId(webView)!, + instanceManager.getInstanceId(configurationFromWebView), + instanceManager.getInstanceId(webView), )); }); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index d77feee22dd1..271fd5c062e2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -400,10 +400,10 @@ void main() { WKUserScriptInjectionTime.atDocumentEnd); expect( zoomScript.source, - "var meta = document.createElement('meta');" - "meta.name = 'viewport';" - "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0," - "user-scalable=no';" + "var meta = document.createElement('meta');\n" + "meta.name = 'viewport';\n" + "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, " + "user-scalable=no';\n" "var head = document.getElementsByTagName('head')[0];head.appendChild(meta);", ); }); @@ -892,10 +892,10 @@ void main() { zoomScript.injectionTime, WKUserScriptInjectionTime.atDocumentEnd); expect( zoomScript.source, - "var meta = document.createElement('meta');" - "meta.name = 'viewport';" - "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0," - "user-scalable=no';" + "var meta = document.createElement('meta');\n" + "meta.name = 'viewport';\n" + "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, " + "user-scalable=no';\n" "var head = document.getElementsByTagName('head')[0];head.appendChild(meta);", ); }); diff --git a/script/tool/lib/src/create_all_plugins_app_command.dart b/script/tool/lib/src/create_all_plugins_app_command.dart index ad836e19d9c6..595779b8be68 100644 --- a/script/tool/lib/src/create_all_plugins_app_command.dart +++ b/script/tool/lib/src/create_all_plugins_app_command.dart @@ -110,7 +110,7 @@ class CreateAllPluginsAppCommand extends PluginCommand { newGradle.writeln(' multiDexEnabled true'); } else if (line.contains('dependencies {')) { newGradle.writeln( - ' implementation \'com.google.guava:guava:27.0.1-android\'\n', + " implementation 'com.google.guava:guava:27.0.1-android'\n", ); // Tests for https://github.com/flutter/flutter/issues/43383 newGradle.writeln( diff --git a/script/tool/lib/src/custom_test_command.dart b/script/tool/lib/src/custom_test_command.dart index cd9ac32606a6..0ef6e602c070 100644 --- a/script/tool/lib/src/custom_test_command.dart +++ b/script/tool/lib/src/custom_test_command.dart @@ -30,7 +30,7 @@ class CustomTestCommand extends PackageLoopingCommand { @override final String description = 'Runs package-specific custom tests defined in ' - 'a package\'s tool/$_scriptName file.\n\n' + "a package's tool/$_scriptName file.\n\n" 'This command requires "dart" to be in your path.'; @override diff --git a/script/tool/lib/src/format_command.dart b/script/tool/lib/src/format_command.dart index 10c0779de927..f640cbaa5f6c 100644 --- a/script/tool/lib/src/format_command.dart +++ b/script/tool/lib/src/format_command.dart @@ -130,8 +130,7 @@ class FormatCommand extends PluginCommand { if (clangFiles.isNotEmpty) { final String clangFormat = getStringArg('clang-format'); if (!await _hasDependency(clangFormat)) { - printError( - 'Unable to run \'clang-format\'. Make sure that it is in your ' + printError('Unable to run "clang-format". Make sure that it is in your ' 'path, or provide a full path with --clang-format.'); throw ToolExit(_exitDependencyMissing); } @@ -156,7 +155,7 @@ class FormatCommand extends PluginCommand { final String java = getStringArg('java'); if (!await _hasDependency(java)) { printError( - 'Unable to run \'java\'. Make sure that it is in your path, or ' + 'Unable to run "java". Make sure that it is in your path, or ' 'provide a full path with --java.'); throw ToolExit(_exitDependencyMissing); } diff --git a/script/tool/lib/src/license_check_command.dart b/script/tool/lib/src/license_check_command.dart index 87e4c8b14861..5e74d846c13f 100644 --- a/script/tool/lib/src/license_check_command.dart +++ b/script/tool/lib/src/license_check_command.dart @@ -241,8 +241,7 @@ class LicenseCheckCommand extends PluginCommand { } // Sort by path for more usable output. - final int Function(File, File) pathCompare = - (File a, File b) => a.path.compareTo(b.path); + int pathCompare(File a, File b) => a.path.compareTo(b.path); incorrectFirstPartyFiles.sort(pathCompare); unrecognizedThirdPartyFiles.sort(pathCompare); diff --git a/script/tool/lib/src/make_deps_path_based_command.dart b/script/tool/lib/src/make_deps_path_based_command.dart index 9b861c34ec91..4bbecb4d2244 100644 --- a/script/tool/lib/src/make_deps_path_based_command.dart +++ b/script/tool/lib/src/make_deps_path_based_command.dart @@ -169,8 +169,8 @@ class MakeDepsPathBasedCommand extends PluginCommand { // then re-serialiazing so that it's a localized change, rather than // rewriting the whole file (e.g., destroying comments), which could be // more disruptive for local use. - String newPubspecContents = pubspecContents + - ''' + String newPubspecContents = ''' +$pubspecContents $_dependencyOverrideWarningComment dependency_overrides: diff --git a/script/tool/lib/src/pubspec_check_command.dart b/script/tool/lib/src/pubspec_check_command.dart index 654675ebb858..23c9c00e33f0 100644 --- a/script/tool/lib/src/pubspec_check_command.dart +++ b/script/tool/lib/src/pubspec_check_command.dart @@ -225,8 +225,8 @@ class PubspecCheckCommand extends PackageLoopingCommand { bool _checkIssueLink(Pubspec pubspec) { return pubspec.issueTracker ?.toString() - .startsWith(_expectedIssueLinkFormat) == - true; + .startsWith(_expectedIssueLinkFormat) ?? + false; } // Validates the "implements" keyword for a plugin, returning an error @@ -287,8 +287,8 @@ class PubspecCheckCommand extends PackageLoopingCommand { .where((String package) => !dependencies.contains(package)); if (missingPackages.isNotEmpty) { return 'The following default_packages are missing ' - 'corresponding dependencies:\n ' + - missingPackages.join('\n '); + 'corresponding dependencies:\n' + ' ${missingPackages.join('\n ')}'; } return null; diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart index c0e67764360e..b816ee56999c 100644 --- a/script/tool/lib/src/version_check_command.dart +++ b/script/tool/lib/src/version_check_command.dart @@ -403,7 +403,7 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} final String badNextErrorMessage = '${indentation}When bumping the version ' 'for release, the NEXT section should be incorporated into the new ' - 'version\'s release notes.'; + "version's release notes."; // Skip validation for the special NEXT version that's used to accumulate // changes that don't warrant publishing on their own. @@ -531,7 +531,7 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. final Directory gitRoot = packagesDir.fileSystem.directory((await gitDir).path); final String relativePackagePath = - getRelativePosixPath(package.directory, from: gitRoot) + '/'; + '${getRelativePosixPath(package.directory, from: gitRoot)}/'; bool hasChanges = false; bool needsVersionChange = false; bool hasChangelogChange = false; @@ -594,7 +594,7 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. 'change description.'); } else { printError( - 'No CHANGELOG change found. If this PR needs an exemption from' + 'No CHANGELOG change found. If this PR needs an exemption from ' 'the standard policy of listing all changes in the CHANGELOG, ' 'please add a line starting with\n' '$_missingChangelogChangeJustificationMarker\n' diff --git a/script/tool/test/analyze_command_test.dart b/script/tool/test/analyze_command_test.dart index e293e8b85e98..a9b83349306f 100644 --- a/script/tool/test/analyze_command_test.dart +++ b/script/tool/test/analyze_command_test.dart @@ -93,7 +93,7 @@ void main() { ])); }); - test('don\'t elide a non-contained example package', () async { + test("don't elide a non-contained example package", () async { final RepositoryPackage plugin1 = createFakePlugin('a', packagesDir); final RepositoryPackage plugin2 = createFakePlugin('example', packagesDir); diff --git a/script/tool/test/common/plugin_command_test.dart b/script/tool/test/common/plugin_command_test.dart index 7ed3d239b2ad..8c6b38682418 100644 --- a/script/tool/test/common/plugin_command_test.dart +++ b/script/tool/test/common/plugin_command_test.dart @@ -162,7 +162,7 @@ void main() { expect(command.plugins, unorderedEquals([plugin2.path])); }); - test('exclude packages when packages flag isn\'t specified', () async { + test("exclude packages when packages flag isn't specified", () async { createFakePlugin('plugin1', packagesDir); createFakePlugin('plugin2', packagesDir); await runCapturingPrint( diff --git a/script/tool/test/format_command_test.dart b/script/tool/test/format_command_test.dart index 3fa7782245a7..5bd6f97832f7 100644 --- a/script/tool/test/format_command_test.dart +++ b/script/tool/test/format_command_test.dart @@ -218,7 +218,7 @@ void main() { output, containsAllInOrder([ contains( - 'Unable to run \'java\'. Make sure that it is in your path, or ' + 'Unable to run "java". Make sure that it is in your path, or ' 'provide a full path with --java.'), ])); }); @@ -330,8 +330,7 @@ void main() { expect( output, containsAllInOrder([ - contains( - 'Unable to run \'clang-format\'. Make sure that it is in your ' + contains('Unable to run "clang-format". Make sure that it is in your ' 'path, or provide a full path with --clang-format.'), ])); }); diff --git a/script/tool/test/publish_plugin_command_test.dart b/script/tool/test/publish_plugin_command_test.dart index 857828ab9306..d443f8ff0178 100644 --- a/script/tool/test/publish_plugin_command_test.dart +++ b/script/tool/test/publish_plugin_command_test.dart @@ -103,7 +103,7 @@ void main() { expect( output, containsAllInOrder([ - contains('There are files in the package directory that haven\'t ' + contains("There are files in the package directory that haven't " 'been saved in git. Refusing to publish these files:\n\n' '?? /packages/foo/tmp\n\n' 'If the directory should be clean, you can run `git clean -xdf && ' @@ -113,7 +113,7 @@ void main() { ])); }); - test('fails immediately if the remote doesn\'t exist', () async { + test("fails immediately if the remote doesn't exist", () async { createFakePlugin('foo', packagesDir, examples: []); processRunner.mockProcessesForExecutable['git-remote'] = [ @@ -877,8 +877,8 @@ class MockStdin extends Mock implements io.Stdin { } @override - StreamSubscription> listen(void onData(List event)?, - {Function? onError, void onDone()?, bool? cancelOnError}) { + StreamSubscription> listen(void Function(List event)? onData, + {Function? onError, void Function()? onDone, bool? cancelOnError}) { return _controller.stream.listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError); } diff --git a/script/tool/test/pubspec_check_command_test.dart b/script/tool/test/pubspec_check_command_test.dart index 89bb98abd80c..fbe31c72bc2b 100644 --- a/script/tool/test/pubspec_check_command_test.dart +++ b/script/tool/test/pubspec_check_command_test.dart @@ -43,7 +43,7 @@ String _headerSection( repositoryPath, ]; final String repoLink = - 'https://github.com/' + repoLinkPathComponents.join('/'); + 'https://github.com/${repoLinkPathComponents.join('/')}'; final String issueTrackerLink = 'https://github.com/flutter/flutter/issues?' 'q=is%3Aissue+is%3Aopen+label%3A%22p%3A+$name%22'; description ??= 'A test package for validating that the pubspec.yaml ' @@ -55,7 +55,7 @@ ${includeRepository ? 'repository: $repoLink' : ''} ${includeHomepage ? 'homepage: $repoLink' : ''} ${includeIssueTracker ? 'issue_tracker: $issueTrackerLink' : ''} version: 1.0.0 -${publishable ? '' : 'publish_to: \'none\''} +${publishable ? '' : "publish_to: 'none'"} '''; } diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index b0a8990e1300..effdd03891dc 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -313,7 +313,7 @@ String _pluginPlatformSection( assert(false, 'Unrecognized platform: $platform'); break; } - entry = lines.join('\n') + '\n'; + entry = '${lines.join('\n')}\n'; } return entry; diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart index 5b8ed97e20c5..6af3c112f9eb 100644 --- a/script/tool/test/version_check_command_test.dart +++ b/script/tool/test/version_check_command_test.dart @@ -561,7 +561,7 @@ This is necessary because of X, Y, and Z output, containsAllInOrder([ contains('When bumping the version for release, the NEXT section ' - 'should be incorporated into the new version\'s release notes.') + "should be incorporated into the new version's release notes.") ]), ); }); @@ -595,7 +595,7 @@ This is necessary because of X, Y, and Z output, containsAllInOrder([ contains('When bumping the version for release, the NEXT section ' - 'should be incorporated into the new version\'s release notes.'), + "should be incorporated into the new version's release notes."), contains('plugin:\n' ' CHANGELOG.md failed validation.'), ]), @@ -627,7 +627,7 @@ This is necessary because of X, Y, and Z output, containsAllInOrder([ contains('When bumping the version for release, the NEXT section ' - 'should be incorporated into the new version\'s release notes.') + "should be incorporated into the new version's release notes.") ]), ); }); From ff01c1da8335347b7c6bae2a38ede6d4800f27c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Silva=20Oliveira?= Date: Wed, 11 May 2022 19:34:10 +0200 Subject: [PATCH 293/844] [camera] Fix preview pause orientation (#5209) --- packages/camera/camera/CHANGELOG.md | 4 +++ .../camera/lib/src/camera_controller.dart | 8 +++--- packages/camera/camera/pubspec.yaml | 2 +- packages/camera/camera/test/camera_test.dart | 25 +++++++++++++++++++ 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 4715644a0c13..cde2ca284434 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.4+24 + +* Fixes preview orientation when pausing preview with locked orientation. + ## 0.9.4+23 * Minor fixes for new analysis options. diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index 1492ca193761..5014795320f2 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -359,8 +359,8 @@ class CameraController extends ValueNotifier { await CameraPlatform.instance.pausePreview(_cameraId); value = value.copyWith( isPreviewPaused: true, - previewPauseOrientation: - Optional.of(value.deviceOrientation)); + previewPauseOrientation: Optional.of( + value.lockedCaptureOrientation ?? value.deviceOrientation)); } on PlatformException catch (e) { throw CameraException(e.code, e.message); } @@ -520,7 +520,7 @@ class CameraController extends ValueNotifier { value = value.copyWith( isRecordingVideo: true, isRecordingPaused: false, - recordingOrientation: Optional.fromNullable( + recordingOrientation: Optional.of( value.lockedCaptureOrientation ?? value.deviceOrientation)); } on PlatformException catch (e) { throw CameraException(e.code, e.message); @@ -762,7 +762,7 @@ class CameraController extends ValueNotifier { await CameraPlatform.instance.lockCaptureOrientation( _cameraId, orientation ?? value.deviceOrientation); value = value.copyWith( - lockedCaptureOrientation: Optional.fromNullable( + lockedCaptureOrientation: Optional.of( orientation ?? value.deviceOrientation)); } on PlatformException catch (e) { throw CameraException(e.code, e.message); diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 9d56f1b6e4da..d763843d0572 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+23 +version: 0.9.4+24 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera/test/camera_test.dart b/packages/camera/camera/test/camera_test.dart index 34a474b2b4f3..0d3195ba4b4b 100644 --- a/packages/camera/camera/test/camera_test.dart +++ b/packages/camera/camera/test/camera_test.dart @@ -13,6 +13,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:quiver/core.dart'; List get mockAvailableCameras => [ const CameraDescription( @@ -1180,6 +1181,30 @@ void main() { expect(cameraController.value.isPreviewPaused, equals(true)); }); + test( + 'pausePreview() sets previewPauseOrientation according to locked orientation', + () async { + final CameraController cameraController = CameraController( + const CameraDescription( + name: 'cam', + lensDirection: CameraLensDirection.back, + sensorOrientation: 90), + ResolutionPreset.max); + await cameraController.initialize(); + cameraController.value = cameraController.value.copyWith( + isPreviewPaused: false, + deviceOrientation: DeviceOrientation.portraitUp, + lockedCaptureOrientation: + Optional.of(DeviceOrientation.landscapeRight)); + + await cameraController.pausePreview(); + + expect(cameraController.value.deviceOrientation, + equals(DeviceOrientation.portraitUp)); + expect(cameraController.value.previewPauseOrientation, + equals(DeviceOrientation.landscapeRight)); + }); + test('pausePreview() throws $CameraException on $PlatformException', () async { final CameraController cameraController = CameraController( From a15d65d0ef3da2205b017bdaa7b86ecb314e4080 Mon Sep 17 00:00:00 2001 From: Jami Couch Date: Wed, 11 May 2022 14:44:09 -0500 Subject: [PATCH 294/844] [google_sign_in] Add forceCodeForRefreshToken parameter (and new SignInInitParameters class) (#5325) --- .../google_sign_in_platform_interface/AUTHORS | 1 + .../CHANGELOG.md | 3 +- .../google_sign_in_platform_interface.dart | 18 ++++++++- .../src/method_channel_google_sign_in.dart | 18 +++++++-- .../lib/src/types.dart | 37 +++++++++++++++++++ .../pubspec.yaml | 2 +- .../method_channel_google_sign_in_test.dart | 19 ++++++++++ 7 files changed, 90 insertions(+), 8 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_platform_interface/AUTHORS b/packages/google_sign_in/google_sign_in_platform_interface/AUTHORS index 493a0b4ef9c2..35d24a5ae0b5 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/AUTHORS +++ b/packages/google_sign_in/google_sign_in_platform_interface/AUTHORS @@ -64,3 +64,4 @@ Aleksandr Yurkovskiy Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> +Twin Sun, LLC diff --git a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md index da214d3ce6a9..abf01c847d83 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.1.3 * Removes unnecessary imports. +* Adds `SignInInitParameters` class to hold all sign in params, including the new `forceCodeForRefreshToken`. ## 2.1.2 diff --git a/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart b/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart index 50f261bfa578..69d8455b6bd2 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart @@ -63,8 +63,7 @@ abstract class GoogleSignInPlatform { /// if the provided instance is a class implemented with `implements`. void _verifyProvidesDefaultImplementations() {} - /// Initializes the plugin. You must call this method before calling other - /// methods. + /// Initializes the plugin. Deprecated: call [initWithParams] instead. /// /// The [hostedDomain] argument specifies a hosted domain restriction. By /// setting this, sign in will be restricted to accounts of the user in the @@ -89,6 +88,21 @@ abstract class GoogleSignInPlatform { throw UnimplementedError('init() has not been implemented.'); } + /// Initializes the plugin with specified [params]. You must call this method + /// before calling other methods. + /// + /// See: + /// + /// * [SignInInitParameters] + Future initWithParams(SignInInitParameters params) async { + await init( + scopes: params.scopes, + signInOption: params.signInOption, + hostedDomain: params.hostedDomain, + clientId: params.clientId, + ); + } + /// Attempts to reuse pre-existing credentials to sign in again, without user interaction. Future signInSilently() async { throw UnimplementedError('signInSilently() has not been implemented.'); diff --git a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart index e56d2028a205..8b755fbf1cdd 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart @@ -25,11 +25,21 @@ class MethodChannelGoogleSignIn extends GoogleSignInPlatform { String? hostedDomain, String? clientId, }) { + return initWithParams(SignInInitParameters( + scopes: scopes, + signInOption: signInOption, + hostedDomain: hostedDomain, + clientId: clientId)); + } + + @override + Future initWithParams(SignInInitParameters params) { return channel.invokeMethod('init', { - 'signInOption': signInOption.toString(), - 'scopes': scopes, - 'hostedDomain': hostedDomain, - 'clientId': clientId, + 'signInOption': params.signInOption.toString(), + 'scopes': params.scopes, + 'hostedDomain': params.hostedDomain, + 'clientId': params.clientId, + 'forceCodeForRefreshToken': params.forceCodeForRefreshToken, }); } diff --git a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart index bc50a1d2516d..2cac5e886729 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.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/widgets.dart'; import 'package:quiver/core.dart'; /// Default configuration options to use when signing in. @@ -22,6 +23,42 @@ enum SignInOption { games } +/// The parameters to use when initializing the sign in process. +/// +/// See: +/// https://developers.google.com/identity/sign-in/web/reference#gapiauth2initparams +@immutable +class SignInInitParameters { + /// The parameters to use when initializing the sign in process. + const SignInInitParameters({ + this.scopes = const [], + this.signInOption = SignInOption.standard, + this.hostedDomain, + this.clientId, + this.forceCodeForRefreshToken = false, + }); + + /// The list of OAuth scope codes to request when signing in. + final List scopes; + + /// The user experience to use when signing in. [SignInOption.games] is + /// only supported on Android. + final SignInOption signInOption; + + /// Restricts sign in to accounts of the user in the specified domain. + /// By default, the list of accounts will not be restricted. + final String? hostedDomain; + + /// The client ID to use when signing in. + final String? clientId; + + /// If true, ensures the authorization code can be exchanged for an access + /// token. + /// + /// This is only used on Android. + final bool forceCodeForRefreshToken; +} + /// Holds information about the signed in user. class GoogleSignInUserData { /// Uses the given data to construct an instance. diff --git a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml index a5bbaedd51e7..0deafe80a863 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%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.1.2 +version: 2.1.3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart b/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart index b6604d1e658e..1ffdc5a4f95e 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart @@ -107,6 +107,7 @@ void main() { 'scopes': ['two', 'scopes'], 'signInOption': 'SignInOption.games', 'clientId': 'fakeClientId', + 'forceCodeForRefreshToken': false, }), () { googleSignIn.getTokens( @@ -136,5 +137,23 @@ void main() { expect(log, tests.values); }); + + test('initWithParams passes through arguments to the channel', () async { + await googleSignIn.initWithParams(const SignInInitParameters( + hostedDomain: 'example.com', + scopes: ['two', 'scopes'], + signInOption: SignInOption.games, + clientId: 'fakeClientId', + forceCodeForRefreshToken: true)); + expect(log, [ + isMethodCall('init', arguments: { + 'hostedDomain': 'example.com', + 'scopes': ['two', 'scopes'], + 'signInOption': 'SignInOption.games', + 'clientId': 'fakeClientId', + 'forceCodeForRefreshToken': true, + }), + ]); + }); }); } From c13e8a50688b65b740a47adc1f6149ffcb9640eb Mon Sep 17 00:00:00 2001 From: Piotr Mitkowski Date: Wed, 11 May 2022 22:29:12 +0200 Subject: [PATCH 295/844] [image_picker] add requestFullMetadata for iOS (optional permissions) - platform interface (#5603) --- .../CHANGELOG.md | 8 +- .../method_channel_image_picker.dart | 20 +- .../image_picker_platform.dart | 32 ++ .../lib/src/types/image_picker_options.dart | 50 +++ .../lib/src/types/types.dart | 1 + .../pubspec.yaml | 2 +- .../new_method_channel_image_picker_test.dart | 322 ++++++++++++++++-- 7 files changed, 409 insertions(+), 26 deletions(-) create mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/image_picker_options.dart diff --git a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md index 2defdd2d84cc..0a4e98bf7dbe 100644 --- a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md +++ b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md @@ -1,6 +1,10 @@ -## NEXT +## 2.5.0 -* Minor fixes for new analysis options. +* Deprecates `getImage` in favor of a new method `getImageFromSource`. + * Adds `requestFullMetadata` option that allows disabling extra permission requests + on certain platforms. + * Moves optional image picking parameters to `ImagePickerOptions` class. +* Minor fixes for new analysis options. ## 2.4.4 diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart index e1e6082c8047..ba5d60d7a677 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart @@ -87,6 +87,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { double? maxHeight, int? imageQuality, CameraDevice preferredCameraDevice = CameraDevice.rear, + bool requestFullMetadata = true, }) { if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { throw ArgumentError.value( @@ -108,7 +109,8 @@ class MethodChannelImagePicker extends ImagePickerPlatform { 'maxWidth': maxWidth, 'maxHeight': maxHeight, 'imageQuality': imageQuality, - 'cameraDevice': preferredCameraDevice.index + 'cameraDevice': preferredCameraDevice.index, + 'requestFullMetadata': requestFullMetadata, }, ); } @@ -197,6 +199,22 @@ class MethodChannelImagePicker extends ImagePickerPlatform { return path != null ? XFile(path) : null; } + @override + Future getImageFromSource({ + required ImageSource source, + ImagePickerOptions options = const ImagePickerOptions(), + }) async { + final String? path = await _getImagePath( + source: source, + maxHeight: options.maxHeight, + maxWidth: options.maxWidth, + imageQuality: options.imageQuality, + preferredCameraDevice: options.preferredCameraDevice, + requestFullMetadata: options.requestFullMetadata, + ); + return path != null ? XFile(path) : null; + } + @override Future?> getMultiImage({ double? maxWidth, diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart index 8f02e1683267..d1d06f904fe6 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart @@ -146,6 +146,8 @@ abstract class ImagePickerPlatform extends PlatformInterface { throw UnimplementedError('retrieveLostData() has not been implemented.'); } + /// This method is deprecated in favor of [getImageFromSource] and will be removed in a future update. + /// /// Returns an [XFile] with the image that was picked. /// /// The `source` argument controls where the image comes from. This can @@ -251,4 +253,34 @@ abstract class ImagePickerPlatform extends PlatformInterface { Future getLostData() { throw UnimplementedError('getLostData() has not been implemented.'); } + + /// Returns an [XFile] with the image that was picked. + /// + /// The `source` argument controls where the image comes from. This can + /// be either [ImageSource.camera] or [ImageSource.gallery]. + /// + /// The `options` argument controls additional settings that can be used when + /// picking an image. See [ImagePickerOptions] for more details. + /// + /// Where iOS supports HEIC images, Android 8 and below doesn't. Android 9 and + /// above only support HEIC images if used in addition to a size modification, + /// of which the usage is explained in [ImagePickerOptions]. + /// + /// In Android, the MainActivity can be destroyed for various reasons. If that + /// happens, the result will be lost in this call. You can then call [getLostData] + /// when your app relaunches to retrieve the lost data. + /// + /// If no images were picked, the return value is null. + Future getImageFromSource({ + required ImageSource source, + ImagePickerOptions options = const ImagePickerOptions(), + }) { + return getImage( + source: source, + maxHeight: options.maxHeight, + maxWidth: options.maxWidth, + imageQuality: options.imageQuality, + preferredCameraDevice: options.preferredCameraDevice, + ); + } } diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/image_picker_options.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/image_picker_options.dart new file mode 100644 index 000000000000..cdc89a920178 --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/image_picker_options.dart @@ -0,0 +1,50 @@ +// 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:image_picker_platform_interface/src/types/types.dart'; + +/// Specifies options for picking a single image from the device's camera or gallery. +class ImagePickerOptions { + /// Creates an instance with the given [maxHeight], [maxWidth], [imageQuality], + /// [referredCameraDevice] and [requestFullMetadata]. + const ImagePickerOptions({ + this.maxHeight, + this.maxWidth, + this.imageQuality, + this.preferredCameraDevice = CameraDevice.rear, + this.requestFullMetadata = true, + }); + + /// The maximum width of the image, in pixels. + /// + /// If null, the image will only be resized if [maxHeight] is specified. + final double? maxWidth; + + /// The maximum height of the image, in pixels. + /// + /// If null, the image will only be resized if [maxWidth] is specified. + final double? maxHeight; + + /// Modifies the quality of the image, ranging from 0-100 where 100 is the + /// original/max quality. + /// + /// Compression is only supported for certain image types such as JPEG. If + /// compression is not supported for the image that is picked, a warning + /// message will be logged. + /// + /// If null, the image will be returned with the original quality. + final int? imageQuality; + + /// Used to specify the camera to use when the `source` is [ImageSource.camera]. + /// + /// Ignored if the source is not [ImageSource.camera], or the chosen camera is not + /// supported on the device. Defaults to [CameraDevice.rear]. + final CameraDevice preferredCameraDevice; + + /// If true, requests full image metadata, which may require extra permissions + /// on some platforms, (e.g., NSPhotoLibraryUsageDescription on iOS). + // + // Defaults to true. + final bool requestFullMetadata; +} diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart index 7f2844230287..dad86c5d1ba1 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. export 'camera_device.dart'; +export 'image_picker_options.dart'; export 'image_source.dart'; export 'lost_data_response.dart'; export 'picked_file/picked_file.dart'; diff --git a/packages/image_picker/image_picker_platform_interface/pubspec.yaml b/packages/image_picker/image_picker_platform_interface/pubspec.yaml index 54fd17e47260..be6d5442d03b 100644 --- a/packages/image_picker/image_picker_platform_interface/pubspec.yaml +++ b/packages/image_picker/image_picker_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/i 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.4.4 +version: 2.5.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart index 79d971f217f0..72ed363ef7ae 100644 --- a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart @@ -40,14 +40,16 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 1, 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), ], ); @@ -93,55 +95,62 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': 70, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': 70, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': 70, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), ], ); }); - test('does not accept a invalid imageQuality argument', () { + test('does not accept an invalid imageQuality argument', () { expect( () => picker.pickImage(imageQuality: -1, source: ImageSource.gallery), throwsArgumentError, @@ -196,6 +205,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': 0, + 'requestFullMetadata': true, }), ], ); @@ -215,6 +225,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': 1, + 'requestFullMetadata': true, }), ], ); @@ -320,7 +331,7 @@ void main() { ); }); - test('does not accept a invalid imageQuality argument', () { + test('does not accept an invalid imageQuality argument', () { returnValue = ['0', '1']; expect( () => picker.pickMultiImage(imageQuality: -1), @@ -509,14 +520,16 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 1, 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), ], ); @@ -562,55 +575,62 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': null, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': 70, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': 70, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), isMethodCall('pickImage', arguments: { 'source': 0, 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': 70, - 'cameraDevice': 0 + 'cameraDevice': 0, + 'requestFullMetadata': true, }), ], ); }); - test('does not accept a invalid imageQuality argument', () { + test('does not accept an invalid imageQuality argument', () { expect( () => picker.getImage(imageQuality: -1, source: ImageSource.gallery), throwsArgumentError, @@ -664,6 +684,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': 0, + 'requestFullMetadata': true, }), ], ); @@ -683,6 +704,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': 1, + 'requestFullMetadata': true, }), ], ); @@ -788,7 +810,7 @@ void main() { ); }); - test('does not accept a invalid imageQuality argument', () { + test('does not accept an invalid imageQuality argument', () { returnValue = ['0', '1']; expect( () => picker.getMultiImage(imageQuality: -1), @@ -979,5 +1001,261 @@ void main() { expect(picker.getLostData(), throwsAssertionError); }); }); + + group('#getImageFromSource', () { + test('passes the image source argument correctly', () async { + await picker.getImageFromSource(source: ImageSource.camera); + await picker.getImageFromSource(source: ImageSource.gallery); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 1, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + await picker.getImageFromSource(source: ImageSource.camera); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxWidth: 10.0), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxHeight: 10.0), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxWidth: 10.0, + maxHeight: 20.0, + ), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxWidth: 10.0, + imageQuality: 70, + ), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxHeight: 10.0, + imageQuality: 70, + ), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ), + ); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('does not accept an invalid imageQuality argument', () { + expect( + () => picker.getImageFromSource( + source: ImageSource.gallery, + options: const ImagePickerOptions(imageQuality: -1), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.gallery, + options: const ImagePickerOptions(imageQuality: 101), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(imageQuality: -1), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(imageQuality: 101), + ), + throwsArgumentError, + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxWidth: -1.0), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxHeight: -1.0), + ), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + picker.channel + .setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.getImageFromSource(source: ImageSource.gallery), + isNull); + expect(await picker.getImageFromSource(source: ImageSource.camera), + isNull); + }); + + test('camera position defaults to back', () async { + await picker.getImageFromSource(source: ImageSource.camera); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + preferredCameraDevice: CameraDevice.front, + ), + ); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 1, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('passes the full metadata argument correctly', () async { + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(requestFullMetadata: false), + ); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': false, + }), + ], + ); + }); + }); }); } From d76b52d8996c4d91ca614e185567f6cac179ca35 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Wed, 11 May 2022 18:30:42 -0700 Subject: [PATCH 296/844] [google_sign_in] Fix tests to recognize new request attribute. (#5702) --- packages/google_sign_in/google_sign_in/CHANGELOG.md | 4 ++++ .../google_sign_in/test/google_sign_in_test.dart | 2 ++ 2 files changed, 6 insertions(+) diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 5b47536cd2e2..8416e81f2412 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes tests to recognize new default `forceCodeForRefreshToken` request attribute. + ## 5.3.1 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart index 119ee50a383b..3b0654637d68 100644 --- a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart @@ -96,6 +96,7 @@ void main() { 'scopes': [], 'hostedDomain': null, 'clientId': fakeClientId, + 'forceCodeForRefreshToken': false, }), isMethodCall('signIn', arguments: null), ], @@ -431,5 +432,6 @@ Matcher _isSignInMethodCall({String signInOption = 'SignInOption.standard'}) { 'scopes': [], 'hostedDomain': null, 'clientId': null, + 'forceCodeForRefreshToken': false, }); } From 298a26d909bf454b8a5054a12f6b6edd12cc5f59 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 12 May 2022 00:44:09 -0400 Subject: [PATCH 297/844] Roll Flutter from df7111a848cb to a9ac7fb03be1 (2 revisions) (#5697) --- .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 bca64d8293a3..e1efdd362301 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -df7111a848cb0cc076a9c89d48332f0ec2700c45 +a9ac7fb03be1d574d726e98b90743015716de7fd From 4479e26172b997a4118599ca45742a7ef224e5b6 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Thu, 12 May 2022 04:59:09 -0700 Subject: [PATCH 298/844] [ios_platform_images] Ignore ImageProvider.load deprecation. (#5701) --- packages/ios_platform_images/CHANGELOG.md | 4 ++++ packages/ios_platform_images/lib/ios_platform_images.dart | 2 ++ packages/ios_platform_images/pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index cf2632feaac7..d432bdb7ee8e 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.0+7 + +* Ignores the warning for the upcoming deprecation of `ImageProvider.load`. + ## 0.2.0+6 * Removes unnecessary imports. diff --git a/packages/ios_platform_images/lib/ios_platform_images.dart b/packages/ios_platform_images/lib/ios_platform_images.dart index 4064fb312506..6a85ea0d189b 100644 --- a/packages/ios_platform_images/lib/ios_platform_images.dart +++ b/packages/ios_platform_images/lib/ios_platform_images.dart @@ -63,7 +63,9 @@ class _FutureMemoryImage extends ImageProvider<_FutureMemoryImage> { } /// See [ImageProvider.load]. + // TODO(jmagman): Implement the new API once it lands, https://github.com/flutter/flutter/issues/103556 @override + // ignore:deprecated_member_use ImageStreamCompleter load(_FutureMemoryImage key, DecoderCallback decode) { return _FutureImageStreamCompleter( codec: _loadAsync(key, decode), diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index 41a177560299..ddc02e6235db 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -2,7 +2,7 @@ name: ios_platform_images description: A plugin to share images between Flutter and iOS in add-to-app setups. repository: https://github.com/flutter/plugins/tree/main/packages/ios_platform_images issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+ios_platform_images%22 -version: 0.2.0+6 +version: 0.2.0+7 environment: sdk: ">=2.14.0 <3.0.0" From 3eedf6c8003be469cc24bd699781be4c48f304d0 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 12 May 2022 19:13:39 -0400 Subject: [PATCH 299/844] [image_picker] Switch unit tests to mock plaform implementation (#5706) `image_picker`'s app-facing tests were never updated during federation to use a mock platform implementation, and instead were still mocking method channels. That makes them fragile to implementation details of the default method channel implementation that is part of another package, and thus subject to breakage when the method channel changes. This converts them to using a mock platform implementation, so it's only testing the layer within this package. Removes some tests that were testing things that only made sense at the method channel layer. Adds argument assertions that there were tests for, but were previously only enforced in the implementations. As these are API constraints, they should be enforced at the API layer, not at each implementation's layer as they currently are. --- .../image_picker/image_picker/CHANGELOG.md | 7 + .../image_picker/lib/image_picker.dart | 22 + .../image_picker/image_picker/pubspec.yaml | 4 +- .../test/image_picker_deprecated_test.dart | 436 ++++++----------- .../image_picker/test/image_picker_test.dart | 449 +++++++----------- .../test/image_picker_test.mocks.dart | 136 ++++++ 6 files changed, 476 insertions(+), 578 deletions(-) create mode 100644 packages/image_picker/image_picker/test/image_picker_test.mocks.dart diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 156b9a3a6d23..36a47b1a3d42 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.8.5+3 + +* Adds argument error assertions to the app-facing package, to ensure + consistency across platform implementations. +* Updates tests to use a mock platform instead of relying on default + method channel implementation internals. + ## 0.8.5+2 * Minor fixes for new analysis options. diff --git a/packages/image_picker/image_picker/lib/image_picker.dart b/packages/image_picker/image_picker/lib/image_picker.dart index 5bc99d7f0bb2..84c649028c96 100755 --- a/packages/image_picker/image_picker/lib/image_picker.dart +++ b/packages/image_picker/image_picker/lib/image_picker.dart @@ -207,6 +207,17 @@ class ImagePicker { int? imageQuality, CameraDevice preferredCameraDevice = CameraDevice.rear, }) { + if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { + throw ArgumentError.value( + imageQuality, 'imageQuality', 'must be between 0 and 100'); + } + if (maxWidth != null && maxWidth < 0) { + throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative'); + } + if (maxHeight != null && maxHeight < 0) { + throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative'); + } + return platform.getImage( source: source, maxWidth: maxWidth, @@ -245,6 +256,17 @@ class ImagePicker { double? maxHeight, int? imageQuality, }) { + if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { + throw ArgumentError.value( + imageQuality, 'imageQuality', 'must be between 0 and 100'); + } + if (maxWidth != null && maxWidth < 0) { + throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative'); + } + if (maxHeight != null && maxHeight < 0) { + throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative'); + } + return platform.getMultiImage( maxWidth: maxWidth, maxHeight: maxHeight, diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index cc34d8ab33f5..9d0cedeec484 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5+2 +version: 0.8.5+3 environment: sdk: ">=2.14.0 <3.0.0" @@ -28,6 +28,8 @@ dependencies: image_picker_platform_interface: ^2.3.0 dev_dependencies: + build_runner: ^2.1.10 + cross_file: ^0.3.1+1 # Mockito generates a direct include. flutter_test: sdk: flutter mockito: ^5.0.0 diff --git a/packages/image_picker/image_picker/test/image_picker_deprecated_test.dart b/packages/image_picker/image_picker/test/image_picker_deprecated_test.dart index 00049e14f808..b3db08020d7e 100644 --- a/packages/image_picker/image_picker/test/image_picker_deprecated_test.dart +++ b/packages/image_picker/image_picker/test/image_picker_deprecated_test.dart @@ -14,63 +14,46 @@ import 'package:image_picker_platform_interface/image_picker_platform_interface. import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - group('$ImagePicker', () { - const MethodChannel channel = - MethodChannel('plugins.flutter.io/image_picker'); +import 'image_picker_test.mocks.dart' as base_mock; - final List log = []; +// Add the mixin to make the platform interface accept the mock. +class MockImagePickerPlatform extends base_mock.MockImagePickerPlatform + with MockPlatformInterfaceMixin {} - final ImagePicker picker = ImagePicker(); +void main() { + group('ImagePicker', () { + late MockImagePickerPlatform mockPlatform; - test('ImagePicker platform instance overrides the actual platform used', - () { - final ImagePickerPlatform savedPlatform = ImagePickerPlatform.instance; - final MockPlatform mockPlatform = MockPlatform(); + setUp(() { + mockPlatform = MockImagePickerPlatform(); ImagePickerPlatform.instance = mockPlatform; - expect(ImagePicker.platform, mockPlatform); - ImagePickerPlatform.instance = savedPlatform; }); group('#Single image/video', () { setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - return ''; - }); - - log.clear(); + when(mockPlatform.pickImage( + source: anyNamed('source'), + maxWidth: anyNamed('maxWidth'), + maxHeight: anyNamed('maxHeight'), + imageQuality: anyNamed('imageQuality'), + preferredCameraDevice: anyNamed('preferredCameraDevice'))) + .thenAnswer((Invocation _) async => null); }); group('#pickImage', () { test('passes the image source argument correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.getImage(source: ImageSource.camera); await picker.getImage(source: ImageSource.gallery); - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 1, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - ], - ); + verifyInOrder([ + mockPlatform.pickImage(source: ImageSource.camera), + mockPlatform.pickImage(source: ImageSource.gallery), + ]); }); test('passes the width and height arguments correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.getImage(source: ImageSource.camera); await picker.getImage( source: ImageSource.camera, @@ -95,277 +78,182 @@ void main() { maxHeight: 20.0, imageQuality: 70); - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': 70, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': 70, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': 70, - 'cameraDevice': 0 - }), - ], - ); - }); - - test('does not accept a negative width or height argument', () { - expect( - picker.getImage(source: ImageSource.camera, maxWidth: -1.0), - throwsArgumentError, - ); - - expect( - picker.getImage(source: ImageSource.camera, maxHeight: -1.0), - throwsArgumentError, - ); + verifyInOrder([ + mockPlatform.pickImage( + source: ImageSource.camera, + maxWidth: null, + maxHeight: null, + imageQuality: null), + mockPlatform.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: null, + imageQuality: null), + mockPlatform.pickImage( + source: ImageSource.camera, + maxWidth: null, + maxHeight: 10.0, + imageQuality: null), + mockPlatform.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: null), + mockPlatform.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: null, + imageQuality: 70), + mockPlatform.pickImage( + source: ImageSource.camera, + maxWidth: null, + maxHeight: 10.0, + imageQuality: 70), + mockPlatform.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70), + ]); }); - test('handles a null image path response gracefully', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) => null); + test('handles a null image file response gracefully', () async { + final ImagePicker picker = ImagePicker(); expect(await picker.getImage(source: ImageSource.gallery), isNull); expect(await picker.getImage(source: ImageSource.camera), isNull); }); test('camera position defaults to back', () async { + final ImagePicker picker = ImagePicker(); await picker.getImage(source: ImageSource.camera); - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0, - }), - ], - ); + verify(mockPlatform.pickImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.rear)); }); test('camera position can set to front', () async { + final ImagePicker picker = ImagePicker(); await picker.getImage( source: ImageSource.camera, preferredCameraDevice: CameraDevice.front); - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 1, - }), - ], - ); + verify(mockPlatform.pickImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front)); }); }); group('#pickVideo', () { + setUp(() { + when(mockPlatform.pickVideo( + source: anyNamed('source'), + preferredCameraDevice: anyNamed('preferredCameraDevice'), + maxDuration: anyNamed('maxDuration'))) + .thenAnswer((Invocation _) async => null); + }); + test('passes the image source argument correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.getVideo(source: ImageSource.camera); await picker.getVideo(source: ImageSource.gallery); - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'cameraDevice': 0, - 'maxDuration': null, - }), - isMethodCall('pickVideo', arguments: { - 'source': 1, - 'cameraDevice': 0, - 'maxDuration': null, - }), - ], - ); + verifyInOrder([ + mockPlatform.pickVideo(source: ImageSource.camera), + mockPlatform.pickVideo(source: ImageSource.gallery), + ]); }); test('passes the duration argument correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.getVideo(source: ImageSource.camera); await picker.getVideo( source: ImageSource.camera, maxDuration: const Duration(seconds: 10)); - await picker.getVideo( - source: ImageSource.camera, - maxDuration: const Duration(minutes: 1)); - await picker.getVideo( - source: ImageSource.camera, - maxDuration: const Duration(hours: 1)); - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': null, - 'cameraDevice': 0, - }), - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': 10, - 'cameraDevice': 0, - }), - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': 60, - 'cameraDevice': 0, - }), - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': 3600, - 'cameraDevice': 0, - }), - ], - ); + + verifyInOrder([ + mockPlatform.pickVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.rear, + maxDuration: null), + mockPlatform.pickVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.rear, + maxDuration: const Duration(seconds: 10)), + ]); }); - test('handles a null video path response gracefully', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) => null); + test('handles a null video file response gracefully', () async { + final ImagePicker picker = ImagePicker(); expect(await picker.getVideo(source: ImageSource.gallery), isNull); expect(await picker.getVideo(source: ImageSource.camera), isNull); }); test('camera position defaults to back', () async { + final ImagePicker picker = ImagePicker(); await picker.getVideo(source: ImageSource.camera); - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'cameraDevice': 0, - 'maxDuration': null, - }), - ], - ); + verify(mockPlatform.pickVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.rear)); }); test('camera position can set to front', () async { + final ImagePicker picker = ImagePicker(); await picker.getVideo( source: ImageSource.camera, preferredCameraDevice: CameraDevice.front); - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': null, - 'cameraDevice': 1, - }), - ], - ); + verify(mockPlatform.pickVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front)); }); }); group('#retrieveLostData', () { test('retrieveLostData get success response', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return { - 'type': 'image', - 'path': '/example/path', - }; - }); + final ImagePicker picker = ImagePicker(); + when(mockPlatform.retrieveLostData()).thenAnswer( + (Invocation _) async => LostData( + file: PickedFile('/example/path'), type: RetrieveType.image)); + final LostData response = await picker.getLostData(); + expect(response.type, RetrieveType.image); expect(response.file!.path, '/example/path'); }); test('retrieveLostData get error response', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return { - 'type': 'video', - 'errorCode': 'test_error_code', - 'errorMessage': 'test_error_message', - }; - }); + final ImagePicker picker = ImagePicker(); + when(mockPlatform.retrieveLostData()).thenAnswer( + (Invocation _) async => LostData( + exception: PlatformException( + code: 'test_error_code', message: 'test_error_message'), + type: RetrieveType.video)); + final LostData response = await picker.getLostData(); + expect(response.type, RetrieveType.video); expect(response.exception!.code, 'test_error_code'); expect(response.exception!.message, 'test_error_message'); }); - - test('retrieveLostData get null response', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return null; - }); - expect((await picker.getLostData()).isEmpty, true); - }); - - test('retrieveLostData get both path and error should throw', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return { - 'type': 'video', - 'errorCode': 'test_error_code', - 'errorMessage': 'test_error_message', - 'path': '/example/path', - }; - }); - expect(picker.getLostData(), throwsAssertionError); - }); }); }); group('Multi images', () { setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - return []; - }); - log.clear(); + when(mockPlatform.pickMultiImage( + maxWidth: anyNamed('maxWidth'), + maxHeight: anyNamed('maxHeight'), + imageQuality: anyNamed('imageQuality'))) + .thenAnswer((Invocation _) async => null); }); group('#pickMultiImage', () { test('passes the width and height arguments correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.getMultiImage(); await picker.getMultiImage( maxWidth: 10.0, @@ -388,62 +276,26 @@ void main() { await picker.getMultiImage( maxWidth: 10.0, maxHeight: 20.0, imageQuality: 70); - expect( - log, - [ - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': 70, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': 70, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': 70, - }), - ], - ); + verifyInOrder([ + mockPlatform.pickMultiImage( + maxWidth: null, maxHeight: null, imageQuality: null), + mockPlatform.pickMultiImage( + maxWidth: 10.0, maxHeight: null, imageQuality: null), + mockPlatform.pickMultiImage( + maxWidth: null, maxHeight: 10.0, imageQuality: null), + mockPlatform.pickMultiImage( + maxWidth: 10.0, maxHeight: 20.0, imageQuality: null), + mockPlatform.pickMultiImage( + maxWidth: 10.0, maxHeight: null, imageQuality: 70), + mockPlatform.pickMultiImage( + maxWidth: null, maxHeight: 10.0, imageQuality: 70), + mockPlatform.pickMultiImage( + maxWidth: 10.0, maxHeight: 20.0, imageQuality: 70), + ]); }); - test('does not accept a negative width or height argument', () { - expect( - picker.getMultiImage(maxWidth: -1.0), - throwsArgumentError, - ); - - expect( - picker.getMultiImage(maxHeight: -1.0), - throwsArgumentError, - ); - }); - - test('handles a null image path response gracefully', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) => null); + test('handles a null image file response gracefully', () async { + final ImagePicker picker = ImagePicker(); expect(await picker.getMultiImage(), isNull); expect(await picker.getMultiImage(), isNull); @@ -452,7 +304,3 @@ void main() { }); }); } - -class MockPlatform extends Mock - with MockPlatformInterfaceMixin - implements ImagePickerPlatform {} diff --git a/packages/image_picker/image_picker/test/image_picker_test.dart b/packages/image_picker/image_picker/test/image_picker_test.dart index b41fbe3381df..f981195fe1b3 100644 --- a/packages/image_picker/image_picker/test/image_picker_test.dart +++ b/packages/image_picker/image_picker/test/image_picker_test.dart @@ -6,66 +6,51 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:image_picker/image_picker.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - group('$ImagePicker', () { - const MethodChannel channel = - MethodChannel('plugins.flutter.io/image_picker'); +import 'image_picker_test.mocks.dart' as base_mock; - final List log = []; +// Add the mixin to make the platform interface accept the mock. +class MockImagePickerPlatform extends base_mock.MockImagePickerPlatform + with MockPlatformInterfaceMixin {} - final ImagePicker picker = ImagePicker(); +@GenerateMocks([ImagePickerPlatform]) +void main() { + group('ImagePicker', () { + late MockImagePickerPlatform mockPlatform; - test('ImagePicker platform instance overrides the actual platform used', - () { - final ImagePickerPlatform savedPlatform = ImagePickerPlatform.instance; - final MockPlatform mockPlatform = MockPlatform(); + setUp(() { + mockPlatform = MockImagePickerPlatform(); ImagePickerPlatform.instance = mockPlatform; - expect(ImagePicker.platform, mockPlatform); - ImagePickerPlatform.instance = savedPlatform; }); group('#Single image/video', () { - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - return ''; + group('#pickImage', () { + setUp(() { + when(mockPlatform.getImage( + source: anyNamed('source'), + maxWidth: anyNamed('maxWidth'), + maxHeight: anyNamed('maxHeight'), + imageQuality: anyNamed('imageQuality'), + preferredCameraDevice: anyNamed('preferredCameraDevice'))) + .thenAnswer((Invocation _) async => null); }); - log.clear(); - }); - - group('#pickImage', () { test('passes the image source argument correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.pickImage(source: ImageSource.camera); await picker.pickImage(source: ImageSource.gallery); - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 1, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - ], - ); + verifyInOrder([ + mockPlatform.getImage(source: ImageSource.camera), + mockPlatform.getImage(source: ImageSource.gallery), + ]); }); test('passes the width and height arguments correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.pickImage(source: ImageSource.camera); await picker.pickImage( source: ImageSource.camera, @@ -90,242 +75,184 @@ void main() { maxHeight: 20.0, imageQuality: 70); - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': null, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': 70, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': 70, - 'cameraDevice': 0 - }), - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': 70, - 'cameraDevice': 0 - }), - ], - ); + verifyInOrder([ + mockPlatform.getImage( + source: ImageSource.camera, + maxWidth: null, + maxHeight: null, + imageQuality: null), + mockPlatform.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: null, + imageQuality: null), + mockPlatform.getImage( + source: ImageSource.camera, + maxWidth: null, + maxHeight: 10.0, + imageQuality: null), + mockPlatform.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: null), + mockPlatform.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: null, + imageQuality: 70), + mockPlatform.getImage( + source: ImageSource.camera, + maxWidth: null, + maxHeight: 10.0, + imageQuality: 70), + mockPlatform.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70), + ]); }); test('does not accept a negative width or height argument', () { + final ImagePicker picker = ImagePicker(); expect( - picker.pickImage(source: ImageSource.camera, maxWidth: -1.0), + () => picker.pickImage(source: ImageSource.camera, maxWidth: -1.0), throwsArgumentError, ); expect( - picker.pickImage(source: ImageSource.camera, maxHeight: -1.0), + () => picker.pickImage(source: ImageSource.camera, maxHeight: -1.0), throwsArgumentError, ); }); - test('handles a null image path response gracefully', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) => null); + test('handles a null image file response gracefully', () async { + final ImagePicker picker = ImagePicker(); expect(await picker.pickImage(source: ImageSource.gallery), isNull); expect(await picker.pickImage(source: ImageSource.camera), isNull); }); test('camera position defaults to back', () async { + final ImagePicker picker = ImagePicker(); await picker.pickImage(source: ImageSource.camera); - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 0, - }), - ], - ); + verify(mockPlatform.getImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.rear)); }); test('camera position can set to front', () async { + final ImagePicker picker = ImagePicker(); await picker.pickImage( source: ImageSource.camera, preferredCameraDevice: CameraDevice.front); - expect( - log, - [ - isMethodCall('pickImage', arguments: { - 'source': 0, - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - 'cameraDevice': 1, - }), - ], - ); + verify(mockPlatform.getImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front)); }); }); group('#pickVideo', () { + setUp(() { + when(mockPlatform.getVideo( + source: anyNamed('source'), + preferredCameraDevice: anyNamed('preferredCameraDevice'), + maxDuration: anyNamed('maxDuration'))) + .thenAnswer((Invocation _) async => null); + }); + test('passes the image source argument correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.pickVideo(source: ImageSource.camera); await picker.pickVideo(source: ImageSource.gallery); - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'cameraDevice': 0, - 'maxDuration': null, - }), - isMethodCall('pickVideo', arguments: { - 'source': 1, - 'cameraDevice': 0, - 'maxDuration': null, - }), - ], - ); + verifyInOrder([ + mockPlatform.getVideo(source: ImageSource.camera), + mockPlatform.getVideo(source: ImageSource.gallery), + ]); }); test('passes the duration argument correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.pickVideo(source: ImageSource.camera); await picker.pickVideo( source: ImageSource.camera, maxDuration: const Duration(seconds: 10)); - await picker.pickVideo( - source: ImageSource.camera, - maxDuration: const Duration(minutes: 1)); - await picker.pickVideo( - source: ImageSource.camera, - maxDuration: const Duration(hours: 1)); - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': null, - 'cameraDevice': 0, - }), - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': 10, - 'cameraDevice': 0, - }), - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': 60, - 'cameraDevice': 0, - }), - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': 3600, - 'cameraDevice': 0, - }), - ], - ); + + verifyInOrder([ + mockPlatform.getVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.rear, + maxDuration: null), + mockPlatform.getVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.rear, + maxDuration: const Duration(seconds: 10)), + ]); }); - test('handles a null video path response gracefully', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) => null); + test('handles a null video file response gracefully', () async { + final ImagePicker picker = ImagePicker(); expect(await picker.pickVideo(source: ImageSource.gallery), isNull); expect(await picker.pickVideo(source: ImageSource.camera), isNull); }); test('camera position defaults to back', () async { + final ImagePicker picker = ImagePicker(); await picker.pickVideo(source: ImageSource.camera); - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'cameraDevice': 0, - 'maxDuration': null, - }), - ], - ); + verify(mockPlatform.getVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.rear)); }); test('camera position can set to front', () async { + final ImagePicker picker = ImagePicker(); await picker.pickVideo( source: ImageSource.camera, preferredCameraDevice: CameraDevice.front); - expect( - log, - [ - isMethodCall('pickVideo', arguments: { - 'source': 0, - 'maxDuration': null, - 'cameraDevice': 1, - }), - ], - ); + verify(mockPlatform.getVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front)); }); }); group('#retrieveLostData', () { test('retrieveLostData get success response', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return { - 'type': 'image', - 'path': '/example/path', - }; - }); + final ImagePicker picker = ImagePicker(); + final XFile lostFile = XFile('/example/path'); + when(mockPlatform.getLostData()).thenAnswer((Invocation _) async => + LostDataResponse( + file: lostFile, + files: [lostFile], + type: RetrieveType.image)); + final LostDataResponse response = await picker.retrieveLostData(); + expect(response.type, RetrieveType.image); expect(response.file!.path, '/example/path'); }); test('retrieveLostData should successfully retrieve multiple files', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return { - 'type': 'image', - 'path': '/example/path1', - 'pathList': ['/example/path0', '/example/path1'], - }; - }); + final ImagePicker picker = ImagePicker(); + final List lostFiles = [ + XFile('/example/path0'), + XFile('/example/path1'), + ]; + when(mockPlatform.getLostData()).thenAnswer((Invocation _) async => + LostDataResponse( + file: lostFiles.last, + files: lostFiles, + type: RetrieveType.image)); final LostDataResponse response = await picker.retrieveLostData(); + expect(response.type, RetrieveType.image); expect(response.file, isNotNull); expect(response.file!.path, '/example/path1'); @@ -334,51 +261,34 @@ void main() { }); test('retrieveLostData get error response', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return { - 'type': 'video', - 'errorCode': 'test_error_code', - 'errorMessage': 'test_error_message', - }; - }); + final ImagePicker picker = ImagePicker(); + when(mockPlatform.getLostData()).thenAnswer((Invocation _) async => + LostDataResponse( + exception: PlatformException( + code: 'test_error_code', message: 'test_error_message'), + type: RetrieveType.video)); + final LostDataResponse response = await picker.retrieveLostData(); + expect(response.type, RetrieveType.video); expect(response.exception!.code, 'test_error_code'); expect(response.exception!.message, 'test_error_message'); }); - - test('retrieveLostData get null response', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return null; - }); - expect((await picker.retrieveLostData()).isEmpty, true); - }); - - test('retrieveLostData get both path and error should throw', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return { - 'type': 'video', - 'errorCode': 'test_error_code', - 'errorMessage': 'test_error_message', - 'path': '/example/path', - }; - }); - expect(picker.retrieveLostData(), throwsAssertionError); - }); }); }); group('#Multi images', () { setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - return []; - }); - log.clear(); + when(mockPlatform.getMultiImage( + maxWidth: anyNamed('maxWidth'), + maxHeight: anyNamed('maxHeight'), + imageQuality: anyNamed('imageQuality'))) + .thenAnswer((Invocation _) async => null); }); group('#pickMultiImage', () { test('passes the width and height arguments correctly', () async { + final ImagePicker picker = ImagePicker(); await picker.pickMultiImage(); await picker.pickMultiImage( maxWidth: 10.0, @@ -401,62 +311,39 @@ void main() { await picker.pickMultiImage( maxWidth: 10.0, maxHeight: 20.0, imageQuality: 70); - expect( - log, - [ - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': null, - 'maxHeight': null, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': null, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': null, - 'imageQuality': 70, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': null, - 'maxHeight': 10.0, - 'imageQuality': 70, - }), - isMethodCall('pickMultiImage', arguments: { - 'maxWidth': 10.0, - 'maxHeight': 20.0, - 'imageQuality': 70, - }), - ], - ); + verifyInOrder([ + mockPlatform.getMultiImage( + maxWidth: null, maxHeight: null, imageQuality: null), + mockPlatform.getMultiImage( + maxWidth: 10.0, maxHeight: null, imageQuality: null), + mockPlatform.getMultiImage( + maxWidth: null, maxHeight: 10.0, imageQuality: null), + mockPlatform.getMultiImage( + maxWidth: 10.0, maxHeight: 20.0, imageQuality: null), + mockPlatform.getMultiImage( + maxWidth: 10.0, maxHeight: null, imageQuality: 70), + mockPlatform.getMultiImage( + maxWidth: null, maxHeight: 10.0, imageQuality: 70), + mockPlatform.getMultiImage( + maxWidth: 10.0, maxHeight: 20.0, imageQuality: 70), + ]); }); test('does not accept a negative width or height argument', () { + final ImagePicker picker = ImagePicker(); expect( - picker.pickMultiImage(maxWidth: -1.0), + () => picker.pickMultiImage(maxWidth: -1.0), throwsArgumentError, ); expect( - picker.pickMultiImage(maxHeight: -1.0), + () => picker.pickMultiImage(maxHeight: -1.0), throwsArgumentError, ); }); - test('handles a null image path response gracefully', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) => null); + test('handles a null image file response gracefully', () async { + final ImagePicker picker = ImagePicker(); expect(await picker.pickMultiImage(), isNull); expect(await picker.pickMultiImage(), isNull); @@ -465,7 +352,3 @@ void main() { }); }); } - -class MockPlatform extends Mock - with MockPlatformInterfaceMixin - implements ImagePickerPlatform {} diff --git a/packages/image_picker/image_picker/test/image_picker_test.mocks.dart b/packages/image_picker/image_picker/test/image_picker_test.mocks.dart new file mode 100644 index 000000000000..641a104a33c5 --- /dev/null +++ b/packages/image_picker/image_picker/test/image_picker_test.mocks.dart @@ -0,0 +1,136 @@ +// Mocks generated by Mockito 5.1.0 from annotations +// in image_picker/test/image_picker_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i4; + +import 'package:cross_file/cross_file.dart' as _i5; +import 'package:image_picker_platform_interface/src/platform_interface/image_picker_platform.dart' + as _i3; +import 'package:image_picker_platform_interface/src/types/types.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; + +// 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: 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 + +class _FakeLostData_0 extends _i1.Fake implements _i2.LostData {} + +class _FakeLostDataResponse_1 extends _i1.Fake implements _i2.LostDataResponse { +} + +/// A class which mocks [ImagePickerPlatform]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockImagePickerPlatform extends _i1.Mock + implements _i3.ImagePickerPlatform { + MockImagePickerPlatform() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Future<_i2.PickedFile?> pickImage( + {_i2.ImageSource? source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + _i2.CameraDevice? preferredCameraDevice = _i2.CameraDevice.rear}) => + (super.noSuchMethod( + Invocation.method(#pickImage, [], { + #source: source, + #maxWidth: maxWidth, + #maxHeight: maxHeight, + #imageQuality: imageQuality, + #preferredCameraDevice: preferredCameraDevice + }), + returnValue: Future<_i2.PickedFile?>.value()) + as _i4.Future<_i2.PickedFile?>); + @override + _i4.Future?> pickMultiImage( + {double? maxWidth, double? maxHeight, int? imageQuality}) => + (super.noSuchMethod( + Invocation.method(#pickMultiImage, [], { + #maxWidth: maxWidth, + #maxHeight: maxHeight, + #imageQuality: imageQuality + }), + returnValue: Future?>.value()) + as _i4.Future?>); + @override + _i4.Future<_i2.PickedFile?> pickVideo( + {_i2.ImageSource? source, + _i2.CameraDevice? preferredCameraDevice = _i2.CameraDevice.rear, + Duration? maxDuration}) => + (super.noSuchMethod( + Invocation.method(#pickVideo, [], { + #source: source, + #preferredCameraDevice: preferredCameraDevice, + #maxDuration: maxDuration + }), + returnValue: Future<_i2.PickedFile?>.value()) + as _i4.Future<_i2.PickedFile?>); + @override + _i4.Future<_i2.LostData> retrieveLostData() => + (super.noSuchMethod(Invocation.method(#retrieveLostData, []), + returnValue: Future<_i2.LostData>.value(_FakeLostData_0())) + as _i4.Future<_i2.LostData>); + @override + _i4.Future<_i5.XFile?> getImage( + {_i2.ImageSource? source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + _i2.CameraDevice? preferredCameraDevice = _i2.CameraDevice.rear}) => + (super.noSuchMethod( + Invocation.method(#getImage, [], { + #source: source, + #maxWidth: maxWidth, + #maxHeight: maxHeight, + #imageQuality: imageQuality, + #preferredCameraDevice: preferredCameraDevice + }), + returnValue: Future<_i5.XFile?>.value()) as _i4.Future<_i5.XFile?>); + @override + _i4.Future?> getMultiImage( + {double? maxWidth, double? maxHeight, int? imageQuality}) => + (super.noSuchMethod( + Invocation.method(#getMultiImage, [], { + #maxWidth: maxWidth, + #maxHeight: maxHeight, + #imageQuality: imageQuality + }), + returnValue: Future?>.value()) + as _i4.Future?>); + @override + _i4.Future<_i5.XFile?> getVideo( + {_i2.ImageSource? source, + _i2.CameraDevice? preferredCameraDevice = _i2.CameraDevice.rear, + Duration? maxDuration}) => + (super.noSuchMethod( + Invocation.method(#getVideo, [], { + #source: source, + #preferredCameraDevice: preferredCameraDevice, + #maxDuration: maxDuration + }), + returnValue: Future<_i5.XFile?>.value()) as _i4.Future<_i5.XFile?>); + @override + _i4.Future<_i2.LostDataResponse> getLostData() => + (super.noSuchMethod(Invocation.method(#getLostData, []), + returnValue: + Future<_i2.LostDataResponse>.value(_FakeLostDataResponse_1())) + as _i4.Future<_i2.LostDataResponse>); + @override + _i4.Future<_i5.XFile?> getImageFromSource( + {_i2.ImageSource? source, + _i2.ImagePickerOptions? options = const _i2.ImagePickerOptions()}) => + (super.noSuchMethod( + Invocation.method( + #getImageFromSource, [], {#source: source, #options: options}), + returnValue: Future<_i5.XFile?>.value()) as _i4.Future<_i5.XFile?>); +} From 3d995d484ce3436ebbb891640bcd48771abeb00b Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 12 May 2022 20:29:07 -0400 Subject: [PATCH 300/844] [google_sign_in] Switch unit tests to mock platform implementation (#5703) --- .../google_sign_in/CHANGELOG.md | 3 +- .../google_sign_in/pubspec.yaml | 2 + .../test/google_sign_in_test.dart | 406 +++++++----------- .../test/google_sign_in_test.mocks.dart | 100 +++++ 4 files changed, 260 insertions(+), 251 deletions(-) create mode 100644 packages/google_sign_in/google_sign_in/test/google_sign_in_test.mocks.dart diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 8416e81f2412..9edadf7b0469 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,6 +1,7 @@ ## NEXT -* Fixes tests to recognize new default `forceCodeForRefreshToken` request attribute. +* Updates tests to use a mock platform instead of relying on default + method channel implementation internals. ## 5.3.1 diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index e58b27af08b7..d1c13c6a8ec4 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -29,6 +29,7 @@ dependencies: google_sign_in_web: ^0.10.0 dev_dependencies: + build_runner: ^2.1.10 flutter_driver: sdk: flutter flutter_test: @@ -36,6 +37,7 @@ dev_dependencies: http: ^0.13.0 integration_test: sdk: flutter + mockito: ^5.1.0 # The example deliberately includes limited-use secrets. false_secrets: diff --git a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart index 3b0654637d68..61acfd81bf09 100644 --- a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart @@ -4,229 +4,176 @@ import 'dart:async'; -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_sign_in/google_sign_in.dart'; -import 'package:google_sign_in/testing.dart'; import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'google_sign_in_test.mocks.dart'; + +@GenerateMocks([GoogleSignInPlatform]) void main() { - TestWidgetsFlutterBinding.ensureInitialized(); + late MockGoogleSignInPlatform mockPlatform; group('GoogleSignIn', () { - const MethodChannel channel = MethodChannel( - 'plugins.flutter.io/google_sign_in', - ); - - const Map kUserData = { - 'email': 'john.doe@gmail.com', - 'id': '8162538176523816253123', - 'photoUrl': 'https://lh5.googleusercontent.com/photo.jpg', - 'displayName': 'John Doe', - 'serverAuthCode': '789' - }; - - const Map kDefaultResponses = { - 'init': null, - 'signInSilently': kUserData, - 'signIn': kUserData, - 'signOut': null, - 'disconnect': null, - 'isSignedIn': true, - 'requestScopes': true, - 'getTokens': { - 'idToken': '123', - 'accessToken': '456', - 'serverAuthCode': '789', - }, - }; - - final List log = []; - late Map responses; - late GoogleSignIn googleSignIn; + final GoogleSignInUserData kDefaultUser = GoogleSignInUserData( + email: 'john.doe@gmail.com', + id: '8162538176523816253123', + photoUrl: 'https://lh5.googleusercontent.com/photo.jpg', + displayName: 'John Doe', + serverAuthCode: '789'); setUp(() { - responses = Map.from(kDefaultResponses); - channel.setMockMethodCallHandler((MethodCall methodCall) { - log.add(methodCall); - final dynamic response = responses[methodCall.method]; - if (response != null && response is Exception) { - return Future.error('$response'); - } - return Future.value(response); - }); - googleSignIn = GoogleSignIn(); - log.clear(); + mockPlatform = MockGoogleSignInPlatform(); + when(mockPlatform.isMock).thenReturn(true); + when(mockPlatform.signInSilently()) + .thenAnswer((Invocation _) async => kDefaultUser); + when(mockPlatform.signIn()) + .thenAnswer((Invocation _) async => kDefaultUser); + + GoogleSignInPlatform.instance = mockPlatform; }); test('signInSilently', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); + await googleSignIn.signInSilently(); + expect(googleSignIn.currentUser, isNotNull); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signInSilently', arguments: null), - ], - ); + _verifyInit(mockPlatform); + verify(mockPlatform.signInSilently()); }); test('signIn', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); + await googleSignIn.signIn(); + expect(googleSignIn.currentUser, isNotNull); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signIn', arguments: null), - ], - ); + _verifyInit(mockPlatform); + verify(mockPlatform.signIn()); }); test('signIn prioritize clientId parameter when available', () async { const String fakeClientId = 'fakeClientId'; - googleSignIn = GoogleSignIn(clientId: fakeClientId); + final GoogleSignIn googleSignIn = GoogleSignIn(clientId: fakeClientId); + await googleSignIn.signIn(); - expect(googleSignIn.currentUser, isNotNull); - expect( - log, - [ - isMethodCall('init', arguments: { - 'signInOption': 'SignInOption.standard', - 'scopes': [], - 'hostedDomain': null, - 'clientId': fakeClientId, - 'forceCodeForRefreshToken': false, - }), - isMethodCall('signIn', arguments: null), - ], - ); + + _verifyInit(mockPlatform, clientId: fakeClientId); + verify(mockPlatform.signIn()); }); test('signOut', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); + await googleSignIn.signOut(); - expect(googleSignIn.currentUser, isNull); - expect(log, [ - _isSignInMethodCall(), - isMethodCall('signOut', arguments: null), - ]); + + _verifyInit(mockPlatform); + verify(mockPlatform.signOut()); }); test('disconnect; null response', () async { - await googleSignIn.disconnect(); - expect(googleSignIn.currentUser, isNull); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('disconnect', arguments: null), - ], - ); - }); + final GoogleSignIn googleSignIn = GoogleSignIn(); - test('disconnect; empty response as on iOS', () async { - responses['disconnect'] = {}; await googleSignIn.disconnect(); + expect(googleSignIn.currentUser, isNull); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('disconnect', arguments: null), - ], - ); + _verifyInit(mockPlatform); + verify(mockPlatform.disconnect()); }); test('isSignedIn', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); + when(mockPlatform.isSignedIn()).thenAnswer((Invocation _) async => true); + final bool result = await googleSignIn.isSignedIn(); + expect(result, isTrue); - expect(log, [ - _isSignInMethodCall(), - isMethodCall('isSignedIn', arguments: null), - ]); + _verifyInit(mockPlatform); + verify(mockPlatform.isSignedIn()); }); test('signIn works even if a previous call throws error in other zone', () async { - responses['signInSilently'] = Exception('Not a user'); + final GoogleSignIn googleSignIn = GoogleSignIn(); + + when(mockPlatform.signInSilently()).thenThrow(Exception('Not a user')); await runZonedGuarded(() async { expect(await googleSignIn.signInSilently(), isNull); }, (Object e, StackTrace st) {}); expect(await googleSignIn.signIn(), isNotNull); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signInSilently', arguments: null), - isMethodCall('signIn', arguments: null), - ], - ); + _verifyInit(mockPlatform); + verify(mockPlatform.signInSilently()); + verify(mockPlatform.signIn()); }); test('concurrent calls of the same method trigger sign in once', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); final List> futures = >[ googleSignIn.signInSilently(), googleSignIn.signInSilently(), ]; + expect(futures.first, isNot(futures.last), reason: 'Must return new Future'); + final List users = await Future.wait(futures); + expect(googleSignIn.currentUser, isNotNull); expect(users, [ googleSignIn.currentUser, googleSignIn.currentUser ]); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signInSilently', arguments: null), - ], - ); + _verifyInit(mockPlatform); + verify(mockPlatform.signInSilently()).called(1); }); test('can sign in after previously failed attempt', () async { - responses['signInSilently'] = Exception('Not a user'); + final GoogleSignIn googleSignIn = GoogleSignIn(); + when(mockPlatform.signInSilently()).thenThrow(Exception('Not a user')); + expect(await googleSignIn.signInSilently(), isNull); expect(await googleSignIn.signIn(), isNotNull); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signInSilently', arguments: null), - isMethodCall('signIn', arguments: null), - ], - ); + + _verifyInit(mockPlatform); + verify(mockPlatform.signInSilently()); + verify(mockPlatform.signIn()); }); test('concurrent calls of different signIn methods', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); final List> futures = >[ googleSignIn.signInSilently(), googleSignIn.signIn(), ]; expect(futures.first, isNot(futures.last)); + final List users = await Future.wait(futures); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signInSilently', arguments: null), - ], - ); + expect(users.first, users.last, reason: 'Must return the same user'); expect(googleSignIn.currentUser, users.last); + _verifyInit(mockPlatform); + verify(mockPlatform.signInSilently()); + verifyNever(mockPlatform.signIn()); }); test('can sign in after aborted flow', () async { - responses['signIn'] = null; + final GoogleSignIn googleSignIn = GoogleSignIn(); + + when(mockPlatform.signIn()).thenAnswer((Invocation _) async => null); expect(await googleSignIn.signIn(), isNull); - responses['signIn'] = kUserData; + + when(mockPlatform.signIn()) + .thenAnswer((Invocation _) async => kDefaultUser); expect(await googleSignIn.signIn(), isNotNull); }); test('signOut/disconnect methods always trigger native calls', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); final List> futures = >[ googleSignIn.signOut(), @@ -234,20 +181,16 @@ void main() { googleSignIn.disconnect(), googleSignIn.disconnect(), ]; + await Future.wait(futures); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signOut', arguments: null), - isMethodCall('signOut', arguments: null), - isMethodCall('disconnect', arguments: null), - isMethodCall('disconnect', arguments: null), - ], - ); + + _verifyInit(mockPlatform); + verify(mockPlatform.signOut()).called(2); + verify(mockPlatform.disconnect()).called(2); }); test('queue of many concurrent calls', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); final List> futures = >[ googleSignIn.signInSilently(), @@ -255,183 +198,146 @@ void main() { googleSignIn.signIn(), googleSignIn.disconnect(), ]; + await Future.wait(futures); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signInSilently', arguments: null), - isMethodCall('signOut', arguments: null), - isMethodCall('signIn', arguments: null), - isMethodCall('disconnect', arguments: null), - ], - ); + + _verifyInit(mockPlatform); + verifyInOrder([ + mockPlatform.signInSilently(), + mockPlatform.signOut(), + mockPlatform.signIn(), + mockPlatform.disconnect(), + ]); }); test('signInSilently suppresses errors by default', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) { - throw 'I am an error'; - }); + final GoogleSignIn googleSignIn = GoogleSignIn(); + when(mockPlatform.signInSilently()).thenThrow(Exception('I am an error')); expect(await googleSignIn.signInSilently(), isNull); // should not throw }); - test('signInSilently forwards errors', () async { - channel.setMockMethodCallHandler((MethodCall methodCall) { - throw 'I am an error'; - }); + test('signInSilently forwards exceptions', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); + when(mockPlatform.signInSilently()).thenThrow(Exception('I am an error')); expect(googleSignIn.signInSilently(suppressErrors: false), - throwsA(isInstanceOf())); + throwsA(isInstanceOf())); }); test('signInSilently allows re-authentication to be requested', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); await googleSignIn.signInSilently(); expect(googleSignIn.currentUser, isNotNull); await googleSignIn.signInSilently(reAuthenticate: true); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signInSilently', arguments: null), - isMethodCall('signInSilently', arguments: null), - ], - ); + _verifyInit(mockPlatform); + verify(mockPlatform.signInSilently()).called(2); }); test('can sign in after init failed before', () async { - int initCount = 0; - channel.setMockMethodCallHandler((MethodCall methodCall) { - if (methodCall.method == 'init') { - initCount++; - if (initCount == 1) { - throw 'First init fails'; - } - } - return Future.value(responses[methodCall.method]); - }); - expect(googleSignIn.signIn(), throwsA(isInstanceOf())); + final GoogleSignIn googleSignIn = GoogleSignIn(); + + when(mockPlatform.init()).thenThrow(Exception('First init fails')); + expect(googleSignIn.signIn(), throwsA(isInstanceOf())); + + when(mockPlatform.init()).thenAnswer((Invocation _) async {}); expect(await googleSignIn.signIn(), isNotNull); }); test('created with standard factory uses correct options', () async { - googleSignIn = GoogleSignIn.standard(); + final GoogleSignIn googleSignIn = GoogleSignIn.standard(); await googleSignIn.signInSilently(); expect(googleSignIn.currentUser, isNotNull); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signInSilently', arguments: null), - ], - ); + _verifyInit(mockPlatform); + verify(mockPlatform.signInSilently()); }); test('created with defaultGamesSignIn factory uses correct options', () async { - googleSignIn = GoogleSignIn.games(); + final GoogleSignIn googleSignIn = GoogleSignIn.games(); await googleSignIn.signInSilently(); expect(googleSignIn.currentUser, isNotNull); - expect( - log, - [ - _isSignInMethodCall(signInOption: 'SignInOption.games'), - isMethodCall('signInSilently', arguments: null), - ], - ); + _verifyInit(mockPlatform, signInOption: SignInOption.games); + verify(mockPlatform.signInSilently()); }); test('authentication', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); + when(mockPlatform.getTokens( + email: anyNamed('email'), + shouldRecoverAuth: anyNamed('shouldRecoverAuth'))) + .thenAnswer((Invocation _) async => GoogleSignInTokenData( + idToken: '123', + accessToken: '456', + serverAuthCode: '789', + )); + await googleSignIn.signIn(); - log.clear(); final GoogleSignInAccount user = googleSignIn.currentUser!; final GoogleSignInAuthentication auth = await user.authentication; expect(auth.accessToken, '456'); expect(auth.idToken, '123'); - expect( - log, - [ - isMethodCall('getTokens', arguments: { - 'email': 'john.doe@gmail.com', - 'shouldRecoverAuth': true, - }), - ], - ); + verify(mockPlatform.getTokens( + email: 'john.doe@gmail.com', shouldRecoverAuth: true)); }); test('requestScopes returns true once new scope is granted', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); + when(mockPlatform.requestScopes(any)) + .thenAnswer((Invocation _) async => true); + await googleSignIn.signIn(); final bool result = await googleSignIn.requestScopes(['testScope']); expect(result, isTrue); - expect( - log, - [ - _isSignInMethodCall(), - isMethodCall('signIn', arguments: null), - isMethodCall('requestScopes', arguments: { - 'scopes': ['testScope'], - }), - ], - ); - }); - }); - - group('GoogleSignIn with fake backend', () { - const FakeUser kUserData = FakeUser( - id: '8162538176523816253123', - displayName: 'John Doe', - email: 'john.doe@gmail.com', - photoUrl: 'https://lh5.googleusercontent.com/photo.jpg', - serverAuthCode: '789'); - - late GoogleSignIn googleSignIn; - - setUp(() { - final MethodChannelGoogleSignIn platformInstance = - GoogleSignInPlatform.instance as MethodChannelGoogleSignIn; - platformInstance.channel.setMockMethodCallHandler( - (FakeSignInBackend()..user = kUserData).handleMethodCall); - googleSignIn = GoogleSignIn(); + _verifyInit(mockPlatform); + verify(mockPlatform.signIn()); + verify(mockPlatform.requestScopes(['testScope'])); }); test('user starts as null', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); expect(googleSignIn.currentUser, isNull); }); test('can sign in and sign out', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); await googleSignIn.signIn(); final GoogleSignInAccount user = googleSignIn.currentUser!; - expect(user.displayName, equals(kUserData.displayName)); - expect(user.email, equals(kUserData.email)); - expect(user.id, equals(kUserData.id)); - expect(user.photoUrl, equals(kUserData.photoUrl)); - expect(user.serverAuthCode, equals(kUserData.serverAuthCode)); + expect(user.displayName, equals(kDefaultUser.displayName)); + expect(user.email, equals(kDefaultUser.email)); + expect(user.id, equals(kDefaultUser.id)); + expect(user.photoUrl, equals(kDefaultUser.photoUrl)); + expect(user.serverAuthCode, equals(kDefaultUser.serverAuthCode)); await googleSignIn.disconnect(); expect(googleSignIn.currentUser, isNull); }); test('disconnect when signout already succeeds', () async { + final GoogleSignIn googleSignIn = GoogleSignIn(); await googleSignIn.disconnect(); expect(googleSignIn.currentUser, isNull); }); }); } -Matcher _isSignInMethodCall({String signInOption = 'SignInOption.standard'}) { - return isMethodCall('init', arguments: { - 'signInOption': signInOption, - 'scopes': [], - 'hostedDomain': null, - 'clientId': null, - 'forceCodeForRefreshToken': false, - }); +void _verifyInit( + MockGoogleSignInPlatform mockSignIn, { + SignInOption signInOption = SignInOption.standard, + String? clientId, +}) { + verify(mockSignIn.init( + signInOption: signInOption, + scopes: [], + hostedDomain: null, + clientId: clientId, + )); } diff --git a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.mocks.dart b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.mocks.dart new file mode 100644 index 000000000000..4e669628391c --- /dev/null +++ b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.mocks.dart @@ -0,0 +1,100 @@ +// Mocks generated by Mockito 5.1.0 from annotations +// in google_sign_in/test/google_sign_in_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i4; + +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart' + as _i3; +import 'package:google_sign_in_platform_interface/src/types.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; + +// 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: 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 + +class _FakeGoogleSignInTokenData_0 extends _i1.Fake + implements _i2.GoogleSignInTokenData {} + +/// A class which mocks [GoogleSignInPlatform]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockGoogleSignInPlatform extends _i1.Mock + implements _i3.GoogleSignInPlatform { + MockGoogleSignInPlatform() { + _i1.throwOnMissingStub(this); + } + + @override + bool get isMock => + (super.noSuchMethod(Invocation.getter(#isMock), returnValue: false) + as bool); + @override + _i4.Future init( + {List? scopes = const [], + _i2.SignInOption? signInOption = _i2.SignInOption.standard, + String? hostedDomain, + String? clientId}) => + (super.noSuchMethod( + Invocation.method(#init, [], { + #scopes: scopes, + #signInOption: signInOption, + #hostedDomain: hostedDomain, + #clientId: clientId + }), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future initWithParams(_i2.SignInInitParameters? params) => + (super.noSuchMethod(Invocation.method(#initWithParams, [params]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future<_i2.GoogleSignInUserData?> signInSilently() => + (super.noSuchMethod(Invocation.method(#signInSilently, []), + returnValue: Future<_i2.GoogleSignInUserData?>.value()) + as _i4.Future<_i2.GoogleSignInUserData?>); + @override + _i4.Future<_i2.GoogleSignInUserData?> signIn() => + (super.noSuchMethod(Invocation.method(#signIn, []), + returnValue: Future<_i2.GoogleSignInUserData?>.value()) + as _i4.Future<_i2.GoogleSignInUserData?>); + @override + _i4.Future<_i2.GoogleSignInTokenData> getTokens( + {String? email, bool? shouldRecoverAuth}) => + (super.noSuchMethod( + Invocation.method(#getTokens, [], + {#email: email, #shouldRecoverAuth: shouldRecoverAuth}), + returnValue: Future<_i2.GoogleSignInTokenData>.value( + _FakeGoogleSignInTokenData_0())) + as _i4.Future<_i2.GoogleSignInTokenData>); + @override + _i4.Future signOut() => + (super.noSuchMethod(Invocation.method(#signOut, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future disconnect() => + (super.noSuchMethod(Invocation.method(#disconnect, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future isSignedIn() => + (super.noSuchMethod(Invocation.method(#isSignedIn, []), + returnValue: Future.value(false)) as _i4.Future); + @override + _i4.Future clearAuthCache({String? token}) => (super.noSuchMethod( + Invocation.method(#clearAuthCache, [], {#token: token}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future requestScopes(List? scopes) => + (super.noSuchMethod(Invocation.method(#requestScopes, [scopes]), + returnValue: Future.value(false)) as _i4.Future); +} From 885be9a9c28d57583c1c0cbb7ba7d1446b20e71f Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Thu, 12 May 2022 18:34:11 -0700 Subject: [PATCH 301/844] [ios_platform_images] Ignore ImageProvider.load deprecation (#5707) --- packages/ios_platform_images/CHANGELOG.md | 4 ++++ packages/ios_platform_images/lib/ios_platform_images.dart | 2 +- packages/ios_platform_images/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index d432bdb7ee8e..ee8e96132fea 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.0+8 + +* Ignores the warning for the upcoming deprecation of `ImageProvider.load` in the correct line. + ## 0.2.0+7 * Ignores the warning for the upcoming deprecation of `ImageProvider.load`. diff --git a/packages/ios_platform_images/lib/ios_platform_images.dart b/packages/ios_platform_images/lib/ios_platform_images.dart index 6a85ea0d189b..1c5b4b817f40 100644 --- a/packages/ios_platform_images/lib/ios_platform_images.dart +++ b/packages/ios_platform_images/lib/ios_platform_images.dart @@ -62,10 +62,10 @@ class _FutureMemoryImage extends ImageProvider<_FutureMemoryImage> { return SynchronousFuture<_FutureMemoryImage>(this); } + // ignore:deprecated_member_use /// See [ImageProvider.load]. // TODO(jmagman): Implement the new API once it lands, https://github.com/flutter/flutter/issues/103556 @override - // ignore:deprecated_member_use ImageStreamCompleter load(_FutureMemoryImage key, DecoderCallback decode) { return _FutureImageStreamCompleter( codec: _loadAsync(key, decode), diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index ddc02e6235db..4ff67ee137b4 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -2,7 +2,7 @@ name: ios_platform_images description: A plugin to share images between Flutter and iOS in add-to-app setups. repository: https://github.com/flutter/plugins/tree/main/packages/ios_platform_images issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+ios_platform_images%22 -version: 0.2.0+7 +version: 0.2.0+8 environment: sdk: ">=2.14.0 <3.0.0" From a2a29355cc0d616280f8eb55211c595b16d0b51c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 12 May 2022 22:19:08 -0400 Subject: [PATCH 302/844] Roll Flutter from a9ac7fb03be1 to 8bec125aaf72 (38 revisions) (#5704) --- .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 e1efdd362301..ee33c592e2fb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a9ac7fb03be1d574d726e98b90743015716de7fd +8bec125aaf72667bf5840dca74b5916cb5dbe9ec From 900e212954d9691e0c9279d01e109de3626fdacb Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 13 May 2022 00:54:05 -0400 Subject: [PATCH 303/844] Roll Flutter from 8bec125aaf72 to 6bba577bf230 (17 revisions) (#5709) --- .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 ee33c592e2fb..e4d9f0fecf0a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8bec125aaf72667bf5840dca74b5916cb5dbe9ec +6bba577bf230f5387ee01d7336ba755cb4c8bc9a From 7441ca729cfad84132b9a01cc34403cb111bc7f5 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 13 May 2022 04:29:07 -0400 Subject: [PATCH 304/844] Roll Flutter from 6bba577bf230 to b3d7a691f6f9 (1 revision) (#5710) --- .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 e4d9f0fecf0a..85e95ee3914a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -6bba577bf230f5387ee01d7336ba755cb4c8bc9a +b3d7a691f6f93da38919fdb358fa03f6b8bcd1b9 From 0620072e157f7bb717682f19a72e7672685931e4 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 13 May 2022 07:09:07 -0400 Subject: [PATCH 305/844] Roll Flutter from b3d7a691f6f9 to c13bc34717a6 (2 revisions) (#5715) --- .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 85e95ee3914a..7e0b68684831 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b3d7a691f6f93da38919fdb358fa03f6b8bcd1b9 +c13bc34717a6595b86fecf590e44a7d27513340e From 1e71398a1cef2d90d40ff01b38a751c975385215 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 13 May 2022 08:24:06 -0400 Subject: [PATCH 306/844] [ci] Update the legacy analysis versions (#5699) --- .cirrus.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index c26118d1443f..eaaac8d7b5b5 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -157,11 +157,10 @@ task: depends_on: analyze env: matrix: - CHANNEL: "2.5.3" + CHANNEL: "2.10.5" CHANNEL: "2.8.1" analyze_script: - ./script/tool_runner.sh analyze --skip-if-not-supporting-flutter-version="$CHANNEL" --custom-analysis=script/configs/custom_analysis.yaml - - echo "If this test fails, the minumum Flutter version should be updated" - name: readme_excerpts env: CIRRUS_CLONE_SUBMODULES: true From 053c4bdb3812365a61e054c462c567aa75f3cf2b Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 13 May 2022 09:24:13 -0400 Subject: [PATCH 307/844] Roll Flutter from c13bc34717a6 to ac80477e8d85 (2 revisions) (#5717) --- .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 7e0b68684831..16035180091a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c13bc34717a6595b86fecf590e44a7d27513340e +ac80477e8d853f45d6075b7502917e8a5b4350a6 From debc272fb02a61832f92af717a6e0cb3ebce5c9a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 13 May 2022 14:44:11 -0400 Subject: [PATCH 308/844] Roll Flutter from ac80477e8d85 to 2b2cda15293d (2 revisions) (#5719) --- .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 16035180091a..6934eda08a73 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ac80477e8d853f45d6075b7502917e8a5b4350a6 +2b2cda15293d86d5c27d345505cde25b9efcb153 From 7dc808a3f4692073f948efd41f577c3cf8f2b7d3 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 13 May 2022 20:34:12 -0400 Subject: [PATCH 309/844] [tools] Fix `publish` flag calculation (#5694) --- script/tool/CHANGELOG.md | 1 + .../tool/lib/src/common/plugin_command.dart | 4 ++- .../tool/lib/src/publish_plugin_command.dart | 17 ++++++----- .../test/publish_plugin_command_test.dart | 29 +++++++++++++++++++ 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 9ed2a9278653..a8a8268c047f 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,6 +1,7 @@ ## NEXT - Fixes changelog validation when reverting to a `NEXT` state. +- Fixes multiplication of `--force` flag when publishing multiple packages. ## 0.8.5 diff --git a/script/tool/lib/src/common/plugin_command.dart b/script/tool/lib/src/common/plugin_command.dart index 0ec890368fb5..be9fb23e57a5 100644 --- a/script/tool/lib/src/common/plugin_command.dart +++ b/script/tool/lib/src/common/plugin_command.dart @@ -192,7 +192,9 @@ abstract class PluginCommand extends Command { /// Convenience accessor for List arguments. List getStringListArg(String key) { - return (argResults![key] as List?) ?? []; + // Clone the list so that if a caller modifies the result it won't change + // the actual arguments list for future queries. + return List.from(argResults![key] as List? ?? []); } /// If true, commands should log timing information that might be useful in diff --git a/script/tool/lib/src/publish_plugin_command.dart b/script/tool/lib/src/publish_plugin_command.dart index 05f0afd0c06f..7aa70bd4fd1c 100644 --- a/script/tool/lib/src/publish_plugin_command.dart +++ b/script/tool/lib/src/publish_plugin_command.dart @@ -121,6 +121,8 @@ class PublishPluginCommand extends PackageLoopingCommand { List _existingGitTags = []; // The remote to push tags to. late _RemoteInfo _remote; + // Flags to pass to `pub publish`. + late List _publishFlags; @override String get successSummaryMessage => 'published'; @@ -149,6 +151,11 @@ class PublishPluginCommand extends PackageLoopingCommand { _existingGitTags = (existingTagsResult.stdout as String).split('\n') ..removeWhere((String element) => element.isEmpty); + _publishFlags = [ + ...getStringListArg(_pubFlagsOption), + if (getBoolArg(_skipConfirmationFlag)) '--force', + ]; + if (getBoolArg(_dryRunFlag)) { print('=============== DRY RUN ==============='); } @@ -333,22 +340,18 @@ Safe to ignore if the package is deleted in this commit. Future _publish(RepositoryPackage package) async { print('Publishing...'); - final List publishFlags = getStringListArg(_pubFlagsOption); - print('Running `pub publish ${publishFlags.join(' ')}` in ' + print('Running `pub publish ${_publishFlags.join(' ')}` in ' '${package.directory.absolute.path}...\n'); if (getBoolArg(_dryRunFlag)) { return true; } - if (getBoolArg(_skipConfirmationFlag)) { - publishFlags.add('--force'); - } - if (publishFlags.contains('--force')) { + if (_publishFlags.contains('--force')) { _ensureValidPubCredential(); } final io.Process publish = await processRunner.start( - flutterCommand, ['pub', 'publish'] + publishFlags, + flutterCommand, ['pub', 'publish', ..._publishFlags], workingDirectory: package.directory); publish.stdout.transform(utf8.decoder).listen((String data) => print(data)); publish.stderr.transform(utf8.decoder).listen((String data) => print(data)); diff --git a/script/tool/test/publish_plugin_command_test.dart b/script/tool/test/publish_plugin_command_test.dart index d443f8ff0178..f3be3b48b1f1 100644 --- a/script/tool/test/publish_plugin_command_test.dart +++ b/script/tool/test/publish_plugin_command_test.dart @@ -224,6 +224,35 @@ void main() { plugin.path))); }); + test('--force is only added once, regardless of plugin count', () async { + _createMockCredentialFile(); + final RepositoryPackage plugin1 = + createFakePlugin('plugin_a', packagesDir, examples: []); + final RepositoryPackage plugin2 = + createFakePlugin('plugin_b', packagesDir, examples: []); + + await runCapturingPrint(commandRunner, [ + 'publish-plugin', + '--packages=plugin_a,plugin_b', + '--skip-confirmation', + '--pub-publish-flags', + '--server=bar' + ]); + + expect( + processRunner.recordedCalls, + containsAllInOrder([ + ProcessCall( + flutterCommand, + const ['pub', 'publish', '--server=bar', '--force'], + plugin1.path), + ProcessCall( + flutterCommand, + const ['pub', 'publish', '--server=bar', '--force'], + plugin2.path), + ])); + }); + test('throws if pub publish fails', () async { createFakePlugin('foo', packagesDir, examples: []); From 486071d7f9bb948e0d8cfeb0abceaf3d36e2374f Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Fri, 13 May 2022 18:24:19 -0700 Subject: [PATCH 310/844] [camera]handle iOS camera access permission (#5215) --- packages/camera/camera/CHANGELOG.md | 4 + packages/camera/camera/README.md | 25 ++++ .../ios/Runner.xcodeproj/project.pbxproj | 6 +- ...eraCaptureSessionQueueRaceConditionTests.m | 9 +- .../RunnerTests/CameraMethodChannelTests.m | 6 +- .../ios/RunnerTests/CameraPermissionTests.m | 123 ++++++++++++++++++ packages/camera/camera/example/lib/main.dart | 32 ++++- .../example/lib/readme_full_example.dart | 11 ++ .../ios/Classes/CameraPermissionUtils.h | 20 +++ .../ios/Classes/CameraPermissionUtils.m | 39 ++++++ .../camera/camera/ios/Classes/CameraPlugin.m | 63 +++++---- .../camera/ios/Classes/CameraPlugin.modulemap | 1 + .../camera/ios/Classes/CameraPlugin_Test.h | 6 + .../ios/Classes/FLTThreadSafeFlutterResult.h | 20 ++- .../ios/Classes/FLTThreadSafeFlutterResult.m | 4 + packages/camera/camera/pubspec.yaml | 2 +- 16 files changed, 330 insertions(+), 41 deletions(-) create mode 100644 packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m create mode 100644 packages/camera/camera/ios/Classes/CameraPermissionUtils.h create mode 100644 packages/camera/camera/ios/Classes/CameraPermissionUtils.m diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index cde2ca284434..8d713c60c276 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.5 + +* Adds camera access permission handling logic on iOS to fix a related crash when using the camera for the first time. + ## 0.9.4+24 * Fixes preview orientation when pausing preview with locked orientation. diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index 0bcaeaeb3b7c..6b2ed7a6b687 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -80,6 +80,20 @@ void didChangeAppLifecycleState(AppLifecycleState state) { } ``` +### Handling camera access permissions + +Permission errors may be thrown when initializing the camera controller, and you are expected to handle them properly. + +Here is a list of all permission error codes that can be thrown: + +- `CameraAccessDenied`: Thrown when user denies the camera access permission. + +- `CameraAccessDeniedWithoutPrompt`: iOS only for now. Thrown when user has previously denied the permission. iOS does not allow prompting alert dialog a second time. Users will have to go to Settings > Privacy in order to enable camera access. + +- `CameraAccessRestricted`: iOS only for now. Thrown when camera access is restricted and users cannot grant permission (parental control). + +- `cameraPermission`: Android and Web only. A legacy error code for all kinds of camera permission errors. + ### Example Here is a small example flutter app displaying a full screen camera preview. @@ -119,6 +133,17 @@ class _CameraAppState extends State { return; } setState(() {}); + }).catchError((Object e) { + if (e is CameraException) { + switch (e.code) { + case 'CameraAccessDenied': + print('User denied camera access.'); + break; + default: + print('Handle other errors.'); + break; + } + } }); } diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index 37f56d0ed52e..b5187d5dd1fa 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 46; objects = { /* Begin PBXBuildFile section */ @@ -26,6 +26,7 @@ E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */; }; E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */; }; E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */; }; + E0B0D2BB27DFF2AF00E71E4B /* CameraPermissionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */; }; E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */; }; E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */; }; E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; @@ -91,6 +92,7 @@ E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTSavePhotoDelegateTests.m; sourceTree = ""; }; E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTCamPhotoCaptureTests.m; sourceTree = ""; }; E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTCamSampleBufferTests.m; sourceTree = ""; }; + E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPermissionTests.m; sourceTree = ""; }; E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeMethodChannelTests.m; sourceTree = ""; }; E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeTextureRegistryTests.m; sourceTree = ""; }; E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = ""; }; @@ -136,6 +138,7 @@ E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */, E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */, E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */, + E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */, E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */, E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */, E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */, @@ -422,6 +425,7 @@ 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */, E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */, + E0B0D2BB27DFF2AF00E71E4B /* CameraPermissionTests.m in Sources */, E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */, E01EE4A82799F3A5008C1950 /* QueueUtilsTests.m in Sources */, ); diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m index 667a122d9375..e99ce4e89a94 100644 --- a/packages/camera/camera/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m @@ -29,10 +29,11 @@ - (void)testFixForCaptureSessionQueueNullPointerCrashDueToRaceCondition { result:^(id _Nullable result) { [disposeExpectation fulfill]; }]; - [camera handleMethodCall:createCall - result:^(id _Nullable result) { - [createExpectation fulfill]; - }]; + [camera createCameraOnSessionQueueWithCreateMethodCall:createCall + result:[[FLTThreadSafeFlutterResult alloc] + initWithResult:^(id _Nullable result) { + [createExpectation fulfill]; + }]]; [self waitForExpectationsWithTimeout:1 handler:nil]; // `captureSessionQueue` must not be nil after `create` call. Otherwise a nil // `captureSessionQueue` passed into `AVCaptureVideoDataOutput::setSampleBufferDelegate:queue:` diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraMethodChannelTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraMethodChannelTests.m index 254a33c7ee4e..62b9cda2ef7b 100644 --- a/packages/camera/camera/example/ios/RunnerTests/CameraMethodChannelTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/CameraMethodChannelTests.m @@ -17,8 +17,7 @@ @implementation CameraMethodChannelTests - (void)testCreate_ShouldCallResultOnMainThread { CameraPlugin *camera = [[CameraPlugin alloc] initWithRegistry:nil messenger:nil]; - XCTestExpectation *expectation = - [[XCTestExpectation alloc] initWithDescription:@"Result finished"]; + XCTestExpectation *expectation = [self expectationWithDescription:@"Result finished"]; // Set up mocks for initWithCameraName method id avCaptureDeviceInputMock = OCMClassMock([AVCaptureDeviceInput class]); @@ -37,7 +36,8 @@ - (void)testCreate_ShouldCallResultOnMainThread { methodCallWithMethodName:@"create" arguments:@{@"resolutionPreset" : @"medium", @"enableAudio" : @(1)}]; - [camera handleMethodCallAsync:call result:resultObject]; + [camera createCameraOnSessionQueueWithCreateMethodCall:call result:resultObject]; + [self waitForExpectationsWithTimeout:1 handler:nil]; // Verify the result NSDictionary *dictionaryResult = (NSDictionary *)resultObject.receivedResult; diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m new file mode 100644 index 000000000000..961b931b7704 --- /dev/null +++ b/packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m @@ -0,0 +1,123 @@ +// 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; +@import camera.Test; +@import AVFoundation; +@import XCTest; +#import +#import "CameraTestUtils.h" + +@interface CameraPermissionTests : XCTestCase + +@end + +@implementation CameraPermissionTests + +- (void)testRequestCameraPermission_completeWithoutErrorIfPrevoiuslyAuthorized { + XCTestExpectation *expectation = + [self expectationWithDescription: + @"Must copmlete without error if camera access was previously authorized."]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo]) + .andReturn(AVAuthorizationStatusAuthorized); + + FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { + if (error == nil) { + [expectation fulfill]; + } + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} +- (void)testRequestCameraPermission_completeWithErrorIfPreviouslyDenied { + XCTestExpectation *expectation = + [self expectationWithDescription: + @"Must complete with error if camera access was previously denied."]; + FlutterError *expectedError = + [FlutterError errorWithCode:@"CameraAccessDeniedWithoutPrompt" + message:@"User has previously denied the camera access request. Go to " + @"Settings to enable camera access." + details:nil]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo]) + .andReturn(AVAuthorizationStatusDenied); + FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { + if ([error isEqual:expectedError]) { + [expectation fulfill]; + } + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testRequestCameraPermission_completeWithErrorIfRestricted { + XCTestExpectation *expectation = + [self expectationWithDescription:@"Must complete with error if camera access is restricted."]; + FlutterError *expectedError = [FlutterError errorWithCode:@"CameraAccessRestricted" + message:@"Camera access is restricted. " + details:nil]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo]) + .andReturn(AVAuthorizationStatusRestricted); + + FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { + if ([error isEqual:expectedError]) { + [expectation fulfill]; + } + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testRequestCameraPermission_completeWithoutErrorIfUserGrantAccess { + XCTestExpectation *grantedExpectation = [self + expectationWithDescription:@"Must complete without error if user choose to grant access"]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo]) + .andReturn(AVAuthorizationStatusNotDetermined); + // Mimic user choosing "allow" in permission dialog. + OCMStub([mockDevice requestAccessForMediaType:AVMediaTypeVideo + completionHandler:[OCMArg checkWithBlock:^BOOL(void (^block)(BOOL)) { + block(YES); + return YES; + }]]); + + FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { + if (error == nil) { + [grantedExpectation fulfill]; + } + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testRequestCameraPermission_completeWithErrorIfUserDenyAccess { + XCTestExpectation *expectation = + [self expectationWithDescription:@"Must complete with error if user choose to deny access"]; + FlutterError *expectedError = + [FlutterError errorWithCode:@"CameraAccessDenied" + message:@"User denied the camera access request." + details:nil]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeVideo]) + .andReturn(AVAuthorizationStatusNotDetermined); + + // Mimic user choosing "deny" in permission dialog. + OCMStub([mockDevice requestAccessForMediaType:AVMediaTypeVideo + completionHandler:[OCMArg checkWithBlock:^BOOL(void (^block)(BOOL)) { + block(NO); + return YES; + }]]); + FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { + if ([error isEqual:expectedError]) { + [expectation fulfill]; + } + }); + + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +@end diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index 10a8a6f75e16..34942ba5aa77 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -633,8 +633,15 @@ class _CameraExampleHomeState extends State } Future onNewCameraSelected(CameraDescription cameraDescription) async { - if (controller != null) { - await controller!.dispose(); + final CameraController? oldController = controller; + if (oldController != null) { + // `controller` needs to be set to null before getting disposed, + // to avoid a race condition when we use the controller that is being + // disposed. This happens when camera permission dialog shows up, + // which triggers `didChangeAppLifecycleState`, which disposes and + // re-creates the controller. + controller = null; + await oldController.dispose(); } final CameraController cameraController = CameraController( @@ -678,7 +685,26 @@ class _CameraExampleHomeState extends State .then((double value) => _minAvailableZoom = value), ]); } on CameraException catch (e) { - _showCameraException(e); + switch (e.code) { + case 'CameraAccessDenied': + showInSnackBar('You have denied camera access.'); + break; + case 'CameraAccessDeniedWithoutPrompt': + // iOS only + showInSnackBar('Please go to Settings app to enable camera access.'); + break; + case 'CameraAccessRestricted': + // iOS only + showInSnackBar('Camera access is restricted.'); + break; + case 'cameraPermission': + // Android & web only + showInSnackBar('Unknown permission error.'); + break; + default: + _showCameraException(e); + break; + } } if (mounted) { diff --git a/packages/camera/camera/example/lib/readme_full_example.dart b/packages/camera/camera/example/lib/readme_full_example.dart index a310fd9daeb0..a3c232ec44f7 100644 --- a/packages/camera/camera/example/lib/readme_full_example.dart +++ b/packages/camera/camera/example/lib/readme_full_example.dart @@ -36,6 +36,17 @@ class _CameraAppState extends State { return; } setState(() {}); + }).catchError((Object e) { + if (e is CameraException) { + switch (e.code) { + case 'CameraAccessDenied': + print('User denied camera access.'); + break; + default: + print('Handle other errors.'); + break; + } + } }); } diff --git a/packages/camera/camera/ios/Classes/CameraPermissionUtils.h b/packages/camera/camera/ios/Classes/CameraPermissionUtils.h new file mode 100644 index 000000000000..80f55db7be32 --- /dev/null +++ b/packages/camera/camera/ios/Classes/CameraPermissionUtils.h @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import Foundation; +#import + +typedef void (^FLTCameraPermissionRequestCompletionHandler)(FlutterError *); + +/// Requests camera access permission. +/// +/// If it is the first time requesting camera access, a permission dialog will show up on the +/// screen. Otherwise AVFoundation simply returns the user's previous choice, and in this case the +/// user will have to update the choice in Settings app. +/// +/// @param handler if access permission is (or was previously) granted, completion handler will be +/// called without error; Otherwise completion handler will be called with error. Handler can be +/// called on an arbitrary dispatch queue. +extern void FLTRequestCameraPermissionWithCompletionHandler( + FLTCameraPermissionRequestCompletionHandler handler); diff --git a/packages/camera/camera/ios/Classes/CameraPermissionUtils.m b/packages/camera/camera/ios/Classes/CameraPermissionUtils.m new file mode 100644 index 000000000000..6318338ea6a2 --- /dev/null +++ b/packages/camera/camera/ios/Classes/CameraPermissionUtils.m @@ -0,0 +1,39 @@ +// 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 AVFoundation; +#import "CameraPermissionUtils.h" + +void FLTRequestCameraPermissionWithCompletionHandler( + FLTCameraPermissionRequestCompletionHandler handler) { + switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) { + case AVAuthorizationStatusAuthorized: + handler(nil); + break; + case AVAuthorizationStatusDenied: + handler([FlutterError errorWithCode:@"CameraAccessDeniedWithoutPrompt" + message:@"User has previously denied the camera access request. " + @"Go to Settings to enable camera access." + details:nil]); + break; + case AVAuthorizationStatusRestricted: + handler([FlutterError errorWithCode:@"CameraAccessRestricted" + message:@"Camera access is restricted. " + details:nil]); + break; + case AVAuthorizationStatusNotDetermined: { + [AVCaptureDevice + requestAccessForMediaType:AVMediaTypeVideo + completionHandler:^(BOOL granted) { + // handler can be invoked on an arbitrary dispatch queue. + handler(granted ? nil + : [FlutterError + errorWithCode:@"CameraAccessDenied" + message:@"User denied the camera access request." + details:nil]); + }]; + break; + } + } +} diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index c0a3833dcd64..43d541e411b4 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -7,6 +7,7 @@ @import AVFoundation; +#import "CameraPermissionUtils.h" #import "CameraProperties.h" #import "FLTCam.h" #import "FLTThreadSafeEventChannel.h" @@ -131,31 +132,14 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call [result sendNotImplemented]; } } else if ([@"create" isEqualToString:call.method]) { - NSString *cameraName = call.arguments[@"cameraName"]; - NSString *resolutionPreset = call.arguments[@"resolutionPreset"]; - NSNumber *enableAudio = call.arguments[@"enableAudio"]; - NSError *error; - FLTCam *cam = [[FLTCam alloc] initWithCameraName:cameraName - resolutionPreset:resolutionPreset - enableAudio:[enableAudio boolValue] - orientation:[[UIDevice currentDevice] orientation] - captureSessionQueue:_captureSessionQueue - error:&error]; - - if (error) { - [result sendError:error]; - } else { - if (_camera) { - [_camera close]; + FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { + // Create FLTCam only if granted camera access. + if (error) { + [result sendFlutterError:error]; + } else { + [self createCameraOnSessionQueueWithCreateMethodCall:call result:result]; } - _camera = cam; - [self.registry registerTexture:cam - completion:^(int64_t textureId) { - [result sendSuccessWithData:@{ - @"cameraId" : @(textureId), - }]; - }]; - } + }); } else if ([@"startImageStream" isEqualToString:call.method]) { [_camera startImageStreamWithMessenger:_messenger]; [result sendSuccess]; @@ -274,4 +258,35 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call } } +- (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)createMethodCall + result:(FLTThreadSafeFlutterResult *)result { + dispatch_async(self.captureSessionQueue, ^{ + NSString *cameraName = createMethodCall.arguments[@"cameraName"]; + NSString *resolutionPreset = createMethodCall.arguments[@"resolutionPreset"]; + NSNumber *enableAudio = createMethodCall.arguments[@"enableAudio"]; + NSError *error; + FLTCam *cam = [[FLTCam alloc] initWithCameraName:cameraName + resolutionPreset:resolutionPreset + enableAudio:[enableAudio boolValue] + orientation:[[UIDevice currentDevice] orientation] + captureSessionQueue:self.captureSessionQueue + error:&error]; + + if (error) { + [result sendError:error]; + } else { + if (self.camera) { + [self.camera close]; + } + self.camera = cam; + [self.registry registerTexture:cam + completion:^(int64_t textureId) { + [result sendSuccessWithData:@{ + @"cameraId" : @(textureId), + }]; + }]; + } + }); +} + @end diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.modulemap b/packages/camera/camera/ios/Classes/CameraPlugin.modulemap index a23848aaccfc..897302799497 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.modulemap +++ b/packages/camera/camera/ios/Classes/CameraPlugin.modulemap @@ -6,6 +6,7 @@ framework module camera { explicit module Test { header "CameraPlugin_Test.h" + header "CameraPermissionUtils.h" header "CameraProperties.h" header "FLTCam.h" header "FLTCam_Test.h" diff --git a/packages/camera/camera/ios/Classes/CameraPlugin_Test.h b/packages/camera/camera/ios/Classes/CameraPlugin_Test.h index 826b05043f78..d1903e0829b4 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin_Test.h +++ b/packages/camera/camera/ios/Classes/CameraPlugin_Test.h @@ -38,4 +38,10 @@ /// that triggered the orientation change. - (void)orientationChanged:(NSNotification *)notification; +/// Creates FLTCam on session queue and reports the creation result. +/// @param createMethodCall the create method call +/// @param result a thread safe flutter result wrapper object to report creation result. +- (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)createMethodCall + result:(FLTThreadSafeFlutterResult *)result; + @end diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h index 70c9f868eda9..6677505671a3 100644 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h @@ -4,6 +4,8 @@ #import +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. @@ -13,13 +15,13 @@ /** * Gets the original FlutterResult object wrapped by this FLTThreadSafeFlutterResult instance. */ -@property(readonly, nonatomic, nonnull) FlutterResult flutterResult; +@property(readonly, nonatomic) FlutterResult flutterResult; /** * Initializes with a FlutterResult object. * @param result The FlutterResult object that the result will be given to. */ -- (nonnull instancetype)initWithResult:(nonnull FlutterResult)result; +- (instancetype)initWithResult:(FlutterResult)result; /** * Sends a successful result on the main thread without any data. @@ -30,18 +32,24 @@ * Sends a successful result on the main thread with data. * @param data Result data that is send to the Flutter Dart side. */ -- (void)sendSuccessWithData:(nonnull id)data; +- (void)sendSuccessWithData:(id)data; /** * Sends an NSError as result on the main thread. * @param error Error that will be send as FlutterError. */ -- (void)sendError:(nonnull NSError *)error; +- (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. + */ +- (void)sendFlutterError:(FlutterError *)flutterError; /** * Sends a FlutterError as result on the main thread. */ -- (void)sendErrorWithCode:(nonnull NSString *)code +- (void)sendErrorWithCode:(NSString *)code message:(nullable NSString *)message details:(nullable id)details; @@ -50,3 +58,5 @@ */ - (void)sendNotImplemented; @end + +NS_ASSUME_NONNULL_END diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m index 58c2e788cdc0..ad125f7f32ed 100644 --- a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m +++ b/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m @@ -39,6 +39,10 @@ - (void)sendErrorWithCode:(NSString *)code [self send:flutterError]; } +- (void)sendFlutterError:(FlutterError *)flutterError { + [self send:flutterError]; +} + - (void)sendNotImplemented { [self send:FlutterMethodNotImplemented]; } diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index d763843d0572..59cde43dd66a 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.4+24 +version: 0.9.5 environment: sdk: ">=2.14.0 <3.0.0" From 4b1d47eebbeb976c5dd4db1a95913cf608a53373 Mon Sep 17 00:00:00 2001 From: Huey Zhang Date: Tue, 17 May 2022 01:24:12 +0800 Subject: [PATCH 311/844] [image_picker] Fix 'messages.g.h' file not found (#5635) --- packages/image_picker/image_picker_ios/CHANGELOG.md | 4 ++++ .../image_picker_ios/ios/Classes/FLTImagePickerPlugin_Test.h | 2 +- packages/image_picker/image_picker_ios/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index 96b1c7f0d0a4..e39a314ca49a 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.5+3 + +* Fixes 'messages.g.h' file not found. + ## 0.8.5+2 * Minor fixes for new analysis options. 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 2c4167746c8e..64c20452987d 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 @@ -6,7 +6,7 @@ #import -#import +#import "messages.g.h" NS_ASSUME_NONNULL_BEGIN diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index d1de0a14ea69..edb884a377db 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 video_picker plugin. repository: https://github.com/flutter/plugins/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.5+2 +version: 0.8.5+3 environment: sdk: ">=2.14.0 <3.0.0" From 49a0d369ac99ffdea5cba63979ff20ca528c046a Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 16 May 2022 19:02:11 -0400 Subject: [PATCH 312/844] [ci] Manually roll Flutter master (#5765) --- .ci/flutter_master.version | 2 +- packages/camera/camera/CHANGELOG.md | 4 +++ .../camera/ios/Classes/FLTSavePhotoDelegate.m | 4 +++ packages/camera/camera/pubspec.yaml | 2 +- .../image_picker_ios/CHANGELOG.md | 4 +++ .../Classes/FLTImagePickerPhotoAssetUtil.m | 3 ++ .../image_picker_ios/pubspec.yaml | 2 +- .../local_auth/local_auth_ios/CHANGELOG.md | 6 +++- .../ios/Classes/FLTLocalAuthPlugin.m | 29 ++++++++++++++++++- .../local_auth/local_auth_ios/pubspec.yaml | 2 +- 10 files changed, 52 insertions(+), 6 deletions(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 6934eda08a73..7bede728c582 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2b2cda15293d86d5c27d345505cde25b9efcb153 +036cae36697a6c078fe28ac6088a2a493d0e7958 diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 8d713c60c276..d101f60cf041 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.5+1 + +* Suppresses warnings for pre-iOS-11 codepaths. + ## 0.9.5 * Adds camera access permission handling logic on iOS to fix a related crash when using the camera for the first time. diff --git a/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m b/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m index ced3cb5e407f..1df1708c54e8 100644 --- a/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m +++ b/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m @@ -42,6 +42,9 @@ - (void)handlePhotoCaptureResultWithError:(NSError *)error }); } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#pragma clang diagnostic ignored "-Wdeprecated-implementations" - (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhotoSampleBuffer:(CMSampleBufferRef)photoSampleBuffer previewPhotoSampleBuffer:(CMSampleBufferRef)previewPhotoSampleBuffer @@ -56,6 +59,7 @@ - (void)captureOutput:(AVCapturePhotoOutput *)output previewPhotoSampleBuffer]; }]; } +#pragma clang diagnostic pop - (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 59cde43dd66a..14acf32e2324 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.5 +version: 0.9.5+1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index e39a314ca49a..e994fcc50c8e 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.5+4 + +* Suppresses warnings for pre-iOS-11 codepaths. + ## 0.8.5+3 * Fixes 'messages.g.h' file not found. diff --git a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPhotoAssetUtil.m b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPhotoAssetUtil.m index 4c705fe54350..37a1a9897cd3 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPhotoAssetUtil.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPhotoAssetUtil.m @@ -14,6 +14,8 @@ + (PHAsset *)getAssetFromImagePickerInfo:(NSDictionary *)info { if (@available(iOS 11, *)) { return [info objectForKey:UIImagePickerControllerPHAsset]; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" NSURL *referenceURL = [info objectForKey:UIImagePickerControllerReferenceURL]; if (!referenceURL) { return nil; @@ -21,6 +23,7 @@ + (PHAsset *)getAssetFromImagePickerInfo:(NSDictionary *)info { PHFetchResult *result = [PHAsset fetchAssetsWithALAssetURLs:@[ referenceURL ] options:nil]; return result.firstObject; +#pragma clang diagnostic pop } + (PHAsset *)getAssetFromPHPickerResult:(PHPickerResult *)result API_AVAILABLE(ios(14)) { diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index edb884a377db..88f4d3352228 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 video_picker plugin. repository: https://github.com/flutter/plugins/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.5+3 +version: 0.8.5+4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index 2237cbe216f0..d44836788ce3 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.6 + +* Suppresses warnings for pre-iOS-11 codepaths. + ## 1.0.5 * Removes unnecessary imports. @@ -6,7 +10,7 @@ ## 1.0.4 -* Fixes `deviceSupportsBiometrics` to return true when biometric hardware +* Fixes `deviceSupportsBiometrics` to return true when biometric hardware is available but not enrolled. ## 1.0.3 diff --git a/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m index eb7f637f7850..8f61fecfd814 100644 --- a/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m +++ b/packages/local_auth/local_auth_ios/ios/Classes/FLTLocalAuthPlugin.m @@ -84,7 +84,16 @@ - (void)alertMessage:(NSString *)message handler:^(UIAlertAction *action) { if (UIApplicationOpenSettingsURLString != NULL) { NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; - [[UIApplication sharedApplication] openURL:url]; + if (@available(iOS 10, *)) { + [[UIApplication sharedApplication] openURL:url + options:@{} + completionHandler:NULL]; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [[UIApplication sharedApplication] openURL:url]; +#pragma clang diagnostic pop + } result(@NO); } }]; @@ -113,9 +122,12 @@ - (void)deviceSupportsBiometrics:(FlutterResult)result { result(@YES); return; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" } else if (authError.code == LAErrorTouchIDNotEnrolled) { result(@YES); return; +#pragma clang diagnostic pop } } @@ -205,9 +217,14 @@ - (void)handleAuthReplyWithSuccess:(BOOL)success } else { switch (error.code) { case LAErrorPasscodeNotSet: +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // TODO(stuartmorgan): Remove the pragma and s/TouchID/Biometry/ in these constants when + // iOS 10 support is dropped. The values are the same, only the names have changed. case LAErrorTouchIDNotAvailable: case LAErrorTouchIDNotEnrolled: case LAErrorTouchIDLockout: +#pragma clang diagnostic pop case LAErrorUserFallback: [self handleErrors:error flutterArguments:arguments withFlutterResult:result]; return; @@ -228,7 +245,12 @@ - (void)handleErrors:(NSError *)authError NSString *errorCode = @"NotAvailable"; switch (authError.code) { case LAErrorPasscodeNotSet: +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // TODO(stuartmorgan): Remove the pragma and s/TouchID/Biometry/ in this constant when + // iOS 10 support is dropped. The values are the same, only the names have changed. case LAErrorTouchIDNotEnrolled: +#pragma clang diagnostic pop if ([arguments[@"useErrorDialogs"] boolValue]) { [self alertMessage:arguments[@"goToSettingDescriptionIOS"] firstButton:arguments[@"okButton"] @@ -238,7 +260,12 @@ - (void)handleErrors:(NSError *)authError } errorCode = authError.code == LAErrorPasscodeNotSet ? @"PasscodeNotSet" : @"NotEnrolled"; break; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // TODO(stuartmorgan): Remove the pragma and s/TouchID/Biometry/ in this constant when + // iOS 10 support is dropped. The values are the same, only the names have changed. case LAErrorTouchIDLockout: +#pragma clang diagnostic pop [self alertMessage:arguments[@"lockOut"] firstButton:arguments[@"okButton"] flutterResult:result diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index dded42b673f4..f491dfb49679 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.5 +version: 1.0.6 environment: sdk: ">=2.14.0 <3.0.0" From 46e8a3d73dbc33fc22032299076650a2b45be0a0 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 16 May 2022 22:17:12 -0400 Subject: [PATCH 313/844] Roll Flutter from 036cae36697a to bf7a32628eef (49 revisions) (#5768) --- .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 7bede728c582..bc6d07c1c54d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -036cae36697a6c078fe28ac6088a2a493d0e7958 +bf7a32628eef08649ded09376424d2e78cce0efb From 1d51057f35faf91b161e905681f3b3a17c345dbc Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 16 May 2022 23:22:13 -0400 Subject: [PATCH 314/844] Roll Flutter from bf7a32628eef to bb9bbc601345 (1 revision) (#5769) --- .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 bc6d07c1c54d..0151ec44ba18 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -bf7a32628eef08649ded09376424d2e78cce0efb +bb9bbc601345447db5d46e3bd97d2dcf4e6bfbd7 From 54cdccb24327e5258e7c00ac1cbd13b0dc5d7124 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 17 May 2022 00:27:11 -0400 Subject: [PATCH 315/844] Roll Flutter from bb9bbc601345 to fd312f1ccff9 (1 revision) (#5770) --- .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 0151ec44ba18..ae8e0b5b1d80 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -bb9bbc601345447db5d46e3bd97d2dcf4e6bfbd7 +fd312f1ccff909fde28d2247a489bf210bbc6c48 From 8ea876cd0fcfdd6a5d30938eee4d2b651fb90921 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 17 May 2022 03:02:07 -0400 Subject: [PATCH 316/844] Roll Flutter from fd312f1ccff9 to c248854d176b (1 revision) (#5771) --- .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 ae8e0b5b1d80..a0755e305782 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fd312f1ccff909fde28d2247a489bf210bbc6c48 +c248854d176b92ac0d50a4ad0edc19bd12210400 From a692f840ce727e4a1f9b6936c9d0219330af5742 Mon Sep 17 00:00:00 2001 From: ChineseDragon Date: Tue, 17 May 2022 22:32:11 +0800 Subject: [PATCH 317/844] [in_app_purchase] fixed a memory leak error (#5358) --- .../in_app_purchase/in_app_purchase_storekit/CHANGELOG.md | 4 ++++ .../ios/Classes/InAppPurchasePlugin.m | 7 +------ .../in_app_purchase/in_app_purchase_storekit/pubspec.yaml | 2 +- 3 files changed, 6 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 aba1d6ed3555..365c7133864b 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.0+8 + +* Fixes a memory leak on iOS. + ## 0.3.0+7 * Minor fixes for new analysis options. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m index a580a46b011d..d64c24563b62 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m @@ -25,9 +25,6 @@ @interface InAppPurchasePlugin () // 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 *registry; -@property(strong, nonatomic, readonly) NSObject *messenger; @property(strong, nonatomic, readonly) NSObject *registrar; @property(strong, nonatomic, readonly) FIAPReceiptManager *receiptManager; @@ -57,8 +54,6 @@ - (instancetype)initWithReceiptManager:(FIAPReceiptManager *)receiptManager { - (instancetype)initWithRegistrar:(NSObject *)registrar { self = [self initWithReceiptManager:[FIAPReceiptManager new]]; _registrar = registrar; - _registry = [registrar textures]; - _messenger = [registrar messenger]; __weak typeof(self) weakSelf = self; _paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:[SKPaymentQueue defaultQueue] @@ -347,7 +342,7 @@ - (void)registerPaymentQueueDelegate:(FlutterResult)result { if (@available(iOS 13.0, *)) { _paymentQueueDelegateCallbackChannel = [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/in_app_purchase_payment_queue_delegate" - binaryMessenger:_messenger]; + binaryMessenger:[_registrar messenger]]; _paymentQueueDelegate = [[FIAPPaymentQueueDelegate alloc] initWithMethodChannel:_paymentQueueDelegateCallbackChannel]; 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 235d491fbff0..ebd5e55acdad 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 platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/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.0+7 +version: 0.3.0+8 environment: sdk: ">=2.14.0 <3.0.0" From 550ba3cd8af9377131bf7c451fe510b6d085be7c Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Tue, 17 May 2022 08:17:11 -0700 Subject: [PATCH 318/844] [local_auth] Windows support. (#4806) --- .../local_auth/local_auth_windows/AUTHORS | 7 + .../local_auth_windows/CHANGELOG.md | 3 + .../local_auth/local_auth_windows/LICENSE | 25 ++ .../local_auth/local_auth_windows/README.md | 11 + .../local_auth_windows/example/.gitignore | 46 ++++ .../local_auth_windows/example/.metadata | 10 + .../local_auth_windows/example/README.md | 3 + .../integration_test/local_auth_test.dart | 19 ++ .../local_auth_windows/example/lib/main.dart | 241 +++++++++++++++++ .../local_auth_windows/example/pubspec.yaml | 28 ++ .../example/test_driver/integration_test.dart | 7 + .../example/windows/.gitignore | 17 ++ .../example/windows/CMakeLists.txt | 100 +++++++ .../example/windows/flutter/CMakeLists.txt | 103 +++++++ .../windows/flutter/generated_plugins.cmake | 24 ++ .../example/windows/runner/CMakeLists.txt | 17 ++ .../example/windows/runner/Runner.rc | 121 +++++++++ .../example/windows/runner/flutter_window.cpp | 65 +++++ .../example/windows/runner/flutter_window.h | 37 +++ .../example/windows/runner/main.cpp | 46 ++++ .../example/windows/runner/resource.h | 16 ++ .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 20 ++ .../example/windows/runner/utils.cpp | 67 +++++ .../example/windows/runner/utils.h | 23 ++ .../example/windows/runner/win32_window.cpp | 241 +++++++++++++++++ .../example/windows/runner/win32_window.h | 99 +++++++ .../lib/local_auth_windows.dart | 82 ++++++ .../lib/types/auth_messages_windows.dart | 22 ++ .../local_auth_windows/pubspec.yaml | 26 ++ .../test/local_auth_test.dart | 79 ++++++ .../local_auth_windows/windows/CMakeLists.txt | 120 +++++++++ .../local_auth_windows/local_auth_plugin.h | 26 ++ .../local_auth_windows/windows/local_auth.h | 89 ++++++ .../windows/local_auth_plugin.cpp | 236 ++++++++++++++++ .../windows/local_auth_windows.cpp | 15 ++ .../windows/test/local_auth_plugin_test.cpp | 253 ++++++++++++++++++ .../local_auth_windows/windows/test/mocks.h | 63 +++++ 38 files changed, 2407 insertions(+) create mode 100644 packages/local_auth/local_auth_windows/AUTHORS create mode 100644 packages/local_auth/local_auth_windows/CHANGELOG.md create mode 100644 packages/local_auth/local_auth_windows/LICENSE create mode 100644 packages/local_auth/local_auth_windows/README.md create mode 100644 packages/local_auth/local_auth_windows/example/.gitignore create mode 100644 packages/local_auth/local_auth_windows/example/.metadata create mode 100644 packages/local_auth/local_auth_windows/example/README.md create mode 100644 packages/local_auth/local_auth_windows/example/integration_test/local_auth_test.dart create mode 100644 packages/local_auth/local_auth_windows/example/lib/main.dart create mode 100644 packages/local_auth/local_auth_windows/example/pubspec.yaml create mode 100644 packages/local_auth/local_auth_windows/example/test_driver/integration_test.dart create mode 100644 packages/local_auth/local_auth_windows/example/windows/.gitignore create mode 100644 packages/local_auth/local_auth_windows/example/windows/CMakeLists.txt create mode 100644 packages/local_auth/local_auth_windows/example/windows/flutter/CMakeLists.txt create mode 100644 packages/local_auth/local_auth_windows/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/CMakeLists.txt create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/Runner.rc create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.cpp create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.h create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/main.cpp create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/resource.h create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/resources/app_icon.ico create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/runner.exe.manifest create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/utils.cpp create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/utils.h create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/win32_window.cpp create mode 100644 packages/local_auth/local_auth_windows/example/windows/runner/win32_window.h create mode 100644 packages/local_auth/local_auth_windows/lib/local_auth_windows.dart create mode 100644 packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart create mode 100644 packages/local_auth/local_auth_windows/pubspec.yaml create mode 100644 packages/local_auth/local_auth_windows/test/local_auth_test.dart create mode 100644 packages/local_auth/local_auth_windows/windows/CMakeLists.txt create mode 100644 packages/local_auth/local_auth_windows/windows/include/local_auth_windows/local_auth_plugin.h create mode 100644 packages/local_auth/local_auth_windows/windows/local_auth.h create mode 100644 packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp create mode 100644 packages/local_auth/local_auth_windows/windows/local_auth_windows.cpp create mode 100644 packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp create mode 100644 packages/local_auth/local_auth_windows/windows/test/mocks.h diff --git a/packages/local_auth/local_auth_windows/AUTHORS b/packages/local_auth/local_auth_windows/AUTHORS new file mode 100644 index 000000000000..5db3d584e6bc --- /dev/null +++ b/packages/local_auth/local_auth_windows/AUTHORS @@ -0,0 +1,7 @@ +# 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. +Alexandre Zollinger Chohfi \ No newline at end of file diff --git a/packages/local_auth/local_auth_windows/CHANGELOG.md b/packages/local_auth/local_auth_windows/CHANGELOG.md new file mode 100644 index 000000000000..7cf171f305de --- /dev/null +++ b/packages/local_auth/local_auth_windows/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +* Initial release of Windows support. diff --git a/packages/local_auth/local_auth_windows/LICENSE b/packages/local_auth/local_auth_windows/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/local_auth/local_auth_windows/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/local_auth/local_auth_windows/README.md b/packages/local_auth/local_auth_windows/README.md new file mode 100644 index 000000000000..0c2984f40003 --- /dev/null +++ b/packages/local_auth/local_auth_windows/README.md @@ -0,0 +1,11 @@ +# local\_auth\_windows + +The Windows implementation of [`local_auth`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `local_auth` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/local_auth +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin \ No newline at end of file diff --git a/packages/local_auth/local_auth_windows/example/.gitignore b/packages/local_auth/local_auth_windows/example/.gitignore new file mode 100644 index 000000000000..0fa6b675c0a5 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/.gitignore @@ -0,0 +1,46 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/packages/local_auth/local_auth_windows/example/.metadata b/packages/local_auth/local_auth_windows/example/.metadata new file mode 100644 index 000000000000..166a9984ca13 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: c860cba910319332564e1e9d470a17074c1f2dfd + channel: stable + +project_type: app diff --git a/packages/local_auth/local_auth_windows/example/README.md b/packages/local_auth/local_auth_windows/example/README.md new file mode 100644 index 000000000000..8f48b8563cad --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/README.md @@ -0,0 +1,3 @@ +# local_auth_example + +Demonstrates how to use the local_auth plugin. \ No newline at end of file diff --git a/packages/local_auth/local_auth_windows/example/integration_test/local_auth_test.dart b/packages/local_auth/local_auth_windows/example/integration_test/local_auth_test.dart new file mode 100644 index 000000000000..cedaaf28ff24 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/integration_test/local_auth_test.dart @@ -0,0 +1,19 @@ +// 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:integration_test/integration_test.dart'; + +import 'package:local_auth_windows/local_auth_windows.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('canCheckBiometrics', (WidgetTester tester) async { + expect( + LocalAuthWindows().getEnrolledBiometrics(), + completion(isList), + ); + }); +} diff --git a/packages/local_auth/local_auth_windows/example/lib/main.dart b/packages/local_auth/local_auth_windows/example/lib/main.dart new file mode 100644 index 000000000000..ef26ec5545c5 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/lib/main.dart @@ -0,0 +1,241 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_windows/local_auth_windows.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + _SupportState _supportState = _SupportState.unknown; + bool? _deviceSupportsBiometrics; + List? _enrolledBiometrics; + String _authorized = 'Not Authorized'; + bool _isAuthenticating = false; + + @override + void initState() { + super.initState(); + LocalAuthPlatform.instance.isDeviceSupported().then( + (bool isSupported) => setState(() => _supportState = isSupported + ? _SupportState.supported + : _SupportState.unsupported), + ); + } + + Future _checkBiometrics() async { + late bool deviceSupportsBiometrics; + try { + deviceSupportsBiometrics = + await LocalAuthPlatform.instance.deviceSupportsBiometrics(); + } on PlatformException catch (e) { + deviceSupportsBiometrics = false; + print(e); + } + if (!mounted) { + return; + } + + setState(() { + _deviceSupportsBiometrics = deviceSupportsBiometrics; + }); + } + + Future _getEnrolledBiometrics() async { + late List availableBiometrics; + try { + availableBiometrics = + await LocalAuthPlatform.instance.getEnrolledBiometrics(); + } on PlatformException catch (e) { + availableBiometrics = []; + print(e); + } + if (!mounted) { + return; + } + + setState(() { + _enrolledBiometrics = availableBiometrics; + }); + } + + Future _authenticate() async { + bool authenticated = false; + try { + setState(() { + _isAuthenticating = true; + _authorized = 'Authenticating'; + }); + authenticated = await LocalAuthPlatform.instance.authenticate( + localizedReason: 'Let OS determine authentication method', + authMessages: [const WindowsAuthMessages()], + options: const AuthenticationOptions( + useErrorDialogs: true, + stickyAuth: true, + ), + ); + setState(() { + _isAuthenticating = false; + }); + } on PlatformException catch (e) { + print(e); + setState(() { + _isAuthenticating = false; + _authorized = 'Error - ${e.message}'; + }); + return; + } + if (!mounted) { + return; + } + + setState( + () => _authorized = authenticated ? 'Authorized' : 'Not Authorized'); + } + + Future _authenticateWithBiometrics() async { + bool authenticated = false; + try { + setState(() { + _isAuthenticating = true; + _authorized = 'Authenticating'; + }); + authenticated = await LocalAuthPlatform.instance.authenticate( + localizedReason: + 'Scan your fingerprint (or face or whatever) to authenticate', + authMessages: [const WindowsAuthMessages()], + options: const AuthenticationOptions( + useErrorDialogs: true, + stickyAuth: true, + biometricOnly: true, + ), + ); + setState(() { + _isAuthenticating = false; + _authorized = 'Authenticating'; + }); + } on PlatformException catch (e) { + print(e); + setState(() { + _isAuthenticating = false; + _authorized = 'Error - ${e.message}'; + }); + return; + } + if (!mounted) { + return; + } + + final String message = authenticated ? 'Authorized' : 'Not Authorized'; + setState(() { + _authorized = message; + }); + } + + Future _cancelAuthentication() async { + await LocalAuthPlatform.instance.stopAuthentication(); + setState(() => _isAuthenticating = false); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: ListView( + padding: const EdgeInsets.only(top: 30), + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (_supportState == _SupportState.unknown) + const CircularProgressIndicator() + else if (_supportState == _SupportState.supported) + const Text('This device is supported') + else + const Text('This device is not supported'), + const Divider(height: 100), + Text( + 'Device supports biometrics: $_deviceSupportsBiometrics\n'), + ElevatedButton( + onPressed: _checkBiometrics, + child: const Text('Check biometrics'), + ), + const Divider(height: 100), + Text('Enrolled biometrics: $_enrolledBiometrics\n'), + ElevatedButton( + onPressed: _getEnrolledBiometrics, + child: const Text('Get enrolled biometrics'), + ), + const Divider(height: 100), + Text('Current State: $_authorized\n'), + if (_isAuthenticating) + ElevatedButton( + onPressed: _cancelAuthentication, + child: Row( + mainAxisSize: MainAxisSize.min, + children: const [ + Text('Cancel Authentication'), + Icon(Icons.cancel), + ], + ), + ) + else + Column( + children: [ + ElevatedButton( + onPressed: _authenticate, + child: Row( + mainAxisSize: MainAxisSize.min, + children: const [ + Text('Authenticate'), + Icon(Icons.perm_device_information), + ], + ), + ), + ElevatedButton( + onPressed: _authenticateWithBiometrics, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(_isAuthenticating + ? 'Cancel' + : 'Authenticate: biometrics only'), + const Icon(Icons.fingerprint), + ], + ), + ), + ], + ), + ], + ), + ], + ), + ), + ); + } +} + +enum _SupportState { + unknown, + supported, + unsupported, +} diff --git a/packages/local_auth/local_auth_windows/example/pubspec.yaml b/packages/local_auth/local_auth_windows/example/pubspec.yaml new file mode 100644 index 000000000000..266c9fc7140d --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/pubspec.yaml @@ -0,0 +1,28 @@ +name: local_auth_windows_example +description: Demonstrates how to use the local_auth_windows plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +dependencies: + flutter: + sdk: flutter + local_auth_platform_interface: ^1.0.0 + local_auth_windows: + # When depending on this package from a real application you should use: + # local_auth_windows: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # 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: ../ + +dev_dependencies: + flutter_driver: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/local_auth/local_auth_windows/example/test_driver/integration_test.dart b/packages/local_auth/local_auth_windows/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/local_auth/local_auth_windows/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/local_auth/local_auth_windows/example/windows/.gitignore b/packages/local_auth/local_auth_windows/example/windows/.gitignore new file mode 100644 index 000000000000..d492d0d98c8f --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/packages/local_auth/local_auth_windows/example/windows/CMakeLists.txt b/packages/local_auth/local_auth_windows/example/windows/CMakeLists.txt new file mode 100644 index 000000000000..2163be881bd2 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/CMakeLists.txt @@ -0,0 +1,100 @@ +cmake_minimum_required(VERSION 3.14) +project(local_auth_windows_example LANGUAGES CXX) + +set(BINARY_NAME "local_auth_windows_example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Enable the test target. +set(include_local_auth_windows_tests TRUE) +# Provide an alias for the test target using the name expected by repo tooling. +add_custom_target(unit_tests DEPENDS local_auth_windows_test) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) 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 new file mode 100644 index 000000000000..b2e4bd8d658b --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/packages/local_auth/local_auth_windows/example/windows/flutter/generated_plugins.cmake b/packages/local_auth/local_auth_windows/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000000..ef187dcae56f --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + local_auth_windows +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/CMakeLists.txt b/packages/local_auth/local_auth_windows/example/windows/runner/CMakeLists.txt new file mode 100644 index 000000000000..de2d8916b72b --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) 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 new file mode 100644 index 000000000000..5fdea291cf19 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.cpp b/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.cpp new file mode 100644 index 000000000000..8254bd9ff3c1 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.cpp @@ -0,0 +1,65 @@ +// 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. + +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.h b/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.h new file mode 100644 index 000000000000..f1fc669093d0 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/flutter_window.h @@ -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. + +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/main.cpp b/packages/local_auth/local_auth_windows/example/windows/runner/main.cpp new file mode 100644 index 000000000000..4e37ae286c01 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/main.cpp @@ -0,0 +1,46 @@ +// 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. + +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t* command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"local_auth_windows_example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/resource.h b/packages/local_auth/local_auth_windows/example/windows/runner/resource.h new file mode 100644 index 000000000000..d5d958dc4257 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/resources/app_icon.ico b/packages/local_auth/local_auth_windows/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 zcmeHQc|26z|35SKE&G-*mXah&B~fFkXr)DEO&hIfqby^T&>|8^_Ub8Vp#`BLl3lbZ zvPO!8k!2X>cg~Elr=IVxo~J*a`+9wR=A83c-k-DFd(XM&UI1VKCqM@V;DDtJ09WB} zRaHKiW(GT00brH|0EeTeKVbpbGZg?nK6-j827q-+NFM34gXjqWxJ*a#{b_apGN<-L_m3#8Z26atkEn& ze87Bvv^6vVmM+p+cQ~{u%=NJF>#(d;8{7Q{^rWKWNtf14H}>#&y7$lqmY6xmZryI& z($uy?c5-+cPnt2%)R&(KIWEXww>Cnz{OUpT>W$CbO$h1= z#4BPMkFG1Y)x}Ui+WXr?Z!w!t_hjRq8qTaWpu}FH{MsHlU{>;08goVLm{V<&`itk~ zE_Ys=D(hjiy+5=?=$HGii=Y5)jMe9|wWoD_K07(}edAxh`~LBorOJ!Cf@f{_gNCC| z%{*04ViE!#>@hc1t5bb+NO>ncf@@Dv01K!NxH$3Eg1%)|wLyMDF8^d44lV!_Sr}iEWefOaL z8f?ud3Q%Sen39u|%00W<#!E=-RpGa+H8}{ulxVl4mwpjaU+%2pzmi{3HM)%8vb*~-M9rPUAfGCSos8GUXp02|o~0BTV2l#`>>aFV&_P$ejS;nGwSVP8 zMbOaG7<7eKD>c12VdGH;?2@q7535sa7MN*L@&!m?L`ASG%boY7(&L5imY#EQ$KrBB z4@_tfP5m50(T--qv1BJcD&aiH#b-QC>8#7Fx@3yXlonJI#aEIi=8&ChiVpc#N=5le zM*?rDIdcpawoc5kizv$GEjnveyrp3sY>+5_R5;>`>erS%JolimF=A^EIsAK zsPoVyyUHCgf0aYr&alx`<)eb6Be$m&`JYSuBu=p8j%QlNNp$-5C{b4#RubPb|CAIS zGE=9OFLP7?Hgc{?k45)84biT0k&-C6C%Q}aI~q<(7BL`C#<6HyxaR%!dFx7*o^laG z=!GBF^cwK$IA(sn9y6>60Rw{mYRYkp%$jH z*xQM~+bp)G$_RhtFPYx2HTsWk80+p(uqv9@I9)y{b$7NK53rYL$ezbmRjdXS?V}fj zWxX_feWoLFNm3MG7pMUuFPs$qrQWO9!l2B(SIuy2}S|lHNbHzoE+M2|Zxhjq9+Ws8c{*}x^VAib7SbxJ*Q3EnY5lgI9 z=U^f3IW6T=TWaVj+2N%K3<%Un;CF(wUp`TC&Y|ZjyFu6co^uqDDB#EP?DV5v_dw~E zIRK*BoY9y-G_ToU2V_XCX4nJ32~`czdjT!zwme zGgJ0nOk3U4@IE5JwtM}pwimLjk{ln^*4HMU%Fl4~n(cnsLB}Ja-jUM>xIB%aY;Nq8 z)Fp8dv1tkqKanv<68o@cN|%thj$+f;zGSO7H#b+eMAV8xH$hLggtt?O?;oYEgbq@= zV(u9bbd12^%;?nyk6&$GPI%|+<_mEpJGNfl*`!KV;VfmZWw{n{rnZ51?}FDh8we_L z8OI9nE31skDqJ5Oa_ybn7|5@ui>aC`s34p4ZEu6-s!%{uU45$Zd1=p$^^dZBh zu<*pDDPLW+c>iWO$&Z_*{VSQKg7=YEpS3PssPn1U!lSm6eZIho*{@&20e4Y_lRklKDTUCKI%o4Pc<|G^Xgu$J^Q|B87U;`c1zGwf^-zH*VQ^x+i^OUWE0yd z;{FJq)2w!%`x7yg@>uGFFf-XJl4H`YtUG%0slGKOlXV`q?RP>AEWg#x!b{0RicxGhS!3$p7 zij;{gm!_u@D4$Ox%>>bPtLJ> zwKtYz?T_DR1jN>DkkfGU^<#6sGz|~p*I{y`aZ>^Di#TC|Z!7j_O1=Wo8thuit?WxR zh9_S>kw^{V^|g}HRUF=dcq>?q(pHxw!8rx4dC6vbQVmIhmICF#zU!HkHpQ>9S%Uo( zMw{eC+`&pb=GZRou|3;Po1}m46H6NGd$t<2mQh}kaK-WFfmj_66_17BX0|j-E2fe3Jat}ijpc53 zJV$$;PC<5aW`{*^Z6e5##^`Ed#a0nwJDT#Qq~^e8^JTA=z^Kl>La|(UQ!bI@#ge{Dzz@61p-I)kc2?ZxFt^QQ}f%ldLjO*GPj(5)V9IyuUakJX=~GnTgZ4$5!3E=V#t`yOG4U z(gphZB6u2zsj=qNFLYShhg$}lNpO`P9xOSnO*$@@UdMYES*{jJVj|9z-}F^riksLK zbsU+4-{281P9e2UjY6tse^&a)WM1MFw;p#_dHhWI7p&U*9TR0zKdVuQed%6{otTsq z$f~S!;wg#Bd9kez=Br{m|66Wv z#g1xMup<0)H;c2ZO6su_ii&m8j&+jJz4iKnGZ&wxoQX|5a>v&_e#6WA!MB_4asTxLRGQCC5cI(em z%$ZfeqP>!*q5kU>a+BO&ln=4Jm>Ef(QE8o&RgLkk%2}4Tf}U%IFP&uS7}&|Q-)`5< z+e>;s#4cJ-z%&-^&!xsYx777Wt(wZY9(3(avmr|gRe4cD+a8&!LY`1^T?7x{E<=kdY9NYw>A;FtTvQ=Y&1M%lyZPl$ss1oY^Sl8we}n}Aob#6 zl4jERwnt9BlSoWb@3HxYgga(752Vu6Y)k4yk9u~Kw>cA5&LHcrvn1Y-HoIuFWg~}4 zEw4bR`mXZQIyOAzo)FYqg?$5W<;^+XX%Uz61{-L6@eP|lLH%|w?g=rFc;OvEW;^qh z&iYXGhVt(G-q<+_j}CTbPS_=K>RKN0&;dubh0NxJyDOHFF;<1k!{k#7b{|Qok9hac z;gHz}6>H6C6RnB`Tt#oaSrX0p-j-oRJ;_WvS-qS--P*8}V943RT6kou-G=A+7QPGQ z!ze^UGxtW3FC0$|(lY9^L!Lx^?Q8cny(rR`es5U;-xBhphF%_WNu|aO<+e9%6LuZq zt(0PoagJG<%hyuf;te}n+qIl_Ej;czWdc{LX^pS>77s9t*2b4s5dvP_!L^3cwlc)E!(!kGrg~FescVT zZCLeua3f4;d;Tk4iXzt}g}O@nlK3?_o91_~@UMIl?@77Qc$IAlLE95#Z=TES>2E%z zxUKpK{_HvGF;5%Q7n&vA?`{%8ohlYT_?(3A$cZSi)MvIJygXD}TS-3UwyUxGLGiJP znblO~G|*uA^|ac8E-w#}uBtg|s_~s&t>-g0X%zIZ@;o_wNMr_;{KDg^O=rg`fhDZu zFp(VKd1Edj%F zWHPl+)FGj%J1BO3bOHVfH^3d1F{)*PL&sRX`~(-Zy3&9UQX)Z;c51tvaI2E*E7!)q zcz|{vpK7bjxix(k&6=OEIBJC!9lTkUbgg?4-yE{9+pFS)$Ar@vrIf`D0Bnsed(Cf? zObt2CJ>BKOl>q8PyFO6w)+6Iz`LW%T5^R`U_NIW0r1dWv6OY=TVF?N=EfA(k(~7VBW(S;Tu5m4Lg8emDG-(mOSSs=M9Q&N8jc^Y4&9RqIsk(yO_P(mcCr}rCs%1MW1VBrn=0-oQN(Xj!k%iKV zb%ricBF3G4S1;+8lzg5PbZ|$Se$)I=PwiK=cDpHYdov2QO1_a-*dL4KUi|g&oh>(* zq$<`dQ^fat`+VW?m)?_KLn&mp^-@d=&7yGDt<=XwZZC=1scwxO2^RRI7n@g-1o8ps z)&+et_~)vr8aIF1VY1Qrq~Xe``KJrQSnAZ{CSq3yP;V*JC;mmCT6oRLSs7=GA?@6g zUooM}@tKtx(^|aKK8vbaHlUQqwE0}>j&~YlN3H#vKGm@u)xxS?n9XrOWUfCRa< z`20Fld2f&;gg7zpo{Adh+mqNntMc-D$N^yWZAZRI+u1T1zWHPxk{+?vcS1D>08>@6 zLhE@`gt1Y9mAK6Z4p|u(5I%EkfU7rKFSM=E4?VG9tI;a*@?6!ey{lzN5=Y-!$WFSe z&2dtO>^0@V4WRc#L&P%R(?@KfSblMS+N+?xUN$u3K4Ys%OmEh+tq}fnU}i>6YHM?< zlnL2gl~sF!j!Y4E;j3eIU-lfa`RsOL*Tt<%EFC0gPzoHfNWAfKFIKZN8}w~(Yi~=q z>=VNLO2|CjkxP}RkutxjV#4fWYR1KNrPYq5ha9Wl+u>ipsk*I(HS@iLnmGH9MFlTU zaFZ*KSR0px>o+pL7BbhB2EC1%PJ{67_ z#kY&#O4@P=OV#-79y_W>Gv2dxL*@G7%LksNSqgId9v;2xJ zrh8uR!F-eU$NMx@S*+sk=C~Dxr9Qn7TfWnTupuHKuQ$;gGiBcU>GF5sWx(~4IP3`f zWE;YFO*?jGwYh%C3X<>RKHC-DZ!*r;cIr}GLOno^3U4tFSSoJp%oHPiSa%nh=Zgn% z14+8v@ygy0>UgEN1bczD6wK45%M>psM)y^)IfG*>3ItX|TzV*0i%@>L(VN!zdKb8S?Qf7BhjNpziA zR}?={-eu>9JDcl*R=OP9B8N$IcCETXah9SUDhr{yrld{G;PnCWRsPD7!eOOFBTWUQ=LrA_~)mFf&!zJX!Oc-_=kT<}m|K52 z)M=G#;p;Rdb@~h5D{q^K;^fX-m5V}L%!wVC2iZ1uu401Ll}#rocTeK|7FAeBRhNdQ zCc2d^aQnQp=MpOmak60N$OgS}a;p(l9CL`o4r(e-nN}mQ?M&isv-P&d$!8|1D1I(3-z!wi zTgoo)*Mv`gC?~bm?S|@}I|m-E2yqPEvYybiD5azInexpK8?9q*$9Yy9-t%5jU8~ym zgZDx>!@ujQ=|HJnwp^wv-FdD{RtzO9SnyfB{mH_(c!jHL*$>0o-(h(eqe*ZwF6Lvu z{7rkk%PEqaA>o+f{H02tzZ@TWy&su?VNw43! z-X+rN`6llvpUms3ZiSt)JMeztB~>9{J8SPmYs&qohxdYFi!ra8KR$35Zp9oR)eFC4 zE;P31#3V)n`w$fZ|4X-|%MX`xZDM~gJyl2W;O$H25*=+1S#%|53>|LyH za@yh+;325%Gq3;J&a)?%7X%t@WXcWL*BaaR*7UEZad4I8iDt7^R_Fd`XeUo256;sAo2F!HcIQKk;h})QxEsPE5BcKc7WyerTchgKmrfRX z!x#H_%cL#B9TWAqkA4I$R^8{%do3Y*&(;WFmJ zU7Dih{t1<{($VtJRl9|&EB?|cJ)xse!;}>6mSO$o5XIx@V|AA8ZcoD88ZM?C*;{|f zZVmf94_l1OmaICt`2sTyG!$^UeTHx9YuUP!omj(r|7zpm5475|yXI=rR>>fteLI+| z)MoiGho0oEt=*J(;?VY0QzwCqw@cVm?d7Y!z0A@u#H?sCJ*ecvyhj& z-F77lO;SH^dmf?L>3i>?Z*U}Em4ZYV_CjgfvzYsRZ+1B!Uo6H6mbS<-FFL`ytqvb& zE7+)2ahv-~dz(Hs+f})z{*4|{)b=2!RZK;PWwOnO=hG7xG`JU5>bAvUbdYd_CjvtHBHgtGdlO+s^9ca^Bv3`t@VRX2_AD$Ckg36OcQRF zXD6QtGfHdw*hx~V(MV-;;ZZF#dJ-piEF+s27z4X1qi5$!o~xBnvf=uopcn7ftfsZc zy@(PuOk`4GL_n(H9(E2)VUjqRCk9kR?w)v@xO6Jm_Mx})&WGEl=GS0#)0FAq^J*o! zAClhvoTsNP*-b~rN{8Yym3g{01}Ep^^Omf=SKqvN?{Q*C4HNNAcrowIa^mf+3PRy! z*_G-|3i8a;+q;iP@~Of_$(vtFkB8yOyWt2*K)vAn9El>=D;A$CEx6b*XF@4y_6M+2 zpeW`RHoI_p(B{%(&jTHI->hmNmZjHUj<@;7w0mx3&koy!2$@cfX{sN19Y}euYJFn& z1?)+?HCkD0MRI$~uB2UWri})0bru_B;klFdwsLc!ne4YUE;t41JqfG# zZJq6%vbsdx!wYeE<~?>o4V`A3?lN%MnKQ`z=uUivQN^vzJ|C;sdQ37Qn?;lpzg})y z)_2~rUdH}zNwX;Tp0tJ78+&I=IwOQ-fl30R79O8@?Ub8IIA(6I`yHn%lARVL`%b8+ z4$8D-|MZZWxc_)vu6@VZN!HsI$*2NOV&uMxBNzIbRgy%ob_ zhwEH{J9r$!dEix9XM7n&c{S(h>nGm?el;gaX0@|QnzFD@bne`el^CO$yXC?BDJ|Qg z+y$GRoR`?ST1z^e*>;!IS@5Ovb7*RlN>BV_UC!7E_F;N#ky%1J{+iixp(dUJj93aK zzHNN>R-oN7>kykHClPnoPTIj7zc6KM(Pnlb(|s??)SMb)4!sMHU^-ntJwY5Big7xv zb1Ew`Xj;|D2kzGja*C$eS44(d&RMU~c_Y14V9_TLTz0J#uHlsx`S6{nhsA0dWZ#cG zJ?`fO50E>*X4TQLv#nl%3GOk*UkAgt=IY+u0LNXqeln3Z zv$~&Li`ZJOKkFuS)dJRA>)b_Da%Q~axwA_8zNK{BH{#}#m}zGcuckz}riDE-z_Ms> zR8-EqAMcfyGJCtvTpaUVQtajhUS%c@Yj}&6Zz;-M7MZzqv3kA7{SuW$oW#=0az2wQ zg-WG@Vb4|D`pl~Il54N7Hmsauc_ne-a!o5#j3WaBBh@Wuefb!QJIOn5;d)%A#s+5% zuD$H=VNux9bE-}1&bcYGZ+>1Fo;3Z@e&zX^n!?JK*adSbONm$XW9z;Q^L>9U!}Toj2WdafJ%oL#h|yWWwyAGxzfrAWdDTtaKl zK4`5tDpPg5>z$MNv=X0LZ0d6l%D{(D8oT@+w0?ce$DZ6pv>{1&Ok67Ix1 zH}3=IEhPJEhItCC8E=`T`N5(k?G=B4+xzZ?<4!~ ze~z6Wk9!CHTI(0rLJ4{JU?E-puc;xusR?>G?;4vt;q~iI9=kDL=z0Rr%O$vU`30X$ zDZRFyZ`(omOy@u|i6h;wtJlP;+}$|Ak|k2dea7n?U1*$T!sXqqOjq^NxLPMmk~&qI zYg0W?yK8T(6+Ea+$YyspKK?kP$+B`~t3^Pib_`!6xCs32!i@pqXfFV6PmBIR<-QW= zN8L{pt0Vap0x`Gzn#E@zh@H)0FfVfA_Iu4fjYZ+umO1LXIbVc$pY+E234u)ttcrl$ z>s92z4vT%n6cMb>=XT6;l0+9e(|CZG)$@C7t7Z7Ez@a)h)!hyuV&B5K%%)P5?Lk|C zZZSVzdXp{@OXSP0hoU-gF8s8Um(#xzjP2Vem zec#-^JqTa&Y#QJ>-FBxd7tf`XB6e^JPUgagB8iBSEps;92KG`!#mvVcPQ5yNC-GEG zTiHEDYfH+0O15}r^+ z#jxj=@x8iNHWALe!P3R67TwmhItn**0JwnzSV2O&KE8KcT+0hWH^OPD1pwiuyx=b@ zNf5Jh0{9X)8;~Es)$t@%(3!OnbY+`@?i{mGX7Yy}8T_*0a6g;kaFPq;*=px5EhO{Cp%1kI<0?*|h8v!6WnO3cCJRF2-CRrU3JiLJnj@6;L)!0kWYAc_}F{2P))3HmCrz zQ&N&gE70;`!6*eJ4^1IR{f6j4(-l&X!tjHxkbHA^Zhrnhr9g{exN|xrS`5Pq=#Xf& zG%P=#ra-TyVFfgW%cZo5OSIwFL9WtXAlFOa+ubmI5t*3=g#Y zF%;70p5;{ZeFL}&}yOY1N1*Q;*<(kTB!7vM$QokF)yr2FlIU@$Ph58$Bz z0J?xQG=MlS4L6jA22eS42g|9*9pX@$#*sUeM(z+t?hr@r5J&D1rx}2pW&m*_`VDCW zUYY@v-;bAO0HqoAgbbiGGC<=ryf96}3pouhy3XJrX+!!u*O_>Si38V{uJmQ&USptX zKp#l(?>%^7;2%h(q@YWS#9;a!JhKlkR#Vd)ERILlgu!Hr@jA@V;sk4BJ-H#p*4EqC zDGjC*tl=@3Oi6)Bn^QwFpul18fpkbpg0+peH$xyPBqb%`$OUhPKyWb32o7clB*9Z< zN=i~NLjavrLtwgJ01bufP+>p-jR2I95|TpmKpQL2!oV>g(4RvS2pK4*ou%m(h6r3A zX#s&`9LU1ZG&;{CkOK!4fLDTnBys`M!vuz>Q&9OZ0hGQl!~!jSDg|~s*w52opC{sB ze|Cf2luD(*G13LcOAGA!s2FjSK8&IE5#W%J25w!vM0^VyQM!t)inj&RTiJ!wXzFgz z3^IqzB7I0L$llljsGq})thBy9UOyjtFO_*hYM_sgcMk>44jeH0V1FDyELc{S1F-;A zS;T^k^~4biG&V*Irq}O;e}j$$+E_#G?HKIn05iP3j|87TkGK~SqG!-KBg5+mN(aLm z8ybhIM`%C19UX$H$KY6JgXbY$0AT%rEpHC;u`rQ$Y=rxUdsc5*Kvc8jaYaO$^)cI6){P6K0r)I6DY4Wr4&B zLQUBraey#0HV|&c4v7PVo3n$zHj99(TZO^3?Ly%C4nYvJTL9eLBLHsM3WKKD>5!B` zQ=BsR3aR6PD(Fa>327E2HAu5TM~Wusc!)>~(gM)+3~m;92Jd;FnSib=M5d6;;5{%R zb4V7DEJ0V!CP-F*oU?gkc>ksUtAYP&V4ND5J>J2^jt*vcFflQWCrB&fLdT%O59PVJ zhid#toR=FNgD!q3&r8#wEBr`!wzvQu5zX?Q>nlSJ4i@WC*CN*-xU66F^V5crWevQ9gsq$I@z1o(a=k7LL~ z7m_~`o;_Ozha1$8Q}{WBehvAlO4EL60y5}8GDrZ< zXh&F}71JbW2A~8KfEWj&UWV#4+Z4p`b{uAj4&WC zha`}X@3~+Iz^WRlOHU&KngK>#j}+_o@LdBC1H-`gT+krWX3-;!)6?{FBp~%20a}FL zFP9%Emqcwa#(`=G>BBZ0qZDQhmZKJg_g8<=bBFKWr!dyg(YkpE+|R*SGpDVU!+VlU zFC54^DLv}`qa%49T>nNiA9Q7Ips#!Xx90tCU2gvK`(F+GPcL=J^>No{)~we#o@&mUb6c$ zCc*<|NJBk-#+{j9xkQ&ujB zI~`#kN~7W!f*-}wkG~Ld!JqZ@tK}eeSnsS5J1fMFXm|`LJx&}5`@dK3W^7#Wnm+_P zBZkp&j1fa2Y=eIjJ0}gh85jt43kaIXXv?xmo@eHrka!Z|vQv12HN#+!I5E z`(fbuW>gFiJL|uXJ!vKt#z3e3HlVdboH7;e#i3(2<)Fg-I@BR!qY#eof3MFZ&*Y@l zI|KJf&ge@p2Dq09Vu$$Qxb7!}{m-iRk@!)%KL)txi3;~Z4Pb}u@GsW;ELiWeG9V51 znX#}B&4Y2E7-H=OpNE@q{%hFLxwIpBF2t{vPREa8_{linXT;#1vMRWjOzLOP$-hf( z>=?$0;~~PnkqY;~K{EM6Vo-T(0K{A0}VUGmu*hR z{tw3hvBN%N3G3Yw`X5Te+F{J`(3w1s3-+1EbnFQKcrgrX1Jqvs@ADGe%M0s$EbK$$ zK)=y=upBc6SjGYAACCcI=Y*6Fi8_jgwZlLxD26fnQfJmb8^gHRN5(TemhX@0e=vr> zg`W}6U>x6VhoA3DqsGGD9uL1DhB3!OXO=k}59TqD@(0Nb{)Ut_luTioK_>7wjc!5C zIr@w}b`Fez3)0wQfKl&bae7;PcTA7%?f2xucM0G)wt_KO!Ewx>F~;=BI0j=Fb4>pp zv}0R^xM4eti~+^+gE$6b81p(kwzuDti(-K9bc|?+pJEl@H+jSYuxZQV8rl8 zjp@M{#%qItIUFN~KcO9Hed*`$5A-2~pAo~K&<-Q+`9`$CK>rzqAI4w~$F%vs9s{~x zg4BP%Gy*@m?;D6=SRX?888Q6peF@_4Z->8wAH~Cn!R$|Hhq2cIzFYqT_+cDourHbY z0qroxJnrZ4Gh+Ay+F`_c%+KRT>y3qw{)89?=hJ@=KO=@ep)aBJ$c!JHfBMJpsP*3G za7|)VJJ8B;4?n{~ldJF7%jmb`-ftIvNd~ekoufG(`K(3=LNc;HBY& z(lp#q8XAD#cIf}k49zX_i`*fO+#!zKA&%T3j@%)R+#yag067CU%yUEe47>wzGU8^` z1EXFT^@I!{J!F8!X?S6ph8J=gUi5tl93*W>7}_uR<2N2~e}FaG?}KPyugQ=-OGEZs z!GBoyYY+H*ANn4?Z)X4l+7H%`17i5~zRlRIX?t)6_eu=g2Q`3WBhxSUeea+M-S?RL zX9oBGKn%a!H+*hx4d2(I!gsi+@SQK%<{X22M~2tMulJoa)0*+z9=-YO+;DFEm5eE1U9b^B(Z}2^9!Qk`!A$wUE z7$Ar5?NRg2&G!AZqnmE64eh^Anss3i!{}%6@Et+4rr!=}!SBF8eZ2*J3ujCWbl;3; z48H~goPSv(8X61fKKdpP!Z7$88NL^Z?j`!^*I?-P4X^pMxyWz~@$(UeAcTSDd(`vO z{~rc;9|GfMJcApU3k}22a!&)k4{CU!e_ny^Y3cO;tOvOMKEyWz!vG(Kp*;hB?d|R3`2X~=5a6#^o5@qn?J-bI8Ppip{-yG z!k|VcGsq!jF~}7DMr49Wap-s&>o=U^T0!Lcy}!(bhtYsPQy z4|EJe{12QL#=c(suQ89Mhw9<`bui%nx7Nep`C&*M3~vMEACmcRYYRGtANq$F%zh&V zc)cEVeHz*Z1N)L7k-(k3np#{GcDh2Q@ya0YHl*n7fl*ZPAsbU-a94MYYtA#&!c`xGIaV;yzsmrjfieTEtqB_WgZp2*NplHx=$O{M~2#i_vJ{ps-NgK zQsxKK_CBM2PP_je+Xft`(vYfXXgIUr{=PA=7a8`2EHk)Ym2QKIforz# tySWtj{oF3N9@_;i*Fv5S)9x^z=nlWP>jpp-9)52ZmLVA=i*%6g{{fxOO~wEK literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/runner.exe.manifest b/packages/local_auth/local_auth_windows/example/windows/runner/runner.exe.manifest new file mode 100644 index 000000000000..c977c4a42589 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/utils.cpp b/packages/local_auth/local_auth_windows/example/windows/runner/utils.cpp new file mode 100644 index 000000000000..fb7e945a63b7 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/utils.cpp @@ -0,0 +1,67 @@ +// 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. + +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE* unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = + ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, + nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/utils.h b/packages/local_auth/local_auth_windows/example/windows/runner/utils.h new file mode 100644 index 000000000000..bd81e1e02338 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/utils.h @@ -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. + +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.cpp b/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.cpp new file mode 100644 index 000000000000..85aa3614e8ad --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.cpp @@ -0,0 +1,241 @@ +// 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. + +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { ++g_active_window_count; } + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { return window_handle_; } + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.h b/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.h new file mode 100644 index 000000000000..d2a730052223 --- /dev/null +++ b/packages/local_auth/local_auth_windows/example/windows/runner/win32_window.h @@ -0,0 +1,99 @@ +// 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. + +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/local_auth/local_auth_windows/lib/local_auth_windows.dart b/packages/local_auth/local_auth_windows/lib/local_auth_windows.dart new file mode 100644 index 000000000000..1d65e81050f1 --- /dev/null +++ b/packages/local_auth/local_auth_windows/lib/local_auth_windows.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/services.dart'; +import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_windows/types/auth_messages_windows.dart'; + +export 'package:local_auth_platform_interface/types/auth_messages.dart'; +export 'package:local_auth_platform_interface/types/auth_options.dart'; +export 'package:local_auth_platform_interface/types/biometric_type.dart'; +export 'package:local_auth_windows/types/auth_messages_windows.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.io/local_auth_windows'); + +/// The implementation of [LocalAuthPlatform] for Windows. +class LocalAuthWindows extends LocalAuthPlatform { + /// Registers this class as the default instance of [LocalAuthPlatform]. + static void registerWith() { + LocalAuthPlatform.instance = LocalAuthWindows(); + } + + @override + Future authenticate({ + required String localizedReason, + required Iterable authMessages, + AuthenticationOptions options = const AuthenticationOptions(), + }) async { + assert(localizedReason.isNotEmpty); + final Map args = { + 'localizedReason': localizedReason, + 'useErrorDialogs': options.useErrorDialogs, + 'stickyAuth': options.stickyAuth, + 'sensitiveTransaction': options.sensitiveTransaction, + 'biometricOnly': options.biometricOnly, + }; + args.addAll(const WindowsAuthMessages().args); + for (final AuthMessages messages in authMessages) { + if (messages is WindowsAuthMessages) { + args.addAll(messages.args); + } + } + return (await _channel.invokeMethod('authenticate', args)) ?? false; + } + + @override + Future deviceSupportsBiometrics() async { + return (await _channel.invokeMethod('deviceSupportsBiometrics')) ?? + false; + } + + @override + Future> getEnrolledBiometrics() async { + final List result = (await _channel.invokeListMethod( + 'getEnrolledBiometrics', + )) ?? + []; + final List biometrics = []; + for (final String value in result) { + switch (value) { + case 'weak': + biometrics.add(BiometricType.weak); + break; + case 'strong': + biometrics.add(BiometricType.strong); + break; + } + } + return biometrics; + } + + @override + Future isDeviceSupported() async => + (await _channel.invokeMethod('isDeviceSupported')) ?? false; + + /// Always returns false as this method is not supported on Windows. + @override + Future stopAuthentication() async { + return false; + } +} diff --git a/packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart b/packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart new file mode 100644 index 000000000000..e47e8737153c --- /dev/null +++ b/packages/local_auth/local_auth_windows/lib/types/auth_messages_windows.dart @@ -0,0 +1,22 @@ +// 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:local_auth_platform_interface/types/auth_messages.dart'; + +/// Windows side authentication messages. +/// +/// Provides default values for all messages. +/// +/// Currently unused. +@immutable +class WindowsAuthMessages extends AuthMessages { + /// Constructs a new instance. + const WindowsAuthMessages(); + + @override + Map get args { + return {}; + } +} diff --git a/packages/local_auth/local_auth_windows/pubspec.yaml b/packages/local_auth/local_auth_windows/pubspec.yaml new file mode 100644 index 000000000000..9edeffbb6530 --- /dev/null +++ b/packages/local_auth/local_auth_windows/pubspec.yaml @@ -0,0 +1,26 @@ +name: local_auth_windows +description: Windows implementation of the local_auth plugin. +repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_windows +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 +version: 1.0.0 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +flutter: + plugin: + implements: local_auth + platforms: + windows: + pluginClass: LocalAuthPlugin + dartPluginClass: LocalAuthWindows + +dependencies: + flutter: + sdk: flutter + local_auth_platform_interface: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter \ No newline at end of file diff --git a/packages/local_auth/local_auth_windows/test/local_auth_test.dart b/packages/local_auth/local_auth_windows/test/local_auth_test.dart new file mode 100644 index 000000000000..b11c19e7b339 --- /dev/null +++ b/packages/local_auth/local_auth_windows/test/local_auth_test.dart @@ -0,0 +1,79 @@ +// 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:flutter_test/flutter_test.dart'; +import 'package:local_auth_windows/local_auth_windows.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('authenticate', () { + const MethodChannel channel = MethodChannel( + 'plugins.flutter.io/local_auth_windows', + ); + + final List log = []; + late LocalAuthWindows localAuthentication; + + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) { + log.add(methodCall); + switch (methodCall.method) { + case 'getEnrolledBiometrics': + return Future>.value(['weak', 'strong']); + default: + return Future.value(true); + } + }); + localAuthentication = LocalAuthWindows(); + log.clear(); + }); + + test('authenticate with no arguments passes expected defaults', () async { + await localAuthentication.authenticate( + authMessages: [const WindowsAuthMessages()], + localizedReason: 'My localized reason'); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'My localized reason', + 'useErrorDialogs': true, + 'stickyAuth': false, + 'sensitiveTransaction': true, + 'biometricOnly': false, + }..addAll(const WindowsAuthMessages().args)), + ], + ); + }); + + test('authenticate passes all options.', () async { + await localAuthentication.authenticate( + authMessages: [const WindowsAuthMessages()], + localizedReason: 'My localized reason', + options: const AuthenticationOptions( + useErrorDialogs: false, + stickyAuth: true, + sensitiveTransaction: false, + biometricOnly: true, + ), + ); + expect( + log, + [ + isMethodCall('authenticate', + arguments: { + 'localizedReason': 'My localized reason', + 'useErrorDialogs': false, + 'stickyAuth': true, + 'sensitiveTransaction': false, + 'biometricOnly': true, + }..addAll(const WindowsAuthMessages().args)), + ], + ); + }); + }); +} diff --git a/packages/local_auth/local_auth_windows/windows/CMakeLists.txt b/packages/local_auth/local_auth_windows/windows/CMakeLists.txt new file mode 100644 index 000000000000..bcf59bb827c7 --- /dev/null +++ b/packages/local_auth/local_auth_windows/windows/CMakeLists.txt @@ -0,0 +1,120 @@ +cmake_minimum_required(VERSION 3.15) +set(PROJECT_NAME "local_auth_windows") +set(WIL_VERSION "1.0.220201.1") +set(CPPWINRT_VERSION "2.0.220418.1") +project(${PROJECT_NAME} LANGUAGES CXX) +include(FetchContent) + +set(PLUGIN_NAME "${PROJECT_NAME}_plugin") + +FetchContent_Declare(nuget + URL "https://dist.nuget.org/win-x86-commandline/v6.0.0/nuget.exe" + URL_HASH SHA256=04eb6c4fe4213907e2773e1be1bbbd730e9a655a3c9c58387ce8d4a714a5b9e1 + DOWNLOAD_NO_EXTRACT true +) + +find_program(NUGET nuget) +if (NOT NUGET) + message("Nuget.exe not found, trying to download or use cached version.") + FetchContent_MakeAvailable(nuget) + set(NUGET ${nuget_SOURCE_DIR}/nuget.exe) +endif() + +execute_process(COMMAND + ${NUGET} install Microsoft.Windows.ImplementationLibrary -Version ${WIL_VERSION} -OutputDirectory ${CMAKE_BINARY_DIR}/packages + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE ret) +if (NOT ret EQUAL 0) + message(FATAL_ERROR "Failed to install nuget package Microsoft.Windows.ImplementationLibrary.${WIL_VERSION}") +endif() + +execute_process(COMMAND + ${NUGET} install Microsoft.Windows.CppWinRT -Version ${CPPWINRT_VERSION} -OutputDirectory packages + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE ret) +if (NOT ret EQUAL 0) + message(FATAL_ERROR "Failed to install nuget package Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}") +endif() + +set(CPPWINRT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}/bin/cppwinrt.exe) +execute_process(COMMAND + ${CPPWINRT} -input sdk -output include + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE ret) +if (NOT ret EQUAL 0) + message(FATAL_ERROR "Failed to run cppwinrt.exe") +endif() + +include_directories(BEFORE SYSTEM ${CMAKE_BINARY_DIR}/include) + +list(APPEND PLUGIN_SOURCES + "local_auth_plugin.cpp" +) + +add_library(${PLUGIN_NAME} SHARED + "include/local_auth_windows/local_auth_plugin.h" + "local_auth_windows.cpp" + "local_auth.h" + ${PLUGIN_SOURCES} +) +apply_standard_settings(${PLUGIN_NAME}) +set_target_properties(${PLUGIN_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden) +target_compile_features(${PLUGIN_NAME} PRIVATE cxx_std_20) +target_compile_options(${PLUGIN_NAME} PRIVATE /await) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.ImplementationLibrary.${WIL_VERSION}/build/native/Microsoft.Windows.ImplementationLibrary.targets) +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin windowsapp) + +# List of absolute paths to libraries that should be bundled with the plugin +set(file_chooser_bundled_libraries + "" + PARENT_SCOPE +) + + +# === Tests === + +if (${include_${PROJECT_NAME}_tests}) +set(TEST_RUNNER "${PROJECT_NAME}_test") +enable_testing() +# TODO(stuartmorgan): Consider using a single shared, pre-checked-in googletest +# instance rather than downloading for each plugin. This approach makes sense +# for a template, but not for a monorepo with many plugins. +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/release-1.11.0.zip +) +# Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +# Disable install commands for gtest so it doesn't end up in the bundle. +set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE) + +FetchContent_MakeAvailable(googletest) + +# The plugin's C API is not very useful for unit testing, so build the sources +# directly into the test binary rather than using the DLL. +add_executable(${TEST_RUNNER} + test/mocks.h + test/local_auth_plugin_test.cpp + ${PLUGIN_SOURCES} +) +apply_standard_settings(${TEST_RUNNER}) +target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") +target_compile_features(${TEST_RUNNER} PRIVATE cxx_std_20) +target_compile_options(${TEST_RUNNER} PRIVATE /await) +target_link_libraries(${TEST_RUNNER} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.ImplementationLibrary.${WIL_VERSION}/build/native/Microsoft.Windows.ImplementationLibrary.targets) +target_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin) +target_link_libraries(${TEST_RUNNER} PRIVATE windowsapp) +target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock) + +# flutter_wrapper_plugin has link dependencies on the Flutter DLL. +add_custom_command(TARGET ${TEST_RUNNER} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${FLUTTER_LIBRARY}" $ +) + +include(GoogleTest) +gtest_discover_tests(${TEST_RUNNER}) +endif() diff --git a/packages/local_auth/local_auth_windows/windows/include/local_auth_windows/local_auth_plugin.h b/packages/local_auth/local_auth_windows/windows/include/local_auth_windows/local_auth_plugin.h new file mode 100644 index 000000000000..0604de8ee2bb --- /dev/null +++ b/packages/local_auth/local_auth_windows/windows/include/local_auth_windows/local_auth_plugin.h @@ -0,0 +1,26 @@ +// 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. +#ifndef FLUTTER_PLUGIN_LOCAL_AUTH_WINDOWS_PLUGIN_H_ +#define FLUTTER_PLUGIN_LOCAL_AUTH_WINDOWS_PLUGIN_H_ + +#include + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) +#else +#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +FLUTTER_PLUGIN_EXPORT void LocalAuthPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_PLUGIN_LOCAL_AUTH_WINDOWS_PLUGIN_H_ diff --git a/packages/local_auth/local_auth_windows/windows/local_auth.h b/packages/local_auth/local_auth_windows/windows/local_auth.h new file mode 100644 index 000000000000..94b91f88345a --- /dev/null +++ b/packages/local_auth/local_auth_windows/windows/local_auth.h @@ -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. + +#include +#include +#include +#include +#include +#include +#include + +#include "include/local_auth_windows/local_auth_plugin.h" + +// Include prior to C++/WinRT Headers +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace local_auth_windows { + +// Abstract class that is used to determine whether a user +// has given consent to a particular action, and if the system +// supports asking this question. +class UserConsentVerifier { + public: + UserConsentVerifier() {} + virtual ~UserConsentVerifier() = default; + + // Abstract method that request the user's verification + // given the provided reason. + virtual winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI::UserConsentVerificationResult> + RequestVerificationForWindowAsync(std::wstring localized_reason) = 0; + + // Abstract method that returns weather the system supports Windows Hello. + virtual winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> + CheckAvailabilityAsync() = 0; + + // Disallow copy and move. + UserConsentVerifier(const UserConsentVerifier&) = delete; + UserConsentVerifier& operator=(const UserConsentVerifier&) = delete; +}; + +class LocalAuthPlugin : public flutter::Plugin { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); + + // Creates a plugin instance that will create the dialog and associate + // it with the HWND returned from the provided function. + LocalAuthPlugin(std::function window_provider); + + // Creates a plugin instance with the given UserConsentVerifier instance. + // Exists for unit testing with mock implementations. + LocalAuthPlugin(std::unique_ptr user_consent_verifier); + + // Handles method calls from Dart on this plugin's channel. + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + + virtual ~LocalAuthPlugin(); + + private: + std::unique_ptr user_consent_verifier_; + + // Starts authentication process. + winrt::fire_and_forget Authenticate( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + + // Returns enrolled biometric types available on device. + winrt::fire_and_forget GetEnrolledBiometrics( + std::unique_ptr> result); + + // Returns whether the system supports Windows Hello. + winrt::fire_and_forget IsDeviceSupported( + std::unique_ptr> result); +}; + +} // namespace local_auth_windows \ No newline at end of file diff --git a/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp b/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp new file mode 100644 index 000000000000..7a25abb53010 --- /dev/null +++ b/packages/local_auth/local_auth_windows/windows/local_auth_plugin.cpp @@ -0,0 +1,236 @@ +// 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. +#include + +#include "local_auth.h" + +namespace { + +template +// Helper method for getting an argument from an EncodableValue. +T GetArgument(const std::string arg, const flutter::EncodableValue* args, + T fallback) { + T result{fallback}; + const auto* arguments = std::get_if(args); + if (arguments) { + auto result_it = arguments->find(flutter::EncodableValue(arg)); + if (result_it != arguments->end()) { + result = std::get(result_it->second); + } + } + return result; +} + +// Returns the window's HWND for a given FlutterView. +HWND GetRootWindow(flutter::FlutterView* view) { + return ::GetAncestor(view->GetNativeWindow(), GA_ROOT); +} + +// Converts the given UTF-8 string to UTF-16. +std::wstring Utf16FromUtf8(const std::string& utf8_string) { + if (utf8_string.empty()) { + return std::wstring(); + } + int target_length = + ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(), + static_cast(utf8_string.length()), nullptr, 0); + if (target_length == 0) { + return std::wstring(); + } + std::wstring utf16_string; + utf16_string.resize(target_length); + int converted_length = + ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(), + static_cast(utf8_string.length()), + utf16_string.data(), target_length); + if (converted_length == 0) { + return std::wstring(); + } + return utf16_string; +} + +} // namespace + +namespace local_auth_windows { + +// Creates an instance of the UserConsentVerifier that +// calls the native Windows APIs to get the user's consent. +class UserConsentVerifierImpl : public UserConsentVerifier { + public: + explicit UserConsentVerifierImpl(std::function window_provider) + : get_root_window_(std::move(window_provider)){}; + virtual ~UserConsentVerifierImpl() = default; + + // Calls the native Windows API to get the user's consent + // with the provided reason. + winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI::UserConsentVerificationResult> + RequestVerificationForWindowAsync(std::wstring localized_reason) override { + winrt::impl::com_ref + user_consent_verifier_interop = winrt::get_activation_factory< + winrt::Windows::Security::Credentials::UI::UserConsentVerifier, + IUserConsentVerifierInterop>(); + + HWND root_window_handle = get_root_window_(); + + auto reason = wil::make_unique_string( + localized_reason.c_str(), localized_reason.size()); + + winrt::Windows::Security::Credentials::UI::UserConsentVerificationResult + consent_result = co_await winrt::capture< + winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult>>( + user_consent_verifier_interop, + &::IUserConsentVerifierInterop::RequestVerificationForWindowAsync, + root_window_handle, reason.get()); + + return consent_result; + } + + // Calls the native Windows API to check for the Windows Hello availability. + winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> + CheckAvailabilityAsync() override { + return winrt::Windows::Security::Credentials::UI::UserConsentVerifier:: + CheckAvailabilityAsync(); + } + + // Disallow copy and move. + UserConsentVerifierImpl(const UserConsentVerifierImpl&) = delete; + UserConsentVerifierImpl& operator=(const UserConsentVerifierImpl&) = delete; + + private: + // The provider for the root window to attach the dialog to. + std::function get_root_window_; +}; + +// static +void LocalAuthPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows* registrar) { + auto channel = + std::make_unique>( + registrar->messenger(), "plugins.flutter.io/local_auth_windows", + &flutter::StandardMethodCodec::GetInstance()); + + auto plugin = std::make_unique( + [registrar]() { return GetRootWindow(registrar->GetView()); }); + + channel->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto& call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + registrar->AddPlugin(std::move(plugin)); +} + +// Default constructor for LocalAuthPlugin. +LocalAuthPlugin::LocalAuthPlugin(std::function window_provider) + : user_consent_verifier_(std::make_unique( + std::move(window_provider))) {} + +LocalAuthPlugin::LocalAuthPlugin( + std::unique_ptr user_consent_verifier) + : user_consent_verifier_(std::move(user_consent_verifier)) {} + +LocalAuthPlugin::~LocalAuthPlugin() {} + +void LocalAuthPlugin::HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result) { + if (method_call.method_name().compare("authenticate") == 0) { + Authenticate(method_call, std::move(result)); + } else if (method_call.method_name().compare("getEnrolledBiometrics") == 0) { + GetEnrolledBiometrics(std::move(result)); + } else if (method_call.method_name().compare("isDeviceSupported") == 0 || + method_call.method_name().compare("deviceSupportsBiometrics") == + 0) { + IsDeviceSupported(std::move(result)); + } else { + result->NotImplemented(); + } +} + +// Starts authentication process. +winrt::fire_and_forget LocalAuthPlugin::Authenticate( + const flutter::MethodCall& method_call, + std::unique_ptr> result) { + std::wstring reason = Utf16FromUtf8(GetArgument( + "localizedReason", method_call.arguments(), std::string())); + + bool biometric_only = + GetArgument("biometricOnly", method_call.arguments(), false); + if (biometric_only) { + result->Error("biometricOnlyNotSupported", + "Windows doesn't support the biometricOnly parameter."); + co_return; + } + + winrt::Windows::Security::Credentials::UI::UserConsentVerifierAvailability + ucv_availability = + co_await user_consent_verifier_->CheckAvailabilityAsync(); + + if (ucv_availability == + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::DeviceNotPresent) { + result->Error("NoHardware", "No biometric hardware found"); + co_return; + } else if (ucv_availability == + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::NotConfiguredForUser) { + result->Error("NotEnrolled", "No biometrics enrolled on this device."); + co_return; + } else if (ucv_availability != + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available) { + result->Error("NotAvailable", "Required security features not enabled"); + co_return; + } + + try { + winrt::Windows::Security::Credentials::UI::UserConsentVerificationResult + consent_result = + co_await user_consent_verifier_->RequestVerificationForWindowAsync( + reason); + + result->Success(flutter::EncodableValue( + consent_result == winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult::Verified)); + } catch (...) { + result->Success(flutter::EncodableValue(false)); + } +} + +// Returns biometric types available on device. +winrt::fire_and_forget LocalAuthPlugin::GetEnrolledBiometrics( + std::unique_ptr> result) { + try { + flutter::EncodableList biometrics; + winrt::Windows::Security::Credentials::UI::UserConsentVerifierAvailability + ucv_availability = + co_await user_consent_verifier_->CheckAvailabilityAsync(); + if (ucv_availability == winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available) { + biometrics.push_back(flutter::EncodableValue("weak")); + biometrics.push_back(flutter::EncodableValue("strong")); + } + result->Success(biometrics); + } catch (const std::exception& e) { + result->Error("no_biometrics_available", e.what()); + } +} + +// Returns whether the device supports Windows Hello or not. +winrt::fire_and_forget LocalAuthPlugin::IsDeviceSupported( + std::unique_ptr> result) { + winrt::Windows::Security::Credentials::UI::UserConsentVerifierAvailability + ucv_availability = + co_await user_consent_verifier_->CheckAvailabilityAsync(); + result->Success(flutter::EncodableValue( + ucv_availability == winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available)); +} + +} // namespace local_auth_windows diff --git a/packages/local_auth/local_auth_windows/windows/local_auth_windows.cpp b/packages/local_auth/local_auth_windows/windows/local_auth_windows.cpp new file mode 100644 index 000000000000..6e5e6a186afb --- /dev/null +++ b/packages/local_auth/local_auth_windows/windows/local_auth_windows.cpp @@ -0,0 +1,15 @@ +// 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. + +#include + +#include "include/local_auth_windows/local_auth_plugin.h" +#include "local_auth.h" + +void LocalAuthPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + local_auth_windows::LocalAuthPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +} diff --git a/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp b/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp new file mode 100644 index 000000000000..3828b05eef07 --- /dev/null +++ b/packages/local_auth/local_auth_windows/windows/test/local_auth_plugin_test.cpp @@ -0,0 +1,253 @@ +// 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. + +#include "include/local_auth_windows/local_auth_plugin.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mocks.h" + +namespace local_auth_windows { +namespace test { + +using flutter::EncodableList; +using flutter::EncodableMap; +using flutter::EncodableValue; +using ::testing::_; +using ::testing::DoAll; +using ::testing::EndsWith; +using ::testing::Eq; +using ::testing::Pointee; +using ::testing::Return; + +TEST(LocalAuthPlugin, IsDeviceSupportedHandlerSuccessIfVerifierAvailable) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) + .Times(1) + .WillOnce([]() -> winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> { + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available; + }); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(true)))); + + plugin.HandleMethodCall( + flutter::MethodCall("isDeviceSupported", + std::make_unique()), + std::move(result)); +} + +TEST(LocalAuthPlugin, IsDeviceSupportedHandlerSuccessIfVerifierNotAvailable) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) + .Times(1) + .WillOnce([]() -> winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> { + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::DeviceNotPresent; + }); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(false)))); + + plugin.HandleMethodCall( + flutter::MethodCall("isDeviceSupported", + std::make_unique()), + std::move(result)); +} + +TEST(LocalAuthPlugin, + GetEnrolledBiometricsHandlerReturnEmptyListIfVerifierNotAvailable) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) + .Times(1) + .WillOnce([]() -> winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> { + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::DeviceNotPresent; + }); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableList()))); + + plugin.HandleMethodCall( + flutter::MethodCall("getEnrolledBiometrics", + std::make_unique()), + std::move(result)); +} + +TEST(LocalAuthPlugin, + GetEnrolledBiometricsHandlerReturnNonEmptyListIfVerifierAvailable) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) + .Times(1) + .WillOnce([]() -> winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> { + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available; + }); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, + SuccessInternal(Pointee(EncodableList( + {EncodableValue("weak"), EncodableValue("strong")})))); + + plugin.HandleMethodCall( + flutter::MethodCall("getEnrolledBiometrics", + std::make_unique()), + std::move(result)); +} + +TEST(LocalAuthPlugin, AuthenticateHandlerDoesNotSupportBiometricOnly) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(1); + EXPECT_CALL(*result, SuccessInternal).Times(0); + + std::unique_ptr args = + std::make_unique(EncodableMap({ + {"localizedReason", EncodableValue("My Reason")}, + {"biometricOnly", EncodableValue(true)}, + })); + + plugin.HandleMethodCall(flutter::MethodCall("authenticate", std::move(args)), + std::move(result)); +} + +TEST(LocalAuthPlugin, AuthenticateHandlerWorksWhenAuthorized) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) + .Times(1) + .WillOnce([]() -> winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> { + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available; + }); + + EXPECT_CALL(*mockConsentVerifier, RequestVerificationForWindowAsync) + .Times(1) + .WillOnce([](std::wstring localizedReason) + -> winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult> { + EXPECT_EQ(localizedReason, L"My Reason"); + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult::Verified; + }); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(true)))); + + std::unique_ptr args = + std::make_unique(EncodableMap({ + {"localizedReason", EncodableValue("My Reason")}, + {"biometricOnly", EncodableValue(false)}, + })); + + plugin.HandleMethodCall(flutter::MethodCall("authenticate", std::move(args)), + std::move(result)); +} + +TEST(LocalAuthPlugin, AuthenticateHandlerWorksWhenNotAuthorized) { + std::unique_ptr result = + std::make_unique(); + + std::unique_ptr mockConsentVerifier = + std::make_unique(); + + EXPECT_CALL(*mockConsentVerifier, CheckAvailabilityAsync) + .Times(1) + .WillOnce([]() -> winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability> { + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability::Available; + }); + + EXPECT_CALL(*mockConsentVerifier, RequestVerificationForWindowAsync) + .Times(1) + .WillOnce([](std::wstring localizedReason) + -> winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult> { + EXPECT_EQ(localizedReason, L"My Reason"); + co_return winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult::Canceled; + }); + + LocalAuthPlugin plugin(std::move(mockConsentVerifier)); + + EXPECT_CALL(*result, ErrorInternal).Times(0); + EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(false)))); + + std::unique_ptr args = + std::make_unique(EncodableMap({ + {"localizedReason", EncodableValue("My Reason")}, + {"biometricOnly", EncodableValue(false)}, + })); + + plugin.HandleMethodCall(flutter::MethodCall("authenticate", std::move(args)), + std::move(result)); +} + +} // namespace test +} // namespace local_auth_windows diff --git a/packages/local_auth/local_auth_windows/windows/test/mocks.h b/packages/local_auth/local_auth_windows/windows/test/mocks.h new file mode 100644 index 000000000000..d82ae801b4b9 --- /dev/null +++ b/packages/local_auth/local_auth_windows/windows/test/mocks.h @@ -0,0 +1,63 @@ +// 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. + +#ifndef PACKAGES_LOCAL_AUTH_LOCAL_AUTH_WINDOWS_WINDOWS_TEST_MOCKS_H_ +#define PACKAGES_LOCAL_AUTH_LOCAL_AUTH_WINDOWS_WINDOWS_TEST_MOCKS_H_ + +#include +#include +#include +#include +#include +#include + +#include "../local_auth.h" + +namespace local_auth_windows { +namespace test { + +namespace { + +using flutter::EncodableMap; +using flutter::EncodableValue; +using ::testing::_; + +class MockMethodResult : public flutter::MethodResult<> { + public: + ~MockMethodResult() = default; + + MOCK_METHOD(void, SuccessInternal, (const EncodableValue* result), + (override)); + MOCK_METHOD(void, ErrorInternal, + (const std::string& error_code, const std::string& error_message, + const EncodableValue* details), + (override)); + MOCK_METHOD(void, NotImplementedInternal, (), (override)); +}; + +class MockUserConsentVerifier : public UserConsentVerifier { + public: + explicit MockUserConsentVerifier(){}; + virtual ~MockUserConsentVerifier() = default; + + MOCK_METHOD(winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerificationResult>, + RequestVerificationForWindowAsync, (std::wstring localizedReason), + (override)); + MOCK_METHOD(winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Security::Credentials::UI:: + UserConsentVerifierAvailability>, + CheckAvailabilityAsync, (), (override)); + + // Disallow copy and move. + MockUserConsentVerifier(const MockUserConsentVerifier&) = delete; + MockUserConsentVerifier& operator=(const MockUserConsentVerifier&) = delete; +}; + +} // namespace +} // namespace test +} // namespace local_auth_windows + +#endif // PACKAGES_LOCAL_AUTH_LOCAL_AUTH_WINDOWS_WINDOWS_TEST_MOCKS_H_ From d436a7c52f9f9235d60fb36f0a962acb975505c5 Mon Sep 17 00:00:00 2001 From: Hwanseok Barth Kang Date: Wed, 18 May 2022 02:07:13 +0900 Subject: [PATCH 319/844] [google_sign_in_platform_interface] Add availability to mock models (#5669) --- .../CHANGELOG.md | 1 + .../lib/src/types.dart | 4 +- ...oogle_sign_in_platform_interface_test.dart | 38 +++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md index abf01c847d83..e78060d84744 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ ## 2.1.3 +* Enables mocking models by changing overridden operator == parameter type from `dynamic` to `Object`. * Removes unnecessary imports. * Adds `SignInInitParameters` class to hold all sign in params, including the new `forceCodeForRefreshToken`. diff --git a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart index 2cac5e886729..e1f455d6ea44 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart @@ -116,7 +116,7 @@ class GoogleSignInUserData { @override // TODO(stuartmorgan): Make this class immutable in the next breaking change. // ignore: avoid_equals_and_hash_code_on_mutable_classes - bool operator ==(dynamic other) { + bool operator ==(Object other) { if (identical(this, other)) { return true; } @@ -159,7 +159,7 @@ class GoogleSignInTokenData { @override // TODO(stuartmorgan): Make this class immutable in the next breaking change. // ignore: avoid_equals_and_hash_code_on_mutable_classes - bool operator ==(dynamic other) { + bool operator ==(Object other) { if (identical(this, other)) { return true; } diff --git a/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart b/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart index 78e57a3eb2ac..bf960abc7375 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart @@ -29,6 +29,44 @@ void main() { GoogleSignInPlatform.instance = ImplementsWithIsMock(); }); }); + + group('GoogleSignInTokenData', () { + test('can be compared by == operator', () { + final GoogleSignInTokenData firstInstance = GoogleSignInTokenData( + accessToken: 'accessToken', + idToken: 'idToken', + serverAuthCode: 'serverAuthCode', + ); + final GoogleSignInTokenData secondInstance = GoogleSignInTokenData( + accessToken: 'accessToken', + idToken: 'idToken', + serverAuthCode: 'serverAuthCode', + ); + expect(firstInstance == secondInstance, isTrue); + }); + }); + + group('GoogleSignInUserData', () { + test('can be compared by == operator', () { + final GoogleSignInUserData firstInstance = GoogleSignInUserData( + email: 'email', + id: 'id', + displayName: 'displayName', + photoUrl: 'photoUrl', + idToken: 'idToken', + serverAuthCode: 'serverAuthCode', + ); + final GoogleSignInUserData secondInstance = GoogleSignInUserData( + email: 'email', + id: 'id', + displayName: 'displayName', + photoUrl: 'photoUrl', + idToken: 'idToken', + serverAuthCode: 'serverAuthCode', + ); + expect(firstInstance == secondInstance, isTrue); + }); + }); } class ImplementsWithIsMock extends Mock implements GoogleSignInPlatform { From ad618d9ea8c84150b719a4510c79eb4faa845be5 Mon Sep 17 00:00:00 2001 From: godofredoc Date: Tue, 17 May 2022 11:07:16 -0700 Subject: [PATCH 320/844] Update cirrus secret. (#5774) --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index eaaac8d7b5b5..c4abdbc5adc1 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,4 +1,4 @@ -gcp_credentials: ENCRYPTED[!2c88dee9c9d9805b214c9f7ad8f3bc8fae936cdb0f881d562101151c408c7e024a41222677d5831df90c60d2dd6cd80a!] +gcp_credentials: ENCRYPTED[!ebad0a1f4f7a446b77944c33651460a7ab010b4617273cb016cf354eb8fc22aa92e37a3c58bfa4a0c40a799351e027a6!] # Don't run on release tags since it creates O(n^2) tasks where n is the # number of plugins. From f25fc10f93018ee15a4906f91ff314ae129c3223 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 17 May 2022 14:57:14 -0400 Subject: [PATCH 321/844] Add more Android plugin owners (#5624) --- CODEOWNERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CODEOWNERS b/CODEOWNERS index bc3e392d1766..26a2a2bc1602 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -18,6 +18,8 @@ packages/**/*_web/** @ditman # - Android packages/camera/camera/android/** @camsim99 +packages/espresso/** @blasten +packages/flutter_plugin_android_lifecycle/** @blasten packages/google_maps_flutter/google_maps_flutter/android/** @GaryQian packages/google_sign_in/google_sign_in_android/** @camsim99 packages/image_picker/image_picker_android/** @GaryQian From 94201eaeb51a44b63fd63b36ccbbb29600538b82 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 17 May 2022 16:02:11 -0400 Subject: [PATCH 322/844] [google_maps_flutter] Fix native unit tests on M1 (#5772) --- packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md | 1 + .../example/ios/Runner.xcodeproj/project.pbxproj | 2 ++ 2 files changed, 3 insertions(+) diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 07ffbfdb4e5d..57d24e2196a5 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Fixes iOS native unit tests on M1 devices. * Minor fixes for new analysis options. ## 2.1.5 diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj index 6a0466c3c6d9..a8d37106bb83 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -651,6 +651,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; INFOPLIST_FILE = RunnerTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_FAST_MATH = YES; @@ -666,6 +667,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; INFOPLIST_FILE = RunnerTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_FAST_MATH = YES; From 1ee23b92c90f4eccc5a5290f80a0fe063db8922e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 May 2022 13:52:13 -0700 Subject: [PATCH 323/844] [video_player]: Bump exoplayer from 2.17.0 to 2.17.1 in /packages/video_player/video_player_android/android (#5579) --- packages/video_player/video_player_android/CHANGELOG.md | 4 ++++ .../video_player_android/android/build.gradle | 8 ++++---- packages/video_player/video_player_android/pubspec.yaml | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 08acba895eba..28d0188213d6 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.4 + +* Updates ExoPlayer to 2.17.1. + ## 2.3.3 * Removes unnecessary imports. diff --git a/packages/video_player/video_player_android/android/build.gradle b/packages/video_player/video_player_android/android/build.gradle index e565a9364bd8..32cc203729b8 100644 --- a/packages/video_player/video_player_android/android/build.gradle +++ b/packages/video_player/video_player_android/android/build.gradle @@ -43,10 +43,10 @@ android { } dependencies { - implementation 'com.google.android.exoplayer:exoplayer-core:2.17.0' - implementation 'com.google.android.exoplayer:exoplayer-hls:2.17.0' - implementation 'com.google.android.exoplayer:exoplayer-dash:2.17.0' - implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.17.0' + implementation 'com.google.android.exoplayer:exoplayer-core:2.17.1' + implementation 'com.google.android.exoplayer:exoplayer-hls:2.17.1' + implementation 'com.google.android.exoplayer:exoplayer-dash:2.17.1' + implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.17.1' testImplementation 'junit:junit:4.12' testImplementation 'org.mockito:mockito-inline:3.9.0' } diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index bc69fd41369a..7e23b3210859 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.3 +version: 2.3.4 environment: sdk: ">=2.14.0 <3.0.0" From af3a088d18ee21a03050d7592f5e78d662cb035d Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 17 May 2022 20:32:10 -0400 Subject: [PATCH 324/844] [tools] Validate example READMEs (#5775) --- packages/espresso/example/README.md | 2 +- .../file_selector/example/README.md | 5 - .../google_maps_flutter/example/README.md | 5 - .../google_sign_in/example/README.md | 5 - .../google_sign_in_android/example/README.md | 5 - .../google_sign_in_ios/example/README.md | 5 - .../image_picker/example/README.md | 5 - .../image_picker_android/example/README.md | 5 - .../image_picker_ios/example/README.md | 5 - .../image_picker_windows/example/README.md | 5 - .../ios_platform_images/example/README.md | 13 -- .../local_auth/local_auth/example/README.md | 5 - .../local_auth_android/example/README.md | 5 - .../local_auth_ios/example/README.md | 5 - .../path_provider/example/README.md | 5 - .../path_provider_android/example/README.md | 5 - .../path_provider_ios/example/README.md | 5 - .../path_provider_linux/example/README.md | 13 -- .../path_provider_macos/example/README.md | 5 - .../path_provider_windows/example/README.md | 5 - .../quick_actions/example/README.md | 5 - .../quick_actions_android/example/README.md | 5 - .../quick_actions_ios/example/README.md | 5 - .../shared_preferences/example/README.md | 5 - .../example/README.md | 5 - .../shared_preferences_ios/example/README.md | 5 - .../example/README.md | 5 - .../example/README.md | 5 - .../example/README.md | 13 -- .../url_launcher/example/README.md | 5 - .../url_launcher_android/example/README.md | 5 - .../url_launcher_ios/example/README.md | 5 - .../url_launcher_linux/example/README.md | 5 - .../url_launcher_macos/example/README.md | 5 - .../video_player/example/README.md | 5 - .../video_player_android/example/README.md | 5 - .../example/README.md | 5 - .../webview_flutter/example/README.md | 5 - .../webview_flutter_android/example/README.md | 5 - .../webview_flutter_web/example/README.md | 5 - .../example/README.md | 5 - script/tool/CHANGELOG.md | 2 + .../src/common/package_looping_command.dart | 43 +++- .../tool/lib/src/pubspec_check_command.dart | 3 +- script/tool/lib/src/readme_check_command.dart | 73 +++++-- script/tool/lib/src/test_command.dart | 3 +- .../common/package_looping_command_test.dart | 80 ++++++- script/tool/test/list_command_test.dart | 3 + .../tool/test/readme_check_command_test.dart | 200 +++++++++++++++--- script/tool/test/util.dart | 17 +- .../tool/test/version_check_command_test.dart | 44 ++-- 51 files changed, 373 insertions(+), 321 deletions(-) diff --git a/packages/espresso/example/README.md b/packages/espresso/example/README.md index 224544e9f83f..edb498a11338 100644 --- a/packages/espresso/example/README.md +++ b/packages/espresso/example/README.md @@ -8,7 +8,7 @@ The espresso package only runs tests on Android. The example runs on iOS, but th To run the Espresso tests: -``` +```java flutter build apk --debug ./gradlew app:connectedAndroidTest ``` diff --git a/packages/file_selector/file_selector/example/README.md b/packages/file_selector/file_selector/example/README.md index 93260dc716b2..e1dcf70473c9 100644 --- a/packages/file_selector/file_selector/example/README.md +++ b/packages/file_selector/file_selector/example/README.md @@ -1,8 +1,3 @@ # file_selector_example Demonstrates how to use the file_selector plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/google_maps_flutter/google_maps_flutter/example/README.md b/packages/google_maps_flutter/google_maps_flutter/example/README.md index b92b9c326143..c8852649b065 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/README.md +++ b/packages/google_maps_flutter/google_maps_flutter/example/README.md @@ -1,8 +1,3 @@ # google_maps_flutter_example Demonstrates how to use the google_maps_flutter plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/google_sign_in/google_sign_in/example/README.md b/packages/google_sign_in/google_sign_in/example/README.md index 0e246e11a8be..24fdb3ec042d 100644 --- a/packages/google_sign_in/google_sign_in/example/README.md +++ b/packages/google_sign_in/google_sign_in/example/README.md @@ -1,8 +1,3 @@ # google_sign_in_example Demonstrates how to use the google_sign_in plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/google_sign_in/google_sign_in_android/example/README.md b/packages/google_sign_in/google_sign_in_android/example/README.md index 79d99dc72982..8eb153eb8efd 100644 --- a/packages/google_sign_in/google_sign_in_android/example/README.md +++ b/packages/google_sign_in/google_sign_in_android/example/README.md @@ -1,8 +1,3 @@ # google_sign_in_android example Exercises the Android implementation of `GoogleSignInPlatform`. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/google_sign_in/google_sign_in_ios/example/README.md b/packages/google_sign_in/google_sign_in_ios/example/README.md index ca3dc7023fc9..04c3372dc3b0 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/README.md +++ b/packages/google_sign_in/google_sign_in_ios/example/README.md @@ -1,8 +1,3 @@ # google_sign_in_ios example Exercises the iOS implementation of `GoogleSignInPlatform`. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/image_picker/image_picker/example/README.md b/packages/image_picker/image_picker/example/README.md index 129aa856c8f2..18497eb11032 100755 --- a/packages/image_picker/image_picker/example/README.md +++ b/packages/image_picker/image_picker/example/README.md @@ -1,8 +1,3 @@ # image_picker_example Demonstrates how to use the image_picker plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/image_picker/image_picker_android/example/README.md b/packages/image_picker/image_picker_android/example/README.md index 86d5c23ba209..16b5c51839f8 100755 --- a/packages/image_picker/image_picker_android/example/README.md +++ b/packages/image_picker/image_picker_android/example/README.md @@ -1,8 +1,3 @@ # image_picker_example Demonstrates how to use the `image_picker` plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/image_picker/image_picker_ios/example/README.md b/packages/image_picker/image_picker_ios/example/README.md index 86d5c23ba209..16b5c51839f8 100755 --- a/packages/image_picker/image_picker_ios/example/README.md +++ b/packages/image_picker/image_picker_ios/example/README.md @@ -1,8 +1,3 @@ # image_picker_example Demonstrates how to use the `image_picker` plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/image_picker/image_picker_windows/example/README.md b/packages/image_picker/image_picker_windows/example/README.md index ae730a5ec846..7f61053c6e30 100644 --- a/packages/image_picker/image_picker_windows/example/README.md +++ b/packages/image_picker/image_picker_windows/example/README.md @@ -1,8 +1,3 @@ # image_picker_windows_example Demonstrates how to use the image_picker_windows plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/ios_platform_images/example/README.md b/packages/ios_platform_images/example/README.md index 2f34abc202f2..91fc3baf5f49 100644 --- a/packages/ios_platform_images/example/README.md +++ b/packages/ios_platform_images/example/README.md @@ -1,16 +1,3 @@ # ios_platform_images_example Demonstrates how to use the ios_platform_images plugin. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/packages/local_auth/local_auth/example/README.md b/packages/local_auth/local_auth/example/README.md index a4a6091c9ba6..bd004a77d86b 100644 --- a/packages/local_auth/local_auth/example/README.md +++ b/packages/local_auth/local_auth/example/README.md @@ -1,8 +1,3 @@ # local_auth_example Demonstrates how to use the local_auth plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/local_auth/local_auth_android/example/README.md b/packages/local_auth/local_auth_android/example/README.md index a4a6091c9ba6..bd004a77d86b 100644 --- a/packages/local_auth/local_auth_android/example/README.md +++ b/packages/local_auth/local_auth_android/example/README.md @@ -1,8 +1,3 @@ # local_auth_example Demonstrates how to use the local_auth plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/local_auth/local_auth_ios/example/README.md b/packages/local_auth/local_auth_ios/example/README.md index a4a6091c9ba6..bd004a77d86b 100644 --- a/packages/local_auth/local_auth_ios/example/README.md +++ b/packages/local_auth/local_auth_ios/example/README.md @@ -1,8 +1,3 @@ # local_auth_example Demonstrates how to use the local_auth plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/path_provider/path_provider/example/README.md b/packages/path_provider/path_provider/example/README.md index 1f8ea7189ccd..801f44409938 100644 --- a/packages/path_provider/path_provider/example/README.md +++ b/packages/path_provider/path_provider/example/README.md @@ -1,8 +1,3 @@ # path_provider_example Demonstrates how to use the path_provider plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/path_provider/path_provider_android/example/README.md b/packages/path_provider/path_provider_android/example/README.md index 1f8ea7189ccd..801f44409938 100644 --- a/packages/path_provider/path_provider_android/example/README.md +++ b/packages/path_provider/path_provider_android/example/README.md @@ -1,8 +1,3 @@ # path_provider_example Demonstrates how to use the path_provider plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/path_provider/path_provider_ios/example/README.md b/packages/path_provider/path_provider_ios/example/README.md index 1f8ea7189ccd..801f44409938 100644 --- a/packages/path_provider/path_provider_ios/example/README.md +++ b/packages/path_provider/path_provider_ios/example/README.md @@ -1,8 +1,3 @@ # path_provider_example Demonstrates how to use the path_provider plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/path_provider/path_provider_linux/example/README.md b/packages/path_provider/path_provider_linux/example/README.md index 751fe4b811f0..333d0f55cec7 100644 --- a/packages/path_provider/path_provider_linux/example/README.md +++ b/packages/path_provider/path_provider_linux/example/README.md @@ -1,16 +1,3 @@ # path_provider_linux_example Demonstrates how to use the path_provider_linux plugin. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/packages/path_provider/path_provider_macos/example/README.md b/packages/path_provider/path_provider_macos/example/README.md index 4f413873b346..158869595c01 100644 --- a/packages/path_provider/path_provider_macos/example/README.md +++ b/packages/path_provider/path_provider_macos/example/README.md @@ -1,8 +1,3 @@ # path_provider_macos_example Demonstrates how to use the path_provider_macos plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/path_provider/path_provider_windows/example/README.md b/packages/path_provider/path_provider_windows/example/README.md index 32f66a86d11d..63723991a2e9 100644 --- a/packages/path_provider/path_provider_windows/example/README.md +++ b/packages/path_provider/path_provider_windows/example/README.md @@ -1,8 +1,3 @@ # path_provider_windows_example Demonstrates how to use the path_provider_windows plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/quick_actions/quick_actions/example/README.md b/packages/quick_actions/quick_actions/example/README.md index d1b72891de9e..c8a629019fc9 100644 --- a/packages/quick_actions/quick_actions/example/README.md +++ b/packages/quick_actions/quick_actions/example/README.md @@ -1,8 +1,3 @@ # quick_actions_example Demonstrates how to use the quick_actions plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/quick_actions/quick_actions_android/example/README.md b/packages/quick_actions/quick_actions_android/example/README.md index d1b72891de9e..c8a629019fc9 100644 --- a/packages/quick_actions/quick_actions_android/example/README.md +++ b/packages/quick_actions/quick_actions_android/example/README.md @@ -1,8 +1,3 @@ # quick_actions_example Demonstrates how to use the quick_actions plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/quick_actions/quick_actions_ios/example/README.md b/packages/quick_actions/quick_actions_ios/example/README.md index d1b72891de9e..c8a629019fc9 100644 --- a/packages/quick_actions/quick_actions_ios/example/README.md +++ b/packages/quick_actions/quick_actions_ios/example/README.md @@ -1,8 +1,3 @@ # quick_actions_example Demonstrates how to use the quick_actions plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/shared_preferences/shared_preferences/example/README.md b/packages/shared_preferences/shared_preferences/example/README.md index 7dd9e9c4aa42..c060637c7ec5 100644 --- a/packages/shared_preferences/shared_preferences/example/README.md +++ b/packages/shared_preferences/shared_preferences/example/README.md @@ -1,8 +1,3 @@ # shared_preferences_example Demonstrates how to use the shared_preferences plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/shared_preferences/shared_preferences_android/example/README.md b/packages/shared_preferences/shared_preferences_android/example/README.md index 7dd9e9c4aa42..c060637c7ec5 100644 --- a/packages/shared_preferences/shared_preferences_android/example/README.md +++ b/packages/shared_preferences/shared_preferences_android/example/README.md @@ -1,8 +1,3 @@ # shared_preferences_example Demonstrates how to use the shared_preferences plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/shared_preferences/shared_preferences_ios/example/README.md b/packages/shared_preferences/shared_preferences_ios/example/README.md index 7dd9e9c4aa42..c060637c7ec5 100644 --- a/packages/shared_preferences/shared_preferences_ios/example/README.md +++ b/packages/shared_preferences/shared_preferences_ios/example/README.md @@ -1,8 +1,3 @@ # shared_preferences_example Demonstrates how to use the shared_preferences plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/shared_preferences/shared_preferences_linux/example/README.md b/packages/shared_preferences/shared_preferences_linux/example/README.md index 7dd9e9c4aa42..c060637c7ec5 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/README.md +++ b/packages/shared_preferences/shared_preferences_linux/example/README.md @@ -1,8 +1,3 @@ # shared_preferences_example Demonstrates how to use the shared_preferences plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/shared_preferences/shared_preferences_macos/example/README.md b/packages/shared_preferences/shared_preferences_macos/example/README.md index 7dd9e9c4aa42..c060637c7ec5 100644 --- a/packages/shared_preferences/shared_preferences_macos/example/README.md +++ b/packages/shared_preferences/shared_preferences_macos/example/README.md @@ -1,8 +1,3 @@ # shared_preferences_example Demonstrates how to use the shared_preferences plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/shared_preferences/shared_preferences_windows/example/README.md b/packages/shared_preferences/shared_preferences_windows/example/README.md index d85bb4107622..30c7f7e50c3b 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/README.md +++ b/packages/shared_preferences/shared_preferences_windows/example/README.md @@ -1,16 +1,3 @@ # shared_preferences_windows_example Demonstrates how to use the shared_preferences_windows plugin. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/packages/url_launcher/url_launcher/example/README.md b/packages/url_launcher/url_launcher/example/README.md index c200da8974d1..35b4bdb7031e 100644 --- a/packages/url_launcher/url_launcher/example/README.md +++ b/packages/url_launcher/url_launcher/example/README.md @@ -1,8 +1,3 @@ # url_launcher_example Demonstrates how to use the url_launcher plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/url_launcher/url_launcher_android/example/README.md b/packages/url_launcher/url_launcher_android/example/README.md index c200da8974d1..35b4bdb7031e 100644 --- a/packages/url_launcher/url_launcher_android/example/README.md +++ b/packages/url_launcher/url_launcher_android/example/README.md @@ -1,8 +1,3 @@ # url_launcher_example Demonstrates how to use the url_launcher plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/url_launcher/url_launcher_ios/example/README.md b/packages/url_launcher/url_launcher_ios/example/README.md index c200da8974d1..35b4bdb7031e 100644 --- a/packages/url_launcher/url_launcher_ios/example/README.md +++ b/packages/url_launcher/url_launcher_ios/example/README.md @@ -1,8 +1,3 @@ # url_launcher_example Demonstrates how to use the url_launcher plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/url_launcher/url_launcher_linux/example/README.md b/packages/url_launcher/url_launcher_linux/example/README.md index c200da8974d1..35b4bdb7031e 100644 --- a/packages/url_launcher/url_launcher_linux/example/README.md +++ b/packages/url_launcher/url_launcher_linux/example/README.md @@ -1,8 +1,3 @@ # url_launcher_example Demonstrates how to use the url_launcher plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/url_launcher/url_launcher_macos/example/README.md b/packages/url_launcher/url_launcher_macos/example/README.md index c200da8974d1..35b4bdb7031e 100644 --- a/packages/url_launcher/url_launcher_macos/example/README.md +++ b/packages/url_launcher/url_launcher_macos/example/README.md @@ -1,8 +1,3 @@ # url_launcher_example Demonstrates how to use the url_launcher plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/video_player/video_player/example/README.md b/packages/video_player/video_player/example/README.md index 8ceb0ff485fa..f5974e947c00 100644 --- a/packages/video_player/video_player/example/README.md +++ b/packages/video_player/video_player/example/README.md @@ -1,8 +1,3 @@ # video_player_example Demonstrates how to use the video_player plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/video_player/video_player_android/example/README.md b/packages/video_player/video_player_android/example/README.md index 8ceb0ff485fa..f5974e947c00 100644 --- a/packages/video_player/video_player_android/example/README.md +++ b/packages/video_player/video_player_android/example/README.md @@ -1,8 +1,3 @@ # video_player_example Demonstrates how to use the video_player plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/video_player/video_player_avfoundation/example/README.md b/packages/video_player/video_player_avfoundation/example/README.md index 8ceb0ff485fa..f5974e947c00 100644 --- a/packages/video_player/video_player_avfoundation/example/README.md +++ b/packages/video_player/video_player_avfoundation/example/README.md @@ -1,8 +1,3 @@ # video_player_example Demonstrates how to use the video_player plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/webview_flutter/webview_flutter/example/README.md b/packages/webview_flutter/webview_flutter/example/README.md index 850ee74397a9..e5bd6e20db63 100644 --- a/packages/webview_flutter/webview_flutter/example/README.md +++ b/packages/webview_flutter/webview_flutter/example/README.md @@ -1,8 +1,3 @@ # webview_flutter_example Demonstrates how to use the webview_flutter plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/webview_flutter/webview_flutter_android/example/README.md b/packages/webview_flutter/webview_flutter_android/example/README.md index 850ee74397a9..e5bd6e20db63 100644 --- a/packages/webview_flutter/webview_flutter_android/example/README.md +++ b/packages/webview_flutter/webview_flutter_android/example/README.md @@ -1,8 +1,3 @@ # webview_flutter_example Demonstrates how to use the webview_flutter plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/webview_flutter/webview_flutter_web/example/README.md b/packages/webview_flutter/webview_flutter_web/example/README.md index 850ee74397a9..e5bd6e20db63 100644 --- a/packages/webview_flutter/webview_flutter_web/example/README.md +++ b/packages/webview_flutter/webview_flutter_web/example/README.md @@ -1,8 +1,3 @@ # webview_flutter_example Demonstrates how to use the webview_flutter plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/README.md b/packages/webview_flutter/webview_flutter_wkwebview/example/README.md index 850ee74397a9..e5bd6e20db63 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/README.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/README.md @@ -1,8 +1,3 @@ # webview_flutter_example Demonstrates how to use the webview_flutter plugin. - -## Getting Started - -For help getting started with Flutter, view our online -[documentation](https://flutter.dev/). diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index a8a8268c047f..0e2a33e15eaf 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -2,6 +2,8 @@ - Fixes changelog validation when reverting to a `NEXT` state. - Fixes multiplication of `--force` flag when publishing multiple packages. +- Checks for template boilerplate in `readme-check`. +- `readme-check` now validates example READMEs when present. ## 0.8.5 diff --git a/script/tool/lib/src/common/package_looping_command.dart b/script/tool/lib/src/common/package_looping_command.dart index b48743be3170..a295215f3628 100644 --- a/script/tool/lib/src/common/package_looping_command.dart +++ b/script/tool/lib/src/common/package_looping_command.dart @@ -16,6 +16,20 @@ import 'plugin_command.dart'; import 'process_runner.dart'; import 'repository_package.dart'; +/// Enumeration options for package looping commands. +enum PackageLoopingType { + /// Only enumerates the top level packages, without including any of their + /// subpackages. + topLevelOnly, + + /// Enumerates the top level packages and any example packages they contain. + includeExamples, + + /// Enumerates all packages recursively, including both example and + /// non-example subpackages. + includeAllSubpackages, +} + /// Possible outcomes of a command run for a package. enum RunState { /// The command succeeded for the package. @@ -109,9 +123,26 @@ abstract class PackageLoopingCommand extends PluginCommand { /// Note: Consistent behavior across commands whenever possibel is a goal for /// this tool, so this should be overridden only in rare cases. Stream getPackagesToProcess() async* { - yield* includeSubpackages - ? getTargetPackagesAndSubpackages(filterExcluded: false) - : getTargetPackages(filterExcluded: false); + switch (packageLoopingType) { + case PackageLoopingType.topLevelOnly: + yield* getTargetPackages(filterExcluded: false); + break; + case PackageLoopingType.includeExamples: + await for (final PackageEnumerationEntry packageEntry + in getTargetPackages(filterExcluded: false)) { + yield packageEntry; + yield* Stream.fromIterable(packageEntry + .package + .getExamples() + .map((RepositoryPackage package) => PackageEnumerationEntry( + package, + excluded: packageEntry.excluded))); + } + break; + case PackageLoopingType.includeAllSubpackages: + yield* getTargetPackagesAndSubpackages(filterExcluded: false); + break; + } } /// Runs the command for [package], returning a list of errors. @@ -140,9 +171,9 @@ abstract class PackageLoopingCommand extends PluginCommand { /// to make the output structure easier to follow. bool get hasLongOutput => true; - /// Whether to loop over all packages (e.g., including example/), rather than - /// only top-level packages. - bool get includeSubpackages => false; + /// Whether to loop over top-level packages only, or some or all of their + /// sub-packages as well. + PackageLoopingType get packageLoopingType => PackageLoopingType.topLevelOnly; /// The text to output at the start when reporting one or more failures. /// This will be followed by a list of packages that reported errors, with diff --git a/script/tool/lib/src/pubspec_check_command.dart b/script/tool/lib/src/pubspec_check_command.dart index 23c9c00e33f0..3598a39da960 100644 --- a/script/tool/lib/src/pubspec_check_command.dart +++ b/script/tool/lib/src/pubspec_check_command.dart @@ -64,7 +64,8 @@ class PubspecCheckCommand extends PackageLoopingCommand { bool get hasLongOutput => false; @override - bool get includeSubpackages => true; + PackageLoopingType get packageLoopingType => + PackageLoopingType.includeAllSubpackages; @override Future runForPackage(RepositoryPackage package) async { diff --git a/script/tool/lib/src/readme_check_command.dart b/script/tool/lib/src/readme_check_command.dart index 0cb64920dea4..6e79b736781f 100644 --- a/script/tool/lib/src/readme_check_command.dart +++ b/script/tool/lib/src/readme_check_command.dart @@ -54,34 +54,72 @@ class ReadmeCheckCommand extends PackageLoopingCommand { @override Future runForPackage(RepositoryPackage package) async { - final File readme = package.readmeFile; + final List errors = _validateReadme(package.readmeFile, + mainPackage: package, isExample: false); + for (final RepositoryPackage packageToCheck in package.getExamples()) { + errors.addAll(_validateReadme(packageToCheck.readmeFile, + mainPackage: package, isExample: true)); + } - if (!readme.existsSync()) { - return PackageResult.fail(['Missing README.md']); + // If there's an example/README.md for a multi-example package, validate + // that as well, as it will be shown on pub.dev. + final Directory exampleDir = package.directory.childDirectory('example'); + final File exampleDirReadme = exampleDir.childFile('README.md'); + if (exampleDir.existsSync() && !isPackage(exampleDir)) { + errors.addAll(_validateReadme(exampleDirReadme, + mainPackage: package, isExample: true)); } - final List errors = []; + return errors.isEmpty + ? PackageResult.success() + : PackageResult.fail(errors); + } + + List _validateReadme(File readme, + {required RepositoryPackage mainPackage, required bool isExample}) { + if (!readme.existsSync()) { + if (isExample) { + print('${indentation}No README for ' + '${getRelativePosixPath(readme.parent, from: mainPackage.directory)}'); + return []; + } else { + printError('${indentation}No README found at ' + '${getRelativePosixPath(readme, from: mainPackage.directory)}'); + return ['Missing README.md']; + } + } - final Pubspec pubspec = package.parsePubspec(); - final bool isPlugin = pubspec.flutter?['plugin'] != null; + print('${indentation}Checking ' + '${getRelativePosixPath(readme, from: mainPackage.directory)}...'); - final List readmeLines = package.readmeFile.readAsLinesSync(); + final List readmeLines = readme.readAsLinesSync(); + final List errors = []; final String? blockValidationError = _validateCodeBlocks(readmeLines); if (blockValidationError != null) { errors.add(blockValidationError); } - if (isPlugin && (!package.isFederated || package.isAppFacing)) { - final String? error = _validateSupportedPlatforms(readmeLines, pubspec); - if (error != null) { - errors.add(error); + if (_containsTemplateBoilerplate(readmeLines)) { + printError('${indentation}The boilerplate section about getting started ' + 'with Flutter should not be left in.'); + errors.add('Contains template boilerplate'); + } + + // Check if this is the main readme for a plugin, and if so enforce extra + // checks. + if (!isExample) { + final Pubspec pubspec = mainPackage.parsePubspec(); + final bool isPlugin = pubspec.flutter?['plugin'] != null; + if (isPlugin && (!mainPackage.isFederated || mainPackage.isAppFacing)) { + final String? error = _validateSupportedPlatforms(readmeLines, pubspec); + if (error != null) { + errors.add(error); + } } } - return errors.isEmpty - ? PackageResult.success() - : PackageResult.fail(errors); + return errors; } /// Validates that code blocks (``` ... ```) follow repository standards. @@ -223,4 +261,11 @@ ${indentation * 2}Please use standard capitalizations: ${sortedListString(expect // https://github.com/flutter/flutter/issues/84200 return null; } + + /// Returns true if the README still has the boilerplate from the + /// `flutter create` templates. + bool _containsTemplateBoilerplate(List readmeLines) { + return readmeLines.any((String line) => + line.contains('For help getting started with Flutter')); + } } diff --git a/script/tool/lib/src/test_command.dart b/script/tool/lib/src/test_command.dart index 27a01c95e851..5101b8f19e7e 100644 --- a/script/tool/lib/src/test_command.dart +++ b/script/tool/lib/src/test_command.dart @@ -37,7 +37,8 @@ class TestCommand extends PackageLoopingCommand { 'This command requires "flutter" to be in your path.'; @override - bool get includeSubpackages => true; + PackageLoopingType get packageLoopingType => + PackageLoopingType.includeAllSubpackages; @override Future runForPackage(RepositoryPackage package) async { diff --git a/script/tool/test/common/package_looping_command_test.dart b/script/tool/test/common/package_looping_command_test.dart index 3dd6a47ae2fb..a7e7dfdf6ebb 100644 --- a/script/tool/test/common/package_looping_command_test.dart +++ b/script/tool/test/common/package_looping_command_test.dart @@ -98,7 +98,7 @@ void main() { TestPackageLoopingCommand createTestCommand({ String gitDiffResponse = '', bool hasLongOutput = true, - bool includeSubpackages = false, + PackageLoopingType packageLoopingType = PackageLoopingType.topLevelOnly, bool failsDuringInit = false, bool warnsDuringInit = false, bool warnsDuringCleanup = false, @@ -122,7 +122,7 @@ void main() { packagesDir, platform: mockPlatform, hasLongOutput: hasLongOutput, - includeSubpackages: includeSubpackages, + packageLoopingType: packageLoopingType, failsDuringInit: failsDuringInit, warnsDuringInit: warnsDuringInit, warnsDuringCleanup: warnsDuringCleanup, @@ -236,14 +236,17 @@ void main() { unorderedEquals([package1.path, package2.path])); }); - test('includes subpackages when requested', () async { + test('includes all subpackages when requested', () async { final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir, examples: ['example1', 'example2']); final RepositoryPackage package = createFakePackage('a_package', packagesDir); + final RepositoryPackage subPackage = createFakePackage( + 'sub_package', package.directory, + examples: []); - final TestPackageLoopingCommand command = - createTestCommand(includeSubpackages: true); + final TestPackageLoopingCommand command = createTestCommand( + packageLoopingType: PackageLoopingType.includeAllSubpackages); await runCommand(command); expect( @@ -254,18 +257,72 @@ void main() { getExampleDir(plugin).childDirectory('example2').path, package.path, getExampleDir(package).path, + subPackage.path, ])); }); + test('includes examples when requested', () async { + final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir, + examples: ['example1', 'example2']); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + final RepositoryPackage subPackage = + createFakePackage('sub_package', package.directory); + + final TestPackageLoopingCommand command = createTestCommand( + packageLoopingType: PackageLoopingType.includeExamples); + await runCommand(command); + + expect( + command.checkedPackages, + unorderedEquals([ + plugin.path, + getExampleDir(plugin).childDirectory('example1').path, + getExampleDir(plugin).childDirectory('example2').path, + package.path, + getExampleDir(package).path, + ])); + expect(command.checkedPackages, isNot(contains(subPackage.path))); + }); + test('excludes subpackages when main package is excluded', () async { final RepositoryPackage excluded = createFakePlugin( 'a_plugin', packagesDir, examples: ['example1', 'example2']); final RepositoryPackage included = createFakePackage('a_package', packagesDir); + final RepositoryPackage subpackage = + createFakePackage('sub_package', excluded.directory); - final TestPackageLoopingCommand command = - createTestCommand(includeSubpackages: true); + final TestPackageLoopingCommand command = createTestCommand( + packageLoopingType: PackageLoopingType.includeAllSubpackages); + await runCommand(command, arguments: ['--exclude=a_plugin']); + + final Iterable examples = excluded.getExamples(); + + expect( + command.checkedPackages, + unorderedEquals([ + included.path, + getExampleDir(included).path, + ])); + expect(command.checkedPackages, isNot(contains(excluded.path))); + expect(examples.length, 2); + for (final RepositoryPackage example in examples) { + expect(command.checkedPackages, isNot(contains(example.path))); + } + expect(command.checkedPackages, isNot(contains(subpackage.path))); + }); + + test('excludes examples when main package is excluded', () async { + final RepositoryPackage excluded = createFakePlugin( + 'a_plugin', packagesDir, + examples: ['example1', 'example2']); + final RepositoryPackage included = + createFakePackage('a_package', packagesDir); + + final TestPackageLoopingCommand command = createTestCommand( + packageLoopingType: PackageLoopingType.includeExamples); await runCommand(command, arguments: ['--exclude=a_plugin']); final Iterable examples = excluded.getExamples(); @@ -290,8 +347,9 @@ void main() { final RepositoryPackage included = createFakePackage('a_package', packagesDir); - final TestPackageLoopingCommand command = - createTestCommand(includeSubpackages: true, hasLongOutput: false); + final TestPackageLoopingCommand command = createTestCommand( + packageLoopingType: PackageLoopingType.includeAllSubpackages, + hasLongOutput: false); final List output = await runCommand(command, arguments: [ '--skip-if-not-supporting-flutter-version=2.5.0' ]); @@ -769,7 +827,7 @@ class TestPackageLoopingCommand extends PackageLoopingCommand { Directory packagesDir, { required Platform platform, this.hasLongOutput = true, - this.includeSubpackages = false, + this.packageLoopingType = PackageLoopingType.topLevelOnly, this.customFailureListHeader, this.customFailureListFooter, this.failsDuringInit = false, @@ -795,7 +853,7 @@ class TestPackageLoopingCommand extends PackageLoopingCommand { bool hasLongOutput; @override - bool includeSubpackages; + PackageLoopingType packageLoopingType; @override String get failureListHeader => diff --git a/script/tool/test/list_command_test.dart b/script/tool/test/list_command_test.dart index c2042c26638c..f74431c5cee7 100644 --- a/script/tool/test/list_command_test.dart +++ b/script/tool/test/list_command_test.dart @@ -101,15 +101,18 @@ void main() { '/packages/plugin1/pubspec.yaml', '/packages/plugin1/AUTHORS', '/packages/plugin1/CHANGELOG.md', + '/packages/plugin1/README.md', '/packages/plugin1/example/pubspec.yaml', '/packages/plugin2/pubspec.yaml', '/packages/plugin2/AUTHORS', '/packages/plugin2/CHANGELOG.md', + '/packages/plugin2/README.md', '/packages/plugin2/example/example1/pubspec.yaml', '/packages/plugin2/example/example2/pubspec.yaml', '/packages/plugin3/pubspec.yaml', '/packages/plugin3/AUTHORS', '/packages/plugin3/CHANGELOG.md', + '/packages/plugin3/README.md', ]), ); }); diff --git a/script/tool/test/readme_check_command_test.dart b/script/tool/test/readme_check_command_test.dart index f53fa06a8133..fa4fc604dd73 100644 --- a/script/tool/test/readme_check_command_test.dart +++ b/script/tool/test/readme_check_command_test.dart @@ -37,8 +37,33 @@ void main() { runner.addCommand(command); }); - test('fails when README is missing', () async { - createFakePackage('a_package', packagesDir); + test('prints paths of checked READMEs', () async { + final RepositoryPackage package = createFakePackage( + 'a_package', packagesDir, + examples: ['example1', 'example2']); + for (final RepositoryPackage example in package.getExamples()) { + example.readmeFile.writeAsStringSync('A readme'); + } + getExampleDir(package).childFile('README.md').writeAsStringSync('A readme'); + + final List output = + await runCapturingPrint(runner, ['readme-check']); + + expect( + output, + containsAll([ + contains(' Checking README.md...'), + contains(' Checking example/README.md...'), + contains(' Checking example/example1/README.md...'), + contains(' Checking example/example2/README.md...'), + ]), + ); + }); + + test('fails when package README is missing', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + package.readmeFile.deleteSync(); Error? commandError; final List output = await runCapturingPrint( @@ -55,6 +80,143 @@ void main() { ); }); + test('passes when example README is missing', () async { + createFakePackage('a_package', packagesDir); + + final List output = + await runCapturingPrint(runner, ['readme-check']); + + expect( + output, + containsAllInOrder([ + contains('No README for example'), + ]), + ); + }); + + test('does not inculde non-example subpackages', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + const String subpackageName = 'special_test'; + final RepositoryPackage miscSubpackage = + createFakePackage(subpackageName, package.directory); + miscSubpackage.readmeFile.delete(); + + final List output = + await runCapturingPrint(runner, ['readme-check']); + + expect(output, isNot(contains(subpackageName))); + }); + + test('fails when README still has plugin template boilerplate', () async { + final RepositoryPackage package = createFakePlugin('a_plugin', packagesDir); + package.readmeFile.writeAsStringSync(''' +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/developing-packages/), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter development, view the +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The boilerplate section about getting started with Flutter ' + 'should not be left in.'), + contains('Contains template boilerplate'), + ]), + ); + }); + + test('fails when example README still has application template boilerplate', + () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + package.getExamples().first.readmeFile.writeAsStringSync(''' +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The boilerplate section about getting started with Flutter ' + 'should not be left in.'), + contains('Contains template boilerplate'), + ]), + ); + }); + + test( + 'fails when multi-example top-level example directory README still has ' + 'application template boilerplate', () async { + final RepositoryPackage package = createFakePackage( + 'a_package', packagesDir, + examples: ['example1', 'example2']); + package.directory + .childDirectory('example') + .childFile('README.md') + .writeAsStringSync(''' +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The boilerplate section about getting started with Flutter ' + 'should not be left in.'), + contains('Contains template boilerplate'), + ]), + ); + }); + group('plugin OS support', () { test( 'does not check support table for anything other than app-facing plugin packages', @@ -62,20 +224,12 @@ void main() { const String federatedPluginName = 'a_federated_plugin'; final Directory federatedDir = packagesDir.childDirectory(federatedPluginName); - final List packages = [ - // A non-plugin package. - createFakePackage('a_package', packagesDir), - // Non-app-facing parts of a federated plugin. - createFakePlugin( - '${federatedPluginName}_platform_interface', federatedDir), - createFakePlugin('${federatedPluginName}_android', federatedDir), - ]; - - for (final RepositoryPackage package in packages) { - package.readmeFile.writeAsStringSync(''' -A very useful package. -'''); - } + // A non-plugin package. + createFakePackage('a_package', packagesDir); + // Non-app-facing parts of a federated plugin. + createFakePlugin( + '${federatedPluginName}_platform_interface', federatedDir); + createFakePlugin('${federatedPluginName}_android', federatedDir); final List output = await runCapturingPrint(runner, [ 'readme-check', @@ -94,12 +248,7 @@ A very useful package. test('fails when non-federated plugin is missing an OS support table', () async { - final RepositoryPackage plugin = - createFakePlugin('a_plugin', packagesDir); - - plugin.readmeFile.writeAsStringSync(''' -A very useful plugin. -'''); + createFakePlugin('a_plugin', packagesDir); Error? commandError; final List output = await runCapturingPrint( @@ -119,12 +268,7 @@ A very useful plugin. test( 'fails when app-facing part of a federated plugin is missing an OS support table', () async { - final RepositoryPackage plugin = - createFakePlugin('a_plugin', packagesDir.childDirectory('a_plugin')); - - plugin.readmeFile.writeAsStringSync(''' -A very useful plugin. -'''); + createFakePlugin('a_plugin', packagesDir.childDirectory('a_plugin')); Error? commandError; final List output = await runCapturingPrint( diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index effdd03891dc..7255bf9a9276 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -125,7 +125,7 @@ RepositoryPackage createFakePlugin( /// separators, of extra files to create in the package. /// /// If [includeCommonFiles] is true, common but non-critical files like -/// CHANGELOG.md and AUTHORS will be included. +/// CHANGELOG.md, README.md, and AUTHORS will be included. /// /// If non-null, [directoryName] will be used for the directory instead of /// [name]. @@ -152,11 +152,12 @@ RepositoryPackage createFakePackage( version: version, flutterConstraint: flutterConstraint); if (includeCommonFiles) { - createFakeCHANGELOG(package, ''' + package.changelogFile.writeAsStringSync(''' ## $version * Some changes. '''); - createFakeAuthors(package); + package.readmeFile.writeAsStringSync('A very useful package'); + package.authorsFile.writeAsStringSync('Google Inc.'); } if (examples.length == 1) { @@ -188,11 +189,6 @@ RepositoryPackage createFakePackage( return package; } -void createFakeCHANGELOG(RepositoryPackage package, String texts) { - package.changelogFile.createSync(); - package.changelogFile.writeAsStringSync(texts); -} - /// Creates a `pubspec.yaml` file with a flutter dependency. /// /// [platformSupport] is a map of platform string to the support details for @@ -267,11 +263,6 @@ $pluginSection package.pubspecFile.writeAsStringSync(yaml); } -void createFakeAuthors(RepositoryPackage package) { - package.authorsFile.createSync(); - package.authorsFile.writeAsStringSync('Google Inc.'); -} - String _pluginPlatformSection( String platform, PlatformDetails support, String packageName) { String entry = ''; diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart index 6af3c112f9eb..a310f0f09fcb 100644 --- a/script/tool/test/version_check_command_test.dart +++ b/script/tool/test/version_check_command_test.dart @@ -421,7 +421,7 @@ This is necessary because of X, Y, and Z ## $version * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main']); expect( @@ -439,7 +439,7 @@ This is necessary because of X, Y, and Z ## 1.0.2 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); Error? commandError; final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main', '--against-pub'], @@ -465,7 +465,7 @@ This is necessary because of X, Y, and Z ## $version * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main']); expect( @@ -488,7 +488,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); bool hasError = false; final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main', '--against-pub'], @@ -518,7 +518,7 @@ This is necessary because of X, Y, and Z ## $version * Some other changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -547,7 +547,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); bool hasError = false; final List output = await runCapturingPrint( runner, ['version-check', '--base-sha=main', '--against-pub'], @@ -580,7 +580,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); bool hasError = false; final List output = await runCapturingPrint( @@ -612,7 +612,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); bool hasError = false; final List output = await runCapturingPrint( @@ -642,8 +642,8 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(plugin, changelog); - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.1'), ]; @@ -671,7 +671,7 @@ This is necessary because of X, Y, and Z # 1.0.0 * Some other changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -704,7 +704,7 @@ This is necessary because of X, Y, and Z ## Alpha * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -749,7 +749,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -778,7 +778,7 @@ This is necessary because of X, Y, and Z ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -813,7 +813,7 @@ packages/plugin/lib/plugin.dart ## 1.0.1 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -844,7 +844,7 @@ packages/plugin/pubspec.yaml ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -874,7 +874,7 @@ tool/plugin/lib/plugin.dart ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -907,7 +907,7 @@ packages/plugin/CHANGELOG.md ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -948,7 +948,7 @@ No version change: Code change is only to implementation comments. ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -983,7 +983,7 @@ packages/plugin/example/lib/foo.dart ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -1014,7 +1014,7 @@ packages/plugin/CHANGELOG.md ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; @@ -1048,7 +1048,7 @@ packages/another_plugin/CHANGELOG.md ## 1.0.0 * Some changes. '''; - createFakeCHANGELOG(plugin, changelog); + plugin.changelogFile.writeAsStringSync(changelog); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; From 364c53f8a37d1dc3260b1f3373336ef8704608f0 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Wed, 18 May 2022 15:32:09 +0200 Subject: [PATCH 325/844] [webview_flutter] Initial v4.0 platform interface implementation (#5109) --- .../CHANGELOG.md | 3 +- .../v4/src/platform_navigation_delegate.dart | 89 ++++ .../v4/src/platform_webview_controller.dart | 285 +++++++++++ .../src/platform_webview_cookie_manager.dart | 55 +++ .../lib/v4/src/platform_webview_widget.dart | 37 ++ .../lib/v4/src/types/javascript_message.dart | 51 ++ .../lib/v4/src/types/javascript_mode.dart | 12 + .../lib/v4/src/types/load_request_params.dart | 89 ++++ ...m_navigation_delegate_creation_params.dart | 44 ++ ...rm_webview_controller_creation_params.dart | 45 ++ ...ebview_cookie_manager_creation_params.dart | 45 ++ ...atform_webview_widget_creation_params.dart | 79 +++ .../lib/v4/src/types/types.dart | 13 + .../lib/v4/src/types/web_resource_error.dart | 119 +++++ .../lib/v4/src/types/webview_cookie.dart | 41 ++ .../lib/v4/src/webview_platform.dart | 82 +++ .../webview_flutter_platform_interface.dart | 10 + .../pubspec.yaml | 4 +- .../v4/platform_navigation_delegate_test.dart | 141 ++++++ .../v4/platform_webview_controller_test.dart | 467 ++++++++++++++++++ ...latform_webview_controller_test.mocks.dart | 72 +++ .../src/v4/platform_webview_widget_test.dart | 89 ++++ .../test/src/v4/webview_platform_test.dart | 109 ++++ .../src/v4/webview_platform_test.mocks.dart | 78 +++ 24 files changed, 2057 insertions(+), 2 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_navigation_delegate.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_controller.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_cookie_manager.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_widget.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/javascript_message.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/javascript_mode.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/load_request_params.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_navigation_delegate_creation_params.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_controller_creation_params.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_cookie_manager_creation_params.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_widget_creation_params.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/types.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/web_resource_error.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/webview_cookie.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/webview_platform.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/lib/v4/webview_flutter_platform_interface.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_navigation_delegate_test.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.mocks.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.dart create mode 100644 packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.mocks.dart diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index c7462ddd47d0..8f6c413ce0cc 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 1.9.0 +* Adds the first iteration of the v4 webview_flutter interface implementation. * Removes unnecessary imports. ## 1.8.2 diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_navigation_delegate.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_navigation_delegate.dart new file mode 100644 index 000000000000..a66f1defdf60 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_navigation_delegate.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:async'; + +import 'package:flutter/foundation.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'webview_platform.dart'; + +/// An interface defining navigation events that occur on the native platform. +/// +/// The [PlatformWebViewController] is notifying this delegate on events that +/// happened on the platform's webview. Platform implementations should +/// implement this class and pass an instance to the [PlatformWebViewController]. +abstract class PlatformNavigationDelegate extends PlatformInterface { + /// Creates a new [PlatformNavigationDelegate] + factory PlatformNavigationDelegate( + PlatformNavigationDelegateCreationParams params) { + final PlatformNavigationDelegate callbackDelegate = + WebViewPlatform.instance!.createPlatformNavigationDelegate(params); + PlatformInterface.verify(callbackDelegate, _token); + return callbackDelegate; + } + + /// Used by the platform implementation to create a new [PlatformNavigationDelegate]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformNavigationDelegate.implementation(this.params) : super(token: _token); + + static final Object _token = Object(); + + /// The parameters used to initialize the [PlatformNavigationDelegate]. + final PlatformNavigationDelegateCreationParams params; + + /// Invoked when a navigation request is pending. + /// + /// See [PlatformWebViewController.setPlatformNavigationDelegate]. + Future setOnNavigationRequest( + FutureOr Function({required String url, required bool isForMainFrame}) + onNavigationRequest, + ) { + throw UnimplementedError( + 'setOnNavigationRequest is not implemented on the current platform.'); + } + + /// Invoked when a page has started loading. + /// + /// See [PlatformWebViewController.setPlatformNavigationDelegate]. + Future setOnPageStarted( + void Function(String url) onPageStarted, + ) { + throw UnimplementedError( + 'setOnPageStarted is not implemented on the current platform.'); + } + + /// Invoked when a page has finished loading. + /// + /// See [PlatformWebViewController.setPlatformNavigationDelegate]. + Future setOnPageFinished( + void Function(String url) onPageFinished, + ) { + throw UnimplementedError( + 'setOnPageFinished is not implemented on the current platform.'); + } + + /// Invoked when a page is loading to report the progress. + /// + /// See [PlatformWebViewController.setPlatformNavigationDelegate]. + Future setOnProgress( + void Function(int progress) onProgress, + ) { + throw UnimplementedError( + 'setOnProgress is not implemented on the current platform.'); + } + + /// Invoked when a resource loading error occurred. + /// + /// See [PlatformWebViewController.setPlatformNavigationDelegate]. + Future setOnWebResourceError( + void Function(WebResourceError error) onWebResourceError, + ) { + throw UnimplementedError( + 'setOnWebResourceError is not implemented on the current platform.'); + } +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_controller.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_controller.dart new file mode 100644 index 000000000000..3585ec8b1886 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_controller.dart @@ -0,0 +1,285 @@ +// 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:math'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'platform_navigation_delegate.dart'; +import 'webview_platform.dart'; + +/// Interface for a platform implementation of a web view controller. +/// +/// Platform implementations should extend this class rather than implement it +/// as `webview_flutter` does not consider newly added methods to be breaking +/// changes. Extending this class (using `extends`) ensures that the subclass +/// will get the default implementation, while platform implementations that +/// `implements` this interface will be broken by newly added +/// [PlatformWebViewCookieManager] methods. +abstract class PlatformWebViewController extends PlatformInterface { + /// Creates a new [PlatformWebViewController] + factory PlatformWebViewController( + PlatformWebViewControllerCreationParams params) { + final PlatformWebViewController webViewControllerDelegate = + WebViewPlatform.instance!.createPlatformWebViewController(params); + PlatformInterface.verify(webViewControllerDelegate, _token); + return webViewControllerDelegate; + } + + /// Used by the platform implementation to create a new [PlatformWebViewController]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformWebViewController.implementation(this.params) : super(token: _token); + + static final Object _token = Object(); + + /// The parameters used to initialize the [PlatformWebViewController]. + final PlatformWebViewControllerCreationParams params; + + /// Loads the file located on the specified [absoluteFilePath]. + /// + /// The [absoluteFilePath] parameter should contain the absolute path to the + /// file as it is stored on the device. For example: + /// `/Users/username/Documents/www/index.html`. + /// + /// Throws an ArgumentError if the [absoluteFilePath] does not exist. + Future loadFile( + String absoluteFilePath, + ) { + throw UnimplementedError( + 'loadFile is not implemented on the current platform'); + } + + /// Loads the Flutter asset specified in the pubspec.yaml file. + /// + /// Throws an ArgumentError if [key] is not part of the specified assets + /// in the pubspec.yaml file. + Future loadFlutterAsset( + String key, + ) { + throw UnimplementedError( + 'loadFlutterAsset is not implemented on the current platform'); + } + + /// Loads the supplied HTML string. + /// + /// The [baseUrl] parameter is used when resolving relative URLs within the + /// HTML string. + Future loadHtmlString( + String html, { + String? baseUrl, + }) { + throw UnimplementedError( + 'loadHtmlString is not implemented on the current platform'); + } + + /// Makes a specific HTTP request ands loads the response in the webview. + /// + /// [WebViewRequest.method] must be one of the supported HTTP methods + /// in [WebViewRequestMethod]. + /// + /// If [WebViewRequest.headers] is not empty, its key-value pairs will be + /// added as the headers for the request. + /// + /// If [WebViewRequest.body] is not null, it will be added as the body + /// for the request. + /// + /// Throws an ArgumentError if [WebViewRequest.uri] has empty scheme. + Future loadRequest( + LoadRequestParams params, + ) { + throw UnimplementedError( + 'loadRequest is not implemented on the current platform'); + } + + /// Accessor to the current URL that the WebView is displaying. + /// + /// If no URL was ever loaded, returns `null`. + Future currentUrl() { + throw UnimplementedError( + 'currentUrl is not implemented on the current platform'); + } + + /// Checks whether there's a back history item. + Future canGoBack() { + throw UnimplementedError( + 'canGoBack is not implemented on the current platform'); + } + + /// Checks whether there's a forward history item. + Future canGoForward() { + throw UnimplementedError( + 'canGoForward is not implemented on the current platform'); + } + + /// Goes back in the history of this WebView. + /// + /// If there is no back history item this is a no-op. + Future goBack() { + throw UnimplementedError( + 'goBack is not implemented on the current platform'); + } + + /// Goes forward in the history of this WebView. + /// + /// If there is no forward history item this is a no-op. + Future goForward() { + throw UnimplementedError( + 'goForward is not implemented on the current platform'); + } + + /// Reloads the current URL. + Future reload() { + throw UnimplementedError( + 'reload is not implemented on the current platform'); + } + + /// Clears all caches used by the [WebView]. + /// + /// The following caches are cleared: + /// 1. Browser HTTP Cache. + /// 2. [Cache API](https://developers.google.com/web/fundamentals/instant-and-offline/web-storage/cache-api) caches. + /// These are not yet supported in iOS WkWebView. Service workers tend to use this cache. + /// 3. Application cache. + Future clearCache() { + throw UnimplementedError( + 'clearCache is not implemented on the current platform'); + } + + /// Clears the local storage used by the [WebView]. + Future clearLocalStorage() { + throw UnimplementedError( + 'clearLocalStorage is not implemented on the current platform'); + } + + /// Sets the [PlatformNavigationDelegate] containing the callback methods that + /// are called during navigation events. + Future setPlatformNavigationDelegate( + PlatformNavigationDelegate handler) { + throw UnimplementedError( + 'setPlatformNavigationDelegate is not implemented on the current platform'); + } + + /// Runs the given JavaScript in the context of the current page. + /// + /// The Future completes with an error if a JavaScript error occurred. + Future runJavaScript(String javaScript) { + throw UnimplementedError( + 'runJavaScript is not implemented on the current platform'); + } + + /// Runs the given JavaScript in the context of the current page, and returns the result. + /// + /// The Future completes with an error if a JavaScript error occurred, or if the + /// type the given expression evaluates to is unsupported. Unsupported values include + /// certain non-primitive types on iOS, as well as `undefined` or `null` on iOS 14+. + Future runJavaScriptReturningResult(String javaScript) { + throw UnimplementedError( + 'runJavaScriptReturningResult is not implemented on the current platform'); + } + + /// Adds a new JavaScript channel to the set of enabled channels. + Future addJavaScriptChannel( + JavaScriptChannelParams javaScriptChannelParams, + ) { + throw UnimplementedError( + 'addJavaScriptChannel is not implemented on the current platform'); + } + + /// Removes the JavaScript channel with the matching name from the set of + /// enabled channels. + /// + /// This disables the channel with the matching name if it was previously + /// enabled through the [addJavaScriptChannel]. + Future removeJavaScriptChannel(String javaScriptChannelName) { + throw UnimplementedError( + 'removeJavaScriptChannel is not implemented on the current platform'); + } + + /// Returns the title of the currently loaded page. + Future getTitle() { + throw UnimplementedError( + 'getTitle is not implemented on the current platform'); + } + + /// Set the scrolled position of this view. + /// + /// The parameters `x` and `y` specify the position to scroll to in WebView pixels. + Future scrollTo(int x, int y) { + throw UnimplementedError( + 'scrollTo is not implemented on the current platform'); + } + + /// Move the scrolled position of this view. + /// + /// The parameters `x` and `y` specify the amount of WebView pixels to scroll by. + Future scrollBy(int x, int y) { + throw UnimplementedError( + 'scrollBy is not implemented on the current platform'); + } + + /// Return the current scroll position of this view. + /// + /// Scroll position is measured from the top left. + Future> getScrollPosition() { + throw UnimplementedError( + 'getScrollPosition is not implemented on the current platform'); + } + + /// Whether to enable the platform's webview content debugging tools. + Future enableDebugging(bool enabled) { + throw UnimplementedError( + 'enableDebugging is not implemented on the current platform'); + } + + /// Whether to allow swipe based navigation on supported platforms. + Future enableGestureNavigation(bool enabled) { + throw UnimplementedError( + 'enableGestureNavigation is not implemented on the current platform'); + } + + /// Whhether to support zooming using its on-screen zoom controls and gestures. + Future enableZoom(bool enabled) { + throw UnimplementedError( + 'enableZoom is not implemented on the current platform'); + } + + /// Set the current background color of this view. + Future setBackgroundColor(Color color) { + throw UnimplementedError( + 'setBackgroundColor is not implemented on the current platform'); + } + + /// Sets the JavaScript execution mode to be used by the webview. + Future setJavaScriptMode(JavaScriptMode javaScriptMode) { + throw UnimplementedError( + 'setJavaScriptMode is not implemented on the current platform'); + } + + /// Sets the value used for the HTTP `User-Agent:` request header. + Future setUserAgent(String? userAgent) { + throw UnimplementedError( + 'setUserAgent is not implemented on the current platform'); + } +} + +/// Describes the parameters necessary for registering a JavaScript channel. +class JavaScriptChannelParams { + /// Creates a new [JavaScriptChannelParams] object. + JavaScriptChannelParams({ + required this.name, + required this.onMessageReceived, + }); + + /// The name that identifies the JavaScript channel. + final String name; + + /// The callback method that is invoked when a [JavaScriptMessage] is + /// received. + final void Function(JavaScriptMessage) onMessageReceived; +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_cookie_manager.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_cookie_manager.dart new file mode 100644 index 000000000000..9e981c9022c6 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_cookie_manager.dart @@ -0,0 +1,55 @@ +// 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:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'webview_platform.dart'; + +/// Interface for a platform implementation of a cookie manager. +/// +/// Platform implementations should extend this class rather than implement it +/// as `webview_flutter` does not consider newly added methods to be breaking +/// changes. Extending this class (using `extends`) ensures that the subclass +/// will get the default implementation, while platform implementations that +/// `implements` this interface will be broken by newly added +/// [PlatformWebViewCookieManager] methods. +abstract class PlatformWebViewCookieManager extends PlatformInterface { + /// Creates a new [PlatformWebViewCookieManager] + factory PlatformWebViewCookieManager( + PlatformWebViewCookieManagerCreationParams params) { + final PlatformWebViewCookieManager cookieManagerDelegate = + WebViewPlatform.instance!.createPlatformCookieManager(params); + PlatformInterface.verify(cookieManagerDelegate, _token); + return cookieManagerDelegate; + } + + /// Used by the platform implementation to create a new + /// [PlatformWebViewCookieManager]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformWebViewCookieManager.implementation(this.params) + : super(token: _token); + + static final Object _token = Object(); + + /// The parameters used to initialize the [PlatformWebViewCookieManager]. + final PlatformWebViewCookieManagerCreationParams params; + + /// Clears all cookies for all [WebView] instances. + /// + /// Returns true if cookies were present before clearing, else false. + Future clearCookies() { + throw UnimplementedError( + 'clearCookies is not implemented on the current platform'); + } + + /// Sets a cookie for all [WebView] instances. + Future setCookie(WebViewCookie cookie) { + throw UnimplementedError( + 'setCookie is not implemented on the current platform'); + } +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_widget.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_widget.dart new file mode 100644 index 000000000000..40334c650b3a --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_widget.dart @@ -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. + +import 'package:flutter/widgets.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'webview_platform.dart'; + +/// Interface for a platform implementation of a web view widget. +abstract class PlatformWebViewWidget extends PlatformInterface { + /// Creates a new [PlatformWebViewWidget] + factory PlatformWebViewWidget(PlatformWebViewWidgetCreationParams params) { + final PlatformWebViewWidget webViewWidgetDelegate = + WebViewPlatform.instance!.createPlatformWebViewWidget(params); + PlatformInterface.verify(webViewWidgetDelegate, _token); + return webViewWidgetDelegate; + } + + /// Used by the platform implementation to create a new + /// [PlatformWebViewWidget]. + /// + /// Should only be used by platform implementations because they can't extend + /// a class that only contains a factory constructor. + @protected + PlatformWebViewWidget.implementation(this.params) : super(token: _token); + + static final Object _token = Object(); + + /// The parameters used to initialize the [PlatformWebViewWidget]. + final PlatformWebViewWidgetCreationParams params; + + /// Builds a new WebView. + /// + /// Returns a Widget tree that embeds the created web view. + Widget build(BuildContext context); +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/javascript_message.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/javascript_message.dart new file mode 100644 index 000000000000..b37661a045a9 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/javascript_message.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/foundation.dart'; + +/// A message that was sent by JavaScript code running in a [WebView]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class and providing a factory method that takes the +/// [JavaScriptMessage] as a parameter. +/// +/// {@tool sample} +/// This example demonstrates how to extend the [JavaScriptMessage] to +/// provide additional platform specific parameters. +/// +/// When extending [JavaScriptMessage] additional parameters should always +/// accept `null` or have a default value to prevent breaking changes. +/// +/// ```dart +/// @immutable +/// class WKWebViewScriptMessage extends JavaScriptMessage { +/// WKWebViewScriptMessage._( +/// JavaScriptMessage javaScriptMessage, +/// this.extraData, +/// ) : super(javaScriptMessage.message); +/// +/// factory WKWebViewScriptMessage.fromJavaScripMessage( +/// JavaScriptMessage javaScripMessage, { +/// String? extraData, +/// }) { +/// return WKWebViewScriptMessage._( +/// javaScriptMessage, +/// extraData: extraData, +/// ); +/// } +/// +/// final String? extraData; +/// } +/// ``` +/// {@end-tool} +@immutable +class JavaScriptMessage { + /// Creates a new JavaScript message object. + const JavaScriptMessage({ + required this.message, + }); + + /// The contents of the message that was sent by the JavaScript code. + final String message; +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/javascript_mode.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/javascript_mode.dart new file mode 100644 index 000000000000..bcbebff8bb1a --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/javascript_mode.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. + +/// Describes the state of JavaScript support in a given web view. +enum JavaScriptMode { + /// JavaScript execution is disabled. + disabled, + + /// JavaScript execution is not restricted. + unrestricted, +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/load_request_params.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/load_request_params.dart new file mode 100644 index 000000000000..2da51f8dc19f --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/load_request_params.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:typed_data'; + +import 'package:flutter/foundation.dart'; + +import '../platform_webview_controller.dart'; + +/// Defines the supported HTTP methods for loading a page in [PlatformWebViewController]. +enum LoadRequestMethod { + /// HTTP GET method. + get, + + /// HTTP POST method. + post, +} + +/// Extension methods on the [LoadRequestMethod] enum. +extension LoadRequestMethodExtensions on LoadRequestMethod { + /// Converts [LoadRequestMethod] to [String] format. + String serialize() { + switch (this) { + case LoadRequestMethod.get: + return 'get'; + case LoadRequestMethod.post: + return 'post'; + } + } +} + +/// Defines the parameters that can be used to load a page with the [PlatformWebViewController]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// {@tool sample} +/// This example demonstrates how to extend the [LoadRequestParams] to +/// provide additional platform specific parameters. +/// +/// When extending [LoadRequestParams] additional parameters should always +/// accept `null` or have a default value to prevent breaking changes. +/// +/// ```dart +/// class AndroidLoadRequestParams extends LoadRequestParams { +/// AndroidLoadRequestParams._({ +/// required LoadRequestParams params, +/// this.historyUrl, +/// }) : super( +/// uri: params.uri, +/// method: params.method, +/// body: params.body, +/// headers: params.headers, +/// ); +/// +/// factory AndroidLoadRequestParams.fromLoadRequestParams( +/// LoadRequestParams params, { +/// Uri? historyUrl, +/// }) { +/// return AndroidLoadRequestParams._(params, historyUrl: historyUrl); +/// } +/// +/// final Uri? historyUrl; +/// } +/// ``` +/// {@end-tool} +@immutable +class LoadRequestParams { + /// Used by the platform implementation to create a new [LoadRequestParams]. + const LoadRequestParams({ + required this.uri, + required this.method, + required this.headers, + this.body, + }); + + /// URI for the request. + final Uri uri; + + /// HTTP method used to make the request. + final LoadRequestMethod method; + + /// Headers for the request. + final Map headers; + + /// HTTP body for the request. + final Uint8List? body; +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_navigation_delegate_creation_params.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_navigation_delegate_creation_params.dart new file mode 100644 index 000000000000..b20e5eb3ed48 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_navigation_delegate_creation_params.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/material.dart'; + +/// Object specifying creation parameters for creating a [PlatformNavigationDelegate]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// {@tool sample} +/// This example demonstrates how to extend the [PlatformNavigationDelegateCreationParams] to +/// provide additional platform specific parameters. +/// +/// When extending [PlatformNavigationDelegateCreationParams] additional +/// parameters should always accept `null` or have a default value to prevent +/// breaking changes. +/// +/// ```dart +/// class AndroidNavigationDelegateCreationParams extends PlatformNavigationDelegateCreationParams { +/// AndroidNavigationDelegateCreationParams._( +/// // This parameter prevents breaking changes later. +/// // ignore: avoid_unused_constructor_parameters +/// PlatformNavigationDelegateCreationParams params, { +/// this.filter, +/// }) : super(); +/// +/// factory AndroidNavigationDelegateCreationParams.fromPlatformNavigationDelegateCreationParams( +/// PlatformNavigationDelegateCreationParams params, { +/// String? filter, +/// }) { +/// return AndroidNavigationDelegateCreationParams._(params, filter: filter); +/// } +/// +/// final String? filter; +/// } +/// ``` +/// {@end-tool} +@immutable +class PlatformNavigationDelegateCreationParams { + /// Used by the platform implementation to create a new [PlatformNavigationkDelegate]. + const PlatformNavigationDelegateCreationParams(); +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_controller_creation_params.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_controller_creation_params.dart new file mode 100644 index 000000000000..778396a79845 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_controller_creation_params.dart @@ -0,0 +1,45 @@ +// 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'; + +/// Object specifying creation parameters for creating a [PlatformWebViewController]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// {@tool sample} +/// This example demonstrates how to extend the [PlatformWebViewControllerCreationParams] to +/// provide additional platform specific parameters. +/// +/// When extending [PlatformWebViewControllerCreationParams] additional parameters +/// should always accept `null` or have a default value to prevent breaking +/// changes. +/// +/// ```dart +/// class WKWebViewControllerCreationParams +/// extends PlatformWebViewControllerCreationParams { +/// WKWebViewControllerCreationParams._( +/// // This parameter prevents breaking changes later. +/// // ignore: avoid_unused_constructor_parameters +/// PlatformWebViewControllerCreationParams params, { +/// this.domain, +/// }) : super(); +/// +/// factory WKWebViewControllerCreationParams.fromPlatformWebViewControllerCreationParams( +/// PlatformWebViewControllerCreationParams params, { +/// String? domain, +/// }) { +/// return WKWebViewControllerCreationParams._(params, domain: domain); +/// } +/// +/// final String? domain; +/// } +/// ``` +/// {@end-tool} +@immutable +class PlatformWebViewControllerCreationParams { + /// Used by the platform implementation to create a new [PlatformWebViewController]. + const PlatformWebViewControllerCreationParams(); +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_cookie_manager_creation_params.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_cookie_manager_creation_params.dart new file mode 100644 index 000000000000..e8c4938f649f --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_cookie_manager_creation_params.dart @@ -0,0 +1,45 @@ +// 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'; + +/// Object specifying creation parameters for creating a [PlatformWebViewCookieManager]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// {@tool sample} +/// This example demonstrates how to extend the [PlatformWebViewCookieManagerCreationParams] to +/// provide additional platform specific parameters. +/// +/// When extending [PlatformWebViewCookieManagerCreationParams] additional +/// parameters should always accept `null` or have a default value to prevent +/// breaking changes. +/// +/// ```dart +/// class WKWebViewCookieManagerCreationParams +/// extends PlatformWebViewCookieManagerCreationParams { +/// WKWebViewCookieManagerCreationParams._( +/// // This parameter prevents breaking changes later. +/// // ignore: avoid_unused_constructor_parameters +/// PlatformWebViewCookieManagerCreationParams params, { +/// this.uri, +/// }) : super(); +/// +/// factory WKWebViewCookieManagerCreationParams.fromPlatformWebViewCookieManagerCreationParams( +/// PlatformWebViewCookieManagerCreationParams params, { +/// Uri? uri, +/// }) { +/// return WKWebViewCookieManagerCreationParams._(params, uri: uri); +/// } +/// +/// final Uri? uri; +/// } +/// ``` +/// {@end-tool} +@immutable +class PlatformWebViewCookieManagerCreationParams { + /// Used by the platform implementation to create a new [PlatformWebViewCookieManagerDelegate]. + const PlatformWebViewCookieManagerCreationParams(); +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_widget_creation_params.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_widget_creation_params.dart new file mode 100644 index 000000000000..1812d7e39c29 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_widget_creation_params.dart @@ -0,0 +1,79 @@ +// 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/gestures.dart'; + +import '../platform_webview_controller.dart'; + +/// Object specifying creation parameters for creating a [WebViewWidgetDelegate]. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// {@tool sample} +/// This example demonstrates how to extend the [PlatformWebViewWidgetCreationParams] to +/// provide additional platform specific parameters. +/// +/// When extending [PlatformWebViewWidgetCreationParams] additional parameters +/// should always accept `null` or have a default value to prevent breaking +/// changes. +/// +/// ```dart +/// class WKWebViewWidgetCreationParams extends PlatformWebViewWidgetCreationParams { +/// WKWebViewWidgetCreationParams._( +/// // This parameter prevents breaking changes later. +/// // ignore: avoid_unused_constructor_parameters +/// PlatformWebViewWidgetCreationParams params, { +/// this.domain, +/// }) : super( +/// key: params.key, +/// controller: params.controller, +/// gestureRecognizers: params.gestureRecognizers, +/// ); +/// +/// factory WKWebViewWidgetCreationParams.fromPlatformWebViewWidgetCreationParams( +/// PlatformWebViewWidgetCreationParams params, { +/// String? domain, +/// }) { +/// return WKWebViewWidgetCreationParams._(params, domain: domain); +/// } +/// +/// final String? domain; +/// } +/// ``` +/// {@end-tool} +@immutable +class PlatformWebViewWidgetCreationParams { + /// Used by the platform implementation to create a new [PlatformWebViewWidget]. + const PlatformWebViewWidgetCreationParams({ + this.key, + required this.controller, + this.gestureRecognizers = const >{}, + }); + + /// Controls how one widget replaces another widget in the tree. + /// + /// See also: + /// + /// * The discussions at [Key] and [GlobalKey]. + final Key? key; + + /// The [PlatformWebViewController] that allows controlling the native web + /// view. + final PlatformWebViewController controller; + + /// The `gestureRecognizers` specifies which gestures should be consumed by the + /// web view. + /// + /// It is possible for other gesture recognizers to be competing with the web + /// view on pointer events, e.g if the web view is inside a [ListView] the + /// [ListView] will want to handle vertical drags. The web view will claim + /// gestures that are recognized by any of the recognizers on this list. + /// + /// When `gestureRecognizers` is empty (default), the web view will only handle + /// pointer events for gestures that were not claimed by any other gesture + /// recognizer. + final Set> gestureRecognizers; +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/types.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/types.dart new file mode 100644 index 000000000000..05504fffd211 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/types.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. + +export 'javascript_message.dart'; +export 'javascript_mode.dart'; +export 'load_request_params.dart'; +export 'platform_navigation_delegate_creation_params.dart'; +export 'platform_webview_controller_creation_params.dart'; +export 'platform_webview_cookie_manager_creation_params.dart'; +export 'platform_webview_widget_creation_params.dart'; +export 'web_resource_error.dart'; +export 'webview_cookie.dart'; diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/web_resource_error.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/web_resource_error.dart new file mode 100644 index 000000000000..465799472912 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/web_resource_error.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'; + +/// Possible error type categorizations used by [WebResourceError]. +enum WebResourceErrorType { + /// User authentication failed on server. + authentication, + + /// Malformed URL. + badUrl, + + /// Failed to connect to the server. + connect, + + /// Failed to perform SSL handshake. + failedSslHandshake, + + /// Generic file error. + file, + + /// File not found. + fileNotFound, + + /// Server or proxy hostname lookup failed. + hostLookup, + + /// Failed to read or write to the server. + io, + + /// User authentication failed on proxy. + proxyAuthentication, + + /// Too many redirects. + redirectLoop, + + /// Connection timed out. + timeout, + + /// Too many requests during this load. + tooManyRequests, + + /// Generic error. + unknown, + + /// Resource load was canceled by Safe Browsing. + unsafeResource, + + /// Unsupported authentication scheme (not basic or digest). + unsupportedAuthScheme, + + /// Unsupported URI scheme. + unsupportedScheme, + + /// The web content process was terminated. + webContentProcessTerminated, + + /// The web view was invalidated. + webViewInvalidated, + + /// A JavaScript exception occurred. + javaScriptExceptionOccurred, + + /// The result of JavaScript execution could not be returned. + javaScriptResultTypeIsUnsupported, +} + +/// Error returned in `WebView.onWebResourceError` when a web resource loading error has occurred. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// {@tool sample} +/// This example demonstrates how to extend the [WebResourceError] to +/// provide additional platform specific parameters. +/// +/// When extending [WebResourceError] additional parameters should always +/// accept `null` or have a default value to prevent breaking changes. +/// +/// ```dart +/// class IOSWebResourceError extends WebResourceError { +/// IOSWebResourceError._(WebResourceError error, {required this.domain}) +/// : super( +/// errorCode: error.errorCode, +/// description: error.description, +/// errorType: error.errorType, +/// ); +/// +/// factory IOSWebResourceError.fromWebResourceError( +/// WebResourceError error, { +/// required String? domain, +/// }) { +/// return IOSWebResourceError._(error, domain: domain); +/// } +/// +/// final String? domain; +/// } +/// ``` +/// {@end-tool} +@immutable +class WebResourceError { + /// Used by the platform implementation to create a new [WebResourceError]. + const WebResourceError({ + required this.errorCode, + required this.description, + this.errorType, + }); + + /// Raw code of the error from the respective platform. + final int errorCode; + + /// Description of the error that can be used to communicate the problem to the user. + final String description; + + /// The type this error can be categorized as. + final WebResourceErrorType? errorType; +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/webview_cookie.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/webview_cookie.dart new file mode 100644 index 000000000000..7f56a312049f --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/webview_cookie.dart @@ -0,0 +1,41 @@ +// 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'; + +/// A cookie that can be set globally for all web views using [WebViewCookieManagerPlatform]. +@immutable +class WebViewCookie { + /// Creates a new [WebViewCookieDelegate] + const WebViewCookie({ + required this.name, + required this.value, + required this.domain, + this.path = '/', + }); + + /// The cookie-name of the cookie. + /// + /// Its value should match "cookie-name" in RFC6265bis: + /// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1 + final String name; + + /// The cookie-value of the cookie. + /// + /// Its value should match "cookie-value" in RFC6265bis: + /// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1 + final String value; + + /// The domain-value of the cookie. + /// + /// Its value should match "domain-value" in RFC6265bis: + /// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1 + final String domain; + + /// The path-value of the cookie, set to `/` by default. + /// + /// Its value should match "path-value" in RFC6265bis: + /// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1 + final String path; +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/webview_platform.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/webview_platform.dart new file mode 100644 index 000000000000..c5c5dffc6a22 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/webview_platform.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:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'platform_navigation_delegate.dart'; +import 'platform_webview_controller.dart'; +import 'platform_webview_cookie_manager.dart'; +import 'platform_webview_widget.dart'; +import 'types/types.dart'; + +export 'types/types.dart'; + +/// Interface for a platform implementation of a WebView. +abstract class WebViewPlatform extends PlatformInterface { + /// Creates a new [WebViewPlatform]. + WebViewPlatform() : super(token: _token); + + static final Object _token = Object(); + + static WebViewPlatform? _instance; + + /// The instance of [WebViewPlatform] to use. + static WebViewPlatform? get instance => _instance; + + /// Platform-specific plugins should set this with their own platform-specific + /// class that extends [WebViewPlatform] when they register themselves. + static set instance(WebViewPlatform? instance) { + if (instance == null) { + throw AssertionError( + 'Platform interfaces can only be set to a non-null instance'); + } + + PlatformInterface.verify(instance, _token); + _instance = instance; + } + + /// Creates a new [PlatformWebViewCookieManager]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebViewCookieManager] in `webview_flutter` instead. + PlatformWebViewCookieManager createPlatformCookieManager( + PlatformWebViewCookieManagerCreationParams params, + ) { + throw UnimplementedError( + 'createPlatformCookieManager is not implemented on the current platform.'); + } + + /// Creates a new [PlatformNavigationDelegate]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [NavigationDelegate] in `webview_flutter` instead. + PlatformNavigationDelegate createPlatformNavigationDelegate( + PlatformNavigationDelegateCreationParams params, + ) { + throw UnimplementedError( + 'createPlatformNavigationDelegate is not implemented on the current platform.'); + } + + /// Create a new [PlatformWebViewController]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebViewController] in `webview_flutter` instead. + PlatformWebViewController createPlatformWebViewController( + PlatformWebViewControllerCreationParams params, + ) { + throw UnimplementedError( + 'createPlatformWebViewController is not implemented on the current platform.'); + } + + /// Create a new [PlatformWebViewWidget]. + /// + /// This function should only be called by the app-facing package. + /// Look at using [WebViewWidget] in `webview_flutter` instead. + PlatformWebViewWidget createPlatformWebViewWidget( + PlatformWebViewWidgetCreationParams params, + ) { + throw UnimplementedError( + 'createPlatformWebViewWidget is not implemented on the current platform.'); + } +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/webview_flutter_platform_interface.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/webview_flutter_platform_interface.dart new file mode 100644 index 000000000000..d14fec163327 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/webview_flutter_platform_interface.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/platform_navigation_delegate.dart'; +export 'src/platform_webview_controller.dart'; +export 'src/platform_webview_cookie_manager.dart'; +export 'src/platform_webview_widget.dart'; +export 'src/types/types.dart'; +export 'src/webview_platform.dart'; diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml index f9e754931bb8..c339a0f4a2ce 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutte issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview_flutter%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.8.2 +version: 1.9.0 environment: sdk: ">=2.12.0 <3.0.0" @@ -13,9 +13,11 @@ environment: dependencies: flutter: sdk: flutter + meta: ^1.7.0 plugin_platform_interface: ^2.1.0 dev_dependencies: + build_runner: ^2.1.8 flutter_test: sdk: flutter mockito: ^5.0.0 diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_navigation_delegate_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_navigation_delegate_test.dart new file mode 100644 index 000000000000..5674c1522408 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_navigation_delegate_test.dart @@ -0,0 +1,141 @@ +// 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:mockito/mockito.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:webview_flutter_platform_interface/v4/src/platform_navigation_delegate.dart'; +import 'package:webview_flutter_platform_interface/v4/src/webview_platform.dart'; + +import 'webview_platform_test.mocks.dart'; + +void main() { + setUp(() { + WebViewPlatform.instance = MockWebViewPlatformWithMixin(); + }); + + test('Cannot be implemented with `implements`', () { + const PlatformNavigationDelegateCreationParams params = + PlatformNavigationDelegateCreationParams(); + when(WebViewPlatform.instance!.createPlatformNavigationDelegate(params)) + .thenReturn(ImplementsPlatformNavigationDelegate()); + + expect(() { + PlatformNavigationDelegate(params); + }, throwsNoSuchMethodError); + }); + + test('Can be extended', () { + const PlatformNavigationDelegateCreationParams params = + PlatformNavigationDelegateCreationParams(); + when(WebViewPlatform.instance!.createPlatformNavigationDelegate(params)) + .thenReturn(ExtendsPlatformNavigationDelegate(params)); + + expect(PlatformNavigationDelegate(params), isNotNull); + }); + + test('Can be mocked with `implements`', () { + const PlatformNavigationDelegateCreationParams params = + PlatformNavigationDelegateCreationParams(); + when(WebViewPlatform.instance!.createPlatformNavigationDelegate(params)) + .thenReturn(MockNavigationDelegate()); + + expect(PlatformNavigationDelegate(params), isNotNull); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of setOnNavigationRequest should throw unimplemented error', + () { + final PlatformNavigationDelegate callbackDelegate = + ExtendsPlatformNavigationDelegate( + const PlatformNavigationDelegateCreationParams()); + + expect( + () => callbackDelegate.setOnNavigationRequest( + ({required bool isForMainFrame, required String url}) => true), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of setOnPageStarted should throw unimplemented error', + () { + final PlatformNavigationDelegate callbackDelegate = + ExtendsPlatformNavigationDelegate( + const PlatformNavigationDelegateCreationParams()); + + expect( + () => callbackDelegate.setOnPageStarted((String url) {}), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of setOnPageFinished should throw unimplemented error', + () { + final PlatformNavigationDelegate callbackDelegate = + ExtendsPlatformNavigationDelegate( + const PlatformNavigationDelegateCreationParams()); + + expect( + () => callbackDelegate.setOnPageFinished((String url) {}), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of setOnProgress should throw unimplemented error', + () { + final PlatformNavigationDelegate callbackDelegate = + ExtendsPlatformNavigationDelegate( + const PlatformNavigationDelegateCreationParams()); + + expect( + () => callbackDelegate.setOnProgress((int progress) {}), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of setOnWebResourceError should throw unimplemented error', + () { + final PlatformNavigationDelegate callbackDelegate = + ExtendsPlatformNavigationDelegate( + const PlatformNavigationDelegateCreationParams()); + + expect( + () => callbackDelegate.setOnWebResourceError((WebResourceError error) {}), + throwsUnimplementedError, + ); + }); +} + +class MockWebViewPlatformWithMixin extends MockWebViewPlatform + with + // ignore: prefer_mixin + MockPlatformInterfaceMixin {} + +class ImplementsPlatformNavigationDelegate + implements PlatformNavigationDelegate { + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + +class MockNavigationDelegate extends Mock + with + // ignore: prefer_mixin + MockPlatformInterfaceMixin + implements + PlatformNavigationDelegate {} + +class ExtendsPlatformNavigationDelegate extends PlatformNavigationDelegate { + ExtendsPlatformNavigationDelegate( + PlatformNavigationDelegateCreationParams params) + : super.implementation(params); +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.dart new file mode 100644 index 000000000000..b6d043cac9c8 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.dart @@ -0,0 +1,467 @@ +// 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:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:webview_flutter_platform_interface/v4/src/platform_navigation_delegate.dart'; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_controller.dart'; +import 'package:webview_flutter_platform_interface/v4/src/webview_platform.dart'; + +import 'platform_navigation_delegate_test.dart'; +import 'webview_platform_test.mocks.dart'; + +@GenerateMocks([PlatformNavigationDelegate]) +void main() { + setUp(() { + WebViewPlatform.instance = MockWebViewPlatformWithMixin(); + }); + + test('Cannot be implemented with `implements`', () { + when((WebViewPlatform.instance! as MockWebViewPlatform) + .createPlatformWebViewController(any)) + .thenReturn(ImplementsPlatformWebViewController()); + + expect(() { + PlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + }, throwsNoSuchMethodError); + }); + + test('Can be extended', () { + const PlatformWebViewControllerCreationParams params = + PlatformWebViewControllerCreationParams(); + when((WebViewPlatform.instance! as MockWebViewPlatform) + .createPlatformWebViewController(any)) + .thenReturn(ExtendsPlatformWebViewController(params)); + + expect(PlatformWebViewController(params), isNotNull); + }); + + test('Can be mocked with `implements`', () { + when((WebViewPlatform.instance! as MockWebViewPlatform) + .createPlatformWebViewController(any)) + .thenReturn(MockWebViewControllerDelegate()); + + expect( + PlatformWebViewController( + const PlatformWebViewControllerCreationParams()), + isNotNull); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of loadFile should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.loadFile(''), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of loadFlutterAsset should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.loadFlutterAsset(''), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of loadHtmlString should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.loadHtmlString(''), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of loadRequest should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.loadRequest(MockLoadRequestParamsDelegate()), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of currentUrl should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.currentUrl(), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of canGoBack should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.canGoBack(), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of canGoForward should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.canGoForward(), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of goBack should throw unimplemented error', () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.goBack(), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of goForward should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.goForward(), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of reload should throw unimplemented error', () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.reload(), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of clearCache should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.clearCache(), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of clearLocalStorage should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.clearLocalStorage(), + throwsUnimplementedError, + ); + }); + + test( + 'Default implementation of the setNavigationCallback should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => + controller.setPlatformNavigationDelegate(MockNavigationDelegate()), + throwsUnimplementedError, + ); + }, + ); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of runJavaScript should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.runJavaScript('javaScript'), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of runJavaScriptReturningResult should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.runJavaScriptReturningResult('javaScript'), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of addJavaScriptChannel should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.addJavaScriptChannel( + JavaScriptChannelParams( + name: 'test', + onMessageReceived: (_) {}, + ), + ), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of removeJavaScriptChannel should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.removeJavaScriptChannel('test'), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of getTitle should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.getTitle(), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of scrollTo should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.scrollTo(0, 0), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of scrollBy should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.scrollBy(0, 0), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of getScrollPosition should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.getScrollPosition(), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of enableDebugging should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.enableDebugging(true), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of enableGestureNavigation should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.enableGestureNavigation(true), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of enableZoom should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.enableZoom(true), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of setBackgroundColor should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.setBackgroundColor(Colors.blue), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of setJavaScriptMode should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.setJavaScriptMode(JavaScriptMode.disabled), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of setUserAgent should throw unimplemented error', + () { + final PlatformWebViewController controller = + ExtendsPlatformWebViewController( + const PlatformWebViewControllerCreationParams()); + + expect( + () => controller.setUserAgent(null), + throwsUnimplementedError, + ); + }); +} + +class MockWebViewPlatformWithMixin extends MockWebViewPlatform + with + // ignore: prefer_mixin + MockPlatformInterfaceMixin {} + +class ImplementsPlatformWebViewController implements PlatformWebViewController { + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + +class MockWebViewControllerDelegate extends Mock + with + // ignore: prefer_mixin + MockPlatformInterfaceMixin + implements + PlatformWebViewController {} + +class ExtendsPlatformWebViewController extends PlatformWebViewController { + ExtendsPlatformWebViewController( + PlatformWebViewControllerCreationParams params) + : super.implementation(params); +} + +// ignore: must_be_immutable +class MockLoadRequestParamsDelegate extends Mock + with + //ignore: prefer_mixin + MockPlatformInterfaceMixin + implements + LoadRequestParams {} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.mocks.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.mocks.dart new file mode 100644 index 000000000000..47e67379f124 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.mocks.dart @@ -0,0 +1,72 @@ +// Mocks generated by Mockito 5.0.16 from annotations +// in webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i4; + +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_platform_interface/v4/src/platform_navigation_delegate.dart' + as _i3; +import 'package:webview_flutter_platform_interface/v4/src/webview_platform.dart' + as _i2; + +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// 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 + +class _FakePlatformNavigationDelegateCreationParams_0 extends _i1.Fake + implements _i2.PlatformNavigationDelegateCreationParams {} + +/// A class which mocks [PlatformNavigationDelegate]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockPlatformNavigationDelegate extends _i1.Mock + implements _i3.PlatformNavigationDelegate { + MockPlatformNavigationDelegate() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.PlatformNavigationDelegateCreationParams get params => + (super.noSuchMethod(Invocation.getter(#params), + returnValue: _FakePlatformNavigationDelegateCreationParams_0()) + as _i2.PlatformNavigationDelegateCreationParams); + @override + _i4.Future setOnNavigationRequest( + _i4.FutureOr Function({bool isForMainFrame, String url})? + onNavigationRequest) => + (super.noSuchMethod( + Invocation.method(#setOnNavigationRequest, [onNavigationRequest]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future setOnPageStarted(void Function(String)? onPageStarted) => + (super.noSuchMethod(Invocation.method(#setOnPageStarted, [onPageStarted]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future setOnPageFinished(void Function(String)? onPageFinished) => + (super.noSuchMethod( + Invocation.method(#setOnPageFinished, [onPageFinished]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future setOnProgress(void Function(int)? onProgress) => + (super.noSuchMethod(Invocation.method(#setOnProgress, [onProgress]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future setOnWebResourceError( + void Function(_i2.WebResourceError)? onWebResourceError) => + (super.noSuchMethod( + Invocation.method(#setOnWebResourceError, [onWebResourceError]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + String toString() => super.toString(); +} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.dart new file mode 100644 index 000000000000..30fa52ece24a --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.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 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_controller.dart'; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_widget.dart'; +import 'package:webview_flutter_platform_interface/v4/src/webview_platform.dart'; + +import 'webview_platform_test.mocks.dart'; + +void main() { + setUp(() { + WebViewPlatform.instance = MockWebViewPlatformWithMixin(); + }); + + test('Cannot be implemented with `implements`', () { + final MockWebViewControllerDelegate controller = + MockWebViewControllerDelegate(); + final PlatformWebViewWidgetCreationParams params = + PlatformWebViewWidgetCreationParams(controller: controller); + when(WebViewPlatform.instance!.createPlatformWebViewWidget(params)) + .thenReturn(ImplementsWebViewWidgetDelegate()); + + expect(() { + PlatformWebViewWidget(params); + }, throwsNoSuchMethodError); + }); + + test('Can be extended', () { + final MockWebViewControllerDelegate controller = + MockWebViewControllerDelegate(); + final PlatformWebViewWidgetCreationParams params = + PlatformWebViewWidgetCreationParams(controller: controller); + when(WebViewPlatform.instance!.createPlatformWebViewWidget(params)) + .thenReturn(ExtendsWebViewWidgetDelegate(params)); + + expect(PlatformWebViewWidget(params), isNotNull); + }); + + test('Can be mocked with `implements`', () { + final MockWebViewControllerDelegate controller = + MockWebViewControllerDelegate(); + final PlatformWebViewWidgetCreationParams params = + PlatformWebViewWidgetCreationParams(controller: controller); + when(WebViewPlatform.instance!.createPlatformWebViewWidget(params)) + .thenReturn(MockWebViewWidgetDelegate()); + + expect(PlatformWebViewWidget(params), isNotNull); + }); +} + +class MockWebViewPlatformWithMixin extends MockWebViewPlatform + with + // ignore: prefer_mixin + MockPlatformInterfaceMixin {} + +class ImplementsWebViewWidgetDelegate implements PlatformWebViewWidget { + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + +class MockWebViewWidgetDelegate extends Mock + with + // ignore: prefer_mixin + MockPlatformInterfaceMixin + implements + PlatformWebViewWidget {} + +class ExtendsWebViewWidgetDelegate extends PlatformWebViewWidget { + ExtendsWebViewWidgetDelegate(PlatformWebViewWidgetCreationParams params) + : super.implementation(params); + + @override + Widget build(BuildContext context) { + throw UnimplementedError( + 'build is not implemented for ExtendedWebViewWidgetDelegate.'); + } +} + +class MockWebViewControllerDelegate extends Mock + with + // ignore: prefer_mixin + MockPlatformInterfaceMixin + implements + PlatformWebViewController {} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.dart new file mode 100644 index 000000000000..f09156919512 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.dart @@ -0,0 +1,109 @@ +// 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:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_controller.dart'; +import 'package:webview_flutter_platform_interface/v4/src/webview_platform.dart'; + +import 'webview_platform_test.mocks.dart'; + +@GenerateMocks([WebViewPlatform]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + test('Default instance WebViewPlatform instance should be null', () { + expect(WebViewPlatform.instance, isNull); + }); + + test('Cannot be implemented with `implements`', () { + expect(() { + WebViewPlatform.instance = ImplementsWebViewPlatform(); + }, throwsNoSuchMethodError); + }); + + test('Can be extended', () { + WebViewPlatform.instance = ExtendsWebViewPlatform(); + }); + + test('Can be mocked with `implements`', () { + final MockWebViewPlatform mock = MockWebViewPlatformWithMixin(); + WebViewPlatform.instance = mock; + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of createCookieManagerDelegate should throw unimplemented error', + () { + final WebViewPlatform webViewPlatform = ExtendsWebViewPlatform(); + + expect( + () => webViewPlatform.createPlatformCookieManager( + const PlatformWebViewCookieManagerCreationParams()), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of createNavigationCallbackHandlerDelegate should throw unimplemented error', + () { + final WebViewPlatform webViewPlatform = ExtendsWebViewPlatform(); + + expect( + () => webViewPlatform.createPlatformNavigationDelegate( + const PlatformNavigationDelegateCreationParams()), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of createWebViewControllerDelegate should throw unimplemented error', + () { + final WebViewPlatform webViewPlatform = ExtendsWebViewPlatform(); + + expect( + () => webViewPlatform.createPlatformWebViewController( + const PlatformWebViewControllerCreationParams()), + throwsUnimplementedError, + ); + }); + + test( + // ignore: lines_longer_than_80_chars + 'Default implementation of createWebViewWidgetDelegate should throw unimplemented error', + () { + final WebViewPlatform webViewPlatform = ExtendsWebViewPlatform(); + final MockWebViewControllerDelegate controller = + MockWebViewControllerDelegate(); + + expect( + () => webViewPlatform.createPlatformWebViewWidget( + PlatformWebViewWidgetCreationParams(controller: controller)), + throwsUnimplementedError, + ); + }); +} + +class ImplementsWebViewPlatform implements WebViewPlatform { + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + +class MockWebViewPlatformWithMixin extends MockWebViewPlatform + with + // ignore: prefer_mixin + MockPlatformInterfaceMixin {} + +class ExtendsWebViewPlatform extends WebViewPlatform {} + +class MockWebViewControllerDelegate extends Mock + with + // ignore: prefer_mixin + MockPlatformInterfaceMixin + implements + PlatformWebViewController {} diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.mocks.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.mocks.dart new file mode 100644 index 000000000000..5ce007579473 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.mocks.dart @@ -0,0 +1,78 @@ +// Mocks generated by Mockito 5.0.16 from annotations +// in webview_flutter_platform_interface/test/src/v4/webview_platform_test.dart. +// Do not manually edit this file. + +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_platform_interface/v4/src/platform_navigation_delegate.dart' + as _i3; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_controller.dart' + as _i4; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_cookie_manager.dart' + as _i2; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_widget.dart' + as _i5; +import 'package:webview_flutter_platform_interface/v4/src/types/types.dart' + as _i7; +import 'package:webview_flutter_platform_interface/v4/src/webview_platform.dart' + as _i6; + +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// 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 + +class _FakePlatformWebViewCookieManager_0 extends _i1.Fake + implements _i2.PlatformWebViewCookieManager {} + +class _FakePlatformNavigationDelegate_1 extends _i1.Fake + implements _i3.PlatformNavigationDelegate {} + +class _FakePlatformWebViewController_2 extends _i1.Fake + implements _i4.PlatformWebViewController {} + +class _FakePlatformWebViewWidget_3 extends _i1.Fake + implements _i5.PlatformWebViewWidget {} + +/// A class which mocks [WebViewPlatform]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWebViewPlatform extends _i1.Mock implements _i6.WebViewPlatform { + MockWebViewPlatform() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.PlatformWebViewCookieManager createPlatformCookieManager( + _i7.PlatformWebViewCookieManagerCreationParams? params) => + (super.noSuchMethod( + Invocation.method(#createPlatformCookieManager, [params]), + returnValue: _FakePlatformWebViewCookieManager_0()) + as _i2.PlatformWebViewCookieManager); + @override + _i3.PlatformNavigationDelegate createPlatformNavigationDelegate( + _i7.PlatformNavigationDelegateCreationParams? params) => + (super.noSuchMethod( + Invocation.method(#createPlatformNavigationDelegate, [params]), + returnValue: _FakePlatformNavigationDelegate_1()) + as _i3.PlatformNavigationDelegate); + @override + _i4.PlatformWebViewController createPlatformWebViewController( + _i7.PlatformWebViewControllerCreationParams? params) => + (super.noSuchMethod( + Invocation.method(#createPlatformWebViewController, [params]), + returnValue: _FakePlatformWebViewController_2()) + as _i4.PlatformWebViewController); + @override + _i5.PlatformWebViewWidget createPlatformWebViewWidget( + _i7.PlatformWebViewWidgetCreationParams? params) => + (super.noSuchMethod( + Invocation.method(#createPlatformWebViewWidget, [params]), + returnValue: _FakePlatformWebViewWidget_3()) + as _i5.PlatformWebViewWidget); + @override + String toString() => super.toString(); +} From bd3f490353b059b510c6f4e52e5d1118af2445ba Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Wed, 18 May 2022 07:17:12 -0700 Subject: [PATCH 326/844] [camera] Request access permission for audio (#5766) --- packages/camera/camera/CHANGELOG.md | 4 + packages/camera/camera/README.md | 8 +- .../ios/RunnerTests/CameraPermissionTests.m | 108 ++++++++++++++++++ packages/camera/camera/example/lib/main.dart | 11 ++ .../ios/Classes/CameraPermissionUtils.h | 12 ++ .../ios/Classes/CameraPermissionUtils.m | 92 +++++++++++---- .../camera/camera/ios/Classes/CameraPlugin.m | 38 ++++-- packages/camera/camera/pubspec.yaml | 2 +- 8 files changed, 242 insertions(+), 33 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index d101f60cf041..9af23011ec50 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.6 + +* Adds audio access permission handling logic on iOS to fix an issue with `prepareForVideoRecording` not awaiting for the audio permission request result. + ## 0.9.5+1 * Suppresses warnings for pre-iOS-11 codepaths. diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index 6b2ed7a6b687..ec9d7379c60b 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -88,10 +88,16 @@ Here is a list of all permission error codes that can be thrown: - `CameraAccessDenied`: Thrown when user denies the camera access permission. -- `CameraAccessDeniedWithoutPrompt`: iOS only for now. Thrown when user has previously denied the permission. iOS does not allow prompting alert dialog a second time. Users will have to go to Settings > Privacy in order to enable camera access. +- `CameraAccessDeniedWithoutPrompt`: iOS only for now. Thrown when user has previously denied the permission. iOS does not allow prompting alert dialog a second time. Users will have to go to Settings > Privacy > Camera in order to enable camera access. - `CameraAccessRestricted`: iOS only for now. Thrown when camera access is restricted and users cannot grant permission (parental control). +- `AudioAccessDenied`: Thrown when user denies the audio access permission. + +- `AudioAccessDeniedWithoutPrompt`: iOS only for now. Thrown when user has previously denied the permission. iOS does not allow prompting alert dialog a second time. Users will have to go to Settings > Privacy > Microphone in order to enable audio access. + +- `AudioAccessRestricted`: iOS only for now. Thrown when audio access is restricted and users cannot grant permission (parental control). + - `cameraPermission`: Android and Web only. A legacy error code for all kinds of camera permission errors. ### Example diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m b/packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m index 961b931b7704..541e0288254c 100644 --- a/packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m +++ b/packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m @@ -15,6 +15,8 @@ @interface CameraPermissionTests : XCTestCase @implementation CameraPermissionTests +#pragma mark - camera permissions + - (void)testRequestCameraPermission_completeWithoutErrorIfPrevoiuslyAuthorized { XCTestExpectation *expectation = [self expectationWithDescription: @@ -120,4 +122,110 @@ - (void)testRequestCameraPermission_completeWithErrorIfUserDenyAccess { [self waitForExpectationsWithTimeout:1 handler:nil]; } +#pragma mark - audio permissions + +- (void)testRequestAudioPermission_completeWithoutErrorIfPrevoiuslyAuthorized { + XCTestExpectation *expectation = + [self expectationWithDescription: + @"Must copmlete without error if audio access was previously authorized."]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio]) + .andReturn(AVAuthorizationStatusAuthorized); + + FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) { + if (error == nil) { + [expectation fulfill]; + } + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} +- (void)testRequestAudioPermission_completeWithErrorIfPreviouslyDenied { + XCTestExpectation *expectation = + [self expectationWithDescription: + @"Must complete with error if audio access was previously denied."]; + FlutterError *expectedError = + [FlutterError errorWithCode:@"AudioAccessDeniedWithoutPrompt" + message:@"User has previously denied the audio access request. Go to " + @"Settings to enable audio access." + details:nil]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio]) + .andReturn(AVAuthorizationStatusDenied); + FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) { + if ([error isEqual:expectedError]) { + [expectation fulfill]; + } + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testRequestAudioPermission_completeWithErrorIfRestricted { + XCTestExpectation *expectation = + [self expectationWithDescription:@"Must complete with error if audio access is restricted."]; + FlutterError *expectedError = [FlutterError errorWithCode:@"AudioAccessRestricted" + message:@"Audio access is restricted. " + details:nil]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio]) + .andReturn(AVAuthorizationStatusRestricted); + + FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) { + if ([error isEqual:expectedError]) { + [expectation fulfill]; + } + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testRequestAudioPermission_completeWithoutErrorIfUserGrantAccess { + XCTestExpectation *grantedExpectation = [self + expectationWithDescription:@"Must complete without error if user choose to grant access"]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio]) + .andReturn(AVAuthorizationStatusNotDetermined); + // Mimic user choosing "allow" in permission dialog. + OCMStub([mockDevice requestAccessForMediaType:AVMediaTypeAudio + completionHandler:[OCMArg checkWithBlock:^BOOL(void (^block)(BOOL)) { + block(YES); + return YES; + }]]); + + FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) { + if (error == nil) { + [grantedExpectation fulfill]; + } + }); + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testRequestAudioPermission_completeWithErrorIfUserDenyAccess { + XCTestExpectation *expectation = + [self expectationWithDescription:@"Must complete with error if user choose to deny access"]; + FlutterError *expectedError = [FlutterError errorWithCode:@"AudioAccessDenied" + message:@"User denied the audio access request." + details:nil]; + + id mockDevice = OCMClassMock([AVCaptureDevice class]); + OCMStub([mockDevice authorizationStatusForMediaType:AVMediaTypeAudio]) + .andReturn(AVAuthorizationStatusNotDetermined); + + // Mimic user choosing "deny" in permission dialog. + OCMStub([mockDevice requestAccessForMediaType:AVMediaTypeAudio + completionHandler:[OCMArg checkWithBlock:^BOOL(void (^block)(BOOL)) { + block(NO); + return YES; + }]]); + FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) { + if ([error isEqual:expectedError]) { + [expectation fulfill]; + } + }); + + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + @end diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index 34942ba5aa77..c0181a5d36a1 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -697,6 +697,17 @@ class _CameraExampleHomeState extends State // iOS only showInSnackBar('Camera access is restricted.'); break; + case 'AudioAccessDenied': + showInSnackBar('You have denied audio access.'); + break; + case 'AudioAccessDeniedWithoutPrompt': + // iOS only + showInSnackBar('Please go to Settings app to enable audio access.'); + break; + case 'AudioAccessRestricted': + // iOS only + showInSnackBar('Audio access is restricted.'); + break; case 'cameraPermission': // Android & web only showInSnackBar('Unknown permission error.'); diff --git a/packages/camera/camera/ios/Classes/CameraPermissionUtils.h b/packages/camera/camera/ios/Classes/CameraPermissionUtils.h index 80f55db7be32..5cbbab055f34 100644 --- a/packages/camera/camera/ios/Classes/CameraPermissionUtils.h +++ b/packages/camera/camera/ios/Classes/CameraPermissionUtils.h @@ -18,3 +18,15 @@ typedef void (^FLTCameraPermissionRequestCompletionHandler)(FlutterError *); /// called on an arbitrary dispatch queue. extern void FLTRequestCameraPermissionWithCompletionHandler( FLTCameraPermissionRequestCompletionHandler handler); + +/// Requests audio access permission. +/// +/// If it is the first time requesting audio access, a permission dialog will show up on the +/// screen. Otherwise AVFoundation simply returns the user's previous choice, and in this case the +/// user will have to update the choice in Settings app. +/// +/// @param handler if access permission is (or was previously) granted, completion handler will be +/// called without error; Otherwise completion handler will be called with error. Handler can be +/// called on an arbitrary dispatch queue. +extern void FLTRequestAudioPermissionWithCompletionHandler( + FLTCameraPermissionRequestCompletionHandler handler); diff --git a/packages/camera/camera/ios/Classes/CameraPermissionUtils.m b/packages/camera/camera/ios/Classes/CameraPermissionUtils.m index 6318338ea6a2..098265a6b74d 100644 --- a/packages/camera/camera/ios/Classes/CameraPermissionUtils.m +++ b/packages/camera/camera/ios/Classes/CameraPermissionUtils.m @@ -5,35 +5,83 @@ @import AVFoundation; #import "CameraPermissionUtils.h" -void FLTRequestCameraPermissionWithCompletionHandler( - FLTCameraPermissionRequestCompletionHandler handler) { - switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) { +void FLTRequestPermission(BOOL forAudio, FLTCameraPermissionRequestCompletionHandler handler) { + AVMediaType mediaType; + if (forAudio) { + mediaType = AVMediaTypeAudio; + } else { + mediaType = AVMediaTypeVideo; + } + + switch ([AVCaptureDevice authorizationStatusForMediaType:mediaType]) { case AVAuthorizationStatusAuthorized: handler(nil); break; - case AVAuthorizationStatusDenied: - handler([FlutterError errorWithCode:@"CameraAccessDeniedWithoutPrompt" - message:@"User has previously denied the camera access request. " - @"Go to Settings to enable camera access." - details:nil]); + case AVAuthorizationStatusDenied: { + FlutterError *flutterError; + if (forAudio) { + flutterError = + [FlutterError errorWithCode:@"AudioAccessDeniedWithoutPrompt" + message:@"User has previously denied the audio access request. " + @"Go to Settings to enable audio access." + details:nil]; + } else { + flutterError = + [FlutterError errorWithCode:@"CameraAccessDeniedWithoutPrompt" + message:@"User has previously denied the camera access request. " + @"Go to Settings to enable camera access." + details:nil]; + } + handler(flutterError); break; - case AVAuthorizationStatusRestricted: - handler([FlutterError errorWithCode:@"CameraAccessRestricted" - message:@"Camera access is restricted. " - details:nil]); + } + case AVAuthorizationStatusRestricted: { + FlutterError *flutterError; + if (forAudio) { + flutterError = [FlutterError errorWithCode:@"AudioAccessRestricted" + message:@"Audio access is restricted. " + details:nil]; + } else { + flutterError = [FlutterError errorWithCode:@"CameraAccessRestricted" + message:@"Camera access is restricted. " + details:nil]; + } + handler(flutterError); break; + } case AVAuthorizationStatusNotDetermined: { - [AVCaptureDevice - requestAccessForMediaType:AVMediaTypeVideo - completionHandler:^(BOOL granted) { - // handler can be invoked on an arbitrary dispatch queue. - handler(granted ? nil - : [FlutterError - errorWithCode:@"CameraAccessDenied" - message:@"User denied the camera access request." - details:nil]); - }]; + [AVCaptureDevice requestAccessForMediaType:mediaType + completionHandler:^(BOOL granted) { + // handler can be invoked on an arbitrary dispatch queue. + if (granted) { + handler(nil); + } else { + FlutterError *flutterError; + if (forAudio) { + flutterError = [FlutterError + errorWithCode:@"AudioAccessDenied" + message:@"User denied the audio access request." + details:nil]; + } else { + flutterError = [FlutterError + errorWithCode:@"CameraAccessDenied" + message:@"User denied the camera access request." + details:nil]; + } + handler(flutterError); + } + }]; break; } } } + +void FLTRequestCameraPermissionWithCompletionHandler( + FLTCameraPermissionRequestCompletionHandler handler) { + FLTRequestPermission(/*forAudio*/ NO, handler); +} + +void FLTRequestAudioPermissionWithCompletionHandler( + FLTCameraPermissionRequestCompletionHandler handler) { + FLTRequestPermission(/*forAudio*/ YES, handler); +} diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index 43d541e411b4..64952e8d70f1 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -132,14 +132,7 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call [result sendNotImplemented]; } } else if ([@"create" isEqualToString:call.method]) { - FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { - // Create FLTCam only if granted camera access. - if (error) { - [result sendFlutterError:error]; - } else { - [self createCameraOnSessionQueueWithCreateMethodCall:call result:result]; - } - }); + [self handleCreateMethodCall:call result:result]; } else if ([@"startImageStream" isEqualToString:call.method]) { [_camera startImageStreamWithMessenger:_messenger]; [result sendSuccess]; @@ -194,7 +187,7 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call [_camera close]; [result sendSuccess]; } else if ([@"prepareForVideoRecording" isEqualToString:call.method]) { - [_camera setUpCaptureSessionForAudio]; + [self.camera setUpCaptureSessionForAudio]; [result sendSuccess]; } else if ([@"startVideoRecording" isEqualToString:call.method]) { [_camera startVideoRecordingWithResult:result]; @@ -258,6 +251,33 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call } } +- (void)handleCreateMethodCall:(FlutterMethodCall *)call + result:(FLTThreadSafeFlutterResult *)result { + // Create FLTCam only if granted camera access (and audio access if audio is enabled) + FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { + if (error) { + [result sendFlutterError:error]; + } else { + // Request audio permission on `create` call with `enableAudio` argument instead of the + // `prepareForVideoRecording` call. This is because `prepareForVideoRecording` call is + // optional, and used as a workaround to fix a missing frame issue on iOS. + BOOL audioEnabled = [call.arguments[@"enableAudio"] boolValue]; + if (audioEnabled) { + // Setup audio capture session only if granted audio access. + FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) { + if (error) { + [result sendFlutterError:error]; + } else { + [self createCameraOnSessionQueueWithCreateMethodCall:call result:result]; + } + }); + } else { + [self createCameraOnSessionQueueWithCreateMethodCall:call result:result]; + } + } + }); +} + - (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)createMethodCall result:(FLTThreadSafeFlutterResult *)result { dispatch_async(self.captureSessionQueue, ^{ diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 14acf32e2324..593e7b5bb978 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.5+1 +version: 0.9.6 environment: sdk: ">=2.14.0 <3.0.0" From 07467dc23408d83f1e264fcdab160ad8e7dfdae3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 18 May 2022 11:02:10 -0400 Subject: [PATCH 327/844] Roll Flutter from c248854d176b to 1994027986cf (1 revision) (#5777) --- .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 a0755e305782..73d69ae3af72 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c248854d176b92ac0d50a4ad0edc19bd12210400 +1994027986cf59a4c307d6612706ce08c16b1ae8 From 76abf7679404513981b52ae51d0a701a222db34e Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 18 May 2022 11:47:12 -0400 Subject: [PATCH 328/844] [ci/tools] Add iOS/macOS analysis to catch deprecated code (#5778) --- .cirrus.yml | 8 ++ .../google_sign_in_ios/example/ios/Podfile | 3 + script/tool/CHANGELOG.md | 3 + .../tool/lib/src/xcode_analyze_command.dart | 25 +++++- .../tool/test/xcode_analyze_command_test.dart | 76 +++++++++++++++++++ 5 files changed, 113 insertions(+), 2 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index c4abdbc5adc1..69fd8955302a 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -334,6 +334,11 @@ task: - ./script/tool_runner.sh build-examples --ios xcode_analyze_script: - ./script/tool_runner.sh xcode-analyze --ios + xcode_analyze_deprecation_script: + # Ensure we don't accidentally introduce deprecated code. + # TODO(stuartmorgan): Update this to a newer version of iOS to get + # ahead of upcoming deprecations. + - ./script/tool_runner.sh xcode-analyze --ios --ios-min-version=11.0 native_test_script: - ./script/tool_runner.sh native-test --ios --ios-destination "platform=iOS Simulator,name=iPhone 11,OS=latest" drive_script: @@ -362,6 +367,9 @@ task: - ./script/tool_runner.sh build-examples --macos xcode_analyze_script: - ./script/tool_runner.sh xcode-analyze --macos + xcode_analyze_deprecation_script: + # Ensure we don't accidentally introduce deprecated code. + - ./script/tool_runner.sh xcode-analyze --macos --macos-min-version=12.3 native_test_script: - ./script/tool_runner.sh native-test --macos drive_script: diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile b/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile index e577a3081fe8..b20e1ad2fca0 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile @@ -25,6 +25,9 @@ end require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) +# Suppress warnings from transitive dependencies that cause analysis to fail. +pod 'AppAuth', :inhibit_warnings => true + flutter_ios_podfile_setup target 'Runner' do diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 0e2a33e15eaf..04101c228793 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -2,6 +2,9 @@ - Fixes changelog validation when reverting to a `NEXT` state. - Fixes multiplication of `--force` flag when publishing multiple packages. +- Adds minimum deployment target flags to `xcode-analyze` to allow + enforcing deprecation warning handling in advance of actually dropping + support for an OS version. - Checks for template boilerplate in `readme-check`. - `readme-check` now validates example READMEs when present. diff --git a/script/tool/lib/src/xcode_analyze_command.dart b/script/tool/lib/src/xcode_analyze_command.dart index 4298acb1c7e5..a81bf15477af 100644 --- a/script/tool/lib/src/xcode_analyze_command.dart +++ b/script/tool/lib/src/xcode_analyze_command.dart @@ -23,8 +23,20 @@ class XcodeAnalyzeCommand extends PackageLoopingCommand { super(packagesDir, processRunner: processRunner, platform: platform) { argParser.addFlag(platformIOS, help: 'Analyze iOS'); argParser.addFlag(platformMacOS, help: 'Analyze macOS'); + argParser.addOption(_minIOSVersionArg, + help: 'Sets the minimum iOS deployment version to use when compiling, ' + 'overriding the default minimum version. This can be used to find ' + 'deprecation warnings that will affect the plugin in the future.'); + argParser.addOption(_minMacOSVersionArg, + help: + 'Sets the minimum macOS deployment version to use when compiling, ' + 'overriding the default minimum version. This can be used to find ' + 'deprecation warnings that will affect the plugin in the future.'); } + static const String _minIOSVersionArg = 'ios-min-version'; + static const String _minMacOSVersionArg = 'macos-min-version'; + final Xcode _xcode; @override @@ -57,15 +69,24 @@ class XcodeAnalyzeCommand extends PackageLoopingCommand { return PackageResult.skip('Not implemented for target platform(s).'); } + final String minIOSVersion = getStringArg(_minIOSVersionArg); + final String minMacOSVersion = getStringArg(_minMacOSVersionArg); + final List failures = []; if (testIOS && !await _analyzePlugin(package, 'iOS', extraFlags: [ '-destination', - 'generic/platform=iOS Simulator' + 'generic/platform=iOS Simulator', + if (minIOSVersion.isNotEmpty) + 'IPHONEOS_DEPLOYMENT_TARGET=$minIOSVersion', ])) { failures.add('iOS'); } - if (testMacOS && !await _analyzePlugin(package, 'macOS')) { + if (testMacOS && + !await _analyzePlugin(package, 'macOS', extraFlags: [ + if (minMacOSVersion.isNotEmpty) + 'MACOSX_DEPLOYMENT_TARGET=$minMacOSVersion', + ])) { failures.add('macOS'); } diff --git a/script/tool/test/xcode_analyze_command_test.dart b/script/tool/test/xcode_analyze_command_test.dart index 51e8e3283295..418c695f295c 100644 --- a/script/tool/test/xcode_analyze_command_test.dart +++ b/script/tool/test/xcode_analyze_command_test.dart @@ -123,6 +123,47 @@ void main() { ])); }); + test('passes min iOS deployment version when requested', () async { + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, + platformSupport: { + platformIOS: const PlatformDetails(PlatformSupport.inline) + }); + + final Directory pluginExampleDirectory = getExampleDir(plugin); + + final List output = await runCapturingPrint(runner, + ['xcode-analyze', '--ios', '--ios-min-version=14.0']); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin'), + contains('plugin/example (iOS) passed analysis.') + ])); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall( + 'xcrun', + const [ + 'xcodebuild', + 'analyze', + '-workspace', + 'ios/Runner.xcworkspace', + '-scheme', + 'Runner', + '-configuration', + 'Debug', + '-destination', + 'generic/platform=iOS Simulator', + 'IPHONEOS_DEPLOYMENT_TARGET=14.0', + 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', + ], + pluginExampleDirectory.path), + ])); + }); + test('fails if xcrun fails', () async { createFakePlugin('plugin', packagesDir, platformSupport: { @@ -218,6 +259,41 @@ void main() { ])); }); + test('passes min macOS deployment version when requested', () async { + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, + platformSupport: { + platformMacOS: const PlatformDetails(PlatformSupport.inline), + }); + + final Directory pluginExampleDirectory = getExampleDir(plugin); + + final List output = await runCapturingPrint(runner, + ['xcode-analyze', '--macos', '--macos-min-version=12.0']); + + expect(output, + contains(contains('plugin/example (macOS) passed analysis.'))); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall( + 'xcrun', + const [ + 'xcodebuild', + 'analyze', + '-workspace', + 'macos/Runner.xcworkspace', + '-scheme', + 'Runner', + '-configuration', + 'Debug', + 'MACOSX_DEPLOYMENT_TARGET=12.0', + 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', + ], + pluginExampleDirectory.path), + ])); + }); + test('fails if xcrun fails', () async { createFakePlugin('plugin', packagesDir, platformSupport: { From 4660811ce76af8ede81b2c042639c95405460c8f Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 18 May 2022 13:57:11 -0400 Subject: [PATCH 329/844] Add more CODEOWNERS (#5779) --- CODEOWNERS | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/CODEOWNERS b/CODEOWNERS index 26a2a2bc1602..68e4cb768a6a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -5,8 +5,17 @@ # reviewed by someone else. # Plugin-level rules. +packages/camera/** @bparrishMines +packages/file_selector/** @stuartmorgan +packages/google_maps_flutter/** @stuartmorgan +packages/google_sign_in/** @stuartmorgan +packages/image_picker/** @stuartmorgan +packages/local_auth/** @stuartmorgan packages/path_provider/** @gaaclarke +packages/plugin_platform_interface/** @stuartmorgan +packages/quick_actions/** @stuartmorgan packages/shared_preferences/** @gaaclarke +packages/url_launcher/** @stuartmorgan packages/video_player/** @gaaclarke packages/webview_flutter/** @bparrishMines @@ -44,3 +53,23 @@ packages/shared_preferences/shared_preferences_ios/** @cyanglaz packages/url_launcher/url_launcher_ios/** @jmagman packages/video_player/video_player_avfoundation/** @hellohuanlin packages/webview_flutter/webview_flutter_wkwebview/** @cyanglaz + +# - Linux +packages/path_provider/path_provider_linux/** @cbracken +packages/shared_preferences/shared_preferences_linux/** @cbracken +packages/url_launcher/url_launcher_linux/** @cbracken + +# - macOS +packages/file_selector/file_selector_macos/** @cbracken +packages/path_provider/path_provider_macos/** @cbracken +packages/shared_preferences/shared_preferences_macos/** @cbracken +packages/url_launcher/url_launcher_macos/** @cbracken + +# - Windows +packages/camera/camera_windows/** @cbracken +packages/file_selector/file_selector_windows/** @cbracken +packages/image_picker/image_picker_windows/** @cbracken +packages/local_auth/local_auth_windows/** @cbracken +packages/path_provider/path_provider_windows/** @cbracken +packages/shared_preferences/shared_preferences_windows/** @cbracken +packages/url_launcher/url_launcher_windows/** @cbracken From 4ed29beeea588fbadf6e40645beddeb3f40a7d82 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 18 May 2022 19:17:29 -0400 Subject: [PATCH 330/844] [tools] Add `update-release-info` (#5643) --- script/tool/CHANGELOG.md | 4 +- script/tool/README.md | 25 + .../lib/src/common/package_state_utils.dart | 95 +++ script/tool/lib/src/main.dart | 2 + .../lib/src/update_release_info_command.dart | 310 +++++++++ .../tool/lib/src/version_check_command.dart | 41 +- script/tool/pubspec.yaml | 2 +- .../test/common/package_state_utils_test.dart | 140 ++++ .../update_release_info_command_test.dart | 645 ++++++++++++++++++ script/tool/test/util.dart | 14 +- 10 files changed, 1238 insertions(+), 40 deletions(-) create mode 100644 script/tool/lib/src/common/package_state_utils.dart create mode 100644 script/tool/lib/src/update_release_info_command.dart create mode 100644 script/tool/test/common/package_state_utils_test.dart create mode 100644 script/tool/test/update_release_info_command_test.dart diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 04101c228793..4b40aecefa9a 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,5 +1,7 @@ -## NEXT +## 0.8.6 +- Adds `update-release-info` to apply changelog and optional version changes + across multiple packages. - Fixes changelog validation when reverting to a `NEXT` state. - Fixes multiplication of `--force` flag when publishing multiple packages. - Adds minimum deployment target flags to `xcode-analyze` to allow diff --git a/script/tool/README.md b/script/tool/README.md index d52ee08fdc34..cc9621f44086 100644 --- a/script/tool/README.md +++ b/script/tool/README.md @@ -118,6 +118,31 @@ cd dart run ./script/tool/bin/flutter_plugin_tools.dart update-excerpts --packages plugin_name ``` +### Update CHANGELOG and Version + +`update-release-info` will automatically update the version and `CHANGELOG.md` +following standard repository style and practice. It can be used for +single-package updates to handle the details of getting the `CHANGELOG.md` +format correct, but is especially useful for bulk updates across multiple packages. + +For instance, if you add a new analysis option that requires production +code changes across many packages: + +```sh +cd +dart run ./script/tool/bin/flutter_plugin_tools.dart update-release-info \ + --version=minimal \ + --changelog="Fixes violations of new analysis option some_new_option." +``` + +The `minimal` option for `--version` will skip unchanged packages, and treat +each changed package as either `bugfix` or `next` depending on the files that +have changed in that package, so it is often the best choice for a bulk change. + +For cases where you know the change time, `minor` or `bugfix` will make the +corresponding version bump, or `next` will update only `CHANGELOG.md` without +changing the version. + ### Publish a Release **Releases are automated for `flutter/plugins` and `flutter/packages`.** diff --git a/script/tool/lib/src/common/package_state_utils.dart b/script/tool/lib/src/common/package_state_utils.dart new file mode 100644 index 000000000000..437bbf6df370 --- /dev/null +++ b/script/tool/lib/src/common/package_state_utils.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:meta/meta.dart'; +import 'package:path/path.dart' as p; + +import 'repository_package.dart'; + +/// The state of a package on disk relative to git state. +@immutable +class PackageChangeState { + /// Creates a new immutable state instance. + const PackageChangeState({ + required this.hasChanges, + required this.hasChangelogChange, + required this.needsVersionChange, + }); + + /// True if there are any changes to files in the package. + final bool hasChanges; + + /// True if the package's CHANGELOG.md has been changed. + final bool hasChangelogChange; + + /// True if any changes in the package require a version change according + /// to repository policy. + final bool needsVersionChange; +} + +/// Checks [package] against [changedPaths] to determine what changes it has +/// and how those changes relate to repository policy about CHANGELOG and +/// version updates. +/// +/// [changedPaths] should be a list of POSIX-style paths from a common root, +/// and [relativePackagePath] should be the path to [package] from that same +/// root. Commonly these will come from `gitVersionFinder.getChangedFiles()` +/// and `getRelativePoixPath(package.directory, gitDir.path)` respectively; +/// they are arguments mainly to allow for caching the changed paths for an +/// entire command run. +PackageChangeState checkPackageChangeState( + RepositoryPackage package, { + required List changedPaths, + required String relativePackagePath, +}) { + final String packagePrefix = relativePackagePath.endsWith('/') + ? relativePackagePath + : '$relativePackagePath/'; + + bool hasChanges = false; + bool hasChangelogChange = false; + bool needsVersionChange = false; + for (final String path in changedPaths) { + // Only consider files within the package. + if (!path.startsWith(packagePrefix)) { + continue; + } + final String packageRelativePath = path.substring(packagePrefix.length); + hasChanges = true; + + final List components = p.posix.split(packageRelativePath); + if (components.isEmpty) { + continue; + } + final bool isChangelog = components.first == 'CHANGELOG.md'; + if (isChangelog) { + hasChangelogChange = true; + } + + if (!needsVersionChange && + !isChangelog && + // One of a few special files example will be shown on pub.dev, but for + // anything else in the example publishing has no purpose. + !(components.first == 'example' && + !{'main.dart', 'readme.md', 'example.md'} + .contains(components.last.toLowerCase())) && + // Changes to tests don't need to be published. + !components.contains('test') && + !components.contains('androidTest') && + !components.contains('RunnerTests') && + !components.contains('RunnerUITests') && + // The top-level "tool" directory is for non-client-facing utility code, + // so doesn't need to be published. + components.first != 'tool' && + // Ignoring lints doesn't affect clients. + !components.contains('lint-baseline.xml')) { + needsVersionChange = true; + } + } + + return PackageChangeState( + hasChanges: hasChanges, + hasChangelogChange: hasChangelogChange, + needsVersionChange: needsVersionChange); +} diff --git a/script/tool/lib/src/main.dart b/script/tool/lib/src/main.dart index 9c572ee270e0..739aef56878d 100644 --- a/script/tool/lib/src/main.dart +++ b/script/tool/lib/src/main.dart @@ -29,6 +29,7 @@ import 'pubspec_check_command.dart'; import 'readme_check_command.dart'; import 'test_command.dart'; import 'update_excerpts_command.dart'; +import 'update_release_info_command.dart'; import 'version_check_command.dart'; import 'xcode_analyze_command.dart'; @@ -70,6 +71,7 @@ void main(List args) { ..addCommand(ReadmeCheckCommand(packagesDir)) ..addCommand(TestCommand(packagesDir)) ..addCommand(UpdateExcerptsCommand(packagesDir)) + ..addCommand(UpdateReleaseInfoCommand(packagesDir)) ..addCommand(VersionCheckCommand(packagesDir)) ..addCommand(XcodeAnalyzeCommand(packagesDir)); diff --git a/script/tool/lib/src/update_release_info_command.dart b/script/tool/lib/src/update_release_info_command.dart new file mode 100644 index 000000000000..b998615ead17 --- /dev/null +++ b/script/tool/lib/src/update_release_info_command.dart @@ -0,0 +1,310 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:flutter_plugin_tools/src/common/core.dart'; +import 'package:git/git.dart'; +import 'package:pub_semver/pub_semver.dart'; +import 'package:yaml_edit/yaml_edit.dart'; + +import 'common/git_version_finder.dart'; +import 'common/package_looping_command.dart'; +import 'common/package_state_utils.dart'; +import 'common/repository_package.dart'; + +/// Supported version change types, from smallest to largest component. +enum _VersionIncrementType { build, bugfix, minor } + +/// Possible results of attempting to update a CHANGELOG.md file. +enum _ChangelogUpdateOutcome { addedSection, updatedSection, failed } + +/// A state machine for the process of updating a CHANGELOG.md. +enum _ChangelogUpdateState { + /// Looking for the first version section. + findingFirstSection, + + /// Looking for the first list entry in an existing section. + findingFirstListItem, + + /// Finished with updates. + finishedUpdating, +} + +/// A command to update the changelog, and optionally version, of packages. +class UpdateReleaseInfoCommand extends PackageLoopingCommand { + /// Creates a publish metadata updater command instance. + UpdateReleaseInfoCommand( + Directory packagesDir, { + GitDir? gitDir, + }) : super(packagesDir, gitDir: gitDir) { + argParser.addOption(_changelogFlag, + mandatory: true, + help: 'The changelog entry to add. ' + 'Each line will be a separate list entry.'); + argParser.addOption(_versionTypeFlag, + mandatory: true, + help: 'The version change level', + allowed: [ + _versionNext, + _versionMinimal, + _versionBugfix, + _versionMinor, + ], + allowedHelp: { + _versionNext: + 'No version change; just adds a NEXT entry to the changelog.', + _versionBugfix: 'Increments the bugfix version.', + _versionMinor: 'Increments the minor version.', + _versionMinimal: 'Depending on the changes to each package: ' + 'increments the bugfix version (for publishable changes), ' + "uses NEXT (for changes that don't need to be published), " + 'or skips (if no changes).', + }); + } + + static const String _changelogFlag = 'changelog'; + static const String _versionTypeFlag = 'version'; + + static const String _versionNext = 'next'; + static const String _versionBugfix = 'bugfix'; + static const String _versionMinor = 'minor'; + static const String _versionMinimal = 'minimal'; + + // The version change type, if there is a set type for all platforms. + // + // If null, either there is no version change, or it is dynamic (`minimal`). + _VersionIncrementType? _versionChange; + + // The cache of changed files, for dynamic version change determination. + // + // Only set for `minimal` version change. + late final List _changedFiles; + + @override + final String name = 'update-release-info'; + + @override + final String description = 'Updates CHANGELOG.md files, and optionally the ' + 'version in pubspec.yaml, in a way that is consistent with version-check ' + 'enforcement.'; + + @override + bool get hasLongOutput => false; + + @override + Future initializeRun() async { + if (getStringArg(_changelogFlag).trim().isEmpty) { + throw UsageException('Changelog message must not be empty.', usage); + } + switch (getStringArg(_versionTypeFlag)) { + case _versionMinor: + _versionChange = _VersionIncrementType.minor; + break; + case _versionBugfix: + _versionChange = _VersionIncrementType.bugfix; + break; + case _versionMinimal: + final GitVersionFinder gitVersionFinder = await retrieveVersionFinder(); + _changedFiles = await gitVersionFinder.getChangedFiles(); + // Anothing other than a fixed change is null. + _versionChange = null; + break; + case _versionNext: + _versionChange = null; + break; + default: + throw UnimplementedError('Unimplemented version change type'); + } + } + + @override + Future runForPackage(RepositoryPackage package) async { + String nextVersionString; + + _VersionIncrementType? versionChange = _versionChange; + + // If the change type is `minimal` determine what changes, if any, are + // needed. + if (versionChange == null && + getStringArg(_versionTypeFlag) == _versionMinimal) { + final Directory gitRoot = + packagesDir.fileSystem.directory((await gitDir).path); + final String relativePackagePath = + getRelativePosixPath(package.directory, from: gitRoot); + final PackageChangeState state = checkPackageChangeState(package, + changedPaths: _changedFiles, + relativePackagePath: relativePackagePath); + + if (!state.hasChanges) { + return PackageResult.skip('No changes to package'); + } + if (state.needsVersionChange) { + versionChange = _VersionIncrementType.bugfix; + } + } + + if (versionChange != null) { + final Version? updatedVersion = + _updatePubspecVersion(package, versionChange); + if (updatedVersion == null) { + return PackageResult.fail( + ['Could not determine current version.']); + } + nextVersionString = updatedVersion.toString(); + print('${indentation}Incremented version to $nextVersionString.'); + } else { + nextVersionString = 'NEXT'; + } + + final _ChangelogUpdateOutcome updateOutcome = + _updateChangelog(package, nextVersionString); + switch (updateOutcome) { + case _ChangelogUpdateOutcome.addedSection: + print('${indentation}Added a $nextVersionString section.'); + break; + case _ChangelogUpdateOutcome.updatedSection: + print('${indentation}Updated NEXT section.'); + break; + case _ChangelogUpdateOutcome.failed: + return PackageResult.fail(['Could not update CHANGELOG.md.']); + } + + return PackageResult.success(); + } + + _ChangelogUpdateOutcome _updateChangelog( + RepositoryPackage package, String version) { + if (!package.changelogFile.existsSync()) { + printError('${indentation}Missing CHANGELOG.md.'); + return _ChangelogUpdateOutcome.failed; + } + + final String newHeader = '## $version'; + final RegExp listItemPattern = RegExp(r'^(\s*[-*])'); + + final StringBuffer newChangelog = StringBuffer(); + _ChangelogUpdateState state = _ChangelogUpdateState.findingFirstSection; + bool updatedExistingSection = false; + + for (final String line in package.changelogFile.readAsLinesSync()) { + switch (state) { + case _ChangelogUpdateState.findingFirstSection: + final String trimmedLine = line.trim(); + if (trimmedLine.isEmpty) { + // Discard any whitespace at the top of the file. + } else if (trimmedLine == '## NEXT') { + // Replace the header with the new version (which may also be NEXT). + newChangelog.writeln(newHeader); + // Find the existing list to add to. + state = _ChangelogUpdateState.findingFirstListItem; + } else { + // The first content in the file isn't a NEXT section, so just add + // the new section. + [ + newHeader, + '', + ..._changelogAdditionsAsList(), + '', + line, // Don't drop the current line. + ].forEach(newChangelog.writeln); + state = _ChangelogUpdateState.finishedUpdating; + } + break; + case _ChangelogUpdateState.findingFirstListItem: + final RegExpMatch? match = listItemPattern.firstMatch(line); + if (match != null) { + final String listMarker = match[1]!; + // Add the new items on top. If the new change is changing the + // version, then the new item should be more relevant to package + // clients than anything that was already there. If it's still + // NEXT, the order doesn't matter. + [ + ..._changelogAdditionsAsList(listMarker: listMarker), + line, // Don't drop the current line. + ].forEach(newChangelog.writeln); + state = _ChangelogUpdateState.finishedUpdating; + updatedExistingSection = true; + } else if (line.trim().isEmpty) { + // Scan past empty lines, but keep them. + newChangelog.writeln(line); + } else { + printError(' Existing NEXT section has unrecognized format.'); + return _ChangelogUpdateOutcome.failed; + } + break; + case _ChangelogUpdateState.finishedUpdating: + // Once changes are done, add the rest of the lines as-is. + newChangelog.writeln(line); + break; + } + } + + package.changelogFile.writeAsStringSync(newChangelog.toString()); + + return updatedExistingSection + ? _ChangelogUpdateOutcome.updatedSection + : _ChangelogUpdateOutcome.addedSection; + } + + /// Returns the changelog to add as a Markdown list, using the given list + /// bullet style (default to the repository standard of '*'), and adding + /// any missing periods. + /// + /// E.g., 'A line\nAnother line.' will become: + /// ``` + /// [ '* A line.', '* Another line.' ] + /// ``` + Iterable _changelogAdditionsAsList({String listMarker = '*'}) { + return getStringArg(_changelogFlag).split('\n').map((String entry) { + String standardizedEntry = entry.trim(); + if (!standardizedEntry.endsWith('.')) { + standardizedEntry = '$standardizedEntry.'; + } + return '$listMarker $standardizedEntry'; + }); + } + + /// Updates the version in [package]'s pubspec according to [type], returning + /// the new version, or null if there was an error updating the version. + Version? _updatePubspecVersion( + RepositoryPackage package, _VersionIncrementType type) { + final Pubspec pubspec = package.parsePubspec(); + final Version? currentVersion = pubspec.version; + if (currentVersion == null) { + printError('${indentation}No version in pubspec.yaml'); + return null; + } + + // For versions less than 1.0, shift the change down one component per + // Dart versioning conventions. + final _VersionIncrementType adjustedType = currentVersion.major > 0 + ? type + : _VersionIncrementType.values[type.index - 1]; + + final Version newVersion = _nextVersion(currentVersion, adjustedType); + + // Write the new version to the pubspec. + final YamlEditor editablePubspec = + YamlEditor(package.pubspecFile.readAsStringSync()); + editablePubspec.update(['version'], newVersion.toString()); + package.pubspecFile.writeAsStringSync(editablePubspec.toString()); + + return newVersion; + } + + Version _nextVersion(Version version, _VersionIncrementType type) { + switch (type) { + case _VersionIncrementType.minor: + return version.nextMinor; + case _VersionIncrementType.bugfix: + return version.nextPatch; + case _VersionIncrementType.build: + final int buildNumber = + version.build.isEmpty ? 0 : version.build.first as int; + return Version(version.major, version.minor, version.patch, + build: '${buildNumber + 1}'); + } + } +} diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart index b816ee56999c..62abdb2a432b 100644 --- a/script/tool/lib/src/version_check_command.dart +++ b/script/tool/lib/src/version_check_command.dart @@ -13,6 +13,7 @@ import 'package:pub_semver/pub_semver.dart'; import 'common/core.dart'; import 'common/git_version_finder.dart'; import 'common/package_looping_command.dart'; +import 'common/package_state_utils.dart'; import 'common/process_runner.dart'; import 'common/pub_version_finder.dart'; import 'common/repository_package.dart'; @@ -531,44 +532,16 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. final Directory gitRoot = packagesDir.fileSystem.directory((await gitDir).path); final String relativePackagePath = - '${getRelativePosixPath(package.directory, from: gitRoot)}/'; - bool hasChanges = false; - bool needsVersionChange = false; - bool hasChangelogChange = false; - for (final String path in _changedFiles) { - // Only consider files within the package. - if (!path.startsWith(relativePackagePath)) { - continue; - } - hasChanges = true; - - final List components = p.posix.split(path); - final bool isChangelog = components.last == 'CHANGELOG.md'; - if (isChangelog) { - hasChangelogChange = true; - } + getRelativePosixPath(package.directory, from: gitRoot); - if (!needsVersionChange && - !isChangelog && - // The example's main.dart is shown on pub.dev, but for anything else - // in the example publishing has no purpose. - !(components.contains('example') && components.last != 'main.dart') && - // Changes to tests don't need to be published. - !components.contains('test') && - !components.contains('androidTest') && - !components.contains('RunnerTests') && - !components.contains('RunnerUITests') && - // Ignoring lints doesn't affect clients. - !components.contains('lint-baseline.xml')) { - needsVersionChange = true; - } - } + final PackageChangeState state = checkPackageChangeState(package, + changedPaths: _changedFiles, relativePackagePath: relativePackagePath); - if (!hasChanges) { + if (!state.hasChanges) { return null; } - if (needsVersionChange) { + if (state.needsVersionChange) { if (_getChangeDescription().split('\n').any((String line) => line.startsWith(_missingVersionChangeJustificationMarker))) { logWarning('Ignoring lack of version change due to ' @@ -586,7 +559,7 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. } } - if (!hasChangelogChange) { + if (!state.hasChangelogChange) { if (_getChangeDescription().split('\n').any((String line) => line.startsWith(_missingChangelogChangeJustificationMarker))) { logWarning('Ignoring lack of CHANGELOG update due to ' diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 32bfc1b62281..138c1183fa1c 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.8.5 +version: 0.8.6 dependencies: args: ^2.1.0 diff --git a/script/tool/test/common/package_state_utils_test.dart b/script/tool/test/common/package_state_utils_test.dart new file mode 100644 index 000000000000..cc9116a9ea25 --- /dev/null +++ b/script/tool/test/common/package_state_utils_test.dart @@ -0,0 +1,140 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +import 'package:file/file.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_plugin_tools/src/common/package_state_utils.dart'; +import 'package:test/test.dart'; + +import '../util.dart'; + +void main() { + late FileSystem fileSystem; + late Directory packagesDir; + + setUp(() { + fileSystem = MemoryFileSystem(); + packagesDir = createPackagesDirectory(fileSystem: fileSystem); + }); + + group('checkPackageChangeState', () { + test('reports version change needed for code changes', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + + const List changedFiles = [ + 'packages/a_package/lib/plugin.dart', + ]; + + final PackageChangeState state = checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_package'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + }); + + test('handles trailing slash on package path', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + + const List changedFiles = [ + 'packages/a_package/lib/plugin.dart', + ]; + + final PackageChangeState state = checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_package/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + expect(state.hasChangelogChange, false); + }); + + test('does not report version change exempt changes', () async { + final RepositoryPackage package = + createFakePlugin('a_plugin', packagesDir); + + const List changedFiles = [ + 'packages/a_plugin/example/android/lint-baseline.xml', + 'packages/a_plugin/example/android/src/androidTest/foo/bar/FooTest.java', + 'packages/a_plugin/example/ios/RunnerTests/Foo.m', + 'packages/a_plugin/example/ios/RunnerUITests/info.plist', + 'packages/a_plugin/tool/a_development_tool.dart', + 'packages/a_plugin/CHANGELOG.md', + ]; + + final PackageChangeState state = checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, false); + expect(state.hasChangelogChange, true); + }); + + test('only considers a root "tool" folder to be special', () async { + final RepositoryPackage package = + createFakePlugin('a_plugin', packagesDir); + + const List changedFiles = [ + 'packages/a_plugin/lib/foo/tool/tool_thing.dart', + ]; + + final PackageChangeState state = checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + }); + + test('requires a version change for example main', () async { + final RepositoryPackage package = + createFakePlugin('a_plugin', packagesDir); + + const List changedFiles = [ + 'packages/a_plugin/example/lib/main.dart', + ]; + + final PackageChangeState state = checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + }); + + test('requires a version change for example readme.md', () async { + final RepositoryPackage package = + createFakePlugin('a_plugin', packagesDir); + + const List changedFiles = [ + 'packages/a_plugin/example/README.md', + ]; + + final PackageChangeState state = checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + }); + + test('requires a version change for example example.md', () async { + final RepositoryPackage package = + createFakePlugin('a_plugin', packagesDir); + + const List changedFiles = [ + 'packages/a_plugin/example/lib/example.md', + ]; + + final PackageChangeState state = checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + }); + }); +} diff --git a/script/tool/test/update_release_info_command_test.dart b/script/tool/test/update_release_info_command_test.dart new file mode 100644 index 000000000000..7e7ff54d5947 --- /dev/null +++ b/script/tool/test/update_release_info_command_test.dart @@ -0,0 +1,645 @@ +// 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:io' as io; + +import 'package:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_plugin_tools/src/common/core.dart'; +import 'package:flutter_plugin_tools/src/update_release_info_command.dart'; +import 'package:mockito/mockito.dart'; +import 'package:test/test.dart'; + +import 'common/plugin_command_test.mocks.dart'; +import 'mocks.dart'; +import 'util.dart'; + +void main() { + late FileSystem fileSystem; + late Directory packagesDir; + late MockGitDir gitDir; + late RecordingProcessRunner processRunner; + late CommandRunner runner; + + setUp(() { + fileSystem = MemoryFileSystem(); + packagesDir = createPackagesDirectory(fileSystem: fileSystem); + processRunner = RecordingProcessRunner(); + + gitDir = MockGitDir(); + when(gitDir.path).thenReturn(packagesDir.parent.path); + when(gitDir.runCommand(any, throwOnError: anyNamed('throwOnError'))) + .thenAnswer((Invocation invocation) { + final List arguments = + invocation.positionalArguments[0]! as List; + // Route git calls through a process runner, to make mock output + // consistent with other processes. Attach the first argument to the + // command to make targeting the mock results easier. + final String gitCommand = arguments.removeAt(0); + return processRunner.run('git-$gitCommand', arguments); + }); + + final UpdateReleaseInfoCommand command = UpdateReleaseInfoCommand( + packagesDir, + gitDir: gitDir, + ); + runner = CommandRunner( + 'update_release_info_command', 'Test for update_release_info_command'); + runner.addCommand(command); + }); + + group('flags', () { + test('fails if --changelog is missing', () async { + Exception? commandError; + await runCapturingPrint(runner, [ + 'update-release-info', + '--version=next', + ], exceptionHandler: (Exception e) { + commandError = e; + }); + + expect(commandError, isA()); + }); + + test('fails if --changelog is blank', () async { + Exception? commandError; + await runCapturingPrint(runner, [ + 'update-release-info', + '--version=next', + '--changelog', + '', + ], exceptionHandler: (Exception e) { + commandError = e; + }); + + expect(commandError, isA()); + }); + + test('fails if --version is missing', () async { + Exception? commandError; + await runCapturingPrint( + runner, ['update-release-info', '--changelog', ''], + exceptionHandler: (Exception e) { + commandError = e; + }); + + expect(commandError, isA()); + }); + + test('fails if --version is an unknown value', () async { + Exception? commandError; + await runCapturingPrint(runner, [ + 'update-release-info', + '--version=foo', + '--changelog', + '', + ], exceptionHandler: (Exception e) { + commandError = e; + }); + + expect(commandError, isA()); + }); + }); + + group('changelog', () { + test('adds new NEXT section', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + const String originalChangelog = ''' +## 1.0.0 + +* Previous changes. +'''; + package.changelogFile.writeAsStringSync(originalChangelog); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=next', + '--changelog', + 'A change.' + ]); + + final String newChangelog = package.changelogFile.readAsStringSync(); + const String expectedChangeLog = ''' +## NEXT + +* A change. + +$originalChangelog'''; + + expect( + output, + containsAllInOrder([ + contains(' Added a NEXT section.'), + ]), + ); + expect(newChangelog, expectedChangeLog); + }); + + test('adds to existing NEXT section', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + const String originalChangelog = ''' +## NEXT + +* Already-pending changes. + +## 1.0.0 + +* Old changes. +'''; + package.changelogFile.writeAsStringSync(originalChangelog); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=next', + '--changelog', + 'A change.' + ]); + + final String newChangelog = package.changelogFile.readAsStringSync(); + const String expectedChangeLog = ''' +## NEXT + +* A change. +* Already-pending changes. + +## 1.0.0 + +* Old changes. +'''; + + expect(output, + containsAllInOrder([contains(' Updated NEXT section.')])); + expect(newChangelog, expectedChangeLog); + }); + + test('adds new version section', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + const String originalChangelog = ''' +## 1.0.0 + +* Previous changes. +'''; + package.changelogFile.writeAsStringSync(originalChangelog); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=bugfix', + '--changelog', + 'A change.' + ]); + + final String newChangelog = package.changelogFile.readAsStringSync(); + const String expectedChangeLog = ''' +## 1.0.1 + +* A change. + +$originalChangelog'''; + + expect( + output, + containsAllInOrder([ + contains(' Added a 1.0.1 section.'), + ]), + ); + expect(newChangelog, expectedChangeLog); + }); + + test('converts existing NEXT section to version section', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + const String originalChangelog = ''' +## NEXT + +* Already-pending changes. + +## 1.0.0 + +* Old changes. +'''; + package.changelogFile.writeAsStringSync(originalChangelog); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=bugfix', + '--changelog', + 'A change.' + ]); + + final String newChangelog = package.changelogFile.readAsStringSync(); + const String expectedChangeLog = ''' +## 1.0.1 + +* A change. +* Already-pending changes. + +## 1.0.0 + +* Old changes. +'''; + + expect(output, + containsAllInOrder([contains(' Updated NEXT section.')])); + expect(newChangelog, expectedChangeLog); + }); + + test('treats multiple lines as multiple list items', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + const String originalChangelog = ''' +## 1.0.0 + +* Previous changes. +'''; + package.changelogFile.writeAsStringSync(originalChangelog); + + await runCapturingPrint(runner, [ + 'update-release-info', + '--version=bugfix', + '--changelog', + 'First change.\nSecond change.' + ]); + + final String newChangelog = package.changelogFile.readAsStringSync(); + const String expectedChangeLog = ''' +## 1.0.1 + +* First change. +* Second change. + +$originalChangelog'''; + + expect(newChangelog, expectedChangeLog); + }); + + test('adds a period to any lines missing it, and removes whitespace', + () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + const String originalChangelog = ''' +## 1.0.0 + +* Previous changes. +'''; + package.changelogFile.writeAsStringSync(originalChangelog); + + await runCapturingPrint(runner, [ + 'update-release-info', + '--version=bugfix', + '--changelog', + 'First change \nSecond change' + ]); + + final String newChangelog = package.changelogFile.readAsStringSync(); + const String expectedChangeLog = ''' +## 1.0.1 + +* First change. +* Second change. + +$originalChangelog'''; + + expect(newChangelog, expectedChangeLog); + }); + + test('handles non-standard changelog format', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + const String originalChangelog = ''' +# 1.0.0 + +* A version with the wrong heading format. +'''; + package.changelogFile.writeAsStringSync(originalChangelog); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=next', + '--changelog', + 'A change.' + ]); + + final String newChangelog = package.changelogFile.readAsStringSync(); + const String expectedChangeLog = ''' +## NEXT + +* A change. + +$originalChangelog'''; + + expect(output, + containsAllInOrder([contains(' Added a NEXT section.')])); + expect(newChangelog, expectedChangeLog); + }); + + test('adds to existing NEXT section using - list style', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + const String originalChangelog = ''' +## NEXT + + - Already-pending changes. + +## 1.0.0 + + - Previous changes. +'''; + package.changelogFile.writeAsStringSync(originalChangelog); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=next', + '--changelog', + 'A change.' + ]); + + final String newChangelog = package.changelogFile.readAsStringSync(); + const String expectedChangeLog = ''' +## NEXT + + - A change. + - Already-pending changes. + +## 1.0.0 + + - Previous changes. +'''; + + expect(output, + containsAllInOrder([contains(' Updated NEXT section.')])); + expect(newChangelog, expectedChangeLog); + }); + + test('skips for "minimal" when there are no changes at all', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.1'); + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/different_package/test/plugin_test.dart +'''), + ]; + final String originalChangelog = package.changelogFile.readAsStringSync(); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=minimal', + '--changelog', + 'A change.', + ]); + + final String version = package.parsePubspec().version?.toString() ?? ''; + expect(version, '1.0.1'); + expect(package.changelogFile.readAsStringSync(), originalChangelog); + expect( + output, + containsAllInOrder([ + contains('No changes to package'), + contains('Skipped 1 package') + ])); + }); + + test('fails if CHANGELOG.md is missing', () async { + createFakePackage('a_package', packagesDir, includeCommonFiles: false); + + Error? commandError; + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=minor', + '--changelog', + 'A change.', + ], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect(output, + containsAllInOrder([contains(' Missing CHANGELOG.md.')])); + }); + + test('fails if CHANGELOG.md has unexpected NEXT block format', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + const String originalChangelog = ''' +## NEXT + +Some free-form text that isn't a list. + +## 1.0.0 + +- Previous changes. +'''; + package.changelogFile.writeAsStringSync(originalChangelog); + + Error? commandError; + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=minor', + '--changelog', + 'A change.', + ], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains(' Existing NEXT section has unrecognized format.') + ])); + }); + }); + + group('pubspec', () { + test('does not change for --next', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + await runCapturingPrint(runner, [ + 'update-release-info', + '--version=next', + '--changelog', + 'A change.' + ]); + + final String version = package.parsePubspec().version?.toString() ?? ''; + expect(version, '1.0.0'); + }); + + test('updates bugfix version for pre-1.0 without existing build number', + () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '0.1.0'); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=bugfix', + '--changelog', + 'A change.', + ]); + + final String version = package.parsePubspec().version?.toString() ?? ''; + expect(version, '0.1.0+1'); + expect( + output, + containsAllInOrder( + [contains(' Incremented version to 0.1.0+1')])); + }); + + test('updates bugfix version for pre-1.0 with existing build number', + () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '0.1.0+2'); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=bugfix', + '--changelog', + 'A change.', + ]); + + final String version = package.parsePubspec().version?.toString() ?? ''; + expect(version, '0.1.0+3'); + expect( + output, + containsAllInOrder( + [contains(' Incremented version to 0.1.0+3')])); + }); + + test('updates bugfix version for post-1.0', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.1'); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=bugfix', + '--changelog', + 'A change.', + ]); + + final String version = package.parsePubspec().version?.toString() ?? ''; + expect(version, '1.0.2'); + expect( + output, + containsAllInOrder( + [contains(' Incremented version to 1.0.2')])); + }); + + test('updates minor version for pre-1.0', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '0.1.0+2'); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=minor', + '--changelog', + 'A change.', + ]); + + final String version = package.parsePubspec().version?.toString() ?? ''; + expect(version, '0.1.1'); + expect( + output, + containsAllInOrder( + [contains(' Incremented version to 0.1.1')])); + }); + + test('updates minor version for post-1.0', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.1'); + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=minor', + '--changelog', + 'A change.', + ]); + + final String version = package.parsePubspec().version?.toString() ?? ''; + expect(version, '1.1.0'); + expect( + output, + containsAllInOrder( + [contains(' Incremented version to 1.1.0')])); + }); + + test('updates bugfix version for "minimal" with publish-worthy changes', + () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.1'); + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/a_package/lib/plugin.dart +'''), + ]; + + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=minimal', + '--changelog', + 'A change.', + ]); + + final String version = package.parsePubspec().version?.toString() ?? ''; + expect(version, '1.0.2'); + expect( + output, + containsAllInOrder( + [contains(' Incremented version to 1.0.2')])); + }); + + test('no version change for "minimal" with non-publish-worthy changes', + () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.1'); + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/a_package/test/plugin_test.dart +'''), + ]; + + await runCapturingPrint(runner, [ + 'update-release-info', + '--version=minimal', + '--changelog', + 'A change.', + ]); + + final String version = package.parsePubspec().version?.toString() ?? ''; + expect(version, '1.0.1'); + }); + + test('fails if there is no version in pubspec', () async { + createFakePackage('a_package', packagesDir, version: null); + + Error? commandError; + final List output = await runCapturingPrint(runner, [ + 'update-release-info', + '--version=minor', + '--changelog', + 'A change.', + ], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder( + [contains('Could not determine current version.')])); + }); + }); +} diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index 7255bf9a9276..4c99873e937d 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -310,14 +310,15 @@ String _pluginPlatformSection( return entry; } -typedef ErrorHandler = void Function(Error error); - /// Run the command [runner] with the given [args] and return /// what was printed. /// A custom [errorHandler] can be used to handle the runner error as desired without throwing. Future> runCapturingPrint( - CommandRunner runner, List args, - {ErrorHandler? errorHandler}) async { + CommandRunner runner, + List args, { + Function(Error error)? errorHandler, + Function(Exception error)? exceptionHandler, +}) async { final List prints = []; final ZoneSpecification spec = ZoneSpecification( print: (_, __, ___, String message) { @@ -333,6 +334,11 @@ Future> runCapturingPrint( rethrow; } errorHandler(e); + } on Exception catch (e) { + if (exceptionHandler == null) { + rethrow; + } + exceptionHandler(e); } return prints; From f33222a1a9cbbf067ef56af6816b553ea458e1f8 Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Wed, 18 May 2022 17:02:19 -0700 Subject: [PATCH 331/844] [local_auth] Adds federated Windows support (#5776) --- packages/local_auth/local_auth/CHANGELOG.md | 4 + packages/local_auth/local_auth/README.md | 8 +- .../local_auth/example/windows/CMakeLists.txt | 95 +++++++ .../example/windows/flutter/CMakeLists.txt | 103 ++++++++ .../windows/flutter/generated_plugins.cmake | 24 ++ .../example/windows/runner/CMakeLists.txt | 17 ++ .../example/windows/runner/Runner.rc | 121 +++++++++ .../example/windows/runner/flutter_window.cpp | 64 +++++ .../example/windows/runner/flutter_window.h | 36 +++ .../example/windows/runner/main.cpp | 45 ++++ .../example/windows/runner/resource.h | 16 ++ .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 20 ++ .../example/windows/runner/utils.cpp | 66 +++++ .../local_auth/example/windows/runner/utils.h | 22 ++ .../example/windows/runner/win32_window.cpp | 240 ++++++++++++++++++ .../example/windows/runner/win32_window.h | 98 +++++++ .../local_auth/lib/error_codes.dart | 3 + .../local_auth/lib/src/local_auth.dart | 4 +- packages/local_auth/local_auth/pubspec.yaml | 5 +- .../local_auth/test/local_auth_test.dart | 2 + 21 files changed, 988 insertions(+), 5 deletions(-) create mode 100644 packages/local_auth/local_auth/example/windows/CMakeLists.txt create mode 100644 packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt create mode 100644 packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/local_auth/local_auth/example/windows/runner/CMakeLists.txt create mode 100644 packages/local_auth/local_auth/example/windows/runner/Runner.rc create mode 100644 packages/local_auth/local_auth/example/windows/runner/flutter_window.cpp create mode 100644 packages/local_auth/local_auth/example/windows/runner/flutter_window.h create mode 100644 packages/local_auth/local_auth/example/windows/runner/main.cpp create mode 100644 packages/local_auth/local_auth/example/windows/runner/resource.h create mode 100644 packages/local_auth/local_auth/example/windows/runner/resources/app_icon.ico create mode 100644 packages/local_auth/local_auth/example/windows/runner/runner.exe.manifest create mode 100644 packages/local_auth/local_auth/example/windows/runner/utils.cpp create mode 100644 packages/local_auth/local_auth/example/windows/runner/utils.h create mode 100644 packages/local_auth/local_auth/example/windows/runner/win32_window.cpp create mode 100644 packages/local_auth/local_auth/example/windows/runner/win32_window.h diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 8a2743e6140b..1aae73d7393e 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.0 + +* Adds Windows support. + ## 2.0.2 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/local_auth/local_auth/README.md b/packages/local_auth/local_auth/README.md index 7ec4829ba521..3077f8e60a1f 100644 --- a/packages/local_auth/local_auth/README.md +++ b/packages/local_auth/local_auth/README.md @@ -8,9 +8,9 @@ the user. On supported devices, this includes authentication with biometrics such as fingerprint or facial recognition. -| | Android | iOS | -|-------------|-----------|------| -| **Support** | SDK 16+\* | 9.0+ | +| | Android | iOS | Windows | +|-------------|-----------|------|-------------| +| **Support** | SDK 16+\* | 9.0+ | Windows 10+ | ## Usage @@ -90,6 +90,8 @@ final bool didAuthenticate = await auth.authenticate( options: const AuthenticationOptions(biometricOnly: true)); ``` +*Note*: `biometricOnly` is not supported on Windows since the Windows implementation's underlying API (Windows Hello) doesn't support selecting the authentication method. + #### Dialogs The plugin provides default dialogs for the following cases: diff --git a/packages/local_auth/local_auth/example/windows/CMakeLists.txt b/packages/local_auth/local_auth/example/windows/CMakeLists.txt new file mode 100644 index 000000000000..e013bd88bcb1 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.14) +project(example LANGUAGES CXX) + +set(BINARY_NAME "example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) \ No newline at end of file diff --git a/packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt b/packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000000..d83cc95319b6 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) \ No newline at end of file diff --git a/packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake b/packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000000..ef187dcae56f --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + local_auth_windows +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/local_auth/local_auth/example/windows/runner/CMakeLists.txt b/packages/local_auth/local_auth/example/windows/runner/CMakeLists.txt new file mode 100644 index 000000000000..2520aa9e5fc7 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) \ No newline at end of file diff --git a/packages/local_auth/local_auth/example/windows/runner/Runner.rc b/packages/local_auth/local_auth/example/windows/runner/Runner.rc new file mode 100644 index 000000000000..7e35b9f56a22 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "io.flutter.plugins" "\0" + VALUE "FileDescription", "example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 io.flutter.plugins. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/packages/local_auth/local_auth/example/windows/runner/flutter_window.cpp b/packages/local_auth/local_auth/example/windows/runner/flutter_window.cpp new file mode 100644 index 000000000000..217bf9b69e67 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/flutter_window.cpp @@ -0,0 +1,64 @@ +// 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. +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/packages/local_auth/local_auth/example/windows/runner/flutter_window.h b/packages/local_auth/local_auth/example/windows/runner/flutter_window.h new file mode 100644 index 000000000000..7cbf3d3ebbb2 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/flutter_window.h @@ -0,0 +1,36 @@ +// 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. +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/local_auth/local_auth/example/windows/runner/main.cpp b/packages/local_auth/local_auth/example/windows/runner/main.cpp new file mode 100644 index 000000000000..1285aabf714a --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/main.cpp @@ -0,0 +1,45 @@ +// 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. +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t* command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/packages/local_auth/local_auth/example/windows/runner/resource.h b/packages/local_auth/local_auth/example/windows/runner/resource.h new file mode 100644 index 000000000000..d5d958dc4257 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/packages/local_auth/local_auth/example/windows/runner/resources/app_icon.ico b/packages/local_auth/local_auth/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 zcmeHQc|26z|35SKE&G-*mXah&B~fFkXr)DEO&hIfqby^T&>|8^_Ub8Vp#`BLl3lbZ zvPO!8k!2X>cg~Elr=IVxo~J*a`+9wR=A83c-k-DFd(XM&UI1VKCqM@V;DDtJ09WB} zRaHKiW(GT00brH|0EeTeKVbpbGZg?nK6-j827q-+NFM34gXjqWxJ*a#{b_apGN<-L_m3#8Z26atkEn& ze87Bvv^6vVmM+p+cQ~{u%=NJF>#(d;8{7Q{^rWKWNtf14H}>#&y7$lqmY6xmZryI& z($uy?c5-+cPnt2%)R&(KIWEXww>Cnz{OUpT>W$CbO$h1= z#4BPMkFG1Y)x}Ui+WXr?Z!w!t_hjRq8qTaWpu}FH{MsHlU{>;08goVLm{V<&`itk~ zE_Ys=D(hjiy+5=?=$HGii=Y5)jMe9|wWoD_K07(}edAxh`~LBorOJ!Cf@f{_gNCC| z%{*04ViE!#>@hc1t5bb+NO>ncf@@Dv01K!NxH$3Eg1%)|wLyMDF8^d44lV!_Sr}iEWefOaL z8f?ud3Q%Sen39u|%00W<#!E=-RpGa+H8}{ulxVl4mwpjaU+%2pzmi{3HM)%8vb*~-M9rPUAfGCSos8GUXp02|o~0BTV2l#`>>aFV&_P$ejS;nGwSVP8 zMbOaG7<7eKD>c12VdGH;?2@q7535sa7MN*L@&!m?L`ASG%boY7(&L5imY#EQ$KrBB z4@_tfP5m50(T--qv1BJcD&aiH#b-QC>8#7Fx@3yXlonJI#aEIi=8&ChiVpc#N=5le zM*?rDIdcpawoc5kizv$GEjnveyrp3sY>+5_R5;>`>erS%JolimF=A^EIsAK zsPoVyyUHCgf0aYr&alx`<)eb6Be$m&`JYSuBu=p8j%QlNNp$-5C{b4#RubPb|CAIS zGE=9OFLP7?Hgc{?k45)84biT0k&-C6C%Q}aI~q<(7BL`C#<6HyxaR%!dFx7*o^laG z=!GBF^cwK$IA(sn9y6>60Rw{mYRYkp%$jH z*xQM~+bp)G$_RhtFPYx2HTsWk80+p(uqv9@I9)y{b$7NK53rYL$ezbmRjdXS?V}fj zWxX_feWoLFNm3MG7pMUuFPs$qrQWO9!l2B(SIuy2}S|lHNbHzoE+M2|Zxhjq9+Ws8c{*}x^VAib7SbxJ*Q3EnY5lgI9 z=U^f3IW6T=TWaVj+2N%K3<%Un;CF(wUp`TC&Y|ZjyFu6co^uqDDB#EP?DV5v_dw~E zIRK*BoY9y-G_ToU2V_XCX4nJ32~`czdjT!zwme zGgJ0nOk3U4@IE5JwtM}pwimLjk{ln^*4HMU%Fl4~n(cnsLB}Ja-jUM>xIB%aY;Nq8 z)Fp8dv1tkqKanv<68o@cN|%thj$+f;zGSO7H#b+eMAV8xH$hLggtt?O?;oYEgbq@= zV(u9bbd12^%;?nyk6&$GPI%|+<_mEpJGNfl*`!KV;VfmZWw{n{rnZ51?}FDh8we_L z8OI9nE31skDqJ5Oa_ybn7|5@ui>aC`s34p4ZEu6-s!%{uU45$Zd1=p$^^dZBh zu<*pDDPLW+c>iWO$&Z_*{VSQKg7=YEpS3PssPn1U!lSm6eZIho*{@&20e4Y_lRklKDTUCKI%o4Pc<|G^Xgu$J^Q|B87U;`c1zGwf^-zH*VQ^x+i^OUWE0yd z;{FJq)2w!%`x7yg@>uGFFf-XJl4H`YtUG%0slGKOlXV`q?RP>AEWg#x!b{0RicxGhS!3$p7 zij;{gm!_u@D4$Ox%>>bPtLJ> zwKtYz?T_DR1jN>DkkfGU^<#6sGz|~p*I{y`aZ>^Di#TC|Z!7j_O1=Wo8thuit?WxR zh9_S>kw^{V^|g}HRUF=dcq>?q(pHxw!8rx4dC6vbQVmIhmICF#zU!HkHpQ>9S%Uo( zMw{eC+`&pb=GZRou|3;Po1}m46H6NGd$t<2mQh}kaK-WFfmj_66_17BX0|j-E2fe3Jat}ijpc53 zJV$$;PC<5aW`{*^Z6e5##^`Ed#a0nwJDT#Qq~^e8^JTA=z^Kl>La|(UQ!bI@#ge{Dzz@61p-I)kc2?ZxFt^QQ}f%ldLjO*GPj(5)V9IyuUakJX=~GnTgZ4$5!3E=V#t`yOG4U z(gphZB6u2zsj=qNFLYShhg$}lNpO`P9xOSnO*$@@UdMYES*{jJVj|9z-}F^riksLK zbsU+4-{281P9e2UjY6tse^&a)WM1MFw;p#_dHhWI7p&U*9TR0zKdVuQed%6{otTsq z$f~S!;wg#Bd9kez=Br{m|66Wv z#g1xMup<0)H;c2ZO6su_ii&m8j&+jJz4iKnGZ&wxoQX|5a>v&_e#6WA!MB_4asTxLRGQCC5cI(em z%$ZfeqP>!*q5kU>a+BO&ln=4Jm>Ef(QE8o&RgLkk%2}4Tf}U%IFP&uS7}&|Q-)`5< z+e>;s#4cJ-z%&-^&!xsYx777Wt(wZY9(3(avmr|gRe4cD+a8&!LY`1^T?7x{E<=kdY9NYw>A;FtTvQ=Y&1M%lyZPl$ss1oY^Sl8we}n}Aob#6 zl4jERwnt9BlSoWb@3HxYgga(752Vu6Y)k4yk9u~Kw>cA5&LHcrvn1Y-HoIuFWg~}4 zEw4bR`mXZQIyOAzo)FYqg?$5W<;^+XX%Uz61{-L6@eP|lLH%|w?g=rFc;OvEW;^qh z&iYXGhVt(G-q<+_j}CTbPS_=K>RKN0&;dubh0NxJyDOHFF;<1k!{k#7b{|Qok9hac z;gHz}6>H6C6RnB`Tt#oaSrX0p-j-oRJ;_WvS-qS--P*8}V943RT6kou-G=A+7QPGQ z!ze^UGxtW3FC0$|(lY9^L!Lx^?Q8cny(rR`es5U;-xBhphF%_WNu|aO<+e9%6LuZq zt(0PoagJG<%hyuf;te}n+qIl_Ej;czWdc{LX^pS>77s9t*2b4s5dvP_!L^3cwlc)E!(!kGrg~FescVT zZCLeua3f4;d;Tk4iXzt}g}O@nlK3?_o91_~@UMIl?@77Qc$IAlLE95#Z=TES>2E%z zxUKpK{_HvGF;5%Q7n&vA?`{%8ohlYT_?(3A$cZSi)MvIJygXD}TS-3UwyUxGLGiJP znblO~G|*uA^|ac8E-w#}uBtg|s_~s&t>-g0X%zIZ@;o_wNMr_;{KDg^O=rg`fhDZu zFp(VKd1Edj%F zWHPl+)FGj%J1BO3bOHVfH^3d1F{)*PL&sRX`~(-Zy3&9UQX)Z;c51tvaI2E*E7!)q zcz|{vpK7bjxix(k&6=OEIBJC!9lTkUbgg?4-yE{9+pFS)$Ar@vrIf`D0Bnsed(Cf? zObt2CJ>BKOl>q8PyFO6w)+6Iz`LW%T5^R`U_NIW0r1dWv6OY=TVF?N=EfA(k(~7VBW(S;Tu5m4Lg8emDG-(mOSSs=M9Q&N8jc^Y4&9RqIsk(yO_P(mcCr}rCs%1MW1VBrn=0-oQN(Xj!k%iKV zb%ricBF3G4S1;+8lzg5PbZ|$Se$)I=PwiK=cDpHYdov2QO1_a-*dL4KUi|g&oh>(* zq$<`dQ^fat`+VW?m)?_KLn&mp^-@d=&7yGDt<=XwZZC=1scwxO2^RRI7n@g-1o8ps z)&+et_~)vr8aIF1VY1Qrq~Xe``KJrQSnAZ{CSq3yP;V*JC;mmCT6oRLSs7=GA?@6g zUooM}@tKtx(^|aKK8vbaHlUQqwE0}>j&~YlN3H#vKGm@u)xxS?n9XrOWUfCRa< z`20Fld2f&;gg7zpo{Adh+mqNntMc-D$N^yWZAZRI+u1T1zWHPxk{+?vcS1D>08>@6 zLhE@`gt1Y9mAK6Z4p|u(5I%EkfU7rKFSM=E4?VG9tI;a*@?6!ey{lzN5=Y-!$WFSe z&2dtO>^0@V4WRc#L&P%R(?@KfSblMS+N+?xUN$u3K4Ys%OmEh+tq}fnU}i>6YHM?< zlnL2gl~sF!j!Y4E;j3eIU-lfa`RsOL*Tt<%EFC0gPzoHfNWAfKFIKZN8}w~(Yi~=q z>=VNLO2|CjkxP}RkutxjV#4fWYR1KNrPYq5ha9Wl+u>ipsk*I(HS@iLnmGH9MFlTU zaFZ*KSR0px>o+pL7BbhB2EC1%PJ{67_ z#kY&#O4@P=OV#-79y_W>Gv2dxL*@G7%LksNSqgId9v;2xJ zrh8uR!F-eU$NMx@S*+sk=C~Dxr9Qn7TfWnTupuHKuQ$;gGiBcU>GF5sWx(~4IP3`f zWE;YFO*?jGwYh%C3X<>RKHC-DZ!*r;cIr}GLOno^3U4tFSSoJp%oHPiSa%nh=Zgn% z14+8v@ygy0>UgEN1bczD6wK45%M>psM)y^)IfG*>3ItX|TzV*0i%@>L(VN!zdKb8S?Qf7BhjNpziA zR}?={-eu>9JDcl*R=OP9B8N$IcCETXah9SUDhr{yrld{G;PnCWRsPD7!eOOFBTWUQ=LrA_~)mFf&!zJX!Oc-_=kT<}m|K52 z)M=G#;p;Rdb@~h5D{q^K;^fX-m5V}L%!wVC2iZ1uu401Ll}#rocTeK|7FAeBRhNdQ zCc2d^aQnQp=MpOmak60N$OgS}a;p(l9CL`o4r(e-nN}mQ?M&isv-P&d$!8|1D1I(3-z!wi zTgoo)*Mv`gC?~bm?S|@}I|m-E2yqPEvYybiD5azInexpK8?9q*$9Yy9-t%5jU8~ym zgZDx>!@ujQ=|HJnwp^wv-FdD{RtzO9SnyfB{mH_(c!jHL*$>0o-(h(eqe*ZwF6Lvu z{7rkk%PEqaA>o+f{H02tzZ@TWy&su?VNw43! z-X+rN`6llvpUms3ZiSt)JMeztB~>9{J8SPmYs&qohxdYFi!ra8KR$35Zp9oR)eFC4 zE;P31#3V)n`w$fZ|4X-|%MX`xZDM~gJyl2W;O$H25*=+1S#%|53>|LyH za@yh+;325%Gq3;J&a)?%7X%t@WXcWL*BaaR*7UEZad4I8iDt7^R_Fd`XeUo256;sAo2F!HcIQKk;h})QxEsPE5BcKc7WyerTchgKmrfRX z!x#H_%cL#B9TWAqkA4I$R^8{%do3Y*&(;WFmJ zU7Dih{t1<{($VtJRl9|&EB?|cJ)xse!;}>6mSO$o5XIx@V|AA8ZcoD88ZM?C*;{|f zZVmf94_l1OmaICt`2sTyG!$^UeTHx9YuUP!omj(r|7zpm5475|yXI=rR>>fteLI+| z)MoiGho0oEt=*J(;?VY0QzwCqw@cVm?d7Y!z0A@u#H?sCJ*ecvyhj& z-F77lO;SH^dmf?L>3i>?Z*U}Em4ZYV_CjgfvzYsRZ+1B!Uo6H6mbS<-FFL`ytqvb& zE7+)2ahv-~dz(Hs+f})z{*4|{)b=2!RZK;PWwOnO=hG7xG`JU5>bAvUbdYd_CjvtHBHgtGdlO+s^9ca^Bv3`t@VRX2_AD$Ckg36OcQRF zXD6QtGfHdw*hx~V(MV-;;ZZF#dJ-piEF+s27z4X1qi5$!o~xBnvf=uopcn7ftfsZc zy@(PuOk`4GL_n(H9(E2)VUjqRCk9kR?w)v@xO6Jm_Mx})&WGEl=GS0#)0FAq^J*o! zAClhvoTsNP*-b~rN{8Yym3g{01}Ep^^Omf=SKqvN?{Q*C4HNNAcrowIa^mf+3PRy! z*_G-|3i8a;+q;iP@~Of_$(vtFkB8yOyWt2*K)vAn9El>=D;A$CEx6b*XF@4y_6M+2 zpeW`RHoI_p(B{%(&jTHI->hmNmZjHUj<@;7w0mx3&koy!2$@cfX{sN19Y}euYJFn& z1?)+?HCkD0MRI$~uB2UWri})0bru_B;klFdwsLc!ne4YUE;t41JqfG# zZJq6%vbsdx!wYeE<~?>o4V`A3?lN%MnKQ`z=uUivQN^vzJ|C;sdQ37Qn?;lpzg})y z)_2~rUdH}zNwX;Tp0tJ78+&I=IwOQ-fl30R79O8@?Ub8IIA(6I`yHn%lARVL`%b8+ z4$8D-|MZZWxc_)vu6@VZN!HsI$*2NOV&uMxBNzIbRgy%ob_ zhwEH{J9r$!dEix9XM7n&c{S(h>nGm?el;gaX0@|QnzFD@bne`el^CO$yXC?BDJ|Qg z+y$GRoR`?ST1z^e*>;!IS@5Ovb7*RlN>BV_UC!7E_F;N#ky%1J{+iixp(dUJj93aK zzHNN>R-oN7>kykHClPnoPTIj7zc6KM(Pnlb(|s??)SMb)4!sMHU^-ntJwY5Big7xv zb1Ew`Xj;|D2kzGja*C$eS44(d&RMU~c_Y14V9_TLTz0J#uHlsx`S6{nhsA0dWZ#cG zJ?`fO50E>*X4TQLv#nl%3GOk*UkAgt=IY+u0LNXqeln3Z zv$~&Li`ZJOKkFuS)dJRA>)b_Da%Q~axwA_8zNK{BH{#}#m}zGcuckz}riDE-z_Ms> zR8-EqAMcfyGJCtvTpaUVQtajhUS%c@Yj}&6Zz;-M7MZzqv3kA7{SuW$oW#=0az2wQ zg-WG@Vb4|D`pl~Il54N7Hmsauc_ne-a!o5#j3WaBBh@Wuefb!QJIOn5;d)%A#s+5% zuD$H=VNux9bE-}1&bcYGZ+>1Fo;3Z@e&zX^n!?JK*adSbONm$XW9z;Q^L>9U!}Toj2WdafJ%oL#h|yWWwyAGxzfrAWdDTtaKl zK4`5tDpPg5>z$MNv=X0LZ0d6l%D{(D8oT@+w0?ce$DZ6pv>{1&Ok67Ix1 zH}3=IEhPJEhItCC8E=`T`N5(k?G=B4+xzZ?<4!~ ze~z6Wk9!CHTI(0rLJ4{JU?E-puc;xusR?>G?;4vt;q~iI9=kDL=z0Rr%O$vU`30X$ zDZRFyZ`(omOy@u|i6h;wtJlP;+}$|Ak|k2dea7n?U1*$T!sXqqOjq^NxLPMmk~&qI zYg0W?yK8T(6+Ea+$YyspKK?kP$+B`~t3^Pib_`!6xCs32!i@pqXfFV6PmBIR<-QW= zN8L{pt0Vap0x`Gzn#E@zh@H)0FfVfA_Iu4fjYZ+umO1LXIbVc$pY+E234u)ttcrl$ z>s92z4vT%n6cMb>=XT6;l0+9e(|CZG)$@C7t7Z7Ez@a)h)!hyuV&B5K%%)P5?Lk|C zZZSVzdXp{@OXSP0hoU-gF8s8Um(#xzjP2Vem zec#-^JqTa&Y#QJ>-FBxd7tf`XB6e^JPUgagB8iBSEps;92KG`!#mvVcPQ5yNC-GEG zTiHEDYfH+0O15}r^+ z#jxj=@x8iNHWALe!P3R67TwmhItn**0JwnzSV2O&KE8KcT+0hWH^OPD1pwiuyx=b@ zNf5Jh0{9X)8;~Es)$t@%(3!OnbY+`@?i{mGX7Yy}8T_*0a6g;kaFPq;*=px5EhO{Cp%1kI<0?*|h8v!6WnO3cCJRF2-CRrU3JiLJnj@6;L)!0kWYAc_}F{2P))3HmCrz zQ&N&gE70;`!6*eJ4^1IR{f6j4(-l&X!tjHxkbHA^Zhrnhr9g{exN|xrS`5Pq=#Xf& zG%P=#ra-TyVFfgW%cZo5OSIwFL9WtXAlFOa+ubmI5t*3=g#Y zF%;70p5;{ZeFL}&}yOY1N1*Q;*<(kTB!7vM$QokF)yr2FlIU@$Ph58$Bz z0J?xQG=MlS4L6jA22eS42g|9*9pX@$#*sUeM(z+t?hr@r5J&D1rx}2pW&m*_`VDCW zUYY@v-;bAO0HqoAgbbiGGC<=ryf96}3pouhy3XJrX+!!u*O_>Si38V{uJmQ&USptX zKp#l(?>%^7;2%h(q@YWS#9;a!JhKlkR#Vd)ERILlgu!Hr@jA@V;sk4BJ-H#p*4EqC zDGjC*tl=@3Oi6)Bn^QwFpul18fpkbpg0+peH$xyPBqb%`$OUhPKyWb32o7clB*9Z< zN=i~NLjavrLtwgJ01bufP+>p-jR2I95|TpmKpQL2!oV>g(4RvS2pK4*ou%m(h6r3A zX#s&`9LU1ZG&;{CkOK!4fLDTnBys`M!vuz>Q&9OZ0hGQl!~!jSDg|~s*w52opC{sB ze|Cf2luD(*G13LcOAGA!s2FjSK8&IE5#W%J25w!vM0^VyQM!t)inj&RTiJ!wXzFgz z3^IqzB7I0L$llljsGq})thBy9UOyjtFO_*hYM_sgcMk>44jeH0V1FDyELc{S1F-;A zS;T^k^~4biG&V*Irq}O;e}j$$+E_#G?HKIn05iP3j|87TkGK~SqG!-KBg5+mN(aLm z8ybhIM`%C19UX$H$KY6JgXbY$0AT%rEpHC;u`rQ$Y=rxUdsc5*Kvc8jaYaO$^)cI6){P6K0r)I6DY4Wr4&B zLQUBraey#0HV|&c4v7PVo3n$zHj99(TZO^3?Ly%C4nYvJTL9eLBLHsM3WKKD>5!B` zQ=BsR3aR6PD(Fa>327E2HAu5TM~Wusc!)>~(gM)+3~m;92Jd;FnSib=M5d6;;5{%R zb4V7DEJ0V!CP-F*oU?gkc>ksUtAYP&V4ND5J>J2^jt*vcFflQWCrB&fLdT%O59PVJ zhid#toR=FNgD!q3&r8#wEBr`!wzvQu5zX?Q>nlSJ4i@WC*CN*-xU66F^V5crWevQ9gsq$I@z1o(a=k7LL~ z7m_~`o;_Ozha1$8Q}{WBehvAlO4EL60y5}8GDrZ< zXh&F}71JbW2A~8KfEWj&UWV#4+Z4p`b{uAj4&WC zha`}X@3~+Iz^WRlOHU&KngK>#j}+_o@LdBC1H-`gT+krWX3-;!)6?{FBp~%20a}FL zFP9%Emqcwa#(`=G>BBZ0qZDQhmZKJg_g8<=bBFKWr!dyg(YkpE+|R*SGpDVU!+VlU zFC54^DLv}`qa%49T>nNiA9Q7Ips#!Xx90tCU2gvK`(F+GPcL=J^>No{)~we#o@&mUb6c$ zCc*<|NJBk-#+{j9xkQ&ujB zI~`#kN~7W!f*-}wkG~Ld!JqZ@tK}eeSnsS5J1fMFXm|`LJx&}5`@dK3W^7#Wnm+_P zBZkp&j1fa2Y=eIjJ0}gh85jt43kaIXXv?xmo@eHrka!Z|vQv12HN#+!I5E z`(fbuW>gFiJL|uXJ!vKt#z3e3HlVdboH7;e#i3(2<)Fg-I@BR!qY#eof3MFZ&*Y@l zI|KJf&ge@p2Dq09Vu$$Qxb7!}{m-iRk@!)%KL)txi3;~Z4Pb}u@GsW;ELiWeG9V51 znX#}B&4Y2E7-H=OpNE@q{%hFLxwIpBF2t{vPREa8_{linXT;#1vMRWjOzLOP$-hf( z>=?$0;~~PnkqY;~K{EM6Vo-T(0K{A0}VUGmu*hR z{tw3hvBN%N3G3Yw`X5Te+F{J`(3w1s3-+1EbnFQKcrgrX1Jqvs@ADGe%M0s$EbK$$ zK)=y=upBc6SjGYAACCcI=Y*6Fi8_jgwZlLxD26fnQfJmb8^gHRN5(TemhX@0e=vr> zg`W}6U>x6VhoA3DqsGGD9uL1DhB3!OXO=k}59TqD@(0Nb{)Ut_luTioK_>7wjc!5C zIr@w}b`Fez3)0wQfKl&bae7;PcTA7%?f2xucM0G)wt_KO!Ewx>F~;=BI0j=Fb4>pp zv}0R^xM4eti~+^+gE$6b81p(kwzuDti(-K9bc|?+pJEl@H+jSYuxZQV8rl8 zjp@M{#%qItIUFN~KcO9Hed*`$5A-2~pAo~K&<-Q+`9`$CK>rzqAI4w~$F%vs9s{~x zg4BP%Gy*@m?;D6=SRX?888Q6peF@_4Z->8wAH~Cn!R$|Hhq2cIzFYqT_+cDourHbY z0qroxJnrZ4Gh+Ay+F`_c%+KRT>y3qw{)89?=hJ@=KO=@ep)aBJ$c!JHfBMJpsP*3G za7|)VJJ8B;4?n{~ldJF7%jmb`-ftIvNd~ekoufG(`K(3=LNc;HBY& z(lp#q8XAD#cIf}k49zX_i`*fO+#!zKA&%T3j@%)R+#yag067CU%yUEe47>wzGU8^` z1EXFT^@I!{J!F8!X?S6ph8J=gUi5tl93*W>7}_uR<2N2~e}FaG?}KPyugQ=-OGEZs z!GBoyYY+H*ANn4?Z)X4l+7H%`17i5~zRlRIX?t)6_eu=g2Q`3WBhxSUeea+M-S?RL zX9oBGKn%a!H+*hx4d2(I!gsi+@SQK%<{X22M~2tMulJoa)0*+z9=-YO+;DFEm5eE1U9b^B(Z}2^9!Qk`!A$wUE z7$Ar5?NRg2&G!AZqnmE64eh^Anss3i!{}%6@Et+4rr!=}!SBF8eZ2*J3ujCWbl;3; z48H~goPSv(8X61fKKdpP!Z7$88NL^Z?j`!^*I?-P4X^pMxyWz~@$(UeAcTSDd(`vO z{~rc;9|GfMJcApU3k}22a!&)k4{CU!e_ny^Y3cO;tOvOMKEyWz!vG(Kp*;hB?d|R3`2X~=5a6#^o5@qn?J-bI8Ppip{-yG z!k|VcGsq!jF~}7DMr49Wap-s&>o=U^T0!Lcy}!(bhtYsPQy z4|EJe{12QL#=c(suQ89Mhw9<`bui%nx7Nep`C&*M3~vMEACmcRYYRGtANq$F%zh&V zc)cEVeHz*Z1N)L7k-(k3np#{GcDh2Q@ya0YHl*n7fl*ZPAsbU-a94MYYtA#&!c`xGIaV;yzsmrjfieTEtqB_WgZp2*NplHx=$O{M~2#i_vJ{ps-NgK zQsxKK_CBM2PP_je+Xft`(vYfXXgIUr{=PA=7a8`2EHk)Ym2QKIforz# tySWtj{oF3N9@_;i*Fv5S)9x^z=nlWP>jpp-9)52ZmLVA=i*%6g{{fxOO~wEK literal 0 HcmV?d00001 diff --git a/packages/local_auth/local_auth/example/windows/runner/runner.exe.manifest b/packages/local_auth/local_auth/example/windows/runner/runner.exe.manifest new file mode 100644 index 000000000000..c977c4a42589 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/packages/local_auth/local_auth/example/windows/runner/utils.cpp b/packages/local_auth/local_auth/example/windows/runner/utils.cpp new file mode 100644 index 000000000000..8b8eaa54539a --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/utils.cpp @@ -0,0 +1,66 @@ +// 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. +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE* unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = + ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, + nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/packages/local_auth/local_auth/example/windows/runner/utils.h b/packages/local_auth/local_auth/example/windows/runner/utils.h new file mode 100644 index 000000000000..6d1cc48f0426 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/utils.h @@ -0,0 +1,22 @@ +// 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. +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/packages/local_auth/local_auth/example/windows/runner/win32_window.cpp b/packages/local_auth/local_auth/example/windows/runner/win32_window.cpp new file mode 100644 index 000000000000..34738de2d35b --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/win32_window.cpp @@ -0,0 +1,240 @@ +// 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. +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { ++g_active_window_count; } + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { return window_handle_; } + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/packages/local_auth/local_auth/example/windows/runner/win32_window.h b/packages/local_auth/local_auth/example/windows/runner/win32_window.h new file mode 100644 index 000000000000..0f8bd1b7f920 --- /dev/null +++ b/packages/local_auth/local_auth/example/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +// 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. +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/local_auth/local_auth/lib/error_codes.dart b/packages/local_auth/local_auth/lib/error_codes.dart index 15660ee948df..8959bf297700 100644 --- a/packages/local_auth/local_auth/lib/error_codes.dart +++ b/packages/local_auth/local_auth/lib/error_codes.dart @@ -24,3 +24,6 @@ const String lockedOut = 'LockedOut'; /// Indicates the API is locked out more persistently than [lockedOut]. /// Strong authentication like PIN/Pattern/Password is required to unlock. const String permanentlyLockedOut = 'PermanentlyLockedOut'; + +/// Indicates that the biometricOnly parameter can't be true on Windows +const String biometricOnlyNotSupported = 'biometricOnlyNotSupported'; 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 206bd04f7b32..e369f67187a5 100644 --- a/packages/local_auth/local_auth/lib/src/local_auth.dart +++ b/packages/local_auth/local_auth/lib/src/local_auth.dart @@ -14,6 +14,7 @@ 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_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_windows/local_auth_windows.dart'; /// A Flutter plugin for authenticating the user identity locally. class LocalAuthentication { @@ -39,7 +40,8 @@ class LocalAuthentication { {required String localizedReason, Iterable authMessages = const [ IOSAuthMessages(), - AndroidAuthMessages() + AndroidAuthMessages(), + WindowsAuthMessages() ], AuthenticationOptions options = const AuthenticationOptions()}) { return LocalAuthPlatform.instance.authenticate( diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index 119b8d778cbc..a555150617b8 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -3,7 +3,7 @@ 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/plugins/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.0.2 +version: 2.1.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -16,6 +16,8 @@ flutter: default_package: local_auth_android ios: default_package: local_auth_ios + windows: + default_package: local_auth_windows dependencies: flutter: @@ -24,6 +26,7 @@ dependencies: local_auth_android: ^1.0.0 local_auth_ios: ^1.0.1 local_auth_platform_interface: ^1.0.1 + local_auth_windows: ^1.0.0 dev_dependencies: flutter_driver: 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 069f9fec2966..844c981e8120 100644 --- a/packages/local_auth/local_auth/test/local_auth_test.dart +++ b/packages/local_auth/local_auth/test/local_auth_test.dart @@ -8,6 +8,7 @@ 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_platform_interface/local_auth_platform_interface.dart'; +import 'package:local_auth_windows/local_auth_windows.dart'; import 'package:mockito/mockito.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; @@ -34,6 +35,7 @@ void main() { authMessages: [ const IOSAuthMessages(), const AndroidAuthMessages(), + const WindowsAuthMessages(), ], options: const AuthenticationOptions(), )).called(1); From 1e753ab0c4c1988ceba3cc3da58089dd29893206 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Thu, 19 May 2022 04:48:10 -0700 Subject: [PATCH 332/844] [google_sign_in] Upgrade to GoogleSignIn 6.2, support arm64 simulators (#5708) --- .../google_sign_in/CHANGELOG.md | 1 + .../google_sign_in/example/ios/Podfile | 4 - .../ios/Runner.xcodeproj/project.pbxproj | 2 - .../google_sign_in_ios/CHANGELOG.md | 4 + .../google_sign_in_ios/example/ios/Podfile | 4 - .../ios/Runner.xcodeproj/project.pbxproj | 6 +- .../ios/RunnerTests/GoogleSignInTests.m | 430 +++++++++++++----- .../ios/Classes/FLTGoogleSignInPlugin.m | 248 +++++----- .../ios/google_sign_in_ios.podspec | 7 +- .../google_sign_in_ios/pubspec.yaml | 4 +- 10 files changed, 458 insertions(+), 252 deletions(-) diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 9edadf7b0469..86a3b565da21 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -2,6 +2,7 @@ * Updates tests to use a mock platform instead of relying on default method channel implementation internals. +* Removes example workaround to build for arm64 iOS simulators. ## 5.3.1 diff --git a/packages/google_sign_in/google_sign_in/example/ios/Podfile b/packages/google_sign_in/google_sign_in/example/ios/Podfile index 56085c312df7..f7d6a5e68c3a 100644 --- a/packages/google_sign_in/google_sign_in/example/ios/Podfile +++ b/packages/google_sign_in/google_sign_in/example/ios/Podfile @@ -34,9 +34,5 @@ end post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) - target.build_configurations.each do |build_configuration| - # GoogleSignIn does not support arm64 simulators. - build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386' - end end end diff --git a/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj index 8909bb9b31c6..6c698e15ba15 100644 --- a/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_sign_in/google_sign_in/example/ios/Runner.xcodeproj/project.pbxproj @@ -426,7 +426,6 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ENABLE_BITCODE = NO; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -448,7 +447,6 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ENABLE_BITCODE = NO; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", 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 90069d05f442..e5de49da7f22 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.3.0 + +* Supports arm64 iOS simulators by increasing GoogleSignIn dependency to version 6.2. + ## 5.2.7 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile b/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile index b20e1ad2fca0..6c315d202770 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile @@ -42,9 +42,5 @@ end post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) - target.build_configurations.each do |build_configuration| - # GoogleSignIn does not support arm64 simulators. - build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386' - end end end diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj index f2bf4ebc514e..a7f2019ac311 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -384,7 +384,7 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", - "${PODS_ROOT}/GoogleSignIn/Resources/GoogleSignIn.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/GoogleSignIn/GoogleSignIn.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( @@ -603,7 +603,6 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ENABLE_BITCODE = NO; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -625,7 +624,6 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ENABLE_BITCODE = NO; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -647,7 +645,6 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; INFOPLIST_FILE = RunnerTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; @@ -662,7 +659,6 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; INFOPLIST_FILE = RunnerTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m index 3bc08d18604a..7efd490f30fe 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m @@ -64,66 +64,103 @@ - (void)testSignOut { } - (void)testDisconnect { + [[self.mockSignIn stub] disconnectWithCallback:[OCMArg invokeBlockWithArgs:[NSNull null], nil]]; FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"disconnect" arguments:nil]; + XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; [self.plugin handleMethodCall:methodCall - result:^(id result){ + result:^(NSDictionary *result) { + XCTAssertEqualObjects(result, @{}); + [expectation fulfill]; }]; - OCMVerify([self.mockSignIn disconnect]); + [self waitForExpectationsWithTimeout:5.0 handler:nil]; } -#pragma mark - Init - -- (void)testInitGoogleServiceInfoPlist { - FlutterMethodCall *methodCall = [FlutterMethodCall - methodCallWithMethodName:@"init" - arguments:@{@"scopes" : @[ @"mockScope1" ], @"hostedDomain" : @"example.com"}]; +- (void)testDisconnectIgnoresError { + NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain + code:kGIDSignInErrorCodeHasNoAuthInKeychain + userInfo:nil]; + [[self.mockSignIn stub] disconnectWithCallback:[OCMArg invokeBlockWithArgs:error, nil]]; + FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"disconnect" + arguments:nil]; XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; [self.plugin handleMethodCall:methodCall - result:^(id result) { - XCTAssertNil(result); + result:^(NSDictionary *result) { + XCTAssertEqualObjects(result, @{}); [expectation fulfill]; }]; [self waitForExpectationsWithTimeout:5.0 handler:nil]; - - id mockSignIn = self.mockSignIn; - OCMVerify([mockSignIn setScopes:@[ @"mockScope1" ]]); - OCMVerify([mockSignIn setHostedDomain:@"example.com"]); - - // Set in example app GoogleService-Info.plist. - OCMVerify([mockSignIn - setClientID:@"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"]); - OCMVerify([mockSignIn setServerClientID:@"YOUR_SERVER_CLIENT_ID"]); } -- (void)testInitNullDomain { - FlutterMethodCall *methodCall = +#pragma mark - Init + +- (void)testInitGoogleServiceInfoPlist { + FlutterMethodCall *initMethodCall = [FlutterMethodCall methodCallWithMethodName:@"init" - arguments:@{@"hostedDomain" : [NSNull null]}]; + arguments:@{@"hostedDomain" : @"example.com"}]; - XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; - [self.plugin handleMethodCall:methodCall - result:^(id r) { - [expectation fulfill]; + XCTestExpectation *initExpectation = + [self expectationWithDescription:@"expect result returns true"]; + [self.plugin handleMethodCall:initMethodCall + result:^(id result) { + XCTAssertNil(result); + [initExpectation fulfill]; }]; [self waitForExpectationsWithTimeout:5.0 handler:nil]; - OCMVerify([self.mockSignIn setHostedDomain:nil]); + + // Initialization values used in the next sign in request. + FlutterMethodCall *signInMethodCall = [FlutterMethodCall methodCallWithMethodName:@"signIn" + arguments:nil]; + [self.plugin handleMethodCall:signInMethodCall + result:^(id r){ + }]; + OCMVerify([self.mockSignIn + signInWithConfiguration:[OCMArg checkWithBlock:^BOOL(GIDConfiguration *configuration) { + // Set in example app GoogleService-Info.plist. + return + [configuration.hostedDomain isEqualToString:@"example.com"] && + [configuration.clientID + isEqualToString: + @"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"] && + [configuration.serverClientID isEqualToString:@"YOUR_SERVER_CLIENT_ID"]; + }] + presentingViewController:[OCMArg isKindOfClass:[FlutterViewController class]] + hint:nil + additionalScopes:OCMOCK_ANY + callback:OCMOCK_ANY]); } -- (void)testInitDynamicClientId { - FlutterMethodCall *methodCall = - [FlutterMethodCall methodCallWithMethodName:@"init" - arguments:@{@"clientId" : @"mockClientId"}]; +- (void)testInitDynamicClientIdNullDomain { + FlutterMethodCall *initMethodCall = [FlutterMethodCall + methodCallWithMethodName:@"init" + arguments:@{@"hostedDomain" : [NSNull null], @"clientId" : @"mockClientId"}]; - XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; - [self.plugin handleMethodCall:methodCall - result:^(id r) { - [expectation fulfill]; + XCTestExpectation *initExpectation = + [self expectationWithDescription:@"expect result returns true"]; + [self.plugin handleMethodCall:initMethodCall + result:^(id result) { + XCTAssertNil(result); + [initExpectation fulfill]; }]; [self waitForExpectationsWithTimeout:5.0 handler:nil]; - OCMVerify([self.mockSignIn setClientID:@"mockClientId"]); + + // Initialization values used in the next sign in request. + FlutterMethodCall *signInMethodCall = [FlutterMethodCall methodCallWithMethodName:@"signIn" + arguments:nil]; + [self.plugin handleMethodCall:signInMethodCall + result:^(id r){ + }]; + OCMVerify([self.mockSignIn + signInWithConfiguration:[OCMArg checkWithBlock:^BOOL(GIDConfiguration *configuration) { + return configuration.hostedDomain == nil && + [configuration.clientID isEqualToString:@"mockClientId"]; + }] + presentingViewController:[OCMArg isKindOfClass:[FlutterViewController class]] + hint:nil + additionalScopes:OCMOCK_ANY + callback:OCMOCK_ANY]); } #pragma mark - Is signed in @@ -161,59 +198,195 @@ - (void)testIsSignedIn { #pragma mark - Sign in silently - (void)testSignInSilently { - OCMExpect([self.mockSignIn restorePreviousSignIn]); + id mockUser = OCMClassMock([GIDGoogleUser class]); + OCMStub([mockUser userID]).andReturn(@"mockID"); + + [[self.mockSignIn stub] + restorePreviousSignInWithCallback:[OCMArg invokeBlockWithArgs:mockUser, [NSNull null], nil]]; FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"signInSilently" arguments:nil]; + XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; [self.plugin handleMethodCall:methodCall - result:^(id result){ + result:^(NSDictionary *result) { + XCTAssertEqualObjects(result[@"displayName"], [NSNull null]); + XCTAssertEqualObjects(result[@"email"], [NSNull null]); + XCTAssertEqualObjects(result[@"id"], @"mockID"); + XCTAssertEqualObjects(result[@"photoUrl"], [NSNull null]); + XCTAssertEqualObjects(result[@"serverAuthCode"], [NSNull null]); + [expectation fulfill]; }]; - OCMVerifyAll(self.mockSignIn); + [self waitForExpectationsWithTimeout:5.0 handler:nil]; } -- (void)testSignInSilentlyFailsConcurrently { +- (void)testSignInSilentlyWithError { + NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain + code:kGIDSignInErrorCodeHasNoAuthInKeychain + userInfo:nil]; + + [[self.mockSignIn stub] + restorePreviousSignInWithCallback:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"signInSilently" arguments:nil]; XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; - - OCMExpect([self.mockSignIn restorePreviousSignIn]).andDo(^(NSInvocation *invocation) { - // Simulate calling the same method while the previous one is in flight. - [self.plugin handleMethodCall:methodCall - result:^(FlutterError *result) { - XCTAssertEqualObjects(result.code, @"concurrent-requests"); - [expectation fulfill]; - }]; - }); - [self.plugin handleMethodCall:methodCall - result:^(id result){ + result:^(FlutterError *result) { + XCTAssertEqualObjects(result.code, @"sign_in_required"); + [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5.0 handler:nil]; } #pragma mark - Sign in - (void)testSignIn { + id mockUser = OCMClassMock([GIDGoogleUser class]); + id mockUserProfile = OCMClassMock([GIDProfileData class]); + OCMStub([mockUserProfile name]).andReturn(@"mockDisplay"); + OCMStub([mockUserProfile email]).andReturn(@"mock@example.com"); + OCMStub([mockUserProfile hasImage]).andReturn(YES); + OCMStub([mockUserProfile imageURLWithDimension:1337]) + .andReturn([NSURL URLWithString:@"https://example.com/profile.png"]); + + OCMStub([mockUser profile]).andReturn(mockUserProfile); + OCMStub([mockUser userID]).andReturn(@"mockID"); + OCMStub([mockUser serverAuthCode]).andReturn(@"mockAuthCode"); + + [[self.mockSignIn expect] + signInWithConfiguration:[OCMArg checkWithBlock:^BOOL(GIDConfiguration *configuration) { + return [configuration.clientID + isEqualToString: + @"479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com"]; + }] + presentingViewController:[OCMArg isKindOfClass:[FlutterViewController class]] + hint:nil + additionalScopes:@[] + callback:[OCMArg invokeBlockWithArgs:mockUser, [NSNull null], nil]]; + + FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"signIn" + arguments:nil]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; + [self.plugin + handleMethodCall:methodCall + result:^(NSDictionary *result) { + XCTAssertEqualObjects(result[@"displayName"], @"mockDisplay"); + XCTAssertEqualObjects(result[@"email"], @"mock@example.com"); + XCTAssertEqualObjects(result[@"id"], @"mockID"); + XCTAssertEqualObjects(result[@"photoUrl"], @"https://example.com/profile.png"); + XCTAssertEqualObjects(result[@"serverAuthCode"], @"mockAuthCode"); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:nil]; + + OCMVerifyAll(self.mockSignIn); +} + +- (void)testSignInWithInitializedScopes { + FlutterMethodCall *initMethodCall = + [FlutterMethodCall methodCallWithMethodName:@"init" + arguments:@{@"scopes" : @[ @"initial1", @"initial2" ]}]; + + XCTestExpectation *initExpectation = + [self expectationWithDescription:@"expect result returns true"]; + [self.plugin handleMethodCall:initMethodCall + result:^(id result) { + XCTAssertNil(result); + [initExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:nil]; + + id mockUser = OCMClassMock([GIDGoogleUser class]); + OCMStub([mockUser userID]).andReturn(@"mockID"); + + [[self.mockSignIn expect] + signInWithConfiguration:OCMOCK_ANY + presentingViewController:OCMOCK_ANY + hint:nil + additionalScopes:[OCMArg checkWithBlock:^BOOL(NSArray *scopes) { + return [[NSSet setWithArray:scopes] + isEqualToSet:[NSSet setWithObjects:@"initial1", @"initial2", nil]]; + }] + callback:[OCMArg invokeBlockWithArgs:mockUser, [NSNull null], nil]]; + FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"signIn" arguments:nil]; + XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; [self.plugin handleMethodCall:methodCall - result:^(NSNumber *result){ + result:^(NSDictionary *result) { + XCTAssertEqualObjects(result[@"id"], @"mockID"); + [expectation fulfill]; }]; + [self waitForExpectationsWithTimeout:5.0 handler:nil]; - id mockSignIn = self.mockSignIn; - OCMVerify([mockSignIn - setPresentingViewController:[OCMArg isKindOfClass:[FlutterViewController class]]]); - OCMVerify([mockSignIn signIn]); + OCMVerifyAll(self.mockSignIn); +} + +- (void)testSignInAlreadyGranted { + id mockUser = OCMClassMock([GIDGoogleUser class]); + OCMStub([mockUser userID]).andReturn(@"mockID"); + + [[self.mockSignIn stub] + signInWithConfiguration:OCMOCK_ANY + presentingViewController:OCMOCK_ANY + hint:nil + additionalScopes:OCMOCK_ANY + callback:[OCMArg invokeBlockWithArgs:mockUser, [NSNull null], nil]]; + + NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain + code:kGIDSignInErrorCodeScopesAlreadyGranted + userInfo:nil]; + [[self.mockSignIn stub] addScopes:OCMOCK_ANY + presentingViewController:OCMOCK_ANY + callback:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + + FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"signIn" + arguments:nil]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; + [self.plugin handleMethodCall:methodCall + result:^(NSDictionary *result) { + XCTAssertEqualObjects(result[@"id"], @"mockID"); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:nil]; +} + +- (void)testSignInError { + NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain + code:kGIDSignInErrorCodeCanceled + userInfo:nil]; + [[self.mockSignIn stub] + signInWithConfiguration:OCMOCK_ANY + presentingViewController:OCMOCK_ANY + hint:nil + additionalScopes:OCMOCK_ANY + callback:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + + FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"signIn" + arguments:nil]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; + [self.plugin handleMethodCall:methodCall + result:^(FlutterError *result) { + XCTAssertEqualObjects(result.code, @"sign_in_canceled"); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:nil]; } -- (void)testSignInExecption { +- (void)testSignInException { FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"signIn" arguments:nil]; - OCMExpect([self.mockSignIn signIn]) + OCMExpect([self.mockSignIn signInWithConfiguration:OCMOCK_ANY + presentingViewController:OCMOCK_ANY + hint:OCMOCK_ANY + additionalScopes:OCMOCK_ANY + callback:OCMOCK_ANY]) .andThrow([NSException exceptionWithName:@"MockName" reason:@"MockReason" userInfo:nil]); __block FlutterError *error; @@ -237,7 +410,7 @@ - (void)testGetTokens { OCMStub([mockAuthentication idToken]).andReturn(@"mockIdToken"); OCMStub([mockAuthentication accessToken]).andReturn(@"mockAccessToken"); [[mockAuthentication stub] - getTokensWithHandler:[OCMArg invokeBlockWithArgs:mockAuthentication, [NSNull null], nil]]; + doWithFreshTokens:[OCMArg invokeBlockWithArgs:mockAuthentication, [NSNull null], nil]]; OCMStub([mockUser authentication]).andReturn(mockAuthentication); FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"getTokens" @@ -262,7 +435,7 @@ - (void)testGetTokensNoAuthKeychainError { code:kGIDSignInErrorCodeHasNoAuthInKeychain userInfo:nil]; [[mockAuthentication stub] - getTokensWithHandler:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + doWithFreshTokens:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; OCMStub([mockUser authentication]).andReturn(mockAuthentication); FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"getTokens" @@ -287,7 +460,7 @@ - (void)testGetTokensCancelledError { code:kGIDSignInErrorCodeCanceled userInfo:nil]; [[mockAuthentication stub] - getTokensWithHandler:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + doWithFreshTokens:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; OCMStub([mockUser authentication]).andReturn(mockAuthentication); FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"getTokens" @@ -310,7 +483,7 @@ - (void)testGetTokensURLError { id mockAuthentication = OCMClassMock([GIDAuthentication class]); NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorTimedOut userInfo:nil]; [[mockAuthentication stub] - getTokensWithHandler:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + doWithFreshTokens:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; OCMStub([mockUser authentication]).andReturn(mockAuthentication); FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"getTokens" @@ -333,7 +506,7 @@ - (void)testGetTokensUnknownError { id mockAuthentication = OCMClassMock([GIDAuthentication class]); NSError *error = [NSError errorWithDomain:@"BogusDomain" code:42 userInfo:nil]; [[mockAuthentication stub] - getTokensWithHandler:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + doWithFreshTokens:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; OCMStub([mockUser authentication]).andReturn(mockAuthentication); FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"getTokens" @@ -352,7 +525,12 @@ - (void)testGetTokensUnknownError { #pragma mark - Request scopes - (void)testRequestScopesResultErrorIfNotSignedIn { - OCMStub([self.mockSignIn currentUser]).andReturn(nil); + NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain + code:kGIDSignInErrorCodeNoCurrentUser + userInfo:nil]; + [[self.mockSignIn stub] addScopes:@[ @"mockScope1" ] + presentingViewController:OCMOCK_ANY + callback:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"requestScopes" @@ -368,14 +546,16 @@ - (void)testRequestScopesResultErrorIfNotSignedIn { } - (void)testRequestScopesIfNoMissingScope { - // Mock Google Signin internal calls - GIDGoogleUser *mockUser = OCMClassMock([GIDGoogleUser class]); - OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); - NSArray *requestedScopes = @[ @"mockScope1" ]; - OCMStub(mockUser.grantedScopes).andReturn(requestedScopes); + NSError *error = [NSError errorWithDomain:kGIDSignInErrorDomain + code:kGIDSignInErrorCodeScopesAlreadyGranted + userInfo:nil]; + [[self.mockSignIn stub] addScopes:@[ @"mockScope1" ] + presentingViewController:OCMOCK_ANY + callback:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; + FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"requestScopes" - arguments:@{@"scopes" : requestedScopes}]; + arguments:@{@"scopes" : @[ @"mockScope1" ]}]; XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; [self.plugin handleMethodCall:methodCall @@ -386,39 +566,50 @@ - (void)testRequestScopesIfNoMissingScope { [self waitForExpectationsWithTimeout:5.0 handler:nil]; } -- (void)testRequestScopesRequestsIfNotGranted { - // Mock Google Signin internal calls - GIDGoogleUser *mockUser = OCMClassMock([GIDGoogleUser class]); - OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); - NSArray *requestedScopes = @[ @"mockScope1" ]; - OCMStub(mockUser.grantedScopes).andReturn(@[]); - id mockSignIn = self.mockSignIn; - OCMStub([mockSignIn scopes]).andReturn(@[]); +- (void)testRequestScopesWithUnknownError { + NSError *error = [NSError errorWithDomain:@"BogusDomain" code:42 userInfo:nil]; + [[self.mockSignIn stub] addScopes:@[ @"mockScope1" ] + presentingViewController:OCMOCK_ANY + callback:[OCMArg invokeBlockWithArgs:[NSNull null], error, nil]]; FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"requestScopes" - arguments:@{@"scopes" : requestedScopes}]; + arguments:@{@"scopes" : @[ @"mockScope1" ]}]; + XCTestExpectation *expectation = [self expectationWithDescription:@"expect result returns true"]; [self.plugin handleMethodCall:methodCall - result:^(id r){ + result:^(NSNumber *result) { + XCTAssertFalse(result.boolValue); + [expectation fulfill]; }]; + [self waitForExpectationsWithTimeout:5.0 handler:nil]; +} + +- (void)testRequestScopesException { + FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"requestScopes" + arguments:nil]; + OCMExpect([self.mockSignIn addScopes:@[] presentingViewController:OCMOCK_ANY callback:OCMOCK_ANY]) + .andThrow([NSException exceptionWithName:@"MockName" reason:@"MockReason" userInfo:nil]); - OCMVerify([mockSignIn setScopes:@[ @"mockScope1" ]]); - OCMVerify([mockSignIn signIn]); + [self.plugin handleMethodCall:methodCall + result:^(FlutterError *result) { + XCTAssertEqualObjects(result.code, @"request_scopes"); + XCTAssertEqualObjects(result.message, @"MockReason"); + XCTAssertEqualObjects(result.details, @"MockName"); + }]; } -- (void)testRequestScopesReturnsFalseIfNotGranted { - // Mock Google Signin internal calls +- (void)testRequestScopesReturnsFalseIfOnlySubsetGranted { GIDGoogleUser *mockUser = OCMClassMock([GIDGoogleUser class]); OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); - NSArray *requestedScopes = @[ @"mockScope1" ]; - OCMStub(mockUser.grantedScopes).andReturn(@[]); + NSArray *requestedScopes = @[ @"mockScope1", @"mockScope2" ]; + + // Only grant one of the two requested scopes. + OCMStub(mockUser.grantedScopes).andReturn(@[ @"mockScope1" ]); - OCMStub([self.mockSignIn signIn]).andDo(^(NSInvocation *invocation) { - [((NSObject *)self.plugin) signIn:self.mockSignIn - didSignInForUser:mockUser - withError:nil]; - }); + [[self.mockSignIn stub] addScopes:requestedScopes + presentingViewController:OCMOCK_ANY + callback:[OCMArg invokeBlockWithArgs:mockUser, [NSNull null], nil]]; FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"requestScopes" @@ -433,20 +624,53 @@ - (void)testRequestScopesReturnsFalseIfNotGranted { [self waitForExpectationsWithTimeout:5.0 handler:nil]; } +- (void)testRequestsInitializedScopes { + FlutterMethodCall *initMethodCall = + [FlutterMethodCall methodCallWithMethodName:@"init" + arguments:@{@"scopes" : @[ @"initial1", @"initial2" ]}]; + + XCTestExpectation *initExpectation = + [self expectationWithDescription:@"expect result returns true"]; + [self.plugin handleMethodCall:initMethodCall + result:^(id result) { + XCTAssertNil(result); + [initExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:nil]; + + // Include one of the initially requested scopes. + NSArray *addedScopes = @[ @"initial1", @"addScope1", @"addScope2" ]; + + FlutterMethodCall *methodCall = + [FlutterMethodCall methodCallWithMethodName:@"requestScopes" + arguments:@{@"scopes" : addedScopes}]; + + [self.plugin handleMethodCall:methodCall + result:^(id result){ + }]; + + // All four scopes are requested. + [[self.mockSignIn verify] + addScopes:[OCMArg checkWithBlock:^BOOL(NSArray *scopes) { + return [[NSSet setWithArray:scopes] + isEqualToSet:[NSSet setWithObjects:@"initial1", @"initial2", + @"addScope1", @"addScope2", nil]]; + }] + presentingViewController:OCMOCK_ANY + callback:OCMOCK_ANY]; +} + - (void)testRequestScopesReturnsTrueIfGranted { - // Mock Google Signin internal calls GIDGoogleUser *mockUser = OCMClassMock([GIDGoogleUser class]); OCMStub([self.mockSignIn currentUser]).andReturn(mockUser); - NSArray *requestedScopes = @[ @"mockScope1" ]; - NSMutableArray *availableScopes = [NSMutableArray new]; - OCMStub(mockUser.grantedScopes).andReturn(availableScopes); - - OCMStub([self.mockSignIn signIn]).andDo(^(NSInvocation *invocation) { - [availableScopes addObject:@"mockScope1"]; - [((NSObject *)self.plugin) signIn:self.mockSignIn - didSignInForUser:mockUser - withError:nil]; - }); + NSArray *requestedScopes = @[ @"mockScope1", @"mockScope2" ]; + + // Grant both of the requested scopes. + OCMStub(mockUser.grantedScopes).andReturn(requestedScopes); + + [[self.mockSignIn stub] addScopes:requestedScopes + presentingViewController:OCMOCK_ANY + callback:[OCMArg invokeBlockWithArgs:mockUser, [NSNull null], nil]]; FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"requestScopes" diff --git a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m index 5ad69e2ad052..55d09bd903d4 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m @@ -36,17 +36,28 @@ details:error.localizedDescription]; } -@interface FLTGoogleSignInPlugin () +@interface FLTGoogleSignInPlugin () + +// Configuration wrapping Google Cloud Console, Google Apps, OpenID, +// and other initialization metadata. +@property(strong) GIDConfiguration *configuration; + +// Permissions requested during at sign in "init" method call +// unioned with scopes requested later with incremental authorization +// "requestScopes" method call. +// The "email" and "profile" base scopes are always implicitly requested. +@property(copy) NSSet *requestedScopes; + +// Instance used to manage Google Sign In authentication including +// sign in, sign out, and requesting additional scopes. @property(strong, readonly) GIDSignIn *signIn; // Redeclared as not a designated initializer. - (instancetype)init; + @end -@implementation FLTGoogleSignInPlugin { - FlutterResult _accountRequest; - NSArray *_additionalScopesRequest; -} +@implementation FLTGoogleSignInPlugin + (void)registerWithRegistrar:(NSObject *)registrar { FlutterMethodChannel *channel = @@ -65,11 +76,11 @@ - (instancetype)initWithSignIn:(GIDSignIn *)signIn { self = [super init]; if (self) { _signIn = signIn; - _signIn.delegate = self; // On the iOS simulator, we get "Broken pipe" errors after sign-in for some // unknown reason. We can avoid crashing the app by ignoring them. signal(SIGPIPE, SIG_IGN); + _requestedScopes = [[NSSet alloc] init]; } return self; } @@ -78,25 +89,14 @@ - (instancetype)initWithSignIn:(GIDSignIn *)signIn { - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { if ([call.method isEqualToString:@"init"]) { - NSString *path = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"]; - if (path) { - NSMutableDictionary *plist = - [[NSMutableDictionary alloc] initWithContentsOfFile:path]; - BOOL hasDynamicClientId = [call.arguments[@"clientId"] isKindOfClass:[NSString class]]; - - if (hasDynamicClientId) { - self.signIn.clientID = call.arguments[@"clientId"]; - } else { - self.signIn.clientID = plist[kClientIdKey]; - } - - self.signIn.serverClientID = plist[kServerClientIdKey]; - self.signIn.scopes = call.arguments[@"scopes"]; - if (call.arguments[@"hostedDomain"] == [NSNull null]) { - self.signIn.hostedDomain = nil; - } else { - self.signIn.hostedDomain = call.arguments[@"hostedDomain"]; + GIDConfiguration *configuration = + [self configurationWithClientIdArgument:call.arguments[@"clientId"] + hostedDomainArgument:call.arguments[@"hostedDomain"]]; + if (configuration != nil) { + if ([call.arguments[@"scopes"] isKindOfClass:[NSArray class]]) { + self.requestedScopes = [NSSet setWithArray:call.arguments[@"scopes"]]; } + self.configuration = configuration; result(nil); } else { result([FlutterError errorWithCode:@"missing-config" @@ -104,26 +104,31 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result details:nil]); } } else if ([call.method isEqualToString:@"signInSilently"]) { - if ([self setAccountRequest:result]) { - [self.signIn restorePreviousSignIn]; - } + [self.signIn restorePreviousSignInWithCallback:^(GIDGoogleUser *user, NSError *error) { + [self didSignInForUser:user result:result withError:error]; + }]; } else if ([call.method isEqualToString:@"isSignedIn"]) { result(@([self.signIn hasPreviousSignIn])); } else if ([call.method isEqualToString:@"signIn"]) { - self.signIn.presentingViewController = [self topViewController]; - - if ([self setAccountRequest:result]) { - @try { - [self.signIn signIn]; - } @catch (NSException *e) { - result([FlutterError errorWithCode:@"google_sign_in" message:e.reason details:e.name]); - [e raise]; - } + @try { + GIDConfiguration *configuration = self.configuration + ?: [self configurationWithClientIdArgument:nil + hostedDomainArgument:nil]; + [self.signIn signInWithConfiguration:configuration + presentingViewController:[self topViewController] + hint:nil + additionalScopes:self.requestedScopes.allObjects + callback:^(GIDGoogleUser *user, NSError *error) { + [self didSignInForUser:user result:result withError:error]; + }]; + } @catch (NSException *e) { + result([FlutterError errorWithCode:@"google_sign_in" message:e.reason details:e.name]); + [e raise]; } } else if ([call.method isEqualToString:@"getTokens"]) { GIDGoogleUser *currentUser = self.signIn.currentUser; GIDAuthentication *auth = currentUser.authentication; - [auth getTokensWithHandler:^void(GIDAuthentication *authentication, NSError *error) { + [auth doWithFreshTokens:^void(GIDAuthentication *authentication, NSError *error) { result(error != nil ? getFlutterError(error) : @{ @"idToken" : authentication.idToken, @"accessToken" : authentication.accessToken, @@ -133,61 +138,49 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result [self.signIn signOut]; result(nil); } else if ([call.method isEqualToString:@"disconnect"]) { - if ([self setAccountRequest:result]) { - [self.signIn disconnect]; - } + [self.signIn disconnectWithCallback:^(NSError *error) { + [self respondWithAccount:@{} result:result error:nil]; + }]; } else if ([call.method isEqualToString:@"requestScopes"]) { - GIDGoogleUser *user = self.signIn.currentUser; - if (user == nil) { - result([FlutterError errorWithCode:@"sign_in_required" - message:@"No account to grant scopes." - details:nil]); - return; + id scopeArgument = call.arguments[@"scopes"]; + if ([scopeArgument isKindOfClass:[NSArray class]]) { + self.requestedScopes = [self.requestedScopes setByAddingObjectsFromArray:scopeArgument]; } - - NSArray *currentScopes = self.signIn.scopes; - NSArray *scopes = call.arguments[@"scopes"]; - NSArray *missingScopes = [scopes - filteredArrayUsingPredicate:[NSPredicate - predicateWithBlock:^BOOL(id scope, NSDictionary *bindings) { - return ![user.grantedScopes containsObject:scope]; - }]]; - - if (!missingScopes || !missingScopes.count) { - result(@(YES)); - return; - } - - if ([self setAccountRequest:result]) { - _additionalScopesRequest = missingScopes; - self.signIn.scopes = [currentScopes arrayByAddingObjectsFromArray:missingScopes]; - self.signIn.presentingViewController = [self topViewController]; - self.signIn.loginHint = user.profile.email; - @try { - [self.signIn signIn]; - } @catch (NSException *e) { - result([FlutterError errorWithCode:@"request_scopes" message:e.reason details:e.name]); - } + NSSet *requestedScopes = self.requestedScopes; + + @try { + [self.signIn addScopes:requestedScopes.allObjects + presentingViewController:[self topViewController] + callback:^(GIDGoogleUser *addedScopeUser, NSError *addedScopeError) { + if ([addedScopeError.domain isEqualToString:kGIDSignInErrorDomain] && + addedScopeError.code == kGIDSignInErrorCodeNoCurrentUser) { + result([FlutterError errorWithCode:@"sign_in_required" + message:@"No account to grant scopes." + details:nil]); + } else if ([addedScopeError.domain + isEqualToString:kGIDSignInErrorDomain] && + addedScopeError.code == + kGIDSignInErrorCodeScopesAlreadyGranted) { + // Scopes already granted, report success. + result(@YES); + } else if (addedScopeUser == nil) { + result(@NO); + } else { + NSSet *grantedScopes = + [NSSet setWithArray:addedScopeUser.grantedScopes]; + BOOL granted = [requestedScopes isSubsetOfSet:grantedScopes]; + result(@(granted)); + } + }]; + } @catch (NSException *e) { + result([FlutterError errorWithCode:@"request_scopes" message:e.reason details:e.name]); } } else { result(FlutterMethodNotImplemented); } } -- (BOOL)setAccountRequest:(FlutterResult)request { - if (_accountRequest != nil) { - request([FlutterError errorWithCode:@"concurrent-requests" - message:@"Concurrent requests to account signin" - details:nil]); - return NO; - } - _accountRequest = request; - return YES; -} - -- (BOOL)application:(UIApplication *)app - openURL:(NSURL *)url - options:(NSDictionary *)options { +- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { return [self.signIn handleURL:url]; } @@ -203,57 +196,58 @@ - (void)signIn:(GIDSignIn *)signIn dismissViewController:(UIViewController *)vie [viewController dismissViewControllerAnimated:YES completion:nil]; } -#pragma mark - protocol +#pragma mark - private methods + +/// @return @c nil if GoogleService-Info.plist not found. +- (GIDConfiguration *)configurationWithClientIdArgument:(id)clientIDArg + hostedDomainArgument:(id)hostedDomainArg { + NSString *plistPath = [NSBundle.mainBundle pathForResource:@"GoogleService-Info" ofType:@"plist"]; + if (plistPath == nil) { + return nil; + } + + NSDictionary *plist = [[NSDictionary alloc] initWithContentsOfFile:plistPath]; + + BOOL hasDynamicClientId = [clientIDArg isKindOfClass:[NSString class]]; + NSString *clientID = hasDynamicClientId ? clientIDArg : plist[kClientIdKey]; + + NSString *hostedDomain = nil; + if (hostedDomainArg != [NSNull null]) { + hostedDomain = hostedDomainArg; + } + return [[GIDConfiguration alloc] initWithClientID:clientID + serverClientID:plist[kServerClientIdKey] + hostedDomain:hostedDomain + openIDRealm:nil]; +} -- (void)signIn:(GIDSignIn *)signIn - didSignInForUser:(GIDGoogleUser *)user - withError:(NSError *)error { +- (void)didSignInForUser:(GIDGoogleUser *)user + result:(FlutterResult)result + withError:(NSError *)error { if (error != nil) { // Forward all errors and let Dart side decide how to handle. - [self respondWithAccount:nil error:error]; + [self respondWithAccount:nil result:result error:error]; } else { - if (_additionalScopesRequest) { - bool granted = YES; - for (NSString *scope in _additionalScopesRequest) { - if (![user.grantedScopes containsObject:scope]) { - granted = NO; - break; - } - } - _accountRequest(@(granted)); - _accountRequest = nil; - _additionalScopesRequest = nil; - return; - } else { - NSURL *photoUrl; - if (user.profile.hasImage) { - // Placeholder that will be replaced by on the Dart side based on screen - // size - photoUrl = [user.profile imageURLWithDimension:1337]; - } - [self respondWithAccount:@{ - @"displayName" : user.profile.name ?: [NSNull null], - @"email" : user.profile.email ?: [NSNull null], - @"id" : user.userID ?: [NSNull null], - @"photoUrl" : [photoUrl absoluteString] ?: [NSNull null], - @"serverAuthCode" : user.serverAuthCode ?: [NSNull null] - } - error:nil]; + NSURL *photoUrl; + if (user.profile.hasImage) { + // Placeholder that will be replaced by on the Dart side based on screen size. + photoUrl = [user.profile imageURLWithDimension:1337]; + } + [self respondWithAccount:@{ + @"displayName" : user.profile.name ?: [NSNull null], + @"email" : user.profile.email ?: [NSNull null], + @"id" : user.userID ?: [NSNull null], + @"photoUrl" : [photoUrl absoluteString] ?: [NSNull null], + @"serverAuthCode" : user.serverAuthCode ?: [NSNull null] } + result:result + error:nil]; } } -- (void)signIn:(GIDSignIn *)signIn - didDisconnectWithUser:(GIDGoogleUser *)user - withError:(NSError *)error { - [self respondWithAccount:@{} error:nil]; -} - -#pragma mark - private methods - -- (void)respondWithAccount:(NSDictionary *)account error:(NSError *)error { - FlutterResult result = _accountRequest; - _accountRequest = nil; +- (void)respondWithAccount:(NSDictionary *)account + result:(FlutterResult)result + error:(NSError *)error { result(error != nil ? getFlutterError(error) : account); } diff --git a/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec b/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec index f583f6cffbf0..18a213579a23 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec +++ b/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec @@ -16,11 +16,8 @@ Enables Google Sign-In in Flutter apps. s.public_header_files = 'Classes/**/*.h' s.module_map = 'Classes/FLTGoogleSignInPlugin.modulemap' s.dependency 'Flutter' - s.dependency 'GoogleSignIn', '~> 5.0' + s.dependency 'GoogleSignIn', '~> 6.2' s.static_framework = true - s.platform = :ios, '9.0' - - # GoogleSignIn ~> 5.0 does not support arm64 simulators. - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64' } + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } end 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 b6f541b22ce1..5604ee6a5f18 100644 --- a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml @@ -1,8 +1,8 @@ name: google_sign_in_ios -description: Android implementation of the google_sign_in plugin. +description: iOS implementation of the google_sign_in plugin. repository: https://github.com/flutter/plugins/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.2.7 +version: 5.3.0 environment: sdk: ">=2.14.0 <3.0.0" From 65620edc0718454e63ab9a78aaa8df1f9fde1e44 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 19 May 2022 10:33:10 -0400 Subject: [PATCH 333/844] Roll Flutter from 1994027986cf to a4a8e73bce15 (31 revisions) (#5782) --- .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 73d69ae3af72..822ceaa4a9b1 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1994027986cf59a4c307d6612706ce08c16b1ae8 +a4a8e73bce152ab39d6ae839ca51e447f87293fa From 74f8a63f279778aa3514ba4b8516a48ab96917ed Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 19 May 2022 11:28:11 -0400 Subject: [PATCH 334/844] [ci] Updates iOS deprecation check to iOS 13 (#5786) --- .cirrus.yml | 4 +- .../google_sign_in_ios/CHANGELOG.md | 4 ++ .../google_sign_in_ios/example/ios/Podfile | 1 + .../ios/Classes/FLTGoogleSignInPlugin.m | 5 ++ .../google_sign_in_ios/pubspec.yaml | 2 +- .../image_picker_ios/CHANGELOG.md | 4 ++ .../ios/Classes/FLTImagePickerPlugin.m | 44 ++++++++++++----- .../FLTPHPickerSaveImageToPathOperation.m | 47 +++++++++++++------ .../image_picker_ios/pubspec.yaml | 2 +- .../url_launcher_ios/CHANGELOG.md | 4 ++ .../ios/Classes/FLTURLLauncherPlugin.m | 5 ++ .../url_launcher_ios/pubspec.yaml | 2 +- 12 files changed, 92 insertions(+), 32 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 69fd8955302a..2b8fde823826 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -336,9 +336,7 @@ task: - ./script/tool_runner.sh xcode-analyze --ios xcode_analyze_deprecation_script: # Ensure we don't accidentally introduce deprecated code. - # TODO(stuartmorgan): Update this to a newer version of iOS to get - # ahead of upcoming deprecations. - - ./script/tool_runner.sh xcode-analyze --ios --ios-min-version=11.0 + - ./script/tool_runner.sh xcode-analyze --ios --ios-min-version=13.0 native_test_script: - ./script/tool_runner.sh native-test --ios --ios-destination "platform=iOS Simulator,name=iPhone 11,OS=latest" drive_script: 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 e5de49da7f22..c17f1415b724 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.3.1 + +* Suppresses warnings for pre-iOS-13 codepaths. + ## 5.3.0 * Supports arm64 iOS simulators by increasing GoogleSignIn dependency to version 6.2. diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile b/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile index 6c315d202770..b95dfa75ea04 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/Podfile @@ -27,6 +27,7 @@ require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelpe # Suppress warnings from transitive dependencies that cause analysis to fail. pod 'AppAuth', :inhibit_warnings => true +pod 'GTMAppAuth', :inhibit_warnings => true flutter_ios_podfile_setup diff --git a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m index 55d09bd903d4..608cdc2bec6d 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m @@ -252,8 +252,13 @@ - (void)respondWithAccount:(NSDictionary *)account } - (UIViewController *)topViewController { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // TODO(stuartmorgan) Provide a non-deprecated codepath. See + // https://github.com/flutter/flutter/issues/104117 return [self topViewControllerFromViewController:[UIApplication sharedApplication] .keyWindow.rootViewController]; +#pragma clang diagnostic pop } /** 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 5604ee6a5f18..13f045b6006c 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/plugins/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.3.0 +version: 5.3.1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index e994fcc50c8e..86ee7d0a2d00 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.5+5 + +* Adds non-deprecated codepaths for iOS 13+. + ## 0.8.5+4 * Suppresses warnings for pre-iOS-11 codepaths. 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 76ed9623a57c..18d4ad2f054c 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m @@ -567,18 +567,38 @@ - (void)imagePickerController:(UIImagePickerController *)picker // Image picked without an original asset (e.g. User took a photo directly) [self saveImageWithPickerInfo:info image:image imageQuality:desiredImageQuality]; } else { - [[PHImageManager defaultManager] - requestImageDataForAsset:originalAsset - options:nil - resultHandler:^(NSData *_Nullable imageData, NSString *_Nullable dataUTI, - UIImageOrientation orientation, NSDictionary *_Nullable info) { - // maxWidth and maxHeight are used only for GIF images. - [self saveImageWithOriginalImageData:imageData - image:image - maxWidth:maxWidth - maxHeight:maxHeight - imageQuality:desiredImageQuality]; - }]; + void (^resultHandler)(NSData *imageData, NSString *dataUTI, NSDictionary *info) = ^( + NSData *_Nullable imageData, NSString *_Nullable dataUTI, NSDictionary *_Nullable info) { + // maxWidth and maxHeight are used only for GIF images. + [self saveImageWithOriginalImageData:imageData + image:image + maxWidth:maxWidth + maxHeight:maxHeight + imageQuality:desiredImageQuality]; + }; + if (@available(iOS 13.0, *)) { + [[PHImageManager defaultManager] + requestImageDataAndOrientationForAsset:originalAsset + options:nil + resultHandler:^(NSData *_Nullable imageData, + NSString *_Nullable dataUTI, + CGImagePropertyOrientation orientation, + NSDictionary *_Nullable info) { + resultHandler(imageData, dataUTI, info); + }]; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [[PHImageManager defaultManager] + requestImageDataForAsset:originalAsset + options:nil + resultHandler:^(NSData *_Nullable imageData, NSString *_Nullable dataUTI, + UIImageOrientation orientation, + NSDictionary *_Nullable info) { + resultHandler(imageData, dataUTI, info); + }]; +#pragma clang diagnostic pop + } } } } 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 9e7e7e87a30c..a81c95f1b120 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m @@ -122,20 +122,39 @@ - (void)processImage:(UIImage *)localImage API_AVAILABLE(ios(14)) { isMetadataAvailable:originalAsset != nil]; } if (originalAsset) { - [[PHImageManager defaultManager] - requestImageDataForAsset:originalAsset - options:nil - resultHandler:^(NSData *_Nullable imageData, NSString *_Nullable dataUTI, - UIImageOrientation orientation, NSDictionary *_Nullable info) { - // maxWidth and maxHeight are used only for GIF images. - NSString *savedPath = [FLTImagePickerPhotoAssetUtil - saveImageWithOriginalImageData:imageData - image:localImage - maxWidth:self.maxWidth - maxHeight:self.maxHeight - imageQuality:self.desiredImageQuality]; - [self completeOperationWithPath:savedPath]; - }]; + void (^resultHandler)(NSData *imageData, NSString *dataUTI, NSDictionary *info) = + ^(NSData *_Nullable imageData, NSString *_Nullable dataUTI, NSDictionary *_Nullable info) { + // maxWidth and maxHeight are used only for GIF images. + NSString *savedPath = [FLTImagePickerPhotoAssetUtil + saveImageWithOriginalImageData:imageData + image:localImage + maxWidth:self.maxWidth + maxHeight:self.maxHeight + imageQuality:self.desiredImageQuality]; + [self completeOperationWithPath:savedPath]; + }; + if (@available(iOS 13.0, *)) { + [[PHImageManager defaultManager] + requestImageDataAndOrientationForAsset:originalAsset + options:nil + resultHandler:^(NSData *_Nullable imageData, + NSString *_Nullable dataUTI, + CGImagePropertyOrientation orientation, + NSDictionary *_Nullable info) { + resultHandler(imageData, dataUTI, info); + }]; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [[PHImageManager defaultManager] + requestImageDataForAsset:originalAsset + options:nil + resultHandler:^(NSData *_Nullable imageData, NSString *_Nullable dataUTI, + UIImageOrientation orientation, NSDictionary *_Nullable info) { + resultHandler(imageData, dataUTI, info); + }]; +#pragma clang diagnostic pop + } } else { // Image picked without an original asset (e.g. User pick image without permission) NSString *savedPath = diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index 88f4d3352228..e1bccad60ea4 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 video_picker plugin. repository: https://github.com/flutter/plugins/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.5+4 +version: 0.8.5+5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_ios/CHANGELOG.md b/packages/url_launcher/url_launcher_ios/CHANGELOG.md index 5f6dd37142bb..5fc00bff486e 100644 --- a/packages/url_launcher/url_launcher_ios/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.0.17 + +* Suppresses warnings for pre-iOS-13 codepaths. + ## 6.0.16 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/url_launcher/url_launcher_ios/ios/Classes/FLTURLLauncherPlugin.m b/packages/url_launcher/url_launcher_ios/ios/Classes/FLTURLLauncherPlugin.m index 1aceedc8b1de..af720c87b8b2 100644 --- a/packages/url_launcher/url_launcher_ios/ios/Classes/FLTURLLauncherPlugin.m +++ b/packages/url_launcher/url_launcher_ios/ios/Classes/FLTURLLauncherPlugin.m @@ -136,8 +136,13 @@ - (void)closeWebViewWithResult:(FlutterResult)result API_AVAILABLE(ios(9.0)) { } - (UIViewController *)topViewController { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // TODO(stuartmorgan) Provide a non-deprecated codepath. See + // https://github.com/flutter/flutter/issues/104117 return [self topViewControllerFromViewController:[UIApplication sharedApplication] .keyWindow.rootViewController]; +#pragma clang diagnostic pop } /** diff --git a/packages/url_launcher/url_launcher_ios/pubspec.yaml b/packages/url_launcher/url_launcher_ios/pubspec.yaml index 0b21bad35204..9bb1616441b3 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/plugins/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.0.16 +version: 6.0.17 environment: sdk: ">=2.14.0 <3.0.0" From 3e49ef75f3c1d50b7c78b616ab95c5131b56c75b Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 19 May 2022 18:28:14 -0400 Subject: [PATCH 335/844] [various] Set minimum Flutter versions to 2.8 (#5792) --- packages/camera/camera/example/pubspec.yaml | 2 +- packages/camera/camera/pubspec.yaml | 2 +- packages/camera/camera_web/example/pubspec.yaml | 2 +- packages/espresso/example/pubspec.yaml | 2 +- packages/espresso/pubspec.yaml | 2 +- packages/file_selector/file_selector/pubspec.yaml | 2 +- packages/file_selector/file_selector_macos/example/pubspec.yaml | 2 +- packages/file_selector/file_selector_macos/pubspec.yaml | 2 +- .../file_selector/file_selector_platform_interface/pubspec.yaml | 2 +- packages/file_selector/file_selector_web/example/pubspec.yaml | 2 +- packages/file_selector/file_selector_web/pubspec.yaml | 2 +- .../file_selector/file_selector_windows/example/pubspec.yaml | 2 +- packages/file_selector/file_selector_windows/pubspec.yaml | 2 +- packages/flutter_plugin_android_lifecycle/pubspec.yaml | 2 +- .../google_maps_flutter/example/pubspec.yaml | 2 +- packages/google_maps_flutter/google_maps_flutter/pubspec.yaml | 2 +- .../google_maps_flutter_platform_interface/pubspec.yaml | 2 +- .../google_maps_flutter_web/example/pubspec.yaml | 2 +- .../google_maps_flutter/google_maps_flutter_web/pubspec.yaml | 2 +- packages/google_sign_in/google_sign_in/example/pubspec.yaml | 2 +- packages/google_sign_in/google_sign_in/pubspec.yaml | 2 +- .../google_sign_in/google_sign_in_android/example/pubspec.yaml | 2 +- packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml | 2 +- .../google_sign_in_platform_interface/pubspec.yaml | 2 +- packages/image_picker/image_picker/example/pubspec.yaml | 2 +- packages/image_picker/image_picker/pubspec.yaml | 2 +- packages/image_picker/image_picker_android/example/pubspec.yaml | 2 +- packages/image_picker/image_picker_android/pubspec.yaml | 2 +- packages/image_picker/image_picker_for_web/example/pubspec.yaml | 2 +- packages/image_picker/image_picker_for_web/pubspec.yaml | 2 +- packages/image_picker/image_picker_ios/example/pubspec.yaml | 2 +- .../image_picker/image_picker_platform_interface/pubspec.yaml | 2 +- packages/image_picker/image_picker_windows/example/pubspec.yaml | 2 +- packages/image_picker/image_picker_windows/pubspec.yaml | 2 +- packages/in_app_purchase/in_app_purchase/example/pubspec.yaml | 2 +- packages/in_app_purchase/in_app_purchase/pubspec.yaml | 2 +- .../in_app_purchase_android/example/pubspec.yaml | 2 +- packages/in_app_purchase/in_app_purchase_android/pubspec.yaml | 2 +- .../in_app_purchase_platform_interface/pubspec.yaml | 2 +- .../in_app_purchase_storekit/example/pubspec.yaml | 2 +- packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml | 2 +- packages/ios_platform_images/example/pubspec.yaml | 2 +- packages/ios_platform_images/pubspec.yaml | 2 +- packages/local_auth/local_auth/example/pubspec.yaml | 2 +- packages/path_provider/path_provider/example/pubspec.yaml | 2 +- packages/path_provider/path_provider/pubspec.yaml | 2 +- .../path_provider/path_provider_android/example/pubspec.yaml | 2 +- packages/path_provider/path_provider_ios/example/pubspec.yaml | 2 +- packages/path_provider/path_provider_linux/example/pubspec.yaml | 2 +- packages/path_provider/path_provider_linux/pubspec.yaml | 2 +- packages/path_provider/path_provider_macos/example/pubspec.yaml | 2 +- packages/path_provider/path_provider_macos/pubspec.yaml | 2 +- .../path_provider/path_provider_platform_interface/pubspec.yaml | 2 +- .../path_provider/path_provider_windows/example/pubspec.yaml | 2 +- packages/path_provider/path_provider_windows/pubspec.yaml | 2 +- packages/quick_actions/quick_actions/example/pubspec.yaml | 2 +- .../quick_actions/quick_actions_platform_interface/pubspec.yaml | 2 +- .../shared_preferences/shared_preferences/example/pubspec.yaml | 2 +- packages/shared_preferences/shared_preferences/pubspec.yaml | 2 +- .../shared_preferences_android/example/pubspec.yaml | 2 +- .../shared_preferences_ios/example/pubspec.yaml | 2 +- .../shared_preferences_linux/example/pubspec.yaml | 2 +- .../shared_preferences/shared_preferences_linux/pubspec.yaml | 2 +- .../shared_preferences_macos/example/pubspec.yaml | 2 +- .../shared_preferences/shared_preferences_macos/pubspec.yaml | 2 +- .../shared_preferences_platform_interface/pubspec.yaml | 2 +- .../shared_preferences_web/example/pubspec.yaml | 2 +- packages/shared_preferences/shared_preferences_web/pubspec.yaml | 2 +- .../shared_preferences_windows/example/pubspec.yaml | 2 +- .../shared_preferences/shared_preferences_windows/pubspec.yaml | 2 +- packages/url_launcher/url_launcher/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_android/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_ios/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_linux/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_linux/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_macos/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_macos/pubspec.yaml | 2 +- .../url_launcher/url_launcher_platform_interface/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_web/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_windows/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_windows/pubspec.yaml | 2 +- packages/video_player/video_player_web/example/pubspec.yaml | 2 +- packages/video_player/video_player_web/pubspec.yaml | 2 +- packages/webview_flutter/webview_flutter/example/pubspec.yaml | 2 +- packages/webview_flutter/webview_flutter/pubspec.yaml | 2 +- packages/webview_flutter/webview_flutter_android/pubspec.yaml | 2 +- .../webview_flutter_platform_interface/pubspec.yaml | 2 +- packages/webview_flutter/webview_flutter_web/pubspec.yaml | 2 +- packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml | 2 +- 90 files changed, 90 insertions(+), 90 deletions(-) diff --git a/packages/camera/camera/example/pubspec.yaml b/packages/camera/camera/example/pubspec.yaml index af4d078ff836..e9ae2c74a6be 100644 --- a/packages/camera/camera/example/pubspec.yaml +++ b/packages/camera/camera/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: camera: diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 593e7b5bb978..aca6bebbe066 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -8,7 +8,7 @@ version: 0.9.6 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/camera/camera_web/example/pubspec.yaml b/packages/camera/camera_web/example/pubspec.yaml index 911648ef030d..441c6eb7988f 100644 --- a/packages/camera/camera_web/example/pubspec.yaml +++ b/packages/camera/camera_web/example/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/espresso/example/pubspec.yaml b/packages/espresso/example/pubspec.yaml index 6a5fcdd466fe..c896585be839 100644 --- a/packages/espresso/example/pubspec.yaml +++ b/packages/espresso/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/espresso/pubspec.yaml b/packages/espresso/pubspec.yaml index ac0199cf045f..36b557029f3a 100644 --- a/packages/espresso/pubspec.yaml +++ b/packages/espresso/pubspec.yaml @@ -7,7 +7,7 @@ version: 0.2.0+2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index 7026f7f32287..1c502c055c9a 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -7,7 +7,7 @@ version: 0.8.4+2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/file_selector/file_selector_macos/example/pubspec.yaml b/packages/file_selector/file_selector_macos/example/pubspec.yaml index 2a11958e85cb..dbe127282a17 100644 --- a/packages/file_selector/file_selector_macos/example/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/example/pubspec.yaml @@ -5,7 +5,7 @@ version: 1.0.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: file_selector_macos: diff --git a/packages/file_selector/file_selector_macos/pubspec.yaml b/packages/file_selector/file_selector_macos/pubspec.yaml index 41077c1c04e6..e6f8e9b3c212 100644 --- a/packages/file_selector/file_selector_macos/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.8.2+1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml index 42917751ed58..81ad53a8bab2 100644 --- a/packages/file_selector/file_selector_platform_interface/pubspec.yaml +++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: cross_file: ^0.3.0 diff --git a/packages/file_selector/file_selector_web/example/pubspec.yaml b/packages/file_selector/file_selector_web/example/pubspec.yaml index 8998587615e5..d8b93ee816f3 100644 --- a/packages/file_selector/file_selector_web/example/pubspec.yaml +++ b/packages/file_selector/file_selector_web/example/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.2.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index 488031995e55..c685cca9e884 100644 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.8.1+5 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/file_selector/file_selector_windows/example/pubspec.yaml b/packages/file_selector/file_selector_windows/example/pubspec.yaml index b66a2023deb2..a3e69a6186f8 100644 --- a/packages/file_selector/file_selector_windows/example/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/example/pubspec.yaml @@ -5,7 +5,7 @@ version: 1.0.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: file_selector_platform_interface: ^2.0.0 diff --git a/packages/file_selector/file_selector_windows/pubspec.yaml b/packages/file_selector/file_selector_windows/pubspec.yaml index 152b63ef4a3f..3ca568004ca9 100644 --- a/packages/file_selector/file_selector_windows/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.8.2+1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/flutter_plugin_android_lifecycle/pubspec.yaml b/packages/flutter_plugin_android_lifecycle/pubspec.yaml index c109d0936589..8073cdd9fd76 100644 --- a/packages/flutter_plugin_android_lifecycle/pubspec.yaml +++ b/packages/flutter_plugin_android_lifecycle/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: 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 dbc32b0ef2f8..196f054e1fc0 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: cupertino_icons: ^0.1.0 diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index a294dd09981f..831f3ccd2963 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.1.5 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: 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 8df31fcf626b..998f31936c3b 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 @@ -8,7 +8,7 @@ version: 2.1.6 environment: sdk: '>=2.12.0 <3.0.0' - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: collection: ^1.15.0 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 95a3d4253440..a962e5b864c6 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 @@ -4,7 +4,7 @@ publish_to: none # Tests require flutter beta or greater to run. environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.1.0" + flutter: ">=2.8.0" dependencies: google_maps_flutter_web: 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 271d87d21092..ca8af82dc2db 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.3.2+2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/google_sign_in/google_sign_in/example/pubspec.yaml b/packages/google_sign_in/google_sign_in/example/pubspec.yaml index af9ed877e523..822f83895cfb 100644 --- a/packages/google_sign_in/google_sign_in/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index d1c13c6a8ec4..2a287b1cccd4 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -8,7 +8,7 @@ version: 5.3.1 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml index 316cdd893a2c..3aa8a80ee585 100644 --- a/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml index f2d32c521c1a..ed51e3b63a58 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml index 0deafe80a863..9f6ab508d249 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 2.1.3 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/image_picker/image_picker/example/pubspec.yaml b/packages/image_picker/image_picker/example/pubspec.yaml index 4fe823587398..23c682af3922 100755 --- a/packages/image_picker/image_picker/example/pubspec.yaml +++ b/packages/image_picker/image_picker/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 9d0cedeec484..acc085a06bb9 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -7,7 +7,7 @@ version: 0.8.5+3 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/image_picker/image_picker_android/example/pubspec.yaml b/packages/image_picker/image_picker_android/example/pubspec.yaml index 0d88ae139c71..b5afb16235db 100755 --- a/packages/image_picker/image_picker_android/example/pubspec.yaml +++ b/packages/image_picker/image_picker_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index 095534654ac5..6accfb0eb4ac 100755 --- a/packages/image_picker/image_picker_android/pubspec.yaml +++ b/packages/image_picker/image_picker_android/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.8.4+13 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: 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 a9d6c7b9b5bd..72316ee60988 100644 --- a/packages/image_picker/image_picker_for_web/example/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/example/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.2.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index 0b2c6d2fc0ff..508e32aca5bd 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.1.8 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/image_picker/image_picker_ios/example/pubspec.yaml b/packages/image_picker/image_picker_ios/example/pubspec.yaml index a47893d7687f..84fa77e64d70 100755 --- a/packages/image_picker/image_picker_ios/example/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/image_picker/image_picker_platform_interface/pubspec.yaml b/packages/image_picker/image_picker_platform_interface/pubspec.yaml index be6d5442d03b..4ce1d2fc52f1 100644 --- a/packages/image_picker/image_picker_platform_interface/pubspec.yaml +++ b/packages/image_picker/image_picker_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 2.5.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: cross_file: ^0.3.1+1 diff --git a/packages/image_picker/image_picker_windows/example/pubspec.yaml b/packages/image_picker/image_picker_windows/example/pubspec.yaml index 68c9395c6097..df1dd49327bd 100644 --- a/packages/image_picker/image_picker_windows/example/pubspec.yaml +++ b/packages/image_picker/image_picker_windows/example/pubspec.yaml @@ -5,7 +5,7 @@ version: 1.0.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/image_picker/image_picker_windows/pubspec.yaml b/packages/image_picker/image_picker_windows/pubspec.yaml index af96030debdf..3b6fd922cbea 100644 --- a/packages/image_picker/image_picker_windows/pubspec.yaml +++ b/packages/image_picker/image_picker_windows/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.1.0+2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml index 4a79b190bff9..9db9d63c3a79 100644 --- a/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index 4b9b9b7d64ff..23d771c68afb 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -6,7 +6,7 @@ version: 3.0.4 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml index 9c16efc66e95..0d37b3df1ee5 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.9.1+hotfix.2" + flutter: ">=2.8.0" dependencies: flutter: 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 103251909f14..277be296836c 100644 --- a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.2.2+5 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml index 2a0a6bf061d4..98698b80718c 100644 --- a/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 1.3.1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml index 597dfb0703bb..a98e1693aa40 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: 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 ebd5e55acdad..b154ff304a98 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.3.0+8 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/ios_platform_images/example/pubspec.yaml b/packages/ios_platform_images/example/pubspec.yaml index aa8fea54b287..10be0d6be998 100644 --- a/packages/ios_platform_images/example/pubspec.yaml +++ b/packages/ios_platform_images/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: cupertino_icons: ^1.0.2 diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index 4ff67ee137b4..1ce98c121637 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.2.0+8 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/local_auth/local_auth/example/pubspec.yaml b/packages/local_auth/local_auth/example/pubspec.yaml index c8496fcc0da7..305005b34364 100644 --- a/packages/local_auth/local_auth/example/pubspec.yaml +++ b/packages/local_auth/local_auth/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider/example/pubspec.yaml b/packages/path_provider/path_provider/example/pubspec.yaml index a279bbade6bf..ea6f499622f9 100644 --- a/packages/path_provider/path_provider/example/pubspec.yaml +++ b/packages/path_provider/path_provider/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml index 6ca8325843e4..1e73497a39ff 100644 --- a/packages/path_provider/path_provider/pubspec.yaml +++ b/packages/path_provider/path_provider/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.10 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/path_provider/path_provider_android/example/pubspec.yaml b/packages/path_provider/path_provider_android/example/pubspec.yaml index 75617d8f9747..d546d9f2d729 100644 --- a/packages/path_provider/path_provider_android/example/pubspec.yaml +++ b/packages/path_provider/path_provider_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider_ios/example/pubspec.yaml b/packages/path_provider/path_provider_ios/example/pubspec.yaml index 2166076db2b9..00ac1f1af3a7 100644 --- a/packages/path_provider/path_provider_ios/example/pubspec.yaml +++ b/packages/path_provider/path_provider_ios/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider_linux/example/pubspec.yaml b/packages/path_provider/path_provider_linux/example/pubspec.yaml index 252f3510a789..47ed4be220a6 100644 --- a/packages/path_provider/path_provider_linux/example/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: "none" environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index 16438a3870d1..46e248f81c18 100644 --- a/packages/path_provider/path_provider_linux/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.1.6 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/path_provider/path_provider_macos/example/pubspec.yaml b/packages/path_provider/path_provider_macos/example/pubspec.yaml index d8b93545ed53..42ed28b818d6 100644 --- a/packages/path_provider/path_provider_macos/example/pubspec.yaml +++ b/packages/path_provider/path_provider_macos/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider_macos/pubspec.yaml b/packages/path_provider/path_provider_macos/pubspec.yaml index 444165b86c3f..4381041079b5 100644 --- a/packages/path_provider/path_provider_macos/pubspec.yaml +++ b/packages/path_provider/path_provider_macos/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/path_provider/path_provider_platform_interface/pubspec.yaml b/packages/path_provider/path_provider_platform_interface/pubspec.yaml index 90b40ac7a3d4..92ec432dc394 100644 --- a/packages/path_provider/path_provider_platform_interface/pubspec.yaml +++ b/packages/path_provider/path_provider_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider_windows/example/pubspec.yaml b/packages/path_provider/path_provider_windows/example/pubspec.yaml index d943347df1ff..d48219648b30 100644 --- a/packages/path_provider/path_provider_windows/example/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider_windows/pubspec.yaml b/packages/path_provider/path_provider_windows/pubspec.yaml index 49afdd6293e7..f75dd058b36b 100644 --- a/packages/path_provider/path_provider_windows/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/quick_actions/quick_actions/example/pubspec.yaml b/packages/quick_actions/quick_actions/example/pubspec.yaml index c4ee86039761..64e61b71e720 100644 --- a/packages/quick_actions/quick_actions/example/pubspec.yaml +++ b/packages/quick_actions/quick_actions/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.9.1+hotfix.2" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml index aef2e248bade..c465b2aaf99b 100644 --- a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml +++ b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 1.0.2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences/example/pubspec.yaml b/packages/shared_preferences/shared_preferences/example/pubspec.yaml index 1cb0f185baf4..4ec5cbbb471f 100644 --- a/packages/shared_preferences/shared_preferences/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index 14b56fe69889..a1cea06f5a04 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -7,7 +7,7 @@ version: 2.0.15 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml index 060b94b3ae82..d23270ba386f 100644 --- a/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml index 4a00e6d23c0a..9f5f7124669d 100644 --- a/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml index d34973b9dde6..4d44d4e69f93 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml index 8f3ce1723bc9..922437256748 100644 --- a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.1.1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml index d6f07f8eb2af..b9dfb75c92e7 100644 --- a/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.12.8" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml index 615d0b05ba99..9259ef5888fa 100644 --- a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml b/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml index 8d775ab8b58c..43669d624f2d 100644 --- a/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml index 832ba912e5a8..656fdeb01876 100644 --- a/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.2.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_web/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/pubspec.yaml index 9ff76d27714c..c50958363d16 100644 --- a/packages/shared_preferences/shared_preferences_web/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml index 96762e933a9d..c7a0eb82cc07 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml index 99326cb24f18..57e086b81ed3 100644 --- a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.1.1 environment: sdk: '>=2.12.0 <3.0.0' - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/url_launcher/url_launcher/example/pubspec.yaml b/packages/url_launcher/url_launcher/example/pubspec.yaml index 3b2bba9833a3..43b5265c45ec 100644 --- a/packages/url_launcher/url_launcher/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 2cf75df6b0ef..319b6bfd3e0b 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -7,7 +7,7 @@ version: 6.1.2 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/url_launcher/url_launcher_android/example/pubspec.yaml b/packages/url_launcher/url_launcher_android/example/pubspec.yaml index 9af7b2876da9..cdb19458ba07 100644 --- a/packages/url_launcher/url_launcher_android/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_ios/example/pubspec.yaml b/packages/url_launcher/url_launcher_ios/example/pubspec.yaml index da4c72cd13bb..2e39e92d5638 100644 --- a/packages/url_launcher/url_launcher_ios/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_ios/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_linux/example/pubspec.yaml b/packages/url_launcher/url_launcher_linux/example/pubspec.yaml index 5e6c3fc5384f..90ea19dd2a04 100644 --- a/packages/url_launcher/url_launcher_linux/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_linux/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_linux/pubspec.yaml b/packages/url_launcher/url_launcher_linux/pubspec.yaml index c9472045e499..0bbd4b590cd2 100644 --- a/packages/url_launcher/url_launcher_linux/pubspec.yaml +++ b/packages/url_launcher/url_launcher_linux/pubspec.yaml @@ -6,7 +6,7 @@ version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/url_launcher/url_launcher_macos/example/pubspec.yaml b/packages/url_launcher/url_launcher_macos/example/pubspec.yaml index 9bc3062dd08f..2652df03448a 100644 --- a/packages/url_launcher/url_launcher_macos/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_macos/pubspec.yaml b/packages/url_launcher/url_launcher_macos/pubspec.yaml index edda6b67cfb3..8f93e57c9dc4 100644 --- a/packages/url_launcher/url_launcher_macos/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/pubspec.yaml @@ -6,7 +6,7 @@ version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml index cdbdfefba93a..140a1aee9938 100644 --- a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml +++ b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 2.0.5 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_web/example/pubspec.yaml b/packages/url_launcher/url_launcher_web/example/pubspec.yaml index bface463cfe2..a25f4ce148e9 100644 --- a/packages/url_launcher/url_launcher_web/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/example/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.2.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_windows/example/pubspec.yaml b/packages/url_launcher/url_launcher_windows/example/pubspec.yaml index 08350fdaab65..22b524df2488 100644 --- a/packages/url_launcher/url_launcher_windows/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml index c3f224e26adf..2717e3807e21 100644 --- a/packages/url_launcher/url_launcher_windows/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml @@ -6,7 +6,7 @@ version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/video_player/video_player_web/example/pubspec.yaml b/packages/video_player/video_player_web/example/pubspec.yaml index bc70b7d21aca..6fb2cd07ddf1 100644 --- a/packages/video_player/video_player_web/example/pubspec.yaml +++ b/packages/video_player/video_player_web/example/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.2.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 04fba273a2b6..36b89abd1f31 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.10 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/webview_flutter/webview_flutter/example/pubspec.yaml b/packages/webview_flutter/webview_flutter/example/pubspec.yaml index ae3b57e07a89..f1da7cd17b7e 100644 --- a/packages/webview_flutter/webview_flutter/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 9639b6abe76e..fc1e50f16e24 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -6,7 +6,7 @@ version: 3.0.4 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index d6cf2b2a1c17..04da12f765d4 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.8.8 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml index c339a0f4a2ce..b3a20f8d029d 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 1.9.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" dependencies: flutter: diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml index a834c9b77d51..6464e20fe37c 100644 --- a/packages/webview_flutter/webview_flutter_web/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.1.0+3 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index d85bf329a58e..9ce70bc7fdca 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.7.5 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" + flutter: ">=2.8.0" flutter: plugin: From a87aad1458a6d461c7a70d64fddafe3c925fdca8 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Thu, 19 May 2022 17:38:21 -0700 Subject: [PATCH 336/844] [google_maps_flutter_web] Remove custom analysis file. (#5791) --- .../google_maps_flutter_web/CHANGELOG.md | 8 + .../analysis_options.yaml | 1 - .../google_maps_controller_test.dart | 284 ++++++++++-------- .../google_maps_controller_test.mocks.dart | 11 +- .../google_maps_plugin_test.dart | 242 +++++++++------ .../google_maps_plugin_test.mocks.dart | 17 +- .../example/integration_test/marker_test.dart | 56 ++-- .../integration_test/markers_test.dart | 138 +++++---- .../integration_test/projection_test.dart | 26 +- .../resources/icon_image_base64.dart | 2 +- .../example/integration_test/shape_test.dart | 43 ++- .../example/integration_test/shapes_test.dart | 217 ++++++------- .../example/lib/main.dart | 7 +- .../example/pubspec.yaml | 14 +- .../lib/google_maps_flutter_web.dart | 29 +- .../lib/src/circle.dart | 8 +- .../lib/src/circles.dart | 46 ++- .../lib/src/convert.dart | 231 ++++++++------ .../lib/src/google_maps_controller.dart | 127 ++++---- .../lib/src/google_maps_flutter_web.dart | 24 +- .../lib/src/marker.dart | 24 +- .../lib/src/markers.dart | 57 ++-- .../lib/src/polygon.dart | 10 +- .../lib/src/polygons.dart | 48 +-- .../lib/src/polyline.dart | 10 +- .../lib/src/polylines.dart | 48 +-- .../lib/src/shims/dart_ui.dart | 2 +- .../lib/src/shims/dart_ui_fake.dart | 11 +- .../to_screen_location.dart | 18 +- .../lib/src/types.dart | 2 +- .../google_maps_flutter_web/pubspec.yaml | 5 +- script/configs/custom_analysis.yaml | 1 - 32 files changed, 977 insertions(+), 790 deletions(-) delete mode 100644 packages/google_maps_flutter/google_maps_flutter_web/analysis_options.yaml 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 f2fe971f4591..8bd3d40babbc 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,11 @@ +## 0.3.3 + +* Removes custom `analysis_options.yaml` (and fixes code to comply with newest rules). +* Updates `package:google_maps` dependency to latest (`^6.1.0`). +* Ensures that `convert.dart` sanitizes user-created HTML before passing it to the + Maps JS SDK with `sanitizeHtml` from `package:sanitize_html`. + [More info](https://pub.dev/documentation/sanitize_html/latest/sanitize_html/sanitizeHtml.html). + ## 0.3.2+2 * Removes unnecessary imports. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/analysis_options.yaml b/packages/google_maps_flutter/google_maps_flutter_web/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/google_maps_flutter/google_maps_flutter_web/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml 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 39aa641b10e4..17fdd81df645 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 @@ -18,9 +18,9 @@ import 'google_maps_controller_test.mocks.dart'; // This value is used when comparing long~num, like // LatLng values. -const _acceptableDelta = 0.0000000001; +const double _acceptableDelta = 0.0000000001; -@GenerateMocks([], customMocks: [ +@GenerateMocks([], customMocks: >[ MockSpec(returnNullOnMissingStub: true), MockSpec(returnNullOnMissingStub: true), MockSpec(returnNullOnMissingStub: true), @@ -32,9 +32,9 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('GoogleMapController', () { - final int mapId = 33930; + const int mapId = 33930; late GoogleMapController controller; - late StreamController stream; + late StreamController> stream; // Creates a controller with the default mapId and stream controller, and any `options` needed. GoogleMapController _createController({ @@ -59,7 +59,7 @@ void main() { } setUp(() { - stream = StreamController.broadcast(); + stream = StreamController>.broadcast(); }); group('construct/dispose', () { @@ -70,13 +70,13 @@ void main() { testWidgets('constructor creates widget', (WidgetTester tester) async { expect(controller.widget, isNotNull); expect(controller.widget, isA()); - expect((controller.widget as HtmlElementView).viewType, + expect((controller.widget! as HtmlElementView).viewType, endsWith('$mapId')); }); testWidgets('widget is cached when reused', (WidgetTester tester) async { - final first = controller.widget; - final again = controller.widget; + final Widget? first = controller.widget; + final Widget? again = controller.widget; expect(identical(first, again), isTrue); }); @@ -104,7 +104,7 @@ void main() { expect(() async { await controller.getScreenCoordinate( - LatLng(43.3072465, -5.6918241), + const LatLng(43.3072465, -5.6918241), ); }, throwsAssertionError); }); @@ -115,7 +115,7 @@ void main() { expect(() async { await controller.getLatLng( - ScreenCoordinate(x: 640, y: 480), + const ScreenCoordinate(x: 640, y: 480), ); }, throwsAssertionError); }); @@ -143,7 +143,12 @@ void main() { controller.dispose(); expect(() { - controller.updateCircles(CircleUpdates.from({}, {})); + controller.updateCircles( + CircleUpdates.from( + {}, + {}, + ), + ); }, throwsAssertionError); }); @@ -152,7 +157,12 @@ void main() { controller.dispose(); expect(() { - controller.updatePolygons(PolygonUpdates.from({}, {})); + controller.updatePolygons( + PolygonUpdates.from( + {}, + {}, + ), + ); }, throwsAssertionError); }); @@ -161,7 +171,12 @@ void main() { controller.dispose(); expect(() { - controller.updatePolylines(PolylineUpdates.from({}, {})); + controller.updatePolylines( + PolylineUpdates.from( + {}, + {}, + ), + ); }, throwsAssertionError); }); @@ -170,15 +185,20 @@ void main() { controller.dispose(); expect(() { - controller.updateMarkers(MarkerUpdates.from({}, {})); + controller.updateMarkers( + MarkerUpdates.from( + {}, + {}, + ), + ); }, throwsAssertionError); expect(() { - controller.showInfoWindow(MarkerId('any')); + controller.showInfoWindow(const MarkerId('any')); }, throwsAssertionError); expect(() { - controller.hideInfoWindow(MarkerId('any')); + controller.hideInfoWindow(const MarkerId('any')); }, throwsAssertionError); }); @@ -186,7 +206,7 @@ void main() { (WidgetTester tester) async { controller.dispose(); - expect(controller.isInfoWindowShown(MarkerId('any')), false); + expect(controller.isInfoWindowShown(const MarkerId('any')), false); }); }); }); @@ -219,16 +239,23 @@ void main() { controller.init(); // Trigger events on the map, and verify they've been broadcast to the stream - final capturedEvents = stream.stream.take(5); + final Stream> capturedEvents = stream.stream.take(5); gmaps.Event.trigger( - map, 'click', [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)]); - gmaps.Event.trigger(map, 'rightclick', - [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)]); - gmaps.Event.trigger(map, 'bounds_changed', []); // Causes 2 events - gmaps.Event.trigger(map, 'idle', []); + map, + 'click', + [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)], + ); + gmaps.Event.trigger( + map, + 'rightclick', + [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)], + ); + // The following line causes 2 events + gmaps.Event.trigger(map, 'bounds_changed', []); + gmaps.Event.trigger(map, 'idle', []); - final events = await capturedEvents.toList(); + final List> events = await capturedEvents.toList(); expect(events[0], isA()); expect(events[1], isA()); @@ -237,7 +264,7 @@ void main() { expect(events[4], isA()); }); - testWidgets('binds geometry controllers to map\'s', + testWidgets("binds geometry controllers to map's", (WidgetTester tester) async { controller = _createController(); controller.debugSetOverrides( @@ -257,44 +284,44 @@ void main() { }); testWidgets('renders initial geometry', (WidgetTester tester) async { - controller = _createController(circles: { - Circle( + controller = _createController(circles: { + const Circle( circleId: CircleId('circle-1'), zIndex: 1234, ), - }, markers: { - Marker( + }, markers: { + const Marker( markerId: MarkerId('marker-1'), infoWindow: InfoWindow( title: 'title for test', snippet: 'snippet for test', ), ), - }, polygons: { - Polygon(polygonId: PolygonId('polygon-1'), points: [ + }, polygons: { + const Polygon(polygonId: PolygonId('polygon-1'), points: [ LatLng(43.355114, -5.851333), LatLng(43.354797, -5.851860), LatLng(43.354469, -5.851318), LatLng(43.354762, -5.850824), ]), - Polygon( + const Polygon( polygonId: PolygonId('polygon-2-with-holes'), - points: [ + points: [ LatLng(43.355114, -5.851333), LatLng(43.354797, -5.851860), LatLng(43.354469, -5.851318), LatLng(43.354762, -5.850824), ], - holes: [ - [ + holes: >[ + [ LatLng(41.354797, -6.851860), LatLng(41.354469, -6.851318), LatLng(41.354762, -6.850824), ] ], ), - }, polylines: { - Polyline(polylineId: PolylineId('polyline-1'), points: [ + }, polylines: { + const Polyline(polylineId: PolylineId('polyline-1'), points: [ LatLng(43.355114, -5.851333), LatLng(43.354797, -5.851860), LatLng(43.354469, -5.851318), @@ -311,14 +338,16 @@ void main() { controller.init(); - final capturedCircles = + final Set capturedCircles = verify(circles.addCircles(captureAny)).captured[0] as Set; - final capturedMarkers = + final Set capturedMarkers = verify(markers.addMarkers(captureAny)).captured[0] as Set; - final capturedPolygons = verify(polygons.addPolygons(captureAny)) - .captured[0] as Set; - final capturedPolylines = verify(polylines.addPolylines(captureAny)) - .captured[0] as Set; + final Set capturedPolygons = + verify(polygons.addPolygons(captureAny)).captured[0] + as Set; + final Set capturedPolylines = + verify(polylines.addPolylines(captureAny)).captured[0] + as Set; expect(capturedCircles.first.circleId.value, 'circle-1'); expect(capturedCircles.first.zIndex, 1234); @@ -334,8 +363,8 @@ void main() { testWidgets('empty infoWindow does not create InfoWindow instance.', (WidgetTester tester) async { - controller = _createController(markers: { - Marker(markerId: MarkerId('marker-1')), + controller = _createController(markers: { + const Marker(markerId: MarkerId('marker-1')), }); controller.debugSetOverrides( @@ -344,7 +373,7 @@ void main() { controller.init(); - final capturedMarkers = + final Set capturedMarkers = verify(markers.addMarkers(captureAny)).captured[0] as Set; expect(capturedMarkers.first.infoWindow, InfoWindow.noText); @@ -356,11 +385,12 @@ void main() { capturedOptions = null; }); testWidgets('translates initial options', (WidgetTester tester) async { - controller = _createController(options: { + controller = _createController(options: { 'mapType': 2, 'zoomControlsEnabled': true, }); - controller.debugSetOverrides(createMap: (_, options) { + controller.debugSetOverrides( + createMap: (_, gmaps.MapOptions options) { capturedOptions = options; return map; }); @@ -377,10 +407,11 @@ void main() { testWidgets('disables gestureHandling with scrollGesturesEnabled false', (WidgetTester tester) async { - controller = _createController(options: { + controller = _createController(options: { 'scrollGesturesEnabled': false, }); - controller.debugSetOverrides(createMap: (_, options) { + controller.debugSetOverrides( + createMap: (_, gmaps.MapOptions options) { capturedOptions = options; return map; }); @@ -395,10 +426,11 @@ void main() { testWidgets('disables gestureHandling with zoomGesturesEnabled false', (WidgetTester tester) async { - controller = _createController(options: { + controller = _createController(options: { 'zoomGesturesEnabled': false, }); - controller.debugSetOverrides(createMap: (_, options) { + controller.debugSetOverrides( + createMap: (_, gmaps.MapOptions options) { capturedOptions = options; return map; }); @@ -414,7 +446,7 @@ void main() { testWidgets('sets initial position when passed', (WidgetTester tester) async { controller = _createController( - initialCameraPosition: CameraPosition( + initialCameraPosition: const CameraPosition( target: LatLng(43.308, -5.6910), zoom: 12, bearing: 0, @@ -422,7 +454,8 @@ void main() { ), ); - controller.debugSetOverrides(createMap: (_, options) { + controller.debugSetOverrides( + createMap: (_, gmaps.MapOptions options) { capturedOptions = options; return map; }); @@ -444,7 +477,7 @@ void main() { testWidgets('initializes with traffic layer', (WidgetTester tester) async { - controller = _createController(options: { + controller = _createController(options: { 'trafficEnabled': true, }); controller.debugSetOverrides(createMap: (_, __) => map); @@ -472,7 +505,7 @@ void main() { group('updateRawOptions', () { testWidgets('can update `options`', (WidgetTester tester) async { - controller.updateRawOptions({ + controller.updateRawOptions({ 'mapType': 2, }); @@ -482,13 +515,13 @@ void main() { testWidgets('can turn on/off traffic', (WidgetTester tester) async { expect(controller.trafficLayer, isNull); - controller.updateRawOptions({ + controller.updateRawOptions({ 'trafficEnabled': true, }); expect(controller.trafficLayer, isNotNull); - controller.updateRawOptions({ + controller.updateRawOptions({ 'trafficEnabled': false, }); @@ -498,11 +531,11 @@ void main() { group('viewport getters', () { testWidgets('getVisibleRegion', (WidgetTester tester) async { - final gmCenter = map.center!; - final center = + final gmaps.LatLng gmCenter = map.center!; + final LatLng center = LatLng(gmCenter.lat.toDouble(), gmCenter.lng.toDouble()); - final bounds = await controller.getVisibleRegion(); + final LatLngBounds bounds = await controller.getVisibleRegion(); expect(bounds.contains(center), isTrue, reason: @@ -516,10 +549,14 @@ void main() { group('moveCamera', () { testWidgets('newLatLngZoom', (WidgetTester tester) async { - await (controller - .moveCamera(CameraUpdate.newLatLngZoom(LatLng(19, 26), 12))); + await controller.moveCamera( + CameraUpdate.newLatLngZoom( + const LatLng(19, 26), + 12, + ), + ); - final gmCenter = map.center!; + final gmaps.LatLng gmCenter = map.center!; expect(map.zoom, 12); expect(gmCenter.lat, closeTo(19, _acceptableDelta)); @@ -528,10 +565,7 @@ void main() { }); group('map.projection methods', () { - // These are too much for dart mockito, can't mock: - // map.projection.method() (in Javascript ;) ) - - // Caused https://github.com/flutter/flutter/issues/67606 + // Tested in projection_test.dart }); }); @@ -542,116 +576,122 @@ void main() { }); testWidgets('updateCircles', (WidgetTester tester) async { - final mock = MockCirclesController(); + final MockCirclesController mock = MockCirclesController(); controller.debugSetOverrides(circles: mock); - final previous = { - Circle(circleId: CircleId('to-be-updated')), - Circle(circleId: CircleId('to-be-removed')), + final Set previous = { + const Circle(circleId: CircleId('to-be-updated')), + const Circle(circleId: CircleId('to-be-removed')), }; - final current = { - Circle(circleId: CircleId('to-be-updated'), visible: false), - Circle(circleId: CircleId('to-be-added')), + final Set current = { + const Circle(circleId: CircleId('to-be-updated'), visible: false), + const Circle(circleId: CircleId('to-be-added')), }; controller.updateCircles(CircleUpdates.from(previous, current)); - verify(mock.removeCircles({ - CircleId('to-be-removed'), + verify(mock.removeCircles({ + const CircleId('to-be-removed'), })); - verify(mock.addCircles({ - Circle(circleId: CircleId('to-be-added')), + verify(mock.addCircles({ + const Circle(circleId: CircleId('to-be-added')), })); - verify(mock.changeCircles({ - Circle(circleId: CircleId('to-be-updated'), visible: false), + verify(mock.changeCircles({ + const Circle(circleId: CircleId('to-be-updated'), visible: false), })); }); testWidgets('updateMarkers', (WidgetTester tester) async { - final mock = MockMarkersController(); + final MockMarkersController mock = MockMarkersController(); controller.debugSetOverrides(markers: mock); - final previous = { - Marker(markerId: MarkerId('to-be-updated')), - Marker(markerId: MarkerId('to-be-removed')), + final Set previous = { + const Marker(markerId: MarkerId('to-be-updated')), + const Marker(markerId: MarkerId('to-be-removed')), }; - final current = { - Marker(markerId: MarkerId('to-be-updated'), visible: false), - Marker(markerId: MarkerId('to-be-added')), + final Set current = { + const Marker(markerId: MarkerId('to-be-updated'), visible: false), + const Marker(markerId: MarkerId('to-be-added')), }; controller.updateMarkers(MarkerUpdates.from(previous, current)); - verify(mock.removeMarkers({ - MarkerId('to-be-removed'), + verify(mock.removeMarkers({ + const MarkerId('to-be-removed'), })); - verify(mock.addMarkers({ - Marker(markerId: MarkerId('to-be-added')), + verify(mock.addMarkers({ + const Marker(markerId: MarkerId('to-be-added')), })); - verify(mock.changeMarkers({ - Marker(markerId: MarkerId('to-be-updated'), visible: false), + verify(mock.changeMarkers({ + const Marker(markerId: MarkerId('to-be-updated'), visible: false), })); }); testWidgets('updatePolygons', (WidgetTester tester) async { - final mock = MockPolygonsController(); + final MockPolygonsController mock = MockPolygonsController(); controller.debugSetOverrides(polygons: mock); - final previous = { - Polygon(polygonId: PolygonId('to-be-updated')), - Polygon(polygonId: PolygonId('to-be-removed')), + final Set previous = { + const Polygon(polygonId: PolygonId('to-be-updated')), + const Polygon(polygonId: PolygonId('to-be-removed')), }; - final current = { - Polygon(polygonId: PolygonId('to-be-updated'), visible: false), - Polygon(polygonId: PolygonId('to-be-added')), + final Set current = { + const Polygon(polygonId: PolygonId('to-be-updated'), visible: false), + const Polygon(polygonId: PolygonId('to-be-added')), }; controller.updatePolygons(PolygonUpdates.from(previous, current)); - verify(mock.removePolygons({ - PolygonId('to-be-removed'), + verify(mock.removePolygons({ + const PolygonId('to-be-removed'), })); - verify(mock.addPolygons({ - Polygon(polygonId: PolygonId('to-be-added')), + verify(mock.addPolygons({ + const Polygon(polygonId: PolygonId('to-be-added')), })); - verify(mock.changePolygons({ - Polygon(polygonId: PolygonId('to-be-updated'), visible: false), + verify(mock.changePolygons({ + const Polygon(polygonId: PolygonId('to-be-updated'), visible: false), })); }); testWidgets('updatePolylines', (WidgetTester tester) async { - final mock = MockPolylinesController(); + final MockPolylinesController mock = MockPolylinesController(); controller.debugSetOverrides(polylines: mock); - final previous = { - Polyline(polylineId: PolylineId('to-be-updated')), - Polyline(polylineId: PolylineId('to-be-removed')), + final Set previous = { + const Polyline(polylineId: PolylineId('to-be-updated')), + const Polyline(polylineId: PolylineId('to-be-removed')), }; - final current = { - Polyline(polylineId: PolylineId('to-be-updated'), visible: false), - Polyline(polylineId: PolylineId('to-be-added')), + final Set current = { + const Polyline( + polylineId: PolylineId('to-be-updated'), + visible: false, + ), + const Polyline(polylineId: PolylineId('to-be-added')), }; controller.updatePolylines(PolylineUpdates.from(previous, current)); - verify(mock.removePolylines({ - PolylineId('to-be-removed'), + verify(mock.removePolylines({ + const PolylineId('to-be-removed'), })); - verify(mock.addPolylines({ - Polyline(polylineId: PolylineId('to-be-added')), + verify(mock.addPolylines({ + const Polyline(polylineId: PolylineId('to-be-added')), })); - verify(mock.changePolylines({ - Polyline(polylineId: PolylineId('to-be-updated'), visible: false), + verify(mock.changePolylines({ + const Polyline( + polylineId: PolylineId('to-be-updated'), + visible: false, + ), })); }); testWidgets('infoWindow visibility', (WidgetTester tester) async { - final mock = MockMarkersController(); - final markerId = MarkerId('marker-with-infowindow'); + final MockMarkersController mock = MockMarkersController(); + const MarkerId markerId = MarkerId('marker-with-infowindow'); when(mock.isInfoWindowShown(markerId)).thenReturn(true); controller.debugSetOverrides(markers: mock); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart index 530707c6c328..9565935bd8ed 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.0.16 from annotations +// Mocks generated by Mockito 5.2.0 from annotations // in google_maps_flutter_web_integration_tests/integration_test/google_maps_controller_test.dart. // Do not manually edit this file. @@ -8,6 +8,7 @@ import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platf import 'package:google_maps_flutter_web/google_maps_flutter_web.dart' as _i3; import 'package:mockito/mockito.dart' as _i1; +// ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references @@ -58,8 +59,6 @@ class MockCirclesController extends _i1.Mock implements _i3.CirclesController { void bindToMap(int? mapId, _i2.GMap? googleMap) => super.noSuchMethod(Invocation.method(#bindToMap, [mapId, googleMap]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [PolygonsController]. @@ -102,8 +101,6 @@ class MockPolygonsController extends _i1.Mock void bindToMap(int? mapId, _i2.GMap? googleMap) => super.noSuchMethod(Invocation.method(#bindToMap, [mapId, googleMap]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [PolylinesController]. @@ -146,8 +143,6 @@ class MockPolylinesController extends _i1.Mock void bindToMap(int? mapId, _i2.GMap? googleMap) => super.noSuchMethod(Invocation.method(#bindToMap, [mapId, googleMap]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } /// A class which mocks [MarkersController]. @@ -201,6 +196,4 @@ class MockMarkersController extends _i1.Mock implements _i3.MarkersController { void bindToMap(int? mapId, _i2.GMap? googleMap) => super.noSuchMethod(Invocation.method(#bindToMap, [mapId, googleMap]), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart index a3cf86e593fe..f0fd5a232e00 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart @@ -5,19 +5,18 @@ import 'dart:async'; import 'dart:js_util' show getProperty; -import 'package:integration_test/integration_test.dart'; 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'; -import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; -import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; - import 'google_maps_plugin_test.mocks.dart'; -@GenerateMocks([], customMocks: [ +@GenerateMocks([], customMocks: >[ MockSpec(returnNullOnMissingStub: true), ]) @@ -51,7 +50,7 @@ void main() { group('after buildWidget', () { setUp(() { - plugin.debugSetMapById({0: controller}); + plugin.debugSetMapById({0: controller}); }); testWidgets('cannot call methods after dispose', @@ -69,13 +68,15 @@ void main() { }); group('buildView', () { - final testMapId = 33930; - final initialCameraPosition = CameraPosition(target: LatLng(0, 0)); + const int testMapId = 33930; + const CameraPosition initialCameraPosition = + CameraPosition(target: LatLng(0, 0)); testWidgets( 'returns an HtmlElementView and caches the controller for later', (WidgetTester tester) async { - final Map cache = {}; + final Map cache = + {}; plugin.debugSetMapById(cache); final Widget widget = plugin.buildView( @@ -106,11 +107,14 @@ void main() { testWidgets('returns cached instance if it already exists', (WidgetTester tester) async { - final expected = HtmlElementView(viewType: 'only-for-testing'); + const HtmlElementView expected = + HtmlElementView(viewType: 'only-for-testing'); when(controller.widget).thenReturn(expected); - plugin.debugSetMapById({testMapId: controller}); + plugin.debugSetMapById({ + testMapId: controller, + }); - final widget = plugin.buildView( + final Widget widget = plugin.buildView( testMapId, onPlatformViewCreated, initialCameraPosition: initialCameraPosition, @@ -122,7 +126,8 @@ void main() { testWidgets( 'asynchronously reports onPlatformViewCreated the first time it happens', (WidgetTester tester) async { - final Map cache = {}; + final Map cache = + {}; plugin.debugSetMapById(cache); plugin.buildView( @@ -157,47 +162,53 @@ void main() { }); group('setMapStyles', () { - String mapStyle = '''[{ - "featureType": "poi.park", - "elementType": "labels.text.fill", - "stylers": [{"color": "#6b9a76"}] - }]'''; + const String mapStyle = ''' +[{ + "featureType": "poi.park", + "elementType": "labels.text.fill", + "stylers": [{"color": "#6b9a76"}] +}]'''; testWidgets('translates styles for controller', (WidgetTester tester) async { - plugin.debugSetMapById({0: controller}); + plugin.debugSetMapById({0: controller}); await plugin.setMapStyle(mapStyle, mapId: 0); - var captured = + final dynamic captured = verify(controller.updateRawOptions(captureThat(isMap))).captured[0]; expect(captured, contains('styles')); - var styles = captured['styles']; + final List styles = + captured['styles'] as List; expect(styles.length, 1); // Let's peek inside the styles... - var style = styles[0] as gmaps.MapTypeStyle; + final gmaps.MapTypeStyle style = styles[0]; expect(style.featureType, 'poi.park'); expect(style.elementType, 'labels.text.fill'); expect(style.stylers?.length, 1); - expect(getProperty(style.stylers![0]!, 'color'), '#6b9a76'); + expect(getProperty(style.stylers![0]!, 'color'), '#6b9a76'); }); }); group('Noop methods:', () { - int mapId = 0; + const int mapId = 0; setUp(() { - plugin.debugSetMapById({mapId: controller}); + plugin.debugSetMapById({mapId: controller}); }); // Options testWidgets('updateTileOverlays', (WidgetTester tester) async { - final update = - plugin.updateTileOverlays(mapId: mapId, newTileOverlays: {}); + final Future update = plugin.updateTileOverlays( + mapId: mapId, + newTileOverlays: {}, + ); expect(update, completion(null)); }); testWidgets('updateTileOverlays', (WidgetTester tester) async { - final update = - plugin.clearTileCache(TileOverlayId('any'), mapId: mapId); + final Future update = plugin.clearTileCache( + const TileOverlayId('any'), + mapId: mapId, + ); expect(update, completion(null)); }); }); @@ -205,13 +216,15 @@ void main() { // These methods only pass-through values from the plugin to the controller // so we verify them all together here... group('Pass-through methods:', () { - int mapId = 0; + const int mapId = 0; setUp(() { - plugin.debugSetMapById({mapId: controller}); + plugin.debugSetMapById({mapId: controller}); }); // Options testWidgets('updateMapOptions', (WidgetTester tester) async { - final expectedMapOptions = {'someOption': 12345}; + final Map expectedMapOptions = { + 'someOption': 12345 + }; await plugin.updateMapOptions(expectedMapOptions, mapId: mapId); @@ -219,28 +232,40 @@ void main() { }); // Geometry testWidgets('updateMarkers', (WidgetTester tester) async { - final expectedUpdates = MarkerUpdates.from({}, {}); + final MarkerUpdates expectedUpdates = MarkerUpdates.from( + {}, + {}, + ); await plugin.updateMarkers(expectedUpdates, mapId: mapId); verify(controller.updateMarkers(expectedUpdates)); }); testWidgets('updatePolygons', (WidgetTester tester) async { - final expectedUpdates = PolygonUpdates.from({}, {}); + final PolygonUpdates expectedUpdates = PolygonUpdates.from( + {}, + {}, + ); await plugin.updatePolygons(expectedUpdates, mapId: mapId); verify(controller.updatePolygons(expectedUpdates)); }); testWidgets('updatePolylines', (WidgetTester tester) async { - final expectedUpdates = PolylineUpdates.from({}, {}); + final PolylineUpdates expectedUpdates = PolylineUpdates.from( + {}, + {}, + ); await plugin.updatePolylines(expectedUpdates, mapId: mapId); verify(controller.updatePolylines(expectedUpdates)); }); testWidgets('updateCircles', (WidgetTester tester) async { - final expectedUpdates = CircleUpdates.from({}, {}); + final CircleUpdates expectedUpdates = CircleUpdates.from( + {}, + {}, + ); await plugin.updateCircles(expectedUpdates, mapId: mapId); @@ -248,16 +273,18 @@ void main() { }); // Camera testWidgets('animateCamera', (WidgetTester tester) async { - final expectedUpdates = - CameraUpdate.newLatLng(LatLng(43.3626, -5.8433)); + final CameraUpdate expectedUpdates = CameraUpdate.newLatLng( + const LatLng(43.3626, -5.8433), + ); await plugin.animateCamera(expectedUpdates, mapId: mapId); verify(controller.moveCamera(expectedUpdates)); }); testWidgets('moveCamera', (WidgetTester tester) async { - final expectedUpdates = - CameraUpdate.newLatLng(LatLng(43.3628, -5.8478)); + final CameraUpdate expectedUpdates = CameraUpdate.newLatLng( + const LatLng(43.3628, -5.8478), + ); await plugin.moveCamera(expectedUpdates, mapId: mapId); @@ -268,8 +295,8 @@ void main() { testWidgets('getVisibleRegion', (WidgetTester tester) async { when(controller.getVisibleRegion()) .thenAnswer((_) async => LatLngBounds( - northeast: LatLng(47.2359634, -68.0192019), - southwest: LatLng(34.5019594, -120.4974629), + northeast: const LatLng(47.2359634, -68.0192019), + southwest: const LatLng(34.5019594, -120.4974629), )); await plugin.getVisibleRegion(mapId: mapId); @@ -285,10 +312,10 @@ void main() { testWidgets('getScreenCoordinate', (WidgetTester tester) async { when(controller.getScreenCoordinate(any)).thenAnswer( - (_) async => ScreenCoordinate(x: 320, y: 240) // fake return + (_) async => const ScreenCoordinate(x: 320, y: 240) // fake return ); - final latLng = LatLng(43.3613, -5.8499); + const LatLng latLng = LatLng(43.3613, -5.8499); await plugin.getScreenCoordinate(latLng, mapId: mapId); @@ -296,11 +323,11 @@ void main() { }); testWidgets('getLatLng', (WidgetTester tester) async { - when(controller.getLatLng(any)) - .thenAnswer((_) async => LatLng(43.3613, -5.8499) // fake return - ); + when(controller.getLatLng(any)).thenAnswer( + (_) async => const LatLng(43.3613, -5.8499) // fake return + ); - final coordinates = ScreenCoordinate(x: 19, y: 26); + const ScreenCoordinate coordinates = ScreenCoordinate(x: 19, y: 26); await plugin.getLatLng(coordinates, mapId: mapId); @@ -309,7 +336,7 @@ void main() { // InfoWindows testWidgets('showMarkerInfoWindow', (WidgetTester tester) async { - final markerId = MarkerId('testing-123'); + const MarkerId markerId = MarkerId('testing-123'); await plugin.showMarkerInfoWindow(markerId, mapId: mapId); @@ -317,7 +344,7 @@ void main() { }); testWidgets('hideMarkerInfoWindow', (WidgetTester tester) async { - final markerId = MarkerId('testing-123'); + const MarkerId markerId = MarkerId('testing-123'); await plugin.hideMarkerInfoWindow(markerId, mapId: mapId); @@ -327,7 +354,7 @@ void main() { testWidgets('isMarkerInfoWindowShown', (WidgetTester tester) async { when(controller.isInfoWindowShown(any)).thenReturn(true); - final markerId = MarkerId('testing-123'); + const MarkerId markerId = MarkerId('testing-123'); await plugin.isMarkerInfoWindowShown(markerId, mapId: mapId); @@ -337,18 +364,18 @@ void main() { // Verify all event streams are filtered correctly from the main one... group('Event Streams', () { - int mapId = 0; - late StreamController streamController; + const int mapId = 0; + late StreamController> streamController; setUp(() { - streamController = StreamController.broadcast(); + streamController = StreamController>.broadcast(); when(controller.events) - .thenAnswer((realInvocation) => streamController.stream); - plugin.debugSetMapById({mapId: controller}); + .thenAnswer((Invocation realInvocation) => streamController.stream); + plugin.debugSetMapById({mapId: controller}); }); // Dispatches a few events in the global streamController, and expects *only* the passed event to be there. Future _testStreamFiltering( - Stream stream, MapEvent event) async { + Stream> stream, MapEvent event) async { Timer.run(() { streamController.add(_OtherMapEvent(mapId)); streamController.add(event); @@ -356,7 +383,7 @@ void main() { streamController.close(); }); - final events = await stream.toList(); + final List> events = await stream.toList(); expect(events.length, 1); expect(events[0], event); @@ -364,113 +391,144 @@ void main() { // Camera events testWidgets('onCameraMoveStarted', (WidgetTester tester) async { - final event = CameraMoveStartedEvent(mapId); + final CameraMoveStartedEvent event = CameraMoveStartedEvent(mapId); - final stream = plugin.onCameraMoveStarted(mapId: mapId); + final Stream stream = + plugin.onCameraMoveStarted(mapId: mapId); await _testStreamFiltering(stream, event); }); testWidgets('onCameraMoveStarted', (WidgetTester tester) async { - final event = CameraMoveEvent( + final CameraMoveEvent event = CameraMoveEvent( mapId, - CameraPosition( + const CameraPosition( target: LatLng(43.3790, -5.8660), ), ); - final stream = plugin.onCameraMove(mapId: mapId); + final Stream stream = + plugin.onCameraMove(mapId: mapId); await _testStreamFiltering(stream, event); }); testWidgets('onCameraIdle', (WidgetTester tester) async { - final event = CameraIdleEvent(mapId); + final CameraIdleEvent event = CameraIdleEvent(mapId); - final stream = plugin.onCameraIdle(mapId: mapId); + final Stream stream = + plugin.onCameraIdle(mapId: mapId); await _testStreamFiltering(stream, event); }); // Marker events testWidgets('onMarkerTap', (WidgetTester tester) async { - final event = MarkerTapEvent(mapId, MarkerId('test-123')); + final MarkerTapEvent event = MarkerTapEvent( + mapId, + const MarkerId('test-123'), + ); - final stream = plugin.onMarkerTap(mapId: mapId); + final Stream stream = plugin.onMarkerTap(mapId: mapId); await _testStreamFiltering(stream, event); }); testWidgets('onInfoWindowTap', (WidgetTester tester) async { - final event = InfoWindowTapEvent(mapId, MarkerId('test-123')); + final InfoWindowTapEvent event = InfoWindowTapEvent( + mapId, + const MarkerId('test-123'), + ); - final stream = plugin.onInfoWindowTap(mapId: mapId); + final Stream stream = + plugin.onInfoWindowTap(mapId: mapId); await _testStreamFiltering(stream, event); }); testWidgets('onMarkerDragStart', (WidgetTester tester) async { - final event = MarkerDragStartEvent( + final MarkerDragStartEvent event = MarkerDragStartEvent( mapId, - LatLng(43.3677, -5.8372), - MarkerId('test-123'), + const LatLng(43.3677, -5.8372), + const MarkerId('test-123'), ); - final stream = plugin.onMarkerDragStart(mapId: mapId); + final Stream stream = + plugin.onMarkerDragStart(mapId: mapId); await _testStreamFiltering(stream, event); }); testWidgets('onMarkerDrag', (WidgetTester tester) async { - final event = MarkerDragEvent( + final MarkerDragEvent event = MarkerDragEvent( mapId, - LatLng(43.3677, -5.8372), - MarkerId('test-123'), + const LatLng(43.3677, -5.8372), + const MarkerId('test-123'), ); - final stream = plugin.onMarkerDrag(mapId: mapId); + final Stream stream = + plugin.onMarkerDrag(mapId: mapId); await _testStreamFiltering(stream, event); }); testWidgets('onMarkerDragEnd', (WidgetTester tester) async { - final event = MarkerDragEndEvent( + final MarkerDragEndEvent event = MarkerDragEndEvent( mapId, - LatLng(43.3677, -5.8372), - MarkerId('test-123'), + const LatLng(43.3677, -5.8372), + const MarkerId('test-123'), ); - final stream = plugin.onMarkerDragEnd(mapId: mapId); + final Stream stream = + plugin.onMarkerDragEnd(mapId: mapId); await _testStreamFiltering(stream, event); }); // Geometry testWidgets('onPolygonTap', (WidgetTester tester) async { - final event = PolygonTapEvent(mapId, PolygonId('test-123')); + final PolygonTapEvent event = PolygonTapEvent( + mapId, + const PolygonId('test-123'), + ); - final stream = plugin.onPolygonTap(mapId: mapId); + final Stream stream = + plugin.onPolygonTap(mapId: mapId); await _testStreamFiltering(stream, event); }); testWidgets('onPolylineTap', (WidgetTester tester) async { - final event = PolylineTapEvent(mapId, PolylineId('test-123')); + final PolylineTapEvent event = PolylineTapEvent( + mapId, + const PolylineId('test-123'), + ); - final stream = plugin.onPolylineTap(mapId: mapId); + final Stream stream = + plugin.onPolylineTap(mapId: mapId); await _testStreamFiltering(stream, event); }); testWidgets('onCircleTap', (WidgetTester tester) async { - final event = CircleTapEvent(mapId, CircleId('test-123')); + final CircleTapEvent event = CircleTapEvent( + mapId, + const CircleId('test-123'), + ); - final stream = plugin.onCircleTap(mapId: mapId); + final Stream stream = plugin.onCircleTap(mapId: mapId); await _testStreamFiltering(stream, event); }); // Map taps testWidgets('onTap', (WidgetTester tester) async { - final event = MapTapEvent(mapId, LatLng(43.3597, -5.8458)); + final MapTapEvent event = MapTapEvent( + mapId, + const LatLng(43.3597, -5.8458), + ); - final stream = plugin.onTap(mapId: mapId); + final Stream stream = plugin.onTap(mapId: mapId); await _testStreamFiltering(stream, event); }); testWidgets('onLongPress', (WidgetTester tester) async { - final event = MapLongPressEvent(mapId, LatLng(43.3608, -5.8425)); + final MapLongPressEvent event = MapLongPressEvent( + mapId, + const LatLng(43.3608, -5.8425), + ); - final stream = plugin.onLongPress(mapId: mapId); + final Stream stream = + plugin.onLongPress(mapId: mapId); await _testStreamFiltering(stream, event); }); @@ -478,6 +536,6 @@ void main() { }); } -class _OtherMapEvent extends MapEvent { +class _OtherMapEvent extends MapEvent { _OtherMapEvent(int mapId) : super(mapId, null); } 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 d2df11c6ffa9..bbc92ffc6096 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 @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.0.16 from annotations +// Mocks generated by Mockito 5.2.0 from annotations // in google_maps_flutter_web_integration_tests/integration_test/google_maps_plugin_test.dart. // Do not manually edit this file. @@ -9,6 +9,7 @@ import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platf import 'package:google_maps_flutter_web/google_maps_flutter_web.dart' as _i4; import 'package:mockito/mockito.dart' as _i1; +// ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references @@ -34,15 +35,15 @@ class _FakeLatLng_3 extends _i1.Fake implements _i3.LatLng {} class MockGoogleMapController extends _i1.Mock implements _i4.GoogleMapController { @override - _i2.StreamController<_i3.MapEvent> get stream => + _i2.StreamController<_i3.MapEvent> get stream => (super.noSuchMethod(Invocation.getter(#stream), - returnValue: _FakeStreamController_0<_i3.MapEvent>()) - as _i2.StreamController<_i3.MapEvent>); + returnValue: _FakeStreamController_0<_i3.MapEvent>()) + as _i2.StreamController<_i3.MapEvent>); @override - _i2.Stream<_i3.MapEvent> get events => + _i2.Stream<_i3.MapEvent> get events => (super.noSuchMethod(Invocation.getter(#events), - returnValue: Stream<_i3.MapEvent>.empty()) - as _i2.Stream<_i3.MapEvent>); + returnValue: Stream<_i3.MapEvent>.empty()) + as _i2.Stream<_i3.MapEvent>); @override bool get isInitialized => (super.noSuchMethod(Invocation.getter(#isInitialized), returnValue: false) @@ -126,6 +127,4 @@ class MockGoogleMapController extends _i1.Mock @override void dispose() => super.noSuchMethod(Invocation.method(#dispose, []), returnValueForMissingStub: null); - @override - String toString() => super.toString(); } 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 cfa36febbbfe..e07ade03bba3 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 @@ -5,10 +5,10 @@ import 'dart:async'; import 'dart:html' as html; -import 'package:integration_test/integration_test.dart'; +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'; -import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; /// Test Markers void main() { @@ -40,7 +40,7 @@ void main() { } setUp(() { - _methodCalledCompleter = Completer(); + _methodCalledCompleter = Completer(); methodCalled = _methodCalledCompleter.future; }); @@ -55,7 +55,7 @@ void main() { MarkerController(marker: marker, onTap: onTap); // Trigger a click event... - gmaps.Event.trigger(marker, 'click', [gmaps.MapMouseEvent()]); + gmaps.Event.trigger(marker, 'click', [gmaps.MapMouseEvent()]); // The event handling is now truly async. Wait for it... expect(await methodCalled, isTrue); @@ -66,7 +66,7 @@ void main() { // Trigger a drag end event... gmaps.Event.trigger(marker, 'dragstart', - [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)]); + [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)]); expect(await methodCalled, isTrue); }); @@ -76,7 +76,10 @@ void main() { // Trigger a drag end event... gmaps.Event.trigger( - marker, 'drag', [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)]); + marker, + 'drag', + [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)], + ); expect(await methodCalled, isTrue); }); @@ -85,15 +88,19 @@ void main() { MarkerController(marker: marker, onDragEnd: onDragEnd); // Trigger a drag end event... - gmaps.Event.trigger(marker, 'dragend', - [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)]); + gmaps.Event.trigger( + marker, + 'dragend', + [gmaps.MapMouseEvent()..latLng = gmaps.LatLng(0, 0)], + ); expect(await methodCalled, isTrue); }); testWidgets('update', (WidgetTester tester) async { - final controller = MarkerController(marker: marker); - final options = gmaps.MarkerOptions()..draggable = true; + final MarkerController controller = MarkerController(marker: marker); + final gmaps.MarkerOptions options = gmaps.MarkerOptions() + ..draggable = true; expect(marker.draggable, isNull); @@ -104,7 +111,7 @@ void main() { testWidgets('infoWindow null, showInfoWindow.', (WidgetTester tester) async { - final controller = MarkerController(marker: marker); + final MarkerController controller = MarkerController(marker: marker); controller.showInfoWindow(); @@ -112,11 +119,13 @@ void main() { }); testWidgets('showInfoWindow', (WidgetTester tester) async { - final infoWindow = gmaps.InfoWindow(); - final map = gmaps.GMap(html.DivElement()); + final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); + final gmaps.GMap map = gmaps.GMap(html.DivElement()); marker.set('map', map); - final controller = - MarkerController(marker: marker, infoWindow: infoWindow); + final MarkerController controller = MarkerController( + marker: marker, + infoWindow: infoWindow, + ); controller.showInfoWindow(); @@ -125,11 +134,13 @@ void main() { }); testWidgets('hideInfoWindow', (WidgetTester tester) async { - final infoWindow = gmaps.InfoWindow(); - final map = gmaps.GMap(html.DivElement()); + final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); + final gmaps.GMap map = gmaps.GMap(html.DivElement()); marker.set('map', map); - final controller = - MarkerController(marker: marker, infoWindow: infoWindow); + final MarkerController controller = MarkerController( + marker: marker, + infoWindow: infoWindow, + ); controller.hideInfoWindow(); @@ -141,8 +152,8 @@ void main() { late MarkerController controller; setUp(() { - final infoWindow = gmaps.InfoWindow(); - final map = gmaps.GMap(html.DivElement()); + final gmaps.InfoWindow infoWindow = gmaps.InfoWindow(); + final gmaps.GMap map = gmaps.GMap(html.DivElement()); marker.set('map', map); controller = MarkerController(marker: marker, infoWindow: infoWindow); }); @@ -155,7 +166,8 @@ void main() { testWidgets('cannot call update after remove', (WidgetTester tester) async { - final options = gmaps.MarkerOptions()..draggable = true; + final gmaps.MarkerOptions options = gmaps.MarkerOptions() + ..draggable = true; controller.remove(); 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 6f2bf610f77d..90195ec6397b 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 @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:html' as html; import 'dart:js_util' show getProperty; +import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps/google_maps.dart' as gmaps; @@ -20,54 +21,56 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('MarkersController', () { - late StreamController events; + late StreamController> events; late MarkersController controller; late gmaps.GMap map; setUp(() { - events = StreamController(); + events = StreamController>(); controller = MarkersController(stream: events); map = gmaps.GMap(html.DivElement()); controller.bindToMap(123, map); }); testWidgets('addMarkers', (WidgetTester tester) async { - final markers = { - Marker(markerId: MarkerId('1')), - Marker(markerId: MarkerId('2')), + final Set markers = { + const Marker(markerId: MarkerId('1')), + const Marker(markerId: MarkerId('2')), }; controller.addMarkers(markers); expect(controller.markers.length, 2); - expect(controller.markers, contains(MarkerId('1'))); - expect(controller.markers, contains(MarkerId('2'))); - expect(controller.markers, isNot(contains(MarkerId('66')))); + expect(controller.markers, contains(const MarkerId('1'))); + expect(controller.markers, contains(const MarkerId('2'))); + expect(controller.markers, isNot(contains(const MarkerId('66')))); }); testWidgets('changeMarkers', (WidgetTester tester) async { - final markers = { - Marker(markerId: MarkerId('1')), + final Set markers = { + const Marker(markerId: MarkerId('1')), }; controller.addMarkers(markers); - expect(controller.markers[MarkerId('1')]?.marker?.draggable, isFalse); + expect( + controller.markers[const MarkerId('1')]?.marker?.draggable, isFalse); // Update the marker with radius 10 - final updatedMarkers = { - Marker(markerId: MarkerId('1'), draggable: true), + final Set updatedMarkers = { + const Marker(markerId: MarkerId('1'), draggable: true), }; controller.changeMarkers(updatedMarkers); expect(controller.markers.length, 1); - expect(controller.markers[MarkerId('1')]?.marker?.draggable, isTrue); + expect( + controller.markers[const MarkerId('1')]?.marker?.draggable, isTrue); }); testWidgets('removeMarkers', (WidgetTester tester) async { - final markers = { - Marker(markerId: MarkerId('1')), - Marker(markerId: MarkerId('2')), - Marker(markerId: MarkerId('3')), + final Set markers = { + const Marker(markerId: MarkerId('1')), + const Marker(markerId: MarkerId('2')), + const Marker(markerId: MarkerId('3')), }; controller.addMarkers(markers); @@ -75,91 +78,93 @@ void main() { expect(controller.markers.length, 3); // Remove some markers... - final markerIdsToRemove = { - MarkerId('1'), - MarkerId('3'), + final Set markerIdsToRemove = { + const MarkerId('1'), + const MarkerId('3'), }; controller.removeMarkers(markerIdsToRemove); expect(controller.markers.length, 1); - expect(controller.markers, isNot(contains(MarkerId('1')))); - expect(controller.markers, contains(MarkerId('2'))); - expect(controller.markers, isNot(contains(MarkerId('3')))); + expect(controller.markers, isNot(contains(const MarkerId('1')))); + expect(controller.markers, contains(const MarkerId('2'))); + expect(controller.markers, isNot(contains(const MarkerId('3')))); }); testWidgets('InfoWindow show/hide', (WidgetTester tester) async { - final markers = { - Marker( + final Set markers = { + const Marker( markerId: MarkerId('1'), - infoWindow: InfoWindow(title: "Title", snippet: "Snippet"), + infoWindow: InfoWindow(title: 'Title', snippet: 'Snippet'), ), }; controller.addMarkers(markers); - expect(controller.markers[MarkerId('1')]?.infoWindowShown, isFalse); + expect(controller.markers[const MarkerId('1')]?.infoWindowShown, isFalse); - controller.showMarkerInfoWindow(MarkerId('1')); + controller.showMarkerInfoWindow(const MarkerId('1')); - expect(controller.markers[MarkerId('1')]?.infoWindowShown, isTrue); + expect(controller.markers[const MarkerId('1')]?.infoWindowShown, isTrue); - controller.hideMarkerInfoWindow(MarkerId('1')); + controller.hideMarkerInfoWindow(const MarkerId('1')); - expect(controller.markers[MarkerId('1')]?.infoWindowShown, isFalse); + expect(controller.markers[const MarkerId('1')]?.infoWindowShown, isFalse); }); // https://github.com/flutter/flutter/issues/67380 testWidgets('only single InfoWindow is visible', (WidgetTester tester) async { - final markers = { - Marker( + final Set markers = { + const Marker( markerId: MarkerId('1'), - infoWindow: InfoWindow(title: "Title", snippet: "Snippet"), + infoWindow: InfoWindow(title: 'Title', snippet: 'Snippet'), ), - Marker( + const Marker( markerId: MarkerId('2'), - infoWindow: InfoWindow(title: "Title", snippet: "Snippet"), + infoWindow: InfoWindow(title: 'Title', snippet: 'Snippet'), ), }; controller.addMarkers(markers); - expect(controller.markers[MarkerId('1')]?.infoWindowShown, isFalse); - expect(controller.markers[MarkerId('2')]?.infoWindowShown, isFalse); + expect(controller.markers[const MarkerId('1')]?.infoWindowShown, isFalse); + expect(controller.markers[const MarkerId('2')]?.infoWindowShown, isFalse); - controller.showMarkerInfoWindow(MarkerId('1')); + controller.showMarkerInfoWindow(const MarkerId('1')); - expect(controller.markers[MarkerId('1')]?.infoWindowShown, isTrue); - expect(controller.markers[MarkerId('2')]?.infoWindowShown, isFalse); + expect(controller.markers[const MarkerId('1')]?.infoWindowShown, isTrue); + expect(controller.markers[const MarkerId('2')]?.infoWindowShown, isFalse); - controller.showMarkerInfoWindow(MarkerId('2')); + controller.showMarkerInfoWindow(const MarkerId('2')); - expect(controller.markers[MarkerId('1')]?.infoWindowShown, isFalse); - expect(controller.markers[MarkerId('2')]?.infoWindowShown, isTrue); + expect(controller.markers[const MarkerId('1')]?.infoWindowShown, isFalse); + expect(controller.markers[const MarkerId('2')]?.infoWindowShown, isTrue); }); // https://github.com/flutter/flutter/issues/66622 testWidgets('markers with custom bitmap icon work', (WidgetTester tester) async { - final bytes = Base64Decoder().convert(iconImageBase64); - final markers = { + final Uint8List bytes = const Base64Decoder().convert(iconImageBase64); + final Set markers = { Marker( - markerId: MarkerId('1'), icon: BitmapDescriptor.fromBytes(bytes)), + markerId: const MarkerId('1'), + icon: BitmapDescriptor.fromBytes(bytes), + ), }; controller.addMarkers(markers); expect(controller.markers.length, 1); - expect(controller.markers[MarkerId('1')]?.marker?.icon, isNotNull); + expect(controller.markers[const MarkerId('1')]?.marker?.icon, isNotNull); - final blobUrl = getProperty( - controller.markers[MarkerId('1')]!.marker!.icon!, + final String blobUrl = getProperty( + controller.markers[const MarkerId('1')]!.marker!.icon!, 'url', ); expect(blobUrl, startsWith('blob:')); - final response = await http.get(Uri.parse(blobUrl)); + final http.Response response = await http.get(Uri.parse(blobUrl)); expect(response.bodyBytes, bytes, reason: @@ -169,8 +174,8 @@ void main() { // https://github.com/flutter/flutter/issues/67854 testWidgets('InfoWindow snippet can have links', (WidgetTester tester) async { - final markers = { - Marker( + final Set markers = { + const Marker( markerId: MarkerId('1'), infoWindow: InfoWindow( title: 'title for test', @@ -182,19 +187,20 @@ void main() { controller.addMarkers(markers); expect(controller.markers.length, 1); - final content = controller.markers[MarkerId('1')]?.infoWindow?.content - as html.HtmlElement; - expect(content.innerHtml, contains('title for test')); + final html.HtmlElement? content = controller.markers[const MarkerId('1')] + ?.infoWindow?.content as html.HtmlElement?; + expect(content?.innerHtml, contains('title for test')); expect( - content.innerHtml, + content?.innerHtml, contains( - 'Go to Google >>>')); + 'Go to Google >>>', + )); }); // https://github.com/flutter/flutter/issues/67289 testWidgets('InfoWindow content is clickable', (WidgetTester tester) async { - final markers = { - Marker( + final Set markers = { + const Marker( markerId: MarkerId('1'), infoWindow: InfoWindow( title: 'title for test', @@ -206,15 +212,15 @@ void main() { controller.addMarkers(markers); expect(controller.markers.length, 1); - final content = controller.markers[MarkerId('1')]?.infoWindow?.content - as html.HtmlElement; + final html.HtmlElement? content = controller.markers[const MarkerId('1')] + ?.infoWindow?.content as html.HtmlElement?; - content.click(); + content?.click(); - final event = await events.stream.first; + final MapEvent event = await events.stream.first; expect(event, isA()); - expect((event as InfoWindowTapEvent).value, equals(MarkerId('1'))); + expect((event as InfoWindowTapEvent).value, equals(const MarkerId('1'))); }); }); } 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 1bf0f10f50c8..14e4156b87ec 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 @@ -17,20 +17,20 @@ import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platf import 'package:integration_test/integration_test.dart'; // This value is used when comparing long~num, like LatLng values. -const _acceptableLatLngDelta = 0.0000000001; +const double _acceptableLatLngDelta = 0.0000000001; // This value is used when comparing pixel measurements, mostly to gloss over // browser rounding errors. -const _acceptablePixelDelta = 1; +const int _acceptablePixelDelta = 1; /// Test Google Map Controller void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('Methods that require a proper Projection', () { - final LatLng center = LatLng(43.3078, -5.6958); - final Size size = Size(320, 240); - final CameraPosition initialCamera = CameraPosition( + const LatLng center = LatLng(43.3078, -5.6958); + const Size size = Size(320, 240); + const CameraPosition initialCamera = CameraPosition( target: center, zoom: 14, ); @@ -48,7 +48,7 @@ void main() { group('getScreenCoordinate', () { testWidgets('target of map is in center of widget', (WidgetTester tester) async { - pumpCenteredMap( + await pumpCenteredMap( tester, initialCamera: initialCamera, size: size, @@ -72,7 +72,7 @@ void main() { testWidgets('NorthWest of visible region corresponds to x:0, y:0', (WidgetTester tester) async { - pumpCenteredMap( + await pumpCenteredMap( tester, initialCamera: initialCamera, size: size, @@ -96,7 +96,7 @@ void main() { testWidgets( 'SouthEast of visible region corresponds to x:size.width, y:size.height', (WidgetTester tester) async { - pumpCenteredMap( + await pumpCenteredMap( tester, initialCamera: initialCamera, size: size, @@ -121,7 +121,7 @@ void main() { group('getLatLng', () { testWidgets('Center of widget is the target of map', (WidgetTester tester) async { - pumpCenteredMap( + await pumpCenteredMap( tester, initialCamera: initialCamera, size: size, @@ -146,7 +146,7 @@ void main() { testWidgets('Top-left of widget is NorthWest bound of map', (WidgetTester tester) async { - pumpCenteredMap( + await pumpCenteredMap( tester, initialCamera: initialCamera, size: size, @@ -161,7 +161,7 @@ void main() { ); final LatLng coords = await controller.getLatLng( - ScreenCoordinate(x: 0, y: 0), + const ScreenCoordinate(x: 0, y: 0), ); expect( @@ -176,7 +176,7 @@ void main() { testWidgets('Bottom-right of widget is SouthWest bound of map', (WidgetTester tester) async { - pumpCenteredMap( + await pumpCenteredMap( tester, initialCamera: initialCamera, size: size, @@ -208,7 +208,7 @@ void main() { } // Pumps a CenteredMap Widget into a given tester, with some parameters -void pumpCenteredMap( +Future pumpCenteredMap( WidgetTester tester, { required CameraPosition initialCamera, Size size = const Size(320, 240), diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/resources/icon_image_base64.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/resources/icon_image_base64.dart index 6010f0107031..d08e96a65333 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/resources/icon_image_base64.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/resources/icon_image_base64.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. -final iconImageBase64 = +const String iconImageBase64 = 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAIRlWElmTU' '0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIA' 'AIdpAAQAAAABAAAAWgAAAAAAAABIAAAAAQAAAEgAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQ' diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shape_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shape_test.dart index 547aaec6dc0a..d1426760ceae 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shape_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shape_test.dart @@ -4,10 +4,10 @@ import 'dart:async'; -import 'package:integration_test/integration_test.dart'; +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'; -import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; /// Test Shapes (Circle, Polygon, Polyline) void main() { @@ -27,7 +27,7 @@ void main() { } setUp(() { - _methodCalledCompleter = Completer(); + _methodCalledCompleter = Completer(); methodCalled = _methodCalledCompleter.future; }); @@ -42,15 +42,16 @@ void main() { CircleController(circle: circle, consumeTapEvents: true, onTap: onTap); // Trigger a click event... - gmaps.Event.trigger(circle, 'click', [gmaps.MapMouseEvent()]); + gmaps.Event.trigger(circle, 'click', [gmaps.MapMouseEvent()]); // The event handling is now truly async. Wait for it... expect(await methodCalled, isTrue); }); testWidgets('update', (WidgetTester tester) async { - final controller = CircleController(circle: circle); - final options = gmaps.CircleOptions()..draggable = true; + final CircleController controller = CircleController(circle: circle); + final gmaps.CircleOptions options = gmaps.CircleOptions() + ..draggable = true; expect(circle.draggable, isNull); @@ -74,7 +75,8 @@ void main() { testWidgets('cannot call update after remove', (WidgetTester tester) async { - final options = gmaps.CircleOptions()..draggable = true; + final gmaps.CircleOptions options = gmaps.CircleOptions() + ..draggable = true; controller.remove(); @@ -96,15 +98,16 @@ void main() { PolygonController(polygon: polygon, consumeTapEvents: true, onTap: onTap); // Trigger a click event... - gmaps.Event.trigger(polygon, 'click', [gmaps.MapMouseEvent()]); + gmaps.Event.trigger(polygon, 'click', [gmaps.MapMouseEvent()]); // The event handling is now truly async. Wait for it... expect(await methodCalled, isTrue); }); testWidgets('update', (WidgetTester tester) async { - final controller = PolygonController(polygon: polygon); - final options = gmaps.PolygonOptions()..draggable = true; + final PolygonController controller = PolygonController(polygon: polygon); + final gmaps.PolygonOptions options = gmaps.PolygonOptions() + ..draggable = true; expect(polygon.draggable, isNull); @@ -128,7 +131,8 @@ void main() { testWidgets('cannot call update after remove', (WidgetTester tester) async { - final options = gmaps.PolygonOptions()..draggable = true; + final gmaps.PolygonOptions options = gmaps.PolygonOptions() + ..draggable = true; controller.remove(); @@ -148,18 +152,24 @@ void main() { testWidgets('onTap gets called', (WidgetTester tester) async { PolylineController( - polyline: polyline, consumeTapEvents: true, onTap: onTap); + polyline: polyline, + consumeTapEvents: true, + onTap: onTap, + ); // Trigger a click event... - gmaps.Event.trigger(polyline, 'click', [gmaps.MapMouseEvent()]); + gmaps.Event.trigger(polyline, 'click', [gmaps.MapMouseEvent()]); // The event handling is now truly async. Wait for it... expect(await methodCalled, isTrue); }); testWidgets('update', (WidgetTester tester) async { - final controller = PolylineController(polyline: polyline); - final options = gmaps.PolylineOptions()..draggable = true; + final PolylineController controller = PolylineController( + polyline: polyline, + ); + final gmaps.PolylineOptions options = gmaps.PolylineOptions() + ..draggable = true; expect(polyline.draggable, isNull); @@ -183,7 +193,8 @@ void main() { testWidgets('cannot call update after remove', (WidgetTester tester) async { - final options = gmaps.PolylineOptions()..draggable = true; + final gmaps.PolylineOptions options = gmaps.PolylineOptions() + ..draggable = true; controller.remove(); 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 80b4e0823bb5..b9bc2d371c9b 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,20 +3,20 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:ui'; import 'dart:html' as html; +import 'dart:ui'; -import 'package:integration_test/integration_test.dart'; -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:flutter_test/flutter_test.dart'; import 'package:google_maps/google_maps.dart' as gmaps; import 'package:google_maps/google_maps_geometry.dart' as geometry; -import 'package:flutter_test/flutter_test.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; +import 'package:integration_test/integration_test.dart'; // This value is used when comparing the results of // converting from a byte value to a double between 0 and 1. // (For Color opacity values, for example) -const _acceptableDelta = 0.01; +const double _acceptableDelta = 0.01; /// Test Shapes (Circle, Polygon, Polyline) void main() { @@ -29,51 +29,51 @@ void main() { }); group('CirclesController', () { - late StreamController events; + late StreamController> events; late CirclesController controller; setUp(() { - events = StreamController(); + events = StreamController>(); controller = CirclesController(stream: events); controller.bindToMap(123, map); }); testWidgets('addCircles', (WidgetTester tester) async { - final circles = { - Circle(circleId: CircleId('1')), - Circle(circleId: CircleId('2')), + final Set circles = { + const Circle(circleId: CircleId('1')), + const Circle(circleId: CircleId('2')), }; controller.addCircles(circles); expect(controller.circles.length, 2); - expect(controller.circles, contains(CircleId('1'))); - expect(controller.circles, contains(CircleId('2'))); - expect(controller.circles, isNot(contains(CircleId('66')))); + expect(controller.circles, contains(const CircleId('1'))); + expect(controller.circles, contains(const CircleId('2'))); + expect(controller.circles, isNot(contains(const CircleId('66')))); }); testWidgets('changeCircles', (WidgetTester tester) async { - final circles = { - Circle(circleId: CircleId('1')), + final Set circles = { + const Circle(circleId: CircleId('1')), }; controller.addCircles(circles); - expect(controller.circles[CircleId('1')]?.circle?.visible, isTrue); + expect(controller.circles[const CircleId('1')]?.circle?.visible, isTrue); - final updatedCircles = { - Circle(circleId: CircleId('1'), visible: false), + final Set updatedCircles = { + const Circle(circleId: CircleId('1'), visible: false), }; controller.changeCircles(updatedCircles); expect(controller.circles.length, 1); - expect(controller.circles[CircleId('1')]?.circle?.visible, isFalse); + expect(controller.circles[const CircleId('1')]?.circle?.visible, isFalse); }); testWidgets('removeCircles', (WidgetTester tester) async { - final circles = { - Circle(circleId: CircleId('1')), - Circle(circleId: CircleId('2')), - Circle(circleId: CircleId('3')), + final Set circles = { + const Circle(circleId: CircleId('1')), + const Circle(circleId: CircleId('2')), + const Circle(circleId: CircleId('3')), }; controller.addCircles(circles); @@ -81,22 +81,22 @@ void main() { expect(controller.circles.length, 3); // Remove some circles... - final circleIdsToRemove = { - CircleId('1'), - CircleId('3'), + final Set circleIdsToRemove = { + const CircleId('1'), + const CircleId('3'), }; controller.removeCircles(circleIdsToRemove); expect(controller.circles.length, 1); - expect(controller.circles, isNot(contains(CircleId('1')))); - expect(controller.circles, contains(CircleId('2'))); - expect(controller.circles, isNot(contains(CircleId('3')))); + expect(controller.circles, isNot(contains(const CircleId('1')))); + expect(controller.circles, contains(const CircleId('2'))); + expect(controller.circles, isNot(contains(const CircleId('3')))); }); testWidgets('Converts colors to CSS', (WidgetTester tester) async { - final circles = { - Circle( + final Set circles = { + const Circle( circleId: CircleId('1'), fillColor: Color(0x7FFABADA), strokeColor: Color(0xFFC0FFEE), @@ -105,7 +105,7 @@ void main() { controller.addCircles(circles); - final circle = controller.circles.values.first.circle!; + final gmaps.Circle circle = controller.circles.values.first.circle!; expect(circle.get('fillColor'), '#fabada'); expect(circle.get('fillOpacity'), closeTo(0.5, _acceptableDelta)); @@ -115,52 +115,54 @@ void main() { }); group('PolygonsController', () { - late StreamController events; + late StreamController> events; late PolygonsController controller; setUp(() { - events = StreamController(); + events = StreamController>(); controller = PolygonsController(stream: events); controller.bindToMap(123, map); }); testWidgets('addPolygons', (WidgetTester tester) async { - final polygons = { - Polygon(polygonId: PolygonId('1')), - Polygon(polygonId: PolygonId('2')), + final Set polygons = { + const Polygon(polygonId: PolygonId('1')), + const Polygon(polygonId: PolygonId('2')), }; controller.addPolygons(polygons); expect(controller.polygons.length, 2); - expect(controller.polygons, contains(PolygonId('1'))); - expect(controller.polygons, contains(PolygonId('2'))); - expect(controller.polygons, isNot(contains(PolygonId('66')))); + expect(controller.polygons, contains(const PolygonId('1'))); + expect(controller.polygons, contains(const PolygonId('2'))); + expect(controller.polygons, isNot(contains(const PolygonId('66')))); }); testWidgets('changePolygons', (WidgetTester tester) async { - final polygons = { - Polygon(polygonId: PolygonId('1')), + final Set polygons = { + const Polygon(polygonId: PolygonId('1')), }; controller.addPolygons(polygons); - expect(controller.polygons[PolygonId('1')]?.polygon?.visible, isTrue); + expect( + controller.polygons[const PolygonId('1')]?.polygon?.visible, isTrue); // Update the polygon - final updatedPolygons = { - Polygon(polygonId: PolygonId('1'), visible: false), + final Set updatedPolygons = { + const Polygon(polygonId: PolygonId('1'), visible: false), }; controller.changePolygons(updatedPolygons); expect(controller.polygons.length, 1); - expect(controller.polygons[PolygonId('1')]?.polygon?.visible, isFalse); + expect( + controller.polygons[const PolygonId('1')]?.polygon?.visible, isFalse); }); testWidgets('removePolygons', (WidgetTester tester) async { - final polygons = { - Polygon(polygonId: PolygonId('1')), - Polygon(polygonId: PolygonId('2')), - Polygon(polygonId: PolygonId('3')), + final Set polygons = { + const Polygon(polygonId: PolygonId('1')), + const Polygon(polygonId: PolygonId('2')), + const Polygon(polygonId: PolygonId('3')), }; controller.addPolygons(polygons); @@ -168,22 +170,22 @@ void main() { expect(controller.polygons.length, 3); // Remove some polygons... - final polygonIdsToRemove = { - PolygonId('1'), - PolygonId('3'), + final Set polygonIdsToRemove = { + const PolygonId('1'), + const PolygonId('3'), }; controller.removePolygons(polygonIdsToRemove); expect(controller.polygons.length, 1); - expect(controller.polygons, isNot(contains(PolygonId('1')))); - expect(controller.polygons, contains(PolygonId('2'))); - expect(controller.polygons, isNot(contains(PolygonId('3')))); + expect(controller.polygons, isNot(contains(const PolygonId('1')))); + expect(controller.polygons, contains(const PolygonId('2'))); + expect(controller.polygons, isNot(contains(const PolygonId('3')))); }); testWidgets('Converts colors to CSS', (WidgetTester tester) async { - final polygons = { - Polygon( + final Set polygons = { + const Polygon( polygonId: PolygonId('1'), fillColor: Color(0x7FFABADA), strokeColor: Color(0xFFC0FFEE), @@ -192,7 +194,7 @@ void main() { controller.addPolygons(polygons); - final polygon = controller.polygons.values.first.polygon!; + final gmaps.Polygon polygon = controller.polygons.values.first.polygon!; expect(polygon.get('fillColor'), '#fabada'); expect(polygon.get('fillOpacity'), closeTo(0.5, _acceptableDelta)); @@ -201,16 +203,16 @@ void main() { }); testWidgets('Handle Polygons with holes', (WidgetTester tester) async { - final polygons = { - Polygon( + final Set polygons = { + const Polygon( polygonId: PolygonId('BermudaTriangle'), - points: [ + points: [ LatLng(25.774, -80.19), LatLng(18.466, -66.118), LatLng(32.321, -64.757), ], - holes: [ - [ + holes: >[ + [ LatLng(28.745, -70.579), LatLng(29.57, -67.514), LatLng(27.339, -66.668), @@ -222,21 +224,21 @@ void main() { controller.addPolygons(polygons); expect(controller.polygons.length, 1); - expect(controller.polygons, contains(PolygonId('BermudaTriangle'))); - expect(controller.polygons, isNot(contains(PolygonId('66')))); + expect(controller.polygons, contains(const PolygonId('BermudaTriangle'))); + expect(controller.polygons, isNot(contains(const PolygonId('66')))); }); testWidgets('Polygon with hole has a hole', (WidgetTester tester) async { - final polygons = { - Polygon( + final Set polygons = { + const Polygon( polygonId: PolygonId('BermudaTriangle'), - points: [ + points: [ LatLng(25.774, -80.19), LatLng(18.466, -66.118), LatLng(32.321, -64.757), ], - holes: [ - [ + holes: >[ + [ LatLng(28.745, -70.579), LatLng(29.57, -67.514), LatLng(27.339, -66.668), @@ -247,24 +249,24 @@ void main() { controller.addPolygons(polygons); - final polygon = controller.polygons.values.first.polygon; - final pointInHole = gmaps.LatLng(28.632, -68.401); + final gmaps.Polygon? polygon = controller.polygons.values.first.polygon; + final gmaps.LatLng pointInHole = gmaps.LatLng(28.632, -68.401); expect(geometry.Poly.containsLocation(pointInHole, polygon), false); }); testWidgets('Hole Path gets reversed to display correctly', (WidgetTester tester) async { - final polygons = { - Polygon( + final Set polygons = { + const Polygon( polygonId: PolygonId('BermudaTriangle'), - points: [ + points: [ LatLng(25.774, -80.19), LatLng(18.466, -66.118), LatLng(32.321, -64.757), ], - holes: [ - [ + holes: >[ + [ LatLng(27.339, -66.668), LatLng(29.57, -67.514), LatLng(28.745, -70.579), @@ -275,7 +277,8 @@ void main() { controller.addPolygons(polygons); - final paths = controller.polygons.values.first.polygon!.paths!; + final gmaps.MVCArray?> paths = + controller.polygons.values.first.polygon!.paths!; expect(paths.getAt(1)?.getAt(0)?.lat, 28.745); expect(paths.getAt(1)?.getAt(1)?.lat, 29.57); @@ -284,51 +287,51 @@ void main() { }); group('PolylinesController', () { - late StreamController events; + late StreamController> events; late PolylinesController controller; setUp(() { - events = StreamController(); + events = StreamController>(); controller = PolylinesController(stream: events); controller.bindToMap(123, map); }); testWidgets('addPolylines', (WidgetTester tester) async { - final polylines = { - Polyline(polylineId: PolylineId('1')), - Polyline(polylineId: PolylineId('2')), + final Set polylines = { + const Polyline(polylineId: PolylineId('1')), + const Polyline(polylineId: PolylineId('2')), }; controller.addPolylines(polylines); expect(controller.lines.length, 2); - expect(controller.lines, contains(PolylineId('1'))); - expect(controller.lines, contains(PolylineId('2'))); - expect(controller.lines, isNot(contains(PolylineId('66')))); + expect(controller.lines, contains(const PolylineId('1'))); + expect(controller.lines, contains(const PolylineId('2'))); + expect(controller.lines, isNot(contains(const PolylineId('66')))); }); testWidgets('changePolylines', (WidgetTester tester) async { - final polylines = { - Polyline(polylineId: PolylineId('1')), + final Set polylines = { + const Polyline(polylineId: PolylineId('1')), }; controller.addPolylines(polylines); - expect(controller.lines[PolylineId('1')]?.line?.visible, isTrue); + expect(controller.lines[const PolylineId('1')]?.line?.visible, isTrue); - final updatedPolylines = { - Polyline(polylineId: PolylineId('1'), visible: false), + final Set updatedPolylines = { + const Polyline(polylineId: PolylineId('1'), visible: false), }; controller.changePolylines(updatedPolylines); expect(controller.lines.length, 1); - expect(controller.lines[PolylineId('1')]?.line?.visible, isFalse); + expect(controller.lines[const PolylineId('1')]?.line?.visible, isFalse); }); testWidgets('removePolylines', (WidgetTester tester) async { - final polylines = { - Polyline(polylineId: PolylineId('1')), - Polyline(polylineId: PolylineId('2')), - Polyline(polylineId: PolylineId('3')), + final Set polylines = { + const Polyline(polylineId: PolylineId('1')), + const Polyline(polylineId: PolylineId('2')), + const Polyline(polylineId: PolylineId('3')), }; controller.addPolylines(polylines); @@ -336,22 +339,22 @@ void main() { expect(controller.lines.length, 3); // Remove some polylines... - final polylineIdsToRemove = { - PolylineId('1'), - PolylineId('3'), + final Set polylineIdsToRemove = { + const PolylineId('1'), + const PolylineId('3'), }; controller.removePolylines(polylineIdsToRemove); expect(controller.lines.length, 1); - expect(controller.lines, isNot(contains(PolylineId('1')))); - expect(controller.lines, contains(PolylineId('2'))); - expect(controller.lines, isNot(contains(PolylineId('3')))); + expect(controller.lines, isNot(contains(const PolylineId('1')))); + expect(controller.lines, contains(const PolylineId('2'))); + expect(controller.lines, isNot(contains(const PolylineId('3')))); }); testWidgets('Converts colors to CSS', (WidgetTester tester) async { - final lines = { - Polyline( + final Set lines = { + const Polyline( polylineId: PolylineId('1'), color: Color(0x7FFABADA), ), @@ -359,7 +362,7 @@ void main() { controller.addPolylines(lines); - final line = controller.lines.values.first.line!; + final gmaps.Polyline line = controller.lines.values.first.line!; expect(line.get('strokeColor'), '#fabada'); expect(line.get('strokeOpacity'), closeTo(0.5, _acceptableDelta)); 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 d1ba571b5bd0..e93a60e19906 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 @@ -5,11 +5,14 @@ import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } /// App for testing class MyApp extends StatefulWidget { + /// Constructor with key + const MyApp({Key? key}) : super(key: key); + @override State createState() => _MyAppState(); } @@ -17,6 +20,6 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { @override Widget build(BuildContext context) { - return Text('Testing... Look at the console output for results!'); + return const 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 a962e5b864c6..fb6359fe5b8f 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 @@ -7,21 +7,21 @@ environment: flutter: ">=2.8.0" dependencies: - google_maps_flutter_web: - path: ../ flutter: sdk: flutter + google_maps_flutter_web: + path: ../ dev_dependencies: build_runner: ^2.1.1 - google_maps: ^5.2.0 - google_maps_flutter: # Used for projection_test.dart - path: ../../google_maps_flutter - http: ^0.13.0 - mockito: ^5.0.0 flutter_driver: sdk: flutter flutter_test: sdk: flutter + google_maps: ^6.1.0 + google_maps_flutter: # Used for projection_test.dart + path: ../../google_maps_flutter + http: ^0.13.0 integration_test: sdk: flutter + mockito: ^5.0.0 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 c3079dc2492d..7ae646687f19 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 @@ -5,36 +5,33 @@ library google_maps_flutter_web; import 'dart:async'; +import 'dart:convert'; import 'dart:html'; import 'dart:js_util'; -import 'src/shims/dart_ui.dart' as ui; // Conditionally imports dart:ui in web -import 'dart:convert'; -import 'package:flutter/widgets.dart'; -import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; import 'package:flutter/gestures.dart'; - -import 'package:sanitize_html/sanitize_html.dart'; - -import 'package:stream_transform/stream_transform.dart'; - -import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.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:sanitize_html/sanitize_html.dart'; +import 'package:stream_transform/stream_transform.dart'; +import 'src/shims/dart_ui.dart' as ui; // Conditionally imports dart:ui in web import 'src/third_party/to_screen_location/to_screen_location.dart'; import 'src/types.dart'; -part 'src/google_maps_flutter_web.dart'; -part 'src/google_maps_controller.dart'; part 'src/circle.dart'; part 'src/circles.dart'; +part 'src/convert.dart'; +part 'src/google_maps_controller.dart'; +part 'src/google_maps_flutter_web.dart'; +part 'src/marker.dart'; +part 'src/markers.dart'; part 'src/polygon.dart'; part 'src/polygons.dart'; part 'src/polyline.dart'; part 'src/polylines.dart'; -part 'src/marker.dart'; -part 'src/markers.dart'; -part 'src/convert.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart index 65057d8c869e..9cd3ba1c079c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circle.dart @@ -6,10 +6,6 @@ part of google_maps_flutter_web; /// The `CircleController` class wraps a [gmaps.Circle] and its `onTap` behavior. class CircleController { - gmaps.Circle? _circle; - - final bool _consumeTapEvents; - /// Creates a `CircleController`, which wraps a [gmaps.Circle] object and its `onTap` behavior. CircleController({ required gmaps.Circle circle, @@ -24,6 +20,10 @@ class CircleController { } } + gmaps.Circle? _circle; + + final bool _consumeTapEvents; + /// Returns the wrapped [gmaps.Circle]. Only used for testing. @visibleForTesting gmaps.Circle? get circle => _circle; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart index ae8faa038ea6..bc6eac14200f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/circles.dart @@ -6,17 +6,17 @@ part of google_maps_flutter_web; /// This class manages all the [CircleController]s associated to a [GoogleMapController]. class CirclesController extends GeometryController { + /// Initialize the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. + CirclesController({ + required StreamController> stream, + }) : _streamController = stream, + _circleIdToController = {}; + // A cache of [CircleController]s indexed by their [CircleId]. final Map _circleIdToController; // The stream over which circles broadcast their events - StreamController _streamController; - - /// Initialize the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. - CirclesController({ - required StreamController stream, - }) : _streamController = stream, - _circleIdToController = Map(); + final StreamController> _streamController; /// Returns the cache of [CircleController]s. Test only. @visibleForTesting @@ -26,9 +26,7 @@ class CirclesController extends GeometryController { /// /// Wraps each [Circle] into its corresponding [CircleController]. void addCircles(Set circlesToAdd) { - circlesToAdd.forEach((circle) { - _addCircle(circle); - }); + circlesToAdd.forEach(_addCircle); } void _addCircle(Circle circle) { @@ -36,10 +34,9 @@ class CirclesController extends GeometryController { return; } - final populationOptions = _circleOptionsFromCircle(circle); - gmaps.Circle gmCircle = gmaps.Circle(populationOptions); - gmCircle.map = googleMap; - CircleController controller = CircleController( + final gmaps.CircleOptions circleOptions = _circleOptionsFromCircle(circle); + final gmaps.Circle gmCircle = gmaps.Circle(circleOptions)..map = googleMap; + final CircleController controller = CircleController( circle: gmCircle, consumeTapEvents: circle.consumeTapEvents, onTap: () { @@ -50,24 +47,25 @@ class CirclesController extends GeometryController { /// Updates a set of [Circle] objects with new options. void changeCircles(Set circlesToChange) { - circlesToChange.forEach((circleToChange) { - _changeCircle(circleToChange); - }); + circlesToChange.forEach(_changeCircle); } void _changeCircle(Circle circle) { - final circleController = _circleIdToController[circle.circleId]; + final CircleController? circleController = + _circleIdToController[circle.circleId]; circleController?.update(_circleOptionsFromCircle(circle)); } /// Removes a set of [CircleId]s from the cache. void removeCircles(Set circleIdsToRemove) { - circleIdsToRemove.forEach((circleId) { - final CircleController? circleController = - _circleIdToController[circleId]; - circleController?.remove(); - _circleIdToController.remove(circleId); - }); + circleIdsToRemove.forEach(_removeCircle); + } + + // Removes a circle and its controller by its [CircleId]. + void _removeCircle(CircleId circleId) { + final CircleController? circleController = _circleIdToController[circleId]; + circleController?.remove(); + _circleIdToController.remove(circleId); } // Handles the global onCircleTap function to funnel events from circles into the stream. 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 c026a03be804..c6f3164ff207 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 @@ -5,17 +5,17 @@ part of google_maps_flutter_web; // Default values for when the gmaps objects return null/undefined values. -final _nullGmapsLatLng = gmaps.LatLng(0, 0); -final _nullGmapsLatLngBounds = +final gmaps.LatLng _nullGmapsLatLng = gmaps.LatLng(0, 0); +final gmaps.LatLngBounds _nullGmapsLatLngBounds = gmaps.LatLngBounds(_nullGmapsLatLng, _nullGmapsLatLng); // Defaults taken from the Google Maps Platform SDK documentation. -final _defaultCssColor = '#000000'; -final _defaultCssOpacity = 0.0; +const String _defaultCssColor = '#000000'; +const double _defaultCssOpacity = 0.0; // Indices in the plugin side don't match with the ones // in the gmaps lib. This translates from plugin -> gmaps. -final _mapTypeToMapTypeId = { +final Map _mapTypeToMapTypeId = { 0: gmaps.MapTypeId.ROADMAP, // "none" in the plugin 1: gmaps.MapTypeId.ROADMAP, 2: gmaps.MapTypeId.SATELLITE, @@ -28,7 +28,7 @@ String _getCssColor(Color color) { if (color == null) { return _defaultCssColor; } - return '#' + color.value.toRadixString(16).padLeft(8, '0').substring(2); + return '#${color.value.toRadixString(16).padLeft(8, '0').substring(2)}'; } // Extracts the opacity from a [Color]. @@ -55,17 +55,19 @@ double _getCssOpacity(Color color) { // indoorViewEnabled seems to not have an equivalent in web // buildingsEnabled seems to not have an equivalent in web // padding seems to behave differently in web than mobile. You can't move UI elements in web. -gmaps.MapOptions _rawOptionsToGmapsOptions(Map rawOptions) { - gmaps.MapOptions options = gmaps.MapOptions(); +gmaps.MapOptions _rawOptionsToGmapsOptions(Map rawOptions) { + final gmaps.MapOptions options = gmaps.MapOptions(); if (_mapTypeToMapTypeId.containsKey(rawOptions['mapType'])) { options.mapTypeId = _mapTypeToMapTypeId[rawOptions['mapType']]; } if (rawOptions['minMaxZoomPreference'] != null) { + final List minMaxPreference = + rawOptions['minMaxZoomPreference']! as List; options - ..minZoom = rawOptions['minMaxZoomPreference'][0] - ..maxZoom = rawOptions['minMaxZoomPreference'][1]; + ..minZoom = minMaxPreference[0] as num? + ..maxZoom = minMaxPreference[1] as num?; } if (rawOptions['cameraTargetBounds'] != null) { @@ -74,11 +76,11 @@ gmaps.MapOptions _rawOptionsToGmapsOptions(Map rawOptions) { } if (rawOptions['zoomControlsEnabled'] != null) { - options.zoomControl = rawOptions['zoomControlsEnabled']; + options.zoomControl = rawOptions['zoomControlsEnabled'] as bool?; } if (rawOptions['styles'] != null) { - options.styles = rawOptions['styles']; + options.styles = rawOptions['styles'] as List?; } if (rawOptions['scrollGesturesEnabled'] == false || @@ -110,37 +112,42 @@ gmaps.MapOptions _applyInitialPosition( } // Extracts the status of the traffic layer from the rawOptions map. -bool _isTrafficLayerEnabled(Map rawOptions) { - return rawOptions['trafficEnabled'] ?? false; +bool _isTrafficLayerEnabled(Map rawOptions) { + return rawOptions['trafficEnabled'] as bool? ?? false; } // The keys we'd expect to see in a serialized MapTypeStyle JSON object. -final _mapStyleKeys = { +final Set _mapStyleKeys = { 'elementType', 'featureType', 'stylers', }; // Checks if the passed in Map contains some of the _mapStyleKeys. -bool _isJsonMapStyle(Map value) { +bool _isJsonMapStyle(Map value) { return _mapStyleKeys.intersection(value.keys.toSet()).isNotEmpty; } // Converts an incoming JSON-encoded Style info, into the correct gmaps array. List _mapStyles(String? mapStyleJson) { - List styles = []; + List styles = []; if (mapStyleJson != null) { - styles = json - .decode(mapStyleJson, reviver: (key, value) { - if (value is Map && _isJsonMapStyle(value)) { - return gmaps.MapTypeStyle() - ..elementType = value['elementType'] - ..featureType = value['featureType'] - ..stylers = - (value['stylers'] as List).map((e) => jsify(e)).toList(); - } - return value; - }) + styles = (json.decode(mapStyleJson, reviver: (Object? key, Object? value) { + if (value is Map && _isJsonMapStyle(value as Map)) { + List stylers = []; + if (value['stylers'] != null) { + stylers = (value['stylers']! as List) + .map((Object? e) => e != null ? jsify(e) : null) + .toList(); + } + return gmaps.MapTypeStyle() + ..elementType = value['elementType'] as String? + ..featureType = value['featureType'] as String? + ..stylers = stylers; + } + return value; + }) as List) + .where((Object? element) => element != null) .cast() .toList(); // .toList calls are required so the JS API understands the underlying data structure. @@ -173,12 +180,12 @@ CameraPosition _gmViewportToCameraPosition(gmaps.GMap map) { } // Convert plugin objects to gmaps.Options objects -// TODO: Move to their appropriate objects, maybe make these copy constructors: +// TODO(ditman): Move to their appropriate objects, maybe make them copy constructors? // Marker.fromMarker(anotherMarker, moreOptions); gmaps.InfoWindowOptions? _infoWindowOptionsFromMarker(Marker marker) { - final markerTitle = marker.infoWindow.title ?? ''; - final markerSnippet = marker.infoWindow.snippet ?? ''; + final String markerTitle = marker.infoWindow.title ?? ''; + final String markerSnippet = marker.infoWindow.snippet ?? ''; // If both the title and snippet of an infowindow are empty, we don't really // want an infowindow... @@ -200,6 +207,13 @@ gmaps.InfoWindowOptions? _infoWindowOptionsFromMarker(Marker marker) { if (markerSnippet.isNotEmpty) { final HtmlElement snippet = DivElement() ..className = 'infowindow-snippet' + // `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, @@ -210,7 +224,7 @@ gmaps.InfoWindowOptions? _infoWindowOptionsFromMarker(Marker marker) { return gmaps.InfoWindowOptions() ..content = container ..zIndex = marker.zIndex; - // TODO: Compute the pixelOffset of the infoWindow, from the size of the Marker, + // TODO(ditman): Compute the pixelOffset of the infoWindow, from the size of the Marker, // and the marker.infoWindow.anchor property. } @@ -221,7 +235,7 @@ gmaps.MarkerOptions _markerOptionsFromMarker( Marker marker, gmaps.Marker? currentMarker, ) { - final iconConfig = marker.icon.toJson() as List; + final List iconConfig = marker.icon.toJson() as List; gmaps.Icon? icon; if (iconConfig != null) { @@ -231,19 +245,24 @@ gmaps.MarkerOptions _markerOptionsFromMarker( // already encoded in the iconConfig[1] icon = gmaps.Icon() - ..url = ui.webOnlyAssetManager.getAssetUrl(iconConfig[1]); + ..url = ui.webOnlyAssetManager.getAssetUrl(iconConfig[1]! as String); // iconConfig[3] may contain the [width, height] of the image, if passed! if (iconConfig.length >= 4 && iconConfig[3] != null) { - final size = gmaps.Size(iconConfig[3][0], iconConfig[3][1]); + final List rawIconSize = iconConfig[3]! as List; + final gmaps.Size size = gmaps.Size( + rawIconSize[0] as num?, + rawIconSize[1] as num?, + ); icon ..size = size ..scaledSize = size; } } else if (iconConfig[0] == 'fromBytes') { // Grab the bytes, and put them into a blob - List bytes = iconConfig[1]; - final blob = Blob([bytes]); // Let the browser figure out the encoding + 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); } } @@ -253,18 +272,18 @@ gmaps.MarkerOptions _markerOptionsFromMarker( marker.position.latitude, marker.position.longitude, ) - ..title = sanitizeHtml(marker.infoWindow.title ?? "") + ..title = sanitizeHtml(marker.infoWindow.title ?? '') ..zIndex = marker.zIndex ..visible = marker.visible ..opacity = marker.alpha ..draggable = marker.draggable ..icon = icon; - // TODO: Compute anchor properly, otherwise infowindows attach to the wrong spot. + // TODO(ditman): Compute anchor properly, otherwise infowindows attach to the wrong spot. // Flat and Rotation are not supported directly on the web. } gmaps.CircleOptions _circleOptionsFromCircle(Circle circle) { - final circleOptions = gmaps.CircleOptions() + final gmaps.CircleOptions circleOptions = gmaps.CircleOptions() ..strokeColor = _getCssColor(circle.strokeColor) ..strokeOpacity = _getCssOpacity(circle.strokeColor) ..strokeWeight = circle.strokeWidth @@ -279,28 +298,25 @@ gmaps.CircleOptions _circleOptionsFromCircle(Circle circle) { gmaps.PolygonOptions _polygonOptionsFromPolygon( gmaps.GMap googleMap, Polygon polygon) { - List path = []; - polygon.points.forEach((point) { - path.add(_latLngToGmLatLng(point)); - }); - final polygonDirection = _isPolygonClockwise(path); - List> paths = [path]; - int holeIndex = 0; - polygon.holes.forEach((hole) { - List holePath = - hole.map((point) => _latLngToGmLatLng(point)).toList(); - if (_isPolygonClockwise(holePath) == polygonDirection) { - holePath = holePath.reversed.toList(); - if (kDebugMode) { - print( - 'Hole [$holeIndex] in Polygon [${polygon.polygonId.value}] has been reversed.' - ' Ensure holes in polygons are "wound in the opposite direction to the outer path."' - ' More info: https://github.com/flutter/flutter/issues/74096'); - } - } - paths.add(holePath); - holeIndex++; - }); + // Convert all points to GmLatLng + final List path = + polygon.points.map(_latLngToGmLatLng).toList(); + + final bool isClockwisePolygon = _isPolygonClockwise(path); + + final List> paths = >[path]; + + for (int i = 0; i < polygon.holes.length; i++) { + final List hole = polygon.holes[i]; + final List correctHole = _ensureHoleHasReverseWinding( + hole, + isClockwisePolygon, + holeId: i, + polygonId: polygon.polygonId, + ); + paths.add(correctHole); + } + return gmaps.PolygonOptions() ..paths = paths ..strokeColor = _getCssColor(polygon.strokeColor) @@ -313,6 +329,27 @@ gmaps.PolygonOptions _polygonOptionsFromPolygon( ..geodesic = polygon.geodesic; } +List _ensureHoleHasReverseWinding( + List hole, + bool polyIsClockwise, { + required int holeId, + required PolygonId polygonId, +}) { + List holePath = hole.map(_latLngToGmLatLng).toList(); + final bool holeIsClockwise = _isPolygonClockwise(holePath); + + if (holeIsClockwise == polyIsClockwise) { + holePath = holePath.reversed.toList(); + if (kDebugMode) { + print('Hole [$holeId] in Polygon [${polygonId.value}] has been reversed.' + ' Ensure holes in polygons are "wound in the opposite direction to the outer path."' + ' More info: https://github.com/flutter/flutter/issues/74096'); + } + } + + return holePath; +} + /// Calculates the direction of a given Polygon /// based on: https://stackoverflow.com/a/1165943 /// @@ -325,8 +362,8 @@ gmaps.PolygonOptions _polygonOptionsFromPolygon( /// the `path` is a transformed version of [Polygon.points] or each of the /// [Polygon.holes], guaranteeing that `lat` and `lng` can be accessed with `!`. bool _isPolygonClockwise(List path) { - var direction = 0.0; - for (var i = 0; i < path.length; i++) { + double direction = 0.0; + for (int i = 0; i < path.length; i++) { direction = direction + ((path[(i + 1) % path.length].lat - path[i].lat) * (path[(i + 1) % path.length].lng + path[i].lng)); @@ -336,10 +373,8 @@ bool _isPolygonClockwise(List path) { gmaps.PolylineOptions _polylineOptionsFromPolyline( gmaps.GMap googleMap, Polyline polyline) { - List paths = []; - polyline.points.forEach((point) { - paths.add(_latLngToGmLatLng(point)); - }); + final List paths = + polyline.points.map(_latLngToGmLatLng).toList(); return gmaps.PolylineOptions() ..path = paths @@ -358,40 +393,50 @@ gmaps.PolylineOptions _polylineOptionsFromPolyline( // Translates a [CameraUpdate] into operations on a [gmaps.GMap]. void _applyCameraUpdate(gmaps.GMap map, CameraUpdate update) { - final json = update.toJson() as List; + final List json = update.toJson() as List; switch (json[0]) { case 'newCameraPosition': - map.heading = json[1]['bearing']; - map.zoom = json[1]['zoom']; - map.panTo(gmaps.LatLng(json[1]['target'][0], json[1]['target'][1])); - map.tilt = json[1]['tilt']; + map.heading = json[1]['bearing'] as num?; + map.zoom = json[1]['zoom'] as num?; + map.panTo( + gmaps.LatLng( + json[1]['target'][0] as num?, + json[1]['target'][1] as num?, + ), + ); + map.tilt = json[1]['tilt'] as num?; break; case 'newLatLng': - map.panTo(gmaps.LatLng(json[1][0], json[1][1])); + map.panTo(gmaps.LatLng(json[1][0] as num?, json[1][1] as num?)); break; case 'newLatLngZoom': - map.zoom = json[2]; - map.panTo(gmaps.LatLng(json[1][0], json[1][1])); + map.zoom = json[2] as num?; + map.panTo(gmaps.LatLng(json[1][0] as num?, json[1][1] as num?)); break; case 'newLatLngBounds': - map.fitBounds(gmaps.LatLngBounds( - gmaps.LatLng(json[1][0][0], json[1][0][1]), - gmaps.LatLng(json[1][1][0], json[1][1][1]))); + map.fitBounds( + gmaps.LatLngBounds( + gmaps.LatLng(json[1][0][0] as num?, json[1][0][1] as num?), + gmaps.LatLng(json[1][1][0] as num?, json[1][1][1] as num?), + ), + ); // padding = json[2]; // Needs package:google_maps ^4.0.0 to adjust the padding in fitBounds break; case 'scrollBy': - map.panBy(json[1], json[2]); + map.panBy(json[1] as num?, json[2] as num?); break; case 'zoomBy': gmaps.LatLng? focusLatLng; - double zoomDelta = json[1] ?? 0; + final double zoomDelta = json[1] as double? ?? 0; // Web only supports integer changes... - int newZoomDelta = zoomDelta < 0 ? zoomDelta.floor() : zoomDelta.ceil(); + final int newZoomDelta = + zoomDelta < 0 ? zoomDelta.floor() : zoomDelta.ceil(); if (json.length == 3) { // With focus try { - focusLatLng = _pixelToLatLng(map, json[2][0], json[2][1]); + focusLatLng = + _pixelToLatLng(map, json[2][0] as int, json[2][1] as int); } catch (e) { // https://github.com/a14n/dart-google-maps/issues/87 // print('Error computing new focus LatLng. JS Error: ' + e.toString()); @@ -409,7 +454,7 @@ void _applyCameraUpdate(gmaps.GMap map, CameraUpdate update) { map.zoom = (map.zoom ?? 0) - 1; break; case 'zoomTo': - map.zoom = json[1]; + map.zoom = json[1] as num?; break; default: throw UnimplementedError('Unimplemented CameraMove: ${json[0]}.'); @@ -418,9 +463,9 @@ void _applyCameraUpdate(gmaps.GMap map, CameraUpdate update) { // original JS by: Byron Singh (https://stackoverflow.com/a/30541162) gmaps.LatLng _pixelToLatLng(gmaps.GMap map, int x, int y) { - final bounds = map.bounds; - final projection = map.projection; - final zoom = map.zoom; + final gmaps.LatLngBounds? bounds = map.bounds; + final gmaps.Projection? projection = map.projection; + final num? zoom = map.zoom; assert( bounds != null, 'Map Bounds required to compute LatLng of screen x/y.'); @@ -429,15 +474,15 @@ gmaps.LatLng _pixelToLatLng(gmaps.GMap map, int x, int y) { assert(zoom != null, 'Current map zoom level required to compute LatLng of screen x/y'); - final ne = bounds!.northEast; - final sw = bounds.southWest; + final gmaps.LatLng ne = bounds!.northEast; + final gmaps.LatLng sw = bounds.southWest; - final topRight = projection!.fromLatLngToPoint!(ne)!; - final bottomLeft = projection.fromLatLngToPoint!(sw)!; + final gmaps.Point topRight = projection!.fromLatLngToPoint!(ne)!; + final gmaps.Point bottomLeft = projection.fromLatLngToPoint!(sw)!; - final scale = 1 << (zoom!.toInt()); // 2 ^ zoom + final int scale = 1 << (zoom!.toInt()); // 2 ^ zoom - final point = + final gmaps.Point point = gmaps.Point((x / scale) + bottomLeft.x!, (y / scale) + topRight.y!); return projection.fromPointToLatLng!(point)!; 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 edf47764f346..b7e902014281 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 @@ -11,6 +11,43 @@ typedef DebugCreateMapFunction = gmaps.GMap Function( /// 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. + GoogleMapController({ + required int mapId, + required StreamController> streamController, + required CameraPosition initialCameraPosition, + Set markers = const {}, + Set polygons = const {}, + Set polylines = const {}, + Set circles = const {}, + Map mapOptions = const {}, + }) : _mapId = mapId, + _streamController = streamController, + _initialCameraPosition = initialCameraPosition, + _markers = markers, + _polygons = polygons, + _polylines = polylines, + _circles = circles, + _rawMapOptions = mapOptions { + _circlesController = CirclesController(stream: _streamController); + _polygonsController = PolygonsController(stream: _streamController); + _polylinesController = PolylinesController(stream: _streamController); + _markersController = MarkersController(stream: _streamController); + + // 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() + ..id = _getViewType(mapId) + ..style.width = '100%' + ..style.height = '100%'; + + ui.platformViewRegistry.registerViewFactory( + _getViewType(mapId), + (int viewId) => _div, + ); + } + // The internal ID of the map. Used to broadcast events, DOM IDs and everything where a unique ID is needed. final int _mapId; @@ -51,14 +88,14 @@ class GoogleMapController { gmaps.GMap? _googleMap; // The StreamController used by this controller and the geometry ones. - final StreamController _streamController; + final StreamController> _streamController; /// The StreamController for the events of this Map. Only for integration testing. @visibleForTesting - StreamController get stream => _streamController; + StreamController> get stream => _streamController; /// The Stream over which this controller broadcasts events. - Stream get events => _streamController.stream; + Stream> get events => _streamController.stream; // Geometry controllers, for different features of the map. CirclesController? _circlesController; @@ -71,46 +108,6 @@ class GoogleMapController { // Keeps track if the map is moving or not. bool _mapIsMoving = false; - /// Initializes the GMap, and the sub-controllers related to it. Wires events. - GoogleMapController({ - required int mapId, - required StreamController streamController, - required CameraPosition initialCameraPosition, - Set markers = const {}, - Set polygons = const {}, - Set polylines = const {}, - Set circles = const {}, - Set tileOverlays = const {}, - Set> gestureRecognizers = - const >{}, - Map mapOptions = const {}, - }) : _mapId = mapId, - _streamController = streamController, - _initialCameraPosition = initialCameraPosition, - _markers = markers, - _polygons = polygons, - _polylines = polylines, - _circles = circles, - _rawMapOptions = mapOptions { - _circlesController = CirclesController(stream: this._streamController); - _polygonsController = PolygonsController(stream: this._streamController); - _polylinesController = PolylinesController(stream: this._streamController); - _markersController = MarkersController(stream: this._streamController); - - // 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() - ..id = _getViewType(mapId) - ..style.width = '100%' - ..style.height = '100%'; - - ui.platformViewRegistry.registerViewFactory( - _getViewType(mapId), - (int viewId) => _div, - ); - } - /// Overrides certain properties to install mocks defined during testing. @visibleForTesting void debugSetOverrides({ @@ -161,12 +158,12 @@ class GoogleMapController { /// Failure to call this method would result in the GMap not rendering at all, /// and most of the public methods on this class no-op'ing. void init() { - var options = _rawOptionsToGmapsOptions(_rawMapOptions); + gmaps.MapOptions options = _rawOptionsToGmapsOptions(_rawMapOptions); // Initial position can only to be set here! options = _applyInitialPosition(_initialCameraPosition, options); // Create the map... - final map = _createMap(_div, options); + final gmaps.GMap map = _createMap(_div, options); _googleMap = map; _attachMapEvents(map); @@ -185,23 +182,23 @@ class GoogleMapController { // Funnels map gmap events into the plugin's stream controller. void _attachMapEvents(gmaps.GMap map) { - map.onTilesloaded.first.then((event) { + map.onTilesloaded.first.then((void _) { // Report the map as ready to go the first time the tiles load _streamController.add(WebMapReadyEvent(_mapId)); }); - map.onClick.listen((event) { + map.onClick.listen((gmaps.IconMouseEvent event) { assert(event.latLng != null); _streamController.add( MapTapEvent(_mapId, _gmLatLngToLatLng(event.latLng!)), ); }); - map.onRightclick.listen((event) { + map.onRightclick.listen((gmaps.MapMouseEvent event) { assert(event.latLng != null); _streamController.add( MapLongPressEvent(_mapId, _gmLatLngToLatLng(event.latLng!)), ); }); - map.onBoundsChanged.listen((event) { + map.onBoundsChanged.listen((void _) { if (!_mapIsMoving) { _mapIsMoving = true; _streamController.add(CameraMoveStartedEvent(_mapId)); @@ -210,7 +207,7 @@ class GoogleMapController { CameraMoveEvent(_mapId, _gmViewportToCameraPosition(map)), ); }); - map.onIdle.listen((event) { + map.onIdle.listen((void _) { _mapIsMoving = false; _streamController.add(CameraIdleEvent(_mapId)); }); @@ -243,15 +240,15 @@ class GoogleMapController { // Renders the initial sets of geometry. void _renderInitialGeometry({ - Set markers = const {}, - Set circles = const {}, - Set polygons = const {}, - Set polylines = const {}, + Set markers = const {}, + Set circles = const {}, + Set polygons = const {}, + Set polylines = const {}, }) { assert( _controllersBoundToMap, - 'Geometry controllers must be bound to a map before any geometry can ' + - 'be added to them. Ensure _attachGeometryControllers is called first.'); + 'Geometry controllers must be bound to a map before any geometry can ' + 'be added to them. Ensure _attachGeometryControllers is called first.'); // The above assert will only succeed if the controllers have been bound to a map // in the [_attachGeometryControllers] method, which ensures that all these @@ -280,13 +277,14 @@ class GoogleMapController { void updateRawOptions(Map optionsUpdate) { assert(_googleMap != null, 'Cannot update options on a null map.'); - final newOptions = _mergeRawOptions(optionsUpdate); + final Map newOptions = _mergeRawOptions(optionsUpdate); _setOptions(_rawOptionsToGmapsOptions(newOptions)); _setTrafficLayer(_googleMap!, _isTrafficLayerEnabled(newOptions)); } // Sets new [gmaps.MapOptions] on the wrapped map. + // ignore: use_setters_to_change_properties void _setOptions(gmaps.MapOptions options) { _googleMap?.options = options; } @@ -309,9 +307,11 @@ class GoogleMapController { Future getVisibleRegion() async { assert(_googleMap != null, 'Cannot get the visible region of a null map.'); - return _gmLatLngBoundsTolatLngBounds( - await _googleMap!.bounds ?? _nullGmapsLatLngBounds, - ); + final gmaps.LatLngBounds bounds = + await Future.value(_googleMap!.bounds) ?? + _nullGmapsLatLngBounds; + + return _gmLatLngBoundsTolatLngBounds(bounds); } /// Returns the [ScreenCoordinate] for a given viewport [LatLng]. @@ -319,7 +319,8 @@ class GoogleMapController { assert(_googleMap != null, 'Cannot get the screen coordinates with a null map.'); - final point = toScreenLocation(_googleMap!, _latLngToGmLatLng(latLng)); + final gmaps.Point point = + toScreenLocation(_googleMap!, _latLngToGmLatLng(latLng)); return ScreenCoordinate(x: point.x!.toInt(), y: point.y!.toInt()); } @@ -424,8 +425,8 @@ class GoogleMapController { } } -/// An event fired when a [mapId] on web is interactive. -class WebMapReadyEvent extends MapEvent { +/// A MapEvent event fired when a [mapId] on web is interactive. +class WebMapReadyEvent extends MapEvent { /// Build a WebMapReady Event for the map represented by `mapId`. WebMapReadyEvent(int mapId) : super(mapId, null); } 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 47bfdc7bba15..043952d176a0 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 @@ -14,23 +14,24 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { } // A cache of map controllers by map Id. - Map _mapById = Map(); + Map _mapById = {}; /// Allows tests to inject controllers without going through the buildView flow. @visibleForTesting + // ignore: use_setters_to_change_properties void debugSetMapById(Map mapById) { _mapById = mapById; } // Convenience getter for a stream of events filtered by their mapId. - Stream _events(int mapId) => _map(mapId).events; + Stream> _events(int mapId) => _map(mapId).events; // Convenience getter for a map controller by its mapId. GoogleMapController _map(int mapId) { - final controller = _mapById[mapId]; + final GoogleMapController? controller = _mapById[mapId]; assert(controller != null, 'Maps cannot be retrieved before calling buildView!'); - return controller; + return controller!; } @override @@ -134,7 +135,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { String? mapStyle, { required int mapId, }) async { - _map(mapId).updateRawOptions({ + _map(mapId).updateRawOptions({ 'styles': _mapStyles(mapStyle), }); } @@ -303,13 +304,13 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { }) { // Bail fast if we've already rendered this map ID... if (_mapById[creationId]?.widget != null) { - return _mapById[creationId].widget; + return _mapById[creationId]!.widget!; } - final StreamController controller = - StreamController.broadcast(); + final StreamController> controller = + StreamController>.broadcast(); - final mapController = GoogleMapController( + final GoogleMapController mapController = GoogleMapController( initialCameraPosition: initialCameraPosition, mapId: creationId, streamController: controller, @@ -322,7 +323,10 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { _mapById[creationId] = mapController; - mapController.events.whereType().first.then((event) { + mapController.events + .whereType() + .first + .then((WebMapReadyEvent event) { assert(creationId == event.mapId, 'Received WebMapReadyEvent for the wrong map'); // Notify the plugin now that there's a fully initialized controller. 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 c4cd40f43323..9d607e9bbc6a 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 @@ -6,14 +6,6 @@ part of google_maps_flutter_web; /// The `MarkerController` class wraps a [gmaps.Marker], how it handles events, and its associated (optional) [gmaps.InfoWindow] widget. class MarkerController { - gmaps.Marker? _marker; - - final bool _consumeTapEvents; - - final gmaps.InfoWindow? _infoWindow; - - bool _infoWindowShown = false; - /// Creates a `MarkerController`, which wraps a [gmaps.Marker] object, its `onTap`/`onDrag` behavior, and its associated [gmaps.InfoWindow]. MarkerController({ required gmaps.Marker marker, @@ -27,12 +19,12 @@ class MarkerController { _infoWindow = infoWindow, _consumeTapEvents = consumeTapEvents { if (onTap != null) { - marker.onClick.listen((event) { + marker.onClick.listen((gmaps.MapMouseEvent event) { onTap.call(); }); } if (onDragStart != null) { - marker.onDragstart.listen((event) { + marker.onDragstart.listen((gmaps.MapMouseEvent event) { if (marker != null) { marker.position = event.latLng; } @@ -40,7 +32,7 @@ class MarkerController { }); } if (onDrag != null) { - marker.onDrag.listen((event) { + marker.onDrag.listen((gmaps.MapMouseEvent event) { if (marker != null) { marker.position = event.latLng; } @@ -48,7 +40,7 @@ class MarkerController { }); } if (onDragEnd != null) { - marker.onDragend.listen((event) { + marker.onDragend.listen((gmaps.MapMouseEvent event) { if (marker != null) { marker.position = event.latLng; } @@ -57,6 +49,14 @@ class MarkerController { } } + gmaps.Marker? _marker; + + final bool _consumeTapEvents; + + final gmaps.InfoWindow? _infoWindow; + + bool _infoWindowShown = false; + /// Returns `true` if this Controller will use its own `onTap` handler to consume events. bool get consumeTapEvents => _consumeTapEvents; 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 542a48bcb707..1a712b109677 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 @@ -6,17 +6,17 @@ part of google_maps_flutter_web; /// This class manages a set of [MarkerController]s associated to a [GoogleMapController]. class MarkersController extends GeometryController { + /// Initialize the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. + MarkersController({ + required StreamController> stream, + }) : _streamController = stream, + _markerIdToController = {}; + // A cache of [MarkerController]s indexed by their [MarkerId]. final Map _markerIdToController; // The stream over which markers broadcast their events - StreamController _streamController; - - /// Initialize the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. - MarkersController({ - required StreamController stream, - }) : _streamController = stream, - _markerIdToController = Map(); + final StreamController> _streamController; /// Returns the cache of [MarkerController]s. Test only. @visibleForTesting @@ -34,32 +34,35 @@ class MarkersController extends GeometryController { return; } - final infoWindowOptions = _infoWindowOptionsFromMarker(marker); + final gmaps.InfoWindowOptions? infoWindowOptions = + _infoWindowOptionsFromMarker(marker); gmaps.InfoWindow? gmInfoWindow; if (infoWindowOptions != null) { gmInfoWindow = gmaps.InfoWindow(infoWindowOptions); // Google Maps' JS SDK does not have a click event on the InfoWindow, so // we make one... - if (infoWindowOptions.content is HtmlElement) { - final content = infoWindowOptions.content as HtmlElement; + if (infoWindowOptions.content != null && + infoWindowOptions.content is HtmlElement) { + final HtmlElement content = infoWindowOptions.content! as HtmlElement; content.onClick.listen((_) { _onInfoWindowTap(marker.markerId); }); } } - final currentMarker = _markerIdToController[marker.markerId]?.marker; + final gmaps.Marker? currentMarker = + _markerIdToController[marker.markerId]?.marker; - final populationOptions = _markerOptionsFromMarker(marker, currentMarker); - gmaps.Marker gmMarker = gmaps.Marker(populationOptions); - gmMarker.map = googleMap; - MarkerController controller = MarkerController( + final gmaps.MarkerOptions markerOptions = + _markerOptionsFromMarker(marker, currentMarker); + final gmaps.Marker gmMarker = gmaps.Marker(markerOptions)..map = googleMap; + final MarkerController controller = MarkerController( marker: gmMarker, infoWindow: gmInfoWindow, consumeTapEvents: marker.consumeTapEvents, onTap: () { - this.showMarkerInfoWindow(marker.markerId); + showMarkerInfoWindow(marker.markerId); _onMarkerTap(marker.markerId); }, onDragStart: (gmaps.LatLng latLng) { @@ -81,13 +84,15 @@ class MarkersController extends GeometryController { } void _changeMarker(Marker marker) { - MarkerController? markerController = _markerIdToController[marker.markerId]; + final MarkerController? markerController = + _markerIdToController[marker.markerId]; if (markerController != null) { - final markerOptions = _markerOptionsFromMarker( + final gmaps.MarkerOptions markerOptions = _markerOptionsFromMarker( marker, markerController.marker, ); - final infoWindow = _infoWindowOptionsFromMarker(marker); + final gmaps.InfoWindowOptions? infoWindow = + _infoWindowOptionsFromMarker(marker); markerController.update( markerOptions, newInfoWindowContent: infoWindow?.content as HtmlElement?, @@ -113,7 +118,7 @@ class MarkersController extends GeometryController { /// See also [hideMarkerInfoWindow] and [isInfoWindowShown]. void showMarkerInfoWindow(MarkerId markerId) { _hideAllMarkerInfoWindow(); - MarkerController? markerController = _markerIdToController[markerId]; + final MarkerController? markerController = _markerIdToController[markerId]; markerController?.showInfoWindow(); } @@ -121,7 +126,7 @@ class MarkersController extends GeometryController { /// /// See also [showMarkerInfoWindow] and [isInfoWindowShown]. void hideMarkerInfoWindow(MarkerId markerId) { - MarkerController? markerController = _markerIdToController[markerId]; + final MarkerController? markerController = _markerIdToController[markerId]; markerController?.hideInfoWindow(); } @@ -129,7 +134,7 @@ class MarkersController extends GeometryController { /// /// See also [showMarkerInfoWindow] and [hideMarkerInfoWindow]. bool isInfoWindowShown(MarkerId markerId) { - MarkerController? markerController = _markerIdToController[markerId]; + final MarkerController? markerController = _markerIdToController[markerId]; return markerController?.infoWindowShown ?? false; } @@ -172,8 +177,10 @@ class MarkersController extends GeometryController { void _hideAllMarkerInfoWindow() { _markerIdToController.values - .where((controller) => - controller == null ? false : controller.infoWindowShown) - .forEach((controller) => controller.hideInfoWindow()); + .where((MarkerController? controller) => + controller?.infoWindowShown ?? false) + .forEach((MarkerController controller) { + controller.hideInfoWindow(); + }); } } diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart index 9921d2ff3876..719eeeecdb43 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygon.dart @@ -6,10 +6,6 @@ part of google_maps_flutter_web; /// The `PolygonController` class wraps a [gmaps.Polygon] and its `onTap` behavior. class PolygonController { - gmaps.Polygon? _polygon; - - final bool _consumeTapEvents; - /// Creates a `PolygonController` that wraps a [gmaps.Polygon] object and its `onTap` behavior. PolygonController({ required gmaps.Polygon polygon, @@ -18,12 +14,16 @@ class PolygonController { }) : _polygon = polygon, _consumeTapEvents = consumeTapEvents { if (onTap != null) { - polygon.onClick.listen((event) { + polygon.onClick.listen((gmaps.PolyMouseEvent event) { onTap.call(); }); } } + gmaps.Polygon? _polygon; + + final bool _consumeTapEvents; + /// Returns the wrapped [gmaps.Polygon]. Only used for testing. @visibleForTesting gmaps.Polygon? get polygon => _polygon; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart index 8a9643156351..12e378cfc59c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polygons.dart @@ -6,17 +6,17 @@ part of google_maps_flutter_web; /// This class manages a set of [PolygonController]s associated to a [GoogleMapController]. class PolygonsController extends GeometryController { + /// Initializes the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. + PolygonsController({ + required StreamController> stream, + }) : _streamController = stream, + _polygonIdToController = {}; + // A cache of [PolygonController]s indexed by their [PolygonId]. final Map _polygonIdToController; // The stream over which polygons broadcast events - StreamController _streamController; - - /// Initializes the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. - PolygonsController({ - required StreamController stream, - }) : _streamController = stream, - _polygonIdToController = Map(); + final StreamController> _streamController; /// Returns the cache of [PolygonController]s. Test only. @visibleForTesting @@ -27,9 +27,7 @@ class PolygonsController extends GeometryController { /// Wraps each Polygon into its corresponding [PolygonController]. void addPolygons(Set polygonsToAdd) { if (polygonsToAdd != null) { - polygonsToAdd.forEach((polygon) { - _addPolygon(polygon); - }); + polygonsToAdd.forEach(_addPolygon); } } @@ -38,10 +36,11 @@ class PolygonsController extends GeometryController { return; } - final populationOptions = _polygonOptionsFromPolygon(googleMap, polygon); - gmaps.Polygon gmPolygon = gmaps.Polygon(populationOptions); - gmPolygon.map = googleMap; - PolygonController controller = PolygonController( + final gmaps.PolygonOptions polygonOptions = + _polygonOptionsFromPolygon(googleMap, polygon); + final gmaps.Polygon gmPolygon = gmaps.Polygon(polygonOptions) + ..map = googleMap; + final PolygonController controller = PolygonController( polygon: gmPolygon, consumeTapEvents: polygon.consumeTapEvents, onTap: () { @@ -53,26 +52,27 @@ class PolygonsController extends GeometryController { /// Updates a set of [Polygon] objects with new options. void changePolygons(Set polygonsToChange) { if (polygonsToChange != null) { - polygonsToChange.forEach((polygonToChange) { - _changePolygon(polygonToChange); - }); + polygonsToChange.forEach(_changePolygon); } } void _changePolygon(Polygon polygon) { - PolygonController? polygonController = + final PolygonController? polygonController = _polygonIdToController[polygon.polygonId]; polygonController?.update(_polygonOptionsFromPolygon(googleMap, polygon)); } /// Removes a set of [PolygonId]s from the cache. void removePolygons(Set polygonIdsToRemove) { - polygonIdsToRemove.forEach((polygonId) { - final PolygonController? polygonController = - _polygonIdToController[polygonId]; - polygonController?.remove(); - _polygonIdToController.remove(polygonId); - }); + polygonIdsToRemove.forEach(_removePolygon); + } + + // Removes a polygon and its controller by its [PolygonId]. + void _removePolygon(PolygonId polygonId) { + final PolygonController? polygonController = + _polygonIdToController[polygonId]; + polygonController?.remove(); + _polygonIdToController.remove(polygonId); } // Handle internal events diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart index eb4b6d88b503..428bb7fce016 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polyline.dart @@ -6,10 +6,6 @@ part of google_maps_flutter_web; /// The `PolygonController` class wraps a [gmaps.Polyline] and its `onTap` behavior. class PolylineController { - gmaps.Polyline? _polyline; - - final bool _consumeTapEvents; - /// Creates a `PolylineController` that wraps a [gmaps.Polyline] object and its `onTap` behavior. PolylineController({ required gmaps.Polyline polyline, @@ -18,12 +14,16 @@ class PolylineController { }) : _polyline = polyline, _consumeTapEvents = consumeTapEvents { if (onTap != null) { - polyline.onClick.listen((event) { + polyline.onClick.listen((gmaps.PolyMouseEvent event) { onTap.call(); }); } } + gmaps.Polyline? _polyline; + + final bool _consumeTapEvents; + /// Returns the wrapped [gmaps.Polyline]. Only used for testing. @visibleForTesting gmaps.Polyline? get line => _polyline; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart index 695b29554c04..2d3f1618b42c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/polylines.dart @@ -6,17 +6,17 @@ part of google_maps_flutter_web; /// This class manages a set of [PolylinesController]s associated to a [GoogleMapController]. class PolylinesController extends GeometryController { + /// Initializes the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. + PolylinesController({ + required StreamController> stream, + }) : _streamController = stream, + _polylineIdToController = {}; + // A cache of [PolylineController]s indexed by their [PolylineId]. final Map _polylineIdToController; // The stream over which polylines broadcast their events - StreamController _streamController; - - /// Initializes the cache. The [StreamController] comes from the [GoogleMapController], and is shared with other controllers. - PolylinesController({ - required StreamController stream, - }) : _streamController = stream, - _polylineIdToController = Map(); + final StreamController> _streamController; /// Returns the cache of [PolylineContrller]s. Test only. @visibleForTesting @@ -26,9 +26,7 @@ class PolylinesController extends GeometryController { /// /// Wraps each line into its corresponding [PolylineController]. void addPolylines(Set polylinesToAdd) { - polylinesToAdd.forEach((polyline) { - _addPolyline(polyline); - }); + polylinesToAdd.forEach(_addPolyline); } void _addPolyline(Polyline polyline) { @@ -36,10 +34,11 @@ class PolylinesController extends GeometryController { return; } - final polylineOptions = _polylineOptionsFromPolyline(googleMap, polyline); - gmaps.Polyline gmPolyline = gmaps.Polyline(polylineOptions); - gmPolyline.map = googleMap; - PolylineController controller = PolylineController( + final gmaps.PolylineOptions polylineOptions = + _polylineOptionsFromPolyline(googleMap, polyline); + final gmaps.Polyline gmPolyline = gmaps.Polyline(polylineOptions) + ..map = googleMap; + final PolylineController controller = PolylineController( polyline: gmPolyline, consumeTapEvents: polyline.consumeTapEvents, onTap: () { @@ -50,13 +49,11 @@ class PolylinesController extends GeometryController { /// Updates a set of [Polyline] objects with new options. void changePolylines(Set polylinesToChange) { - polylinesToChange.forEach((polylineToChange) { - _changePolyline(polylineToChange); - }); + polylinesToChange.forEach(_changePolyline); } void _changePolyline(Polyline polyline) { - PolylineController? polylineController = + final PolylineController? polylineController = _polylineIdToController[polyline.polylineId]; polylineController ?.update(_polylineOptionsFromPolyline(googleMap, polyline)); @@ -64,12 +61,15 @@ class PolylinesController extends GeometryController { /// Removes a set of [PolylineId]s from the cache. void removePolylines(Set polylineIdsToRemove) { - polylineIdsToRemove.forEach((polylineId) { - final PolylineController? polylineController = - _polylineIdToController[polylineId]; - polylineController?.remove(); - _polylineIdToController.remove(polylineId); - }); + polylineIdsToRemove.forEach(_removePolyline); + } + + // Removes a polyline and its controller by its [PolylineId]. + void _removePolyline(PolylineId polylineId) { + final PolylineController? polylineController = + _polylineIdToController[polylineId]; + polylineController?.remove(); + _polylineIdToController.remove(polylineId); } // Handle internal events diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui.dart index 5eacec5fe867..2b254a95b951 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui.dart @@ -5,6 +5,6 @@ /// This file shims dart:ui in web-only scenarios, getting rid of the need to /// suppress analyzer warnings. -// TODO(flutter/flutter#55000) Remove this file once web-only dart:ui APIs +// TODO(ditman): Remove this file once web-only dart:ui APIs, https://github.com/flutter/flutter/issues/55000 // are exposed from a dedicated place. export 'dart_ui_fake.dart' if (dart.library.html) 'dart_ui_real.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui_fake.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui_fake.dart index f2862af8b704..8757ca22be17 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui_fake.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui_fake.dart @@ -7,13 +7,18 @@ import 'dart:html' as html; // Fake interface for the logic that this package needs from (web-only) dart:ui. // This is conditionally exported so the analyzer sees these methods as available. +// ignore_for_file: avoid_classes_with_only_static_members +// ignore_for_file: camel_case_types + /// Shim for web_ui engine.PlatformViewRegistry /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L62 class platformViewRegistry { /// Shim for registerViewFactory /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L72 - static registerViewFactory( - String viewTypeId, html.Element Function(int viewId) viewFactory) {} + static bool registerViewFactory( + String viewTypeId, html.Element Function(int viewId) viewFactory) { + return false; + } } /// Shim for web_ui engine.AssetManager. @@ -21,7 +26,7 @@ class platformViewRegistry { class webOnlyAssetManager { /// Shim for getAssetUrl. /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L45 - static getAssetUrl(String asset) {} + static String getAssetUrl(String asset) => ''; } /// Signature of callbacks that have no arguments and return no data. diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/third_party/to_screen_location/to_screen_location.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/third_party/to_screen_location/to_screen_location.dart index 2963111fdcc3..fc25b18b43ec 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/third_party/to_screen_location/to_screen_location.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/third_party/to_screen_location/to_screen_location.dart @@ -29,9 +29,9 @@ import 'package:google_maps/google_maps.dart' as gmaps; /// /// See: https://developers.google.com/maps/documentation/android-sdk/reference/com/google/android/libraries/maps/Projection#public-point-toscreenlocation-latlng-location gmaps.Point toScreenLocation(gmaps.GMap map, gmaps.LatLng coords) { - final zoom = map.zoom; - final bounds = map.bounds; - final projection = map.projection; + final num? zoom = map.zoom; + final gmaps.LatLngBounds? bounds = map.bounds; + final gmaps.Projection? projection = map.projection; assert( bounds != null, 'Map Bounds required to compute screen x/y of LatLng.'); @@ -40,15 +40,15 @@ gmaps.Point toScreenLocation(gmaps.GMap map, gmaps.LatLng coords) { assert(zoom != null, 'Current map zoom level required to compute screen x/y of LatLng.'); - final ne = bounds!.northEast; - final sw = bounds.southWest; + final gmaps.LatLng ne = bounds!.northEast; + final gmaps.LatLng sw = bounds.southWest; - final topRight = projection!.fromLatLngToPoint!(ne)!; - final bottomLeft = projection.fromLatLngToPoint!(sw)!; + final gmaps.Point topRight = projection!.fromLatLngToPoint!(ne)!; + final gmaps.Point bottomLeft = projection.fromLatLngToPoint!(sw)!; - final scale = 1 << (zoom!.toInt()); // 2 ^ zoom + final int scale = 1 << (zoom!.toInt()); // 2 ^ zoom - final worldPoint = projection.fromLatLngToPoint!(coords)!; + final gmaps.Point worldPoint = projection.fromLatLngToPoint!(coords)!; return gmaps.Point( ((worldPoint.x! - bottomLeft.x!) * scale).toInt(), diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/types.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/types.dart index ff980eb4c34b..84c66264db7b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/types.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/types.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; import 'package:google_maps/google_maps.dart' as gmaps; +import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; /// A void function that handles a [gmaps.LatLng] as a parameter. /// 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 ca8af82dc2db..b46f7561e78d 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/plugins/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.3.2+2 +version: 0.3.3 environment: sdk: ">=2.12.0 <3.0.0" @@ -21,9 +21,8 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter + google_maps: ^6.1.0 google_maps_flutter_platform_interface: ^2.1.2 - google_maps: ^5.2.0 - pedantic: ^1.10.0 sanitize_html: ^2.0.0 stream_transform: ^2.0.0 diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index 23b7f7bbc35b..bcd7d37dea0a 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -12,4 +12,3 @@ # TODO(ecosystem): Remove everything from this list. See: # https://github.com/flutter/flutter/issues/76229 - google_maps_flutter/google_maps_flutter_platform_interface -- google_maps_flutter/google_maps_flutter_web From 5273dab486eec097cf82a075425ddfcc7fba3932 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 19 May 2022 21:23:11 -0400 Subject: [PATCH 337/844] [path_provider] Fix integration tests on macOS (#5773) --- packages/path_provider/path_provider/CHANGELOG.md | 4 ++++ .../example/integration_test/path_provider_test.dart | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index 990021671801..0567b7e7ae33 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes integration test permission issue on recent versions of macOS. + ## 2.0.10 * Removes unnecessary imports. diff --git a/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart b/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart index 27493a76012c..4f5a1873bfb8 100644 --- a/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart +++ b/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart @@ -92,7 +92,14 @@ void main() { expect(result, throwsA(isInstanceOf())); } else { final Directory? result = await getDownloadsDirectory(); - _verifySampleFile(result, 'downloads'); + if (Platform.isMacOS) { + // On recent versions of macOS, actually using the downloads directory + // requires a user prompt, so will fail on CI. Instead, just check that + // it returned a path with the expected directory name. + expect(result?.path, endsWith('Downloads')); + } else { + _verifySampleFile(result, 'downloads'); + } } }); } From f4ca73237db83f6ce207269d3000f58ad5b39000 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 20 May 2022 09:28:07 -0400 Subject: [PATCH 338/844] [video_player] Fix order-dependent tests (#5672) --- packages/video_player/video_player/CHANGELOG.md | 4 ++++ .../video_player/video_player/test/video_player_test.dart | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 1dbc4f73e9c2..bfefb9902768 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes order-dependent unit tests. + ## 2.4.2 * Minor fixes for new analysis options. diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index 9eca7f921acb..83a49882ee9a 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -958,6 +958,13 @@ void main() { }); group('VideoPlayerOptions', () { + late FakeVideoPlayerPlatform fakeVideoPlayerPlatform; + + setUp(() { + fakeVideoPlayerPlatform = FakeVideoPlayerPlatform(); + VideoPlayerPlatform.instance = fakeVideoPlayerPlatform; + }); + test('setMixWithOthers', () async { final VideoPlayerController controller = VideoPlayerController.file( File(''), From aaaa81c67f8f3a2ecb4ee0e2f00929787c446464 Mon Sep 17 00:00:00 2001 From: Ahmed Ashour Date: Fri, 20 May 2022 17:28:11 +0200 Subject: [PATCH 339/844] [google_sign_in] Suppress `deprecation` warnings (#5049) --- packages/google_sign_in/google_sign_in_android/CHANGELOG.md | 4 ++++ .../io/flutter/plugins/googlesignin/GoogleSignInPlugin.java | 3 +++ packages/google_sign_in/google_sign_in_android/pubspec.yaml | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) 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 90069d05f442..67fb08f66e17 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.2.8 + +* Suppresses `deprecation` warnings (for using Android V1 embedding). + ## 5.2.7 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java index a1237f0013a1..9bee8fad38d3 100644 --- a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java +++ b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java @@ -76,6 +76,7 @@ public void initInstance( } @VisibleForTesting + @SuppressWarnings("deprecation") public void setUpRegistrar(PluginRegistry.Registrar registrar) { delegate.setUpRegistrar(registrar); } @@ -267,6 +268,7 @@ public static class Delegate implements IDelegate, PluginRegistry.ActivityResult private final Context context; // Only set registrar for v1 embedder. + @SuppressWarnings("deprecation") private PluginRegistry.Registrar registrar; // Only set activity for v2 embedder. Always access activity from getActivity() method. private Activity activity; @@ -282,6 +284,7 @@ public Delegate(Context context, GoogleSignInWrapper googleSignInWrapper) { this.googleSignInWrapper = googleSignInWrapper; } + @SuppressWarnings("deprecation") public void setUpRegistrar(PluginRegistry.Registrar registrar) { this.registrar = registrar; registrar.addActivityResultListener(this); 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 d9b272320694..c4c30cd3e545 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/plugins/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: 5.2.7 +version: 5.2.8 environment: sdk: ">=2.14.0 <3.0.0" From 20669497505c1799c11443c938228a593b33cd48 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 20 May 2022 21:43:09 -0400 Subject: [PATCH 340/844] Roll Flutter from a4a8e73bce15 to 1e1f4bcfb561 (65 revisions) (#5795) --- .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 822ceaa4a9b1..a77e197cf665 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a4a8e73bce152ab39d6ae839ca51e447f87293fa +1e1f4bcfb56105912a90ffa1a3a317dfb724612b From 7f349dc9915cc29c4af6f185da5923933db19955 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 21 May 2022 05:18:08 -0400 Subject: [PATCH 341/844] Roll Flutter from 1e1f4bcfb561 to 1e10ceceb036 (6 revisions) (#5799) --- .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 a77e197cf665..10fe2447e178 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1e1f4bcfb56105912a90ffa1a3a317dfb724612b +1e10ceceb036fbd24774937b924741d0cdee1d6f From ea56ff0d58153ec5e2ac8b84d113c73607d6d2af Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 21 May 2022 08:28:08 -0400 Subject: [PATCH 342/844] Roll Flutter from 1e10ceceb036 to 6aaabf6d5695 (1 revision) (#5800) --- .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 10fe2447e178..5821f2e5f8b2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1e10ceceb036fbd24774937b924741d0cdee1d6f +6aaabf6d569543b982b1d23ac3bf81d3b2667751 From 9459549522e8a2e740d17a3d66bb216843b01b58 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 21 May 2022 10:48:08 -0400 Subject: [PATCH 343/844] Roll Flutter from 6aaabf6d5695 to 4654fd011d13 (2 revisions) (#5802) --- .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 5821f2e5f8b2..d8897d8f416f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -6aaabf6d569543b982b1d23ac3bf81d3b2667751 +4654fd011d13c1d49d5fc21b60a5916ca8c4f2e3 From 407f4e30ab777229864c1122b1cbc4527cdc3db4 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 21 May 2022 14:58:08 -0400 Subject: [PATCH 344/844] Roll Flutter from 4654fd011d13 to b8b0c807bbe5 (1 revision) (#5803) --- .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 d8897d8f416f..1e26cf421732 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -4654fd011d13c1d49d5fc21b60a5916ca8c4f2e3 +b8b0c807bbe54718c35d17c8df8afc712cef7ec2 From f7052de7bd0c5fcb87cb22acad7e69c2aa62e262 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 21 May 2022 16:03:08 -0400 Subject: [PATCH 345/844] Roll Flutter from b8b0c807bbe5 to de7c23e7e963 (1 revision) (#5804) --- .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 1e26cf421732..1938f7f8a7c9 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b8b0c807bbe54718c35d17c8df8afc712cef7ec2 +de7c23e7e963265b6b10e785f26c83ef4df9168a From e360a5fe4da8756a8a59c13dc45a6a4decefa6b6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 21 May 2022 17:33:07 -0400 Subject: [PATCH 346/844] Roll Flutter from de7c23e7e963 to ec20ea80ad98 (1 revision) (#5805) --- .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 1938f7f8a7c9..7e7222ac9f95 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -de7c23e7e963265b6b10e785f26c83ef4df9168a +ec20ea80ad98007bd022648168e7b3cc38b2e665 From ef9effec50812aaae75f189c43ec768ae7b3a0b6 Mon Sep 17 00:00:00 2001 From: Hwanseok Barth Kang Date: Mon, 23 May 2022 15:53:05 +0900 Subject: [PATCH 347/844] [google_sign_in, in_app_purchase_android] Add availability to mock models (#5642) --- AUTHORS | 1 + .../google_sign_in/CHANGELOG.md | 3 +- .../google_sign_in/lib/google_sign_in.dart | 2 +- .../google_sign_in/pubspec.yaml | 2 +- .../test/google_sign_in_test.dart | 5 +- .../in_app_purchase_android/CHANGELOG.md | 4 ++ .../sku_details_wrapper.dart | 6 +-- .../in_app_purchase_android/pubspec.yaml | 3 +- .../sku_details_wrapper_test.dart | 54 +++++++++++++++++++ 9 files changed, 72 insertions(+), 8 deletions(-) diff --git a/AUTHORS b/AUTHORS index 41a31ed39cd4..31402c79d54a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -67,3 +67,4 @@ Rahul Raj <64.rahulraj@gmail.com> Daniel Roek TheOneWithTheBraid Rulong Chen(陈汝龙) +Hwanseok Kang diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 86a3b565da21..ad21ee245359 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 5.3.2 +* Enables mocking models by changing overridden operator == parameter type from `dynamic` to `Object`. * Updates tests to use a mock platform instead of relying on default method channel implementation internals. * Removes example workaround to build for arm64 iOS simulators. diff --git a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart index 228f34b651c5..3c62e0e1a655 100644 --- a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart +++ b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart @@ -129,7 +129,7 @@ class GoogleSignInAccount implements GoogleIdentity { } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { if (identical(this, other)) { return true; } diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 2a287b1cccd4..6862a5560f83 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/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 and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.3.1 +version: 5.3.2 environment: diff --git a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart index 61acfd81bf09..2bc51b63d111 100644 --- a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart @@ -9,9 +9,12 @@ import 'package:google_sign_in/google_sign_in.dart'; import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; - import 'google_sign_in_test.mocks.dart'; +/// Verify that [GoogleSignInAccount] can be mocked even though it's unused +// ignore: must_be_immutable +class MockGoogleSignInAccount extends Mock implements GoogleSignInAccount {} + @GenerateMocks([GoogleSignInPlatform]) void main() { late MockGoogleSignInPlatform mockPlatform; 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 18284f29a2d9..05c2952fc615 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.2+6 + +* Enables mocking models by changing overridden operator == parameter type from `dynamic` to `Object`. + ## 0.2.2+5 * Minor fixes for new analysis options. diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart index 07f9d8f29abf..1c5c2d1fcee9 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/sku_details_wrapper.dart @@ -135,7 +135,7 @@ class SkuDetailsWrapper { final int originalPriceAmountMicros; @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } @@ -203,7 +203,7 @@ class SkuDetailsResponseWrapper { final List skuDetailsList; @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } @@ -248,7 +248,7 @@ class BillingResultWrapper { final String? debugMessage; @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } 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 277be296836c..1099c3a12bfa 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/plugins/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.2.2+5 +version: 0.2.2+6 environment: sdk: ">=2.14.0 <3.0.0" @@ -28,4 +28,5 @@ dev_dependencies: flutter_test: sdk: flutter json_serializable: ^6.0.0 + mockito: ^5.1.0 test: ^1.16.0 diff --git a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/sku_details_wrapper_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/sku_details_wrapper_test.dart index ecc399b27716..2d1436885427 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/sku_details_wrapper_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/sku_details_wrapper_test.dart @@ -125,6 +125,60 @@ void main() { expect(billingResult.debugMessage, kInvalidBillingResultErrorMessage); expect(billingResult.responseCode, BillingResponse.error); }); + + test('operator == of SkuDetailsWrapper works fine', () { + const SkuDetailsWrapper firstSkuDetailsInstance = SkuDetailsWrapper( + description: 'description', + freeTrialPeriod: 'freeTrialPeriod', + introductoryPrice: 'introductoryPrice', + introductoryPriceAmountMicros: 990000, + introductoryPriceCycles: 1, + introductoryPricePeriod: 'introductoryPricePeriod', + price: 'price', + priceAmountMicros: 1000, + priceCurrencyCode: 'priceCurrencyCode', + priceCurrencySymbol: r'$', + sku: 'sku', + subscriptionPeriod: 'subscriptionPeriod', + title: 'title', + type: SkuType.inapp, + originalPrice: 'originalPrice', + originalPriceAmountMicros: 1000, + ); + const SkuDetailsWrapper secondSkuDetailsInstance = SkuDetailsWrapper( + description: 'description', + freeTrialPeriod: 'freeTrialPeriod', + introductoryPrice: 'introductoryPrice', + introductoryPriceAmountMicros: 990000, + introductoryPriceCycles: 1, + introductoryPricePeriod: 'introductoryPricePeriod', + price: 'price', + priceAmountMicros: 1000, + priceCurrencyCode: 'priceCurrencyCode', + priceCurrencySymbol: r'$', + sku: 'sku', + subscriptionPeriod: 'subscriptionPeriod', + title: 'title', + type: SkuType.inapp, + originalPrice: 'originalPrice', + originalPriceAmountMicros: 1000, + ); + expect(firstSkuDetailsInstance == secondSkuDetailsInstance, isTrue); + }); + + test('operator == of BillingResultWrapper works fine', () { + const BillingResultWrapper firstBillingResultInstance = + BillingResultWrapper( + responseCode: BillingResponse.ok, + debugMessage: 'debugMessage', + ); + const BillingResultWrapper secondBillingResultInstance = + BillingResultWrapper( + responseCode: BillingResponse.ok, + debugMessage: 'debugMessage', + ); + expect(firstBillingResultInstance == secondBillingResultInstance, isTrue); + }); }); } From bbae271cffde3e81850a76acab702ec31b70cbed Mon Sep 17 00:00:00 2001 From: Martin Georgiu Date: Mon, 23 May 2022 18:48:09 +0200 Subject: [PATCH 348/844] [Camera] Return all possible cameras on iOS (#5636) --- packages/camera/camera/CHANGELOG.md | 4 + .../ios/Runner.xcodeproj/project.pbxproj | 4 + .../ios/RunnerTests/AvailableCamerasTest.m | 121 ++++++++++++++++++ .../camera/camera/ios/Classes/CameraPlugin.m | 8 +- packages/camera/camera/pubspec.yaml | 2 +- 5 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 packages/camera/camera/example/ios/RunnerTests/AvailableCamerasTest.m diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 9af23011ec50..bd42ef441287 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.7 + +* Returns all the available cameras on iOS. + ## 0.9.6 * Adds audio access permission handling logic on iOS to fix an issue with `prepareForVideoRecording` not awaiting for the audio permission request result. diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index b5187d5dd1fa..03c80d79c578 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 25C3919135C3D981E6F800D0 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */; }; 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 43ED1537282570DE00EB00DE /* AvailableCamerasTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */; }; 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 788A065927B0E02900533D74 /* StreamingTest.m */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; @@ -71,6 +72,7 @@ 14AE82C910C2A12F2ECB2094 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AvailableCamerasTest.m; sourceTree = ""; }; 59848A7CA98C1FADF8840207 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 788A065927B0E02900533D74 /* StreamingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StreamingTest.m; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -148,6 +150,7 @@ E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */, E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */, 788A065927B0E02900533D74 /* StreamingTest.m */, + 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */, ); path = RunnerTests; sourceTree = ""; @@ -418,6 +421,7 @@ E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */, E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */, E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */, + 43ED1537282570DE00EB00DE /* AvailableCamerasTest.m in Sources */, F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */, E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */, 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */, diff --git a/packages/camera/camera/example/ios/RunnerTests/AvailableCamerasTest.m b/packages/camera/camera/example/ios/RunnerTests/AvailableCamerasTest.m new file mode 100644 index 000000000000..2caac0a03891 --- /dev/null +++ b/packages/camera/camera/example/ios/RunnerTests/AvailableCamerasTest.m @@ -0,0 +1,121 @@ +// 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; +@import camera.Test; +@import XCTest; +@import AVFoundation; +#import +#import "MockFLTThreadSafeFlutterResult.h" + +@interface AvailableCamerasTest : XCTestCase +@end + +@implementation AvailableCamerasTest + +- (void)testAvailableCamerasShouldReturnAllCamerasOnMultiCameraIPhone { + CameraPlugin *camera = [[CameraPlugin alloc] initWithRegistry:nil messenger:nil]; + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Result finished"]; + + // iPhone 13 Cameras: + AVCaptureDevice *wideAngleCamera = OCMClassMock([AVCaptureDevice class]); + OCMStub([wideAngleCamera uniqueID]).andReturn(@"0"); + OCMStub([wideAngleCamera position]).andReturn(AVCaptureDevicePositionBack); + + AVCaptureDevice *frontFacingCamera = OCMClassMock([AVCaptureDevice class]); + OCMStub([frontFacingCamera uniqueID]).andReturn(@"1"); + OCMStub([frontFacingCamera position]).andReturn(AVCaptureDevicePositionFront); + + AVCaptureDevice *ultraWideCamera = OCMClassMock([AVCaptureDevice class]); + OCMStub([ultraWideCamera uniqueID]).andReturn(@"2"); + OCMStub([ultraWideCamera position]).andReturn(AVCaptureDevicePositionBack); + + AVCaptureDevice *telephotoCamera = OCMClassMock([AVCaptureDevice class]); + OCMStub([telephotoCamera uniqueID]).andReturn(@"3"); + OCMStub([telephotoCamera position]).andReturn(AVCaptureDevicePositionBack); + + NSMutableArray *requiredTypes = + [@[ AVCaptureDeviceTypeBuiltInWideAngleCamera, AVCaptureDeviceTypeBuiltInTelephotoCamera ] + mutableCopy]; + if (@available(iOS 13.0, *)) { + [requiredTypes addObject:AVCaptureDeviceTypeBuiltInUltraWideCamera]; + } + + id discoverySessionMock = OCMClassMock([AVCaptureDeviceDiscoverySession class]); + OCMStub([discoverySessionMock discoverySessionWithDeviceTypes:requiredTypes + mediaType:AVMediaTypeVideo + position:AVCaptureDevicePositionUnspecified]) + .andReturn(discoverySessionMock); + + NSMutableArray *cameras = [NSMutableArray array]; + [cameras addObjectsFromArray:@[ wideAngleCamera, frontFacingCamera, telephotoCamera ]]; + if (@available(iOS 13.0, *)) { + [cameras addObject:ultraWideCamera]; + } + OCMStub([discoverySessionMock devices]).andReturn([NSArray arrayWithArray:cameras]); + + MockFLTThreadSafeFlutterResult *resultObject = + [[MockFLTThreadSafeFlutterResult alloc] initWithExpectation:expectation]; + + // Set up method call + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"availableCameras" + arguments:nil]; + + [camera handleMethodCallAsync:call result:resultObject]; + + // Verify the result + NSDictionary *dictionaryResult = (NSDictionary *)resultObject.receivedResult; + if (@available(iOS 13.0, *)) { + XCTAssertTrue([dictionaryResult count] == 4); + } else { + XCTAssertTrue([dictionaryResult count] == 3); + } +} +- (void)testAvailableCamerasShouldReturnOneCameraOnSingleCameraIPhone { + CameraPlugin *camera = [[CameraPlugin alloc] initWithRegistry:nil messenger:nil]; + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Result finished"]; + + // iPhone 8 Cameras: + AVCaptureDevice *wideAngleCamera = OCMClassMock([AVCaptureDevice class]); + OCMStub([wideAngleCamera uniqueID]).andReturn(@"0"); + OCMStub([wideAngleCamera position]).andReturn(AVCaptureDevicePositionBack); + + AVCaptureDevice *frontFacingCamera = OCMClassMock([AVCaptureDevice class]); + OCMStub([frontFacingCamera uniqueID]).andReturn(@"1"); + OCMStub([frontFacingCamera position]).andReturn(AVCaptureDevicePositionFront); + + NSMutableArray *requiredTypes = + [@[ AVCaptureDeviceTypeBuiltInWideAngleCamera, AVCaptureDeviceTypeBuiltInTelephotoCamera ] + mutableCopy]; + if (@available(iOS 13.0, *)) { + [requiredTypes addObject:AVCaptureDeviceTypeBuiltInUltraWideCamera]; + } + + id discoverySessionMock = OCMClassMock([AVCaptureDeviceDiscoverySession class]); + OCMStub([discoverySessionMock discoverySessionWithDeviceTypes:requiredTypes + mediaType:AVMediaTypeVideo + position:AVCaptureDevicePositionUnspecified]) + .andReturn(discoverySessionMock); + + NSMutableArray *cameras = [NSMutableArray array]; + [cameras addObjectsFromArray:@[ wideAngleCamera, frontFacingCamera ]]; + OCMStub([discoverySessionMock devices]).andReturn([NSArray arrayWithArray:cameras]); + + MockFLTThreadSafeFlutterResult *resultObject = + [[MockFLTThreadSafeFlutterResult alloc] initWithExpectation:expectation]; + + // Set up method call + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"availableCameras" + arguments:nil]; + + [camera handleMethodCallAsync:call result:resultObject]; + + // Verify the result + NSDictionary *dictionaryResult = (NSDictionary *)resultObject.receivedResult; + XCTAssertTrue([dictionaryResult count] == 2); +} + +@end diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera/ios/Classes/CameraPlugin.m index 64952e8d70f1..90327e35e187 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera/ios/Classes/CameraPlugin.m @@ -101,8 +101,14 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call result:(FLTThreadSafeFlutterResult *)result { if ([@"availableCameras" isEqualToString:call.method]) { if (@available(iOS 10.0, *)) { + NSMutableArray *discoveryDevices = + [@[ AVCaptureDeviceTypeBuiltInWideAngleCamera, AVCaptureDeviceTypeBuiltInTelephotoCamera ] + mutableCopy]; + if (@available(iOS 13.0, *)) { + [discoveryDevices addObject:AVCaptureDeviceTypeBuiltInUltraWideCamera]; + } AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession - discoverySessionWithDeviceTypes:@[ AVCaptureDeviceTypeBuiltInWideAngleCamera ] + discoverySessionWithDeviceTypes:discoveryDevices mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified]; NSArray *devices = discoverySession.devices; diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index aca6bebbe066..ea9f2e036161 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.6 +version: 0.9.7 environment: sdk: ">=2.14.0 <3.0.0" From 99aa799ea07ac717e9fc3205b22c7cb00cb528f3 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 23 May 2022 14:08:08 -0400 Subject: [PATCH 349/844] [google_maps_flutter] Updates platform interface to new analysis options (#5793) --- analysis_options.yaml | 4 - analysis_options_legacy.yaml | 14 --- .../CHANGELOG.md | 3 +- .../analysis_options.yaml | 1 - ...oogle_maps_flutter_platform_interface.dart | 2 +- .../lib/src/events/map_event.dart | 18 ++-- .../method_channel_google_maps_flutter.dart | 57 +++++----- .../google_maps_flutter_platform.dart | 11 +- .../lib/src/types/bitmap.dart | 101 +++++++++--------- .../lib/src/types/callbacks.dart | 6 +- .../lib/src/types/camera.dart | 29 +++-- .../lib/src/types/circle.dart | 32 +++--- .../lib/src/types/location.dart | 32 +++--- .../lib/src/types/maps_object.dart | 11 +- .../lib/src/types/maps_object_updates.dart | 14 +-- .../lib/src/types/marker.dart | 55 ++++++---- .../lib/src/types/polygon.dart | 34 +++--- .../lib/src/types/polyline.dart | 38 ++++--- .../lib/src/types/screen_coordinate.dart | 12 +-- .../lib/src/types/tile_overlay.dart | 6 +- .../lib/src/types/types.dart | 11 +- .../lib/src/types/ui.dart | 28 +++-- .../lib/src/types/utils/maps_object.dart | 9 +- .../pubspec.yaml | 2 +- ...thod_channel_google_maps_flutter_test.dart | 55 +++++----- .../google_maps_flutter_platform_test.dart | 8 +- .../test/types/bitmap_test.dart | 67 ++++++------ .../test/types/camera_test.dart | 9 +- .../test/types/location_test.dart | 30 +++--- .../test/types/maps_object_test.dart | 6 +- .../test/types/maps_object_updates_test.dart | 68 +++++++----- .../test/types/marker_test.dart | 70 ++++++------ .../test/types/test_maps_object.dart | 5 +- .../test/types/tile_overlay_test.dart | 12 +-- .../test/types/tile_overlay_updates_test.dart | 39 +++---- .../test/types/tile_test.dart | 2 +- script/configs/custom_analysis.yaml | 10 +- script/tool/CHANGELOG.md | 4 + script/tool/lib/src/analyze_command.dart | 8 +- script/tool/test/analyze_command_test.dart | 12 +++ 40 files changed, 505 insertions(+), 430 deletions(-) delete mode 100644 analysis_options_legacy.yaml delete mode 100644 packages/google_maps_flutter/google_maps_flutter_platform_interface/analysis_options.yaml diff --git a/analysis_options.yaml b/analysis_options.yaml index 87f7d6f9840b..87515a471050 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -2,10 +2,6 @@ # with minimal changes for this repository. The goal is to move toward using a # shared set of analysis options as much as possible, and eventually a shared # file. -# -# Plugins that have not yet switched from the previous set of options have a -# local analysis_options.yaml that points to analysis_options_legacy.yaml -# instead. # Specify analysis options. # diff --git a/analysis_options_legacy.yaml b/analysis_options_legacy.yaml deleted file mode 100644 index da3c18071650..000000000000 --- a/analysis_options_legacy.yaml +++ /dev/null @@ -1,14 +0,0 @@ -include: package:pedantic/analysis_options.1.8.0.yaml -analyzer: - exclude: - # Ignore generated files - - '**/*.g.dart' - - 'lib/src/generated/*.dart' - - '**/*.mocks.dart' # Mockito @GenerateMocks - - '**/*.pigeon.dart' # Pigeon generated file - errors: - always_require_non_null_named_parameters: false # not needed with nnbd - unnecessary_null_comparison: false # Turned as long as nnbd mix-mode is supported. -linter: - rules: - - public_member_api_docs 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 b6e15d2ad14c..0359bab0b7c5 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,6 @@ -## NEXT +## 2.1.7 +* Updates code for stricter analysis options. * Removes unnecessary imports. ## 2.1.6 diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/analysis_options.yaml b/packages/google_maps_flutter/google_maps_flutter_platform_interface/analysis_options.yaml deleted file mode 100644 index 5aeb4e7c5e21..000000000000 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../../analysis_options_legacy.yaml diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/google_maps_flutter_platform_interface.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/google_maps_flutter_platform_interface.dart index 300700071102..b83eaf4fdfc7 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/google_maps_flutter_platform_interface.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/google_maps_flutter_platform_interface.dart @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +export 'src/events/map_event.dart'; export 'src/method_channel/method_channel_google_maps_flutter.dart' show MethodChannelGoogleMapsFlutter; export 'src/platform_interface/google_maps_flutter_platform.dart'; export 'src/types/types.dart'; -export 'src/events/map_event.dart'; 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 bb4124612be4..8759126d4b67 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 @@ -34,29 +34,29 @@ import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platf /// events to access the `.position` property, rather than the more generic `.value` /// yielded from the latter. class MapEvent { - /// The ID of the Map this event is associated to. - final int mapId; - - /// The value wrapped by this event - final T value; - /// Build a Map Event, that relates a mapId with a given value. /// /// The `mapId` is the id of the map that triggered the event. /// `value` may be `null` in events that don't transport any meaningful data. MapEvent(this.mapId, this.value); + + /// The ID of the Map this event is associated to. + final int mapId; + + /// The value wrapped by this event + final T value; } /// A `MapEvent` associated to a `position`. class _PositionedMapEvent extends MapEvent { - /// The position where this event happened. - final LatLng position; - /// Build a Positioned MapEvent, that relates a mapId and a position with a value. /// /// The `mapId` is the id of the map that triggered the event. /// `value` may be `null` in events that don't transport any meaningful data. _PositionedMapEvent(int mapId, this.position, T value) : super(mapId, value); + + /// The position where this event happened. + final LatLng position; } // The following events are the ones exposed to the end user. They are semantic extensions 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 9c5cbf5a54f0..365da757e435 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 @@ -27,11 +27,12 @@ class UnknownMapIDError extends Error { /// Message describing the assertion error. final Object? message; + @override String toString() { if (message != null) { - return "Unknown map ID $mapId: ${Error.safeToString(message)}"; + return 'Unknown map ID $mapId: ${Error.safeToString(message)}'; } - return "Unknown map ID $mapId"; + return 'Unknown map ID $mapId'; } } @@ -48,11 +49,11 @@ class UnknownMapIDError extends Error { class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { // Keep a collection of id -> channel // Every method call passes the int mapId - final Map _channels = {}; + final Map _channels = {}; /// Accesses the MethodChannel associated to the passed mapId. MethodChannel channel(int mapId) { - MethodChannel? channel = _channels[mapId]; + final MethodChannel? channel = _channels[mapId]; if (channel == null) { throw UnknownMapIDError(mapId); } @@ -60,7 +61,8 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { } // Keep a collection of mapId to a map of TileOverlays. - final Map> _tileOverlays = {}; + final Map> _tileOverlays = + >{}; /// Returns the channel for [mapId], creating it if it doesn't already exist. @visibleForTesting @@ -77,7 +79,7 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { @override Future init(int mapId) { - MethodChannel channel = ensureChannelInitialized(mapId); + final MethodChannel channel = ensureChannelInitialized(mapId); return channel.invokeMethod('map#waitForMap'); } @@ -91,12 +93,13 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { // // It is a `broadcast` because multiple controllers will connect to // different stream views of this Controller. - final StreamController _mapEventStreamController = - StreamController.broadcast(); + final StreamController> _mapEventStreamController = + StreamController>.broadcast(); // Returns a filtered view of the events in the _controller, by mapId. - Stream _events(int mapId) => - _mapEventStreamController.stream.where((event) => event.mapId == mapId); + Stream> _events(int mapId) => + _mapEventStreamController.stream + .where((MapEvent event) => event.mapId == mapId); @override Stream onCameraMoveStarted({required int mapId}) { @@ -180,52 +183,52 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { case 'marker#onTap': _mapEventStreamController.add(MarkerTapEvent( mapId, - MarkerId(call.arguments['markerId']), + MarkerId(call.arguments['markerId'] as String), )); break; case 'marker#onDragStart': _mapEventStreamController.add(MarkerDragStartEvent( mapId, LatLng.fromJson(call.arguments['position'])!, - MarkerId(call.arguments['markerId']), + MarkerId(call.arguments['markerId'] as String), )); break; case 'marker#onDrag': _mapEventStreamController.add(MarkerDragEvent( mapId, LatLng.fromJson(call.arguments['position'])!, - MarkerId(call.arguments['markerId']), + MarkerId(call.arguments['markerId'] as String), )); break; case 'marker#onDragEnd': _mapEventStreamController.add(MarkerDragEndEvent( mapId, LatLng.fromJson(call.arguments['position'])!, - MarkerId(call.arguments['markerId']), + MarkerId(call.arguments['markerId'] as String), )); break; case 'infoWindow#onTap': _mapEventStreamController.add(InfoWindowTapEvent( mapId, - MarkerId(call.arguments['markerId']), + MarkerId(call.arguments['markerId'] as String), )); break; case 'polyline#onTap': _mapEventStreamController.add(PolylineTapEvent( mapId, - PolylineId(call.arguments['polylineId']), + PolylineId(call.arguments['polylineId'] as String), )); break; case 'polygon#onTap': _mapEventStreamController.add(PolygonTapEvent( mapId, - PolygonId(call.arguments['polygonId']), + PolygonId(call.arguments['polygonId'] as String), )); break; case 'circle#onTap': _mapEventStreamController.add(CircleTapEvent( mapId, - CircleId(call.arguments['circleId']), + CircleId(call.arguments['circleId'] as String), )); break; case 'map#onTap': @@ -243,17 +246,17 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { case 'tileOverlay#getTile': final Map? tileOverlaysForThisMap = _tileOverlays[mapId]; - final String tileOverlayId = call.arguments['tileOverlayId']; + final String tileOverlayId = call.arguments['tileOverlayId'] as String; final TileOverlay? tileOverlay = tileOverlaysForThisMap?[TileOverlayId(tileOverlayId)]; - TileProvider? tileProvider = tileOverlay?.tileProvider; + final TileProvider? tileProvider = tileOverlay?.tileProvider; if (tileProvider == null) { return TileProvider.noTile.toJson(); } final Tile tile = await tileProvider.getTile( - call.arguments['x'], - call.arguments['y'], - call.arguments['zoom'], + call.arguments['x'] as int, + call.arguments['y'] as int, + call.arguments['zoom'] as int?, ); return tile.toJson(); default: @@ -330,7 +333,7 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { }) { final Map? currentTileOverlays = _tileOverlays[mapId]; - Set previousSet = currentTileOverlays != null + final Set previousSet = currentTileOverlays != null ? currentTileOverlays.values.toSet() : {}; final TileOverlayUpdates updates = @@ -380,9 +383,9 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { }) async { final List successAndError = (await channel(mapId) .invokeMethod>('map#setStyle', mapStyle))!; - final bool success = successAndError[0]; + final bool success = successAndError[0] as bool; if (!success) { - throw MapStyleException(successAndError[1]); + throw MapStyleException(successAndError[1] as String); } } @@ -418,7 +421,7 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { final List latLng = (await channel(mapId) .invokeMethod>( 'map#getLatLng', screenCoordinate.toJson()))!; - return LatLng(latLng[0], latLng[1]); + return LatLng(latLng[0] as double, latLng[1] as double); } @override 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 b6c7b8be692d..6386ed2523f2 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 @@ -5,12 +5,11 @@ import 'dart:async'; import 'dart:typed_data'; -import 'package:flutter/widgets.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; - +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; @@ -359,8 +358,8 @@ abstract class GoogleMapsFlutterPlatform extends PlatformInterface { Set tileOverlays = const {}, Set>? gestureRecognizers = const >{}, - // TODO: Replace with a structured type that's part of the interface. - // See https://github.com/flutter/flutter/issues/70330. + // TODO(stuartmorgan): Replace with a structured type that's part of the + // interface. See https://github.com/flutter/flutter/issues/70330. Map mapOptions = const {}, }) { throw UnimplementedError('buildView() has not been implemented.'); diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/bitmap.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/bitmap.dart index d3dc37e327fe..c43baf42db45 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/bitmap.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/bitmap.dart @@ -6,24 +6,70 @@ import 'dart:async' show Future; import 'dart:typed_data' show Uint8List; import 'dart:ui' show Size; +import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/material.dart' show ImageConfiguration, AssetImage, AssetBundleImageKey; import 'package:flutter/services.dart' show AssetBundle; -import 'package:flutter/foundation.dart' show kIsWeb; - /// Defines a bitmap image. For a marker, this class can be used to set the /// image of the marker icon. For a ground overlay, it can be used to set the /// image to place on the surface of the earth. class BitmapDescriptor { const BitmapDescriptor._(this._json); + /// The inverse of .toJson. + // This is needed in Web to re-hydrate BitmapDescriptors that have been + // transformed to JSON for transport. + // TODO(stuartmorgan): Clean this up. See + // https://github.com/flutter/flutter/issues/70330 + BitmapDescriptor.fromJson(Object json) : _json = json { + assert(_json is List); + final List jsonList = json as List; + assert(_validTypes.contains(jsonList[0])); + switch (jsonList[0]) { + case _defaultMarker: + assert(jsonList.length <= 2); + if (jsonList.length == 2) { + assert(jsonList[1] is num); + final num secondElement = jsonList[1] as num; + assert(0 <= secondElement && secondElement < 360); + } + break; + case _fromBytes: + assert(jsonList.length == 2); + assert(jsonList[1] != null && jsonList[1] is List); + assert((jsonList[1] as List).isNotEmpty); + break; + case _fromAsset: + assert(jsonList.length <= 3); + assert(jsonList[1] != null && jsonList[1] is String); + assert((jsonList[1] as String).isNotEmpty); + if (jsonList.length == 3) { + assert(jsonList[2] != null && jsonList[2] is String); + assert((jsonList[2] as String).isNotEmpty); + } + break; + case _fromAssetImage: + assert(jsonList.length <= 4); + assert(jsonList[1] != null && jsonList[1] is String); + assert((jsonList[1] as String).isNotEmpty); + assert(jsonList[2] != null && jsonList[2] is double); + if (jsonList.length == 4) { + assert(jsonList[3] != null && jsonList[3] is List); + assert((jsonList[3] as List).length == 2); + } + break; + default: + break; + } + } + static const String _defaultMarker = 'defaultMarker'; static const String _fromAsset = 'fromAsset'; static const String _fromAssetImage = 'fromAssetImage'; static const String _fromBytes = 'fromBytes'; - static const Set _validTypes = { + static const Set _validTypes = { _defaultMarker, _fromAsset, _fromAssetImage, @@ -86,7 +132,7 @@ class BitmapDescriptor { String? package, bool mipmaps = true, }) async { - double? devicePixelRatio = configuration.devicePixelRatio; + final double? devicePixelRatio = configuration.devicePixelRatio; if (!mipmaps && devicePixelRatio != null) { return BitmapDescriptor._([ _fromAssetImage, @@ -104,7 +150,7 @@ class BitmapDescriptor { assetBundleImageKey.name, assetBundleImageKey.scale, if (kIsWeb && size != null) - [ + [ size.width, size.height, ], @@ -117,51 +163,6 @@ class BitmapDescriptor { return BitmapDescriptor._([_fromBytes, byteData]); } - /// The inverse of .toJson. - // This is needed in Web to re-hydrate BitmapDescriptors that have been - // transformed to JSON for transport. - // TODO(https://github.com/flutter/flutter/issues/70330): Clean this up. - BitmapDescriptor.fromJson(Object json) : _json = json { - assert(_json is List); - final jsonList = json as List; - assert(_validTypes.contains(jsonList[0])); - switch (jsonList[0]) { - case _defaultMarker: - assert(jsonList.length <= 2); - if (jsonList.length == 2) { - assert(jsonList[1] is num); - assert(0 <= jsonList[1] && jsonList[1] < 360); - } - break; - case _fromBytes: - assert(jsonList.length == 2); - assert(jsonList[1] != null && jsonList[1] is List); - assert((jsonList[1] as List).isNotEmpty); - break; - case _fromAsset: - assert(jsonList.length <= 3); - assert(jsonList[1] != null && jsonList[1] is String); - assert((jsonList[1] as String).isNotEmpty); - if (jsonList.length == 3) { - assert(jsonList[2] != null && jsonList[2] is String); - assert((jsonList[2] as String).isNotEmpty); - } - break; - case _fromAssetImage: - assert(jsonList.length <= 4); - assert(jsonList[1] != null && jsonList[1] is String); - assert((jsonList[1] as String).isNotEmpty); - assert(jsonList[2] != null && jsonList[2] is double); - if (jsonList.length == 4) { - assert(jsonList[3] != null && jsonList[3] is List); - assert((jsonList[3] as List).length == 2); - } - break; - default: - break; - } - } - final Object _json; /// Convert the object to a Json format. diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/callbacks.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/callbacks.dart index 3b484c1feb05..5d6af90290e0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/callbacks.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/callbacks.dart @@ -10,10 +10,10 @@ import 'types.dart'; /// registers a camera movement. /// /// This is used in [GoogleMap.onCameraMove]. -typedef void CameraPositionCallback(CameraPosition position); +typedef CameraPositionCallback = void Function(CameraPosition position); /// Callback function taking a single argument. -typedef void ArgumentCallback(T argument); +typedef ArgumentCallback = void Function(T argument); /// Mutable collection of [ArgumentCallback] instances, itself an [ArgumentCallback]. /// @@ -35,7 +35,7 @@ class ArgumentCallbacks { if (length == 1) { _callbacks[0].call(argument); } else if (0 < length) { - for (ArgumentCallback callback + for (final ArgumentCallback callback in List>.from(_callbacks)) { callback(argument); } diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/camera.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/camera.dart index 89006eba6214..6d1ce164238b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/camera.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/camera.dart @@ -4,12 +4,15 @@ import 'dart:ui' show Offset; +import 'package:flutter/foundation.dart'; + import 'types.dart'; /// The position of the map "camera", the view point from which the world is shown in the map view. /// /// Aggregates the camera's [target] geographical location, its [zoom] level, /// [tilt] angle, and [bearing]. +@immutable class CameraPosition { /// Creates a immutable representation of the [GoogleMap] camera. /// @@ -72,7 +75,7 @@ class CameraPosition { /// /// Mainly for internal use. static CameraPosition? fromMap(Object? json) { - if (json == null || !(json is Map)) { + if (json == null || json is! Map) { return null; } final LatLng? target = LatLng.fromJson(json['target']); @@ -80,22 +83,26 @@ class CameraPosition { return null; } return CameraPosition( - bearing: json['bearing'], + bearing: json['bearing'] as double, target: target, - tilt: json['tilt'], - zoom: json['zoom'], + tilt: json['tilt'] as double, + zoom: json['zoom'] as double, ); } @override bool operator ==(Object other) { - if (identical(this, other)) return true; - if (runtimeType != other.runtimeType) return false; - final CameraPosition typedOther = other as CameraPosition; - return bearing == typedOther.bearing && - target == typedOther.target && - tilt == typedOther.tilt && - zoom == typedOther.zoom; + if (identical(this, other)) { + return true; + } + if (runtimeType != other.runtimeType) { + return false; + } + return other is CameraPosition && + bearing == other.bearing && + target == other.target && + tilt == other.tilt && + zoom == other.zoom; } @override diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/circle.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/circle.dart index 1e9b2181778e..d9e4b2d705c9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/circle.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/circle.dart @@ -3,8 +3,8 @@ // found in the LICENSE file. import 'package:flutter/foundation.dart' show VoidCallback; -import 'package:flutter/material.dart' show Color, Colors; import 'package:flutter/foundation.dart' show immutable; +import 'package:flutter/material.dart' show Color, Colors; import 'types.dart'; @@ -105,9 +105,11 @@ class Circle implements MapsObject { } /// Creates a new [Circle] object whose values are the same as this instance. + @override Circle clone() => copyWith(); /// Converts this object to something serializable in JSON. + @override Object toJson() { final Map json = {}; @@ -132,18 +134,22 @@ class Circle implements MapsObject { @override bool operator ==(Object other) { - if (identical(this, other)) return true; - if (other.runtimeType != runtimeType) return false; - final Circle typedOther = other as Circle; - return circleId == typedOther.circleId && - consumeTapEvents == typedOther.consumeTapEvents && - fillColor == typedOther.fillColor && - center == typedOther.center && - radius == typedOther.radius && - strokeColor == typedOther.strokeColor && - strokeWidth == typedOther.strokeWidth && - visible == typedOther.visible && - zIndex == typedOther.zIndex; + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is Circle && + circleId == other.circleId && + consumeTapEvents == other.consumeTapEvents && + fillColor == other.fillColor && + center == other.center && + radius == other.radius && + strokeColor == other.strokeColor && + strokeWidth == other.strokeWidth && + visible == other.visible && + zIndex == other.zIndex; } @override diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/location.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/location.dart index 5bc4ca608f1a..81fe08bb1329 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/location.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/location.dart @@ -2,9 +2,11 @@ // 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 visibleForTesting; +import 'package:flutter/foundation.dart' + show immutable, objectRuntimeType, visibleForTesting; /// A pair of latitude and longitude coordinates, stored as degrees. +@immutable class LatLng { /// Creates a geographical location specified in degrees [latitude] and /// [longitude]. @@ -17,7 +19,7 @@ class LatLng { : assert(latitude != null), assert(longitude != null), latitude = - (latitude < -90.0 ? -90.0 : (90.0 < latitude ? 90.0 : latitude)), + latitude < -90.0 ? -90.0 : (90.0 < latitude ? 90.0 : latitude), // Avoids normalization if possible to prevent unnecessary loss of precision longitude = longitude >= -180 && longitude < 180 ? longitude @@ -40,16 +42,19 @@ class LatLng { return null; } assert(json is List && json.length == 2); - final list = json as List; - return LatLng(list[0], list[1]); + final List list = json as List; + return LatLng(list[0]! as double, list[1]! as double); } @override - String toString() => '$runtimeType($latitude, $longitude)'; + String toString() => + '${objectRuntimeType(this, 'LatLng')}($latitude, $longitude)'; @override - bool operator ==(Object o) { - return o is LatLng && o.latitude == latitude && o.longitude == longitude; + bool operator ==(Object other) { + return other is LatLng && + other.latitude == latitude && + other.longitude == longitude; } @override @@ -64,6 +69,7 @@ class LatLng { /// if `southwest.longitude` ≤ `northeast.longitude`, /// * lng ∈ [-180, `northeast.longitude`] ∪ [`southwest.longitude`, 180], /// if `northeast.longitude` < `southwest.longitude` +@immutable class LatLngBounds { /// Creates geographical bounding box with the specified corners. /// @@ -110,7 +116,7 @@ class LatLngBounds { return null; } assert(json is List && json.length == 2); - final list = json as List; + final List list = json as List; return LatLngBounds( southwest: LatLng.fromJson(list[0])!, northeast: LatLng.fromJson(list[1])!, @@ -119,14 +125,14 @@ class LatLngBounds { @override String toString() { - return '$runtimeType($southwest, $northeast)'; + return '${objectRuntimeType(this, 'LatLngBounds')}($southwest, $northeast)'; } @override - bool operator ==(Object o) { - return o is LatLngBounds && - o.southwest == southwest && - o.northeast == northeast; + bool operator ==(Object other) { + return other is LatLngBounds && + other.southwest == southwest && + other.northeast == northeast; } @override diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object.dart index be629e174143..953746daa745 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object.dart @@ -20,10 +20,13 @@ class MapsObjectId { @override bool operator ==(Object other) { - if (identical(this, other)) return true; - if (other.runtimeType != runtimeType) return false; - final MapsObjectId typedOther = other as MapsObjectId; - return value == typedOther.value; + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is MapsObjectId && value == other.value; } @override diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object_updates.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object_updates.dart index 3267cad8d8a2..0051afcefbab 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object_updates.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object_updates.dart @@ -2,13 +2,15 @@ // 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 objectRuntimeType, setEquals; +import 'package:flutter/foundation.dart' + show immutable, objectRuntimeType, setEquals; import 'maps_object.dart'; import 'utils/maps_object.dart'; /// Update specification for a set of objects. -class MapsObjectUpdates { +@immutable +class MapsObjectUpdates> { /// Computes updates given previous and current object sets. /// /// [objectName] is the prefix to use when serializing the updates into a JSON @@ -43,7 +45,7 @@ class MapsObjectUpdates { // Returns `true` if [current] is not equals to previous one with the // same id. bool hasChanged(T current) { - final T? previous = previousObjects[current.mapsId as MapsObjectId]; + final T? previous = previousObjects[current.mapsId]; return current != previous; } @@ -62,21 +64,21 @@ class MapsObjectUpdates { return _objectsToAdd; } - late Set _objectsToAdd; + late final Set _objectsToAdd; /// Set of objects to be removed in this update. Set> get objectIdsToRemove { return _objectIdsToRemove; } - late Set> _objectIdsToRemove; + late final Set> _objectIdsToRemove; /// Set of objects to be changed in this update. Set get objectsToChange { return _objectsToChange; } - late Set _objectsToChange; + late final Set _objectsToChange; /// Converts this object to JSON. Object toJson() { 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 8057d2962e9e..914e77a64c9f 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 @@ -14,6 +14,7 @@ Object _offsetToJson(Offset offset) { } /// Text labels for a [Marker] info window. +@immutable class InfoWindow { /// Creates an immutable representation of a label on for [Marker]. const InfoWindow({ @@ -81,12 +82,16 @@ class InfoWindow { @override bool operator ==(Object other) { - if (identical(this, other)) return true; - if (other.runtimeType != runtimeType) return false; - final InfoWindow typedOther = other as InfoWindow; - return title == typedOther.title && - snippet == typedOther.snippet && - anchor == typedOther.anchor; + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is InfoWindow && + title == other.title && + snippet == other.snippet && + anchor == other.anchor; } @override @@ -113,7 +118,7 @@ class MarkerId extends MapsObjectId { /// the map's surface; that is, it will not necessarily change orientation /// due to map rotations, tilting, or zooming. @immutable -class Marker implements MapsObject { +class Marker implements MapsObject { /// Creates a set of marker configuration options. /// /// Default marker options. @@ -258,9 +263,11 @@ class Marker implements MapsObject { } /// Creates a new [Marker] object whose values are the same as this instance. + @override Marker clone() => copyWith(); /// Converts this object to something serializable in JSON. + @override Object toJson() { final Map json = {}; @@ -287,21 +294,25 @@ class Marker implements MapsObject { @override bool operator ==(Object other) { - if (identical(this, other)) return true; - if (other.runtimeType != runtimeType) return false; - final Marker typedOther = other as Marker; - return markerId == typedOther.markerId && - alpha == typedOther.alpha && - anchor == typedOther.anchor && - consumeTapEvents == typedOther.consumeTapEvents && - draggable == typedOther.draggable && - flat == typedOther.flat && - icon == typedOther.icon && - infoWindow == typedOther.infoWindow && - position == typedOther.position && - rotation == typedOther.rotation && - visible == typedOther.visible && - zIndex == typedOther.zIndex; + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is Marker && + markerId == other.markerId && + alpha == other.alpha && + anchor == other.anchor && + consumeTapEvents == other.consumeTapEvents && + draggable == other.draggable && + flat == other.flat && + icon == other.icon && + infoWindow == other.infoWindow && + position == other.position && + rotation == other.rotation && + visible == other.visible && + zIndex == other.zIndex; } @override diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/polygon.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/polygon.dart index 7b6f24831e59..8653ba0ed0f6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/polygon.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/polygon.dart @@ -20,7 +20,7 @@ class PolygonId extends MapsObjectId { /// Draws a polygon through geographical locations on the map. @immutable -class Polygon implements MapsObject { +class Polygon implements MapsObject { /// Creates an immutable representation of a polygon through geographical locations on the map. const Polygon({ required this.polygonId, @@ -123,11 +123,13 @@ class Polygon implements MapsObject { } /// Creates a new [Polygon] object whose values are the same as this instance. + @override Polygon clone() { return copyWith(pointsParam: List.of(points)); } /// Converts this object to something serializable in JSON. + @override Object toJson() { final Map json = {}; @@ -159,19 +161,23 @@ class Polygon implements MapsObject { @override bool operator ==(Object other) { - if (identical(this, other)) return true; - if (other.runtimeType != runtimeType) return false; - final Polygon typedOther = other as Polygon; - return polygonId == typedOther.polygonId && - consumeTapEvents == typedOther.consumeTapEvents && - fillColor == typedOther.fillColor && - geodesic == typedOther.geodesic && - listEquals(points, typedOther.points) && - const DeepCollectionEquality().equals(holes, typedOther.holes) && - visible == typedOther.visible && - strokeColor == typedOther.strokeColor && - strokeWidth == typedOther.strokeWidth && - zIndex == typedOther.zIndex; + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is Polygon && + polygonId == other.polygonId && + consumeTapEvents == other.consumeTapEvents && + fillColor == other.fillColor && + geodesic == other.geodesic && + listEquals(points, other.points) && + const DeepCollectionEquality().equals(holes, other.holes) && + visible == other.visible && + strokeColor == other.strokeColor && + strokeWidth == other.strokeWidth && + zIndex == other.zIndex; } @override diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/polyline.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/polyline.dart index 00c718646229..39e62e3c0160 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/polyline.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/polyline.dart @@ -21,7 +21,7 @@ class PolylineId extends MapsObjectId { /// Draws a line through geographical locations on the map. @immutable -class Polyline implements MapsObject { +class Polyline implements MapsObject { /// Creates an immutable object representing a line drawn through geographical locations on the map. const Polyline({ required this.polylineId, @@ -150,6 +150,7 @@ class Polyline implements MapsObject { /// Creates a new [Polyline] object whose values are the same as this /// instance. + @override Polyline clone() { return copyWith( patternsParam: List.of(patterns), @@ -158,6 +159,7 @@ class Polyline implements MapsObject { } /// Converts this object to something serializable in JSON. + @override Object toJson() { final Map json = {}; @@ -191,21 +193,25 @@ class Polyline implements MapsObject { @override bool operator ==(Object other) { - if (identical(this, other)) return true; - if (other.runtimeType != runtimeType) return false; - final Polyline typedOther = other as Polyline; - return polylineId == typedOther.polylineId && - consumeTapEvents == typedOther.consumeTapEvents && - color == typedOther.color && - geodesic == typedOther.geodesic && - jointType == typedOther.jointType && - listEquals(patterns, typedOther.patterns) && - listEquals(points, typedOther.points) && - startCap == typedOther.startCap && - endCap == typedOther.endCap && - visible == typedOther.visible && - width == typedOther.width && - zIndex == typedOther.zIndex; + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is Polyline && + polylineId == other.polylineId && + consumeTapEvents == other.consumeTapEvents && + color == other.color && + geodesic == other.geodesic && + jointType == other.jointType && + listEquals(patterns, other.patterns) && + listEquals(points, other.points) && + startCap == other.startCap && + endCap == other.endCap && + visible == other.visible && + width == other.width && + zIndex == other.zIndex; } @override diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/screen_coordinate.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/screen_coordinate.dart index b424aa5c00e4..b1d37dc2c234 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/screen_coordinate.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/screen_coordinate.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:flutter/foundation.dart' show immutable; +import 'package:flutter/foundation.dart' show immutable, objectRuntimeType; /// Represents a point coordinate in the [GoogleMap]'s view. /// @@ -26,17 +26,17 @@ class ScreenCoordinate { /// Converts this object to something serializable in JSON. Object toJson() { return { - "x": x, - "y": y, + 'x': x, + 'y': y, }; } @override - String toString() => '$runtimeType($x, $y)'; + String toString() => '${objectRuntimeType(this, 'ScreenCoordinate')}($x, $y)'; @override - bool operator ==(Object o) { - return o is ScreenCoordinate && o.x == x && o.y == y; + bool operator ==(Object other) { + return other is ScreenCoordinate && other.x == x && other.y == y; } @override diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile_overlay.dart index 80b05272e21d..aaf0f800f47f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile_overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/tile_overlay.dart @@ -41,8 +41,8 @@ class TileOverlayId extends MapsObjectId { /// The coordinates of the tiles are measured from the top left (northwest) corner of the map. /// At zoom level N, the x values of the tile coordinates range from 0 to 2N - 1 and increase from /// west to east and the y values range from 0 to 2N - 1 and increase from north to south. -/// -class TileOverlay implements MapsObject { +@immutable +class TileOverlay implements MapsObject { /// Creates an immutable representation of a [TileOverlay] to draw on [GoogleMap]. const TileOverlay({ required this.tileOverlayId, @@ -106,9 +106,11 @@ class TileOverlay implements MapsObject { ); } + @override TileOverlay clone() => copyWith(); /// Converts this object to JSON. + @override Object toJson() { final Map json = {}; 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 5e2e4c234ccf..1e1bef8ee6c0 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 @@ -7,25 +7,24 @@ export 'bitmap.dart'; export 'callbacks.dart'; export 'camera.dart'; export 'cap.dart'; -export 'circle_updates.dart'; export 'circle.dart'; +export 'circle_updates.dart'; export 'joint_type.dart'; export 'location.dart'; -export 'maps_object_updates.dart'; export 'maps_object.dart'; -export 'marker_updates.dart'; +export 'maps_object_updates.dart'; export 'marker.dart'; +export 'marker_updates.dart'; export 'pattern_item.dart'; -export 'polygon_updates.dart'; export 'polygon.dart'; -export 'polyline_updates.dart'; +export 'polygon_updates.dart'; export 'polyline.dart'; +export 'polyline_updates.dart'; export 'screen_coordinate.dart'; export 'tile.dart'; export 'tile_overlay.dart'; export 'tile_provider.dart'; export 'ui.dart'; - // Export the utils, they're used by the Widget export 'utils/circle.dart'; export 'utils/marker.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/ui.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/ui.dart index 18f88b910b48..482f64be8b4f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/ui.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/ui.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 'package:flutter/foundation.dart'; + import 'types.dart'; /// Type of map tiles to display. @@ -29,6 +31,7 @@ enum MapType { // Used with [GoogleMapOptions] to wrap a [LatLngBounds] value. This allows // distinguishing between specifying an unbounded target (null `LatLngBounds`) // from not specifying anything (null `CameraTargetBounds`). +@immutable class CameraTargetBounds { /// Creates a camera target bounds with the specified bounding box, or null /// to indicate that the camera target is not bounded. @@ -47,10 +50,13 @@ class CameraTargetBounds { @override bool operator ==(Object other) { - if (identical(this, other)) return true; - if (runtimeType != other.runtimeType) return false; - final CameraTargetBounds typedOther = other as CameraTargetBounds; - return bounds == typedOther.bounds; + if (identical(this, other)) { + return true; + } + if (runtimeType != other.runtimeType) { + return false; + } + return other is CameraTargetBounds && bounds == other.bounds; } @override @@ -66,6 +72,7 @@ class CameraTargetBounds { // Used with [GoogleMapOptions] to wrap min and max zoom. This allows // distinguishing between specifying unbounded zooming (null `minZoom` and // `maxZoom`) from not specifying anything (null `MinMaxZoomPreference`). +@immutable class MinMaxZoomPreference { /// Creates a immutable representation of the preferred minimum and maximum zoom values for the map camera. /// @@ -88,10 +95,15 @@ class MinMaxZoomPreference { @override bool operator ==(Object other) { - if (identical(this, other)) return true; - if (runtimeType != other.runtimeType) return false; - final MinMaxZoomPreference typedOther = other as MinMaxZoomPreference; - return minZoom == typedOther.minZoom && maxZoom == typedOther.maxZoom; + if (identical(this, other)) { + return true; + } + if (runtimeType != other.runtimeType) { + return false; + } + return other is MinMaxZoomPreference && + minZoom == other.minZoom && + maxZoom == other.maxZoom; } @override diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/utils/maps_object.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/utils/maps_object.dart index da5a49825c7f..d17dbd279dfe 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/utils/maps_object.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/utils/maps_object.dart @@ -5,14 +5,13 @@ import '../maps_object.dart'; /// Converts an [Iterable] of [MapsObject]s in a Map of [MapObjectId] -> [MapObject]. -Map, T> keyByMapsObjectId( +Map, T> keyByMapsObjectId>( Iterable objects) { return Map, T>.fromEntries(objects.map((T object) => - MapEntry, T>( - object.mapsId as MapsObjectId, object.clone()))); + MapEntry, T>(object.mapsId, object.clone()))); } /// Converts a Set of [MapsObject]s into something serializable in JSON. -Object serializeMapsObjectSet(Set mapsObjects) { - return mapsObjects.map((MapsObject p) => p.toJson()).toList(); +Object serializeMapsObjectSet(Set> mapsObjects) { + return mapsObjects.map((MapsObject p) => p.toJson()).toList(); } 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 998f31936c3b..759daf2bb1cb 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/plugins/tree/main/packages/google_maps_fl 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.1.6 +version: 2.1.7 environment: sdk: '>=2.12.0 <3.0.0' diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/method_channel/method_channel_google_maps_flutter_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/method_channel/method_channel_google_maps_flutter_test.dart index 9ae42ced6c42..e5052184915f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/method_channel/method_channel_google_maps_flutter_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/method_channel/method_channel_google_maps_flutter_test.dart @@ -2,13 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:async/async.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; - import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; -import 'package:async/async.dart'; - void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -39,8 +37,8 @@ void main() { final ByteData byteData = const StandardMethodCodec() .encodeMethodCall(MethodCall(method, data)); await TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger - .handlePlatformMessage( - "plugins.flutter.io/google_maps_$mapId", byteData, (data) {}); + .handlePlatformMessage('plugins.flutter.io/google_maps_$mapId', + byteData, (ByteData? data) {}); } // Calls each method that uses invokeMethod with a return type other than @@ -66,8 +64,8 @@ void main() { } }); - await maps.getLatLng(ScreenCoordinate(x: 0, y: 0), mapId: mapId); - await maps.isMarkerInfoWindowShown(MarkerId(''), mapId: mapId); + await maps.getLatLng(const ScreenCoordinate(x: 0, y: 0), mapId: mapId); + await maps.isMarkerInfoWindowShown(const MarkerId(''), mapId: mapId); await maps.getZoomLevel(mapId: mapId); await maps.takeSnapshot(mapId: mapId); // Check that all the invokeMethod calls happened. @@ -80,20 +78,20 @@ void main() { }); test('markers send drag event to correct streams', () async { const int mapId = 1; - final jsonMarkerDragStartEvent = { - "mapId": mapId, - "markerId": "drag-start-marker", - "position": [1.0, 1.0] + final Map jsonMarkerDragStartEvent = { + 'mapId': mapId, + 'markerId': 'drag-start-marker', + 'position': [1.0, 1.0] }; - final jsonMarkerDragEvent = { - "mapId": mapId, - "markerId": "drag-marker", - "position": [1.0, 1.0] + final Map jsonMarkerDragEvent = { + 'mapId': mapId, + 'markerId': 'drag-marker', + 'position': [1.0, 1.0] }; - final jsonMarkerDragEndEvent = { - "mapId": mapId, - "markerId": "drag-end-marker", - "position": [1.0, 1.0] + final Map jsonMarkerDragEndEvent = { + 'mapId': mapId, + 'markerId': 'drag-end-marker', + 'position': [1.0, 1.0] }; final MethodChannelGoogleMapsFlutter maps = @@ -101,23 +99,24 @@ void main() { maps.ensureChannelInitialized(mapId); final StreamQueue markerDragStartStream = - StreamQueue(maps.onMarkerDragStart(mapId: mapId)); + StreamQueue( + maps.onMarkerDragStart(mapId: mapId)); final StreamQueue markerDragStream = - StreamQueue(maps.onMarkerDrag(mapId: mapId)); + StreamQueue(maps.onMarkerDrag(mapId: mapId)); final StreamQueue markerDragEndStream = - StreamQueue(maps.onMarkerDragEnd(mapId: mapId)); + StreamQueue(maps.onMarkerDragEnd(mapId: mapId)); await sendPlatformMessage( - mapId, "marker#onDragStart", jsonMarkerDragStartEvent); - await sendPlatformMessage(mapId, "marker#onDrag", jsonMarkerDragEvent); + mapId, 'marker#onDragStart', jsonMarkerDragStartEvent); + await sendPlatformMessage(mapId, 'marker#onDrag', jsonMarkerDragEvent); await sendPlatformMessage( - mapId, "marker#onDragEnd", jsonMarkerDragEndEvent); + mapId, 'marker#onDragEnd', jsonMarkerDragEndEvent); expect((await markerDragStartStream.next).value.value, - equals("drag-start-marker")); - expect((await markerDragStream.next).value.value, equals("drag-marker")); + equals('drag-start-marker')); + expect((await markerDragStream.next).value.value, equals('drag-marker')); expect((await markerDragEndStream.next).value.value, - equals("drag-end-marker")); + equals('drag-end-marker')); }); }); } 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 bdbaff7e2599..0899bb6a8fb2 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 @@ -6,11 +6,10 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/services.dart'; -import 'package:mockito/mockito.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'package:mockito/mockito.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -50,7 +49,8 @@ void main() { platform.buildViewWithTextDirection( 0, (_) {}, - initialCameraPosition: CameraPosition(target: LatLng(0.0, 0.0)), + initialCameraPosition: + const CameraPosition(target: LatLng(0.0, 0.0)), textDirection: TextDirection.ltr, ), isA(), diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/bitmap_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/bitmap_test.dart index 6d02b2c630df..7fbaf4998355 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/bitmap_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/bitmap_test.dart @@ -13,13 +13,14 @@ void main() { group('$BitmapDescriptor', () { test('toJson / fromJson', () { - final descriptor = + final BitmapDescriptor descriptor = BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueCyan); - final json = descriptor.toJson(); + final Object json = descriptor.toJson(); // Rehydrate a new bitmap descriptor... // ignore: deprecated_member_use_from_same_package - final descriptorFromJson = BitmapDescriptor.fromJson(json); + final BitmapDescriptor descriptorFromJson = + BitmapDescriptor.fromJson(json); expect(descriptorFromJson, isNot(descriptor)); // New instance expect(identical(descriptorFromJson.toJson(), json), isTrue); // Same JSON @@ -28,81 +29,85 @@ void main() { group('fromJson validation', () { group('type validation', () { test('correct type', () { - expect(BitmapDescriptor.fromJson(['defaultMarker']), + expect(BitmapDescriptor.fromJson(['defaultMarker']), isA()); }); test('wrong type', () { expect(() { - BitmapDescriptor.fromJson(['bogusType']); + BitmapDescriptor.fromJson(['bogusType']); }, throwsAssertionError); }); }); group('defaultMarker', () { test('hue is null', () { - expect(BitmapDescriptor.fromJson(['defaultMarker']), + expect(BitmapDescriptor.fromJson(['defaultMarker']), isA()); }); test('hue is number', () { - expect(BitmapDescriptor.fromJson(['defaultMarker', 158]), + expect(BitmapDescriptor.fromJson(['defaultMarker', 158]), isA()); }); test('hue is not number', () { expect(() { - BitmapDescriptor.fromJson(['defaultMarker', 'nope']); + BitmapDescriptor.fromJson(['defaultMarker', 'nope']); }, throwsAssertionError); }); test('hue is out of range', () { expect(() { - BitmapDescriptor.fromJson(['defaultMarker', -1]); + BitmapDescriptor.fromJson(['defaultMarker', -1]); }, throwsAssertionError); expect(() { - BitmapDescriptor.fromJson(['defaultMarker', 361]); + BitmapDescriptor.fromJson(['defaultMarker', 361]); }, throwsAssertionError); }); }); group('fromBytes', () { test('with bytes', () { expect( - BitmapDescriptor.fromJson([ + BitmapDescriptor.fromJson([ 'fromBytes', - Uint8List.fromList([1, 2, 3]) + Uint8List.fromList([1, 2, 3]) ]), isA()); }); test('without bytes', () { expect(() { - BitmapDescriptor.fromJson(['fromBytes', null]); + BitmapDescriptor.fromJson(['fromBytes', null]); }, throwsAssertionError); expect(() { - BitmapDescriptor.fromJson(['fromBytes', []]); + BitmapDescriptor.fromJson(['fromBytes', []]); }, throwsAssertionError); }); }); group('fromAsset', () { test('name is passed', () { - expect(BitmapDescriptor.fromJson(['fromAsset', 'some/path.png']), + expect( + BitmapDescriptor.fromJson( + ['fromAsset', 'some/path.png']), isA()); }); test('name cannot be null or empty', () { expect(() { - BitmapDescriptor.fromJson(['fromAsset', null]); + BitmapDescriptor.fromJson(['fromAsset', null]); }, throwsAssertionError); expect(() { - BitmapDescriptor.fromJson(['fromAsset', '']); + BitmapDescriptor.fromJson(['fromAsset', '']); }, throwsAssertionError); }); test('package is passed', () { expect( BitmapDescriptor.fromJson( - ['fromAsset', 'some/path.png', 'some_package']), + ['fromAsset', 'some/path.png', 'some_package']), isA()); }); test('package cannot be null or empty', () { expect(() { - BitmapDescriptor.fromJson(['fromAsset', 'some/path.png', null]); + BitmapDescriptor.fromJson( + ['fromAsset', 'some/path.png', null]); }, throwsAssertionError); expect(() { - BitmapDescriptor.fromJson(['fromAsset', 'some/path.png', '']); + BitmapDescriptor.fromJson( + ['fromAsset', 'some/path.png', '']); }, throwsAssertionError); }); }); @@ -110,34 +115,34 @@ void main() { test('name and dpi passed', () { expect( BitmapDescriptor.fromJson( - ['fromAssetImage', 'some/path.png', 1.0]), + ['fromAssetImage', 'some/path.png', 1.0]), isA()); }); test('name cannot be null or empty', () { expect(() { - BitmapDescriptor.fromJson(['fromAssetImage', null, 1.0]); + BitmapDescriptor.fromJson(['fromAssetImage', null, 1.0]); }, throwsAssertionError); expect(() { - BitmapDescriptor.fromJson(['fromAssetImage', '', 1.0]); + BitmapDescriptor.fromJson(['fromAssetImage', '', 1.0]); }, throwsAssertionError); }); test('dpi must be number', () { expect(() { BitmapDescriptor.fromJson( - ['fromAssetImage', 'some/path.png', null]); + ['fromAssetImage', 'some/path.png', null]); }, throwsAssertionError); expect(() { BitmapDescriptor.fromJson( - ['fromAssetImage', 'some/path.png', 'one']); + ['fromAssetImage', 'some/path.png', 'one']); }, throwsAssertionError); }); test('with optional [width, height] List', () { expect( - BitmapDescriptor.fromJson([ + BitmapDescriptor.fromJson([ 'fromAssetImage', 'some/path.png', 1.0, - [640, 480] + [640, 480] ]), isA()); }); @@ -146,18 +151,18 @@ void main() { () { expect(() { BitmapDescriptor.fromJson( - ['fromAssetImage', 'some/path.png', 1.0, null]); + ['fromAssetImage', 'some/path.png', 1.0, null]); }, throwsAssertionError); expect(() { BitmapDescriptor.fromJson( - ['fromAssetImage', 'some/path.png', 1.0, []]); + ['fromAssetImage', 'some/path.png', 1.0, []]); }, throwsAssertionError); expect(() { - BitmapDescriptor.fromJson([ + BitmapDescriptor.fromJson([ 'fromAssetImage', 'some/path.png', 1.0, - [640, 480, 1024] + [640, 480, 1024] ]); }, throwsAssertionError); }); diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/camera_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/camera_test.dart index 11665d904556..70e57aa67ac9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/camera_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/camera_test.dart @@ -9,13 +9,14 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); test('toMap / fromMap', () { - const cameraPosition = CameraPosition( + const CameraPosition cameraPosition = CameraPosition( target: LatLng(10.0, 15.0), bearing: 0.5, tilt: 30.0, zoom: 1.5); // Cast to to ensure that recreating from JSON, where // type information will have likely been lost, still works. - final json = (cameraPosition.toMap() as Map) - .cast(); - final cameraPositionFromJson = CameraPosition.fromMap(json); + final Map json = + (cameraPosition.toMap() as Map) + .cast(); + final CameraPosition? cameraPositionFromJson = CameraPosition.fromMap(json); expect(cameraPosition, cameraPositionFromJson); }); diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/location_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/location_test.dart index 80f696177dfd..9da3e543ea58 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/location_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/location_test.dart @@ -10,50 +10,50 @@ void main() { group('LanLng constructor', () { test('Maintains longitude precision if within acceptable range', () async { - const lat = -34.509981; - const lng = 150.792384; + const double lat = -34.509981; + const double lng = 150.792384; - final latLng = LatLng(lat, lng); + const LatLng latLng = LatLng(lat, lng); expect(latLng.latitude, equals(lat)); expect(latLng.longitude, equals(lng)); }); test('Normalizes longitude that is below lower limit', () async { - const lat = -34.509981; - const lng = -270.0; + const double lat = -34.509981; + const double lng = -270.0; - final latLng = LatLng(lat, lng); + const LatLng latLng = LatLng(lat, lng); expect(latLng.latitude, equals(lat)); expect(latLng.longitude, equals(90.0)); }); test('Normalizes longitude that is above upper limit', () async { - const lat = -34.509981; - const lng = 270.0; + const double lat = -34.509981; + const double lng = 270.0; - final latLng = LatLng(lat, lng); + const LatLng latLng = LatLng(lat, lng); expect(latLng.latitude, equals(lat)); expect(latLng.longitude, equals(-90.0)); }); test('Includes longitude set to lower limit', () async { - const lat = -34.509981; - const lng = -180.0; + const double lat = -34.509981; + const double lng = -180.0; - final latLng = LatLng(lat, lng); + const LatLng latLng = LatLng(lat, lng); expect(latLng.latitude, equals(lat)); expect(latLng.longitude, equals(-180.0)); }); test('Normalizes longitude set to upper limit', () async { - const lat = -34.509981; - const lng = 180.0; + const double lat = -34.509981; + const double lng = 180.0; - final latLng = LatLng(lat, lng); + const LatLng latLng = LatLng(lat, lng); expect(latLng.latitude, equals(lat)); expect(latLng.longitude, equals(-180.0)); diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/maps_object_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/maps_object_test.dart index c2ca2bdda5b7..7c5106c23173 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/maps_object_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/maps_object_test.dart @@ -37,9 +37,9 @@ void main() { expect( serializeMapsObjectSet({object1, object2, object3}), >[ - {'id': '1'}, - {'id': '2'}, - {'id': '3'} + {'id': '1'}, + {'id': '2'}, + {'id': '3'} ]); }); } diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/maps_object_updates_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/maps_object_updates_test.dart index 73ed1d9d1a90..414196b8333c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/maps_object_updates_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/maps_object_updates_test.dart @@ -30,24 +30,25 @@ void main() { TestMapsObject(MapsObjectId('id3'), data: 2); const TestMapsObject to4 = TestMapsObject(MapsObjectId('id4')); - final Set previous = - Set.from([to1, to2, to3]); - final Set current = - Set.from([to2, to3Changed, to4]); + final Set previous = {to1, to2, to3}; + final Set current = { + to2, + to3Changed, + to4 + }; final TestMapsObjectUpdate updates = TestMapsObjectUpdate.from(previous, current); final Set> toRemove = - Set.from(>[ + >{ const MapsObjectId('id1') - ]); + }; expect(updates.objectIdsToRemove, toRemove); - final Set toAdd = Set.from([to4]); + final Set toAdd = {to4}; expect(updates.objectsToAdd, toAdd); - final Set toChange = - Set.from([to3Changed]); + final Set toChange = {to3Changed}; expect(updates.objectsToChange, toChange); }); @@ -62,10 +63,12 @@ void main() { TestMapsObject(MapsObjectId('id3'), data: 2); const TestMapsObject to4 = TestMapsObject(MapsObjectId('id4')); - final Set previous = - Set.from([to1, to2, to3]); - final Set current = - Set.from([to2, to3Changed, to4]); + final Set previous = {to1, to2, to3}; + final Set current = { + to2, + to3Changed, + to4 + }; final TestMapsObjectUpdate updates = TestMapsObjectUpdate.from(previous, current); @@ -90,13 +93,18 @@ void main() { TestMapsObject(MapsObjectId('id3'), data: 2); const TestMapsObject to4 = TestMapsObject(MapsObjectId('id4')); - final Set previous = - Set.from([to1, to2, to3]); - final Set current1 = - Set.from([to2, to3Changed, to4]); - final Set current2 = - Set.from([to2, to3Changed, to4]); - final Set current3 = Set.from([to2, to4]); + final Set previous = {to1, to2, to3}; + final Set current1 = { + to2, + to3Changed, + to4 + }; + final Set current2 = { + to2, + to3Changed, + to4 + }; + final Set current3 = {to2, to4}; final TestMapsObjectUpdate updates1 = TestMapsObjectUpdate.from(previous, current1); final TestMapsObjectUpdate updates2 = @@ -118,10 +126,12 @@ void main() { TestMapsObject(MapsObjectId('id3'), data: 2); const TestMapsObject to4 = TestMapsObject(MapsObjectId('id4')); - final Set previous = - Set.from([to1, to2, to3]); - final Set current = - Set.from([to2, to3Changed, to4]); + final Set previous = {to1, to2, to3}; + final Set current = { + to2, + to3Changed, + to4 + }; final TestMapsObjectUpdate updates = TestMapsObjectUpdate.from(previous, current); expect( @@ -143,10 +153,12 @@ void main() { TestMapsObject(MapsObjectId('id3'), data: 2); const TestMapsObject to4 = TestMapsObject(MapsObjectId('id4')); - final Set previous = - Set.from([to1, to2, to3]); - final Set current = - Set.from([to2, to3Changed, to4]); + final Set previous = {to1, to2, to3}; + final Set current = { + to2, + to3Changed, + to4 + }; final TestMapsObjectUpdate updates = TestMapsObjectUpdate.from(previous, current); expect( 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 c8f6fa527a95..db7afcbb0398 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 @@ -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/cupertino.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; @@ -12,7 +11,7 @@ void main() { group('$Marker', () { test('constructor defaults', () { - final Marker marker = Marker(markerId: MarkerId("ABC123")); + const Marker marker = Marker(markerId: MarkerId('ABC123')); expect(marker.alpha, equals(1.0)); expect(marker.anchor, equals(const Offset(0.5, 1.0))); @@ -31,9 +30,10 @@ void main() { expect(marker.onDragEnd, equals(null)); }); test('constructor alpha is >= 0.0 and <= 1.0', () { - final ValueSetter initWithAlpha = (double alpha) { - Marker(markerId: MarkerId("ABC123"), alpha: alpha); - }; + void initWithAlpha(double alpha) { + Marker(markerId: const MarkerId('ABC123'), alpha: alpha); + } + expect(() => initWithAlpha(-0.5), throwsAssertionError); expect(() => initWithAlpha(0.0), isNot(throwsAssertionError)); expect(() => initWithAlpha(0.5), isNot(throwsAssertionError)); @@ -45,19 +45,19 @@ void main() { final BitmapDescriptor testDescriptor = BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueCyan); final Marker marker = Marker( - markerId: MarkerId("ABC123"), + markerId: const MarkerId('ABC123'), alpha: 0.12345, - anchor: Offset(100, 100), + anchor: const Offset(100, 100), consumeTapEvents: true, draggable: true, flat: true, icon: testDescriptor, - infoWindow: InfoWindow( - title: "Test title", - snippet: "Test snippet", + infoWindow: const InfoWindow( + title: 'Test title', + snippet: 'Test snippet', anchor: Offset(100, 200), ), - position: LatLng(50, 50), + position: const LatLng(50, 50), rotation: 100, visible: false, zIndex: 100, @@ -70,7 +70,7 @@ void main() { final Map json = marker.toJson() as Map; expect(json, { - 'markerId': "ABC123", + 'markerId': 'ABC123', 'alpha': 0.12345, 'anchor': [100, 100], 'consumeTapEvents': true, @@ -78,8 +78,8 @@ void main() { 'flat': true, 'icon': testDescriptor.toJson(), 'infoWindow': { - 'title': "Test title", - 'snippet': "Test snippet", + 'title': 'Test title', + 'snippet': 'Test snippet', 'anchor': [100.0, 200.0], }, 'position': [50, 50], @@ -89,31 +89,31 @@ void main() { }); }); test('clone', () { - final Marker marker = Marker(markerId: MarkerId("ABC123")); + const Marker marker = Marker(markerId: MarkerId('ABC123')); final Marker clone = marker.clone(); expect(identical(clone, marker), isFalse); expect(clone, equals(marker)); }); test('copyWith', () { - final Marker marker = Marker(markerId: MarkerId("ABC123")); + const Marker marker = Marker(markerId: MarkerId('ABC123')); final BitmapDescriptor testDescriptor = BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueCyan); - final double testAlphaParam = 0.12345; - final Offset testAnchorParam = Offset(100, 100); + const double testAlphaParam = 0.12345; + const Offset testAnchorParam = Offset(100, 100); final bool testConsumeTapEventsParam = !marker.consumeTapEvents; final bool testDraggableParam = !marker.draggable; final bool testFlatParam = !marker.flat; final BitmapDescriptor testIconParam = testDescriptor; - final InfoWindow testInfoWindowParam = InfoWindow(title: "Test"); - final LatLng testPositionParam = LatLng(100, 100); - final double testRotationParam = 100; + const InfoWindow testInfoWindowParam = InfoWindow(title: 'Test'); + const LatLng testPositionParam = LatLng(100, 100); + const double testRotationParam = 100; final bool testVisibleParam = !marker.visible; - final double testZIndexParam = 100; - final List log = []; + const double testZIndexParam = 100; + final List log = []; - final copy = marker.copyWith( + final Marker copy = marker.copyWith( alphaParam: testAlphaParam, anchorParam: testAnchorParam, consumeTapEventsParam: testConsumeTapEventsParam, @@ -126,16 +126,16 @@ void main() { visibleParam: testVisibleParam, zIndexParam: testZIndexParam, onTapParam: () { - log.add("onTapParam"); + log.add('onTapParam'); }, onDragStartParam: (LatLng latLng) { - log.add("onDragStartParam"); + log.add('onDragStartParam'); }, onDragParam: (LatLng latLng) { - log.add("onDragParam"); + log.add('onDragParam'); }, onDragEndParam: (LatLng latLng) { - log.add("onDragEndParam"); + log.add('onDragEndParam'); }, ); @@ -152,16 +152,16 @@ void main() { expect(copy.zIndex, equals(testZIndexParam)); copy.onTap!(); - expect(log, contains("onTapParam")); + expect(log, contains('onTapParam')); - copy.onDragStart!(LatLng(0, 1)); - expect(log, contains("onDragStartParam")); + copy.onDragStart!(const LatLng(0, 1)); + expect(log, contains('onDragStartParam')); - copy.onDrag!(LatLng(0, 1)); - expect(log, contains("onDragParam")); + copy.onDrag!(const LatLng(0, 1)); + expect(log, contains('onDragParam')); - copy.onDragEnd!(LatLng(0, 1)); - expect(log, contains("onDragEndParam")); + copy.onDragEnd!(const LatLng(0, 1)); + expect(log, contains('onDragEndParam')); }); }); } diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/test_maps_object.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/test_maps_object.dart index e28da7ab79ad..0da077dbc300 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/test_maps_object.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/test_maps_object.dart @@ -2,13 +2,16 @@ // 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:google_maps_flutter_platform_interface/src/types/maps_object.dart'; import 'package:google_maps_flutter_platform_interface/src/types/maps_object_updates.dart'; /// A trivial TestMapsObject implementation for testing updates with. -class TestMapsObject implements MapsObject { +@immutable +class TestMapsObject implements MapsObject { const TestMapsObject(this.mapsId, {this.data = 1}); + @override final MapsObjectId mapsId; final int data; diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_test.dart index c3ccf695032b..1a9a9d480f1a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_test.dart @@ -8,7 +8,7 @@ import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platf class _TestTileProvider extends TileProvider { @override Future getTile(int x, int y, int? zoom) async { - return Tile(0, 0, null); + return const Tile(0, 0, null); } } @@ -65,7 +65,7 @@ void main() { test('equality', () async { final TileProvider tileProvider = _TestTileProvider(); final TileOverlay tileOverlay1 = TileOverlay( - tileOverlayId: TileOverlayId('id1'), + tileOverlayId: const TileOverlayId('id1'), fadeIn: false, tileProvider: tileProvider, transparency: 0.1, @@ -73,7 +73,7 @@ void main() { visible: false, tileSize: 128); final TileOverlay tileOverlaySameValues = TileOverlay( - tileOverlayId: TileOverlayId('id1'), + tileOverlayId: const TileOverlayId('id1'), fadeIn: false, tileProvider: tileProvider, transparency: 0.1, @@ -81,14 +81,14 @@ void main() { visible: false, tileSize: 128); final TileOverlay tileOverlayDifferentId = TileOverlay( - tileOverlayId: TileOverlayId('id2'), + tileOverlayId: const TileOverlayId('id2'), fadeIn: false, tileProvider: tileProvider, transparency: 0.1, zIndex: 1, visible: false, tileSize: 128); - final TileOverlay tileOverlayDifferentProvider = TileOverlay( + const TileOverlay tileOverlayDifferentProvider = TileOverlay( tileOverlayId: TileOverlayId('id1'), fadeIn: false, tileProvider: null, @@ -105,7 +105,7 @@ void main() { final TileProvider tileProvider = _TestTileProvider(); // Set non-default values for every parameter. final TileOverlay tileOverlay = TileOverlay( - tileOverlayId: TileOverlayId('id1'), + tileOverlayId: const TileOverlayId('id1'), fadeIn: false, tileProvider: tileProvider, transparency: 0.1, diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_updates_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_updates_test.dart index fbb345c50563..b62f7326d831 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_updates_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_updates_test.dart @@ -18,20 +18,20 @@ void main() { const TileOverlay to3Changed = TileOverlay(tileOverlayId: TileOverlayId('id3'), transparency: 0.5); const TileOverlay to4 = TileOverlay(tileOverlayId: TileOverlayId('id4')); - final Set previous = Set.from([to1, to2, to3]); - final Set current = - Set.from([to2, to3Changed, to4]); + final Set previous = {to1, to2, to3}; + final Set current = {to2, to3Changed, to4}; final TileOverlayUpdates updates = TileOverlayUpdates.from(previous, current); - final Set toRemove = - Set.from([const TileOverlayId('id1')]); + final Set toRemove = { + const TileOverlayId('id1') + }; expect(updates.tileOverlayIdsToRemove, toRemove); - final Set toAdd = Set.from([to4]); + final Set toAdd = {to4}; expect(updates.tileOverlaysToAdd, toAdd); - final Set toChange = Set.from([to3Changed]); + final Set toChange = {to3Changed}; expect(updates.tileOverlaysToChange, toChange); }); @@ -42,9 +42,8 @@ void main() { const TileOverlay to3Changed = TileOverlay(tileOverlayId: TileOverlayId('id3'), transparency: 0.5); const TileOverlay to4 = TileOverlay(tileOverlayId: TileOverlayId('id4')); - final Set previous = Set.from([to1, to2, to3]); - final Set current = - Set.from([to2, to3Changed, to4]); + final Set previous = {to1, to2, to3}; + final Set current = {to2, to3Changed, to4}; final TileOverlayUpdates updates = TileOverlayUpdates.from(previous, current); @@ -66,12 +65,10 @@ void main() { const TileOverlay to3Changed = TileOverlay(tileOverlayId: TileOverlayId('id3'), transparency: 0.5); const TileOverlay to4 = TileOverlay(tileOverlayId: TileOverlayId('id4')); - final Set previous = Set.from([to1, to2, to3]); - final Set current1 = - Set.from([to2, to3Changed, to4]); - final Set current2 = - Set.from([to2, to3Changed, to4]); - final Set current3 = Set.from([to2, to4]); + final Set previous = {to1, to2, to3}; + final Set current1 = {to2, to3Changed, to4}; + final Set current2 = {to2, to3Changed, to4}; + final Set current3 = {to2, to4}; final TileOverlayUpdates updates1 = TileOverlayUpdates.from(previous, current1); final TileOverlayUpdates updates2 = @@ -89,9 +86,8 @@ void main() { const TileOverlay to3Changed = TileOverlay(tileOverlayId: TileOverlayId('id3'), transparency: 0.5); const TileOverlay to4 = TileOverlay(tileOverlayId: TileOverlayId('id4')); - final Set previous = Set.from([to1, to2, to3]); - final Set current = - Set.from([to2, to3Changed, to4]); + final Set previous = {to1, to2, to3}; + final Set current = {to2, to3Changed, to4}; final TileOverlayUpdates updates = TileOverlayUpdates.from(previous, current); expect( @@ -109,9 +105,8 @@ void main() { const TileOverlay to3Changed = TileOverlay(tileOverlayId: TileOverlayId('id3'), transparency: 0.5); const TileOverlay to4 = TileOverlay(tileOverlayId: TileOverlayId('id4')); - final Set previous = Set.from([to1, to2, to3]); - final Set current = - Set.from([to2, to3Changed, to4]); + final Set previous = {to1, to2, to3}; + final Set current = {to2, to3Changed, to4}; final TileOverlayUpdates updates = TileOverlayUpdates.from(previous, current); expect( diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_test.dart index 653958474185..ab49fd1a6c56 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_test.dart @@ -12,7 +12,7 @@ void main() { group('tile tests', () { test('toJson returns correct format', () async { - final Uint8List data = Uint8List.fromList([0, 1]); + final Uint8List data = Uint8List.fromList([0, 1]); final Tile tile = Tile(100, 200, data); final Object json = tile.toJson(); expect(json, { diff --git a/script/configs/custom_analysis.yaml b/script/configs/custom_analysis.yaml index bcd7d37dea0a..f735019d61c4 100644 --- a/script/configs/custom_analysis.yaml +++ b/script/configs/custom_analysis.yaml @@ -1,14 +1,12 @@ # Plugins that deliberately use their own analysis_options.yaml. # -# This only exists to allow incrementally switching to the newer, stricter -# analysis_options.yaml based on flutter/flutter, rather than the original -# rules based on pedantic (now at analysis_options_legacy.yaml). +# This only exists to allow incrementally adopting new analysis options in +# cases where a new option can't be applied to the entire repository at +# once. Do not add anything to this file without an issue reference and +# a concrete plan for removing it relatively quickly. # # DO NOT move or delete this file without updating # https://github.com/dart-lang/sdk/blob/master/tools/bots/flutter/analyze_flutter_plugins.sh # which references this file from source, but out-of-repo. # Contact stuartmorgan or devoncarew for assistance if necessary. -# TODO(ecosystem): Remove everything from this list. See: -# https://github.com/flutter/flutter/issues/76229 -- google_maps_flutter/google_maps_flutter_platform_interface diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 4b40aecefa9a..adc7bfcd29ae 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +- Supports empty custom analysis allow list files. + ## 0.8.6 - Adds `update-release-info` to apply changelog and optional version changes diff --git a/script/tool/lib/src/analyze_command.dart b/script/tool/lib/src/analyze_command.dart index 1cd85af076fc..8778b3de9d86 100644 --- a/script/tool/lib/src/analyze_command.dart +++ b/script/tool/lib/src/analyze_command.dart @@ -87,9 +87,11 @@ class AnalyzeCommand extends PackageLoopingCommand { getStringListArg(_customAnalysisFlag).expand((String item) { if (item.endsWith('.yaml')) { final File file = packagesDir.fileSystem.file(item); - return (loadYaml(file.readAsStringSync()) as YamlList) - .toList() - .cast(); + final Object? yaml = loadYaml(file.readAsStringSync()); + if (yaml == null) { + return []; + } + return (yaml as YamlList).toList().cast(); } return [item]; }).toSet(); diff --git a/script/tool/test/analyze_command_test.dart b/script/tool/test/analyze_command_test.dart index a9b83349306f..a4a47a221189 100644 --- a/script/tool/test/analyze_command_test.dart +++ b/script/tool/test/analyze_command_test.dart @@ -213,6 +213,18 @@ void main() { ])); }); + test('allows an empty config file', () async { + createFakePlugin('foo', packagesDir, + extraFiles: ['analysis_options.yaml']); + final File allowFile = packagesDir.childFile('custom.yaml'); + allowFile.createSync(); + + await expectLater( + () => runCapturingPrint( + runner, ['analyze', '--custom-analysis', allowFile.path]), + throwsA(isA())); + }); + // See: https://github.com/flutter/flutter/issues/78994 test('takes an empty allow list', () async { createFakePlugin('foo', packagesDir, From 58478a525d130b8e072188239f998831be602661 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Mon, 23 May 2022 15:41:47 -0700 Subject: [PATCH 350/844] [google_maps_flutter] Fix prefer_const_literals_to_create_immutables (#5811) --- .../google_maps_flutter_web/CHANGELOG.md | 4 ++++ .../google_maps_controller_test.dart | 16 ++++++++-------- .../google_maps_plugin_test.dart | 16 ++++++++-------- 3 files changed, 20 insertions(+), 16 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 8bd3d40babbc..95dfaede8d92 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 @@ +## NEXT + +* Adds `const` constructor parameters in example tests. + ## 0.3.3 * Removes custom `analysis_options.yaml` (and fixes code to comply with newest rules). 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 17fdd81df645..ef9136afa961 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 @@ -145,8 +145,8 @@ void main() { expect(() { controller.updateCircles( CircleUpdates.from( - {}, - {}, + const {}, + const {}, ), ); }, throwsAssertionError); @@ -159,8 +159,8 @@ void main() { expect(() { controller.updatePolygons( PolygonUpdates.from( - {}, - {}, + const {}, + const {}, ), ); }, throwsAssertionError); @@ -173,8 +173,8 @@ void main() { expect(() { controller.updatePolylines( PolylineUpdates.from( - {}, - {}, + const {}, + const {}, ), ); }, throwsAssertionError); @@ -187,8 +187,8 @@ void main() { expect(() { controller.updateMarkers( MarkerUpdates.from( - {}, - {}, + const {}, + const {}, ), ); }, throwsAssertionError); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart index f0fd5a232e00..f28a6041da2a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart @@ -233,8 +233,8 @@ void main() { // Geometry testWidgets('updateMarkers', (WidgetTester tester) async { final MarkerUpdates expectedUpdates = MarkerUpdates.from( - {}, - {}, + const {}, + const {}, ); await plugin.updateMarkers(expectedUpdates, mapId: mapId); @@ -243,8 +243,8 @@ void main() { }); testWidgets('updatePolygons', (WidgetTester tester) async { final PolygonUpdates expectedUpdates = PolygonUpdates.from( - {}, - {}, + const {}, + const {}, ); await plugin.updatePolygons(expectedUpdates, mapId: mapId); @@ -253,8 +253,8 @@ void main() { }); testWidgets('updatePolylines', (WidgetTester tester) async { final PolylineUpdates expectedUpdates = PolylineUpdates.from( - {}, - {}, + const {}, + const {}, ); await plugin.updatePolylines(expectedUpdates, mapId: mapId); @@ -263,8 +263,8 @@ void main() { }); testWidgets('updateCircles', (WidgetTester tester) async { final CircleUpdates expectedUpdates = CircleUpdates.from( - {}, - {}, + const {}, + const {}, ); await plugin.updateCircles(expectedUpdates, mapId: mapId); From ef62dc80d91aa01f8ccef51b3a74b03d7b2c9997 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 23 May 2022 20:08:12 -0400 Subject: [PATCH 351/844] Roll Flutter from ec20ea80ad98 to 7ece8f9f9435 (3 revisions) (#5813) --- .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 7e7222ac9f95..5b1865368a2c 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ec20ea80ad98007bd022648168e7b3cc38b2e665 +7ece8f9f9435c36bfc60e3d0285d27e61441d121 From 1b3d3c86fe7f140837eb288bd04ff73475fe237b Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 23 May 2022 22:18:05 -0400 Subject: [PATCH 352/844] Roll Flutter from 7ece8f9f9435 to f85209272dca (5 revisions) (#5815) --- .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 5b1865368a2c..39a860755c27 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -7ece8f9f9435c36bfc60e3d0285d27e61441d121 +f85209272dca619d15cb3cbc59fc6681319f7da7 From 09bd1941eeee5e952592de754d07bd5174a977b9 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 24 May 2022 11:43:10 -0400 Subject: [PATCH 353/844] Roll Flutter from f85209272dca to 9398c14daf27 (3 revisions) (#5817) --- .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 39a860755c27..7d00d38f478c 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f85209272dca619d15cb3cbc59fc6681319f7da7 +9398c14daf2708baf32f69f74ebdcdef98862d71 From b515201c6f9a7218977f3793e77556c341593d69 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 24 May 2022 12:48:12 -0400 Subject: [PATCH 354/844] Roll Flutter from 9398c14daf27 to 35c0a3ee5c98 (9 revisions) (#5821) --- .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 7d00d38f478c..1f072b3c33b4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -9398c14daf2708baf32f69f74ebdcdef98862d71 +35c0a3ee5c98926ec944087f1246843ab94aad5c From b2009a5c1994e19ee22e8bde43b2fb3262595eca Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 24 May 2022 13:28:09 -0400 Subject: [PATCH 355/844] [ci] Initial migration to Cirrus Apple silicon (#5794) --- .cirrus.yml | 72 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 2b8fde823826..a81f24a31035 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -57,9 +57,17 @@ macos_template: &MACOS_TEMPLATE # Only one macOS task can run in parallel without credits, so use them for # PRs on macOS. use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true' + +macos_intel_template: &MACOS_INTEL_TEMPLATE + << : *MACOS_TEMPLATE osx_instance: image: big-sur-xcode-13 +macos_arm_template: &MACOS_ARM_TEMPLATE + << : *MACOS_TEMPLATE + macos_instance: + image: ghcr.io/cirruslabs/macos-monterey-xcode:13.4 + # Light-workload Linux tasks. # These use default machines, with fewer CPUs, to reduce pressure on the # concurrency limits. @@ -298,15 +306,11 @@ task: drive_script: - ./script/tool_runner.sh drive-examples --web --exclude=script/configs/exclude_integration_web.yaml -# macOS tasks. +# ARM macOS tasks. task: - << : *MACOS_TEMPLATE + << : *MACOS_ARM_TEMPLATE << : *FLUTTER_UPGRADE_TEMPLATE matrix: - ### iOS+macOS tasks *** - - name: darwin-lint_podspecs - script: - - ./script/tool_runner.sh podspecs ### iOS tasks ### - name: ios-build_all_plugins env: @@ -315,6 +319,42 @@ task: CHANNEL: "master" CHANNEL: "stable" << : *BUILD_ALL_PLUGINS_APP_TEMPLATE + ### macOS desktop tasks ### + - name: macos-platform_tests + env: + matrix: + CHANNEL: "master" + CHANNEL: "stable" + PATH: $PATH:/usr/local/bin + build_script: + - flutter config --enable-macos-desktop + - ./script/tool_runner.sh build-examples --macos + xcode_analyze_script: + - ./script/tool_runner.sh xcode-analyze --macos + xcode_analyze_deprecation_script: + # Ensure we don't accidentally introduce deprecated code. + - ./script/tool_runner.sh xcode-analyze --macos --macos-min-version=12.3 + native_test_script: + - ./script/tool_runner.sh native-test --macos + drive_script: + - ./script/tool_runner.sh drive-examples --macos --exclude=script/configs/exclude_integration_macos.yaml + +# Intel macOS tasks. +task: + << : *MACOS_INTEL_TEMPLATE + << : *FLUTTER_UPGRADE_TEMPLATE + matrix: + ### iOS+macOS tasks *** + # TODO(stuartmorgan): Move this to ARM once google_maps_flutter has ARM + # support. `pod lint` makes a synthetic target that doesn't respect the + # pod's arch exclusions, so fails to build. + - name: darwin-lint_podspecs + script: + - ./script/tool_runner.sh podspecs + ### iOS tasks ### + # TODO(stuartmorgan): Swap this and ios-build_all_plugins once simulator + # tests are reliable on the ARM infrastructure. See discussion at + # https://github.com/flutter/plugins/pull/5693#issuecomment-1126011089 - name: ios-platform_tests env: PATH: $PATH:/usr/local/bin @@ -345,6 +385,8 @@ task: # So we run `drive-examples` after `native-test`; changing the order will result ci failure. - ./script/tool_runner.sh drive-examples --ios --exclude=script/configs/exclude_integration_ios.yaml ### macOS desktop tasks ### + # macos-platform_tests builds all the plugins on M1, so this build is run + # on Intel to give us build coverage of both host types. - name: macos-build_all_plugins env: BUILD_ALL_ARGS: "macos" @@ -354,21 +396,3 @@ task: setup_script: - flutter config --enable-macos-desktop << : *BUILD_ALL_PLUGINS_APP_TEMPLATE - - name: macos-platform_tests - env: - matrix: - CHANNEL: "master" - CHANNEL: "stable" - PATH: $PATH:/usr/local/bin - build_script: - - flutter config --enable-macos-desktop - - ./script/tool_runner.sh build-examples --macos - xcode_analyze_script: - - ./script/tool_runner.sh xcode-analyze --macos - xcode_analyze_deprecation_script: - # Ensure we don't accidentally introduce deprecated code. - - ./script/tool_runner.sh xcode-analyze --macos --macos-min-version=12.3 - native_test_script: - - ./script/tool_runner.sh native-test --macos - drive_script: - - ./script/tool_runner.sh drive-examples --macos --exclude=script/configs/exclude_integration_macos.yaml From fbb74e3e891e3663fb06f19e735ecf8e11db8c76 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 24 May 2022 14:58:17 -0400 Subject: [PATCH 356/844] Roll Flutter from 35c0a3ee5c98 to 7ca498489d09 (3 revisions) (#5823) --- .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 1f072b3c33b4..96af97726387 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -35c0a3ee5c98926ec944087f1246843ab94aad5c +7ca498489d09eb70491a8a7d69c6334a2fe403c4 From a8e9e6b1dc348c552ec3d4fa902a198fc44f2fd5 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 24 May 2022 16:03:09 -0400 Subject: [PATCH 357/844] Roll Flutter from 7ca498489d09 to e4c7f6e1b027 (4 revisions) (#5824) --- .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 96af97726387..fcc00aac3fe8 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -7ca498489d09eb70491a8a7d69c6334a2fe403c4 +e4c7f6e1b0277ff49da48de1d3628e14c9a6e84f From c3696c188b6273c30c3933cad1deb70ba901fa34 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 24 May 2022 17:08:10 -0400 Subject: [PATCH 358/844] Roll Flutter from e4c7f6e1b027 to ac29c11aecb8 (1 revision) (#5826) --- .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 fcc00aac3fe8..9e8ff7cd8d73 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e4c7f6e1b0277ff49da48de1d3628e14c9a6e84f +ac29c11aecb866d2265314e012d8d3e872cb7640 From 5942a8524135c824339fb82797d9200839d80616 Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Tue, 24 May 2022 15:13:12 -0700 Subject: [PATCH 359/844] Fix issue where map updates don't take effect in Flutter v3.0.0 (#5787) --- .../google_maps_flutter/CHANGELOG.md | 3 +- .../googlemaps/GoogleMapController.java | 78 ++++++++++++++++- .../googlemaps/GoogleMapControllerTest.java | 87 +++++++++++++++++++ .../google_maps_flutter/pubspec.yaml | 2 +- 4 files changed, 164 insertions(+), 6 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 57d24e2196a5..b4fb6622edc2 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.1.6 +* Fixes issue in Flutter v3.0.0 where some updates to the map don't take effect on Android. * Fixes iOS native unit tests on M1 devices. * Minor fixes for new analysis options. diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java b/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java index 9b8810354b8f..2c2287cf59d4 100644 --- a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java +++ b/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java @@ -12,9 +12,11 @@ import android.graphics.Point; import android.os.Bundle; import android.util.Log; +import android.view.Choreographer; import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleOwner; @@ -109,6 +111,11 @@ public View getView() { return mapView; } + @VisibleForTesting + /*package*/ void setView(MapView view) { + mapView = view; + } + void init() { lifecycleProvider.getLifecycle().addObserver(this); mapView.getMapAsync(this); @@ -126,6 +133,58 @@ private CameraPosition getCameraPosition() { return trackCameraPosition ? googleMap.getCameraPosition() : null; } + private boolean loadedCallbackPending = false; + + /** + * Invalidates the map view after the map has finished rendering. + * + *

gmscore GL renderer uses a {@link android.view.TextureView}. Android platform views that are + * displayed as a texture after Flutter v3.0.0. require that the view hierarchy is notified after + * all drawing operations have been flushed. + * + *

Since the GL renderer doesn't use standard Android views, and instead uses GL directly, we + * notify the view hierarchy by invalidating the view. + * + *

Unfortunately, when {@link GoogleMap.OnMapLoadedCallback} is fired, the texture may not have + * been updated yet. + * + *

To workaround this limitation, wait two frames. This ensures that at least the frame budget + * (16.66ms at 60hz) have passed since the drawing operation was issued. + */ + private void invalidateMapIfNeeded() { + if (googleMap == null || loadedCallbackPending) { + return; + } + loadedCallbackPending = true; + googleMap.setOnMapLoadedCallback( + new GoogleMap.OnMapLoadedCallback() { + @Override + public void onMapLoaded() { + loadedCallbackPending = false; + postFrameCallback( + () -> { + postFrameCallback( + () -> { + if (mapView != null) { + mapView.invalidate(); + } + }); + }); + } + }); + } + + private static void postFrameCallback(Runnable f) { + Choreographer.getInstance() + .postFrameCallback( + new Choreographer.FrameCallback() { + @Override + public void doFrame(long frameTimeNanos) { + f.run(); + } + }); + } + @Override public void onMapReady(GoogleMap googleMap) { this.googleMap = googleMap; @@ -244,6 +303,7 @@ public void onSnapshotReady(Bitmap bitmap) { } case "markers#update": { + invalidateMapIfNeeded(); List markersToAdd = call.argument("markersToAdd"); markersController.addMarkers(markersToAdd); List markersToChange = call.argument("markersToChange"); @@ -273,6 +333,7 @@ public void onSnapshotReady(Bitmap bitmap) { } case "polygons#update": { + invalidateMapIfNeeded(); List polygonsToAdd = call.argument("polygonsToAdd"); polygonsController.addPolygons(polygonsToAdd); List polygonsToChange = call.argument("polygonsToChange"); @@ -284,6 +345,7 @@ public void onSnapshotReady(Bitmap bitmap) { } case "polylines#update": { + invalidateMapIfNeeded(); List polylinesToAdd = call.argument("polylinesToAdd"); polylinesController.addPolylines(polylinesToAdd); List polylinesToChange = call.argument("polylinesToChange"); @@ -295,6 +357,7 @@ public void onSnapshotReady(Bitmap bitmap) { } case "circles#update": { + invalidateMapIfNeeded(); List circlesToAdd = call.argument("circlesToAdd"); circlesController.addCircles(circlesToAdd); List circlesToChange = call.argument("circlesToChange"); @@ -374,12 +437,17 @@ public void onSnapshotReady(Bitmap bitmap) { } case "map#setStyle": { - String mapStyle = (String) call.arguments; + invalidateMapIfNeeded(); boolean mapStyleSet; - if (mapStyle == null) { - mapStyleSet = googleMap.setMapStyle(null); + 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(new MapStyleOptions(mapStyle)); + mapStyleSet = googleMap.setMapStyle(null); } ArrayList mapStyleResult = new ArrayList<>(2); mapStyleResult.add(mapStyleSet); @@ -392,6 +460,7 @@ public void onSnapshotReady(Bitmap bitmap) { } case "tileOverlays#update": { + invalidateMapIfNeeded(); List> tileOverlaysToAdd = call.argument("tileOverlaysToAdd"); tileOverlaysController.addTileOverlays(tileOverlaysToAdd); List> tileOverlaysToChange = call.argument("tileOverlaysToChange"); @@ -403,6 +472,7 @@ public void onSnapshotReady(Bitmap bitmap) { } case "tileOverlays#clearTileCache": { + invalidateMapIfNeeded(); String tileOverlayId = call.argument("tileOverlayId"); tileOverlaysController.clearTileCache(tileOverlayId); result.success(null); diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java b/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java index 6bda085caf46..d8082b57e3db 100644 --- a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java +++ b/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java @@ -6,16 +6,24 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import android.content.Context; import android.os.Build; import androidx.activity.ComponentActivity; import androidx.test.core.app.ApplicationProvider; import com.google.android.gms.maps.GoogleMap; +import com.google.android.gms.maps.MapView; import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import java.util.HashMap; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; @@ -58,4 +66,83 @@ public void OnDestroyReleaseTheMap() throws InterruptedException { googleMapController.onDestroy(activity); assertNull(googleMapController.getView()); } + + @Test + public void InvalidateMapAfterMethodCalls() throws InterruptedException { + String[] methodsThatTriggerInvalidation = { + "markers#update", + "polygons#update", + "polylines#update", + "circles#update", + "map#setStyle", + "tileOverlays#update", + "tileOverlays#clearTileCache" + }; + + for (String methodName : methodsThatTriggerInvalidation) { + googleMapController = + new GoogleMapController(0, context, mockMessenger, activity::getLifecycle, null); + googleMapController.init(); + + mockGoogleMap = mock(GoogleMap.class); + googleMapController.onMapReady(mockGoogleMap); + + MethodChannel.Result result = mock(MethodChannel.Result.class); + System.out.println(methodName); + googleMapController.onMethodCall( + new MethodCall(methodName, new HashMap()), result); + + ArgumentCaptor argument = + ArgumentCaptor.forClass(GoogleMap.OnMapLoadedCallback.class); + verify(mockGoogleMap).setOnMapLoadedCallback(argument.capture()); + + MapView mapView = mock(MapView.class); + googleMapController.setView(mapView); + + verify(mapView, never()).invalidate(); + argument.getValue().onMapLoaded(); + verify(mapView).invalidate(); + } + } + + @Test + public void InvalidateMapOnceAfterMethodCall() throws InterruptedException { + googleMapController.onMapReady(mockGoogleMap); + + MethodChannel.Result result = mock(MethodChannel.Result.class); + googleMapController.onMethodCall( + new MethodCall("markers#update", new HashMap()), result); + googleMapController.onMethodCall( + new MethodCall("polygons#update", new HashMap()), result); + + ArgumentCaptor argument = + ArgumentCaptor.forClass(GoogleMap.OnMapLoadedCallback.class); + verify(mockGoogleMap).setOnMapLoadedCallback(argument.capture()); + + MapView mapView = mock(MapView.class); + googleMapController.setView(mapView); + + verify(mapView, never()).invalidate(); + argument.getValue().onMapLoaded(); + verify(mapView).invalidate(); + } + + @Test + public void MethodCalledAfterControllerIsDestroyed() throws InterruptedException { + googleMapController.onMapReady(mockGoogleMap); + MethodChannel.Result result = mock(MethodChannel.Result.class); + googleMapController.onMethodCall( + new MethodCall("markers#update", new HashMap()), result); + + ArgumentCaptor argument = + ArgumentCaptor.forClass(GoogleMap.OnMapLoadedCallback.class); + verify(mockGoogleMap).setOnMapLoadedCallback(argument.capture()); + + MapView mapView = mock(MapView.class); + googleMapController.setView(mapView); + googleMapController.onDestroy(activity); + + argument.getValue().onMapLoaded(); + verify(mapView, never()).invalidate(); + } } diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index 831f3ccd2963..59ee23d0b260 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/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.1.5 +version: 2.1.6 environment: sdk: ">=2.14.0 <3.0.0" From 2ec0a9723435903ed34e4d8648a23eba10ff6f81 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 24 May 2022 19:08:08 -0400 Subject: [PATCH 360/844] Roll Flutter from ac29c11aecb8 to efb9368573f6 (3 revisions) (#5827) --- .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 9e8ff7cd8d73..6b6ab9a94331 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ac29c11aecb866d2265314e012d8d3e872cb7640 +efb9368573f605fdf63b6143d7a352b400805a72 From 287af0f8f99d3afb9eaef1576ee34d683f0e2f79 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Tue, 24 May 2022 18:03:12 -0700 Subject: [PATCH 361/844] [ios_platform_images] ignore DecoderCallback deprecation (#5806) --- packages/ios_platform_images/CHANGELOG.md | 4 ++++ packages/ios_platform_images/lib/ios_platform_images.dart | 2 ++ packages/ios_platform_images/pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index ee8e96132fea..f7616dc9aa5d 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.0+9 + +* Ignores the warning for the upcoming deprecation of `DecoderCallback`. + ## 0.2.0+8 * Ignores the warning for the upcoming deprecation of `ImageProvider.load` in the correct line. diff --git a/packages/ios_platform_images/lib/ios_platform_images.dart b/packages/ios_platform_images/lib/ios_platform_images.dart index 1c5b4b817f40..29733a3f8c2d 100644 --- a/packages/ios_platform_images/lib/ios_platform_images.dart +++ b/packages/ios_platform_images/lib/ios_platform_images.dart @@ -66,6 +66,7 @@ class _FutureMemoryImage extends ImageProvider<_FutureMemoryImage> { /// See [ImageProvider.load]. // TODO(jmagman): Implement the new API once it lands, https://github.com/flutter/flutter/issues/103556 @override + // ignore: deprecated_member_use ImageStreamCompleter load(_FutureMemoryImage key, DecoderCallback decode) { return _FutureImageStreamCompleter( codec: _loadAsync(key, decode), @@ -75,6 +76,7 @@ class _FutureMemoryImage extends ImageProvider<_FutureMemoryImage> { Future _loadAsync( _FutureMemoryImage key, + // ignore: deprecated_member_use DecoderCallback decode, ) async { assert(key == this); diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index 1ce98c121637..c4397c551c27 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -2,7 +2,7 @@ name: ios_platform_images description: A plugin to share images between Flutter and iOS in add-to-app setups. repository: https://github.com/flutter/plugins/tree/main/packages/ios_platform_images issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+ios_platform_images%22 -version: 0.2.0+8 +version: 0.2.0+9 environment: sdk: ">=2.14.0 <3.0.0" From bdab12048fb464d2c1afc44641732ea5ff3a0ac7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 25 May 2022 13:18:13 -0400 Subject: [PATCH 362/844] Roll Flutter from efb9368573f6 to b5adbee145fd (10 revisions) (#5830) --- .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 6b6ab9a94331..d6c773a4c9a3 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -efb9368573f605fdf63b6143d7a352b400805a72 +b5adbee145fd99d72af8b5829ce2c7a6acf5ed53 From 431f7d234f3f12fe809e3b98ea0909ecd18fb213 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 25 May 2022 15:33:15 -0400 Subject: [PATCH 363/844] [camera] Move camera streaming to platform interface (#5783) --- .../camera_platform_interface/CHANGELOG.md | 3 +- .../method_channel/method_channel_camera.dart | 54 ++++++++ .../src/method_channel/type_conversion.dart | 61 +++++++++ .../platform_interface/camera_platform.dart | 15 +++ .../lib/src/types/camera_image_data.dart | 126 ++++++++++++++++++ .../lib/src/types/types.dart | 1 + .../camera_platform_interface/pubspec.yaml | 2 +- .../method_channel_camera_test.dart | 46 +++++++ .../method_channel/type_conversion_test.dart | 85 ++++++++++++ .../test/types/camera_image_data_test.dart | 38 ++++++ 10 files changed, 429 insertions(+), 2 deletions(-) create mode 100644 packages/camera/camera_platform_interface/lib/src/method_channel/type_conversion.dart create mode 100644 packages/camera/camera_platform_interface/lib/src/types/camera_image_data.dart create mode 100644 packages/camera/camera_platform_interface/test/method_channel/type_conversion_test.dart create mode 100644 packages/camera/camera_platform_interface/test/types/camera_image_data_test.dart diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index 3cad35d71ae5..5ecd8891fe20 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.2.0 +* Adds image streaming to the platform interface. * Removes unnecessary imports. ## 2.1.6 diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index c856f3467821..babef144b086 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -12,6 +12,8 @@ import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:stream_transform/stream_transform.dart'; +import 'type_conversion.dart'; + const MethodChannel _channel = MethodChannel('plugins.flutter.io/camera'); /// An implementation of [CameraPlatform] that uses method channels. @@ -48,6 +50,12 @@ class MethodChannelCamera extends CameraPlatform { final StreamController deviceEventStreamController = StreamController.broadcast(); + // The stream to receive frames from the native code. + StreamSubscription? _platformImageStreamSubscription; + + // The stream for vending frames to platform interface clients. + StreamController? _frameStreamController; + Stream _cameraEvents(int cameraId) => cameraEventStreamController.stream .where((CameraEvent event) => event.cameraId == cameraId); @@ -267,6 +275,52 @@ class MethodChannelCamera extends CameraPlatform { {'cameraId': cameraId}, ); + @override + Stream onStreamedFrameAvailable(int cameraId, + {CameraImageStreamOptions? options}) { + _frameStreamController = StreamController( + onListen: _onFrameStreamListen, + onPause: _onFrameStreamPauseResume, + onResume: _onFrameStreamPauseResume, + onCancel: _onFrameStreamCancel, + ); + return _frameStreamController!.stream; + } + + void _onFrameStreamListen() { + _startPlatformStream(); + } + + Future _startPlatformStream() async { + await _channel.invokeMethod('startImageStream'); + const EventChannel cameraEventChannel = + EventChannel('plugins.flutter.io/camera/imageStream'); + _platformImageStreamSubscription = + cameraEventChannel.receiveBroadcastStream().listen((dynamic imageData) { + if (defaultTargetPlatform == TargetPlatform.iOS) { + try { + _channel.invokeMethod('receivedImageStreamData'); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } + _frameStreamController! + .add(cameraImageFromPlatformData(imageData as Map)); + }); + } + + FutureOr _onFrameStreamCancel() async { + await _channel.invokeMethod('stopImageStream'); + await _platformImageStreamSubscription?.cancel(); + _platformImageStreamSubscription = null; + _frameStreamController = null; + } + + void _onFrameStreamPauseResume() { + throw CameraException('InvalidCall', + 'Pause and resume are not supported for onStreamedFrameAvailable'); + } + @override Future setFlashMode(int cameraId, FlashMode mode) => _channel.invokeMethod( diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/type_conversion.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/type_conversion.dart new file mode 100644 index 000000000000..9dffbbf6ae3a --- /dev/null +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/type_conversion.dart @@ -0,0 +1,61 @@ +// 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:typed_data'; + +import 'package:flutter/foundation.dart'; + +import '../types/types.dart'; + +/// Converts method channel call [data] for `receivedImageStreamData` to a +/// [CameraImageData]. +CameraImageData cameraImageFromPlatformData(Map data) { + return CameraImageData( + format: _cameraImageFormatFromPlatformData(data['format']), + height: data['height'] as int, + width: data['width'] as int, + lensAperture: data['lensAperture'] as double?, + sensorExposureTime: data['sensorExposureTime'] as int?, + sensorSensitivity: data['sensorSensitivity'] as double?, + planes: List.unmodifiable( + (data['planes'] as List).map( + (dynamic planeData) => _cameraImagePlaneFromPlatformData( + planeData as Map)))); +} + +CameraImageFormat _cameraImageFormatFromPlatformData(dynamic data) { + return CameraImageFormat(_imageFormatGroupFromPlatformData(data), raw: data); +} + +ImageFormatGroup _imageFormatGroupFromPlatformData(dynamic data) { + if (defaultTargetPlatform == TargetPlatform.android) { + switch (data) { + case 35: // android.graphics.ImageFormat.YUV_420_888 + return ImageFormatGroup.yuv420; + case 256: // android.graphics.ImageFormat.JPEG + return ImageFormatGroup.jpeg; + } + } + + if (defaultTargetPlatform == TargetPlatform.iOS) { + switch (data) { + case 875704438: // kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange + return ImageFormatGroup.yuv420; + + case 1111970369: // kCVPixelFormatType_32BGRA + return ImageFormatGroup.bgra8888; + } + } + + return ImageFormatGroup.unknown; +} + +CameraImagePlane _cameraImagePlaneFromPlatformData(Map data) { + return CameraImagePlane( + bytes: data['bytes'] as Uint8List, + bytesPerPixel: data['bytesPerPixel'] as int?, + bytesPerRow: data['bytesPerRow'] as int, + height: data['height'] as int?, + width: data['width'] as int?); +} 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 daa19b8b4011..eaa779a943db 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 @@ -149,6 +149,21 @@ abstract class CameraPlatform extends PlatformInterface { throw UnimplementedError('resumeVideoRecording() is not implemented.'); } + /// A new streamed frame is available. + /// + /// Listening to this stream will start streaming, and canceling will stop. + /// Pausing will throw a [CameraException], as pausing the stream would cause + /// very high memory usage; to temporarily stop receiving frames, cancel, then + /// listen again later. + /// + /// + // TODO(bmparr): Add options to control streaming settings (e.g., + // resolution and FPS). + Stream onStreamedFrameAvailable(int cameraId, + {CameraImageStreamOptions? options}) { + throw UnimplementedError('onStreamedFrameAvailable() is not implemented.'); + } + /// Sets the flash mode for the selected camera. /// On Web [FlashMode.auto] corresponds to [FlashMode.always]. Future setFlashMode(int cameraId, FlashMode mode) { diff --git a/packages/camera/camera_platform_interface/lib/src/types/camera_image_data.dart b/packages/camera/camera_platform_interface/lib/src/types/camera_image_data.dart new file mode 100644 index 000000000000..6971dbb39737 --- /dev/null +++ b/packages/camera/camera_platform_interface/lib/src/types/camera_image_data.dart @@ -0,0 +1,126 @@ +// 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:typed_data'; + +import 'package:flutter/foundation.dart'; + +import '../../camera_platform_interface.dart'; + +/// Options for configuring camera streaming. +/// +/// Currently unused; this exists for future-proofing of the platform interface +/// API. +@immutable +class CameraImageStreamOptions {} + +/// A single color plane of image data. +/// +/// The number and meaning of the planes in an image are determined by its +/// format. +@immutable +class CameraImagePlane { + /// Creates a new instance with the given bytes and optional metadata. + const CameraImagePlane({ + required this.bytes, + required this.bytesPerRow, + this.bytesPerPixel, + this.height, + this.width, + }); + + /// Bytes representing this plane. + final Uint8List bytes; + + /// The row stride for this color plane, in bytes. + final int bytesPerRow; + + /// The distance between adjacent pixel samples in bytes, when available. + final int? bytesPerPixel; + + /// Height of the pixel buffer, when available. + final int? height; + + /// Width of the pixel buffer, when available. + final int? width; +} + +/// Describes how pixels are represented in an image. +@immutable +class CameraImageFormat { + /// Create a new format with the given cross-platform group and raw underyling + /// platform identifier. + const CameraImageFormat(this.group, {required this.raw}); + + /// Describes the format group the raw image format falls into. + final ImageFormatGroup group; + + /// Raw version of the format from the underlying platform. + /// + /// On Android, this should be an `int` from class + /// `android.graphics.ImageFormat`. See + /// https://developer.android.com/reference/android/graphics/ImageFormat + /// + /// On iOS, this should be a `FourCharCode` constant from Pixel Format + /// Identifiers. See + /// https://developer.apple.com/documentation/corevideo/1563591-pixel_format_identifiers + final dynamic raw; +} + +/// A single complete image buffer from the platform camera. +/// +/// This class allows for direct application access to the pixel data of an +/// Image through one or more [Uint8List]. Each buffer is encapsulated in a +/// [CameraImagePlane] that describes the layout of the pixel data in that +/// plane. [CameraImageData] is not directly usable as a UI resource. +/// +/// Although not all image formats are planar on all platforms, this class +/// treats 1-dimensional images as single planar images. +@immutable +class CameraImageData { + /// Creates a new instance with the given format, planes, and metadata. + const CameraImageData({ + required this.format, + required this.planes, + required this.height, + required this.width, + this.lensAperture, + this.sensorExposureTime, + this.sensorSensitivity, + }); + + /// Format of the image provided. + /// + /// Determines the number of planes needed to represent the image, and + /// the general layout of the pixel data in each [Uint8List]. + final CameraImageFormat format; + + /// Height of the image in pixels. + /// + /// For formats where some color channels are subsampled, this is the height + /// of the largest-resolution plane. + final int height; + + /// Width of the image in pixels. + /// + /// For formats where some color channels are subsampled, this is the width + /// of the largest-resolution plane. + final int width; + + /// The pixels planes for this image. + /// + /// The number of planes is determined by the format of the image. + final List planes; + + /// The aperture settings for this image. + /// + /// Represented as an f-stop value. + final double? lensAperture; + + /// The sensor exposure time for this image in nanoseconds. + final int? sensorExposureTime; + + /// The sensor sensitivity in standard ISO arithmetic units. + final double? sensorSensitivity; +} diff --git a/packages/camera/camera_platform_interface/lib/src/types/types.dart b/packages/camera/camera_platform_interface/lib/src/types/types.dart index 0c24839d6445..3eb09fcb833c 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/types.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/types.dart @@ -4,6 +4,7 @@ export 'camera_description.dart'; export 'camera_exception.dart'; +export 'camera_image_data.dart'; export 'exposure_mode.dart'; export 'flash_mode.dart'; export 'focus_mode.dart'; diff --git a/packages/camera/camera_platform_interface/pubspec.yaml b/packages/camera/camera_platform_interface/pubspec.yaml index ab163b4e9f3f..473dcb552c82 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/plugins/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.1.6 +version: 2.2.0 environment: sdk: '>=2.12.0 <3.0.0' diff --git a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart index 7da4262cdf79..d096f0012c86 100644 --- a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart +++ b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart @@ -1038,6 +1038,52 @@ void main() { arguments: {'cameraId': cameraId}), ]); }); + + test('Should start streaming', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: 'plugins.flutter.io/camera', + methods: { + 'startImageStream': null, + 'stopImageStream': null, + }, + ); + + // Act + final StreamSubscription subscription = camera + .onStreamedFrameAvailable(cameraId) + .listen((CameraImageData imageData) {}); + + // Assert + expect(channel.log, [ + isMethodCall('startImageStream', arguments: null), + ]); + + subscription.cancel(); + }); + + test('Should stop streaming', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: 'plugins.flutter.io/camera', + methods: { + 'startImageStream': null, + 'stopImageStream': null, + }, + ); + + // Act + final StreamSubscription subscription = camera + .onStreamedFrameAvailable(cameraId) + .listen((CameraImageData imageData) {}); + subscription.cancel(); + + // Assert + expect(channel.log, [ + isMethodCall('startImageStream', arguments: null), + isMethodCall('stopImageStream', arguments: null), + ]); + }); }); }); } diff --git a/packages/camera/camera_platform_interface/test/method_channel/type_conversion_test.dart b/packages/camera/camera_platform_interface/test/method_channel/type_conversion_test.dart new file mode 100644 index 000000000000..a8ca45eca43b --- /dev/null +++ b/packages/camera/camera_platform_interface/test/method_channel/type_conversion_test.dart @@ -0,0 +1,85 @@ +// 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:typed_data'; + +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:camera_platform_interface/src/method_channel/type_conversion.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('CameraImageData can be created', () { + final CameraImageData cameraImage = + cameraImageFromPlatformData({ + 'format': 35, + 'height': 1, + 'width': 4, + 'lensAperture': 1.8, + 'sensorExposureTime': 9991324, + 'sensorSensitivity': 92.0, + 'planes': [ + { + 'bytes': Uint8List.fromList([1, 2, 3, 4]), + 'bytesPerPixel': 1, + 'bytesPerRow': 4, + 'height': 1, + 'width': 4 + } + ] + }); + expect(cameraImage.height, 1); + expect(cameraImage.width, 4); + expect(cameraImage.format.group, ImageFormatGroup.yuv420); + expect(cameraImage.planes.length, 1); + }); + + test('CameraImageData has ImageFormatGroup.yuv420 for iOS', () { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + + final CameraImageData cameraImage = + cameraImageFromPlatformData({ + 'format': 875704438, + 'height': 1, + 'width': 4, + 'lensAperture': 1.8, + 'sensorExposureTime': 9991324, + 'sensorSensitivity': 92.0, + 'planes': [ + { + 'bytes': Uint8List.fromList([1, 2, 3, 4]), + 'bytesPerPixel': 1, + 'bytesPerRow': 4, + 'height': 1, + 'width': 4 + } + ] + }); + expect(cameraImage.format.group, ImageFormatGroup.yuv420); + }); + + test('CameraImageData has ImageFormatGroup.yuv420 for Android', () { + debugDefaultTargetPlatformOverride = TargetPlatform.android; + + final CameraImageData cameraImage = + cameraImageFromPlatformData({ + 'format': 35, + 'height': 1, + 'width': 4, + 'lensAperture': 1.8, + 'sensorExposureTime': 9991324, + 'sensorSensitivity': 92.0, + 'planes': [ + { + 'bytes': Uint8List.fromList([1, 2, 3, 4]), + 'bytesPerPixel': 1, + 'bytesPerRow': 4, + 'height': 1, + 'width': 4 + } + ] + }); + expect(cameraImage.format.group, ImageFormatGroup.yuv420); + }); +} diff --git a/packages/camera/camera_platform_interface/test/types/camera_image_data_test.dart b/packages/camera/camera_platform_interface/test/types/camera_image_data_test.dart new file mode 100644 index 000000000000..f06213e2b0e4 --- /dev/null +++ b/packages/camera/camera_platform_interface/test/types/camera_image_data_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 'dart:typed_data'; + +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('CameraImageData can be created', () { + debugDefaultTargetPlatformOverride = TargetPlatform.android; + final CameraImageData cameraImage = CameraImageData( + format: const CameraImageFormat(ImageFormatGroup.jpeg, raw: 42), + height: 100, + width: 200, + lensAperture: 1.8, + sensorExposureTime: 11, + sensorSensitivity: 92.0, + planes: [ + CameraImagePlane( + bytes: Uint8List.fromList([1, 2, 3, 4]), + bytesPerRow: 4, + bytesPerPixel: 2, + height: 100, + width: 200) + ], + ); + expect(cameraImage.format.group, ImageFormatGroup.jpeg); + expect(cameraImage.lensAperture, 1.8); + expect(cameraImage.sensorExposureTime, 11); + expect(cameraImage.sensorSensitivity, 92.0); + expect(cameraImage.height, 100); + expect(cameraImage.width, 200); + expect(cameraImage.planes.length, 1); + }); +} From b26da9802ee26da60f0a258025af47a156dd07e7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 25 May 2022 17:33:18 -0400 Subject: [PATCH 364/844] Roll Flutter from b5adbee145fd to da24f105bd31 (10 revisions) (#5832) --- .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 d6c773a4c9a3..07cf2dc3a573 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b5adbee145fd99d72af8b5829ce2c7a6acf5ed53 +da24f105bd31ff2086c3f33105a0f2053bd14760 From c5ac270ca07ff72c744bd8ee03ad25cd06af8adc Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 25 May 2022 15:18:17 -0700 Subject: [PATCH 365/844] [webview_flutter_wkwebview] Update variable names for changes coming in flutter/plugins#5700 (#5829) --- .../FWFHTTPCookieStoreHostApiTests.m | 12 +- .../ios/RunnerTests/FWFInstanceManagerTests.m | 8 +- .../FWFNavigationDelegateHostApiTests.m | 4 +- .../ios/RunnerTests/FWFObjectHostApiTests.m | 22 +- .../RunnerTests/FWFPreferencesHostApiTests.m | 12 +- .../FWFScriptMessageHandlerHostApiTests.m | 4 +- .../RunnerTests/FWFScrollViewHostApiTests.m | 18 +- .../RunnerTests/FWFUIDelegateHostApiTests.m | 4 +- .../ios/RunnerTests/FWFUIViewHostApiTests.m | 12 +- .../FWFUserContentControllerHostApiTests.m | 38 +- .../FWFWebViewConfigurationHostApiTests.m | 22 +- .../ios/RunnerTests/FWFWebViewHostApiTests.m | 118 ++--- .../FWFWebsiteDataStoreHostApiTests.m | 16 +- .../ios/Classes/FWFGeneratedWebKitApis.h | 130 +++--- .../ios/Classes/FWFGeneratedWebKitApis.m | 322 ++++++------- .../ios/Classes/FWFHTTPCookieStoreHostApi.m | 18 +- .../ios/Classes/FWFInstanceManager.h | 4 +- .../ios/Classes/FWFInstanceManager.m | 3 +- .../Classes/FWFNavigationDelegateHostApi.m | 13 +- .../ios/Classes/FWFObjectHostApi.m | 16 +- .../ios/Classes/FWFPreferencesHostApi.m | 21 +- .../Classes/FWFScriptMessageHandlerHostApi.m | 9 +- .../ios/Classes/FWFScrollViewHostApi.m | 25 +- .../ios/Classes/FWFUIDelegateHostApi.m | 8 +- .../ios/Classes/FWFUIViewHostApi.m | 14 +- .../Classes/FWFUserContentControllerHostApi.m | 34 +- .../Classes/FWFWebViewConfigurationHostApi.m | 26 +- .../ios/Classes/FWFWebViewHostApi.m | 88 ++-- .../ios/Classes/FWFWebsiteDataStoreHostApi.m | 24 +- .../common/function_flutter_api_impls.dart | 6 +- .../lib/src/common/instance_manager.dart | 34 +- .../lib/src/common/web_kit.pigeon.dart | 218 ++++----- .../src/foundation/foundation_api_impls.dart | 14 +- .../lib/src/ui_kit/ui_kit_api_impls.dart | 23 +- .../lib/src/web_kit/web_kit_api_impls.dart | 188 ++++---- .../pigeons/web_kit.dart | 114 ++--- .../src/common/function_flutter_api_test.dart | 5 +- .../src/common/instance_manager_test.dart | 13 +- .../test/src/common/test_web_kit.pigeon.dart | 432 +++++++++--------- .../test/src/foundation/foundation_test.dart | 16 +- .../test/src/ui_kit/ui_kit_test.dart | 4 +- .../test/src/web_kit/web_kit_test.dart | 66 +-- 42 files changed, 1076 insertions(+), 1102 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFHTTPCookieStoreHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFHTTPCookieStoreHostApiTests.m index 315640a99247..45eefc3897ec 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFHTTPCookieStoreHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFHTTPCookieStoreHostApiTests.m @@ -14,15 +14,15 @@ @interface FWFHTTPCookieStoreHostApiTests : XCTestCase @implementation FWFHTTPCookieStoreHostApiTests - (void)testCreateFromWebsiteDataStoreWithIdentifier API_AVAILABLE(ios(11.0)) { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - FWFHTTPCookieStoreHostApiImpl *hostApi = + FWFHTTPCookieStoreHostApiImpl *hostAPI = [[FWFHTTPCookieStoreHostApiImpl alloc] initWithInstanceManager:instanceManager]; WKWebsiteDataStore *mockDataStore = OCMClassMock([WKWebsiteDataStore class]); OCMStub([mockDataStore httpCookieStore]).andReturn(OCMClassMock([WKHTTPCookieStore class])); - [instanceManager addInstance:mockDataStore withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockDataStore withIdentifier:0]; FlutterError *error; - [hostApi createFromWebsiteDataStoreWithIdentifier:@1 dataStoreIdentifier:@0 error:&error]; + [hostAPI createFromWebsiteDataStoreWithIdentifier:@1 dataStoreIdentifier:@0 error:&error]; WKHTTPCookieStore *cookieStore = (WKHTTPCookieStore *)[instanceManager instanceForIdentifier:1]; XCTAssertTrue([cookieStore isKindOfClass:[WKHTTPCookieStore class]]); XCTAssertNil(error); @@ -32,9 +32,9 @@ - (void)testSetCookie API_AVAILABLE(ios(11.0)) { WKHTTPCookieStore *mockHttpCookieStore = OCMClassMock([WKHTTPCookieStore class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockHttpCookieStore withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockHttpCookieStore withIdentifier:0]; - FWFHTTPCookieStoreHostApiImpl *hostApi = + FWFHTTPCookieStoreHostApiImpl *hostAPI = [[FWFHTTPCookieStoreHostApiImpl alloc] initWithInstanceManager:instanceManager]; FWFNSHttpCookieData *cookieData = [FWFNSHttpCookieData @@ -42,7 +42,7 @@ - (void)testSetCookie API_AVAILABLE(ios(11.0)) { makeWithValue:FWFNSHttpCookiePropertyKeyEnumName] ] propertyValues:@[ @"hello" ]]; FlutterError *__block blockError; - [hostApi setCookieForStoreWithIdentifier:@0 + [hostAPI setCookieForStoreWithIdentifier:@0 cookie:cookieData completion:^(FlutterError *error) { blockError = error; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m index 7b40da131d23..264b623dd8cf 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m @@ -9,11 +9,11 @@ @interface FWFInstanceManagerTests : XCTestCase @end @implementation FWFInstanceManagerTests -- (void)testAddInstance { +- (void)testAddInstanceCreatedFromDart { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; NSObject *object = [[NSObject alloc] init]; - [instanceManager addInstance:object withIdentifier:5]; + [instanceManager addDartCreatedInstance:object withIdentifier:5]; XCTAssertEqualObjects([instanceManager instanceForIdentifier:5], object); XCTAssertEqual([instanceManager identifierForInstance:object], 5); } @@ -21,7 +21,7 @@ - (void)testAddInstance { - (void)testRemoveInstance { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; NSObject *object = [[NSObject alloc] init]; - [instanceManager addInstance:object withIdentifier:5]; + [instanceManager addDartCreatedInstance:object withIdentifier:5]; [instanceManager removeInstance:object]; XCTAssertNil([instanceManager instanceForIdentifier:5]); @@ -31,7 +31,7 @@ - (void)testRemoveInstance { - (void)testRemoveInstanceWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; NSObject *object = [[NSObject alloc] init]; - [instanceManager addInstance:object withIdentifier:5]; + [instanceManager addDartCreatedInstance:object withIdentifier:5]; [instanceManager removeInstanceWithIdentifier:5]; XCTAssertNil([instanceManager instanceForIdentifier:5]); 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 02e473f8b795..9025b2e5ce43 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 @@ -14,11 +14,11 @@ @interface FWFNavigationDelegateHostApiTests : XCTestCase @implementation FWFNavigationDelegateHostApiTests - (void)testCreateWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - FWFNavigationDelegateHostApiImpl *hostApi = + FWFNavigationDelegateHostApiImpl *hostAPI = [[FWFNavigationDelegateHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi createWithIdentifier:@0 error:&error]; + [hostAPI createWithIdentifier:@0 error:&error]; FWFNavigationDelegate *navigationDelegate = (FWFNavigationDelegate *)[instanceManager instanceForIdentifier:0]; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m index 6886c2600e13..271a1d0eb696 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m @@ -16,16 +16,16 @@ - (void)testAddObserver { NSObject *mockObject = OCMClassMock([NSObject class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockObject withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockObject withIdentifier:0]; - FWFObjectHostApiImpl *hostApi = + FWFObjectHostApiImpl *hostAPI = [[FWFObjectHostApiImpl alloc] initWithInstanceManager:instanceManager]; NSObject *observerObject = [[NSObject alloc] init]; - [instanceManager addInstance:observerObject withIdentifier:1]; + [instanceManager addDartCreatedInstance:observerObject withIdentifier:1]; FlutterError *error; - [hostApi + [hostAPI addObserverForObjectWithIdentifier:@0 observerIdentifier:@1 keyPath:@"myKey" @@ -48,16 +48,16 @@ - (void)testRemoveObserver { NSObject *mockObject = OCMClassMock([NSObject class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockObject withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockObject withIdentifier:0]; - FWFObjectHostApiImpl *hostApi = + FWFObjectHostApiImpl *hostAPI = [[FWFObjectHostApiImpl alloc] initWithInstanceManager:instanceManager]; NSObject *observerObject = [[NSObject alloc] init]; - [instanceManager addInstance:observerObject withIdentifier:1]; + [instanceManager addDartCreatedInstance:observerObject withIdentifier:1]; FlutterError *error; - [hostApi removeObserverForObjectWithIdentifier:@0 + [hostAPI removeObserverForObjectWithIdentifier:@0 observerIdentifier:@1 keyPath:@"myKey" error:&error]; @@ -69,13 +69,13 @@ - (void)testDispose { NSObject *object = [[NSObject alloc] init]; FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:object withIdentifier:0]; + [instanceManager addDartCreatedInstance:object withIdentifier:0]; - FWFObjectHostApiImpl *hostApi = + FWFObjectHostApiImpl *hostAPI = [[FWFObjectHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi disposeObjectWithIdentifier:@0 error:&error]; + [hostAPI disposeObjectWithIdentifier:@0 error:&error]; XCTAssertEqual([instanceManager identifierForInstance:object], NSNotFound); XCTAssertNil(error); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFPreferencesHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFPreferencesHostApiTests.m index 1837a9373930..95b81ad5c389 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFPreferencesHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFPreferencesHostApiTests.m @@ -14,13 +14,13 @@ @interface FWFPreferencesHostApiTests : XCTestCase @implementation FWFPreferencesHostApiTests - (void)testCreateFromWebViewConfigurationWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - FWFPreferencesHostApiImpl *hostApi = + FWFPreferencesHostApiImpl *hostAPI = [[FWFPreferencesHostApiImpl alloc] initWithInstanceManager:instanceManager]; - [instanceManager addInstance:[[WKWebViewConfiguration alloc] init] withIdentifier:0]; + [instanceManager addDartCreatedInstance:[[WKWebViewConfiguration alloc] init] withIdentifier:0]; FlutterError *error; - [hostApi createFromWebViewConfigurationWithIdentifier:@1 configurationIdentifier:@0 error:&error]; + [hostAPI createFromWebViewConfigurationWithIdentifier:@1 configurationIdentifier:@0 error:&error]; WKPreferences *preferences = (WKPreferences *)[instanceManager instanceForIdentifier:1]; XCTAssertTrue([preferences isKindOfClass:[WKPreferences class]]); XCTAssertNil(error); @@ -30,13 +30,13 @@ - (void)testSetJavaScriptEnabled { WKPreferences *mockPreferences = OCMClassMock([WKPreferences class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockPreferences withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockPreferences withIdentifier:0]; - FWFPreferencesHostApiImpl *hostApi = + FWFPreferencesHostApiImpl *hostAPI = [[FWFPreferencesHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi setJavaScriptEnabledForPreferencesWithIdentifier:@0 isEnabled:@YES error:&error]; + [hostAPI setJavaScriptEnabledForPreferencesWithIdentifier:@0 isEnabled:@YES error:&error]; OCMVerify([mockPreferences setJavaScriptEnabled:YES]); XCTAssertNil(error); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m index cb8348fc4702..b74d21114cc4 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m @@ -14,11 +14,11 @@ @interface FWFScriptMessageHandlerHostApiTests : XCTestCase @implementation FWFScriptMessageHandlerHostApiTests - (void)testCreateWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - FWFScriptMessageHandlerHostApiImpl *hostApi = + FWFScriptMessageHandlerHostApiImpl *hostAPI = [[FWFScriptMessageHandlerHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi createWithIdentifier:@0 error:&error]; + [hostAPI createWithIdentifier:@0 error:&error]; FWFScriptMessageHandler *scriptMessageHandler = (FWFScriptMessageHandler *)[instanceManager instanceForIdentifier:0]; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScrollViewHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScrollViewHostApiTests.m index 87d17119e5e7..ede8dcf35d89 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScrollViewHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScrollViewHostApiTests.m @@ -17,14 +17,14 @@ - (void)testGetContentOffset { OCMStub([mockScrollView contentOffset]).andReturn(CGPointMake(1.0, 2.0)); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockScrollView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockScrollView withIdentifier:0]; - FWFScrollViewHostApiImpl *hostApi = + FWFScrollViewHostApiImpl *hostAPI = [[FWFScrollViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; NSArray *expectedValue = @[ @1.0, @2.0 ]; - XCTAssertEqualObjects([hostApi contentOffsetForScrollViewWithIdentifier:@0 error:&error], + XCTAssertEqualObjects([hostAPI contentOffsetForScrollViewWithIdentifier:@0 error:&error], expectedValue); XCTAssertNil(error); } @@ -34,13 +34,13 @@ - (void)testScrollBy { scrollView.contentOffset = CGPointMake(1, 2); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:scrollView withIdentifier:0]; + [instanceManager addDartCreatedInstance:scrollView withIdentifier:0]; - FWFScrollViewHostApiImpl *hostApi = + FWFScrollViewHostApiImpl *hostAPI = [[FWFScrollViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi scrollByForScrollViewWithIdentifier:@0 x:@1 y:@2 error:&error]; + [hostAPI scrollByForScrollViewWithIdentifier:@0 x:@1 y:@2 error:&error]; XCTAssertEqual(scrollView.contentOffset.x, 2); XCTAssertEqual(scrollView.contentOffset.y, 4); XCTAssertNil(error); @@ -50,13 +50,13 @@ - (void)testSetContentOffset { UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:scrollView withIdentifier:0]; + [instanceManager addDartCreatedInstance:scrollView withIdentifier:0]; - FWFScrollViewHostApiImpl *hostApi = + FWFScrollViewHostApiImpl *hostAPI = [[FWFScrollViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi setContentOffsetForScrollViewWithIdentifier:@0 toX:@1 y:@2 error:&error]; + [hostAPI setContentOffsetForScrollViewWithIdentifier:@0 toX:@1 y:@2 error:&error]; XCTAssertEqual(scrollView.contentOffset.x, 1); XCTAssertEqual(scrollView.contentOffset.y, 2); XCTAssertNil(error); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m index 2f7838be5aa8..4ee36ae7c492 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m @@ -14,11 +14,11 @@ @interface FWFUIDelegateHostApiTests : XCTestCase @implementation FWFUIDelegateHostApiTests - (void)testCreateWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - FWFUIDelegateHostApiImpl *hostApi = + FWFUIDelegateHostApiImpl *hostAPI = [[FWFUIDelegateHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi createWithIdentifier:@0 error:&error]; + [hostAPI createWithIdentifier:@0 error:&error]; FWFUIDelegate *delegate = (FWFUIDelegate *)[instanceManager instanceForIdentifier:0]; XCTAssertTrue([delegate conformsToProtocol:@protocol(WKUIDelegate)]); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIViewHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIViewHostApiTests.m index 8c7c9c9b45b1..65a24d97a39a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIViewHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIViewHostApiTests.m @@ -16,13 +16,13 @@ - (void)testSetBackgroundColor { UIView *mockUIView = OCMClassMock([UIView class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockUIView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockUIView withIdentifier:0]; - FWFUIViewHostApiImpl *hostApi = + FWFUIViewHostApiImpl *hostAPI = [[FWFUIViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi setBackgroundColorForViewWithIdentifier:@0 toValue:@123 error:&error]; + [hostAPI setBackgroundColorForViewWithIdentifier:@0 toValue:@123 error:&error]; OCMVerify([mockUIView setBackgroundColor:[UIColor colorWithRed:(123 >> 16 & 0xff) / 255.0 green:(123 >> 8 & 0xff) / 255.0 @@ -35,13 +35,13 @@ - (void)testSetOpaque { UIView *mockUIView = OCMClassMock([UIView class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockUIView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockUIView withIdentifier:0]; - FWFUIViewHostApiImpl *hostApi = + FWFUIViewHostApiImpl *hostAPI = [[FWFUIViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi setOpaqueForViewWithIdentifier:@0 isOpaque:@YES error:&error]; + [hostAPI setOpaqueForViewWithIdentifier:@0 isOpaque:@YES error:&error]; OCMVerify([mockUIView setOpaque:YES]); XCTAssertNil(error); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUserContentControllerHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUserContentControllerHostApiTests.m index d70341e87890..4f523e6da402 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUserContentControllerHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUserContentControllerHostApiTests.m @@ -14,13 +14,13 @@ @interface FWFUserContentControllerHostApiTests : XCTestCase @implementation FWFUserContentControllerHostApiTests - (void)testCreateFromWebViewConfigurationWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - FWFUserContentControllerHostApiImpl *hostApi = + FWFUserContentControllerHostApiImpl *hostAPI = [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]; - [instanceManager addInstance:[[WKWebViewConfiguration alloc] init] withIdentifier:0]; + [instanceManager addDartCreatedInstance:[[WKWebViewConfiguration alloc] init] withIdentifier:0]; FlutterError *error; - [hostApi createFromWebViewConfigurationWithIdentifier:@1 configurationIdentifier:@0 error:&error]; + [hostAPI createFromWebViewConfigurationWithIdentifier:@1 configurationIdentifier:@0 error:&error]; WKUserContentController *userContentController = (WKUserContentController *)[instanceManager instanceForIdentifier:1]; XCTAssertTrue([userContentController isKindOfClass:[WKUserContentController class]]); @@ -32,17 +32,17 @@ - (void)testAddScriptMessageHandler { OCMClassMock([WKUserContentController class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockUserContentController withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockUserContentController withIdentifier:0]; - FWFUserContentControllerHostApiImpl *hostApi = + FWFUserContentControllerHostApiImpl *hostAPI = [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]; id mockMessageHandler = OCMProtocolMock(@protocol(WKScriptMessageHandler)); - [instanceManager addInstance:mockMessageHandler withIdentifier:1]; + [instanceManager addDartCreatedInstance:mockMessageHandler withIdentifier:1]; FlutterError *error; - [hostApi addScriptMessageHandlerForControllerWithIdentifier:@0 + [hostAPI addScriptMessageHandlerForControllerWithIdentifier:@0 handlerIdentifier:@1 ofName:@"apple" error:&error]; @@ -55,13 +55,13 @@ - (void)testRemoveScriptMessageHandler { OCMClassMock([WKUserContentController class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockUserContentController withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockUserContentController withIdentifier:0]; - FWFUserContentControllerHostApiImpl *hostApi = + FWFUserContentControllerHostApiImpl *hostAPI = [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi removeScriptMessageHandlerForControllerWithIdentifier:@0 name:@"apple" error:&error]; + [hostAPI removeScriptMessageHandlerForControllerWithIdentifier:@0 name:@"apple" error:&error]; OCMVerify([mockUserContentController removeScriptMessageHandlerForName:@"apple"]); XCTAssertNil(error); } @@ -71,13 +71,13 @@ - (void)testRemoveAllScriptMessageHandlers API_AVAILABLE(ios(14.0)) { OCMClassMock([WKUserContentController class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockUserContentController withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockUserContentController withIdentifier:0]; - FWFUserContentControllerHostApiImpl *hostApi = + FWFUserContentControllerHostApiImpl *hostAPI = [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi removeAllScriptMessageHandlersForControllerWithIdentifier:@0 error:&error]; + [hostAPI removeAllScriptMessageHandlersForControllerWithIdentifier:@0 error:&error]; OCMVerify([mockUserContentController removeAllScriptMessageHandlers]); XCTAssertNil(error); } @@ -87,13 +87,13 @@ - (void)testAddUserScript { OCMClassMock([WKUserContentController class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockUserContentController withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockUserContentController withIdentifier:0]; - FWFUserContentControllerHostApiImpl *hostApi = + FWFUserContentControllerHostApiImpl *hostAPI = [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi + [hostAPI addUserScriptForControllerWithIdentifier:@0 userScript: [FWFWKUserScriptData @@ -114,13 +114,13 @@ - (void)testRemoveAllUserScripts { OCMClassMock([WKUserContentController class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockUserContentController withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockUserContentController withIdentifier:0]; - FWFUserContentControllerHostApiImpl *hostApi = + FWFUserContentControllerHostApiImpl *hostAPI = [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi removeAllUserScriptsForControllerWithIdentifier:@0 error:&error]; + [hostAPI removeAllUserScriptsForControllerWithIdentifier:@0 error:&error]; OCMVerify([mockUserContentController removeAllUserScripts]); XCTAssertNil(error); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewConfigurationHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewConfigurationHostApiTests.m index dab10799891b..e09e16b62e45 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewConfigurationHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewConfigurationHostApiTests.m @@ -14,11 +14,11 @@ @interface FWFWebViewConfigurationHostApiTests : XCTestCase @implementation FWFWebViewConfigurationHostApiTests - (void)testCreateWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - FWFWebViewConfigurationHostApiImpl *hostApi = + FWFWebViewConfigurationHostApiImpl *hostAPI = [[FWFWebViewConfigurationHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi createWithIdentifier:@0 error:&error]; + [hostAPI createWithIdentifier:@0 error:&error]; WKWebViewConfiguration *configuration = (WKWebViewConfiguration *)[instanceManager instanceForIdentifier:0]; XCTAssertTrue([configuration isKindOfClass:[WKWebViewConfiguration class]]); @@ -27,15 +27,15 @@ - (void)testCreateWithIdentifier { - (void)testCreateFromWebViewWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - FWFWebViewConfigurationHostApiImpl *hostApi = + FWFWebViewConfigurationHostApiImpl *hostAPI = [[FWFWebViewConfigurationHostApiImpl alloc] initWithInstanceManager:instanceManager]; WKWebView *mockWebView = OCMClassMock([WKWebView class]); OCMStub([mockWebView configuration]).andReturn(OCMClassMock([WKWebViewConfiguration class])); - [instanceManager addInstance:mockWebView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; FlutterError *error; - [hostApi createFromWebViewWithIdentifier:@1 webViewIdentifier:@0 error:&error]; + [hostAPI createFromWebViewWithIdentifier:@1 webViewIdentifier:@0 error:&error]; WKWebViewConfiguration *configuration = (WKWebViewConfiguration *)[instanceManager instanceForIdentifier:1]; XCTAssertTrue([configuration isKindOfClass:[WKWebViewConfiguration class]]); @@ -46,13 +46,13 @@ - (void)testSetAllowsInlineMediaPlayback { WKWebViewConfiguration *mockWebViewConfiguration = OCMClassMock([WKWebViewConfiguration class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebViewConfiguration withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebViewConfiguration withIdentifier:0]; - FWFWebViewConfigurationHostApiImpl *hostApi = + FWFWebViewConfigurationHostApiImpl *hostAPI = [[FWFWebViewConfigurationHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:@0 + [hostAPI setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:@0 isAllowed:@NO error:&error]; OCMVerify([mockWebViewConfiguration setAllowsInlineMediaPlayback:NO]); @@ -63,13 +63,13 @@ - (void)testSetMediaTypesRequiringUserActionForPlayback { WKWebViewConfiguration *mockWebViewConfiguration = OCMClassMock([WKWebViewConfiguration class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebViewConfiguration withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebViewConfiguration withIdentifier:0]; - FWFWebViewConfigurationHostApiImpl *hostApi = + FWFWebViewConfigurationHostApiImpl *hostAPI = [[FWFWebViewConfigurationHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi + [hostAPI setMediaTypesRequiresUserActionForConfigurationWithIdentifier:@0 forTypes:@[ [FWFWKAudiovisualMediaTypeEnumData diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m index 87960c07ee5e..0c71e3391dbb 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m @@ -14,13 +14,13 @@ @interface FWFWebViewHostApiTests : XCTestCase @implementation FWFWebViewHostApiTests - (void)testCreateWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - FWFWebViewHostApiImpl *hostApi = + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; - [instanceManager addInstance:[[WKWebViewConfiguration alloc] init] withIdentifier:0]; + [instanceManager addDartCreatedInstance:[[WKWebViewConfiguration alloc] init] withIdentifier:0]; FlutterError *error; - [hostApi createWithIdentifier:@1 configurationIdentifier:@0 error:&error]; + [hostAPI createWithIdentifier:@1 configurationIdentifier:@0 error:&error]; WKWebView *webView = (WKWebView *)[instanceManager instanceForIdentifier:1]; XCTAssertTrue([webView isKindOfClass:[WKWebView class]]); XCTAssertNil(error); @@ -30,9 +30,9 @@ - (void)testLoadRequest { FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostApi = + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; @@ -40,7 +40,7 @@ - (void)testLoadRequest { httpMethod:@"get" httpBody:nil allHttpHeaderFields:@{@"a" : @"header"}]; - [hostApi loadRequestForWebViewWithIdentifier:@0 request:requestData error:&error]; + [hostAPI loadRequestForWebViewWithIdentifier:@0 request:requestData error:&error]; NSURL *url = [NSURL URLWithString:@"https://www.flutter.dev"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; @@ -55,9 +55,9 @@ - (void)testLoadRequestWithInvalidUrl { OCMReject([mockWebView loadRequest:OCMOCK_ANY]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostApi = + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; @@ -65,7 +65,7 @@ - (void)testLoadRequestWithInvalidUrl { httpMethod:nil httpBody:nil allHttpHeaderFields:@{}]; - [hostApi loadRequestForWebViewWithIdentifier:@0 request:requestData error:&error]; + [hostAPI loadRequestForWebViewWithIdentifier:@0 request:requestData error:&error]; XCTAssertNotNil(error); XCTAssertEqualObjects(error.code, @"FWFURLRequestParsingError"); XCTAssertEqualObjects(error.message, @"Failed instantiating an NSURLRequest."); @@ -76,13 +76,13 @@ - (void)testSetCustomUserAgent { FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostApi = + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi setUserAgentForWebViewWithIdentifier:@0 userAgent:@"userA" error:&error]; + [hostAPI setUserAgentForWebViewWithIdentifier:@0 userAgent:@"userA" error:&error]; OCMVerify([mockWebView setCustomUserAgent:@"userA"]); XCTAssertNil(error); } @@ -92,13 +92,13 @@ - (void)testURL { OCMStub([mockWebView URL]).andReturn([NSURL URLWithString:@"https://www.flutter.dev/"]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostApi = + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - XCTAssertEqualObjects([hostApi URLForWebViewWithIdentifier:@0 error:&error], + XCTAssertEqualObjects([hostAPI URLForWebViewWithIdentifier:@0 error:&error], @"https://www.flutter.dev/"); XCTAssertNil(error); } @@ -108,13 +108,13 @@ - (void)testCanGoBack { OCMStub([mockWebView canGoBack]).andReturn(YES); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostApi = + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - XCTAssertEqualObjects([hostApi canGoBackForWebViewWithIdentifier:@0 error:&error], @YES); + XCTAssertEqualObjects([hostAPI canGoBackForWebViewWithIdentifier:@0 error:&error], @YES); XCTAssertNil(error); } @@ -122,16 +122,16 @@ - (void)testSetUIDelegate { FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostApi = + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; id mockDelegate = OCMProtocolMock(@protocol(WKUIDelegate)); - [instanceManager addInstance:mockDelegate withIdentifier:1]; + [instanceManager addDartCreatedInstance:mockDelegate withIdentifier:1]; FlutterError *error; - [hostApi setUIDelegateForWebViewWithIdentifier:@0 delegateIdentifier:@1 error:&error]; + [hostAPI setUIDelegateForWebViewWithIdentifier:@0 delegateIdentifier:@1 error:&error]; OCMVerify([mockWebView setUIDelegate:mockDelegate]); XCTAssertNil(error); } @@ -140,16 +140,16 @@ - (void)testSetNavigationDelegate { FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostApi = + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; id mockDelegate = OCMProtocolMock(@protocol(WKNavigationDelegate)); - [instanceManager addInstance:mockDelegate withIdentifier:1]; + [instanceManager addDartCreatedInstance:mockDelegate withIdentifier:1]; FlutterError *error; - [hostApi setNavigationDelegateForWebViewWithIdentifier:@0 delegateIdentifier:@1 error:&error]; + [hostAPI setNavigationDelegateForWebViewWithIdentifier:@0 delegateIdentifier:@1 error:&error]; OCMVerify([mockWebView setNavigationDelegate:mockDelegate]); XCTAssertNil(error); } @@ -159,13 +159,13 @@ - (void)testEstimatedProgress { OCMStub([mockWebView estimatedProgress]).andReturn(34.0); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostApi = + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - XCTAssertEqualObjects([hostApi estimatedProgressForWebViewWithIdentifier:@0 error:&error], @34.0); + XCTAssertEqualObjects([hostAPI estimatedProgressForWebViewWithIdentifier:@0 error:&error], @34.0); XCTAssertNil(error); } @@ -173,13 +173,13 @@ - (void)testloadHTMLString { FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostApi = + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi loadHTMLForWebViewWithIdentifier:@0 + [hostAPI loadHTMLForWebViewWithIdentifier:@0 HTMLString:@"myString" baseURL:@"myBaseUrl" error:&error]; @@ -191,13 +191,13 @@ - (void)testLoadFileURL { FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostApi = + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi loadFileForWebViewWithIdentifier:@0 + [hostAPI loadFileForWebViewWithIdentifier:@0 fileURL:@"myFolder/apple.txt" readAccessURL:@"myFolder" error:&error]; @@ -212,7 +212,7 @@ - (void)testLoadFlutterAsset { FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; FWFAssetManager *mockAssetManager = OCMClassMock([FWFAssetManager class]); OCMStub([mockAssetManager lookupKeyForAsset:@"assets/index.html"]) @@ -222,13 +222,13 @@ - (void)testLoadFlutterAsset { OCMStub([mockBundle URLForResource:@"myFolder/assets/index" withExtension:@"html"]) .andReturn([NSURL URLWithString:@"webview_flutter/myFolder/assets/index.html"]); - FWFWebViewHostApiImpl *hostApi = + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager bundle:mockBundle assetManager:mockAssetManager]; FlutterError *error; - [hostApi loadAssetForWebViewWithIdentifier:@0 assetKey:@"assets/index.html" error:&error]; + [hostAPI loadAssetForWebViewWithIdentifier:@0 assetKey:@"assets/index.html" error:&error]; XCTAssertNil(error); OCMVerify([mockWebView @@ -241,13 +241,13 @@ - (void)testCanGoForward { OCMStub([mockWebView canGoForward]).andReturn(NO); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostApi = + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - XCTAssertEqualObjects([hostApi canGoForwardForWebViewWithIdentifier:@0 error:&error], @NO); + XCTAssertEqualObjects([hostAPI canGoForwardForWebViewWithIdentifier:@0 error:&error], @NO); XCTAssertNil(error); } @@ -255,13 +255,13 @@ - (void)testGoBack { FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostApi = + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi goBackForWebViewWithIdentifier:@0 error:&error]; + [hostAPI goBackForWebViewWithIdentifier:@0 error:&error]; OCMVerify([mockWebView goBack]); XCTAssertNil(error); } @@ -270,13 +270,13 @@ - (void)testGoForward { FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostApi = + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi goForwardForWebViewWithIdentifier:@0 error:&error]; + [hostAPI goForwardForWebViewWithIdentifier:@0 error:&error]; OCMVerify([mockWebView goForward]); XCTAssertNil(error); } @@ -285,13 +285,13 @@ - (void)testReload { FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostApi = + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi reloadWebViewWithIdentifier:@0 error:&error]; + [hostAPI reloadWebViewWithIdentifier:@0 error:&error]; OCMVerify([mockWebView reload]); XCTAssertNil(error); } @@ -301,13 +301,13 @@ - (void)testTitle { OCMStub([mockWebView title]).andReturn(@"myTitle"); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostApi = + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - XCTAssertEqualObjects([hostApi titleForWebViewWithIdentifier:@0 error:&error], @"myTitle"); + XCTAssertEqualObjects([hostAPI titleForWebViewWithIdentifier:@0 error:&error], @"myTitle"); XCTAssertNil(error); } @@ -315,13 +315,13 @@ - (void)testSetAllowsBackForwardNavigationGestures { FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostApi = + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi setAllowsBackForwardForWebViewWithIdentifier:@0 isAllowed:@YES error:&error]; + [hostAPI setAllowsBackForwardForWebViewWithIdentifier:@0 isAllowed:@YES error:&error]; OCMVerify([mockWebView setAllowsBackForwardNavigationGestures:YES]); XCTAssertNil(error); } @@ -334,14 +334,14 @@ - (void)testEvaluateJavaScript { completionHandler:([OCMArg invokeBlockWithArgs:@"result", [NSNull null], nil])]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebView withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostApi = + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; NSString __block *returnValue; FlutterError __block *returnError; - [hostApi evaluateJavaScriptForWebViewWithIdentifier:@0 + [hostAPI evaluateJavaScriptForWebViewWithIdentifier:@0 javaScriptString:@"runJavaScript" completion:^(id result, FlutterError *error) { returnValue = result; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebsiteDataStoreHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebsiteDataStoreHostApiTests.m index c754f78551b9..18bc21facd21 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebsiteDataStoreHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebsiteDataStoreHostApiTests.m @@ -14,13 +14,13 @@ @interface FWFWebsiteDataStoreHostApiTests : XCTestCase @implementation FWFWebsiteDataStoreHostApiTests - (void)testCreateFromWebViewConfigurationWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - FWFWebsiteDataStoreHostApiImpl *hostApi = + FWFWebsiteDataStoreHostApiImpl *hostAPI = [[FWFWebsiteDataStoreHostApiImpl alloc] initWithInstanceManager:instanceManager]; - [instanceManager addInstance:[[WKWebViewConfiguration alloc] init] withIdentifier:0]; + [instanceManager addDartCreatedInstance:[[WKWebViewConfiguration alloc] init] withIdentifier:0]; FlutterError *error; - [hostApi createFromWebViewConfigurationWithIdentifier:@1 configurationIdentifier:@0 error:&error]; + [hostAPI createFromWebViewConfigurationWithIdentifier:@1 configurationIdentifier:@0 error:&error]; WKWebsiteDataStore *dataStore = (WKWebsiteDataStore *)[instanceManager instanceForIdentifier:1]; XCTAssertTrue([dataStore isKindOfClass:[WKWebsiteDataStore class]]); XCTAssertNil(error); @@ -28,11 +28,11 @@ - (void)testCreateFromWebViewConfigurationWithIdentifier { - (void)testCreateDefaultDataStoreWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - FWFWebsiteDataStoreHostApiImpl *hostApi = + FWFWebsiteDataStoreHostApiImpl *hostAPI = [[FWFWebsiteDataStoreHostApiImpl alloc] initWithInstanceManager:instanceManager]; FlutterError *error; - [hostApi createDefaultDataStoreWithIdentifier:@0 error:&error]; + [hostAPI createDefaultDataStoreWithIdentifier:@0 error:&error]; WKWebsiteDataStore *dataStore = (WKWebsiteDataStore *)[instanceManager instanceForIdentifier:0]; XCTAssertEqualObjects(dataStore, [WKWebsiteDataStore defaultDataStore]); XCTAssertNil(error); @@ -52,14 +52,14 @@ - (void)testRemoveDataOfTypes { completionHandler:([OCMArg invokeBlock])]); FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - [instanceManager addInstance:mockWebsiteDataStore withIdentifier:0]; + [instanceManager addDartCreatedInstance:mockWebsiteDataStore withIdentifier:0]; - FWFWebsiteDataStoreHostApiImpl *hostApi = + FWFWebsiteDataStoreHostApiImpl *hostAPI = [[FWFWebsiteDataStoreHostApiImpl alloc] initWithInstanceManager:instanceManager]; NSNumber __block *returnValue; FlutterError *__block blockError; - [hostApi removeDataFromDataStoreWithIdentifier:@0 + [hostAPI removeDataFromDataStoreWithIdentifier:@0 ofTypes:@[ [FWFWKWebsiteDataTypeEnumData makeWithValue:FWFWKWebsiteDataTypeEnumLocalStorage] 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 7d2bd44050ea..3617b4ecc731 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 (v3.0.3), do not edit directly. +// Autogenerated from Pigeon (v3.1.2), do not edit directly. // See also: https://pub.dev/packages/pigeon #import @protocol FlutterBinaryMessenger; @@ -159,12 +159,12 @@ typedef NS_ENUM(NSUInteger, FWFNSHttpCookiePropertyKeyEnum) { NSObject *FWFWKWebsiteDataStoreHostApiGetCodec(void); @protocol FWFWKWebsiteDataStoreHostApi -- (void)createFromWebViewConfigurationWithIdentifier:(NSNumber *)instanceId - configurationIdentifier:(NSNumber *)configurationInstanceId +- (void)createFromWebViewConfigurationWithIdentifier:(NSNumber *)identifier + configurationIdentifier:(NSNumber *)configurationIdentifier error:(FlutterError *_Nullable *_Nonnull)error; -- (void)createDefaultDataStoreWithIdentifier:(NSNumber *)instanceId +- (void)createDefaultDataStoreWithIdentifier:(NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error; -- (void)removeDataFromDataStoreWithIdentifier:(NSNumber *)instanceId +- (void)removeDataFromDataStoreWithIdentifier:(NSNumber *)identifier ofTypes:(NSArray *)dataTypes modifiedSince:(NSNumber *)modificationTimeInSecondsSinceEpoch completion:(void (^)(NSNumber *_Nullable, @@ -179,10 +179,10 @@ extern void FWFWKWebsiteDataStoreHostApiSetup( NSObject *FWFUIViewHostApiGetCodec(void); @protocol FWFUIViewHostApi -- (void)setBackgroundColorForViewWithIdentifier:(NSNumber *)instanceId +- (void)setBackgroundColorForViewWithIdentifier:(NSNumber *)identifier toValue:(nullable NSNumber *)value error:(FlutterError *_Nullable *_Nonnull)error; -- (void)setOpaqueForViewWithIdentifier:(NSNumber *)instanceId +- (void)setOpaqueForViewWithIdentifier:(NSNumber *)identifier isOpaque:(NSNumber *)opaque error:(FlutterError *_Nullable *_Nonnull)error; @end @@ -194,18 +194,18 @@ extern void FWFUIViewHostApiSetup(id binaryMessenger, NSObject *FWFUIScrollViewHostApiGetCodec(void); @protocol FWFUIScrollViewHostApi -- (void)createFromWebViewWithIdentifier:(NSNumber *)instanceId - webViewIdentifier:(NSNumber *)webViewInstanceId +- (void)createFromWebViewWithIdentifier:(NSNumber *)identifier + webViewIdentifier:(NSNumber *)webViewIdentifier error:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. - (nullable NSArray *) - contentOffsetForScrollViewWithIdentifier:(NSNumber *)instanceId + contentOffsetForScrollViewWithIdentifier:(NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error; -- (void)scrollByForScrollViewWithIdentifier:(NSNumber *)instanceId +- (void)scrollByForScrollViewWithIdentifier:(NSNumber *)identifier x:(NSNumber *)x y:(NSNumber *)y error:(FlutterError *_Nullable *_Nonnull)error; -- (void)setContentOffsetForScrollViewWithIdentifier:(NSNumber *)instanceId +- (void)setContentOffsetForScrollViewWithIdentifier:(NSNumber *)identifier toX:(NSNumber *)x y:(NSNumber *)y error:(FlutterError *_Nullable *_Nonnull)error; @@ -218,17 +218,17 @@ extern void FWFUIScrollViewHostApiSetup(id binaryMesseng NSObject *FWFWKWebViewConfigurationHostApiGetCodec(void); @protocol FWFWKWebViewConfigurationHostApi -- (void)createWithIdentifier:(NSNumber *)instanceId error:(FlutterError *_Nullable *_Nonnull)error; -- (void)createFromWebViewWithIdentifier:(NSNumber *)instanceId - webViewIdentifier:(NSNumber *)webViewInstanceId +- (void)createWithIdentifier:(NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error; +- (void)createFromWebViewWithIdentifier:(NSNumber *)identifier + webViewIdentifier:(NSNumber *)webViewIdentifier error:(FlutterError *_Nullable *_Nonnull)error; -- (void)setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:(NSNumber *)instanceId +- (void)setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:(NSNumber *)identifier isAllowed:(NSNumber *)allow error: (FlutterError *_Nullable *_Nonnull) error; - (void) - setMediaTypesRequiresUserActionForConfigurationWithIdentifier:(NSNumber *)instanceId + setMediaTypesRequiresUserActionForConfigurationWithIdentifier:(NSNumber *)identifier forTypes: (NSArray< FWFWKAudiovisualMediaTypeEnumData @@ -246,25 +246,25 @@ extern void FWFWKWebViewConfigurationHostApiSetup( NSObject *FWFWKUserContentControllerHostApiGetCodec(void); @protocol FWFWKUserContentControllerHostApi -- (void)createFromWebViewConfigurationWithIdentifier:(NSNumber *)instanceId - configurationIdentifier:(NSNumber *)configurationInstanceId +- (void)createFromWebViewConfigurationWithIdentifier:(NSNumber *)identifier + configurationIdentifier:(NSNumber *)configurationIdentifier error:(FlutterError *_Nullable *_Nonnull)error; -- (void)addScriptMessageHandlerForControllerWithIdentifier:(NSNumber *)instanceId - handlerIdentifier:(NSNumber *)handlerInstanceid +- (void)addScriptMessageHandlerForControllerWithIdentifier:(NSNumber *)identifier + handlerIdentifier:(NSNumber *)handlerIdentifier ofName:(NSString *)name error:(FlutterError *_Nullable *_Nonnull)error; -- (void)removeScriptMessageHandlerForControllerWithIdentifier:(NSNumber *)instanceId +- (void)removeScriptMessageHandlerForControllerWithIdentifier:(NSNumber *)identifier name:(NSString *)name error:(FlutterError *_Nullable *_Nonnull) error; -- (void)removeAllScriptMessageHandlersForControllerWithIdentifier:(NSNumber *)instanceId +- (void)removeAllScriptMessageHandlersForControllerWithIdentifier:(NSNumber *)identifier error: (FlutterError *_Nullable *_Nonnull) error; -- (void)addUserScriptForControllerWithIdentifier:(NSNumber *)instanceId +- (void)addUserScriptForControllerWithIdentifier:(NSNumber *)identifier userScript:(FWFWKUserScriptData *)userScript error:(FlutterError *_Nullable *_Nonnull)error; -- (void)removeAllUserScriptsForControllerWithIdentifier:(NSNumber *)instanceId +- (void)removeAllUserScriptsForControllerWithIdentifier:(NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error; @end @@ -276,10 +276,10 @@ extern void FWFWKUserContentControllerHostApiSetup( NSObject *FWFWKPreferencesHostApiGetCodec(void); @protocol FWFWKPreferencesHostApi -- (void)createFromWebViewConfigurationWithIdentifier:(NSNumber *)instanceId - configurationIdentifier:(NSNumber *)configurationInstanceId +- (void)createFromWebViewConfigurationWithIdentifier:(NSNumber *)identifier + configurationIdentifier:(NSNumber *)configurationIdentifier error:(FlutterError *_Nullable *_Nonnull)error; -- (void)setJavaScriptEnabledForPreferencesWithIdentifier:(NSNumber *)instanceId +- (void)setJavaScriptEnabledForPreferencesWithIdentifier:(NSNumber *)identifier isEnabled:(NSNumber *)enabled error:(FlutterError *_Nullable *_Nonnull)error; @end @@ -291,7 +291,7 @@ extern void FWFWKPreferencesHostApiSetup(id binaryMessen NSObject *FWFWKScriptMessageHandlerHostApiGetCodec(void); @protocol FWFWKScriptMessageHandlerHostApi -- (void)createWithIdentifier:(NSNumber *)instanceId error:(FlutterError *_Nullable *_Nonnull)error; +- (void)createWithIdentifier:(NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error; @end extern void FWFWKScriptMessageHandlerHostApiSetup( @@ -302,9 +302,9 @@ extern void FWFWKScriptMessageHandlerHostApiSetup( NSObject *FWFWKNavigationDelegateHostApiGetCodec(void); @protocol FWFWKNavigationDelegateHostApi -- (void)createWithIdentifier:(NSNumber *)instanceId error:(FlutterError *_Nullable *_Nonnull)error; -- (void)setDidFinishNavigationForDelegateWithIdentifier:(NSNumber *)instanceId - functionIdentifier:(nullable NSNumber *)functionInstanceId +- (void)createWithIdentifier:(NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setDidFinishNavigationForDelegateWithIdentifier:(NSNumber *)identifier + functionIdentifier:(nullable NSNumber *)functionIdentifier error:(FlutterError *_Nullable *_Nonnull)error; @end @@ -317,8 +317,8 @@ NSObject *FWFWKNavigationDelegateFlutterApiGetCodec(void); @interface FWFWKNavigationDelegateFlutterApi : NSObject - (instancetype)initWithBinaryMessenger:(id)binaryMessenger; -- (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)functionInstanceId - webViewIdentifier:(NSNumber *)webViewInstanceId +- (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)functionIdentifier + webViewIdentifier:(NSNumber *)webViewIdentifier URL:(nullable NSString *)url completion:(void (^)(NSError *_Nullable))completion; @end @@ -326,16 +326,16 @@ NSObject *FWFWKNavigationDelegateFlutterApiGetCodec(void); NSObject *FWFNSObjectHostApiGetCodec(void); @protocol FWFNSObjectHostApi -- (void)disposeObjectWithIdentifier:(NSNumber *)instanceId +- (void)disposeObjectWithIdentifier:(NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error; -- (void)addObserverForObjectWithIdentifier:(NSNumber *)instanceId - observerIdentifier:(NSNumber *)observerInstanceId +- (void)addObserverForObjectWithIdentifier:(NSNumber *)identifier + observerIdentifier:(NSNumber *)observerIdentifier keyPath:(NSString *)keyPath options: (NSArray *)options error:(FlutterError *_Nullable *_Nonnull)error; -- (void)removeObserverForObjectWithIdentifier:(NSNumber *)instanceId - observerIdentifier:(NSNumber *)observerInstanceId +- (void)removeObserverForObjectWithIdentifier:(NSNumber *)identifier + observerIdentifier:(NSNumber *)observerIdentifier keyPath:(NSString *)keyPath error:(FlutterError *_Nullable *_Nonnull)error; @end @@ -348,65 +348,65 @@ NSObject *FWFFunctionFlutterApiGetCodec(void); @interface FWFFunctionFlutterApi : NSObject - (instancetype)initWithBinaryMessenger:(id)binaryMessenger; -- (void)disposeFunctionWithIdentifier:(NSNumber *)instanceId +- (void)disposeFunctionWithIdentifier:(NSNumber *)identifier completion:(void (^)(NSError *_Nullable))completion; @end /// The codec used by FWFWKWebViewHostApi. NSObject *FWFWKWebViewHostApiGetCodec(void); @protocol FWFWKWebViewHostApi -- (void)createWithIdentifier:(NSNumber *)instanceId - configurationIdentifier:(NSNumber *)configurationInstanceId +- (void)createWithIdentifier:(NSNumber *)identifier + configurationIdentifier:(NSNumber *)configurationIdentifier error:(FlutterError *_Nullable *_Nonnull)error; -- (void)setUIDelegateForWebViewWithIdentifier:(NSNumber *)instanceId - delegateIdentifier:(nullable NSNumber *)uiDelegateInstanceId +- (void)setUIDelegateForWebViewWithIdentifier:(NSNumber *)identifier + delegateIdentifier:(nullable NSNumber *)uiDelegateIdentifier error:(FlutterError *_Nullable *_Nonnull)error; -- (void)setNavigationDelegateForWebViewWithIdentifier:(NSNumber *)instanceId +- (void)setNavigationDelegateForWebViewWithIdentifier:(NSNumber *)identifier delegateIdentifier: - (nullable NSNumber *)navigationDelegateInstanceId + (nullable NSNumber *)navigationDelegateIdentifier error:(FlutterError *_Nullable *_Nonnull)error; -- (nullable NSString *)URLForWebViewWithIdentifier:(NSNumber *)instanceId +- (nullable NSString *)URLForWebViewWithIdentifier:(NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. -- (nullable NSNumber *)estimatedProgressForWebViewWithIdentifier:(NSNumber *)instanceId +- (nullable NSNumber *)estimatedProgressForWebViewWithIdentifier:(NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull) error; -- (void)loadRequestForWebViewWithIdentifier:(NSNumber *)instanceId +- (void)loadRequestForWebViewWithIdentifier:(NSNumber *)identifier request:(FWFNSUrlRequestData *)request error:(FlutterError *_Nullable *_Nonnull)error; -- (void)loadHTMLForWebViewWithIdentifier:(NSNumber *)instanceId +- (void)loadHTMLForWebViewWithIdentifier:(NSNumber *)identifier HTMLString:(NSString *)string baseURL:(nullable NSString *)baseUrl error:(FlutterError *_Nullable *_Nonnull)error; -- (void)loadFileForWebViewWithIdentifier:(NSNumber *)instanceId +- (void)loadFileForWebViewWithIdentifier:(NSNumber *)identifier fileURL:(NSString *)url readAccessURL:(NSString *)readAccessUrl error:(FlutterError *_Nullable *_Nonnull)error; -- (void)loadAssetForWebViewWithIdentifier:(NSNumber *)instanceId +- (void)loadAssetForWebViewWithIdentifier:(NSNumber *)identifier assetKey:(NSString *)key error:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. -- (nullable NSNumber *)canGoBackForWebViewWithIdentifier:(NSNumber *)instanceId +- (nullable NSNumber *)canGoBackForWebViewWithIdentifier:(NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. -- (nullable NSNumber *)canGoForwardForWebViewWithIdentifier:(NSNumber *)instanceId +- (nullable NSNumber *)canGoForwardForWebViewWithIdentifier:(NSNumber *)identifier error: (FlutterError *_Nullable *_Nonnull)error; -- (void)goBackForWebViewWithIdentifier:(NSNumber *)instanceId +- (void)goBackForWebViewWithIdentifier:(NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error; -- (void)goForwardForWebViewWithIdentifier:(NSNumber *)instanceId +- (void)goForwardForWebViewWithIdentifier:(NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error; -- (void)reloadWebViewWithIdentifier:(NSNumber *)instanceId +- (void)reloadWebViewWithIdentifier:(NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error; -- (nullable NSString *)titleForWebViewWithIdentifier:(NSNumber *)instanceId +- (nullable NSString *)titleForWebViewWithIdentifier:(NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error; -- (void)setAllowsBackForwardForWebViewWithIdentifier:(NSNumber *)instanceId +- (void)setAllowsBackForwardForWebViewWithIdentifier:(NSNumber *)identifier isAllowed:(NSNumber *)allow error:(FlutterError *_Nullable *_Nonnull)error; -- (void)setUserAgentForWebViewWithIdentifier:(NSNumber *)instanceId +- (void)setUserAgentForWebViewWithIdentifier:(NSNumber *)identifier userAgent:(nullable NSString *)userAgent error:(FlutterError *_Nullable *_Nonnull)error; -- (void)evaluateJavaScriptForWebViewWithIdentifier:(NSNumber *)instanceId +- (void)evaluateJavaScriptForWebViewWithIdentifier:(NSNumber *)identifier javaScriptString:(NSString *)javaScriptString completion:(void (^)(id _Nullable, FlutterError *_Nullable))completion; @@ -419,7 +419,7 @@ extern void FWFWKWebViewHostApiSetup(id binaryMessenger, NSObject *FWFWKUIDelegateHostApiGetCodec(void); @protocol FWFWKUIDelegateHostApi -- (void)createWithIdentifier:(NSNumber *)instanceId error:(FlutterError *_Nullable *_Nonnull)error; +- (void)createWithIdentifier:(NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error; @end extern void FWFWKUIDelegateHostApiSetup(id binaryMessenger, @@ -429,10 +429,10 @@ extern void FWFWKUIDelegateHostApiSetup(id binaryMesseng NSObject *FWFWKHttpCookieStoreHostApiGetCodec(void); @protocol FWFWKHttpCookieStoreHostApi -- (void)createFromWebsiteDataStoreWithIdentifier:(NSNumber *)instanceId - dataStoreIdentifier:(NSNumber *)websiteDataStoreInstanceId +- (void)createFromWebsiteDataStoreWithIdentifier:(NSNumber *)identifier + dataStoreIdentifier:(NSNumber *)websiteDataStoreIdentifier error:(FlutterError *_Nullable *_Nonnull)error; -- (void)setCookieForStoreWithIdentifier:(NSNumber *)instanceId +- (void)setCookieForStoreWithIdentifier:(NSNumber *)identifier cookie:(FWFNSHttpCookieData *)cookie completion:(void (^)(FlutterError *_Nullable))completion; @end 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 aaeccc5cc1b0..1d20cb350175 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 (v3.0.3), do not edit directly. +// Autogenerated from Pigeon (v3.1.2), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "FWFGeneratedWebKitApis.h" #import @@ -14,13 +14,13 @@ NSDictionary *errorDict = (NSDictionary *)[NSNull null]; if (error) { errorDict = @{ - @"code" : (error.code ? error.code : [NSNull null]), - @"message" : (error.message ? error.message : [NSNull null]), - @"details" : (error.details ? error.details : [NSNull null]), + @"code" : (error.code ?: [NSNull null]), + @"message" : (error.message ?: [NSNull null]), + @"details" : (error.details ?: [NSNull null]), }; } return @{ - @"result" : (result ? result : [NSNull null]), + @"result" : (result ?: [NSNull null]), @"error" : errorDict, }; } @@ -80,7 +80,9 @@ + (FWFNSKeyValueObservingOptionsEnumData *)fromMap:(NSDictionary *)dict { return pigeonResult; } - (NSDictionary *)toMap { - return [NSDictionary dictionaryWithObjectsAndKeys:@(self.value), @"value", nil]; + return @{ + @"value" : @(self.value), + }; } @end @@ -98,7 +100,9 @@ + (FWFWKUserScriptInjectionTimeEnumData *)fromMap:(NSDictionary *)dict { return pigeonResult; } - (NSDictionary *)toMap { - return [NSDictionary dictionaryWithObjectsAndKeys:@(self.value), @"value", nil]; + return @{ + @"value" : @(self.value), + }; } @end @@ -116,7 +120,9 @@ + (FWFWKAudiovisualMediaTypeEnumData *)fromMap:(NSDictionary *)dict { return pigeonResult; } - (NSDictionary *)toMap { - return [NSDictionary dictionaryWithObjectsAndKeys:@(self.value), @"value", nil]; + return @{ + @"value" : @(self.value), + }; } @end @@ -132,7 +138,9 @@ + (FWFWKWebsiteDataTypeEnumData *)fromMap:(NSDictionary *)dict { return pigeonResult; } - (NSDictionary *)toMap { - return [NSDictionary dictionaryWithObjectsAndKeys:@(self.value), @"value", nil]; + return @{ + @"value" : @(self.value), + }; } @end @@ -150,7 +158,9 @@ + (FWFNSHttpCookiePropertyKeyEnumData *)fromMap:(NSDictionary *)dict { return pigeonResult; } - (NSDictionary *)toMap { - return [NSDictionary dictionaryWithObjectsAndKeys:@(self.value), @"value", nil]; + return @{ + @"value" : @(self.value), + }; } @end @@ -177,14 +187,12 @@ + (FWFNSUrlRequestData *)fromMap:(NSDictionary *)dict { return pigeonResult; } - (NSDictionary *)toMap { - return [NSDictionary - dictionaryWithObjectsAndKeys:(self.url ? self.url : [NSNull null]), @"url", - (self.httpMethod ? self.httpMethod : [NSNull null]), - @"httpMethod", (self.httpBody ? self.httpBody : [NSNull null]), - @"httpBody", - (self.allHttpHeaderFields ? self.allHttpHeaderFields - : [NSNull null]), - @"allHttpHeaderFields", nil]; + return @{ + @"url" : (self.url ?: [NSNull null]), + @"httpMethod" : (self.httpMethod ?: [NSNull null]), + @"httpBody" : (self.httpBody ?: [NSNull null]), + @"allHttpHeaderFields" : (self.allHttpHeaderFields ?: [NSNull null]), + }; } @end @@ -209,13 +217,11 @@ + (FWFWKUserScriptData *)fromMap:(NSDictionary *)dict { return pigeonResult; } - (NSDictionary *)toMap { - return [NSDictionary - dictionaryWithObjectsAndKeys:(self.source ? self.source : [NSNull null]), @"source", - (self.injectionTime ? [self.injectionTime toMap] - : [NSNull null]), - @"injectionTime", - (self.isMainFrameOnly ? self.isMainFrameOnly : [NSNull null]), - @"isMainFrameOnly", nil]; + return @{ + @"source" : (self.source ?: [NSNull null]), + @"injectionTime" : (self.injectionTime ? [self.injectionTime toMap] : [NSNull null]), + @"isMainFrameOnly" : (self.isMainFrameOnly ?: [NSNull null]), + }; } @end @@ -236,11 +242,10 @@ + (FWFNSHttpCookieData *)fromMap:(NSDictionary *)dict { return pigeonResult; } - (NSDictionary *)toMap { - return [NSDictionary - dictionaryWithObjectsAndKeys:(self.propertyKeys ? self.propertyKeys : [NSNull null]), - @"propertyKeys", - (self.propertyValues ? self.propertyValues : [NSNull null]), - @"propertyValues", nil]; + return @{ + @"propertyKeys" : (self.propertyKeys ?: [NSNull null]), + @"propertyValues" : (self.propertyValues ?: [NSNull null]), + }; } @end @@ -310,11 +315,11 @@ void FWFWKWebsiteDataStoreHostApiSetup(id binaryMessenge api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); - NSNumber *arg_configurationInstanceId = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_configurationIdentifier = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api createFromWebViewConfigurationWithIdentifier:arg_instanceId - configurationIdentifier:arg_configurationInstanceId + [api createFromWebViewConfigurationWithIdentifier:arg_identifier + configurationIdentifier:arg_configurationIdentifier error:&error]; callback(wrapResult(nil, error)); }]; @@ -334,9 +339,9 @@ void FWFWKWebsiteDataStoreHostApiSetup(id binaryMessenge api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FlutterError *error; - [api createDefaultDataStoreWithIdentifier:arg_instanceId error:&error]; + [api createDefaultDataStoreWithIdentifier:arg_identifier error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -357,10 +362,10 @@ void FWFWKWebsiteDataStoreHostApiSetup(id binaryMessenge api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); NSArray *arg_dataTypes = GetNullableObjectAtIndex(args, 1); NSNumber *arg_modificationTimeInSecondsSinceEpoch = GetNullableObjectAtIndex(args, 2); - [api removeDataFromDataStoreWithIdentifier:arg_instanceId + [api removeDataFromDataStoreWithIdentifier:arg_identifier ofTypes:arg_dataTypes modifiedSince:arg_modificationTimeInSecondsSinceEpoch completion:^(NSNumber *_Nullable output, @@ -420,10 +425,10 @@ void FWFUIViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); NSNumber *arg_value = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api setBackgroundColorForViewWithIdentifier:arg_instanceId toValue:arg_value error:&error]; + [api setBackgroundColorForViewWithIdentifier:arg_identifier toValue:arg_value error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -442,10 +447,10 @@ void FWFUIViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); NSNumber *arg_opaque = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api setOpaqueForViewWithIdentifier:arg_instanceId isOpaque:arg_opaque error:&error]; + [api setOpaqueForViewWithIdentifier:arg_identifier isOpaque:arg_opaque error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -500,11 +505,11 @@ void FWFUIScrollViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); - NSNumber *arg_webViewInstanceId = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_webViewIdentifier = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api createFromWebViewWithIdentifier:arg_instanceId - webViewIdentifier:arg_webViewInstanceId + [api createFromWebViewWithIdentifier:arg_identifier + webViewIdentifier:arg_webViewIdentifier error:&error]; callback(wrapResult(nil, error)); }]; @@ -524,9 +529,9 @@ void FWFUIScrollViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FlutterError *error; - NSArray *output = [api contentOffsetForScrollViewWithIdentifier:arg_instanceId + NSArray *output = [api contentOffsetForScrollViewWithIdentifier:arg_identifier error:&error]; callback(wrapResult(output, error)); }]; @@ -546,11 +551,11 @@ void FWFUIScrollViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); NSNumber *arg_x = GetNullableObjectAtIndex(args, 1); NSNumber *arg_y = GetNullableObjectAtIndex(args, 2); FlutterError *error; - [api scrollByForScrollViewWithIdentifier:arg_instanceId x:arg_x y:arg_y error:&error]; + [api scrollByForScrollViewWithIdentifier:arg_identifier x:arg_x y:arg_y error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -570,11 +575,11 @@ void FWFUIScrollViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); NSNumber *arg_x = GetNullableObjectAtIndex(args, 1); NSNumber *arg_y = GetNullableObjectAtIndex(args, 2); FlutterError *error; - [api setContentOffsetForScrollViewWithIdentifier:arg_instanceId + [api setContentOffsetForScrollViewWithIdentifier:arg_identifier toX:arg_x y:arg_y error:&error]; @@ -648,9 +653,9 @@ void FWFWKWebViewConfigurationHostApiSetup(id binaryMess api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FlutterError *error; - [api createWithIdentifier:arg_instanceId error:&error]; + [api createWithIdentifier:arg_identifier error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -670,11 +675,11 @@ void FWFWKWebViewConfigurationHostApiSetup(id binaryMess api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); - NSNumber *arg_webViewInstanceId = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_webViewIdentifier = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api createFromWebViewWithIdentifier:arg_instanceId - webViewIdentifier:arg_webViewInstanceId + [api createFromWebViewWithIdentifier:arg_identifier + webViewIdentifier:arg_webViewIdentifier error:&error]; callback(wrapResult(nil, error)); }]; @@ -697,10 +702,10 @@ void FWFWKWebViewConfigurationHostApiSetup(id binaryMess api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); NSNumber *arg_allow = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:arg_instanceId + [api setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:arg_identifier isAllowed:arg_allow error:&error]; callback(wrapResult(nil, error)); @@ -725,10 +730,10 @@ void FWFWKWebViewConfigurationHostApiSetup(id binaryMess api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); NSArray *arg_types = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api setMediaTypesRequiresUserActionForConfigurationWithIdentifier:arg_instanceId + [api setMediaTypesRequiresUserActionForConfigurationWithIdentifier:arg_identifier forTypes:arg_types error:&error]; callback(wrapResult(nil, error)); @@ -810,11 +815,11 @@ void FWFWKUserContentControllerHostApiSetup(id binaryMes api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); - NSNumber *arg_configurationInstanceId = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_configurationIdentifier = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api createFromWebViewConfigurationWithIdentifier:arg_instanceId - configurationIdentifier:arg_configurationInstanceId + [api createFromWebViewConfigurationWithIdentifier:arg_identifier + configurationIdentifier:arg_configurationIdentifier error:&error]; callback(wrapResult(nil, error)); }]; @@ -837,12 +842,12 @@ void FWFWKUserContentControllerHostApiSetup(id binaryMes api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); - NSNumber *arg_handlerInstanceid = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_handlerIdentifier = GetNullableObjectAtIndex(args, 1); NSString *arg_name = GetNullableObjectAtIndex(args, 2); FlutterError *error; - [api addScriptMessageHandlerForControllerWithIdentifier:arg_instanceId - handlerIdentifier:arg_handlerInstanceid + [api addScriptMessageHandlerForControllerWithIdentifier:arg_identifier + handlerIdentifier:arg_handlerIdentifier ofName:arg_name error:&error]; callback(wrapResult(nil, error)); @@ -865,10 +870,10 @@ void FWFWKUserContentControllerHostApiSetup(id binaryMes api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); NSString *arg_name = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api removeScriptMessageHandlerForControllerWithIdentifier:arg_instanceId + [api removeScriptMessageHandlerForControllerWithIdentifier:arg_identifier name:arg_name error:&error]; callback(wrapResult(nil, error)); @@ -891,9 +896,9 @@ void FWFWKUserContentControllerHostApiSetup(id binaryMes api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FlutterError *error; - [api removeAllScriptMessageHandlersForControllerWithIdentifier:arg_instanceId error:&error]; + [api removeAllScriptMessageHandlersForControllerWithIdentifier:arg_identifier error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -913,10 +918,10 @@ void FWFWKUserContentControllerHostApiSetup(id binaryMes api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FWFWKUserScriptData *arg_userScript = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api addUserScriptForControllerWithIdentifier:arg_instanceId + [api addUserScriptForControllerWithIdentifier:arg_identifier userScript:arg_userScript error:&error]; callback(wrapResult(nil, error)); @@ -938,9 +943,9 @@ void FWFWKUserContentControllerHostApiSetup(id binaryMes api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FlutterError *error; - [api removeAllUserScriptsForControllerWithIdentifier:arg_instanceId error:&error]; + [api removeAllUserScriptsForControllerWithIdentifier:arg_identifier error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -996,11 +1001,11 @@ void FWFWKPreferencesHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); - NSNumber *arg_configurationInstanceId = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_configurationIdentifier = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api createFromWebViewConfigurationWithIdentifier:arg_instanceId - configurationIdentifier:arg_configurationInstanceId + [api createFromWebViewConfigurationWithIdentifier:arg_identifier + configurationIdentifier:arg_configurationIdentifier error:&error]; callback(wrapResult(nil, error)); }]; @@ -1021,10 +1026,10 @@ void FWFWKPreferencesHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); NSNumber *arg_enabled = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api setJavaScriptEnabledForPreferencesWithIdentifier:arg_instanceId + [api setJavaScriptEnabledForPreferencesWithIdentifier:arg_identifier isEnabled:arg_enabled error:&error]; callback(wrapResult(nil, error)); @@ -1080,9 +1085,9 @@ void FWFWKScriptMessageHandlerHostApiSetup(id binaryMess api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FlutterError *error; - [api createWithIdentifier:arg_instanceId error:&error]; + [api createWithIdentifier:arg_identifier error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -1136,9 +1141,9 @@ void FWFWKNavigationDelegateHostApiSetup(id binaryMessen api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FlutterError *error; - [api createWithIdentifier:arg_instanceId error:&error]; + [api createWithIdentifier:arg_identifier error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -1159,11 +1164,11 @@ void FWFWKNavigationDelegateHostApiSetup(id binaryMessen api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); - NSNumber *arg_functionInstanceId = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_functionIdentifier = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api setDidFinishNavigationForDelegateWithIdentifier:arg_instanceId - functionIdentifier:arg_functionInstanceId + [api setDidFinishNavigationForDelegateWithIdentifier:arg_identifier + functionIdentifier:arg_functionIdentifier error:&error]; callback(wrapResult(nil, error)); }]; @@ -1217,8 +1222,8 @@ - (instancetype)initWithBinaryMessenger:(NSObject *)bina } return self; } -- (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)arg_functionInstanceId - webViewIdentifier:(NSNumber *)arg_webViewInstanceId +- (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)arg_functionIdentifier + webViewIdentifier:(NSNumber *)arg_webViewIdentifier URL:(nullable NSString *)arg_url completion:(void (^)(NSError *_Nullable))completion { FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel @@ -1227,9 +1232,8 @@ - (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)arg_functionIns binaryMessenger:self.binaryMessenger codec:FWFWKNavigationDelegateFlutterApiGetCodec()]; [channel sendMessage:@[ - (arg_functionInstanceId == nil) ? [NSNull null] : arg_functionInstanceId, - (arg_webViewInstanceId == nil) ? [NSNull null] : arg_webViewInstanceId, - (arg_url == nil) ? [NSNull null] : arg_url + arg_functionIdentifier ?: [NSNull null], arg_webViewIdentifier ?: [NSNull null], + arg_url ?: [NSNull null] ] reply:^(id reply) { completion(nil); @@ -1299,9 +1303,9 @@ void FWFNSObjectHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FlutterError *error; - [api disposeObjectWithIdentifier:arg_instanceId error:&error]; + [api disposeObjectWithIdentifier:arg_identifier error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -1323,14 +1327,14 @@ void FWFNSObjectHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); - NSNumber *arg_observerInstanceId = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_observerIdentifier = GetNullableObjectAtIndex(args, 1); NSString *arg_keyPath = GetNullableObjectAtIndex(args, 2); NSArray *arg_options = GetNullableObjectAtIndex(args, 3); FlutterError *error; - [api addObserverForObjectWithIdentifier:arg_instanceId - observerIdentifier:arg_observerInstanceId + [api addObserverForObjectWithIdentifier:arg_identifier + observerIdentifier:arg_observerIdentifier keyPath:arg_keyPath options:arg_options error:&error]; @@ -1354,12 +1358,12 @@ void FWFNSObjectHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); - NSNumber *arg_observerInstanceId = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_observerIdentifier = GetNullableObjectAtIndex(args, 1); NSString *arg_keyPath = GetNullableObjectAtIndex(args, 2); FlutterError *error; - [api removeObserverForObjectWithIdentifier:arg_instanceId - observerIdentifier:arg_observerInstanceId + [api removeObserverForObjectWithIdentifier:arg_identifier + observerIdentifier:arg_observerIdentifier keyPath:arg_keyPath error:&error]; callback(wrapResult(nil, error)); @@ -1414,13 +1418,13 @@ - (instancetype)initWithBinaryMessenger:(NSObject *)bina } return self; } -- (void)disposeFunctionWithIdentifier:(NSNumber *)arg_instanceId +- (void)disposeFunctionWithIdentifier:(NSNumber *)arg_identifier completion:(void (^)(NSError *_Nullable))completion { FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel messageChannelWithName:@"dev.flutter.pigeon.FunctionFlutterApi.dispose" binaryMessenger:self.binaryMessenger codec:FWFFunctionFlutterApiGetCodec()]; - [channel sendMessage:@[ (arg_instanceId == nil) ? [NSNull null] : arg_instanceId ] + [channel sendMessage:@[ arg_identifier ?: [NSNull null] ] reply:^(id reply) { completion(nil); }]; @@ -1532,11 +1536,11 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); - NSNumber *arg_configurationInstanceId = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_configurationIdentifier = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api createWithIdentifier:arg_instanceId - configurationIdentifier:arg_configurationInstanceId + [api createWithIdentifier:arg_identifier + configurationIdentifier:arg_configurationIdentifier error:&error]; callback(wrapResult(nil, error)); }]; @@ -1557,11 +1561,11 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); - NSNumber *arg_uiDelegateInstanceId = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_uiDelegateIdentifier = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api setUIDelegateForWebViewWithIdentifier:arg_instanceId - delegateIdentifier:arg_uiDelegateInstanceId + [api setUIDelegateForWebViewWithIdentifier:arg_identifier + delegateIdentifier:arg_uiDelegateIdentifier error:&error]; callback(wrapResult(nil, error)); }]; @@ -1583,11 +1587,11 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); - NSNumber *arg_navigationDelegateInstanceId = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_navigationDelegateIdentifier = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api setNavigationDelegateForWebViewWithIdentifier:arg_instanceId - delegateIdentifier:arg_navigationDelegateInstanceId + [api setNavigationDelegateForWebViewWithIdentifier:arg_identifier + delegateIdentifier:arg_navigationDelegateIdentifier error:&error]; callback(wrapResult(nil, error)); }]; @@ -1607,9 +1611,9 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FlutterError *error; - NSString *output = [api URLForWebViewWithIdentifier:arg_instanceId error:&error]; + NSString *output = [api URLForWebViewWithIdentifier:arg_identifier error:&error]; callback(wrapResult(output, error)); }]; } else { @@ -1629,9 +1633,9 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FlutterError *error; - NSNumber *output = [api estimatedProgressForWebViewWithIdentifier:arg_instanceId + NSNumber *output = [api estimatedProgressForWebViewWithIdentifier:arg_identifier error:&error]; callback(wrapResult(output, error)); }]; @@ -1652,10 +1656,10 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FWFNSUrlRequestData *arg_request = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api loadRequestForWebViewWithIdentifier:arg_instanceId request:arg_request error:&error]; + [api loadRequestForWebViewWithIdentifier:arg_identifier request:arg_request error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -1675,11 +1679,11 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); NSString *arg_string = GetNullableObjectAtIndex(args, 1); NSString *arg_baseUrl = GetNullableObjectAtIndex(args, 2); FlutterError *error; - [api loadHTMLForWebViewWithIdentifier:arg_instanceId + [api loadHTMLForWebViewWithIdentifier:arg_identifier HTMLString:arg_string baseURL:arg_baseUrl error:&error]; @@ -1702,11 +1706,11 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); NSString *arg_url = GetNullableObjectAtIndex(args, 1); NSString *arg_readAccessUrl = GetNullableObjectAtIndex(args, 2); FlutterError *error; - [api loadFileForWebViewWithIdentifier:arg_instanceId + [api loadFileForWebViewWithIdentifier:arg_identifier fileURL:arg_url readAccessURL:arg_readAccessUrl error:&error]; @@ -1729,10 +1733,10 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); NSString *arg_key = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api loadAssetForWebViewWithIdentifier:arg_instanceId assetKey:arg_key error:&error]; + [api loadAssetForWebViewWithIdentifier:arg_identifier assetKey:arg_key error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -1751,9 +1755,9 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FlutterError *error; - NSNumber *output = [api canGoBackForWebViewWithIdentifier:arg_instanceId error:&error]; + NSNumber *output = [api canGoBackForWebViewWithIdentifier:arg_identifier error:&error]; callback(wrapResult(output, error)); }]; } else { @@ -1772,9 +1776,9 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FlutterError *error; - NSNumber *output = [api canGoForwardForWebViewWithIdentifier:arg_instanceId error:&error]; + NSNumber *output = [api canGoForwardForWebViewWithIdentifier:arg_identifier error:&error]; callback(wrapResult(output, error)); }]; } else { @@ -1793,9 +1797,9 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FlutterError *error; - [api goBackForWebViewWithIdentifier:arg_instanceId error:&error]; + [api goBackForWebViewWithIdentifier:arg_identifier error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -1814,9 +1818,9 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FlutterError *error; - [api goForwardForWebViewWithIdentifier:arg_instanceId error:&error]; + [api goForwardForWebViewWithIdentifier:arg_identifier error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -1835,9 +1839,9 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FlutterError *error; - [api reloadWebViewWithIdentifier:arg_instanceId error:&error]; + [api reloadWebViewWithIdentifier:arg_identifier error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -1856,9 +1860,9 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FlutterError *error; - NSString *output = [api titleForWebViewWithIdentifier:arg_instanceId error:&error]; + NSString *output = [api titleForWebViewWithIdentifier:arg_identifier error:&error]; callback(wrapResult(output, error)); }]; } else { @@ -1879,10 +1883,10 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); NSNumber *arg_allow = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api setAllowsBackForwardForWebViewWithIdentifier:arg_instanceId + [api setAllowsBackForwardForWebViewWithIdentifier:arg_identifier isAllowed:arg_allow error:&error]; callback(wrapResult(nil, error)); @@ -1904,10 +1908,10 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); NSString *arg_userAgent = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api setUserAgentForWebViewWithIdentifier:arg_instanceId + [api setUserAgentForWebViewWithIdentifier:arg_identifier userAgent:arg_userAgent error:&error]; callback(wrapResult(nil, error)); @@ -1930,9 +1934,9 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); NSString *arg_javaScriptString = GetNullableObjectAtIndex(args, 1); - [api evaluateJavaScriptForWebViewWithIdentifier:arg_instanceId + [api evaluateJavaScriptForWebViewWithIdentifier:arg_identifier javaScriptString:arg_javaScriptString completion:^(id _Nullable output, FlutterError *_Nullable error) { @@ -1990,9 +1994,9 @@ void FWFWKUIDelegateHostApiSetup(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FlutterError *error; - [api createWithIdentifier:arg_instanceId error:&error]; + [api createWithIdentifier:arg_identifier error:&error]; callback(wrapResult(nil, error)); }]; } else { @@ -2070,11 +2074,11 @@ void FWFWKHttpCookieStoreHostApiSetup(id binaryMessenger api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); - NSNumber *arg_websiteDataStoreInstanceId = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_websiteDataStoreIdentifier = GetNullableObjectAtIndex(args, 1); FlutterError *error; - [api createFromWebsiteDataStoreWithIdentifier:arg_instanceId - dataStoreIdentifier:arg_websiteDataStoreInstanceId + [api createFromWebsiteDataStoreWithIdentifier:arg_identifier + dataStoreIdentifier:arg_websiteDataStoreIdentifier error:&error]; callback(wrapResult(nil, error)); }]; @@ -2095,9 +2099,9 @@ void FWFWKHttpCookieStoreHostApiSetup(id binaryMessenger api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSNumber *arg_instanceId = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); FWFNSHttpCookieData *arg_cookie = GetNullableObjectAtIndex(args, 1); - [api setCookieForStoreWithIdentifier:arg_instanceId + [api setCookieForStoreWithIdentifier:arg_identifier cookie:arg_cookie completion:^(FlutterError *_Nullable error) { callback(wrapResult(nil, error)); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.m index 6ebd60a4ac40..3ad0a169b81f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.m @@ -19,20 +19,20 @@ - (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { return self; } -- (WKHTTPCookieStore *)HTTPCookieStoreForIdentifier:(NSNumber *)instanceId +- (WKHTTPCookieStore *)HTTPCookieStoreForIdentifier:(NSNumber *)identifier API_AVAILABLE(ios(11.0)) { - return (WKHTTPCookieStore *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; + return (WKHTTPCookieStore *)[self.instanceManager instanceForIdentifier:identifier.longValue]; } -- (void)createFromWebsiteDataStoreWithIdentifier:(nonnull NSNumber *)instanceId - dataStoreIdentifier:(nonnull NSNumber *)websiteDataStoreInstanceId +- (void)createFromWebsiteDataStoreWithIdentifier:(nonnull NSNumber *)identifier + dataStoreIdentifier:(nonnull NSNumber *)websiteDataStoreIdentifier error:(FlutterError *_Nullable __autoreleasing *_Nonnull) error { if (@available(iOS 11.0, *)) { WKWebsiteDataStore *dataStore = (WKWebsiteDataStore *)[self.instanceManager - instanceForIdentifier:websiteDataStoreInstanceId.longValue]; - [self.instanceManager addInstance:dataStore.httpCookieStore - withIdentifier:instanceId.longValue]; + instanceForIdentifier:websiteDataStoreIdentifier.longValue]; + [self.instanceManager addDartCreatedInstance:dataStore.httpCookieStore + withIdentifier:identifier.longValue]; } else { *error = [FlutterError errorWithCode:@"FWFUnsupportedVersionError" @@ -41,13 +41,13 @@ - (void)createFromWebsiteDataStoreWithIdentifier:(nonnull NSNumber *)instanceId } } -- (void)setCookieForStoreWithIdentifier:(nonnull NSNumber *)instanceId +- (void)setCookieForStoreWithIdentifier:(nonnull NSNumber *)identifier cookie:(nonnull FWFNSHttpCookieData *)cookie completion:(nonnull void (^)(FlutterError *_Nullable))completion { NSHTTPCookie *nsCookie = FWFNSHTTPCookieFromCookieData(cookie); if (@available(iOS 11.0, *)) { - [[self HTTPCookieStoreForIdentifier:instanceId] setCookie:nsCookie + [[self HTTPCookieStoreForIdentifier:identifier] setCookie:nsCookie completionHandler:^{ completion(nil); }]; 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 79506bf72adc..cd84d705aaef 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.h @@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN // should be replaced with a call to clear the manager in the event of a hot restart // instead. /** - * Adds a new instance to the manager. + * Adds a new instance to the manager that was instantiated by 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 @@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN * @param instance The instance to be stored. * @param instanceIdentifier The identifier to be paired with instance. This value must be >= 0. */ -- (void)addInstance:(NSObject *)instance withIdentifier:(long)instanceIdentifier; +- (void)addDartCreatedInstance:(NSObject *)instance withIdentifier:(long)instanceIdentifier; /** * Removes the instance paired with a given identifier from the manager. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m index 445ecd3e2cf9..995e37424bb9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m @@ -20,7 +20,8 @@ - (instancetype)init { return self; } -- (void)addInstance:(nonnull NSObject *)instance withIdentifier:(long)instanceIdentifier { +- (void)addDartCreatedInstance:(nonnull NSObject *)instance + withIdentifier:(long)instanceIdentifier { NSAssert(instance && instanceIdentifier >= 0, @"Instance must be nonnull and identifier must be >= 0."); dispatch_async(_lockQueue, ^{ 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 188d83ff81b6..836349b5f29b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m @@ -21,18 +21,19 @@ - (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { return self; } -- (FWFNavigationDelegate *)navigationDelegateForIdentifier:(NSNumber *)instanceId { - return (FWFNavigationDelegate *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +- (FWFNavigationDelegate *)navigationDelegateForIdentifier:(NSNumber *)identifier { + return (FWFNavigationDelegate *)[self.instanceManager instanceForIdentifier:identifier.longValue]; } -- (void)createWithIdentifier:(nonnull NSNumber *)instanceId +- (void)createWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error { FWFNavigationDelegate *navigationDelegate = [[FWFNavigationDelegate alloc] init]; - [self.instanceManager addInstance:navigationDelegate withIdentifier:instanceId.longValue]; + [self.instanceManager addDartCreatedInstance:navigationDelegate + withIdentifier:identifier.longValue]; } -- (void)setDidFinishNavigationForDelegateWithIdentifier:(nonnull NSNumber *)instanceId - functionIdentifier:(nullable NSNumber *)functionInstanceId +- (void)setDidFinishNavigationForDelegateWithIdentifier:(nonnull NSNumber *)identifier + functionIdentifier:(nullable NSNumber *)functionIdentifier error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { // TODO(bparrishMines): Implement when callback method design is finalized. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m index b229c2e819a9..38417f9297f0 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m @@ -18,11 +18,11 @@ - (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { return self; } -- (NSObject *)objectForIdentifier:(NSNumber *)instanceId { - return (NSObject *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +- (NSObject *)objectForIdentifier:(NSNumber *)identifier { + return (NSObject *)[self.instanceManager instanceForIdentifier:identifier.longValue]; } -- (void)addObserverForObjectWithIdentifier:(nonnull NSNumber *)instanceId +- (void)addObserverForObjectWithIdentifier:(nonnull NSNumber *)identifier observerIdentifier:(nonnull NSNumber *)observer keyPath:(nonnull NSString *)keyPath options: @@ -33,22 +33,22 @@ - (void)addObserverForObjectWithIdentifier:(nonnull NSNumber *)instanceId for (FWFNSKeyValueObservingOptionsEnumData *data in options) { optionsInt |= FWFNSKeyValueObservingOptionsFromEnumData(data); } - [[self objectForIdentifier:instanceId] addObserver:[self objectForIdentifier:observer] + [[self objectForIdentifier:identifier] addObserver:[self objectForIdentifier:observer] forKeyPath:keyPath options:optionsInt context:nil]; } -- (void)removeObserverForObjectWithIdentifier:(nonnull NSNumber *)instanceId +- (void)removeObserverForObjectWithIdentifier:(nonnull NSNumber *)identifier observerIdentifier:(nonnull NSNumber *)observer keyPath:(nonnull NSString *)keyPath error:(FlutterError *_Nullable *_Nonnull)error { - [[self objectForIdentifier:instanceId] removeObserver:[self objectForIdentifier:observer] + [[self objectForIdentifier:identifier] removeObserver:[self objectForIdentifier:observer] forKeyPath:keyPath]; } -- (void)disposeObjectWithIdentifier:(nonnull NSNumber *)instanceId +- (void)disposeObjectWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error { - [self.instanceManager removeInstanceWithIdentifier:instanceId.longValue]; + [self.instanceManager removeInstanceWithIdentifier:identifier.longValue]; } @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.m index f907d211f08e..dbb04fccccd2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.m @@ -18,27 +18,28 @@ - (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { return self; } -- (WKPreferences *)preferencesForIdentifier:(NSNumber *)instanceId { - return (WKPreferences *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +- (WKPreferences *)preferencesForIdentifier:(NSNumber *)identifier { + return (WKPreferences *)[self.instanceManager instanceForIdentifier:identifier.longValue]; } -- (void)createWithIdentifier:(nonnull NSNumber *)instanceId +- (void)createWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error { WKPreferences *preferences = [[WKPreferences alloc] init]; - [self.instanceManager addInstance:preferences withIdentifier:instanceId.longValue]; + [self.instanceManager addDartCreatedInstance:preferences withIdentifier:identifier.longValue]; } -- (void)createFromWebViewConfigurationWithIdentifier:(nonnull NSNumber *)instanceId - configurationIdentifier:(nonnull NSNumber *)configurationInstanceId +- (void)createFromWebViewConfigurationWithIdentifier:(nonnull NSNumber *)identifier + configurationIdentifier:(nonnull NSNumber *)configurationIdentifier error:(FlutterError *_Nullable *_Nonnull)error { WKWebViewConfiguration *configuration = (WKWebViewConfiguration *)[self.instanceManager - instanceForIdentifier:configurationInstanceId.longValue]; - [self.instanceManager addInstance:configuration.preferences withIdentifier:instanceId.longValue]; + instanceForIdentifier:configurationIdentifier.longValue]; + [self.instanceManager addDartCreatedInstance:configuration.preferences + withIdentifier:identifier.longValue]; } -- (void)setJavaScriptEnabledForPreferencesWithIdentifier:(nonnull NSNumber *)instanceId +- (void)setJavaScriptEnabledForPreferencesWithIdentifier:(nonnull NSNumber *)identifier isEnabled:(nonnull NSNumber *)enabled error:(FlutterError *_Nullable *_Nonnull)error { - [[self preferencesForIdentifier:instanceId] setJavaScriptEnabled:enabled.boolValue]; + [[self preferencesForIdentifier:identifier] setJavaScriptEnabled:enabled.boolValue]; } @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m index 37662b5fc9f4..9e4b5f003abc 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m @@ -24,14 +24,15 @@ - (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { return self; } -- (FWFScriptMessageHandler *)scriptMessageHandlerForIdentifier:(NSNumber *)instanceId { +- (FWFScriptMessageHandler *)scriptMessageHandlerForIdentifier:(NSNumber *)identifier { return (FWFScriptMessageHandler *)[self.instanceManager - instanceForIdentifier:instanceId.longValue]; + instanceForIdentifier:identifier.longValue]; } -- (void)createWithIdentifier:(nonnull NSNumber *)instanceId +- (void)createWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error { FWFScriptMessageHandler *scriptMessageHandler = [[FWFScriptMessageHandler alloc] init]; - [self.instanceManager addInstance:scriptMessageHandler withIdentifier:instanceId.longValue]; + [self.instanceManager addDartCreatedInstance:scriptMessageHandler + withIdentifier:identifier.longValue]; } @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.m index a7522995e0e1..fb77b717aa64 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.m @@ -18,40 +18,41 @@ - (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { return self; } -- (UIScrollView *)scrollViewForIdentifier:(NSNumber *)instanceId { - return (UIScrollView *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +- (UIScrollView *)scrollViewForIdentifier:(NSNumber *)identifier { + return (UIScrollView *)[self.instanceManager instanceForIdentifier:identifier.longValue]; } -- (void)createFromWebViewWithIdentifier:(nonnull NSNumber *)instanceId - webViewIdentifier:(nonnull NSNumber *)webViewInstanceId +- (void)createFromWebViewWithIdentifier:(nonnull NSNumber *)identifier + webViewIdentifier:(nonnull NSNumber *)webViewIdentifier error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { WKWebView *webView = - (WKWebView *)[self.instanceManager instanceForIdentifier:webViewInstanceId.longValue]; - [self.instanceManager addInstance:webView.scrollView withIdentifier:instanceId.longValue]; + (WKWebView *)[self.instanceManager instanceForIdentifier:webViewIdentifier.longValue]; + [self.instanceManager addDartCreatedInstance:webView.scrollView + withIdentifier:identifier.longValue]; } - (NSArray *) - contentOffsetForScrollViewWithIdentifier:(nonnull NSNumber *)instanceId + contentOffsetForScrollViewWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error { - CGPoint point = [[self scrollViewForIdentifier:instanceId] contentOffset]; + CGPoint point = [[self scrollViewForIdentifier:identifier] contentOffset]; return @[ @(point.x), @(point.y) ]; } -- (void)scrollByForScrollViewWithIdentifier:(nonnull NSNumber *)instanceId +- (void)scrollByForScrollViewWithIdentifier:(nonnull NSNumber *)identifier x:(nonnull NSNumber *)x y:(nonnull NSNumber *)y error:(FlutterError *_Nullable *_Nonnull)error { - UIScrollView *scrollView = [self scrollViewForIdentifier:instanceId]; + UIScrollView *scrollView = [self scrollViewForIdentifier:identifier]; CGPoint contentOffset = scrollView.contentOffset; [scrollView setContentOffset:CGPointMake(contentOffset.x + x.doubleValue, contentOffset.y + y.doubleValue)]; } -- (void)setContentOffsetForScrollViewWithIdentifier:(nonnull NSNumber *)instanceId +- (void)setContentOffsetForScrollViewWithIdentifier:(nonnull NSNumber *)identifier toX:(nonnull NSNumber *)x y:(nonnull NSNumber *)y error:(FlutterError *_Nullable *_Nonnull)error { - [[self scrollViewForIdentifier:instanceId] + [[self scrollViewForIdentifier:identifier] setContentOffset:CGPointMake(x.doubleValue, y.doubleValue)]; } @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m index 2621c5dde287..28d0ab53aaad 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m @@ -21,13 +21,13 @@ - (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { return self; } -- (FWFUIDelegate *)delegateForIdentifier:(NSNumber *)instanceId { - return (FWFUIDelegate *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +- (FWFUIDelegate *)delegateForIdentifier:(NSNumber *)identifier { + return (FWFUIDelegate *)[self.instanceManager instanceForIdentifier:identifier.longValue]; } -- (void)createWithIdentifier:(nonnull NSNumber *)instanceId +- (void)createWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error { FWFUIDelegate *uIDelegate = [[FWFUIDelegate alloc] init]; - [self.instanceManager addInstance:uIDelegate withIdentifier:instanceId.longValue]; + [self.instanceManager addDartCreatedInstance:uIDelegate withIdentifier:identifier.longValue]; } @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.m index b2c1f639d1f6..465738b570cd 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.m @@ -17,27 +17,27 @@ - (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { return self; } -- (UIView *)viewForIdentifier:(NSNumber *)instanceId { - return (UIView *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +- (UIView *)viewForIdentifier:(NSNumber *)identifier { + return (UIView *)[self.instanceManager instanceForIdentifier:identifier.longValue]; } -- (void)setBackgroundColorForViewWithIdentifier:(nonnull NSNumber *)instanceId +- (void)setBackgroundColorForViewWithIdentifier:(nonnull NSNumber *)identifier toValue:(nullable NSNumber *)color error:(FlutterError *_Nullable *_Nonnull)error { if (!color) { - [[self viewForIdentifier:instanceId] setBackgroundColor:nil]; + [[self viewForIdentifier:identifier] setBackgroundColor:nil]; } int colorInt = color.intValue; UIColor *colorObject = [UIColor colorWithRed:(colorInt >> 16 & 0xff) / 255.0 green:(colorInt >> 8 & 0xff) / 255.0 blue:(colorInt & 0xff) / 255.0 alpha:(colorInt >> 24 & 0xff) / 255.0]; - [[self viewForIdentifier:instanceId] setBackgroundColor:colorObject]; + [[self viewForIdentifier:identifier] setBackgroundColor:colorObject]; } -- (void)setOpaqueForViewWithIdentifier:(nonnull NSNumber *)instanceId +- (void)setOpaqueForViewWithIdentifier:(nonnull NSNumber *)identifier isOpaque:(nonnull NSNumber *)opaque error:(FlutterError *_Nullable *_Nonnull)error { - [[self viewForIdentifier:instanceId] setOpaque:opaque.boolValue]; + [[self viewForIdentifier:identifier] setOpaque:opaque.boolValue]; } @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.m index 2db7cff0b68e..d15341a7883d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.m @@ -19,44 +19,44 @@ - (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { return self; } -- (WKUserContentController *)userContentControllerForIdentifier:(NSNumber *)instanceId { +- (WKUserContentController *)userContentControllerForIdentifier:(NSNumber *)identifier { return (WKUserContentController *)[self.instanceManager - instanceForIdentifier:instanceId.longValue]; + instanceForIdentifier:identifier.longValue]; } -- (void)createFromWebViewConfigurationWithIdentifier:(nonnull NSNumber *)instanceId - configurationIdentifier:(nonnull NSNumber *)configurationInstanceId +- (void)createFromWebViewConfigurationWithIdentifier:(nonnull NSNumber *)identifier + configurationIdentifier:(nonnull NSNumber *)configurationIdentifier error:(FlutterError *_Nullable *_Nonnull)error { WKWebViewConfiguration *configuration = (WKWebViewConfiguration *)[self.instanceManager - instanceForIdentifier:configurationInstanceId.longValue]; - [self.instanceManager addInstance:configuration.userContentController - withIdentifier:instanceId.longValue]; + instanceForIdentifier:configurationIdentifier.longValue]; + [self.instanceManager addDartCreatedInstance:configuration.userContentController + withIdentifier:identifier.longValue]; } -- (void)addScriptMessageHandlerForControllerWithIdentifier:(nonnull NSNumber *)instanceId +- (void)addScriptMessageHandlerForControllerWithIdentifier:(nonnull NSNumber *)identifier handlerIdentifier:(nonnull NSNumber *)handler ofName:(nonnull NSString *)name error: (FlutterError *_Nullable *_Nonnull)error { - [[self userContentControllerForIdentifier:instanceId] + [[self userContentControllerForIdentifier:identifier] addScriptMessageHandler:(id)[self.instanceManager instanceForIdentifier:handler.longValue] name:name]; } -- (void)removeScriptMessageHandlerForControllerWithIdentifier:(nonnull NSNumber *)instanceId +- (void)removeScriptMessageHandlerForControllerWithIdentifier:(nonnull NSNumber *)identifier name:(nonnull NSString *)name error:(FlutterError *_Nullable *_Nonnull) error { - [[self userContentControllerForIdentifier:instanceId] removeScriptMessageHandlerForName:name]; + [[self userContentControllerForIdentifier:identifier] removeScriptMessageHandlerForName:name]; } -- (void)removeAllScriptMessageHandlersForControllerWithIdentifier:(nonnull NSNumber *)instanceId +- (void)removeAllScriptMessageHandlersForControllerWithIdentifier:(nonnull NSNumber *)identifier error: (FlutterError *_Nullable *_Nonnull) error { if (@available(iOS 14.0, *)) { - [[self userContentControllerForIdentifier:instanceId] removeAllScriptMessageHandlers]; + [[self userContentControllerForIdentifier:identifier] removeAllScriptMessageHandlers]; } else { *error = [FlutterError errorWithCode:@"FWFUnsupportedVersionError" @@ -65,16 +65,16 @@ - (void)removeAllScriptMessageHandlersForControllerWithIdentifier:(nonnull NSNum } } -- (void)addUserScriptForControllerWithIdentifier:(nonnull NSNumber *)instanceId +- (void)addUserScriptForControllerWithIdentifier:(nonnull NSNumber *)identifier userScript:(nonnull FWFWKUserScriptData *)userScript error:(FlutterError *_Nullable *_Nonnull)error { - [[self userContentControllerForIdentifier:instanceId] + [[self userContentControllerForIdentifier:identifier] addUserScript:FWFWKUserScriptFromScriptData(userScript)]; } -- (void)removeAllUserScriptsForControllerWithIdentifier:(nonnull NSNumber *)instanceId +- (void)removeAllUserScriptsForControllerWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error { - [[self userContentControllerForIdentifier:instanceId] removeAllUserScripts]; + [[self userContentControllerForIdentifier:identifier] removeAllUserScripts]; } @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m index 0b239daebf99..05ca38e2a477 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m @@ -19,36 +19,38 @@ - (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { return self; } -- (WKWebViewConfiguration *)webViewConfigurationForIdentifier:(NSNumber *)instanceId { +- (WKWebViewConfiguration *)webViewConfigurationForIdentifier:(NSNumber *)identifier { return (WKWebViewConfiguration *)[self.instanceManager - instanceForIdentifier:instanceId.longValue]; + instanceForIdentifier:identifier.longValue]; } -- (void)createWithIdentifier:(nonnull NSNumber *)instanceId +- (void)createWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error { WKWebViewConfiguration *webViewConfiguration = [[WKWebViewConfiguration alloc] init]; - [self.instanceManager addInstance:webViewConfiguration withIdentifier:instanceId.longValue]; + [self.instanceManager addDartCreatedInstance:webViewConfiguration + withIdentifier:identifier.longValue]; } -- (void)createFromWebViewWithIdentifier:(nonnull NSNumber *)instanceId - webViewIdentifier:(nonnull NSNumber *)webViewInstanceId +- (void)createFromWebViewWithIdentifier:(nonnull NSNumber *)identifier + webViewIdentifier:(nonnull NSNumber *)webViewIdentifier error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { WKWebView *webView = - (WKWebView *)[self.instanceManager instanceForIdentifier:webViewInstanceId.longValue]; - [self.instanceManager addInstance:webView.configuration withIdentifier:instanceId.longValue]; + (WKWebView *)[self.instanceManager instanceForIdentifier:webViewIdentifier.longValue]; + [self.instanceManager addDartCreatedInstance:webView.configuration + withIdentifier:identifier.longValue]; } -- (void)setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:(nonnull NSNumber *)instanceId +- (void)setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:(nonnull NSNumber *)identifier isAllowed:(nonnull NSNumber *)allow error: (FlutterError *_Nullable *_Nonnull) error { - [[self webViewConfigurationForIdentifier:instanceId] + [[self webViewConfigurationForIdentifier:identifier] setAllowsInlineMediaPlayback:allow.boolValue]; } - (void) - setMediaTypesRequiresUserActionForConfigurationWithIdentifier:(nonnull NSNumber *)instanceId + setMediaTypesRequiresUserActionForConfigurationWithIdentifier:(nonnull NSNumber *)identifier forTypes: (nonnull NSArray< FWFWKAudiovisualMediaTypeEnumData @@ -59,7 +61,7 @@ - (void)setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:(nonnull NSNu NSAssert(types.count, @"Types must not be empty."); WKWebViewConfiguration *configuration = - (WKWebViewConfiguration *)[self webViewConfigurationForIdentifier:instanceId]; + (WKWebViewConfiguration *)[self webViewConfigurationForIdentifier:identifier]; if (@available(iOS 10.0, *)) { WKAudiovisualMediaTypes typesInt = 0; for (FWFWKAudiovisualMediaTypeEnumData *data in types) { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m index 35677575dddb..66149dd9ed37 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m @@ -58,8 +58,8 @@ - (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager return self; } -- (FWFWebView *)webViewForIdentifier:(NSNumber *)instanceId { - return (FWFWebView *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +- (FWFWebView *)webViewForIdentifier:(NSNumber *)identifier { + return (FWFWebView *)[self.instanceManager instanceForIdentifier:identifier.longValue]; } + (nonnull FlutterError *)errorForURLString:(nonnull NSString *)string { @@ -71,17 +71,17 @@ + (nonnull FlutterError *)errorForURLString:(nonnull NSString *)string { details:errorDetails]; } -- (void)createWithIdentifier:(nonnull NSNumber *)instanceId - configurationIdentifier:(nonnull NSNumber *)configurationInstanceId +- (void)createWithIdentifier:(nonnull NSNumber *)identifier + configurationIdentifier:(nonnull NSNumber *)configurationIdentifier error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { WKWebViewConfiguration *configuration = (WKWebViewConfiguration *)[self.instanceManager - instanceForIdentifier:configurationInstanceId.longValue]; + instanceForIdentifier:configurationIdentifier.longValue]; FWFWebView *webView = [[FWFWebView alloc] initWithFrame:CGRectMake(0, 0, 0, 0) configuration:configuration]; - [self.instanceManager addInstance:webView withIdentifier:instanceId.longValue]; + [self.instanceManager addDartCreatedInstance:webView withIdentifier:identifier.longValue]; } -- (void)loadRequestForWebViewWithIdentifier:(nonnull NSNumber *)instanceId +- (void)loadRequestForWebViewWithIdentifier:(nonnull NSNumber *)identifier request:(nonnull FWFNSUrlRequestData *)request error: (FlutterError *_Nullable __autoreleasing *_Nonnull)error { @@ -92,47 +92,47 @@ - (void)loadRequestForWebViewWithIdentifier:(nonnull NSNumber *)instanceId details:[NSString stringWithFormat:@"URL was: '%@'", request.url]]; return; } - [[self webViewForIdentifier:instanceId] loadRequest:urlRequest]; + [[self webViewForIdentifier:identifier] loadRequest:urlRequest]; } -- (void)setUserAgentForWebViewWithIdentifier:(nonnull NSNumber *)instanceId +- (void)setUserAgentForWebViewWithIdentifier:(nonnull NSNumber *)identifier userAgent:(nullable NSString *)userAgent error:(FlutterError *_Nullable __autoreleasing *_Nonnull) error { - [[self webViewForIdentifier:instanceId] setCustomUserAgent:userAgent]; + [[self webViewForIdentifier:identifier] setCustomUserAgent:userAgent]; } - (nullable NSNumber *) - canGoBackForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + canGoBackForWebViewWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { - return @([self webViewForIdentifier:instanceId].canGoBack); + return @([self webViewForIdentifier:identifier].canGoBack); } - (nullable NSString *) - URLForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + URLForWebViewWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { - return [self webViewForIdentifier:instanceId].URL.absoluteString; + return [self webViewForIdentifier:identifier].URL.absoluteString; } - (nullable NSNumber *) - canGoForwardForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + canGoForwardForWebViewWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { - return @([[self webViewForIdentifier:instanceId] canGoForward]); + return @([[self webViewForIdentifier:identifier] canGoForward]); } - (nullable NSNumber *) - estimatedProgressForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + estimatedProgressForWebViewWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable __autoreleasing *_Nonnull) error { - return @([[self webViewForIdentifier:instanceId] estimatedProgress]); + return @([[self webViewForIdentifier:identifier] estimatedProgress]); } -- (void)evaluateJavaScriptForWebViewWithIdentifier:(nonnull NSNumber *)instanceId +- (void)evaluateJavaScriptForWebViewWithIdentifier:(nonnull NSNumber *)identifier javaScriptString:(nonnull NSString *)javaScriptString completion: (nonnull void (^)(id _Nullable, FlutterError *_Nullable))completion { - [[self webViewForIdentifier:instanceId] + [[self webViewForIdentifier:identifier] evaluateJavaScript:javaScriptString completionHandler:^(id _Nullable result, NSError *_Nullable error) { id returnValue = nil; @@ -158,17 +158,17 @@ - (void)evaluateJavaScriptForWebViewWithIdentifier:(nonnull NSNumber *)instanceI }]; } -- (void)goBackForWebViewWithIdentifier:(nonnull NSNumber *)instanceId +- (void)goBackForWebViewWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { - [[self webViewForIdentifier:instanceId] goBack]; + [[self webViewForIdentifier:identifier] goBack]; } -- (void)goForwardForWebViewWithIdentifier:(nonnull NSNumber *)instanceId +- (void)goForwardForWebViewWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { - [[self webViewForIdentifier:instanceId] goForward]; + [[self webViewForIdentifier:identifier] goForward]; } -- (void)loadAssetForWebViewWithIdentifier:(nonnull NSNumber *)instanceId +- (void)loadAssetForWebViewWithIdentifier:(nonnull NSNumber *)identifier assetKey:(nonnull NSString *)key error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { NSString *assetFilePath = [self.assetManager lookupKeyForAsset:key]; @@ -178,12 +178,12 @@ - (void)loadAssetForWebViewWithIdentifier:(nonnull NSNumber *)instanceId if (!url) { *error = [FWFWebViewHostApiImpl errorForURLString:assetFilePath]; } else { - [[self webViewForIdentifier:instanceId] loadFileURL:url + [[self webViewForIdentifier:identifier] loadFileURL:url allowingReadAccessToURL:[url URLByDeletingLastPathComponent]]; } } -- (void)loadFileForWebViewWithIdentifier:(nonnull NSNumber *)instanceId +- (void)loadFileForWebViewWithIdentifier:(nonnull NSNumber *)identifier fileURL:(nonnull NSString *)url readAccessURL:(nonnull NSString *)readAccessUrl error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { @@ -195,55 +195,55 @@ - (void)loadFileForWebViewWithIdentifier:(nonnull NSNumber *)instanceId } else if (!readAccessNSURL) { *error = [FWFWebViewHostApiImpl errorForURLString:readAccessUrl]; } else { - [[self webViewForIdentifier:instanceId] loadFileURL:fileURL + [[self webViewForIdentifier:identifier] loadFileURL:fileURL allowingReadAccessToURL:readAccessNSURL]; } } -- (void)loadHTMLForWebViewWithIdentifier:(nonnull NSNumber *)instanceId +- (void)loadHTMLForWebViewWithIdentifier:(nonnull NSNumber *)identifier HTMLString:(nonnull NSString *)string baseURL:(nullable NSString *)baseUrl error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { - [[self webViewForIdentifier:instanceId] loadHTMLString:string + [[self webViewForIdentifier:identifier] loadHTMLString:string baseURL:[NSURL URLWithString:baseUrl]]; } -- (void)reloadWebViewWithIdentifier:(nonnull NSNumber *)instanceId +- (void)reloadWebViewWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { - [[self webViewForIdentifier:instanceId] reload]; + [[self webViewForIdentifier:identifier] reload]; } - (void) - setAllowsBackForwardForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + setAllowsBackForwardForWebViewWithIdentifier:(nonnull NSNumber *)identifier isAllowed:(nonnull NSNumber *)allow error:(FlutterError *_Nullable __autoreleasing *_Nonnull) error { - [[self webViewForIdentifier:instanceId] setAllowsBackForwardNavigationGestures:allow.boolValue]; + [[self webViewForIdentifier:identifier] setAllowsBackForwardNavigationGestures:allow.boolValue]; } - (void) - setNavigationDelegateForWebViewWithIdentifier:(nonnull NSNumber *)instanceId - delegateIdentifier:(nullable NSNumber *)navigationDelegateInstanceId + setNavigationDelegateForWebViewWithIdentifier:(nonnull NSNumber *)identifier + delegateIdentifier:(nullable NSNumber *)navigationDelegateIdentifier error: (FlutterError *_Nullable __autoreleasing *_Nonnull) error { id navigationDelegate = (id)[self.instanceManager - instanceForIdentifier:navigationDelegateInstanceId.longValue]; - [[self webViewForIdentifier:instanceId] setNavigationDelegate:navigationDelegate]; + instanceForIdentifier:navigationDelegateIdentifier.longValue]; + [[self webViewForIdentifier:identifier] setNavigationDelegate:navigationDelegate]; } -- (void)setUIDelegateForWebViewWithIdentifier:(nonnull NSNumber *)instanceId - delegateIdentifier:(nullable NSNumber *)uiDelegateInstanceId +- (void)setUIDelegateForWebViewWithIdentifier:(nonnull NSNumber *)identifier + delegateIdentifier:(nullable NSNumber *)uiDelegateIdentifier error:(FlutterError *_Nullable __autoreleasing *_Nonnull) error { id navigationDelegate = - (id)[self.instanceManager instanceForIdentifier:uiDelegateInstanceId.longValue]; - [[self webViewForIdentifier:instanceId] setUIDelegate:navigationDelegate]; + (id)[self.instanceManager instanceForIdentifier:uiDelegateIdentifier.longValue]; + [[self webViewForIdentifier:identifier] setUIDelegate:navigationDelegate]; } - (nullable NSString *) - titleForWebViewWithIdentifier:(nonnull NSNumber *)instanceId + titleForWebViewWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { - return [[self webViewForIdentifier:instanceId] title]; + return [[self webViewForIdentifier:identifier] title]; } @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m index e052ae03543c..4587917ba640 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m @@ -19,28 +19,28 @@ - (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { return self; } -- (WKWebsiteDataStore *)websiteDataStoreForIdentifier:(NSNumber *)instanceId { - return (WKWebsiteDataStore *)[self.instanceManager instanceForIdentifier:instanceId.longValue]; +- (WKWebsiteDataStore *)websiteDataStoreForIdentifier:(NSNumber *)identifier { + return (WKWebsiteDataStore *)[self.instanceManager instanceForIdentifier:identifier.longValue]; } -- (void)createFromWebViewConfigurationWithIdentifier:(nonnull NSNumber *)instanceId - configurationIdentifier:(nonnull NSNumber *)configurationInstanceId +- (void)createFromWebViewConfigurationWithIdentifier:(nonnull NSNumber *)identifier + configurationIdentifier:(nonnull NSNumber *)configurationIdentifier error:(FlutterError *_Nullable *_Nonnull)error { WKWebViewConfiguration *configuration = (WKWebViewConfiguration *)[self.instanceManager - instanceForIdentifier:configurationInstanceId.longValue]; - [self.instanceManager addInstance:configuration.websiteDataStore - withIdentifier:instanceId.longValue]; + instanceForIdentifier:configurationIdentifier.longValue]; + [self.instanceManager addDartCreatedInstance:configuration.websiteDataStore + withIdentifier:identifier.longValue]; } -- (void)createDefaultDataStoreWithIdentifier:(nonnull NSNumber *)instanceId +- (void)createDefaultDataStoreWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable __autoreleasing *_Nonnull) error { - [self.instanceManager addInstance:[WKWebsiteDataStore defaultDataStore] - withIdentifier:instanceId.longValue]; + [self.instanceManager addDartCreatedInstance:[WKWebsiteDataStore defaultDataStore] + withIdentifier:identifier.longValue]; } - (void) - removeDataFromDataStoreWithIdentifier:(nonnull NSNumber *)instanceId + removeDataFromDataStoreWithIdentifier:(nonnull NSNumber *)identifier ofTypes: (nonnull NSArray *)dataTypes modifiedSince:(nonnull NSNumber *)modificationTimeInSecondsSinceEpoch @@ -51,7 +51,7 @@ - (void)createDefaultDataStoreWithIdentifier:(nonnull NSNumber *)instanceId [stringDataTypes addObject:FWFWKWebsiteDataTypeFromEnumData(type)]; } - WKWebsiteDataStore *dataStore = [self websiteDataStoreForIdentifier:instanceId]; + WKWebsiteDataStore *dataStore = [self websiteDataStoreForIdentifier:identifier]; [dataStore fetchDataRecordsOfTypes:stringDataTypes completionHandler:^(NSArray *records) { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/function_flutter_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/function_flutter_api_impls.dart index c6eb711513d2..ebbf032aa673 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/function_flutter_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/function_flutter_api_impls.dart @@ -16,10 +16,10 @@ class FunctionFlutterApiImpl extends FunctionFlutterApi { late final InstanceManager instanceManager; @override - void dispose(int instanceId) { - final Function? function = instanceManager.getInstance(instanceId); + void dispose(int identifier) { + final Function? function = instanceManager.getInstance(identifier); if (function != null) { - instanceManager.removeInstance(function); + instanceManager.removeWeakReference(function); } } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.dart index 830ba2e94935..b855022d57e5 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.dart @@ -4,10 +4,10 @@ /// Maintains instances stored to communicate with Objective-C objects. class InstanceManager { - final Map _instanceIdsToInstances = {}; - final Map _instancesToInstanceIds = {}; + final Map _strongInstances = {}; + final Map _identifiers = {}; - int _nextInstanceId = 0; + int _nextIdentifier = 0; /// Global instance of [InstanceManager]. static final InstanceManager instance = InstanceManager(); @@ -16,14 +16,12 @@ class InstanceManager { /// /// Returns new if [instance] has already been added. Otherwise, it is added /// with a new instance id. - int? tryAddInstance(Object instance) { - if (_instancesToInstanceIds.containsKey(instance)) { - return null; - } + int addDartCreatedInstance(Object instance) { + assert(getIdentifier(instance) == null); - final int instanceId = _nextInstanceId++; - _instancesToInstanceIds[instance] = instanceId; - _instanceIdsToInstances[instanceId] = instance; + final int instanceId = _nextIdentifier++; + _identifiers[instance] = instanceId; + _strongInstances[instanceId] = instance; return instanceId; } @@ -31,22 +29,22 @@ class InstanceManager { /// /// Returns null if the instance is removed. Otherwise, return the instanceId /// of the removed instance. - int? removeInstance(T instance) { - final int? instanceId = _instancesToInstanceIds[instance]; + int? removeWeakReference(T instance) { + final int? instanceId = _identifiers[instance]; if (instanceId != null) { - _instancesToInstanceIds.remove(instance); - _instanceIdsToInstances.remove(instanceId); + _identifiers.remove(instance); + _strongInstances.remove(instanceId); } return instanceId; } /// Retrieve the Object paired with instanceId. - T? getInstance(int instanceId) { - return _instanceIdsToInstances[instanceId] as T?; + T? getInstance(int identifier) { + return _strongInstances[identifier] as T?; } /// Retrieve the instanceId paired with instance. - int? getInstanceId(Object instance) { - return _instancesToInstanceIds[instance]; + int? getIdentifier(Object instance) { + return _identifiers[instance]; } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart index f84ba68bfbc1..3072cb1cd0e7 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.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 (v3.0.3), do not edit directly. +// Autogenerated from Pigeon (v3.1.2), 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 // @dart = 2.12 @@ -313,13 +313,13 @@ class WKWebsiteDataStoreHostApi { static const MessageCodec codec = _WKWebsiteDataStoreHostApiCodec(); Future createFromWebViewConfiguration( - int arg_instanceId, int arg_configurationInstanceId) async { + int arg_identifier, int arg_configurationIdentifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createFromWebViewConfiguration', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_configurationInstanceId]) + .send([arg_identifier, arg_configurationIdentifier]) as Map?; if (replyMap == null) { throw PlatformException( @@ -339,13 +339,13 @@ class WKWebsiteDataStoreHostApi { } } - Future createDefaultDataStore(int arg_instanceId) async { + Future createDefaultDataStore(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createDefaultDataStore', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_identifier]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -365,14 +365,14 @@ class WKWebsiteDataStoreHostApi { } Future removeDataOfTypes( - int arg_instanceId, + int arg_identifier, List arg_dataTypes, double arg_modificationTimeInSecondsSinceEpoch) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([ - arg_instanceId, + arg_identifier, arg_dataTypes, arg_modificationTimeInSecondsSinceEpoch ]) as Map?; @@ -415,12 +415,12 @@ class UIViewHostApi { static const MessageCodec codec = _UIViewHostApiCodec(); - Future setBackgroundColor(int arg_instanceId, int? arg_value) async { + Future setBackgroundColor(int arg_identifier, int? arg_value) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.UIViewHostApi.setBackgroundColor', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_value]) as Map?; + .send([arg_identifier, arg_value]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -439,12 +439,12 @@ class UIViewHostApi { } } - Future setOpaque(int arg_instanceId, bool arg_opaque) async { + Future setOpaque(int arg_identifier, bool arg_opaque) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.UIViewHostApi.setOpaque', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_opaque]) as Map?; + .send([arg_identifier, arg_opaque]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -480,12 +480,12 @@ class UIScrollViewHostApi { static const MessageCodec codec = _UIScrollViewHostApiCodec(); Future createFromWebView( - int arg_instanceId, int arg_webViewInstanceId) async { + int arg_identifier, int arg_webViewIdentifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.UIScrollViewHostApi.createFromWebView', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_webViewInstanceId]) + await channel.send([arg_identifier, arg_webViewIdentifier]) as Map?; if (replyMap == null) { throw PlatformException( @@ -505,12 +505,12 @@ class UIScrollViewHostApi { } } - Future> getContentOffset(int arg_instanceId) async { + Future> getContentOffset(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.UIScrollViewHostApi.getContentOffset', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_identifier]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -534,12 +534,12 @@ class UIScrollViewHostApi { } } - Future scrollBy(int arg_instanceId, double arg_x, double arg_y) async { + Future scrollBy(int arg_identifier, double arg_x, double arg_y) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.UIScrollViewHostApi.scrollBy', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_x, arg_y]) + await channel.send([arg_identifier, arg_x, arg_y]) as Map?; if (replyMap == null) { throw PlatformException( @@ -560,12 +560,12 @@ class UIScrollViewHostApi { } Future setContentOffset( - int arg_instanceId, double arg_x, double arg_y) async { + int arg_identifier, double arg_x, double arg_y) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.UIScrollViewHostApi.setContentOffset', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_x, arg_y]) + await channel.send([arg_identifier, arg_x, arg_y]) as Map?; if (replyMap == null) { throw PlatformException( @@ -622,12 +622,12 @@ class WKWebViewConfigurationHostApi { static const MessageCodec codec = _WKWebViewConfigurationHostApiCodec(); - Future create(int arg_instanceId) async { + Future create(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_identifier]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -647,13 +647,13 @@ class WKWebViewConfigurationHostApi { } Future createFromWebView( - int arg_instanceId, int arg_webViewInstanceId) async { + int arg_identifier, int arg_webViewIdentifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.createFromWebView', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_webViewInstanceId]) + await channel.send([arg_identifier, arg_webViewIdentifier]) as Map?; if (replyMap == null) { throw PlatformException( @@ -674,13 +674,13 @@ class WKWebViewConfigurationHostApi { } Future setAllowsInlineMediaPlayback( - int arg_instanceId, bool arg_allow) async { + int arg_identifier, bool arg_allow) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.setAllowsInlineMediaPlayback', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_allow]) as Map?; + .send([arg_identifier, arg_allow]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -699,14 +699,14 @@ class WKWebViewConfigurationHostApi { } } - Future setMediaTypesRequiringUserActionForPlayback(int arg_instanceId, + Future setMediaTypesRequiringUserActionForPlayback(int arg_identifier, List arg_types) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewConfigurationHostApi.setMediaTypesRequiringUserActionForPlayback', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_types]) as Map?; + .send([arg_identifier, arg_types]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -769,13 +769,13 @@ class WKUserContentControllerHostApi { _WKUserContentControllerHostApiCodec(); Future createFromWebViewConfiguration( - int arg_instanceId, int arg_configurationInstanceId) async { + int arg_identifier, int arg_configurationIdentifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKUserContentControllerHostApi.createFromWebViewConfiguration', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_configurationInstanceId]) + .send([arg_identifier, arg_configurationIdentifier]) as Map?; if (replyMap == null) { throw PlatformException( @@ -796,13 +796,13 @@ class WKUserContentControllerHostApi { } Future addScriptMessageHandler( - int arg_instanceId, int arg_handlerInstanceid, String arg_name) async { + int arg_identifier, int arg_handlerIdentifier, String arg_name) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKUserContentControllerHostApi.addScriptMessageHandler', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_handlerInstanceid, arg_name]) + .send([arg_identifier, arg_handlerIdentifier, arg_name]) as Map?; if (replyMap == null) { throw PlatformException( @@ -823,13 +823,13 @@ class WKUserContentControllerHostApi { } Future removeScriptMessageHandler( - int arg_instanceId, String arg_name) async { + int arg_identifier, String arg_name) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKUserContentControllerHostApi.removeScriptMessageHandler', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_name]) as Map?; + .send([arg_identifier, arg_name]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -848,13 +848,13 @@ class WKUserContentControllerHostApi { } } - Future removeAllScriptMessageHandlers(int arg_instanceId) async { + Future removeAllScriptMessageHandlers(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllScriptMessageHandlers', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_identifier]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -874,13 +874,13 @@ class WKUserContentControllerHostApi { } Future addUserScript( - int arg_instanceId, WKUserScriptData arg_userScript) async { + int arg_identifier, WKUserScriptData arg_userScript) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKUserContentControllerHostApi.addUserScript', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_userScript]) + await channel.send([arg_identifier, arg_userScript]) as Map?; if (replyMap == null) { throw PlatformException( @@ -900,13 +900,13 @@ class WKUserContentControllerHostApi { } } - Future removeAllUserScripts(int arg_instanceId) async { + Future removeAllUserScripts(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllUserScripts', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_identifier]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -942,13 +942,13 @@ class WKPreferencesHostApi { static const MessageCodec codec = _WKPreferencesHostApiCodec(); Future createFromWebViewConfiguration( - int arg_instanceId, int arg_configurationInstanceId) async { + int arg_identifier, int arg_configurationIdentifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKPreferencesHostApi.createFromWebViewConfiguration', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_configurationInstanceId]) + .send([arg_identifier, arg_configurationIdentifier]) as Map?; if (replyMap == null) { throw PlatformException( @@ -969,12 +969,12 @@ class WKPreferencesHostApi { } Future setJavaScriptEnabled( - int arg_instanceId, bool arg_enabled) async { + int arg_identifier, bool arg_enabled) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKPreferencesHostApi.setJavaScriptEnabled', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_enabled]) as Map?; + .send([arg_identifier, arg_enabled]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -1010,12 +1010,12 @@ class WKScriptMessageHandlerHostApi { static const MessageCodec codec = _WKScriptMessageHandlerHostApiCodec(); - Future create(int arg_instanceId) async { + Future create(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKScriptMessageHandlerHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_identifier]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -1051,12 +1051,12 @@ class WKNavigationDelegateHostApi { static const MessageCodec codec = _WKNavigationDelegateHostApiCodec(); - Future create(int arg_instanceId) async { + Future create(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKNavigationDelegateHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_identifier]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -1076,13 +1076,13 @@ class WKNavigationDelegateHostApi { } Future setDidFinishNavigation( - int arg_instanceId, int? arg_functionInstanceId) async { + int arg_identifier, int? arg_functionIdentifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_functionInstanceId]) + await channel.send([arg_identifier, arg_functionIdentifier]) as Map?; if (replyMap == null) { throw PlatformException( @@ -1112,7 +1112,7 @@ abstract class WKNavigationDelegateFlutterApi { _WKNavigationDelegateFlutterApiCodec(); void didFinishNavigation( - int functionInstanceId, int webViewInstanceId, String? url); + int functionIdentifier, int webViewIdentifier, String? url); static void setup(WKNavigationDelegateFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1127,15 +1127,15 @@ abstract class WKNavigationDelegateFlutterApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null.'); final List args = (message as List?)!; - final int? arg_functionInstanceId = (args[0] as int?); - assert(arg_functionInstanceId != null, + final int? arg_functionIdentifier = (args[0] as int?); + assert(arg_functionIdentifier != null, 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null int.'); - final int? arg_webViewInstanceId = (args[1] as int?); - assert(arg_webViewInstanceId != null, + final int? arg_webViewIdentifier = (args[1] as int?); + assert(arg_webViewIdentifier != null, 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null int.'); final String? arg_url = (args[2] as String?); api.didFinishNavigation( - arg_functionInstanceId!, arg_webViewInstanceId!, arg_url); + arg_functionIdentifier!, arg_webViewIdentifier!, arg_url); return; }); } @@ -1178,12 +1178,12 @@ class NSObjectHostApi { static const MessageCodec codec = _NSObjectHostApiCodec(); - Future dispose(int arg_instanceId) async { + Future dispose(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.NSObjectHostApi.dispose', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_identifier]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -1203,16 +1203,16 @@ class NSObjectHostApi { } Future addObserver( - int arg_instanceId, - int arg_observerInstanceId, + int arg_identifier, + int arg_observerIdentifier, String arg_keyPath, List arg_options) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.NSObjectHostApi.addObserver', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([ - arg_instanceId, - arg_observerInstanceId, + arg_identifier, + arg_observerIdentifier, arg_keyPath, arg_options ]) as Map?; @@ -1234,13 +1234,13 @@ class NSObjectHostApi { } } - Future removeObserver(int arg_instanceId, int arg_observerInstanceId, + Future removeObserver(int arg_identifier, int arg_observerIdentifier, String arg_keyPath) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.NSObjectHostApi.removeObserver', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send( - [arg_instanceId, arg_observerInstanceId, arg_keyPath]) + [arg_identifier, arg_observerIdentifier, arg_keyPath]) as Map?; if (replyMap == null) { throw PlatformException( @@ -1268,7 +1268,7 @@ class _FunctionFlutterApiCodec extends StandardMessageCodec { abstract class FunctionFlutterApi { static const MessageCodec codec = _FunctionFlutterApiCodec(); - void dispose(int instanceId); + void dispose(int identifier); static void setup(FunctionFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1282,10 +1282,10 @@ abstract class FunctionFlutterApi { assert(message != null, 'Argument for dev.flutter.pigeon.FunctionFlutterApi.dispose was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.FunctionFlutterApi.dispose was null, expected non-null int.'); - api.dispose(arg_instanceId!); + api.dispose(arg_identifier!); return; }); } @@ -1371,12 +1371,12 @@ class WKWebViewHostApi { static const MessageCodec codec = _WKWebViewHostApiCodec(); Future create( - int arg_instanceId, int arg_configurationInstanceId) async { + int arg_identifier, int arg_configurationIdentifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_configurationInstanceId]) + .send([arg_identifier, arg_configurationIdentifier]) as Map?; if (replyMap == null) { throw PlatformException( @@ -1397,12 +1397,12 @@ class WKWebViewHostApi { } Future setUIDelegate( - int arg_instanceId, int? arg_uiDelegateInstanceId) async { + int arg_identifier, int? arg_uiDelegateIdentifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.setUIDelegate', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_uiDelegateInstanceId]) + await channel.send([arg_identifier, arg_uiDelegateIdentifier]) as Map?; if (replyMap == null) { throw PlatformException( @@ -1423,12 +1423,12 @@ class WKWebViewHostApi { } Future setNavigationDelegate( - int arg_instanceId, int? arg_navigationDelegateInstanceId) async { + int arg_identifier, int? arg_navigationDelegateIdentifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.setNavigationDelegate', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_navigationDelegateInstanceId]) + .send([arg_identifier, arg_navigationDelegateIdentifier]) as Map?; if (replyMap == null) { throw PlatformException( @@ -1448,12 +1448,12 @@ class WKWebViewHostApi { } } - Future getUrl(int arg_instanceId) async { + Future getUrl(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.getUrl', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_identifier]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -1472,12 +1472,12 @@ class WKWebViewHostApi { } } - Future getEstimatedProgress(int arg_instanceId) async { + Future getEstimatedProgress(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.getEstimatedProgress', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_identifier]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -1502,12 +1502,12 @@ class WKWebViewHostApi { } Future loadRequest( - int arg_instanceId, NSUrlRequestData arg_request) async { + int arg_identifier, NSUrlRequestData arg_request) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.loadRequest', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_request]) as Map?; + .send([arg_identifier, arg_request]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -1527,12 +1527,12 @@ class WKWebViewHostApi { } Future loadHtmlString( - int arg_instanceId, String arg_string, String? arg_baseUrl) async { + int arg_identifier, String arg_string, String? arg_baseUrl) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_string, arg_baseUrl]) + await channel.send([arg_identifier, arg_string, arg_baseUrl]) as Map?; if (replyMap == null) { throw PlatformException( @@ -1553,12 +1553,12 @@ class WKWebViewHostApi { } Future loadFileUrl( - int arg_instanceId, String arg_url, String arg_readAccessUrl) async { + int arg_identifier, String arg_url, String arg_readAccessUrl) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.loadFileUrl', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_url, arg_readAccessUrl]) + .send([arg_identifier, arg_url, arg_readAccessUrl]) as Map?; if (replyMap == null) { throw PlatformException( @@ -1578,12 +1578,12 @@ class WKWebViewHostApi { } } - Future loadFlutterAsset(int arg_instanceId, String arg_key) async { + Future loadFlutterAsset(int arg_identifier, String arg_key) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.loadFlutterAsset', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_key]) as Map?; + .send([arg_identifier, arg_key]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -1602,12 +1602,12 @@ class WKWebViewHostApi { } } - Future canGoBack(int arg_instanceId) async { + Future canGoBack(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.canGoBack', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_identifier]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -1631,12 +1631,12 @@ class WKWebViewHostApi { } } - Future canGoForward(int arg_instanceId) async { + Future canGoForward(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.canGoForward', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_identifier]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -1660,12 +1660,12 @@ class WKWebViewHostApi { } } - Future goBack(int arg_instanceId) async { + Future goBack(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.goBack', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_identifier]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -1684,12 +1684,12 @@ class WKWebViewHostApi { } } - Future goForward(int arg_instanceId) async { + Future goForward(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.goForward', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_identifier]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -1708,12 +1708,12 @@ class WKWebViewHostApi { } } - Future reload(int arg_instanceId) async { + Future reload(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.reload', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_identifier]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -1732,12 +1732,12 @@ class WKWebViewHostApi { } } - Future getTitle(int arg_instanceId) async { + Future getTitle(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.getTitle', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_identifier]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -1757,13 +1757,13 @@ class WKWebViewHostApi { } Future setAllowsBackForwardNavigationGestures( - int arg_instanceId, bool arg_allow) async { + int arg_identifier, bool arg_allow) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.setAllowsBackForwardNavigationGestures', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_allow]) as Map?; + .send([arg_identifier, arg_allow]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -1783,12 +1783,12 @@ class WKWebViewHostApi { } Future setCustomUserAgent( - int arg_instanceId, String? arg_userAgent) async { + int arg_identifier, String? arg_userAgent) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_userAgent]) + await channel.send([arg_identifier, arg_userAgent]) as Map?; if (replyMap == null) { throw PlatformException( @@ -1809,12 +1809,12 @@ class WKWebViewHostApi { } Future evaluateJavaScript( - int arg_instanceId, String arg_javaScriptString) async { + int arg_identifier, String arg_javaScriptString) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_javaScriptString]) + await channel.send([arg_identifier, arg_javaScriptString]) as Map?; if (replyMap == null) { throw PlatformException( @@ -1850,12 +1850,12 @@ class WKUIDelegateHostApi { static const MessageCodec codec = _WKUIDelegateHostApiCodec(); - Future create(int arg_instanceId) async { + Future create(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKUIDelegateHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId]) as Map?; + await channel.send([arg_identifier]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -1917,13 +1917,13 @@ class WKHttpCookieStoreHostApi { static const MessageCodec codec = _WKHttpCookieStoreHostApiCodec(); Future createFromWebsiteDataStore( - int arg_instanceId, int arg_websiteDataStoreInstanceId) async { + int arg_identifier, int arg_websiteDataStoreIdentifier) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKHttpCookieStoreHostApi.createFromWebsiteDataStore', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_websiteDataStoreInstanceId]) + .send([arg_identifier, arg_websiteDataStoreIdentifier]) as Map?; if (replyMap == null) { throw PlatformException( @@ -1944,12 +1944,12 @@ class WKHttpCookieStoreHostApi { } Future setCookie( - int arg_instanceId, NSHttpCookieData arg_cookie) async { + int arg_identifier, NSHttpCookieData arg_cookie) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKHttpCookieStoreHostApi.setCookie', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel - .send([arg_instanceId, arg_cookie]) as Map?; + .send([arg_identifier, arg_cookie]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart index b007d1a8312c..e8f1c6723d8f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart @@ -105,8 +105,8 @@ class NSObjectHostApiImpl extends NSObjectHostApi { Set options, ) { return addObserver( - instanceManager.getInstanceId(instance)!, - instanceManager.getInstanceId(observer)!, + instanceManager.getIdentifier(instance)!, + instanceManager.getIdentifier(observer)!, keyPath, _toNSKeyValueObservingOptionsEnumData(options).toList(), ); @@ -119,17 +119,17 @@ class NSObjectHostApiImpl extends NSObjectHostApi { String keyPath, ) { return removeObserver( - instanceManager.getInstanceId(instance)!, - instanceManager.getInstanceId(observer)!, + instanceManager.getIdentifier(instance)!, + instanceManager.getIdentifier(observer)!, keyPath, ); } /// Calls [dispose] with the ids of the provided object instances. Future disposeForInstances(NSObject instance) async { - final int? instanceId = instanceManager.removeInstance(instance); - if (instanceId != null) { - await dispose(instanceId); + final int? identifier = instanceManager.removeWeakReference(instance); + if (identifier != null) { + await dispose(identifier); } } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart index b2ca5672f8e2..328965295d56 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart @@ -29,14 +29,11 @@ class UIScrollViewHostApiImpl extends UIScrollViewHostApi { Future createFromWebViewForInstances( UIScrollView instance, WKWebView webView, - ) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - await createFromWebView( - instanceId, - instanceManager.getInstanceId(webView)!, - ); - } + ) { + return createFromWebView( + instanceManager.addDartCreatedInstance(instance), + instanceManager.getIdentifier(webView)!, + ); } /// Calls [getContentOffset] with the ids of the provided object instances. @@ -44,7 +41,7 @@ class UIScrollViewHostApiImpl extends UIScrollViewHostApi { UIScrollView instance, ) async { final List point = await getContentOffset( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, ); return Point(point[0]!, point[1]!); } @@ -55,7 +52,7 @@ class UIScrollViewHostApiImpl extends UIScrollViewHostApi { Point offset, ) { return scrollBy( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, offset.x, offset.y, ); @@ -67,7 +64,7 @@ class UIScrollViewHostApiImpl extends UIScrollViewHostApi { Point offset, ) async { return setContentOffset( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, offset.x, offset.y, ); @@ -92,7 +89,7 @@ class UIViewHostApiImpl extends UIViewHostApi { Color? color, ) async { return setBackgroundColor( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, color?.value, ); } @@ -102,6 +99,6 @@ class UIViewHostApiImpl extends UIViewHostApi { UIView instance, bool opaque, ) async { - return setOpaque(instanceManager.getInstanceId(instance)!, opaque); + return setOpaque(instanceManager.getIdentifier(instance)!, opaque); } } 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 8bf29308371e..f908256fd6ed 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 @@ -236,24 +236,19 @@ class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi { Future createFromWebViewConfigurationForInstances( WKWebsiteDataStore instance, WKWebViewConfiguration configuration, - ) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - await createFromWebViewConfiguration( - instanceId, - instanceManager.getInstanceId(configuration)!, - ); - } + ) { + return createFromWebViewConfiguration( + instanceManager.addDartCreatedInstance(instance), + instanceManager.getIdentifier(configuration)!, + ); } /// Calls [createDefaultDataStore] with the ids of the provided object instances. Future createDefaultDataStoreForInstances( WKWebsiteDataStore instance, - ) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - await createDefaultDataStore(instanceId); - } + ) { + return createDefaultDataStore( + instanceManager.addDartCreatedInstance(instance)); } /// Calls [removeDataOfTypes] with the ids of the provided object instances. @@ -263,7 +258,7 @@ class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi { required double secondsModifiedSinceEpoch, }) { return removeDataOfTypes( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, _toWKWebsiteDataTypeEnumData(dataTypes).toList(), secondsModifiedSinceEpoch, ); @@ -283,11 +278,8 @@ class WKScriptMessageHandlerHostApiImpl extends WKScriptMessageHandlerHostApi { final InstanceManager instanceManager; /// Calls [create] with the ids of the provided object instances. - Future createForInstances(WKScriptMessageHandler instance) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - await create(instanceId); - } + Future createForInstances(WKScriptMessageHandler instance) { + return create(instanceManager.addDartCreatedInstance(instance)); } } @@ -307,14 +299,11 @@ class WKPreferencesHostApiImpl extends WKPreferencesHostApi { Future createFromWebViewConfigurationForInstances( WKPreferences instance, WKWebViewConfiguration configuration, - ) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - await createFromWebViewConfiguration( - instanceId, - instanceManager.getInstanceId(configuration)!, - ); - } + ) { + return createFromWebViewConfiguration( + instanceManager.addDartCreatedInstance(instance), + instanceManager.getIdentifier(configuration)!, + ); } /// Calls [setJavaScriptEnabled] with the ids of the provided object instances. @@ -323,7 +312,7 @@ class WKPreferencesHostApiImpl extends WKPreferencesHostApi { bool enabled, ) { return setJavaScriptEnabled( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, enabled, ); } @@ -345,14 +334,11 @@ class WKHttpCookieStoreHostApiImpl extends WKHttpCookieStoreHostApi { Future createFromWebsiteDataStoreForInstances( WKHttpCookieStore instance, WKWebsiteDataStore dataStore, - ) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - await createFromWebsiteDataStore( - instanceId, - instanceManager.getInstanceId(dataStore)!, - ); - } + ) { + return createFromWebsiteDataStore( + instanceManager.addDartCreatedInstance(instance), + instanceManager.getIdentifier(dataStore)!, + ); } /// Calls [setCookie] with the ids of the provided object instances. @@ -361,7 +347,7 @@ class WKHttpCookieStoreHostApiImpl extends WKHttpCookieStoreHostApi { NSHttpCookie cookie, ) { return setCookie( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, cookie.toNSHttpCookieData(), ); } @@ -384,14 +370,11 @@ class WKUserContentControllerHostApiImpl Future createFromWebViewConfigurationForInstances( WKUserContentController instance, WKWebViewConfiguration configuration, - ) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - await createFromWebViewConfiguration( - instanceId, - instanceManager.getInstanceId(configuration)!, - ); - } + ) { + return createFromWebViewConfiguration( + instanceManager.addDartCreatedInstance(instance), + instanceManager.getIdentifier(configuration)!, + ); } /// Calls [addScriptMessageHandler] with the ids of the provided object instances. @@ -401,8 +384,8 @@ class WKUserContentControllerHostApiImpl String name, ) { return addScriptMessageHandler( - instanceManager.getInstanceId(instance)!, - instanceManager.getInstanceId(handler)!, + instanceManager.getIdentifier(instance)!, + instanceManager.getIdentifier(handler)!, name, ); } @@ -413,7 +396,7 @@ class WKUserContentControllerHostApiImpl String name, ) { return removeScriptMessageHandler( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, name, ); } @@ -423,7 +406,7 @@ class WKUserContentControllerHostApiImpl WKUserContentController instance, ) { return removeAllScriptMessageHandlers( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, ); } @@ -433,7 +416,7 @@ class WKUserContentControllerHostApiImpl WKUserScript userScript, ) { return addUserScript( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, userScript.toWKUserScriptData(), ); } @@ -442,7 +425,7 @@ class WKUserContentControllerHostApiImpl Future removeAllUserScriptsForInstances( WKUserContentController instance, ) { - return removeAllUserScripts(instanceManager.getInstanceId(instance)!); + return removeAllUserScripts(instanceManager.getIdentifier(instance)!); } } @@ -459,25 +442,19 @@ class WKWebViewConfigurationHostApiImpl extends WKWebViewConfigurationHostApi { final InstanceManager instanceManager; /// Calls [create] with the ids of the provided object instances. - Future createForInstances(WKWebViewConfiguration instance) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - await create(instanceId); - } + Future createForInstances(WKWebViewConfiguration instance) { + return create(instanceManager.addDartCreatedInstance(instance)); } /// Calls [createFromWebView] with the ids of the provided object instances. Future createFromWebViewForInstances( WKWebViewConfiguration instance, WKWebView webView, - ) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - await createFromWebView( - instanceId, - instanceManager.getInstanceId(webView)!, - ); - } + ) { + return createFromWebView( + instanceManager.addDartCreatedInstance(instance), + instanceManager.getIdentifier(webView)!, + ); } /// Calls [setAllowsInlineMediaPlayback] with the ids of the provided object instances. @@ -486,7 +463,7 @@ class WKWebViewConfigurationHostApiImpl extends WKWebViewConfigurationHostApi { bool allow, ) { return setAllowsInlineMediaPlayback( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, allow, ); } @@ -497,7 +474,7 @@ class WKWebViewConfigurationHostApiImpl extends WKWebViewConfigurationHostApi { Set types, ) { return setMediaTypesRequiringUserActionForPlayback( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, _toWKAudiovisualMediaTypeEnumData(types).toList(), ); } @@ -516,11 +493,8 @@ class WKUIDelegateHostApiImpl extends WKUIDelegateHostApi { final InstanceManager instanceManager; /// Calls [create] with the ids of the provided object instances. - Future createForInstances(WKUIDelegate instance) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - await create(instanceId); - } + Future createForInstances(WKUIDelegate instance) { + return create(instanceManager.addDartCreatedInstance(instance)); } } @@ -537,11 +511,8 @@ class WKNavigationDelegateHostApiImpl extends WKNavigationDelegateHostApi { final InstanceManager instanceManager; /// Calls [create] with the ids of the provided object instances. - Future createForInstances(WKNavigationDelegate instance) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - await create(instanceId); - } + Future createForInstances(WKNavigationDelegate instance) { + return create(instanceManager.addDartCreatedInstance(instance)); } /// Calls [setDidFinishNavigation] with the ids of the provided object instances. @@ -551,11 +522,11 @@ class WKNavigationDelegateHostApiImpl extends WKNavigationDelegateHostApi { ) { int? functionInstanceId; if (didFinishNavigation != null) { - functionInstanceId = instanceManager.getInstanceId(didFinishNavigation) ?? - instanceManager.tryAddInstance(didFinishNavigation)!; + functionInstanceId = instanceManager.getIdentifier(didFinishNavigation) ?? + instanceManager.addDartCreatedInstance(didFinishNavigation); } return setDidFinishNavigation( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, functionInstanceId, ); } @@ -574,15 +545,15 @@ class WKNavigationDelegateFlutterApiImpl @override void didFinishNavigation( - int functionInstanceId, - int webViewInstanceId, + int functionIdentifier, + int webViewIdentifier, String? url, ) { final void Function( WKWebView webView, String? url, - ) function = instanceManager.getInstance(functionInstanceId)!; - function(instanceManager.getInstance(webViewInstanceId)!, url); + ) function = instanceManager.getInstance(functionIdentifier)!; + function(instanceManager.getInstance(webViewIdentifier)!, url); } } @@ -602,21 +573,18 @@ class WKWebViewHostApiImpl extends WKWebViewHostApi { Future createForInstances( WKWebView instance, WKWebViewConfiguration configuration, - ) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - await create( - instanceId, - instanceManager.getInstanceId(configuration)!, - ); - } + ) { + return create( + instanceManager.addDartCreatedInstance(instance), + instanceManager.getIdentifier(configuration)!, + ); } /// Calls [loadRequest] with the ids of the provided object instances. Future loadRequestForInstances( WKWebView webView, NSUrlRequest request) { return loadRequest( - instanceManager.getInstanceId(webView)!, + instanceManager.getIdentifier(webView)!, request.toNSUrlRequestData(), ); } @@ -628,7 +596,7 @@ class WKWebViewHostApiImpl extends WKWebViewHostApi { String? baseUrl, ) { return loadHtmlString( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, string, baseUrl, ); @@ -641,7 +609,7 @@ class WKWebViewHostApiImpl extends WKWebViewHostApi { String readAccessUrl, ) { return loadFileUrl( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, url, readAccessUrl, ); @@ -650,49 +618,49 @@ class WKWebViewHostApiImpl extends WKWebViewHostApi { /// Calls [loadFlutterAsset] with the ids of the provided object instances. Future loadFlutterAssetForInstances(WKWebView instance, String key) { return loadFlutterAsset( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, key, ); } /// Calls [canGoBack] with the ids of the provided object instances. Future canGoBackForInstances(WKWebView instance) { - return canGoBack(instanceManager.getInstanceId(instance)!); + return canGoBack(instanceManager.getIdentifier(instance)!); } /// Calls [canGoForward] with the ids of the provided object instances. Future canGoForwardForInstances(WKWebView instance) { - return canGoForward(instanceManager.getInstanceId(instance)!); + return canGoForward(instanceManager.getIdentifier(instance)!); } /// Calls [goBack] with the ids of the provided object instances. Future goBackForInstances(WKWebView instance) { - return goBack(instanceManager.getInstanceId(instance)!); + return goBack(instanceManager.getIdentifier(instance)!); } /// Calls [goForward] with the ids of the provided object instances. Future goForwardForInstances(WKWebView instance) { - return goForward(instanceManager.getInstanceId(instance)!); + return goForward(instanceManager.getIdentifier(instance)!); } /// Calls [reload] with the ids of the provided object instances. Future reloadForInstances(WKWebView instance) { - return reload(instanceManager.getInstanceId(instance)!); + return reload(instanceManager.getIdentifier(instance)!); } /// Calls [getUrl] with the ids of the provided object instances. Future getUrlForInstances(WKWebView instance) { - return getUrl(instanceManager.getInstanceId(instance)!); + return getUrl(instanceManager.getIdentifier(instance)!); } /// Calls [getTitle] with the ids of the provided object instances. Future getTitleForInstances(WKWebView instance) { - return getTitle(instanceManager.getInstanceId(instance)!); + return getTitle(instanceManager.getIdentifier(instance)!); } /// Calls [getEstimatedProgress] with the ids of the provided object instances. Future getEstimatedProgressForInstances(WKWebView instance) { - return getEstimatedProgress(instanceManager.getInstanceId(instance)!); + return getEstimatedProgress(instanceManager.getIdentifier(instance)!); } /// Calls [setAllowsBackForwardNavigationGestures] with the ids of the provided object instances. @@ -701,7 +669,7 @@ class WKWebViewHostApiImpl extends WKWebViewHostApi { bool allow, ) { return setAllowsBackForwardNavigationGestures( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, allow, ); } @@ -712,7 +680,7 @@ class WKWebViewHostApiImpl extends WKWebViewHostApi { String? userAgent, ) { return setCustomUserAgent( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, userAgent, ); } @@ -723,7 +691,7 @@ class WKWebViewHostApiImpl extends WKWebViewHostApi { String javaScriptString, ) { return evaluateJavaScript( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, javaScriptString, ); } @@ -734,8 +702,8 @@ class WKWebViewHostApiImpl extends WKWebViewHostApi { WKNavigationDelegate? delegate, ) { return setNavigationDelegate( - instanceManager.getInstanceId(instance)!, - delegate != null ? instanceManager.getInstanceId(delegate)! : null, + instanceManager.getIdentifier(instance)!, + delegate != null ? instanceManager.getIdentifier(delegate)! : null, ); } @@ -745,8 +713,8 @@ class WKWebViewHostApiImpl extends WKWebViewHostApi { WKUIDelegate? delegate, ) { return setUIDelegate( - instanceManager.getInstanceId(instance)!, - delegate != null ? instanceManager.getInstanceId(delegate)! : null, + instanceManager.getIdentifier(instance)!, + delegate != null ? instanceManager.getIdentifier(delegate)! : null, ); } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart index 6844a5fa67ad..41806d3f4601 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -239,19 +239,19 @@ abstract class WKWebsiteDataStoreHostApi { 'createFromWebViewConfigurationWithIdentifier:configurationIdentifier:', ) void createFromWebViewConfiguration( - int instanceId, - int configurationInstanceId, + int identifier, + int configurationIdentifier, ); @ObjCSelector('createDefaultDataStoreWithIdentifier:') - void createDefaultDataStore(int instanceId); + void createDefaultDataStore(int identifier); @ObjCSelector( 'removeDataFromDataStoreWithIdentifier:ofTypes:modifiedSince:', ) @async bool removeDataOfTypes( - int instanceId, + int identifier, List dataTypes, double modificationTimeInSecondsSinceEpoch, ); @@ -263,10 +263,10 @@ abstract class WKWebsiteDataStoreHostApi { @HostApi(dartHostTestHandler: 'TestUIViewHostApi') abstract class UIViewHostApi { @ObjCSelector('setBackgroundColorForViewWithIdentifier:toValue:') - void setBackgroundColor(int instanceId, int? value); + void setBackgroundColor(int identifier, int? value); @ObjCSelector('setOpaqueForViewWithIdentifier:isOpaque:') - void setOpaque(int instanceId, bool opaque); + void setOpaque(int identifier, bool opaque); } /// Mirror of UIScrollView. @@ -275,16 +275,16 @@ abstract class UIViewHostApi { @HostApi(dartHostTestHandler: 'TestUIScrollViewHostApi') abstract class UIScrollViewHostApi { @ObjCSelector('createFromWebViewWithIdentifier:webViewIdentifier:') - void createFromWebView(int instanceId, int webViewInstanceId); + void createFromWebView(int identifier, int webViewIdentifier); @ObjCSelector('contentOffsetForScrollViewWithIdentifier:') - List getContentOffset(int instanceId); + List getContentOffset(int identifier); @ObjCSelector('scrollByForScrollViewWithIdentifier:x:y:') - void scrollBy(int instanceId, double x, double y); + void scrollBy(int identifier, double x, double y); @ObjCSelector('setContentOffsetForScrollViewWithIdentifier:toX:y:') - void setContentOffset(int instanceId, double x, double y); + void setContentOffset(int identifier, double x, double y); } /// Mirror of WKWebViewConfiguration. @@ -293,21 +293,21 @@ abstract class UIScrollViewHostApi { @HostApi(dartHostTestHandler: 'TestWKWebViewConfigurationHostApi') abstract class WKWebViewConfigurationHostApi { @ObjCSelector('createWithIdentifier:') - void create(int instanceId); + void create(int identifier); @ObjCSelector('createFromWebViewWithIdentifier:webViewIdentifier:') - void createFromWebView(int instanceId, int webViewInstanceId); + void createFromWebView(int identifier, int webViewIdentifier); @ObjCSelector( 'setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:isAllowed:', ) - void setAllowsInlineMediaPlayback(int instanceId, bool allow); + void setAllowsInlineMediaPlayback(int identifier, bool allow); @ObjCSelector( 'setMediaTypesRequiresUserActionForConfigurationWithIdentifier:forTypes:', ) void setMediaTypesRequiringUserActionForPlayback( - int instanceId, + int identifier, List types, ); } @@ -321,30 +321,30 @@ abstract class WKUserContentControllerHostApi { 'createFromWebViewConfigurationWithIdentifier:configurationIdentifier:', ) void createFromWebViewConfiguration( - int instanceId, - int configurationInstanceId, + int identifier, + int configurationIdentifier, ); @ObjCSelector( 'addScriptMessageHandlerForControllerWithIdentifier:handlerIdentifier:ofName:', ) void addScriptMessageHandler( - int instanceId, - int handlerInstanceid, + int identifier, + int handlerIdentifier, String name, ); @ObjCSelector('removeScriptMessageHandlerForControllerWithIdentifier:name:') - void removeScriptMessageHandler(int instanceId, String name); + void removeScriptMessageHandler(int identifier, String name); @ObjCSelector('removeAllScriptMessageHandlersForControllerWithIdentifier:') - void removeAllScriptMessageHandlers(int instanceId); + void removeAllScriptMessageHandlers(int identifier); @ObjCSelector('addUserScriptForControllerWithIdentifier:userScript:') - void addUserScript(int instanceId, WKUserScriptData userScript); + void addUserScript(int identifier, WKUserScriptData userScript); @ObjCSelector('removeAllUserScriptsForControllerWithIdentifier:') - void removeAllUserScripts(int instanceId); + void removeAllUserScripts(int identifier); } /// Mirror of WKUserPreferences. @@ -356,12 +356,12 @@ abstract class WKPreferencesHostApi { 'createFromWebViewConfigurationWithIdentifier:configurationIdentifier:', ) void createFromWebViewConfiguration( - int instanceId, - int configurationInstanceId, + int identifier, + int configurationIdentifier, ); @ObjCSelector('setJavaScriptEnabledForPreferencesWithIdentifier:isEnabled:') - void setJavaScriptEnabled(int instanceId, bool enabled); + void setJavaScriptEnabled(int identifier, bool enabled); } /// Mirror of WKScriptMessageHandler. @@ -370,7 +370,7 @@ abstract class WKPreferencesHostApi { @HostApi(dartHostTestHandler: 'TestWKScriptMessageHandlerHostApi') abstract class WKScriptMessageHandlerHostApi { @ObjCSelector('createWithIdentifier:') - void create(int instanceId); + void create(int identifier); } /// Mirror of WKNavigationDelegate. @@ -379,12 +379,12 @@ abstract class WKScriptMessageHandlerHostApi { @HostApi(dartHostTestHandler: 'TestWKNavigationDelegateHostApi') abstract class WKNavigationDelegateHostApi { @ObjCSelector('createWithIdentifier:') - void create(int instanceId); + void create(int identifier); @ObjCSelector( 'setDidFinishNavigationForDelegateWithIdentifier:functionIdentifier:', ) - void setDidFinishNavigation(int instanceId, int? functionInstanceId); + void setDidFinishNavigation(int identifier, int? functionIdentifier); } /// Mirror of WKNavigationDelegate. @@ -396,8 +396,8 @@ abstract class WKNavigationDelegateFlutterApi { 'didFinishNavigationForDelegateWithIdentifier:webViewIdentifier:URL:', ) void didFinishNavigation( - int functionInstanceId, - int webViewInstanceId, + int functionIdentifier, + int webViewIdentifier, String? url, ); } @@ -408,14 +408,14 @@ abstract class WKNavigationDelegateFlutterApi { @HostApi(dartHostTestHandler: 'TestNSObjectHostApi') abstract class NSObjectHostApi { @ObjCSelector('disposeObjectWithIdentifier:') - void dispose(int instanceId); + void dispose(int identifier); @ObjCSelector( 'addObserverForObjectWithIdentifier:observerIdentifier:keyPath:options:', ) void addObserver( - int instanceId, - int observerInstanceId, + int identifier, + int observerIdentifier, String keyPath, List options, ); @@ -423,14 +423,14 @@ abstract class NSObjectHostApi { @ObjCSelector( 'removeObserverForObjectWithIdentifier:observerIdentifier:keyPath:', ) - void removeObserver(int instanceId, int observerInstanceId, String keyPath); + void removeObserver(int identifier, int observerIdentifier, String keyPath); } /// Disposes references to functions. @FlutterApi() abstract class FunctionFlutterApi { @ObjCSelector('disposeFunctionWithIdentifier:') - void dispose(int instanceId); + void dispose(int identifier); } /// Mirror of WKWebView. @@ -439,61 +439,61 @@ abstract class FunctionFlutterApi { @HostApi(dartHostTestHandler: 'TestWKWebViewHostApi') abstract class WKWebViewHostApi { @ObjCSelector('createWithIdentifier:configurationIdentifier:') - void create(int instanceId, int configurationInstanceId); + void create(int identifier, int configurationIdentifier); @ObjCSelector('setUIDelegateForWebViewWithIdentifier:delegateIdentifier:') - void setUIDelegate(int instanceId, int? uiDelegateInstanceId); + void setUIDelegate(int identifier, int? uiDelegateIdentifier); @ObjCSelector( 'setNavigationDelegateForWebViewWithIdentifier:delegateIdentifier:', ) - void setNavigationDelegate(int instanceId, int? navigationDelegateInstanceId); + void setNavigationDelegate(int identifier, int? navigationDelegateIdentifier); @ObjCSelector('URLForWebViewWithIdentifier:') - String? getUrl(int instanceId); + String? getUrl(int identifier); @ObjCSelector('estimatedProgressForWebViewWithIdentifier:') - double getEstimatedProgress(int instanceId); + double getEstimatedProgress(int identifier); @ObjCSelector('loadRequestForWebViewWithIdentifier:request:') - void loadRequest(int instanceId, NSUrlRequestData request); + void loadRequest(int identifier, NSUrlRequestData request); @ObjCSelector('loadHTMLForWebViewWithIdentifier:HTMLString:baseURL:') - void loadHtmlString(int instanceId, String string, String? baseUrl); + void loadHtmlString(int identifier, String string, String? baseUrl); @ObjCSelector('loadFileForWebViewWithIdentifier:fileURL:readAccessURL:') - void loadFileUrl(int instanceId, String url, String readAccessUrl); + void loadFileUrl(int identifier, String url, String readAccessUrl); @ObjCSelector('loadAssetForWebViewWithIdentifier:assetKey:') - void loadFlutterAsset(int instanceId, String key); + void loadFlutterAsset(int identifier, String key); @ObjCSelector('canGoBackForWebViewWithIdentifier:') - bool canGoBack(int instanceId); + bool canGoBack(int identifier); @ObjCSelector('canGoForwardForWebViewWithIdentifier:') - bool canGoForward(int instanceId); + bool canGoForward(int identifier); @ObjCSelector('goBackForWebViewWithIdentifier:') - void goBack(int instanceId); + void goBack(int identifier); @ObjCSelector('goForwardForWebViewWithIdentifier:') - void goForward(int instanceId); + void goForward(int identifier); @ObjCSelector('reloadWebViewWithIdentifier:') - void reload(int instanceId); + void reload(int identifier); @ObjCSelector('titleForWebViewWithIdentifier:') - String? getTitle(int instanceId); + String? getTitle(int identifier); @ObjCSelector('setAllowsBackForwardForWebViewWithIdentifier:isAllowed:') - void setAllowsBackForwardNavigationGestures(int instanceId, bool allow); + void setAllowsBackForwardNavigationGestures(int identifier, bool allow); @ObjCSelector('setUserAgentForWebViewWithIdentifier:userAgent:') - void setCustomUserAgent(int instanceId, String? userAgent); + void setCustomUserAgent(int identifier, String? userAgent); @ObjCSelector('evaluateJavaScriptForWebViewWithIdentifier:javaScriptString:') @async - Object? evaluateJavaScript(int instanceId, String javaScriptString); + Object? evaluateJavaScript(int identifier, String javaScriptString); } /// Mirror of WKUIDelegate. @@ -502,7 +502,7 @@ abstract class WKWebViewHostApi { @HostApi(dartHostTestHandler: 'TestWKUIDelegateHostApi') abstract class WKUIDelegateHostApi { @ObjCSelector('createWithIdentifier:') - void create(int instanceId); + void create(int identifier); } /// Mirror of WKHttpCookieStore. @@ -512,11 +512,11 @@ abstract class WKUIDelegateHostApi { abstract class WKHttpCookieStoreHostApi { @ObjCSelector('createFromWebsiteDataStoreWithIdentifier:dataStoreIdentifier:') void createFromWebsiteDataStore( - int instanceId, - int websiteDataStoreInstanceId, + int identifier, + int websiteDataStoreIdentifier, ); @ObjCSelector('setCookieForStoreWithIdentifier:cookie:') @async - void setCookie(int instanceId, NSHttpCookieData cookie); + void setCookie(int identifier, NSHttpCookieData cookie); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart index 6af9510b4f03..74264bf7e577 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart @@ -19,7 +19,8 @@ void main() { test('dispose', () { void function() {} - final int functionInstanceId = instanceManager.tryAddInstance(function)!; + final int functionInstanceId = + instanceManager.addDartCreatedInstance(function); FoundationFlutterApis.instance = FoundationFlutterApis( instanceManager: instanceManager, @@ -27,7 +28,7 @@ void main() { FoundationFlutterApis.instance.functionFlutterApi .dispose(functionInstanceId); - expect(instanceManager.getInstanceId(function), isNull); + expect(instanceManager.getIdentifier(function), isNull); }); }); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/instance_manager_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/instance_manager_test.dart index 10956c0a4aba..dad44f9cd535 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/instance_manager_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/instance_manager_test.dart @@ -16,20 +16,19 @@ void main() { test('tryAddInstance', () { final Object object = Object(); - expect(testInstanceManager.tryAddInstance(object), 0); - expect(testInstanceManager.getInstanceId(object), 0); + expect(testInstanceManager.addDartCreatedInstance(object), 0); + expect(testInstanceManager.getIdentifier(object), 0); expect(testInstanceManager.getInstance(0), object); - expect(testInstanceManager.tryAddInstance(object), null); }); test('removeInstance', () { final Object object = Object(); - testInstanceManager.tryAddInstance(object); + testInstanceManager.addDartCreatedInstance(object); - expect(testInstanceManager.removeInstance(object), 0); - expect(testInstanceManager.getInstanceId(object), null); + expect(testInstanceManager.removeWeakReference(object), 0); + expect(testInstanceManager.getIdentifier(object), null); expect(testInstanceManager.getInstance(0), null); - expect(testInstanceManager.removeInstance(object), null); + expect(testInstanceManager.removeWeakReference(object), null); }); }); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart index 1f963b4d9bc3..4a0002569058 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.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 (v3.0.3), do not edit directly. +// Autogenerated from Pigeon (v3.1.2), 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 // ignore_for_file: avoid_relative_lib_imports @@ -43,10 +43,10 @@ abstract class TestWKWebsiteDataStoreHostApi { _TestWKWebsiteDataStoreHostApiCodec(); void createFromWebViewConfiguration( - int instanceId, int configurationInstanceId); - void createDefaultDataStore(int instanceId); + int identifier, int configurationIdentifier); + void createDefaultDataStore(int identifier); Future removeDataOfTypes( - int instanceId, + int identifier, List dataTypes, double modificationTimeInSecondsSinceEpoch); static void setup(TestWKWebsiteDataStoreHostApi? api, @@ -63,14 +63,14 @@ abstract class TestWKWebsiteDataStoreHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createFromWebViewConfiguration was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createFromWebViewConfiguration was null, expected non-null int.'); - final int? arg_configurationInstanceId = (args[1] as int?); - assert(arg_configurationInstanceId != null, + final int? arg_configurationIdentifier = (args[1] as int?); + assert(arg_configurationIdentifier != null, 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createFromWebViewConfiguration was null, expected non-null int.'); api.createFromWebViewConfiguration( - arg_instanceId!, arg_configurationInstanceId!); + arg_identifier!, arg_configurationIdentifier!); return {}; }); } @@ -87,10 +87,10 @@ abstract class TestWKWebsiteDataStoreHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createDefaultDataStore was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.createDefaultDataStore was null, expected non-null int.'); - api.createDefaultDataStore(arg_instanceId!); + api.createDefaultDataStore(arg_identifier!); return {}; }); } @@ -107,8 +107,8 @@ abstract class TestWKWebsiteDataStoreHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes was null, expected non-null int.'); final List? arg_dataTypes = (args[1] as List?)?.cast(); @@ -118,7 +118,7 @@ abstract class TestWKWebsiteDataStoreHostApi { (args[2] as double?); assert(arg_modificationTimeInSecondsSinceEpoch != null, 'Argument for dev.flutter.pigeon.WKWebsiteDataStoreHostApi.removeDataOfTypes was null, expected non-null double.'); - final bool output = await api.removeDataOfTypes(arg_instanceId!, + final bool output = await api.removeDataOfTypes(arg_identifier!, arg_dataTypes!, arg_modificationTimeInSecondsSinceEpoch!); return {'result': output}; }); @@ -134,8 +134,8 @@ class _TestUIViewHostApiCodec extends StandardMessageCodec { abstract class TestUIViewHostApi { static const MessageCodec codec = _TestUIViewHostApiCodec(); - void setBackgroundColor(int instanceId, int? value); - void setOpaque(int instanceId, bool opaque); + void setBackgroundColor(int identifier, int? value); + void setOpaque(int identifier, bool opaque); static void setup(TestUIViewHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -149,11 +149,11 @@ abstract class TestUIViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.UIViewHostApi.setBackgroundColor was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.UIViewHostApi.setBackgroundColor was null, expected non-null int.'); final int? arg_value = (args[1] as int?); - api.setBackgroundColor(arg_instanceId!, arg_value); + api.setBackgroundColor(arg_identifier!, arg_value); return {}; }); } @@ -169,13 +169,13 @@ abstract class TestUIViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.UIViewHostApi.setOpaque was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.UIViewHostApi.setOpaque was null, expected non-null int.'); final bool? arg_opaque = (args[1] as bool?); assert(arg_opaque != null, 'Argument for dev.flutter.pigeon.UIViewHostApi.setOpaque was null, expected non-null bool.'); - api.setOpaque(arg_instanceId!, arg_opaque!); + api.setOpaque(arg_identifier!, arg_opaque!); return {}; }); } @@ -190,10 +190,10 @@ class _TestUIScrollViewHostApiCodec extends StandardMessageCodec { abstract class TestUIScrollViewHostApi { static const MessageCodec codec = _TestUIScrollViewHostApiCodec(); - void createFromWebView(int instanceId, int webViewInstanceId); - List getContentOffset(int instanceId); - void scrollBy(int instanceId, double x, double y); - void setContentOffset(int instanceId, double x, double y); + void createFromWebView(int identifier, int webViewIdentifier); + List getContentOffset(int identifier); + void scrollBy(int identifier, double x, double y); + void setContentOffset(int identifier, double x, double y); static void setup(TestUIScrollViewHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -207,13 +207,13 @@ abstract class TestUIScrollViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.createFromWebView was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.createFromWebView was null, expected non-null int.'); - final int? arg_webViewInstanceId = (args[1] as int?); - assert(arg_webViewInstanceId != null, + final int? arg_webViewIdentifier = (args[1] as int?); + assert(arg_webViewIdentifier != null, 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.createFromWebView was null, expected non-null int.'); - api.createFromWebView(arg_instanceId!, arg_webViewInstanceId!); + api.createFromWebView(arg_identifier!, arg_webViewIdentifier!); return {}; }); } @@ -229,10 +229,10 @@ abstract class TestUIScrollViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.getContentOffset was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.getContentOffset was null, expected non-null int.'); - final List output = api.getContentOffset(arg_instanceId!); + final List output = api.getContentOffset(arg_identifier!); return {'result': output}; }); } @@ -248,8 +248,8 @@ abstract class TestUIScrollViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.scrollBy was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.scrollBy was null, expected non-null int.'); final double? arg_x = (args[1] as double?); assert(arg_x != null, @@ -257,7 +257,7 @@ abstract class TestUIScrollViewHostApi { final double? arg_y = (args[2] as double?); assert(arg_y != null, 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.scrollBy was null, expected non-null double.'); - api.scrollBy(arg_instanceId!, arg_x!, arg_y!); + api.scrollBy(arg_identifier!, arg_x!, arg_y!); return {}; }); } @@ -273,8 +273,8 @@ abstract class TestUIScrollViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.setContentOffset was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.setContentOffset was null, expected non-null int.'); final double? arg_x = (args[1] as double?); assert(arg_x != null, @@ -282,7 +282,7 @@ abstract class TestUIScrollViewHostApi { final double? arg_y = (args[2] as double?); assert(arg_y != null, 'Argument for dev.flutter.pigeon.UIScrollViewHostApi.setContentOffset was null, expected non-null double.'); - api.setContentOffset(arg_instanceId!, arg_x!, arg_y!); + api.setContentOffset(arg_identifier!, arg_x!, arg_y!); return {}; }); } @@ -318,11 +318,11 @@ abstract class TestWKWebViewConfigurationHostApi { static const MessageCodec codec = _TestWKWebViewConfigurationHostApiCodec(); - void create(int instanceId); - void createFromWebView(int instanceId, int webViewInstanceId); - void setAllowsInlineMediaPlayback(int instanceId, bool allow); + void create(int identifier); + void createFromWebView(int identifier, int webViewIdentifier); + void setAllowsInlineMediaPlayback(int identifier, bool allow); void setMediaTypesRequiringUserActionForPlayback( - int instanceId, List types); + int identifier, List types); static void setup(TestWKWebViewConfigurationHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -336,10 +336,10 @@ abstract class TestWKWebViewConfigurationHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.create was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.create was null, expected non-null int.'); - api.create(arg_instanceId!); + api.create(arg_identifier!); return {}; }); } @@ -356,13 +356,13 @@ abstract class TestWKWebViewConfigurationHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.createFromWebView was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.createFromWebView was null, expected non-null int.'); - final int? arg_webViewInstanceId = (args[1] as int?); - assert(arg_webViewInstanceId != null, + final int? arg_webViewIdentifier = (args[1] as int?); + assert(arg_webViewIdentifier != null, 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.createFromWebView was null, expected non-null int.'); - api.createFromWebView(arg_instanceId!, arg_webViewInstanceId!); + api.createFromWebView(arg_identifier!, arg_webViewIdentifier!); return {}; }); } @@ -379,13 +379,13 @@ abstract class TestWKWebViewConfigurationHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.setAllowsInlineMediaPlayback was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.setAllowsInlineMediaPlayback was null, expected non-null int.'); final bool? arg_allow = (args[1] as bool?); assert(arg_allow != null, 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.setAllowsInlineMediaPlayback was null, expected non-null bool.'); - api.setAllowsInlineMediaPlayback(arg_instanceId!, arg_allow!); + api.setAllowsInlineMediaPlayback(arg_identifier!, arg_allow!); return {}; }); } @@ -402,8 +402,8 @@ abstract class TestWKWebViewConfigurationHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.setMediaTypesRequiringUserActionForPlayback was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.setMediaTypesRequiringUserActionForPlayback was null, expected non-null int.'); final List? arg_types = (args[1] as List?) @@ -411,7 +411,7 @@ abstract class TestWKWebViewConfigurationHostApi { assert(arg_types != null, 'Argument for dev.flutter.pigeon.WKWebViewConfigurationHostApi.setMediaTypesRequiringUserActionForPlayback was null, expected non-null List.'); api.setMediaTypesRequiringUserActionForPlayback( - arg_instanceId!, arg_types!); + arg_identifier!, arg_types!); return {}; }); } @@ -454,13 +454,13 @@ abstract class TestWKUserContentControllerHostApi { _TestWKUserContentControllerHostApiCodec(); void createFromWebViewConfiguration( - int instanceId, int configurationInstanceId); + int identifier, int configurationIdentifier); void addScriptMessageHandler( - int instanceId, int handlerInstanceid, String name); - void removeScriptMessageHandler(int instanceId, String name); - void removeAllScriptMessageHandlers(int instanceId); - void addUserScript(int instanceId, WKUserScriptData userScript); - void removeAllUserScripts(int instanceId); + int identifier, int handlerIdentifier, String name); + void removeScriptMessageHandler(int identifier, String name); + void removeAllScriptMessageHandlers(int identifier); + void addUserScript(int identifier, WKUserScriptData userScript); + void removeAllUserScripts(int identifier); static void setup(TestWKUserContentControllerHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -475,14 +475,14 @@ abstract class TestWKUserContentControllerHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.createFromWebViewConfiguration was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.createFromWebViewConfiguration was null, expected non-null int.'); - final int? arg_configurationInstanceId = (args[1] as int?); - assert(arg_configurationInstanceId != null, + final int? arg_configurationIdentifier = (args[1] as int?); + assert(arg_configurationIdentifier != null, 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.createFromWebViewConfiguration was null, expected non-null int.'); api.createFromWebViewConfiguration( - arg_instanceId!, arg_configurationInstanceId!); + arg_identifier!, arg_configurationIdentifier!); return {}; }); } @@ -499,17 +499,17 @@ abstract class TestWKUserContentControllerHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.addScriptMessageHandler was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.addScriptMessageHandler was null, expected non-null int.'); - final int? arg_handlerInstanceid = (args[1] as int?); - assert(arg_handlerInstanceid != null, + final int? arg_handlerIdentifier = (args[1] as int?); + assert(arg_handlerIdentifier != null, 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.addScriptMessageHandler was null, expected non-null int.'); final String? arg_name = (args[2] as String?); assert(arg_name != null, 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.addScriptMessageHandler was null, expected non-null String.'); api.addScriptMessageHandler( - arg_instanceId!, arg_handlerInstanceid!, arg_name!); + arg_identifier!, arg_handlerIdentifier!, arg_name!); return {}; }); } @@ -526,13 +526,13 @@ abstract class TestWKUserContentControllerHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.removeScriptMessageHandler was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.removeScriptMessageHandler was null, expected non-null int.'); final String? arg_name = (args[1] as String?); assert(arg_name != null, 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.removeScriptMessageHandler was null, expected non-null String.'); - api.removeScriptMessageHandler(arg_instanceId!, arg_name!); + api.removeScriptMessageHandler(arg_identifier!, arg_name!); return {}; }); } @@ -549,10 +549,10 @@ abstract class TestWKUserContentControllerHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllScriptMessageHandlers was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllScriptMessageHandlers was null, expected non-null int.'); - api.removeAllScriptMessageHandlers(arg_instanceId!); + api.removeAllScriptMessageHandlers(arg_identifier!); return {}; }); } @@ -569,14 +569,14 @@ abstract class TestWKUserContentControllerHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.addUserScript was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.addUserScript was null, expected non-null int.'); final WKUserScriptData? arg_userScript = (args[1] as WKUserScriptData?); assert(arg_userScript != null, 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.addUserScript was null, expected non-null WKUserScriptData.'); - api.addUserScript(arg_instanceId!, arg_userScript!); + api.addUserScript(arg_identifier!, arg_userScript!); return {}; }); } @@ -593,10 +593,10 @@ abstract class TestWKUserContentControllerHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllUserScripts was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKUserContentControllerHostApi.removeAllUserScripts was null, expected non-null int.'); - api.removeAllUserScripts(arg_instanceId!); + api.removeAllUserScripts(arg_identifier!); return {}; }); } @@ -612,8 +612,8 @@ abstract class TestWKPreferencesHostApi { static const MessageCodec codec = _TestWKPreferencesHostApiCodec(); void createFromWebViewConfiguration( - int instanceId, int configurationInstanceId); - void setJavaScriptEnabled(int instanceId, bool enabled); + int identifier, int configurationIdentifier); + void setJavaScriptEnabled(int identifier, bool enabled); static void setup(TestWKPreferencesHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -628,14 +628,14 @@ abstract class TestWKPreferencesHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKPreferencesHostApi.createFromWebViewConfiguration was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKPreferencesHostApi.createFromWebViewConfiguration was null, expected non-null int.'); - final int? arg_configurationInstanceId = (args[1] as int?); - assert(arg_configurationInstanceId != null, + final int? arg_configurationIdentifier = (args[1] as int?); + assert(arg_configurationIdentifier != null, 'Argument for dev.flutter.pigeon.WKPreferencesHostApi.createFromWebViewConfiguration was null, expected non-null int.'); api.createFromWebViewConfiguration( - arg_instanceId!, arg_configurationInstanceId!); + arg_identifier!, arg_configurationIdentifier!); return {}; }); } @@ -651,13 +651,13 @@ abstract class TestWKPreferencesHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKPreferencesHostApi.setJavaScriptEnabled was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKPreferencesHostApi.setJavaScriptEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); assert(arg_enabled != null, 'Argument for dev.flutter.pigeon.WKPreferencesHostApi.setJavaScriptEnabled was null, expected non-null bool.'); - api.setJavaScriptEnabled(arg_instanceId!, arg_enabled!); + api.setJavaScriptEnabled(arg_identifier!, arg_enabled!); return {}; }); } @@ -673,7 +673,7 @@ abstract class TestWKScriptMessageHandlerHostApi { static const MessageCodec codec = _TestWKScriptMessageHandlerHostApiCodec(); - void create(int instanceId); + void create(int identifier); static void setup(TestWKScriptMessageHandlerHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -687,10 +687,10 @@ abstract class TestWKScriptMessageHandlerHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKScriptMessageHandlerHostApi.create was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKScriptMessageHandlerHostApi.create was null, expected non-null int.'); - api.create(arg_instanceId!); + api.create(arg_identifier!); return {}; }); } @@ -706,8 +706,8 @@ abstract class TestWKNavigationDelegateHostApi { static const MessageCodec codec = _TestWKNavigationDelegateHostApiCodec(); - void create(int instanceId); - void setDidFinishNavigation(int instanceId, int? functionInstanceId); + void create(int identifier); + void setDidFinishNavigation(int identifier, int? functionIdentifier); static void setup(TestWKNavigationDelegateHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -721,10 +721,10 @@ abstract class TestWKNavigationDelegateHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKNavigationDelegateHostApi.create was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKNavigationDelegateHostApi.create was null, expected non-null int.'); - api.create(arg_instanceId!); + api.create(arg_identifier!); return {}; }); } @@ -741,11 +741,11 @@ abstract class TestWKNavigationDelegateHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation was null, expected non-null int.'); - final int? arg_functionInstanceId = (args[1] as int?); - api.setDidFinishNavigation(arg_instanceId!, arg_functionInstanceId); + final int? arg_functionIdentifier = (args[1] as int?); + api.setDidFinishNavigation(arg_identifier!, arg_functionIdentifier); return {}; }); } @@ -780,10 +780,10 @@ class _TestNSObjectHostApiCodec extends StandardMessageCodec { abstract class TestNSObjectHostApi { static const MessageCodec codec = _TestNSObjectHostApiCodec(); - void dispose(int instanceId); - void addObserver(int instanceId, int observerInstanceId, String keyPath, + void dispose(int identifier); + void addObserver(int identifier, int observerIdentifier, String keyPath, List options); - void removeObserver(int instanceId, int observerInstanceId, String keyPath); + void removeObserver(int identifier, int observerIdentifier, String keyPath); static void setup(TestNSObjectHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -797,10 +797,10 @@ abstract class TestNSObjectHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.NSObjectHostApi.dispose was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.NSObjectHostApi.dispose was null, expected non-null int.'); - api.dispose(arg_instanceId!); + api.dispose(arg_identifier!); return {}; }); } @@ -816,11 +816,11 @@ abstract class TestNSObjectHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.NSObjectHostApi.addObserver was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.NSObjectHostApi.addObserver was null, expected non-null int.'); - final int? arg_observerInstanceId = (args[1] as int?); - assert(arg_observerInstanceId != null, + final int? arg_observerIdentifier = (args[1] as int?); + assert(arg_observerIdentifier != null, 'Argument for dev.flutter.pigeon.NSObjectHostApi.addObserver was null, expected non-null int.'); final String? arg_keyPath = (args[2] as String?); assert(arg_keyPath != null, @@ -830,7 +830,7 @@ abstract class TestNSObjectHostApi { ?.cast(); assert(arg_options != null, 'Argument for dev.flutter.pigeon.NSObjectHostApi.addObserver was null, expected non-null List.'); - api.addObserver(arg_instanceId!, arg_observerInstanceId!, + api.addObserver(arg_identifier!, arg_observerIdentifier!, arg_keyPath!, arg_options!); return {}; }); @@ -847,17 +847,17 @@ abstract class TestNSObjectHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.NSObjectHostApi.removeObserver was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.NSObjectHostApi.removeObserver was null, expected non-null int.'); - final int? arg_observerInstanceId = (args[1] as int?); - assert(arg_observerInstanceId != null, + final int? arg_observerIdentifier = (args[1] as int?); + assert(arg_observerIdentifier != null, 'Argument for dev.flutter.pigeon.NSObjectHostApi.removeObserver was null, expected non-null int.'); final String? arg_keyPath = (args[2] as String?); assert(arg_keyPath != null, 'Argument for dev.flutter.pigeon.NSObjectHostApi.removeObserver was null, expected non-null String.'); api.removeObserver( - arg_instanceId!, arg_observerInstanceId!, arg_keyPath!); + arg_identifier!, arg_observerIdentifier!, arg_keyPath!); return {}; }); } @@ -934,24 +934,24 @@ class _TestWKWebViewHostApiCodec extends StandardMessageCodec { abstract class TestWKWebViewHostApi { static const MessageCodec codec = _TestWKWebViewHostApiCodec(); - void create(int instanceId, int configurationInstanceId); - void setUIDelegate(int instanceId, int? uiDelegateInstanceId); - void setNavigationDelegate(int instanceId, int? navigationDelegateInstanceId); - String? getUrl(int instanceId); - double getEstimatedProgress(int instanceId); - void loadRequest(int instanceId, NSUrlRequestData request); - void loadHtmlString(int instanceId, String string, String? baseUrl); - void loadFileUrl(int instanceId, String url, String readAccessUrl); - void loadFlutterAsset(int instanceId, String key); - bool canGoBack(int instanceId); - bool canGoForward(int instanceId); - void goBack(int instanceId); - void goForward(int instanceId); - void reload(int instanceId); - String? getTitle(int instanceId); - void setAllowsBackForwardNavigationGestures(int instanceId, bool allow); - void setCustomUserAgent(int instanceId, String? userAgent); - Future evaluateJavaScript(int instanceId, String javaScriptString); + void create(int identifier, int configurationIdentifier); + void setUIDelegate(int identifier, int? uiDelegateIdentifier); + void setNavigationDelegate(int identifier, int? navigationDelegateIdentifier); + String? getUrl(int identifier); + double getEstimatedProgress(int identifier); + void loadRequest(int identifier, NSUrlRequestData request); + void loadHtmlString(int identifier, String string, String? baseUrl); + void loadFileUrl(int identifier, String url, String readAccessUrl); + void loadFlutterAsset(int identifier, String key); + bool canGoBack(int identifier); + bool canGoForward(int identifier); + void goBack(int identifier); + void goForward(int identifier); + void reload(int identifier); + String? getTitle(int identifier); + void setAllowsBackForwardNavigationGestures(int identifier, bool allow); + void setCustomUserAgent(int identifier, String? userAgent); + Future evaluateJavaScript(int identifier, String javaScriptString); static void setup(TestWKWebViewHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -965,13 +965,13 @@ abstract class TestWKWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.create was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.create was null, expected non-null int.'); - final int? arg_configurationInstanceId = (args[1] as int?); - assert(arg_configurationInstanceId != null, + final int? arg_configurationIdentifier = (args[1] as int?); + assert(arg_configurationIdentifier != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.create was null, expected non-null int.'); - api.create(arg_instanceId!, arg_configurationInstanceId!); + api.create(arg_identifier!, arg_configurationIdentifier!); return {}; }); } @@ -987,11 +987,11 @@ abstract class TestWKWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setUIDelegate was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setUIDelegate was null, expected non-null int.'); - final int? arg_uiDelegateInstanceId = (args[1] as int?); - api.setUIDelegate(arg_instanceId!, arg_uiDelegateInstanceId); + final int? arg_uiDelegateIdentifier = (args[1] as int?); + api.setUIDelegate(arg_identifier!, arg_uiDelegateIdentifier); return {}; }); } @@ -1007,12 +1007,12 @@ abstract class TestWKWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setNavigationDelegate was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setNavigationDelegate was null, expected non-null int.'); - final int? arg_navigationDelegateInstanceId = (args[1] as int?); + final int? arg_navigationDelegateIdentifier = (args[1] as int?); api.setNavigationDelegate( - arg_instanceId!, arg_navigationDelegateInstanceId); + arg_identifier!, arg_navigationDelegateIdentifier); return {}; }); } @@ -1028,10 +1028,10 @@ abstract class TestWKWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.getUrl was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.getUrl was null, expected non-null int.'); - final String? output = api.getUrl(arg_instanceId!); + final String? output = api.getUrl(arg_identifier!); return {'result': output}; }); } @@ -1047,10 +1047,10 @@ abstract class TestWKWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.getEstimatedProgress was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.getEstimatedProgress was null, expected non-null int.'); - final double output = api.getEstimatedProgress(arg_instanceId!); + final double output = api.getEstimatedProgress(arg_identifier!); return {'result': output}; }); } @@ -1066,13 +1066,13 @@ abstract class TestWKWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadRequest was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadRequest was null, expected non-null int.'); final NSUrlRequestData? arg_request = (args[1] as NSUrlRequestData?); assert(arg_request != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadRequest was null, expected non-null NSUrlRequestData.'); - api.loadRequest(arg_instanceId!, arg_request!); + api.loadRequest(arg_identifier!, arg_request!); return {}; }); } @@ -1088,14 +1088,14 @@ abstract class TestWKWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString was null, expected non-null int.'); final String? arg_string = (args[1] as String?); assert(arg_string != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadHtmlString was null, expected non-null String.'); final String? arg_baseUrl = (args[2] as String?); - api.loadHtmlString(arg_instanceId!, arg_string!, arg_baseUrl); + api.loadHtmlString(arg_identifier!, arg_string!, arg_baseUrl); return {}; }); } @@ -1111,8 +1111,8 @@ abstract class TestWKWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadFileUrl was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadFileUrl was null, expected non-null int.'); final String? arg_url = (args[1] as String?); assert(arg_url != null, @@ -1120,7 +1120,7 @@ abstract class TestWKWebViewHostApi { final String? arg_readAccessUrl = (args[2] as String?); assert(arg_readAccessUrl != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadFileUrl was null, expected non-null String.'); - api.loadFileUrl(arg_instanceId!, arg_url!, arg_readAccessUrl!); + api.loadFileUrl(arg_identifier!, arg_url!, arg_readAccessUrl!); return {}; }); } @@ -1136,13 +1136,13 @@ abstract class TestWKWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadFlutterAsset was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadFlutterAsset was null, expected non-null int.'); final String? arg_key = (args[1] as String?); assert(arg_key != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.loadFlutterAsset was null, expected non-null String.'); - api.loadFlutterAsset(arg_instanceId!, arg_key!); + api.loadFlutterAsset(arg_identifier!, arg_key!); return {}; }); } @@ -1158,10 +1158,10 @@ abstract class TestWKWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.canGoBack was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.canGoBack was null, expected non-null int.'); - final bool output = api.canGoBack(arg_instanceId!); + final bool output = api.canGoBack(arg_identifier!); return {'result': output}; }); } @@ -1177,10 +1177,10 @@ abstract class TestWKWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.canGoForward was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.canGoForward was null, expected non-null int.'); - final bool output = api.canGoForward(arg_instanceId!); + final bool output = api.canGoForward(arg_identifier!); return {'result': output}; }); } @@ -1196,10 +1196,10 @@ abstract class TestWKWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.goBack was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.goBack was null, expected non-null int.'); - api.goBack(arg_instanceId!); + api.goBack(arg_identifier!); return {}; }); } @@ -1215,10 +1215,10 @@ abstract class TestWKWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.goForward was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.goForward was null, expected non-null int.'); - api.goForward(arg_instanceId!); + api.goForward(arg_identifier!); return {}; }); } @@ -1234,10 +1234,10 @@ abstract class TestWKWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.reload was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.reload was null, expected non-null int.'); - api.reload(arg_instanceId!); + api.reload(arg_identifier!); return {}; }); } @@ -1253,10 +1253,10 @@ abstract class TestWKWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.getTitle was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.getTitle was null, expected non-null int.'); - final String? output = api.getTitle(arg_instanceId!); + final String? output = api.getTitle(arg_identifier!); return {'result': output}; }); } @@ -1273,14 +1273,14 @@ abstract class TestWKWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setAllowsBackForwardNavigationGestures was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setAllowsBackForwardNavigationGestures was null, expected non-null int.'); final bool? arg_allow = (args[1] as bool?); assert(arg_allow != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setAllowsBackForwardNavigationGestures was null, expected non-null bool.'); api.setAllowsBackForwardNavigationGestures( - arg_instanceId!, arg_allow!); + arg_identifier!, arg_allow!); return {}; }); } @@ -1296,11 +1296,11 @@ abstract class TestWKWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent was null, expected non-null int.'); final String? arg_userAgent = (args[1] as String?); - api.setCustomUserAgent(arg_instanceId!, arg_userAgent); + api.setCustomUserAgent(arg_identifier!, arg_userAgent); return {}; }); } @@ -1316,14 +1316,14 @@ abstract class TestWKWebViewHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript was null, expected non-null int.'); final String? arg_javaScriptString = (args[1] as String?); assert(arg_javaScriptString != null, 'Argument for dev.flutter.pigeon.WKWebViewHostApi.evaluateJavaScript was null, expected non-null String.'); final Object? output = await api.evaluateJavaScript( - arg_instanceId!, arg_javaScriptString!); + arg_identifier!, arg_javaScriptString!); return {'result': output}; }); } @@ -1338,7 +1338,7 @@ class _TestWKUIDelegateHostApiCodec extends StandardMessageCodec { abstract class TestWKUIDelegateHostApi { static const MessageCodec codec = _TestWKUIDelegateHostApiCodec(); - void create(int instanceId); + void create(int identifier); static void setup(TestWKUIDelegateHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1352,10 +1352,10 @@ abstract class TestWKUIDelegateHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKUIDelegateHostApi.create was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKUIDelegateHostApi.create was null, expected non-null int.'); - api.create(arg_instanceId!); + api.create(arg_identifier!); return {}; }); } @@ -1398,8 +1398,8 @@ abstract class TestWKHttpCookieStoreHostApi { _TestWKHttpCookieStoreHostApiCodec(); void createFromWebsiteDataStore( - int instanceId, int websiteDataStoreInstanceId); - Future setCookie(int instanceId, NSHttpCookieData cookie); + int identifier, int websiteDataStoreIdentifier); + Future setCookie(int identifier, NSHttpCookieData cookie); static void setup(TestWKHttpCookieStoreHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1414,14 +1414,14 @@ abstract class TestWKHttpCookieStoreHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKHttpCookieStoreHostApi.createFromWebsiteDataStore was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKHttpCookieStoreHostApi.createFromWebsiteDataStore was null, expected non-null int.'); - final int? arg_websiteDataStoreInstanceId = (args[1] as int?); - assert(arg_websiteDataStoreInstanceId != null, + final int? arg_websiteDataStoreIdentifier = (args[1] as int?); + assert(arg_websiteDataStoreIdentifier != null, 'Argument for dev.flutter.pigeon.WKHttpCookieStoreHostApi.createFromWebsiteDataStore was null, expected non-null int.'); api.createFromWebsiteDataStore( - arg_instanceId!, arg_websiteDataStoreInstanceId!); + arg_identifier!, arg_websiteDataStoreIdentifier!); return {}; }); } @@ -1437,13 +1437,13 @@ abstract class TestWKHttpCookieStoreHostApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKHttpCookieStoreHostApi.setCookie was null.'); final List args = (message as List?)!; - final int? arg_instanceId = (args[0] as int?); - assert(arg_instanceId != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKHttpCookieStoreHostApi.setCookie was null, expected non-null int.'); final NSHttpCookieData? arg_cookie = (args[1] as NSHttpCookieData?); assert(arg_cookie != null, 'Argument for dev.flutter.pigeon.WKHttpCookieStoreHostApi.setCookie was null, expected non-null NSHttpCookieData.'); - await api.setCookie(arg_instanceId!, arg_cookie!); + await api.setCookie(arg_identifier!, arg_cookie!); return {}; }); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart index 007c2bc32252..03bb7747a048 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart @@ -35,7 +35,7 @@ void main() { TestNSObjectHostApi.setup(mockPlatformHostApi); object = NSObject(instanceManager: instanceManager); - instanceManager.tryAddInstance(object); + instanceManager.addDartCreatedInstance(object); }); tearDown(() { @@ -44,7 +44,7 @@ void main() { test('addObserver', () async { final NSObject observer = NSObject(instanceManager: instanceManager); - instanceManager.tryAddInstance(observer); + instanceManager.addDartCreatedInstance(observer); await object.addObserver( observer, @@ -57,8 +57,8 @@ void main() { final List optionsData = verify(mockPlatformHostApi.addObserver( - instanceManager.getInstanceId(object), - instanceManager.getInstanceId(observer), + instanceManager.getIdentifier(object), + instanceManager.getIdentifier(observer), 'aKeyPath', captureAny, )).captured.single as List; @@ -76,19 +76,19 @@ void main() { test('removeObserver', () async { final NSObject observer = NSObject(instanceManager: instanceManager); - instanceManager.tryAddInstance(observer); + instanceManager.addDartCreatedInstance(observer); await object.removeObserver(observer, keyPath: 'aKeyPath'); verify(mockPlatformHostApi.removeObserver( - instanceManager.getInstanceId(object), - instanceManager.getInstanceId(observer), + instanceManager.getIdentifier(object), + instanceManager.getIdentifier(observer), 'aKeyPath', )); }); test('dispose', () async { - final int instanceId = instanceManager.getInstanceId(object)!; + final int instanceId = instanceManager.getIdentifier(object)!; await object.dispose(); verify( diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart index 7db190f8192c..ef68bbb41809 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart @@ -54,7 +54,7 @@ void main() { webView, instanceManager: instanceManager, ); - scrollViewInstanceId = instanceManager.getInstanceId(scrollView)!; + scrollViewInstanceId = instanceManager.getIdentifier(scrollView)!; }); tearDown(() { @@ -98,7 +98,7 @@ void main() { TestUIViewHostApi.setup(mockPlatformHostApi); view = UIView(instanceManager: instanceManager); - viewInstanceId = instanceManager.tryAddInstance(view)!; + viewInstanceId = instanceManager.addDartCreatedInstance(view); }); tearDown(() { 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 b09f3461c397..da92ab71999b 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 @@ -71,8 +71,8 @@ void main() { test('createFromWebViewConfiguration', () { verify(mockPlatformHostApi.createFromWebViewConfiguration( - instanceManager.getInstanceId(websiteDataStore), - instanceManager.getInstanceId(webViewConfiguration), + instanceManager.getIdentifier(websiteDataStore), + instanceManager.getIdentifier(webViewConfiguration), )); }); @@ -81,7 +81,7 @@ void main() { WKWebsiteDataStore.defaultDataStore; verify( mockPlatformHostApi.createDefaultDataStore( - InstanceManager.instance.getInstanceId(defaultDataStore), + InstanceManager.instance.getIdentifier(defaultDataStore), ), ); }); @@ -103,7 +103,7 @@ void main() { final List typeData = verify(mockPlatformHostApi.removeDataOfTypes( - instanceManager.getInstanceId(websiteDataStore), + instanceManager.getIdentifier(websiteDataStore), captureAny, 5.0, )).captured.single.cast() @@ -150,8 +150,8 @@ void main() { test('createFromWebsiteDataStore', () { verify(mockPlatformHostApi.createFromWebsiteDataStore( - instanceManager.getInstanceId(httpCookieStore), - instanceManager.getInstanceId(websiteDataStore), + instanceManager.getIdentifier(httpCookieStore), + instanceManager.getIdentifier(websiteDataStore), )); }); @@ -163,7 +163,7 @@ void main() { final NSHttpCookieData cookie = verify( mockPlatformHostApi.setCookie( - instanceManager.getInstanceId(httpCookieStore), + instanceManager.getIdentifier(httpCookieStore), captureAny, ), ).captured.single as NSHttpCookieData; @@ -196,7 +196,7 @@ void main() { test('create', () async { verify(mockPlatformHostApi.create( - instanceManager.getInstanceId(scriptMessageHandler), + instanceManager.getIdentifier(scriptMessageHandler), )); }); }); @@ -232,15 +232,15 @@ void main() { test('createFromWebViewConfiguration', () async { verify(mockPlatformHostApi.createFromWebViewConfiguration( - instanceManager.getInstanceId(preferences), - instanceManager.getInstanceId(webViewConfiguration), + instanceManager.getIdentifier(preferences), + instanceManager.getIdentifier(webViewConfiguration), )); }); test('setJavaScriptEnabled', () async { await preferences.setJavaScriptEnabled(true); verify(mockPlatformHostApi.setJavaScriptEnabled( - instanceManager.getInstanceId(preferences), + instanceManager.getIdentifier(preferences), true, )); }); @@ -278,8 +278,8 @@ void main() { test('createFromWebViewConfiguration', () async { verify(mockPlatformHostApi.createFromWebViewConfiguration( - instanceManager.getInstanceId(userContentController), - instanceManager.getInstanceId(webViewConfiguration), + instanceManager.getIdentifier(userContentController), + instanceManager.getIdentifier(webViewConfiguration), )); }); @@ -293,8 +293,8 @@ void main() { userContentController.addScriptMessageHandler(handler, 'handlerName'); verify(mockPlatformHostApi.addScriptMessageHandler( - instanceManager.getInstanceId(userContentController), - instanceManager.getInstanceId(handler), + instanceManager.getIdentifier(userContentController), + instanceManager.getIdentifier(handler), 'handlerName', )); }); @@ -302,7 +302,7 @@ void main() { test('removeScriptMessageHandler', () async { userContentController.removeScriptMessageHandler('handlerName'); verify(mockPlatformHostApi.removeScriptMessageHandler( - instanceManager.getInstanceId(userContentController), + instanceManager.getIdentifier(userContentController), 'handlerName', )); }); @@ -310,7 +310,7 @@ void main() { test('removeAllScriptMessageHandlers', () async { userContentController.removeAllScriptMessageHandlers(); verify(mockPlatformHostApi.removeAllScriptMessageHandlers( - instanceManager.getInstanceId(userContentController), + instanceManager.getIdentifier(userContentController), )); }); @@ -321,7 +321,7 @@ void main() { isMainFrameOnly: false, )); verify(mockPlatformHostApi.addUserScript( - instanceManager.getInstanceId(userContentController), + instanceManager.getIdentifier(userContentController), argThat(isA()), )); }); @@ -329,7 +329,7 @@ void main() { test('removeAllUserScripts', () { userContentController.removeAllUserScripts(); verify(mockPlatformHostApi.removeAllUserScripts( - instanceManager.getInstanceId(userContentController), + instanceManager.getIdentifier(userContentController), )); }); }); @@ -354,7 +354,7 @@ void main() { test('create', () async { verify( - mockPlatformHostApi.create(instanceManager.getInstanceId( + mockPlatformHostApi.create(instanceManager.getIdentifier( webViewConfiguration, )), ); @@ -373,15 +373,15 @@ void main() { instanceManager: instanceManager, ); verify(mockPlatformHostApi.createFromWebView( - instanceManager.getInstanceId(configurationFromWebView), - instanceManager.getInstanceId(webView), + instanceManager.getIdentifier(configurationFromWebView), + instanceManager.getIdentifier(webView), )); }); test('allowsInlineMediaPlayback', () { webViewConfiguration.setAllowsInlineMediaPlayback(true); verify(mockPlatformHostApi.setAllowsInlineMediaPlayback( - instanceManager.getInstanceId(webViewConfiguration), + instanceManager.getIdentifier(webViewConfiguration), true, )); }); @@ -396,7 +396,7 @@ void main() { final List typeData = verify( mockPlatformHostApi.setMediaTypesRequiringUserActionForPlayback( - instanceManager.getInstanceId(webViewConfiguration), + instanceManager.getIdentifier(webViewConfiguration), captureAny, )).captured.single as List; @@ -439,7 +439,7 @@ void main() { test('create', () async { verify(mockPlatformHostApi.create( - instanceManager.getInstanceId(navigationDelegate), + instanceManager.getIdentifier(navigationDelegate), )); }); @@ -455,13 +455,13 @@ void main() { final int functionInstanceId = verify(mockPlatformHostApi.setDidFinishNavigation( - instanceManager.getInstanceId(navigationDelegate), + instanceManager.getIdentifier(navigationDelegate), captureAny, )).captured.single as int; flutterApis.navigationDelegateFlutterApi.didFinishNavigation( functionInstanceId, - instanceManager.getInstanceId(webView)!, + instanceManager.getIdentifier(webView)!, 'url', ); @@ -491,7 +491,7 @@ void main() { webViewConfiguration, instanceManager: instanceManager, ); - webViewInstanceId = instanceManager.getInstanceId(webView)!; + webViewInstanceId = instanceManager.getIdentifier(webView)!; }); tearDown(() { @@ -501,8 +501,8 @@ void main() { test('create', () async { verify(mockPlatformHostApi.create( - instanceManager.getInstanceId(webView), - instanceManager.getInstanceId( + instanceManager.getIdentifier(webView), + instanceManager.getIdentifier( webViewConfiguration, ), )); @@ -517,7 +517,7 @@ void main() { await webView.setUIDelegate(uiDelegate); verify(mockPlatformHostApi.setUIDelegate( webViewInstanceId, - instanceManager.getInstanceId(uiDelegate), + instanceManager.getIdentifier(uiDelegate), )); TestWKUIDelegateHostApi.setup(null); @@ -534,7 +534,7 @@ void main() { await webView.setNavigationDelegate(navigationDelegate); verify(mockPlatformHostApi.setNavigationDelegate( webViewInstanceId, - instanceManager.getInstanceId(navigationDelegate), + instanceManager.getIdentifier(navigationDelegate), )); TestWKNavigationDelegateHostApi.setup(null); @@ -650,7 +650,7 @@ void main() { test('create', () async { verify(mockPlatformHostApi.create( - instanceManager.getInstanceId(uiDelegate), + instanceManager.getIdentifier(uiDelegate), )); }); }); From ae6757435c975bd55083cf6075e1e7dc552725e7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 25 May 2022 19:08:13 -0400 Subject: [PATCH 366/844] Roll Flutter from da24f105bd31 to e8995736bf55 (15 revisions) (#5834) --- .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 07cf2dc3a573..da8d2239a3a1 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -da24f105bd31ff2086c3f33105a0f2053bd14760 +e8995736bf556458db4e908d918b48cae0fa1992 From 95ec9400af7a75142433f851f09e2e2f206abd1f Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 26 May 2022 12:55:13 -0400 Subject: [PATCH 367/844] [camera] Switch to platform-interface-provided streaming (#5833) --- packages/camera/camera/CHANGELOG.md | 4 ++ .../camera/lib/src/camera_controller.dart | 32 +++------ .../camera/camera/lib/src/camera_image.dart | 35 +++++++++- packages/camera/camera/pubspec.yaml | 4 +- .../camera/test/camera_image_stream_test.dart | 70 +++++++++---------- .../camera/camera/test/camera_image_test.dart | 55 ++++++++++++++- .../test/utils/method_channel_mock.dart | 39 ----------- 7 files changed, 137 insertions(+), 102 deletions(-) delete mode 100644 packages/camera/camera/test/utils/method_channel_mock.dart diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index bd42ef441287..72af38a9f9de 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.7+1 + +* Moves streaming implementation to the platform interface package. + ## 0.9.7 * Returns all the available cameras on iOS. diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index 5014795320f2..6566e2abc883 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -12,8 +12,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:quiver/core.dart'; -const MethodChannel _channel = MethodChannel('plugins.flutter.io/camera'); - /// Signature for a callback receiving the a camera image. /// /// This is used by [CameraController.startImageStream]. @@ -257,7 +255,7 @@ class CameraController extends ValueNotifier { int _cameraId = kUninitializedCameraId; bool _isDisposed = false; - StreamSubscription? _imageStreamSubscription; + StreamSubscription? _imageStreamSubscription; FutureOr? _initCalled; StreamSubscription? _deviceOrientationSubscription; @@ -438,27 +436,15 @@ class CameraController extends ValueNotifier { } try { - await _channel.invokeMethod('startImageStream'); + _imageStreamSubscription = CameraPlatform.instance + .onStreamedFrameAvailable(_cameraId) + .listen((CameraImageData imageData) { + onAvailable(CameraImage.fromPlatformInterface(imageData)); + }); value = value.copyWith(isStreamingImages: true); } on PlatformException catch (e) { throw CameraException(e.code, e.message); } - const EventChannel cameraEventChannel = - EventChannel('plugins.flutter.io/camera/imageStream'); - _imageStreamSubscription = - cameraEventChannel.receiveBroadcastStream().listen( - (dynamic imageData) { - if (defaultTargetPlatform == TargetPlatform.iOS) { - try { - _channel.invokeMethod('receivedImageStreamData'); - } on PlatformException catch (e) { - throw CameraException(e.code, e.message); - } - } - onAvailable( - CameraImage.fromPlatformData(imageData as Map)); - }, - ); } /// Stop streaming images from platform camera. @@ -487,13 +473,11 @@ class CameraController extends ValueNotifier { try { value = value.copyWith(isStreamingImages: false); - await _channel.invokeMethod('stopImageStream'); + await _imageStreamSubscription?.cancel(); + _imageStreamSubscription = null; } on PlatformException catch (e) { throw CameraException(e.code, e.message); } - - await _imageStreamSubscription?.cancel(); - _imageStreamSubscription = null; } /// Start a video recording. diff --git a/packages/camera/camera/lib/src/camera_image.dart b/packages/camera/camera/lib/src/camera_image.dart index 0f2377ed170c..cb3d306eaf6e 100644 --- a/packages/camera/camera/lib/src/camera_image.dart +++ b/packages/camera/camera/lib/src/camera_image.dart @@ -7,11 +7,24 @@ import 'dart:typed_data'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/foundation.dart'; +// TODO(stuartmorgan): Remove all of these classes in a breaking change, and +// vend the platform interface versions directly. See +// https://github.com/flutter/flutter/issues/104188 + /// A single color plane of image data. /// /// The number and meaning of the planes in an image are determined by the /// format of the Image. class Plane { + Plane._fromPlatformInterface(CameraImagePlane plane) + : bytes = plane.bytes, + bytesPerPixel = plane.bytesPerPixel, + bytesPerRow = plane.bytesPerRow, + height = plane.height, + width = plane.width; + + // Only used by the deprecated codepath that's kept to avoid breaking changes. + // Never called by the plugin itself. Plane._fromPlatformData(Map data) : bytes = data['bytes'] as Uint8List, bytesPerPixel = data['bytesPerPixel'] as int?, @@ -43,6 +56,12 @@ class Plane { /// Describes how pixels are represented in an image. class ImageFormat { + ImageFormat._fromPlatformInterface(CameraImageFormat format) + : group = format.group, + raw = format.raw; + + // Only used by the deprecated codepath that's kept to avoid breaking changes. + // Never called by the plugin itself. ImageFormat._fromPlatformData(this.raw) : group = _asImageFormatGroup(raw); /// Describes the format group the raw image format falls into. @@ -58,6 +77,8 @@ class ImageFormat { final dynamic raw; } +// Only used by the deprecated codepath that's kept to avoid breaking changes. +// Never called by the plugin itself. ImageFormatGroup _asImageFormatGroup(dynamic rawFormat) { if (defaultTargetPlatform == TargetPlatform.android) { switch (rawFormat) { @@ -94,7 +115,19 @@ ImageFormatGroup _asImageFormatGroup(dynamic rawFormat) { /// Although not all image formats are planar on iOS, we treat 1-dimensional /// images as single planar images. class CameraImage { - /// CameraImage Constructor + /// Creates a [CameraImage] from the platform interface version. + CameraImage.fromPlatformInterface(CameraImageData data) + : format = ImageFormat._fromPlatformInterface(data.format), + height = data.height, + width = data.width, + planes = List.unmodifiable(data.planes.map( + (CameraImagePlane plane) => Plane._fromPlatformInterface(plane))), + lensAperture = data.lensAperture, + sensorExposureTime = data.sensorExposureTime, + sensorSensitivity = data.sensorSensitivity; + + /// Creates a [CameraImage] from method channel data. + @Deprecated('Use fromPlatformInterface instead') CameraImage.fromPlatformData(Map data) : format = ImageFormat._fromPlatformData(data['format']), height = data['height'] as int, diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index ea9f2e036161..d1f70d906626 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.7 +version: 0.9.7+1 environment: sdk: ">=2.14.0 <3.0.0" @@ -22,7 +22,7 @@ flutter: default_package: camera_web dependencies: - camera_platform_interface: ^2.1.0 + camera_platform_interface: ^2.2.0 camera_web: ^0.2.1 flutter: sdk: flutter diff --git a/packages/camera/camera/test/camera_image_stream_test.dart b/packages/camera/camera/test/camera_image_stream_test.dart index 7055b2239a5a..a9320e46dfb5 100644 --- a/packages/camera/camera/test/camera_image_stream_test.dart +++ b/packages/camera/camera/test/camera_image_stream_test.dart @@ -2,18 +2,21 @@ // 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:camera/camera.dart'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter_test/flutter_test.dart'; import 'camera_test.dart'; -import 'utils/method_channel_mock.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); + late MockStreamingCameraPlatform mockPlatform; setUp(() { - CameraPlatform.instance = MockCameraPlatform(); + mockPlatform = MockStreamingCameraPlatform(); + CameraPlatform.instance = mockPlatform; }); test('startImageStream() throws $CameraException when uninitialized', () { @@ -87,13 +90,6 @@ void main() { }); test('startImageStream() calls CameraPlatform', () async { - final MethodChannelMock cameraChannelMock = MethodChannelMock( - channelName: 'plugins.flutter.io/camera', - methods: {'startImageStream': {}}); - final MethodChannelMock streamChannelMock = MethodChannelMock( - channelName: 'plugins.flutter.io/camera/imageStream', - methods: {'listen': {}}); - final CameraController cameraController = CameraController( const CameraDescription( name: 'cam', @@ -104,10 +100,8 @@ void main() { await cameraController.startImageStream((CameraImage image) => null); - expect(cameraChannelMock.log, - [isMethodCall('startImageStream', arguments: null)]); - expect(streamChannelMock.log, - [isMethodCall('listen', arguments: null)]); + expect(mockPlatform.streamCallLog, + ['onStreamedFrameAvailable', 'listen']); }); test('stopImageStream() throws $CameraException when uninitialized', () { @@ -178,19 +172,6 @@ void main() { }); test('stopImageStream() intended behaviour', () async { - final MethodChannelMock cameraChannelMock = MethodChannelMock( - channelName: 'plugins.flutter.io/camera', - methods: { - 'startImageStream': {}, - 'stopImageStream': {} - }); - final MethodChannelMock streamChannelMock = MethodChannelMock( - channelName: 'plugins.flutter.io/camera/imageStream', - methods: { - 'listen': {}, - 'cancel': {} - }); - final CameraController cameraController = CameraController( const CameraDescription( name: 'cam', @@ -201,14 +182,33 @@ void main() { await cameraController.startImageStream((CameraImage image) => null); await cameraController.stopImageStream(); - expect(cameraChannelMock.log, [ - isMethodCall('startImageStream', arguments: null), - isMethodCall('stopImageStream', arguments: null) - ]); - - expect(streamChannelMock.log, [ - isMethodCall('listen', arguments: null), - isMethodCall('cancel', arguments: null) - ]); + expect(mockPlatform.streamCallLog, + ['onStreamedFrameAvailable', 'listen', 'cancel']); }); } + +class MockStreamingCameraPlatform extends MockCameraPlatform { + List streamCallLog = []; + + StreamController? _streamController; + + @override + Stream onStreamedFrameAvailable(int cameraId, + {CameraImageStreamOptions? options}) { + streamCallLog.add('onStreamedFrameAvailable'); + _streamController = StreamController( + onListen: _onFrameStreamListen, + onCancel: _onFrameStreamCancel, + ); + return _streamController!.stream; + } + + void _onFrameStreamListen() { + streamCallLog.add('listen'); + } + + FutureOr _onFrameStreamCancel() async { + streamCallLog.add('cancel'); + _streamController = null; + } +} diff --git a/packages/camera/camera/test/camera_image_test.dart b/packages/camera/camera/test/camera_image_test.dart index 55bf4a2727e2..c964e7acd97b 100644 --- a/packages/camera/camera/test/camera_image_test.dart +++ b/packages/camera/camera/test/camera_image_test.dart @@ -5,11 +5,64 @@ import 'dart:typed_data'; import 'package:camera/camera.dart'; +import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { - group('$CameraImage tests', () { + test('translates correctly from platform interface classes', () { + final CameraImageData originalImage = CameraImageData( + format: const CameraImageFormat(ImageFormatGroup.jpeg, raw: 1234), + planes: [ + CameraImagePlane( + bytes: Uint8List.fromList([1, 2, 3, 4]), + bytesPerRow: 20, + bytesPerPixel: 3, + width: 200, + height: 100, + ), + CameraImagePlane( + bytes: Uint8List.fromList([5, 6, 7, 8]), + bytesPerRow: 18, + bytesPerPixel: 4, + width: 220, + height: 110, + ), + ], + width: 640, + height: 480, + lensAperture: 2.5, + sensorExposureTime: 5, + sensorSensitivity: 1.3, + ); + + final CameraImage image = CameraImage.fromPlatformInterface(originalImage); + // Simple values. + expect(image.width, 640); + expect(image.height, 480); + expect(image.lensAperture, 2.5); + expect(image.sensorExposureTime, 5); + expect(image.sensorSensitivity, 1.3); + // Format. + expect(image.format.group, ImageFormatGroup.jpeg); + expect(image.format.raw, 1234); + // Planes. + expect(image.planes.length, originalImage.planes.length); + for (int i = 0; i < image.planes.length; i++) { + expect( + image.planes[i].bytes.length, originalImage.planes[i].bytes.length); + for (int j = 0; j < image.planes[i].bytes.length; j++) { + expect(image.planes[i].bytes[j], originalImage.planes[i].bytes[j]); + } + expect( + image.planes[i].bytesPerPixel, originalImage.planes[i].bytesPerPixel); + expect(image.planes[i].bytesPerRow, originalImage.planes[i].bytesPerRow); + expect(image.planes[i].width, originalImage.planes[i].width); + expect(image.planes[i].height, originalImage.planes[i].height); + } + }); + + group('legacy constructors', () { test('$CameraImage can be created', () { debugDefaultTargetPlatformOverride = TargetPlatform.android; final CameraImage cameraImage = diff --git a/packages/camera/camera/test/utils/method_channel_mock.dart b/packages/camera/camera/test/utils/method_channel_mock.dart deleted file mode 100644 index 7c8b4ca3d3f0..000000000000 --- a/packages/camera/camera/test/utils/method_channel_mock.dart +++ /dev/null @@ -1,39 +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/services.dart'; -import 'package:flutter_test/flutter_test.dart'; - -class MethodChannelMock { - MethodChannelMock({ - required String channelName, - this.delay, - required this.methods, - }) : methodChannel = MethodChannel(channelName) { - methodChannel.setMockMethodCallHandler(_handler); - } - - final Duration? delay; - final MethodChannel methodChannel; - final Map methods; - final List log = []; - - Future _handler(MethodCall methodCall) async { - log.add(methodCall); - - if (!methods.containsKey(methodCall.method)) { - throw MissingPluginException('No implementation found for method ' - '${methodCall.method} on channel ${methodChannel.name}'); - } - - return Future.delayed(delay ?? Duration.zero, () { - final Object? result = methods[methodCall.method]; - if (result is Exception) { - throw result; - } - - return Future.value(result); - }); - } -} From b6acfc46dc8c5d90fcbbfabc00a670e7254e26e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 May 2022 13:15:22 -0700 Subject: [PATCH 368/844] [webview]: Bump gradle from 3.3.0 to 7.2.1 in /packages/webview_flutter/webview_flutter_android/android (#5842) --- packages/webview_flutter/webview_flutter_android/CHANGELOG.md | 4 ++++ .../webview_flutter_android/android/build.gradle | 2 +- packages/webview_flutter/webview_flutter_android/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 41a6fa273149..70e93697bb96 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.9 + +* Updates Gradle to 7.2.1. + ## 2.8.8 * Minor fixes for new analysis options. diff --git a/packages/webview_flutter/webview_flutter_android/android/build.gradle b/packages/webview_flutter/webview_flutter_android/android/build.gradle index 37954b36a834..09579c4abc70 100644 --- a/packages/webview_flutter/webview_flutter_android/android/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:7.2.1' } } diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 04da12f765d4..873d3f7cbf51 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/plugins/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: 2.8.8 +version: 2.8.9 environment: sdk: ">=2.14.0 <3.0.0" From 55ea6ec058424ac67a411d3064e2a1d74b2bef27 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 26 May 2022 14:10:16 -0700 Subject: [PATCH 369/844] [webview_flutter_wkwebview] Raise minimum Dart and Flutter version to 2.17 and 3.0.0, respectively. (#5850) --- .../webview_flutter_wkwebview/CHANGELOG.md | 4 ++ .../src/foundation/foundation_api_impls.dart | 5 +-- .../lib/src/ui_kit/ui_kit_api_impls.dart | 11 ++--- .../lib/src/web_kit/web_kit.dart | 18 +++----- .../lib/src/web_kit/web_kit_api_impls.dart | 45 ++++++++----------- .../lib/src/web_kit_webview_widget.dart | 4 +- .../webview_flutter_wkwebview/pubspec.yaml | 6 +-- 7 files changed, 38 insertions(+), 55 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 00aa7293c9ad..64a2b0974faf 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.0 + +* Raises minimum Dart version to 2.17 and Flutter version to 3.0.0. + ## 2.7.5 * Minor fixes for new analysis options. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart index e8f1c6723d8f..5e5e577a4f41 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart @@ -89,10 +89,9 @@ class FoundationFlutterApis { class NSObjectHostApiImpl extends NSObjectHostApi { /// Constructs an [NSObjectHostApiImpl]. NSObjectHostApiImpl({ - BinaryMessenger? binaryMessenger, + super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance, - super(binaryMessenger: binaryMessenger); + }) : instanceManager = instanceManager ?? InstanceManager.instance; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart index 328965295d56..1d962ee788db 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'dart:math'; import 'package:flutter/painting.dart' show Color; -import 'package:flutter/services.dart'; import '../common/instance_manager.dart'; import '../common/web_kit.pigeon.dart'; @@ -17,10 +16,9 @@ import 'ui_kit.dart'; class UIScrollViewHostApiImpl extends UIScrollViewHostApi { /// Constructs a [UIScrollViewHostApiImpl]. UIScrollViewHostApiImpl({ - BinaryMessenger? binaryMessenger, + super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance, - super(binaryMessenger: binaryMessenger); + }) : instanceManager = instanceManager ?? InstanceManager.instance; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -75,10 +73,9 @@ class UIScrollViewHostApiImpl extends UIScrollViewHostApi { class UIViewHostApiImpl extends UIViewHostApi { /// Constructs a [UIViewHostApiImpl]. UIViewHostApiImpl({ - BinaryMessenger? binaryMessenger, + super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance, - super(binaryMessenger: binaryMessenger); + }) : instanceManager = instanceManager ?? InstanceManager.instance; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; 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 88f763fe6ba3..16490a24d474 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 @@ -600,13 +600,9 @@ class WKUIDelegate { class WKNavigationDelegate extends NSObject { /// Constructs a [WKNavigationDelegate]. WKNavigationDelegate({ - BinaryMessenger? binaryMessenger, - InstanceManager? instanceManager, - }) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl( - binaryMessenger: binaryMessenger, - instanceManager: instanceManager, - ), - super( + super.binaryMessenger, + super.instanceManager, + }) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, ) { @@ -682,17 +678,13 @@ class WKWebView extends UIView { /// configuration object. WKWebView( WKWebViewConfiguration configuration, { - BinaryMessenger? binaryMessenger, - InstanceManager? instanceManager, + super.binaryMessenger, + super.instanceManager, }) : _binaryMessenger = binaryMessenger, _instanceManager = instanceManager, _webViewApi = WKWebViewHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, - ), - super( - binaryMessenger: binaryMessenger, - instanceManager: instanceManager, ) { _webViewApi.createForInstances(this, configuration); } 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 f908256fd6ed..fe4d7d85d2d2 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 @@ -224,10 +224,9 @@ class WebKitFlutterApis { class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi { /// Constructs a [WebsiteDataStoreHostApiImpl]. WKWebsiteDataStoreHostApiImpl({ - BinaryMessenger? binaryMessenger, + super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance, - super(binaryMessenger: binaryMessenger); + }) : instanceManager = instanceManager ?? InstanceManager.instance; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -269,10 +268,9 @@ class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi { class WKScriptMessageHandlerHostApiImpl extends WKScriptMessageHandlerHostApi { /// Constructs a [WKScriptMessageHandlerHostApiImpl]. WKScriptMessageHandlerHostApiImpl({ - BinaryMessenger? binaryMessenger, + super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance, - super(binaryMessenger: binaryMessenger); + }) : instanceManager = instanceManager ?? InstanceManager.instance; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -287,10 +285,9 @@ class WKScriptMessageHandlerHostApiImpl extends WKScriptMessageHandlerHostApi { class WKPreferencesHostApiImpl extends WKPreferencesHostApi { /// Constructs a [WKPreferencesHostApiImpl]. WKPreferencesHostApiImpl({ - BinaryMessenger? binaryMessenger, + super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance, - super(binaryMessenger: binaryMessenger); + }) : instanceManager = instanceManager ?? InstanceManager.instance; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -322,10 +319,9 @@ class WKPreferencesHostApiImpl extends WKPreferencesHostApi { class WKHttpCookieStoreHostApiImpl extends WKHttpCookieStoreHostApi { /// Constructs a [WKHttpCookieStoreHostApiImpl]. WKHttpCookieStoreHostApiImpl({ - BinaryMessenger? binaryMessenger, + super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance, - super(binaryMessenger: binaryMessenger); + }) : instanceManager = instanceManager ?? InstanceManager.instance; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -358,10 +354,9 @@ class WKUserContentControllerHostApiImpl extends WKUserContentControllerHostApi { /// Constructs a [WKUserContentControllerHostApiImpl]. WKUserContentControllerHostApiImpl({ - BinaryMessenger? binaryMessenger, + super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance, - super(binaryMessenger: binaryMessenger); + }) : instanceManager = instanceManager ?? InstanceManager.instance; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -433,10 +428,9 @@ class WKUserContentControllerHostApiImpl class WKWebViewConfigurationHostApiImpl extends WKWebViewConfigurationHostApi { /// Constructs a [WKWebViewConfigurationHostApiImpl]. WKWebViewConfigurationHostApiImpl({ - BinaryMessenger? binaryMessenger, + super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance, - super(binaryMessenger: binaryMessenger); + }) : instanceManager = instanceManager ?? InstanceManager.instance; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -484,10 +478,9 @@ class WKWebViewConfigurationHostApiImpl extends WKWebViewConfigurationHostApi { class WKUIDelegateHostApiImpl extends WKUIDelegateHostApi { /// Constructs a [WKUIDelegateHostApiImpl]. WKUIDelegateHostApiImpl({ - BinaryMessenger? binaryMessenger, + super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance, - super(binaryMessenger: binaryMessenger); + }) : instanceManager = instanceManager ?? InstanceManager.instance; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -502,10 +495,9 @@ class WKUIDelegateHostApiImpl extends WKUIDelegateHostApi { class WKNavigationDelegateHostApiImpl extends WKNavigationDelegateHostApi { /// Constructs a [WKNavigationDelegateHostApiImpl]. WKNavigationDelegateHostApiImpl({ - BinaryMessenger? binaryMessenger, + super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance, - super(binaryMessenger: binaryMessenger); + }) : instanceManager = instanceManager ?? InstanceManager.instance; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -561,10 +553,9 @@ class WKNavigationDelegateFlutterApiImpl class WKWebViewHostApiImpl extends WKWebViewHostApi { /// Constructs a [WKWebViewHostApiImpl]. WKWebViewHostApiImpl({ - BinaryMessenger? binaryMessenger, + super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance, - super(binaryMessenger: binaryMessenger); + }) : instanceManager = instanceManager ?? InstanceManager.instance; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 19051af6ae1a..7001c3131147 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -18,14 +18,14 @@ import 'web_kit/web_kit.dart'; class WebKitWebViewWidget extends StatefulWidget { /// Constructs a [WebKitWebViewWidget]. const WebKitWebViewWidget({ - Key? key, + super.key, required this.creationParams, required this.callbacksHandler, required this.javascriptChannelRegistry, required this.onBuildWidget, this.configuration, @visibleForTesting this.webViewProxy = const WebViewWidgetProxy(), - }) : super(key: key); + }); /// The initial parameters used to setup the WebView. final CreationParams creationParams; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 9ce70bc7fdca..5a8be07cbb91 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -2,11 +2,11 @@ name: webview_flutter_wkwebview description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control. repository: https://github.com/flutter/plugins/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: 2.7.5 +version: 2.8.0 environment: - sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + sdk: ">=2.17.0 <3.0.0" + flutter: ">=3.0.0" flutter: plugin: From 8133236ccc121934d697eb7e131d5bc7a9cf89bc Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 27 May 2022 22:03:14 +0200 Subject: [PATCH 370/844] [path_provider] Support unicode encoded version info values (#4986) --- .../path_provider_windows/CHANGELOG.md | 3 +- .../lib/src/path_provider_windows_real.dart | 46 ++++++++++++--- .../path_provider_windows/pubspec.yaml | 2 +- .../test/path_provider_windows_test.dart | 57 +++++++++++++++++-- 4 files changed, 92 insertions(+), 16 deletions(-) diff --git a/packages/path_provider/path_provider_windows/CHANGELOG.md b/packages/path_provider/path_provider_windows/CHANGELOG.md index d933b0d51da6..f48093bdbcc1 100644 --- a/packages/path_provider/path_provider_windows/CHANGELOG.md +++ b/packages/path_provider/path_provider_windows/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.0.7 +* Added support for unicode encoded VERSIONINFO * Minor fixes for new analysis options. ## 2.0.6 diff --git a/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart b/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart index bfffa848981a..9a8b0cf43bbc 100644 --- a/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart +++ b/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart @@ -13,21 +13,43 @@ import 'package:win32/win32.dart'; import 'folders.dart'; +/// Constant for en-US language used in VersionInfo keys. +@visibleForTesting +const String languageEn = '0409'; + +/// Constant for CP1252 encoding used in VersionInfo keys +@visibleForTesting +const String encodingCP1252 = '04e4'; + +/// Constant for Unicode encoding used in VersionInfo keys +@visibleForTesting +const String encodingUnicode = '04b0'; + /// Wraps the Win32 VerQueryValue API call. /// /// This class exists to allow injecting alternate metadata in tests without /// building multiple custom test binaries. @visibleForTesting class VersionInfoQuerier { - /// Returns the value for [key] in [versionInfo]s English strings section, or - /// null if there is no such entry, or if versionInfo is null. - String? getStringValue(Pointer? versionInfo, String key) { + /// Returns the value for [key] in [versionInfo]s in section with given + /// language and encoding, or null if there is no such entry, + /// or if versionInfo is null. + /// + /// See https://docs.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource + /// for list of possible language and encoding values. + String? getStringValue( + Pointer? versionInfo, + String key, { + required String language, + required String encoding, + }) { + assert(language.isNotEmpty); + assert(encoding.isNotEmpty); if (versionInfo == null) { return null; } - const String kEnUsLanguageCode = '040904e4'; final Pointer keyPath = - TEXT('\\StringFileInfo\\$kEnUsLanguageCode\\$key'); + TEXT('\\StringFileInfo\\$language$encoding\\$key'); final Pointer length = calloc(); final Pointer> valueAddress = calloc>(); try { @@ -150,6 +172,12 @@ class PathProviderWindows extends PathProviderPlatform { } } + String? _getStringValue(Pointer? infoBuffer, String key) => + versionInfoQuerier.getStringValue(infoBuffer, key, + language: languageEn, encoding: encodingCP1252) ?? + versionInfoQuerier.getStringValue(infoBuffer, key, + language: languageEn, encoding: encodingUnicode); + /// Returns the relative path string to append to the root directory returned /// by Win32 APIs for application storage (such as RoamingAppDir) to get a /// directory that is unique to the application. @@ -187,10 +215,10 @@ class PathProviderWindows extends PathProviderPlatform { infoBuffer = null; } } - companyName = _sanitizedDirectoryName( - versionInfoQuerier.getStringValue(infoBuffer, 'CompanyName')); - productName = _sanitizedDirectoryName( - versionInfoQuerier.getStringValue(infoBuffer, 'ProductName')); + companyName = + _sanitizedDirectoryName(_getStringValue(infoBuffer, 'CompanyName')); + productName = + _sanitizedDirectoryName(_getStringValue(infoBuffer, 'ProductName')); // If there was no product name, use the executable name. productName ??= diff --git a/packages/path_provider/path_provider_windows/pubspec.yaml b/packages/path_provider/path_provider_windows/pubspec.yaml index f75dd058b36b..a995b9f81791 100644 --- a/packages/path_provider/path_provider_windows/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_windows description: Windows implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.6 +version: 2.0.7 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart b/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart index 7e4118c1ccc6..571c31473a0b 100644 --- a/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart +++ b/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart @@ -7,15 +7,33 @@ import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; import 'package:path_provider_windows/path_provider_windows.dart'; +import 'package:path_provider_windows/src/path_provider_windows_real.dart' + show languageEn, encodingCP1252, encodingUnicode; // A fake VersionInfoQuerier that just returns preset responses. class FakeVersionInfoQuerier implements VersionInfoQuerier { - FakeVersionInfoQuerier(this.responses); + FakeVersionInfoQuerier( + this.responses, { + this.language = languageEn, + this.encoding = encodingUnicode, + }); + final String language; + final String encoding; final Map responses; - String? getStringValue(Pointer? versionInfo, String key) => - responses[key]; + String? getStringValue( + Pointer? versionInfo, + String key, { + required String language, + required String encoding, + }) { + if (language == this.language && encoding == this.encoding) { + return responses[key]; + } else { + return null; + } + } } void main() { @@ -40,12 +58,26 @@ void main() { expect(path, endsWith(r'flutter_tester')); }, skip: !Platform.isWindows); - test('getApplicationSupportPath with full version info', () async { + test('getApplicationSupportPath with full version info in CP1252', () async { final PathProviderWindows pathProvider = PathProviderWindows(); pathProvider.versionInfoQuerier = FakeVersionInfoQuerier({ 'CompanyName': 'A Company', 'ProductName': 'Amazing App', - }); + }, language: languageEn, encoding: encodingCP1252); + final String? path = await pathProvider.getApplicationSupportPath(); + expect(path, isNotNull); + if (path != null) { + expect(path, endsWith(r'AppData\Roaming\A Company\Amazing App')); + expect(Directory(path).existsSync(), isTrue); + } + }, skip: !Platform.isWindows); + + test('getApplicationSupportPath with full version info in Unicode', () async { + final PathProviderWindows pathProvider = PathProviderWindows(); + pathProvider.versionInfoQuerier = FakeVersionInfoQuerier({ + 'CompanyName': 'A Company', + 'ProductName': 'Amazing App', + }, language: languageEn, encoding: encodingUnicode); final String? path = await pathProvider.getApplicationSupportPath(); expect(path, isNotNull); if (path != null) { @@ -54,6 +86,21 @@ void main() { } }, skip: !Platform.isWindows); + test( + 'getApplicationSupportPath with full version info in Unsupported Encoding', + () async { + final PathProviderWindows pathProvider = PathProviderWindows(); + pathProvider.versionInfoQuerier = FakeVersionInfoQuerier({ + 'CompanyName': 'A Company', + 'ProductName': 'Amazing App', + }, language: '0000', encoding: '0000'); + final String? path = await pathProvider.getApplicationSupportPath(); + expect(path, contains(r'C:\')); + expect(path, contains(r'AppData')); + // The last path component should be the executable name. + expect(path, endsWith(r'flutter_tester')); + }, skip: !Platform.isWindows); + test('getApplicationSupportPath with missing company', () async { final PathProviderWindows pathProvider = PathProviderWindows(); pathProvider.versionInfoQuerier = FakeVersionInfoQuerier({ From 658054829486e5bbddc65a5f7988b0753941c6e6 Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Sat, 28 May 2022 11:03:06 -0700 Subject: [PATCH 371/844] [camera_web] Use CameraAccessDenied for permission error (#5784) --- packages/camera/camera_web/CHANGELOG.md | 4 ++++ .../example/integration_test/camera_error_code_test.dart | 2 +- .../camera/camera_web/lib/src/types/camera_error_code.dart | 2 +- packages/camera/camera_web/pubspec.yaml | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 5a9fb6608323..7d8c2ab6c03b 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.0 + +* **BREAKING CHANGE**: Renames error code `cameraPermission` to `CameraAccessDenied` to be consistent with other platforms. + ## 0.2.1+6 * Minor fixes for new analysis options. diff --git a/packages/camera/camera_web/example/integration_test/camera_error_code_test.dart b/packages/camera/camera_web/example/integration_test/camera_error_code_test.dart index 112683bdad98..e89018f7c512 100644 --- a/packages/camera/camera_web/example/integration_test/camera_error_code_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_error_code_test.dart @@ -46,7 +46,7 @@ void main() { testWidgets('permissionDenied', (WidgetTester tester) async { expect( CameraErrorCode.permissionDenied.toString(), - equals('cameraPermission'), + equals('CameraAccessDenied'), ); }); diff --git a/packages/camera/camera_web/lib/src/types/camera_error_code.dart b/packages/camera/camera_web/lib/src/types/camera_error_code.dart index 2e8a49b63873..8f1831f79cf5 100644 --- a/packages/camera/camera_web/lib/src/types/camera_error_code.dart +++ b/packages/camera/camera_web/lib/src/types/camera_error_code.dart @@ -32,7 +32,7 @@ class CameraErrorCode { /// The camera cannot be used or the permission /// to access the camera is not granted. static const CameraErrorCode permissionDenied = - CameraErrorCode._('cameraPermission'); + CameraErrorCode._('CameraAccessDenied'); /// The camera options are incorrect or attempted /// to access the media input from an insecure context. diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 90d119549e86..68aa79181ce4 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.2.1+6 +version: 0.3.0 environment: sdk: ">=2.12.0 <3.0.0" From b0bfab678f83bebd49e9f9d0a83fe9b40774e853 Mon Sep 17 00:00:00 2001 From: Tim Sneath Date: Sat, 28 May 2022 12:43:00 -0700 Subject: [PATCH 372/844] [path_provider_windows] Update to ffi 2.0.0 (#5853) * Use Win32 type aliases * Generated file update * Bump version * Bump pub dependency in path_provider_linux * Add integration_test dev dependency * Revert "Add integration_test dev dependency" This reverts commit 40e77789de8fb98fd07f4f054ed3fa1521dc29be. * Address review comments --- .../path_provider/path_provider_linux/CHANGELOG.md | 12 ++++++++++-- .../path_provider/path_provider_linux/pubspec.yaml | 4 ++-- .../path_provider/path_provider_windows/CHANGELOG.md | 5 +++-- .../example/windows/flutter/generated_plugins.cmake | 8 ++++++++ .../lib/src/path_provider_windows_real.dart | 11 +++++------ .../path_provider/path_provider_windows/pubspec.yaml | 8 ++++---- 6 files changed, 32 insertions(+), 16 deletions(-) diff --git a/packages/path_provider/path_provider_linux/CHANGELOG.md b/packages/path_provider/path_provider_linux/CHANGELOG.md index c9c4bb3cc906..b69ec900d614 100644 --- a/packages/path_provider/path_provider_linux/CHANGELOG.md +++ b/packages/path_provider/path_provider_linux/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.7 + +* Bumps ffi dependency to match path_provider_windows. + ## 2.1.6 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors @@ -54,20 +58,24 @@ * Check in linux/ directory for example/ -## 0.1.1 - NOT PUBLISHED +## 0.1.1 - NOT PUBLISHED + * Reverts changes on 0.1.0, which broke the tree. +## 0.1.0 - NOT PUBLISHED -## 0.1.0 - NOT PUBLISHED * This release updates getApplicationSupportPath to use the application ID instead of the executable name. * No migration is provided, so any older apps that were using this path will now have a different directory. ## 0.0.1+2 + * This release updates the example to depend on the endorsed plugin rather than relative path ## 0.0.1+1 + * This updates the readme and pubspec and example to reflect the endorsement of this implementation of `path_provider` ## 0.0.1 + * The initial implementation of path\_provider for Linux * Implements getApplicationSupportPath, getApplicationDocumentsPath, getDownloadsPath, and getTemporaryPath diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index 46e248f81c18..c0b7954087f5 100644 --- a/packages/path_provider/path_provider_linux/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_linux description: Linux implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.1.6 +version: 2.1.7 environment: sdk: ">=2.12.0 <3.0.0" @@ -16,7 +16,7 @@ flutter: dartPluginClass: PathProviderLinux dependencies: - ffi: ^1.1.2 + ffi: ">=1.1.2 <3.0.0" flutter: sdk: flutter path: ^1.8.0 diff --git a/packages/path_provider/path_provider_windows/CHANGELOG.md b/packages/path_provider/path_provider_windows/CHANGELOG.md index f48093bdbcc1..3967c38be411 100644 --- a/packages/path_provider/path_provider_windows/CHANGELOG.md +++ b/packages/path_provider/path_provider_windows/CHANGELOG.md @@ -1,6 +1,7 @@ -## 2.0.7 +## 2.1.0 -* Added support for unicode encoded VERSIONINFO +* Upgrades `package:ffi` dependency to 2.0.0. +* Added support for unicode encoded VERSIONINFO. * Minor fixes for new analysis options. ## 2.0.6 diff --git a/packages/path_provider/path_provider_windows/example/windows/flutter/generated_plugins.cmake b/packages/path_provider/path_provider_windows/example/windows/flutter/generated_plugins.cmake index 4d10c2518654..b93c4c30c167 100644 --- a/packages/path_provider/path_provider_windows/example/windows/flutter/generated_plugins.cmake +++ b/packages/path_provider/path_provider_windows/example/windows/flutter/generated_plugins.cmake @@ -5,6 +5,9 @@ list(APPEND FLUTTER_PLUGIN_LIST ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -13,3 +16,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart b/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart index 9a8b0cf43bbc..6a9c138f5346 100644 --- a/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart +++ b/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart @@ -50,7 +50,7 @@ class VersionInfoQuerier { } final Pointer keyPath = TEXT('\\StringFileInfo\\$language$encoding\\$key'); - final Pointer length = calloc(); + final Pointer length = calloc(); final Pointer> valueAddress = calloc>(); try { if (VerQueryValue(versionInfo, keyPath, valueAddress, length) == 0) { @@ -192,10 +192,9 @@ class PathProviderWindows extends PathProviderPlatform { String? companyName; String? productName; - final Pointer moduleNameBuffer = - calloc(MAX_PATH + 1).cast(); - final Pointer unused = calloc(); - Pointer? infoBuffer; + final Pointer moduleNameBuffer = wsalloc(MAX_PATH + 1); + final Pointer unused = calloc(); + Pointer? infoBuffer; try { // Get the module name. final int moduleNameLength = @@ -208,7 +207,7 @@ class PathProviderWindows extends PathProviderPlatform { // From that, load the VERSIONINFO resource final int infoSize = GetFileVersionInfoSize(moduleNameBuffer, unused); if (infoSize != 0) { - infoBuffer = calloc(infoSize); + infoBuffer = calloc(infoSize); if (GetFileVersionInfo(moduleNameBuffer, 0, infoSize, infoBuffer) == 0) { calloc.free(infoBuffer); diff --git a/packages/path_provider/path_provider_windows/pubspec.yaml b/packages/path_provider/path_provider_windows/pubspec.yaml index a995b9f81791..4e99be71da2f 100644 --- a/packages/path_provider/path_provider_windows/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/pubspec.yaml @@ -2,11 +2,11 @@ name: path_provider_windows description: Windows implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.7 +version: 2.1.0 environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + sdk: ">=2.17.0 <3.0.0" + flutter: ">=3.0.0" flutter: plugin: @@ -16,7 +16,7 @@ flutter: dartPluginClass: PathProviderWindows dependencies: - ffi: ^1.0.0 + ffi: ^2.0.0 flutter: sdk: flutter path: ^1.8.0 From 4cf74371557ede36648a2a7b43a37ccbce965249 Mon Sep 17 00:00:00 2001 From: Kyle Finlinson Date: Tue, 31 May 2022 11:53:14 -0600 Subject: [PATCH 373/844] [video_player] Android: video_player_android parts of rotationCorrection fix (#5158) --- .../video_player_android/CHANGELOG.md | 4 + .../video_player_android/android/build.gradle | 4 +- .../plugins/videoplayer/VideoPlayer.java | 45 +++++- .../videoplayer/VideoPlayerPluginTest.java | 15 ++ .../plugins/videoplayer/VideoPlayerTest.java | 148 +++++++++++++++++- .../lib/src/android_video_player.dart | 1 + .../video_player_android/pubspec.yaml | 4 +- .../test/android_video_player_test.dart | 21 +++ 8 files changed, 228 insertions(+), 14 deletions(-) create mode 100644 packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerPluginTest.java diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 28d0188213d6..d5acda4cce22 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.5 + +* Sets rotationCorrection for videos recorded in landscapeRight (https://github.com/flutter/flutter/issues/60327). + ## 2.3.4 * Updates ExoPlayer to 2.17.1. diff --git a/packages/video_player/video_player_android/android/build.gradle b/packages/video_player/video_player_android/android/build.gradle index 32cc203729b8..f9ad4e682630 100644 --- a/packages/video_player/video_player_android/android/build.gradle +++ b/packages/video_player/video_player_android/android/build.gradle @@ -48,7 +48,9 @@ android { implementation 'com.google.android.exoplayer:exoplayer-dash:2.17.1' implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.17.1' testImplementation 'junit:junit:4.12' + testImplementation 'androidx.test:core:1.3.0' testImplementation 'org.mockito:mockito-inline:3.9.0' + testImplementation 'org.robolectric:robolectric:4.5' } @@ -63,4 +65,4 @@ android { } } } -} +} \ No newline at end of file diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java index dc7c88144583..f215354cb929 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java @@ -11,6 +11,7 @@ import android.net.Uri; import android.view.Surface; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.Format; @@ -51,11 +52,11 @@ final class VideoPlayer { private final TextureRegistry.SurfaceTextureEntry textureEntry; - private QueuingEventSink eventSink = new QueuingEventSink(); + private QueuingEventSink eventSink; private final EventChannel eventChannel; - private boolean isInitialized = false; + @VisibleForTesting boolean isInitialized = false; private final VideoPlayerOptions options; @@ -71,10 +72,11 @@ final class VideoPlayer { this.textureEntry = textureEntry; this.options = options; - exoPlayer = new ExoPlayer.Builder(context).build(); + ExoPlayer exoPlayer = new ExoPlayer.Builder(context).build(); Uri uri = Uri.parse(dataSource); DataSource.Factory dataSourceFactory; + if (isHTTP(uri)) { DefaultHttpDataSource.Factory httpDataSourceFactory = new DefaultHttpDataSource.Factory() @@ -90,10 +92,26 @@ final class VideoPlayer { } MediaSource mediaSource = buildMediaSource(uri, dataSourceFactory, formatHint, context); + exoPlayer.setMediaSource(mediaSource); exoPlayer.prepare(); - setupVideoPlayer(eventChannel, textureEntry); + setUpVideoPlayer(exoPlayer, new QueuingEventSink()); + } + + // Constructor used to directly test members of this class. + @VisibleForTesting + VideoPlayer( + ExoPlayer exoPlayer, + EventChannel eventChannel, + TextureRegistry.SurfaceTextureEntry textureEntry, + VideoPlayerOptions options, + QueuingEventSink eventSink) { + this.eventChannel = eventChannel; + this.textureEntry = textureEntry; + this.options = options; + + setUpVideoPlayer(exoPlayer, eventSink); } private static boolean isHTTP(Uri uri) { @@ -106,7 +124,6 @@ private static boolean isHTTP(Uri uri) { private MediaSource buildMediaSource( Uri uri, DataSource.Factory mediaDataSourceFactory, String formatHint, Context context) { - int type; if (formatHint == null) { type = Util.inferContentType(uri.getLastPathSegment()); @@ -153,8 +170,10 @@ private MediaSource buildMediaSource( } } - private void setupVideoPlayer( - EventChannel eventChannel, TextureRegistry.SurfaceTextureEntry textureEntry) { + private void setUpVideoPlayer(ExoPlayer exoPlayer, QueuingEventSink eventSink) { + this.exoPlayer = exoPlayer; + this.eventSink = eventSink; + eventChannel.setStreamHandler( new EventChannel.StreamHandler() { @Override @@ -264,7 +283,8 @@ long getPosition() { } @SuppressWarnings("SuspiciousNameCombination") - private void sendInitialized() { + @VisibleForTesting + void sendInitialized() { if (isInitialized) { Map event = new HashMap<>(); event.put("event", "initialized"); @@ -282,7 +302,16 @@ private void sendInitialized() { } event.put("width", width); event.put("height", height); + + // Rotating the video with ExoPlayer does not seem to be possible with a Surface, + // so inform the Flutter code that the widget needs to be rotated to prevent + // upside-down playback for videos with rotationDegrees of 180 (other orientations work + // correctly without correction). + if (rotationDegrees == 180) { + event.put("rotationCorrection", rotationDegrees); + } } + eventSink.success(event); } } diff --git a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerPluginTest.java b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerPluginTest.java new file mode 100644 index 000000000000..2ed11653a4b8 --- /dev/null +++ b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerPluginTest.java @@ -0,0 +1,15 @@ +// 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.videoplayer; + +import org.junit.Test; + +public class VideoPlayerPluginTest { + // This is only a placeholder test and doesn't actually initialize the plugin. + @Test + public void initPluginDoesNotThrow() { + final VideoPlayerPlugin plugin = new VideoPlayerPlugin(); + } +} diff --git a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java index ec960b7a4480..194f7905b63a 100644 --- a/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java +++ b/packages/video_player/video_player_android/android/src/test/java/io/flutter/plugins/videoplayer/VideoPlayerTest.java @@ -4,12 +4,154 @@ package io.flutter.plugins.videoplayer; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.google.android.exoplayer2.ExoPlayer; +import com.google.android.exoplayer2.Format; +import io.flutter.plugin.common.EventChannel; +import io.flutter.view.TextureRegistry; +import java.util.HashMap; +import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +@RunWith(RobolectricTestRunner.class) public class VideoPlayerTest { - // This is only a placeholder test and doesn't actually initialize the plugin. + private ExoPlayer fakeExoPlayer; + private EventChannel fakeEventChannel; + private TextureRegistry.SurfaceTextureEntry fakeSurfaceTextureEntry; + private VideoPlayerOptions fakeVideoPlayerOptions; + private QueuingEventSink fakeEventSink; + + @Captor private ArgumentCaptor> eventCaptor; + + @Before + public void before() { + MockitoAnnotations.openMocks(this); + + fakeExoPlayer = mock(ExoPlayer.class); + fakeEventChannel = mock(EventChannel.class); + fakeSurfaceTextureEntry = mock(TextureRegistry.SurfaceTextureEntry.class); + fakeVideoPlayerOptions = mock(VideoPlayerOptions.class); + fakeEventSink = mock(QueuingEventSink.class); + } + + @Test + public void sendInitializedSendsExpectedEvent_90RotationDegrees() { + VideoPlayer videoPlayer = + new VideoPlayer( + fakeExoPlayer, + fakeEventChannel, + fakeSurfaceTextureEntry, + fakeVideoPlayerOptions, + fakeEventSink); + Format testFormat = + new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(90).build(); + + when(fakeExoPlayer.getVideoFormat()).thenReturn(testFormat); + when(fakeExoPlayer.getDuration()).thenReturn(10L); + + videoPlayer.isInitialized = true; + videoPlayer.sendInitialized(); + + verify(fakeEventSink).success(eventCaptor.capture()); + HashMap event = eventCaptor.getValue(); + + assertEquals(event.get("event"), "initialized"); + assertEquals(event.get("duration"), 10L); + assertEquals(event.get("width"), 200); + assertEquals(event.get("height"), 100); + assertEquals(event.get("rotationCorrection"), null); + } + @Test - public void initPluginDoesNotThrow() { - final VideoPlayerPlugin plugin = new VideoPlayerPlugin(); + public void sendInitializedSendsExpectedEvent_270RotationDegrees() { + VideoPlayer videoPlayer = + new VideoPlayer( + fakeExoPlayer, + fakeEventChannel, + fakeSurfaceTextureEntry, + fakeVideoPlayerOptions, + fakeEventSink); + Format testFormat = + new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(270).build(); + + when(fakeExoPlayer.getVideoFormat()).thenReturn(testFormat); + when(fakeExoPlayer.getDuration()).thenReturn(10L); + + videoPlayer.isInitialized = true; + videoPlayer.sendInitialized(); + + verify(fakeEventSink).success(eventCaptor.capture()); + HashMap event = eventCaptor.getValue(); + + assertEquals(event.get("event"), "initialized"); + assertEquals(event.get("duration"), 10L); + assertEquals(event.get("width"), 200); + assertEquals(event.get("height"), 100); + assertEquals(event.get("rotationCorrection"), null); + } + + @Test + public void sendInitializedSendsExpectedEvent_0RotationDegrees() { + VideoPlayer videoPlayer = + new VideoPlayer( + fakeExoPlayer, + fakeEventChannel, + fakeSurfaceTextureEntry, + fakeVideoPlayerOptions, + fakeEventSink); + Format testFormat = + new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(0).build(); + + when(fakeExoPlayer.getVideoFormat()).thenReturn(testFormat); + when(fakeExoPlayer.getDuration()).thenReturn(10L); + + videoPlayer.isInitialized = true; + videoPlayer.sendInitialized(); + + verify(fakeEventSink).success(eventCaptor.capture()); + HashMap event = eventCaptor.getValue(); + + assertEquals(event.get("event"), "initialized"); + assertEquals(event.get("duration"), 10L); + assertEquals(event.get("width"), 100); + assertEquals(event.get("height"), 200); + assertEquals(event.get("rotationCorrection"), null); + } + + @Test + public void sendInitializedSendsExpectedEvent_180RotationDegrees() { + VideoPlayer videoPlayer = + new VideoPlayer( + fakeExoPlayer, + fakeEventChannel, + fakeSurfaceTextureEntry, + fakeVideoPlayerOptions, + fakeEventSink); + Format testFormat = + new Format.Builder().setWidth(100).setHeight(200).setRotationDegrees(180).build(); + + when(fakeExoPlayer.getVideoFormat()).thenReturn(testFormat); + when(fakeExoPlayer.getDuration()).thenReturn(10L); + + videoPlayer.isInitialized = true; + videoPlayer.sendInitialized(); + + verify(fakeEventSink).success(eventCaptor.capture()); + HashMap event = eventCaptor.getValue(); + + assertEquals(event.get("event"), "initialized"); + assertEquals(event.get("duration"), 10L); + assertEquals(event.get("width"), 100); + assertEquals(event.get("height"), 200); + assertEquals(event.get("rotationCorrection"), 180); } } diff --git a/packages/video_player/video_player_android/lib/src/android_video_player.dart b/packages/video_player/video_player_android/lib/src/android_video_player.dart index 5c5fd809c199..cee6d7d38f66 100644 --- a/packages/video_player/video_player_android/lib/src/android_video_player.dart +++ b/packages/video_player/video_player_android/lib/src/android_video_player.dart @@ -130,6 +130,7 @@ class AndroidVideoPlayer extends VideoPlayerPlatform { duration: Duration(milliseconds: map['duration'] as int), size: Size((map['width'] as num?)?.toDouble() ?? 0.0, (map['height'] as num?)?.toDouble() ?? 0.0), + rotationCorrection: map['rotationCorrection'] as int? ?? 0, ); case 'completed': return VideoEvent( diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index 7e23b3210859..367b95ea5a60 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.4 +version: 2.3.5 environment: sdk: ">=2.14.0 <3.0.0" @@ -20,7 +20,7 @@ flutter: dependencies: flutter: sdk: flutter - video_player_platform_interface: ">=4.2.0 <6.0.0" + video_player_platform_interface: ^5.1.1 dev_dependencies: flutter_test: diff --git a/packages/video_player/video_player_android/test/android_video_player_test.dart b/packages/video_player/video_player_android/test/android_video_player_test.dart index b7cf763e16a6..aad31e4d83cc 100644 --- a/packages/video_player/video_player_android/test/android_video_player_test.dart +++ b/packages/video_player/video_player_android/test/android_video_player_test.dart @@ -253,6 +253,20 @@ void main() { }), (ByteData? data) {}); + await _ambiguate(ServicesBinding.instance) + ?.defaultBinaryMessenger + .handlePlatformMessage( + 'flutter.io/videoPlayer/videoEvents123', + const StandardMethodCodec() + .encodeSuccessEnvelope({ + 'event': 'initialized', + 'duration': 98765, + 'width': 1920, + 'height': 1080, + 'rotationCorrection': 180, + }), + (ByteData? data) {}); + await _ambiguate(ServicesBinding.instance) ?.defaultBinaryMessenger .handlePlatformMessage( @@ -312,6 +326,13 @@ void main() { eventType: VideoEventType.initialized, duration: const Duration(milliseconds: 98765), size: const Size(1920, 1080), + rotationCorrection: 0, + ), + VideoEvent( + eventType: VideoEventType.initialized, + duration: const Duration(milliseconds: 98765), + size: const Size(1920, 1080), + rotationCorrection: 180, ), VideoEvent(eventType: VideoEventType.completed), VideoEvent( From 801c1f693d56b173c9083f73717c6eea8b1c8882 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Tue, 31 May 2022 17:33:11 -0700 Subject: [PATCH 374/844] [google_maps_flutter] Objective-C code clean up (#5780) --- .../google_maps_flutter/CHANGELOG.md | 4 + .../ios/Runner.xcodeproj/project.pbxproj | 8 + ...TGoogleMapJSONConversionsConversionTests.m | 290 +++++++++++ .../ios/Classes/FLTGoogleMapJSONConversions.h | 30 ++ .../ios/Classes/FLTGoogleMapJSONConversions.m | 144 ++++++ .../FLTGoogleMapTileOverlayController.h | 26 +- .../FLTGoogleMapTileOverlayController.m | 137 +++--- .../ios/Classes/FLTGoogleMapsPlugin.h | 4 + .../ios/Classes/FLTGoogleMapsPlugin.m | 14 +- .../ios/Classes/GoogleMapCircleController.h | 26 +- .../ios/Classes/GoogleMapCircleController.m | 161 +++--- .../ios/Classes/GoogleMapController.h | 25 +- .../ios/Classes/GoogleMapController.m | 464 +++++++----------- .../ios/Classes/GoogleMapMarkerController.h | 51 +- .../ios/Classes/GoogleMapMarkerController.m | 333 +++++++------ .../ios/Classes/GoogleMapPolygonController.h | 23 +- .../ios/Classes/GoogleMapPolygonController.m | 165 +++---- .../ios/Classes/GoogleMapPolylineController.h | 22 +- .../ios/Classes/GoogleMapPolylineController.m | 150 +++--- .../ios/Classes/JsonConversions.h | 19 - .../ios/Classes/JsonConversions.m | 71 --- .../Classes/google_maps_flutter-umbrella.h | 2 +- .../google_maps_flutter/pubspec.yaml | 2 +- 23 files changed, 1190 insertions(+), 981 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m create mode 100644 packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapJSONConversions.h create mode 100644 packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapJSONConversions.m delete mode 100644 packages/google_maps_flutter/google_maps_flutter/ios/Classes/JsonConversions.h delete mode 100644 packages/google_maps_flutter/google_maps_flutter/ios/Classes/JsonConversions.m diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index b4fb6622edc2..8d78ec38b057 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.7 + +* Objective-C code cleanup. + ## 2.1.6 * Fixes issue in Flutter v3.0.0 where some updates to the map don't take effect on Android. diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj index a8d37106bb83..b37246b98a47 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -10,6 +10,8 @@ 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 */; }; + 6851F3562835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */; }; + 68E4726A2836FF0C00BDDDAC /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 68E472692836FF0C00BDDDAC /* MapKit.framework */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -55,6 +57,8 @@ 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 = ""; }; + 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTGoogleMapJSONConversionsConversionTests.m; sourceTree = ""; }; + 68E472692836FF0C00BDDDAC /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/iOSSupport/System/Library/Frameworks/MapKit.framework; sourceTree = DEVELOPER_DIR; }; 733AFAB37683A9DA7512F09C /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 7755F8F4BABC3D6A0BD4048B /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -95,6 +99,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 68E4726A2836FF0C00BDDDAC /* MapKit.framework in Frameworks */, FC8F35FC8CD533B128950487 /* libPods-RunnerTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -112,6 +117,7 @@ 1E7CF0857EFC88FC263CF3B2 /* Frameworks */ = { isa = PBXGroup; children = ( + 68E472692836FF0C00BDDDAC /* MapKit.framework */, 7755F8F4BABC3D6A0BD4048B /* libPods-Runner.a */, F267F68029D1A4E2E4C572A7 /* libPods-RunnerTests.a */, ); @@ -190,6 +196,7 @@ F7151F11265D7ED70028CB91 /* RunnerTests */ = { isa = PBXGroup; children = ( + 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */, F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */, 982F2A6A27BADE17003C81F4 /* PartiallyMockedMapView.h */, 982F2A6B27BADE17003C81F4 /* PartiallyMockedMapView.m */, @@ -446,6 +453,7 @@ buildActionMask = 2147483647; files = ( F7151F13265D7ED70028CB91 /* GoogleMapsTests.m in Sources */, + 6851F3562835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m in Sources */, 982F2A6C27BADE17003C81F4 /* PartiallyMockedMapView.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m b/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m new file mode 100644 index 000000000000..bf226feb2341 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m @@ -0,0 +1,290 @@ +// 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 google_maps_flutter; +@import google_maps_flutter.Test; +@import XCTest; +@import MapKit; +@import GoogleMaps; + +#import +#import "PartiallyMockedMapView.h" + +@interface FLTGoogleMapJSONConversionsTests : XCTestCase +@end + +@implementation FLTGoogleMapJSONConversionsTests + +- (void)testLocationFromLatLong { + NSArray *latlong = @[ @1, @2 ]; + CLLocationCoordinate2D location = [FLTGoogleMapJSONConversions locationFromLatLong:latlong]; + XCTAssertEqual(location.latitude, 1); + XCTAssertEqual(location.longitude, 2); +} + +- (void)testPointFromArray { + NSArray *array = @[ @1, @2 ]; + CGPoint point = [FLTGoogleMapJSONConversions pointFromArray:array]; + XCTAssertEqual(point.x, 1); + XCTAssertEqual(point.y, 2); +} + +- (void)testArrayFromLocation { + CLLocationCoordinate2D location = CLLocationCoordinate2DMake(1, 2); + NSArray *array = [FLTGoogleMapJSONConversions arrayFromLocation:location]; + XCTAssertEqual([array[0] integerValue], 1); + XCTAssertEqual([array[1] integerValue], 2); +} + +- (void)testColorFromRGBA { + NSNumber *rgba = @(0x01020304); + UIColor *color = [FLTGoogleMapJSONConversions colorFromRGBA:rgba]; + CGFloat red, green, blue, alpha; + BOOL success = [color getRed:&red green:&green blue:&blue alpha:&alpha]; + XCTAssertTrue(success); + const CGFloat accuracy = 0.0001; + XCTAssertEqualWithAccuracy(red, 2 / 255.0, accuracy); + XCTAssertEqualWithAccuracy(green, 3 / 255.0, accuracy); + XCTAssertEqualWithAccuracy(blue, 4 / 255.0, accuracy); + XCTAssertEqualWithAccuracy(alpha, 1 / 255.0, accuracy); +} + +- (void)testPointsFromLatLongs { + NSArray *latlongs = @[ @[ @1, @2 ], @[ @(3), @(4) ] ]; + NSArray *locations = [FLTGoogleMapJSONConversions pointsFromLatLongs:latlongs]; + XCTAssertEqual(locations.count, 2); + XCTAssertEqual(locations[0].coordinate.latitude, 1); + XCTAssertEqual(locations[0].coordinate.longitude, 2); + XCTAssertEqual(locations[1].coordinate.latitude, 3); + XCTAssertEqual(locations[1].coordinate.longitude, 4); +} + +- (void)testHolesFromPointsArray { + NSArray *pointsArray = + @[ @[ @[ @1, @2 ], @[ @(3), @(4) ] ], @[ @[ @(5), @(6) ], @[ @(7), @(8) ] ] ]; + NSArray *> *holes = + [FLTGoogleMapJSONConversions holesFromPointsArray:pointsArray]; + XCTAssertEqual(holes.count, 2); + XCTAssertEqual(holes[0][0].coordinate.latitude, 1); + XCTAssertEqual(holes[0][0].coordinate.longitude, 2); + XCTAssertEqual(holes[0][1].coordinate.latitude, 3); + XCTAssertEqual(holes[0][1].coordinate.longitude, 4); + XCTAssertEqual(holes[1][0].coordinate.latitude, 5); + XCTAssertEqual(holes[1][0].coordinate.longitude, 6); + XCTAssertEqual(holes[1][1].coordinate.latitude, 7); + XCTAssertEqual(holes[1][1].coordinate.longitude, 8); +} + +- (void)testDictionaryFromPosition { + id mockPosition = OCMClassMock([GMSCameraPosition class]); + NSValue *locationValue = [NSValue valueWithMKCoordinate:CLLocationCoordinate2DMake(1, 2)]; + [(GMSCameraPosition *)[[mockPosition stub] andReturnValue:locationValue] target]; + [[[mockPosition stub] andReturnValue:@(2.0)] zoom]; + [[[mockPosition stub] andReturnValue:@(3.0)] bearing]; + [[[mockPosition stub] andReturnValue:@(75.0)] viewingAngle]; + NSDictionary *dictionary = [FLTGoogleMapJSONConversions dictionaryFromPosition:mockPosition]; + NSArray *targetArray = @[ @1, @2 ]; + XCTAssertEqualObjects(dictionary[@"target"], targetArray); + XCTAssertEqualObjects(dictionary[@"zoom"], @2.0); + XCTAssertEqualObjects(dictionary[@"bearing"], @3.0); + XCTAssertEqualObjects(dictionary[@"tilt"], @75.0); +} + +- (void)testDictionaryFromPoint { + CGPoint point = CGPointMake(10, 20); + NSDictionary *dictionary = [FLTGoogleMapJSONConversions dictionaryFromPoint:point]; + const CGFloat accuracy = 0.0001; + XCTAssertEqualWithAccuracy([dictionary[@"x"] floatValue], point.x, accuracy); + XCTAssertEqualWithAccuracy([dictionary[@"y"] floatValue], point.y, accuracy); +} + +- (void)testDictionaryFromCoordinateBounds { + XCTAssertNil([FLTGoogleMapJSONConversions dictionaryFromCoordinateBounds:nil]); + + GMSCoordinateBounds *bounds = + [[GMSCoordinateBounds alloc] initWithCoordinate:CLLocationCoordinate2DMake(10, 20) + coordinate:CLLocationCoordinate2DMake(30, 40)]; + NSDictionary *dictionary = [FLTGoogleMapJSONConversions dictionaryFromCoordinateBounds:bounds]; + NSArray *southwest = @[ @10, @20 ]; + NSArray *northeast = @[ @30, @40 ]; + XCTAssertEqualObjects(dictionary[@"southwest"], southwest); + XCTAssertEqualObjects(dictionary[@"northeast"], northeast); +} + +- (void)testCameraPostionFromDictionary { + XCTAssertNil([FLTGoogleMapJSONConversions cameraPostionFromDictionary:nil]); + + NSDictionary *channelValue = + @{@"target" : @[ @1, @2 ], @"zoom" : @3, @"bearing" : @4, @"tilt" : @5}; + + GMSCameraPosition *cameraPosition = + [FLTGoogleMapJSONConversions cameraPostionFromDictionary:channelValue]; + + const CGFloat accuracy = 0.001; + XCTAssertEqualWithAccuracy(cameraPosition.target.latitude, 1, accuracy); + XCTAssertEqualWithAccuracy(cameraPosition.target.longitude, 2, accuracy); + XCTAssertEqualWithAccuracy(cameraPosition.zoom, 3, accuracy); + XCTAssertEqualWithAccuracy(cameraPosition.bearing, 4, accuracy); + XCTAssertEqualWithAccuracy(cameraPosition.viewingAngle, 5, accuracy); +} + +- (void)testPointFromDictionary { + XCTAssertNil([FLTGoogleMapJSONConversions cameraPostionFromDictionary:nil]); + + NSDictionary *dictionary = @{ + @"x" : @1, + @"y" : @2, + }; + + CGPoint point = [FLTGoogleMapJSONConversions pointFromDictionary:dictionary]; + + const CGFloat accuracy = 0.001; + XCTAssertEqualWithAccuracy(point.x, 1, accuracy); + XCTAssertEqualWithAccuracy(point.y, 2, accuracy); +} + +- (void)testCoordinateBoundsFromLatLongs { + NSArray *latlong1 = @[ @1, @2 ]; + NSArray *latlong2 = @[ @(3), @(4) ]; + + GMSCoordinateBounds *bounds = + [FLTGoogleMapJSONConversions coordinateBoundsFromLatLongs:@[ latlong1, latlong2 ]]; + + const CGFloat accuracy = 0.001; + XCTAssertEqualWithAccuracy(bounds.southWest.latitude, 1, accuracy); + XCTAssertEqualWithAccuracy(bounds.southWest.longitude, 2, accuracy); + XCTAssertEqualWithAccuracy(bounds.northEast.latitude, 3, accuracy); + XCTAssertEqualWithAccuracy(bounds.northEast.longitude, 4, accuracy); +} + +- (void)testMapViewTypeFromTypeValue { + XCTAssertEqual(kGMSTypeNormal, [FLTGoogleMapJSONConversions mapViewTypeFromTypeValue:@1]); + XCTAssertEqual(kGMSTypeSatellite, [FLTGoogleMapJSONConversions mapViewTypeFromTypeValue:@2]); + XCTAssertEqual(kGMSTypeTerrain, [FLTGoogleMapJSONConversions mapViewTypeFromTypeValue:@3]); + XCTAssertEqual(kGMSTypeHybrid, [FLTGoogleMapJSONConversions mapViewTypeFromTypeValue:@4]); + XCTAssertEqual(kGMSTypeNone, [FLTGoogleMapJSONConversions mapViewTypeFromTypeValue:@5]); +} + +- (void)testCameraUpdateFromChannelValueNewCameraPosition { + NSArray *channelValue = @[ + @"newCameraPosition", @{@"target" : @[ @1, @2 ], @"zoom" : @3, @"bearing" : @4, @"tilt" : @5} + ]; + id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); + [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValue]; + [[classMockCameraUpdate expect] + setCamera:[FLTGoogleMapJSONConversions cameraPostionFromDictionary:channelValue[1]]]; + [classMockCameraUpdate stopMocking]; +} + +// TODO(cyanglaz): Fix the test for CameraUpdateFromChannelValue with the "NewLatlng" key. +// 2 approaches have been tried and neither worked for the tests. +// +// 1. Use OCMock to vefiry that [GMSCameraUpdate setTarget:] is triggered with the correct value. +// This class method conflicts with certain category method in OCMock, causing OCMock not able to +// disambigious them. +// +// 2. Directly verify the GMSCameraUpdate object returned by the method. +// The GMSCameraUpdate object returned from the method doesn't have any accessors to the "target" +// property. It can be used to update the "camera" property in GMSMapView. However, [GMSMapView +// moveCamera:] doesn't update the camera immediately. Thus the GMSCameraUpdate object cannot be +// verified. +// +// The code in below test uses the 2nd approach. +- (void)skip_testCameraUpdateFromChannelValueNewLatLong { + NSArray *channelValue = @[ @"newLatLng", @[ @1, @2 ] ]; + + GMSCameraUpdate *update = [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValue]; + + GMSMapView *mapView = [[GMSMapView alloc] + initWithFrame:CGRectZero + camera:[GMSCameraPosition cameraWithTarget:CLLocationCoordinate2DMake(5, 6) zoom:1]]; + [mapView moveCamera:update]; + const CGFloat accuracy = 0.001; + XCTAssertEqualWithAccuracy(mapView.camera.target.latitude, 1, + accuracy); // mapView.camera.target.latitude is still 5. + XCTAssertEqualWithAccuracy(mapView.camera.target.longitude, 2, + accuracy); // mapView.camera.target.longitude is still 6. +} + +- (void)testCameraUpdateFromChannelValueNewLatLngBounds { + NSArray *latlong1 = @[ @1, @2 ]; + NSArray *latlong2 = @[ @(3), @(4) ]; + GMSCoordinateBounds *bounds = + [FLTGoogleMapJSONConversions coordinateBoundsFromLatLongs:@[ latlong1, latlong2 ]]; + + NSArray *channelValue = @[ @"newLatLngBounds", @[ latlong1, latlong2 ], @20 ]; + id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); + [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValue]; + + [[classMockCameraUpdate expect] fitBounds:bounds withPadding:20]; + [classMockCameraUpdate stopMocking]; +} + +- (void)testCameraUpdateFromChannelValueNewLatLngZoom { + NSArray *channelValue = @[ @"newLatLngZoom", @[ @1, @2 ], @3 ]; + + id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); + [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValue]; + + [[classMockCameraUpdate expect] setTarget:CLLocationCoordinate2DMake(1, 2) zoom:3]; + [classMockCameraUpdate stopMocking]; +} + +- (void)testCameraUpdateFromChannelValueScrollBy { + NSArray *channelValue = @[ @"scrollBy", @1, @2 ]; + + id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); + [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValue]; + + [[classMockCameraUpdate expect] scrollByX:1 Y:2]; + [classMockCameraUpdate stopMocking]; +} + +- (void)testCameraUpdateFromChannelValueZoomBy { + NSArray *channelValueNoPoint = @[ @"zoomBy", @1 ]; + + id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); + [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValueNoPoint]; + + [[classMockCameraUpdate expect] zoomBy:1]; + + NSArray *channelValueWithPoint = @[ @"zoomBy", @1, @[ @2, @3 ] ]; + + [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValueWithPoint]; + + [[classMockCameraUpdate expect] zoomBy:1 atPoint:CGPointMake(2, 3)]; + [classMockCameraUpdate stopMocking]; +} + +- (void)testCameraUpdateFromChannelValueZoomIn { + NSArray *channelValueNoPoint = @[ @"zoomIn" ]; + + id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); + [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValueNoPoint]; + + [[classMockCameraUpdate expect] zoomIn]; + [classMockCameraUpdate stopMocking]; +} + +- (void)testCameraUpdateFromChannelValueZoomOut { + NSArray *channelValueNoPoint = @[ @"zoomOut" ]; + + id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); + [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValueNoPoint]; + + [[classMockCameraUpdate expect] zoomOut]; + [classMockCameraUpdate stopMocking]; +} + +- (void)testCameraUpdateFromChannelValueZoomTo { + NSArray *channelValueNoPoint = @[ @"zoomTo", @1 ]; + + id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); + [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValueNoPoint]; + + [[classMockCameraUpdate expect] zoomTo:1]; + [classMockCameraUpdate stopMocking]; +} + +@end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapJSONConversions.h b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapJSONConversions.h new file mode 100644 index 000000000000..cfccb7b0b5f9 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapJSONConversions.h @@ -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 +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FLTGoogleMapJSONConversions : NSObject + ++ (CLLocationCoordinate2D)locationFromLatLong:(NSArray *)latlong; ++ (CGPoint)pointFromArray:(NSArray *)array; ++ (NSArray *)arrayFromLocation:(CLLocationCoordinate2D)location; ++ (UIColor *)colorFromRGBA:(NSNumber *)data; ++ (NSArray *)pointsFromLatLongs:(NSArray *)data; ++ (NSArray *> *)holesFromPointsArray:(NSArray *)data; ++ (nullable NSDictionary *)dictionaryFromPosition: + (nullable GMSCameraPosition *)position; ++ (NSDictionary *)dictionaryFromPoint:(CGPoint)point; ++ (nullable NSDictionary *)dictionaryFromCoordinateBounds:(nullable GMSCoordinateBounds *)bounds; ++ (nullable GMSCameraPosition *)cameraPostionFromDictionary:(nullable NSDictionary *)channelValue; ++ (CGPoint)pointFromDictionary:(NSDictionary *)dictionary; ++ (GMSCoordinateBounds *)coordinateBoundsFromLatLongs:(NSArray *)latlongs; ++ (GMSMapViewType)mapViewTypeFromTypeValue:(NSNumber *)value; ++ (nullable GMSCameraUpdate *)cameraUpdateFromChannelValue:(NSArray *)channelValue; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapJSONConversions.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapJSONConversions.m new file mode 100644 index 000000000000..d554c501b1e2 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapJSONConversions.m @@ -0,0 +1,144 @@ +// 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 "FLTGoogleMapJSONConversions.h" + +@implementation FLTGoogleMapJSONConversions + ++ (CLLocationCoordinate2D)locationFromLatLong:(NSArray *)latlong { + return CLLocationCoordinate2DMake([latlong[0] doubleValue], [latlong[1] doubleValue]); +} + ++ (CGPoint)pointFromArray:(NSArray *)array { + return CGPointMake([array[0] doubleValue], [array[1] doubleValue]); +} + ++ (NSArray *)arrayFromLocation:(CLLocationCoordinate2D)location { + return @[ @(location.latitude), @(location.longitude) ]; +} + ++ (UIColor *)colorFromRGBA:(NSNumber *)numberColor { + unsigned long value = [numberColor unsignedLongValue]; + return [UIColor colorWithRed:((float)((value & 0xFF0000) >> 16)) / 255.0 + green:((float)((value & 0xFF00) >> 8)) / 255.0 + blue:((float)(value & 0xFF)) / 255.0 + alpha:((float)((value & 0xFF000000) >> 24)) / 255.0]; +} + ++ (NSArray *)pointsFromLatLongs:(NSArray *)data { + NSMutableArray *points = [[NSMutableArray alloc] init]; + for (unsigned i = 0; i < [data count]; i++) { + NSNumber *latitude = data[i][0]; + NSNumber *longitude = data[i][1]; + CLLocation *point = [[CLLocation alloc] initWithLatitude:[latitude doubleValue] + longitude:[longitude doubleValue]]; + [points addObject:point]; + } + + return points; +} + ++ (NSArray *> *)holesFromPointsArray:(NSArray *)data { + NSMutableArray *> *holes = [[[NSMutableArray alloc] init] init]; + for (unsigned i = 0; i < [data count]; i++) { + NSArray *points = [FLTGoogleMapJSONConversions pointsFromLatLongs:data[i]]; + [holes addObject:points]; + } + + return holes; +} + ++ (nullable NSDictionary *)dictionaryFromPosition:(GMSCameraPosition *)position { + if (!position) { + return nil; + } + return @{ + @"target" : [FLTGoogleMapJSONConversions arrayFromLocation:[position target]], + @"zoom" : @([position zoom]), + @"bearing" : @([position bearing]), + @"tilt" : @([position viewingAngle]), + }; +} + ++ (NSDictionary *)dictionaryFromPoint:(CGPoint)point { + return @{ + @"x" : @(lroundf(point.x)), + @"y" : @(lroundf(point.y)), + }; +} + ++ (nullable NSDictionary *)dictionaryFromCoordinateBounds:(GMSCoordinateBounds *)bounds { + if (!bounds) { + return nil; + } + return @{ + @"southwest" : [FLTGoogleMapJSONConversions arrayFromLocation:[bounds southWest]], + @"northeast" : [FLTGoogleMapJSONConversions arrayFromLocation:[bounds northEast]], + }; +} + ++ (nullable GMSCameraPosition *)cameraPostionFromDictionary:(nullable NSDictionary *)data { + if (!data) { + return nil; + } + return [GMSCameraPosition + cameraWithTarget:[FLTGoogleMapJSONConversions locationFromLatLong:data[@"target"]] + zoom:[data[@"zoom"] floatValue] + bearing:[data[@"bearing"] doubleValue] + viewingAngle:[data[@"tilt"] doubleValue]]; +} + ++ (CGPoint)pointFromDictionary:(NSDictionary *)dictionary { + double x = [dictionary[@"x"] doubleValue]; + double y = [dictionary[@"y"] doubleValue]; + return CGPointMake(x, y); +} + ++ (GMSCoordinateBounds *)coordinateBoundsFromLatLongs:(NSArray *)latlongs { + return [[GMSCoordinateBounds alloc] + initWithCoordinate:[FLTGoogleMapJSONConversions locationFromLatLong:latlongs[0]] + coordinate:[FLTGoogleMapJSONConversions locationFromLatLong:latlongs[1]]]; +} + ++ (GMSMapViewType)mapViewTypeFromTypeValue:(NSNumber *)typeValue { + int value = [typeValue intValue]; + return (GMSMapViewType)(value == 0 ? 5 : value); +} + ++ (nullable GMSCameraUpdate *)cameraUpdateFromChannelValue:(NSArray *)channelValue { + NSString *update = channelValue[0]; + if ([update isEqualToString:@"newCameraPosition"]) { + return [GMSCameraUpdate + setCamera:[FLTGoogleMapJSONConversions cameraPostionFromDictionary:channelValue[1]]]; + } else if ([update isEqualToString:@"newLatLng"]) { + return [GMSCameraUpdate + setTarget:[FLTGoogleMapJSONConversions locationFromLatLong:channelValue[1]]]; + } else if ([update isEqualToString:@"newLatLngBounds"]) { + return [GMSCameraUpdate + fitBounds:[FLTGoogleMapJSONConversions coordinateBoundsFromLatLongs:channelValue[1]] + withPadding:[channelValue[2] doubleValue]]; + } else if ([update isEqualToString:@"newLatLngZoom"]) { + return + [GMSCameraUpdate setTarget:[FLTGoogleMapJSONConversions locationFromLatLong:channelValue[1]] + zoom:[channelValue[2] floatValue]]; + } else if ([update isEqualToString:@"scrollBy"]) { + return [GMSCameraUpdate scrollByX:[channelValue[1] doubleValue] + Y:[channelValue[2] doubleValue]]; + } else if ([update isEqualToString:@"zoomBy"]) { + if (channelValue.count == 2) { + return [GMSCameraUpdate zoomBy:[channelValue[1] floatValue]]; + } else { + return [GMSCameraUpdate zoomBy:[channelValue[1] floatValue] + atPoint:[FLTGoogleMapJSONConversions pointFromArray:channelValue[2]]]; + } + } else if ([update isEqualToString:@"zoomIn"]) { + return [GMSCameraUpdate zoomIn]; + } else if ([update isEqualToString:@"zoomOut"]) { + return [GMSCameraUpdate zoomOut]; + } else if ([update isEqualToString:@"zoomTo"]) { + return [GMSCameraUpdate zoomTo:[channelValue[1] floatValue]]; + } + return nil; +} +@end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.h b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.h index 356a13faba62..5dcc66594f18 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.h +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.h @@ -7,25 +7,19 @@ NS_ASSUME_NONNULL_BEGIN -// Defines map UI options writable from Flutter. -@protocol FLTGoogleMapTileOverlayOptionsSink -- (void)setFadeIn:(BOOL)fadeIn; -- (void)setTransparency:(float)transparency; -- (void)setZIndex:(int)zIndex; -- (void)setVisible:(BOOL)visible; -- (void)setTileSize:(NSInteger)tileSize; -@end - -@interface FLTGoogleMapTileOverlayController : NSObject -- (instancetype)initWithTileLayer:(GMSTileLayer *)tileLayer mapView:(GMSMapView *)mapView; +@interface FLTGoogleMapTileOverlayController : NSObject +- (instancetype)initWithTileLayer:(GMSTileLayer *)tileLayer + mapView:(GMSMapView *)mapView + options:(NSDictionary *)optionsData; - (void)removeTileOverlay; - (void)clearTileCache; - (NSDictionary *)getTileOverlayInfo; @end @interface FLTTileProviderController : GMSTileLayer -@property(copy, nonatomic, readonly) NSString *tileOverlayId; -- (instancetype)init:(FlutterMethodChannel *)methodChannel tileOverlayId:(NSString *)tileOverlayId; +@property(copy, nonatomic, readonly) NSString *tileOverlayIdentifier; +- (instancetype)init:(FlutterMethodChannel *)methodChannel + withTileOverlayIdentifier:(NSString *)identifier; @end @interface FLTTileOverlaysController : NSObject @@ -34,9 +28,9 @@ NS_ASSUME_NONNULL_BEGIN registrar:(NSObject *)registrar; - (void)addTileOverlays:(NSArray *)tileOverlaysToAdd; - (void)changeTileOverlays:(NSArray *)tileOverlaysToChange; -- (void)removeTileOverlayIds:(NSArray *)tileOverlayIdsToRemove; -- (void)clearTileCache:(NSString *)tileOverlayId; -- (nullable NSDictionary *)getTileOverlayInfo:(NSString *)tileverlayId; +- (void)removeTileOverlayWithIdentifiers:(NSArray *)identifiers; +- (void)clearTileCacheWithIdentifier:(NSString *)identifier; +- (nullable NSDictionary *)tileOverlayInfoWithIdentifier:(NSString *)identifier; @end NS_ASSUME_NONNULL_END diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.m index 6baa753ef999..5863697d7b9b 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.m +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.m @@ -3,36 +3,7 @@ // found in the LICENSE file. #import "FLTGoogleMapTileOverlayController.h" -#import "JsonConversions.h" - -static void InterpretTileOverlayOptions(NSDictionary *data, - id sink, - NSObject *registrar) { - NSNumber *visible = data[@"visible"]; - if (visible != nil) { - [sink setVisible:visible.boolValue]; - } - - NSNumber *transparency = data[@"transparency"]; - if (transparency != nil) { - [sink setTransparency:transparency.floatValue]; - } - - NSNumber *zIndex = data[@"zIndex"]; - if (zIndex != nil) { - [sink setZIndex:zIndex.intValue]; - } - - NSNumber *fadeIn = data[@"fadeIn"]; - if (fadeIn != nil) { - [sink setFadeIn:fadeIn.boolValue]; - } - - NSNumber *tileSize = data[@"tileSize"]; - if (tileSize != nil) { - [sink setTileSize:tileSize.integerValue]; - } -} +#import "FLTGoogleMapJSONConversions.h" @interface FLTGoogleMapTileOverlayController () @@ -43,11 +14,14 @@ @interface FLTGoogleMapTileOverlayController () @implementation FLTGoogleMapTileOverlayController -- (instancetype)initWithTileLayer:(GMSTileLayer *)tileLayer mapView:(GMSMapView *)mapView { +- (instancetype)initWithTileLayer:(GMSTileLayer *)tileLayer + mapView:(GMSMapView *)mapView + options:(NSDictionary *)optionsData { self = [super init]; if (self) { - self.layer = tileLayer; - self.mapView = mapView; + _layer = tileLayer; + _mapView = mapView; + [self interpretTileOverlayOptions:optionsData]; } return self; } @@ -71,8 +45,6 @@ - (NSDictionary *)getTileOverlayInfo { return info; } -#pragma mark - FLTGoogleMapTileOverlayOptionsSink methods - - (void)setFadeIn:(BOOL)fadeIn { self.layer.fadeIn = fadeIn; } @@ -93,22 +65,53 @@ - (void)setZIndex:(int)zIndex { - (void)setTileSize:(NSInteger)tileSize { self.layer.tileSize = tileSize; } + +- (void)interpretTileOverlayOptions:(NSDictionary *)data { + if (!data) { + return; + } + NSNumber *visible = data[@"visible"]; + if (visible != nil && visible != (id)[NSNull null]) { + [self setVisible:visible.boolValue]; + } + + NSNumber *transparency = data[@"transparency"]; + if (transparency != nil && transparency != (id)[NSNull null]) { + [self setTransparency:transparency.floatValue]; + } + + NSNumber *zIndex = data[@"zIndex"]; + if (zIndex != nil && zIndex != (id)[NSNull null]) { + [self setZIndex:zIndex.intValue]; + } + + NSNumber *fadeIn = data[@"fadeIn"]; + if (fadeIn != nil && fadeIn != (id)[NSNull null]) { + [self setFadeIn:fadeIn.boolValue]; + } + + NSNumber *tileSize = data[@"tileSize"]; + if (tileSize != nil && tileSize != (id)[NSNull null]) { + [self setTileSize:tileSize.integerValue]; + } +} + @end @interface FLTTileProviderController () -@property(weak, nonatomic) FlutterMethodChannel *methodChannel; -@property(copy, nonatomic, readwrite) NSString *tileOverlayId; +@property(strong, nonatomic) FlutterMethodChannel *methodChannel; @end @implementation FLTTileProviderController -- (instancetype)init:(FlutterMethodChannel *)methodChannel tileOverlayId:(NSString *)tileOverlayId { +- (instancetype)init:(FlutterMethodChannel *)methodChannel + withTileOverlayIdentifier:(NSString *)identifier { self = [super init]; if (self) { - self.methodChannel = methodChannel; - self.tileOverlayId = tileOverlayId; + _methodChannel = methodChannel; + _tileOverlayIdentifier = identifier; } return self; } @@ -122,7 +125,7 @@ - (void)requestTileForX:(NSUInteger)x [self.methodChannel invokeMethod:@"tileOverlay#getTile" arguments:@{ - @"tileOverlayId" : self.tileOverlayId, + @"tileOverlayId" : self.tileOverlayIdentifier, @"x" : @(x), @"y" : @(y), @"zoom" : @(zoom) @@ -156,9 +159,8 @@ - (void)requestTileForX:(NSUInteger)x @interface FLTTileOverlaysController () -@property(strong, nonatomic) NSMutableDictionary *tileOverlayIdToController; -@property(weak, nonatomic) FlutterMethodChannel *methodChannel; -@property(weak, nonatomic) NSObject *registrar; +@property(strong, nonatomic) NSMutableDictionary *tileOverlayIdentifierToController; +@property(strong, nonatomic) FlutterMethodChannel *methodChannel; @property(weak, nonatomic) GMSMapView *mapView; @end @@ -170,64 +172,67 @@ - (instancetype)init:(FlutterMethodChannel *)methodChannel registrar:(NSObject *)registrar { self = [super init]; if (self) { - self.methodChannel = methodChannel; - self.mapView = mapView; - self.tileOverlayIdToController = [[NSMutableDictionary alloc] init]; - self.registrar = registrar; + _methodChannel = methodChannel; + _mapView = mapView; + _tileOverlayIdentifierToController = [[NSMutableDictionary alloc] init]; } return self; } - (void)addTileOverlays:(NSArray *)tileOverlaysToAdd { for (NSDictionary *tileOverlay in tileOverlaysToAdd) { - NSString *tileOverlayId = [FLTTileOverlaysController getTileOverlayId:tileOverlay]; + NSString *identifier = [FLTTileOverlaysController identifierForTileOverlay:tileOverlay]; FLTTileProviderController *tileProvider = - [[FLTTileProviderController alloc] init:self.methodChannel tileOverlayId:tileOverlayId]; + [[FLTTileProviderController alloc] init:self.methodChannel + withTileOverlayIdentifier:identifier]; FLTGoogleMapTileOverlayController *controller = [[FLTGoogleMapTileOverlayController alloc] initWithTileLayer:tileProvider - mapView:self.mapView]; - InterpretTileOverlayOptions(tileOverlay, controller, self.registrar); - self.tileOverlayIdToController[tileOverlayId] = controller; + mapView:self.mapView + options:tileOverlay]; + self.tileOverlayIdentifierToController[identifier] = controller; } } - (void)changeTileOverlays:(NSArray *)tileOverlaysToChange { for (NSDictionary *tileOverlay in tileOverlaysToChange) { - NSString *tileOverlayId = [FLTTileOverlaysController getTileOverlayId:tileOverlay]; - FLTGoogleMapTileOverlayController *controller = self.tileOverlayIdToController[tileOverlayId]; + NSString *identifier = [FLTTileOverlaysController identifierForTileOverlay:tileOverlay]; + FLTGoogleMapTileOverlayController *controller = + self.tileOverlayIdentifierToController[identifier]; if (!controller) { continue; } - InterpretTileOverlayOptions(tileOverlay, controller, self.registrar); + [controller interpretTileOverlayOptions:tileOverlay]; } } -- (void)removeTileOverlayIds:(NSArray *)tileOverlayIdsToRemove { - for (NSString *tileOverlayId in tileOverlayIdsToRemove) { - FLTGoogleMapTileOverlayController *controller = self.tileOverlayIdToController[tileOverlayId]; +- (void)removeTileOverlayWithIdentifiers:(NSArray *)identifiers { + for (NSString *identifier in identifiers) { + FLTGoogleMapTileOverlayController *controller = + self.tileOverlayIdentifierToController[identifier]; if (!controller) { continue; } [controller removeTileOverlay]; - [self.tileOverlayIdToController removeObjectForKey:tileOverlayId]; + [self.tileOverlayIdentifierToController removeObjectForKey:identifier]; } } -- (void)clearTileCache:(NSString *)tileOverlayId { - FLTGoogleMapTileOverlayController *controller = self.tileOverlayIdToController[tileOverlayId]; +- (void)clearTileCacheWithIdentifier:(NSString *)identifier { + FLTGoogleMapTileOverlayController *controller = + self.tileOverlayIdentifierToController[identifier]; if (!controller) { return; } [controller clearTileCache]; } -- (nullable NSDictionary *)getTileOverlayInfo:(NSString *)tileverlayId { - if (self.tileOverlayIdToController[tileverlayId] == nil) { +- (nullable NSDictionary *)tileOverlayInfoWithIdentifier:(NSString *)identifier { + if (self.tileOverlayIdentifierToController[identifier] == nil) { return nil; } - return [self.tileOverlayIdToController[tileverlayId] getTileOverlayInfo]; + return [self.tileOverlayIdentifierToController[identifier] getTileOverlayInfo]; } -+ (NSString *)getTileOverlayId:(NSDictionary *)tileOverlay { ++ (NSString *)identifierForTileOverlay:(NSDictionary *)tileOverlay { return tileOverlay[@"tileOverlayId"]; } diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.h b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.h index 953c0557ff20..26f69eaf3882 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.h +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.h @@ -10,5 +10,9 @@ #import "GoogleMapPolygonController.h" #import "GoogleMapPolylineController.h" +NS_ASSUME_NONNULL_BEGIN + @interface FLTGoogleMapsPlugin : NSObject @end + +NS_ASSUME_NONNULL_END diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.m index e78a505ecfb0..d62f19ced4f3 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.m +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.m @@ -6,11 +6,7 @@ #pragma mark - GoogleMaps plugin implementation -@implementation FLTGoogleMapsPlugin { - NSObject *_registrar; - FlutterMethodChannel *_channel; - NSMutableDictionary *_mapControllers; -} +@implementation FLTGoogleMapsPlugin + (void)registerWithRegistrar:(NSObject *)registrar { FLTGoogleMapFactory *googleMapFactory = [[FLTGoogleMapFactory alloc] initWithRegistrar:registrar]; @@ -20,12 +16,4 @@ + (void)registerWithRegistrar:(NSObject *)registrar { FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded]; } -- (FLTGoogleMapController *)mapFromCall:(FlutterMethodCall *)call error:(FlutterError **)error { - id mapId = call.arguments[@"map"]; - FLTGoogleMapController *controller = _mapControllers[mapId]; - if (!controller && error) { - *error = [FlutterError errorWithCode:@"unknown_map" message:nil details:mapId]; - } - return controller; -} @end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.h b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.h index 26b7ce573bdf..6b67760fdaff 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.h +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.h @@ -5,25 +5,13 @@ #import #import -// Defines circle UI options writable from Flutter. -@protocol FLTGoogleMapCircleOptionsSink -- (void)setConsumeTapEvents:(BOOL)consume; -- (void)setVisible:(BOOL)visible; -- (void)setStrokeColor:(UIColor *)color; -- (void)setStrokeWidth:(CGFloat)width; -- (void)setFillColor:(UIColor *)color; -- (void)setCenter:(CLLocationCoordinate2D)center; -- (void)setRadius:(CLLocationDistance)radius; -- (void)setZIndex:(int)zIndex; -@end - // Defines circle controllable by Flutter. -@interface FLTGoogleMapCircleController : NSObject -@property(atomic, readonly) NSString *circleId; +@interface FLTGoogleMapCircleController : NSObject - (instancetype)initCircleWithPosition:(CLLocationCoordinate2D)position radius:(CLLocationDistance)radius - circleId:(NSString *)circleId - mapView:(GMSMapView *)mapView; + circleId:(NSString *)circleIdentifier + mapView:(GMSMapView *)mapView + options:(NSDictionary *)options; - (void)removeCircle; @end @@ -33,7 +21,7 @@ registrar:(NSObject *)registrar; - (void)addCircles:(NSArray *)circlesToAdd; - (void)changeCircles:(NSArray *)circlesToChange; -- (void)removeCircleIds:(NSArray *)circleIdsToRemove; -- (void)onCircleTap:(NSString *)circleId; -- (bool)hasCircleWithId:(NSString *)circleId; +- (void)removeCircleWithIdentifiers:(NSArray *)identifiers; +- (void)didTapCircleWithIdentifier:(NSString *)identifier; +- (bool)hasCircleWithIdentifier:(NSString *)identifier; @end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.m index d97de587fb17..53bf69075c95 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.m +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.m @@ -3,122 +3,116 @@ // found in the LICENSE file. #import "GoogleMapCircleController.h" -#import "JsonConversions.h" +#import "FLTGoogleMapJSONConversions.h" + +@interface FLTGoogleMapCircleController () + +@property(nonatomic, strong) GMSCircle *circle; +@property(nonatomic, weak) GMSMapView *mapView; + +@end + +@implementation FLTGoogleMapCircleController -@implementation FLTGoogleMapCircleController { - GMSCircle *_circle; - GMSMapView *_mapView; -} - (instancetype)initCircleWithPosition:(CLLocationCoordinate2D)position radius:(CLLocationDistance)radius - circleId:(NSString *)circleId - mapView:(GMSMapView *)mapView { + circleId:(NSString *)circleIdentifier + mapView:(GMSMapView *)mapView + options:(NSDictionary *)options { self = [super init]; if (self) { _circle = [GMSCircle circleWithPosition:position radius:radius]; _mapView = mapView; - _circleId = circleId; - _circle.userData = @[ circleId ]; + _circle.userData = @[ circleIdentifier ]; + [self interpretCircleOptions:options]; } return self; } - (void)removeCircle { - _circle.map = nil; + self.circle.map = nil; } -#pragma mark - FLTGoogleMapCircleOptionsSink methods - - (void)setConsumeTapEvents:(BOOL)consumes { - _circle.tappable = consumes; + self.circle.tappable = consumes; } - (void)setVisible:(BOOL)visible { - _circle.map = visible ? _mapView : nil; + self.circle.map = visible ? self.mapView : nil; } - (void)setZIndex:(int)zIndex { - _circle.zIndex = zIndex; + self.circle.zIndex = zIndex; } - (void)setCenter:(CLLocationCoordinate2D)center { - _circle.position = center; + self.circle.position = center; } - (void)setRadius:(CLLocationDistance)radius { - _circle.radius = radius; + self.circle.radius = radius; } - (void)setStrokeColor:(UIColor *)color { - _circle.strokeColor = color; + self.circle.strokeColor = color; } - (void)setStrokeWidth:(CGFloat)width { - _circle.strokeWidth = width; + self.circle.strokeWidth = width; } - (void)setFillColor:(UIColor *)color { - _circle.fillColor = color; + self.circle.fillColor = color; } -@end - -static int ToInt(NSNumber *data) { return [FLTGoogleMapJsonConversions toInt:data]; } -static BOOL ToBool(NSNumber *data) { return [FLTGoogleMapJsonConversions toBool:data]; } - -static CLLocationCoordinate2D ToLocation(NSArray *data) { - return [FLTGoogleMapJsonConversions toLocation:data]; -} - -static CLLocationDistance ToDistance(NSNumber *data) { - return [FLTGoogleMapJsonConversions toFloat:data]; -} - -static UIColor *ToColor(NSNumber *data) { return [FLTGoogleMapJsonConversions toColor:data]; } - -static void InterpretCircleOptions(NSDictionary *data, id sink, - NSObject *registrar) { +- (void)interpretCircleOptions:(NSDictionary *)data { NSNumber *consumeTapEvents = data[@"consumeTapEvents"]; - if (consumeTapEvents != nil) { - [sink setConsumeTapEvents:ToBool(consumeTapEvents)]; + if (consumeTapEvents && consumeTapEvents != (id)[NSNull null]) { + [self setConsumeTapEvents:consumeTapEvents.boolValue]; } NSNumber *visible = data[@"visible"]; - if (visible != nil) { - [sink setVisible:ToBool(visible)]; + if (visible && visible != (id)[NSNull null]) { + [self setVisible:[visible boolValue]]; } NSNumber *zIndex = data[@"zIndex"]; - if (zIndex != nil) { - [sink setZIndex:ToInt(zIndex)]; + if (zIndex && zIndex != (id)[NSNull null]) { + [self setZIndex:[zIndex intValue]]; } NSArray *center = data[@"center"]; - if (center) { - [sink setCenter:ToLocation(center)]; + if (center && center != (id)[NSNull null]) { + [self setCenter:[FLTGoogleMapJSONConversions locationFromLatLong:center]]; } NSNumber *radius = data[@"radius"]; - if (radius != nil) { - [sink setRadius:ToDistance(radius)]; + if (radius && radius != (id)[NSNull null]) { + [self setRadius:[radius floatValue]]; } NSNumber *strokeColor = data[@"strokeColor"]; - if (strokeColor != nil) { - [sink setStrokeColor:ToColor(strokeColor)]; + if (strokeColor && strokeColor != (id)[NSNull null]) { + [self setStrokeColor:[FLTGoogleMapJSONConversions colorFromRGBA:strokeColor]]; } NSNumber *strokeWidth = data[@"strokeWidth"]; - if (strokeWidth != nil) { - [sink setStrokeWidth:ToInt(strokeWidth)]; + if (strokeWidth && strokeWidth != (id)[NSNull null]) { + [self setStrokeWidth:[strokeWidth intValue]]; } NSNumber *fillColor = data[@"fillColor"]; - if (fillColor != nil) { - [sink setFillColor:ToColor(fillColor)]; + if (fillColor && fillColor != (id)[NSNull null]) { + [self setFillColor:[FLTGoogleMapJSONConversions colorFromRGBA:fillColor]]; } } -@implementation FLTCirclesController { - NSMutableDictionary *_circleIdToController; - FlutterMethodChannel *_methodChannel; - NSObject *_registrar; - GMSMapView *_mapView; -} +@end + +@interface FLTCirclesController () + +@property(strong, nonatomic) FlutterMethodChannel *methodChannel; +@property(weak, nonatomic) GMSMapView *mapView; +@property(strong, nonatomic) NSMutableDictionary *circleIdToController; + +@end + +@implementation FLTCirclesController + - (instancetype)init:(FlutterMethodChannel *)methodChannel mapView:(GMSMapView *)mapView registrar:(NSObject *)registrar { @@ -127,10 +121,10 @@ - (instancetype)init:(FlutterMethodChannel *)methodChannel _methodChannel = methodChannel; _mapView = mapView; _circleIdToController = [NSMutableDictionary dictionaryWithCapacity:1]; - _registrar = registrar; } return self; } + - (void)addCircles:(NSArray *)circlesToAdd { for (NSDictionary *circle in circlesToAdd) { CLLocationCoordinate2D position = [FLTCirclesController getPosition:circle]; @@ -140,59 +134,64 @@ - (void)addCircles:(NSArray *)circlesToAdd { [[FLTGoogleMapCircleController alloc] initCircleWithPosition:position radius:radius circleId:circleId - mapView:_mapView]; - InterpretCircleOptions(circle, controller, _registrar); - _circleIdToController[circleId] = controller; + mapView:self.mapView + options:circle]; + self.circleIdToController[circleId] = controller; } } + - (void)changeCircles:(NSArray *)circlesToChange { for (NSDictionary *circle in circlesToChange) { NSString *circleId = [FLTCirclesController getCircleId:circle]; - FLTGoogleMapCircleController *controller = _circleIdToController[circleId]; + FLTGoogleMapCircleController *controller = self.circleIdToController[circleId]; if (!controller) { continue; } - InterpretCircleOptions(circle, controller, _registrar); + [controller interpretCircleOptions:circle]; } } -- (void)removeCircleIds:(NSArray *)circleIdsToRemove { - for (NSString *circleId in circleIdsToRemove) { - if (!circleId) { - continue; - } - FLTGoogleMapCircleController *controller = _circleIdToController[circleId]; + +- (void)removeCircleWithIdentifiers:(NSArray *)identifiers { + for (NSString *identifier in identifiers) { + FLTGoogleMapCircleController *controller = self.circleIdToController[identifier]; if (!controller) { continue; } [controller removeCircle]; - [_circleIdToController removeObjectForKey:circleId]; + [self.circleIdToController removeObjectForKey:identifier]; } } -- (bool)hasCircleWithId:(NSString *)circleId { - if (!circleId) { + +- (bool)hasCircleWithIdentifier:(NSString *)identifier { + if (!identifier) { return false; } - return _circleIdToController[circleId] != nil; + return self.circleIdToController[identifier] != nil; } -- (void)onCircleTap:(NSString *)circleId { - if (!circleId) { + +- (void)didTapCircleWithIdentifier:(NSString *)identifier { + if (!identifier) { return; } - FLTGoogleMapCircleController *controller = _circleIdToController[circleId]; + FLTGoogleMapCircleController *controller = self.circleIdToController[identifier]; if (!controller) { return; } - [_methodChannel invokeMethod:@"circle#onTap" arguments:@{@"circleId" : circleId}]; + [self.methodChannel invokeMethod:@"circle#onTap" arguments:@{@"circleId" : identifier}]; } + + (CLLocationCoordinate2D)getPosition:(NSDictionary *)circle { NSArray *center = circle[@"center"]; - return ToLocation(center); + return [FLTGoogleMapJSONConversions locationFromLatLong:center]; } + + (CLLocationDistance)getRadius:(NSDictionary *)circle { NSNumber *radius = circle[@"radius"]; - return ToDistance(radius); + return [radius floatValue]; } + + (NSString *)getCircleId:(NSDictionary *)circle { return circle[@"circleId"]; } + @end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.h b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.h index a8cebb983347..d1069ac16b39 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.h +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.h @@ -11,34 +11,13 @@ NS_ASSUME_NONNULL_BEGIN -// Defines map UI options writable from Flutter. -@protocol FLTGoogleMapOptionsSink -- (void)setCameraTargetBounds:(nullable GMSCoordinateBounds *)bounds; -- (void)setCompassEnabled:(BOOL)enabled; -- (void)setIndoorEnabled:(BOOL)enabled; -- (void)setTrafficEnabled:(BOOL)enabled; -- (void)setBuildingsEnabled:(BOOL)enabled; -- (void)setMapType:(GMSMapViewType)type; -- (void)setMinZoom:(float)minZoom maxZoom:(float)maxZoom; -- (void)setPaddingTop:(float)top left:(float)left bottom:(float)bottom right:(float)right; -- (void)setRotateGesturesEnabled:(BOOL)enabled; -- (void)setScrollGesturesEnabled:(BOOL)enabled; -- (void)setTiltGesturesEnabled:(BOOL)enabled; -- (void)setTrackCameraPosition:(BOOL)enabled; -- (void)setZoomGesturesEnabled:(BOOL)enabled; -- (void)setMyLocationEnabled:(BOOL)enabled; -- (void)setMyLocationButtonEnabled:(BOOL)enabled; -- (nullable NSString *)setMapStyle:(NSString *)mapStyle; -@end - // Defines map overlay controllable from Flutter. -@interface FLTGoogleMapController - : NSObject +@interface FLTGoogleMapController : NSObject - (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(nullable id)args registrar:(NSObject *)registrar; -- (void)showAtX:(CGFloat)x Y:(CGFloat)y; +- (void)showAtOrigin:(CGPoint)origin; - (void)hide; - (void)animateWithCameraUpdate:(GMSCameraUpdate *)cameraUpdate; - (void)moveWithCameraUpdate:(GMSCameraUpdate *)cameraUpdate; diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m index ca8068129566..6378994819dd 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m @@ -3,25 +3,18 @@ // found in the LICENSE file. #import "GoogleMapController.h" +#import "FLTGoogleMapJSONConversions.h" #import "FLTGoogleMapTileOverlayController.h" -#import "JsonConversions.h" #pragma mark - Conversion of JSON-like values sent via platform channels. Forward declarations. -static NSDictionary *PositionToJson(GMSCameraPosition *position); -static NSDictionary *PointToJson(CGPoint point); -static NSArray *LocationToJson(CLLocationCoordinate2D position); -static CGPoint ToCGPoint(NSDictionary *json); -static GMSCameraPosition *ToOptionalCameraPosition(NSDictionary *json); -static GMSCoordinateBounds *ToOptionalBounds(NSArray *json); -static GMSCameraUpdate *ToCameraUpdate(NSArray *data); -static NSDictionary *GMSCoordinateBoundsToJson(GMSCoordinateBounds *bounds); -static void InterpretMapOptions(NSDictionary *data, id sink); -static double ToDouble(NSNumber *data) { return [FLTGoogleMapJsonConversions toDouble:data]; } +@interface FLTGoogleMapFactory () -@implementation FLTGoogleMapFactory { - NSObject *_registrar; -} +@property(weak, nonatomic) NSObject *registrar; + +@end + +@implementation FLTGoogleMapFactory - (instancetype)initWithRegistrar:(NSObject *)registrar { self = [super init]; @@ -41,28 +34,32 @@ - (instancetype)initWithRegistrar:(NSObject *)registrar return [[FLTGoogleMapController alloc] initWithFrame:frame viewIdentifier:viewId arguments:args - registrar:_registrar]; + registrar:self.registrar]; } @end -@implementation FLTGoogleMapController { - GMSMapView *_mapView; - int64_t _viewId; - FlutterMethodChannel *_channel; - BOOL _trackCameraPosition; - NSObject *_registrar; - FLTMarkersController *_markersController; - FLTPolygonsController *_polygonsController; - FLTPolylinesController *_polylinesController; - FLTCirclesController *_circlesController; - FLTTileOverlaysController *_tileOverlaysController; -} +@interface FLTGoogleMapController () + +@property(nonatomic, strong) GMSMapView *mapView; +@property(nonatomic, strong) FlutterMethodChannel *channel; +@property(nonatomic, assign) BOOL trackCameraPosition; +@property(nonatomic, weak) NSObject *registrar; +@property(nonatomic, strong) FLTMarkersController *markersController; +@property(nonatomic, strong) FLTPolygonsController *polygonsController; +@property(nonatomic, strong) FLTPolylinesController *polylinesController; +@property(nonatomic, strong) FLTCirclesController *circlesController; +@property(nonatomic, strong) FLTTileOverlaysController *tileOverlaysController; + +@end + +@implementation FLTGoogleMapController - (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args registrar:(NSObject *)registrar { - GMSCameraPosition *camera = ToOptionalCameraPosition(args[@"initialCameraPosition"]); + GMSCameraPosition *camera = + [FLTGoogleMapJSONConversions cameraPostionFromDictionary:args[@"initialCameraPosition"]]; GMSMapView *mapView = [GMSMapView mapWithFrame:frame camera:camera]; return [self initWithMapView:mapView viewIdentifier:viewId arguments:args registrar:registrar]; } @@ -73,11 +70,11 @@ - (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView registrar:(NSObject *_Nonnull)registrar { if (self = [super init]) { _mapView = mapView; - _viewId = viewId; _mapView.accessibilityElementsHidden = NO; - _trackCameraPosition = NO; - InterpretMapOptions(args[@"options"], self); + // TODO(cyanglaz): avoid sending message to self in the middle of the init method. + // https://github.com/flutter/flutter/issues/104121 + [self interpretMapOptions:args[@"options"]]; NSString *channelName = [NSString stringWithFormat:@"plugins.flutter.io/google_maps_%lld", viewId]; _channel = [FlutterMethodChannel methodChannelWithName:channelName @@ -90,9 +87,9 @@ - (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView }]; _mapView.delegate = weakSelf; _registrar = registrar; - _markersController = [[FLTMarkersController alloc] init:_channel - mapView:_mapView - registrar:registrar]; + _markersController = [[FLTMarkersController alloc] initWithMethodChannel:_channel + mapView:_mapView + registrar:registrar]; _polygonsController = [[FLTPolygonsController alloc] init:_channel mapView:_mapView registrar:registrar]; @@ -132,25 +129,25 @@ - (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView } - (UIView *)view { - return _mapView; + return self.mapView; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if (object == _mapView && [keyPath isEqualToString:@"frame"]) { - CGRect bounds = _mapView.bounds; + if (object == self.mapView && [keyPath isEqualToString:@"frame"]) { + CGRect bounds = self.mapView.bounds; if (CGRectEqualToRect(bounds, CGRectZero)) { // The workaround is to fix an issue that the camera location is not current when // the size of the map is zero at initialization. - // So We only care about the size of the `_mapView`, ignore the frame changes when the size is - // zero. + // So We only care about the size of the `self.mapView`, ignore the frame changes when the + // size is zero. return; } // We only observe the frame for initial setup. - [_mapView removeObserver:self forKeyPath:@"frame"]; - [_mapView moveCamera:[GMSCameraUpdate setCamera:_mapView.camera]]; + [self.mapView removeObserver:self forKeyPath:@"frame"]; + [self.mapView moveCamera:[GMSCameraUpdate setCamera:self.mapView.camera]]; } else { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } @@ -158,46 +155,50 @@ - (void)observeValueForKeyPath:(NSString *)keyPath - (void)onMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { if ([call.method isEqualToString:@"map#show"]) { - [self showAtX:ToDouble(call.arguments[@"x"]) Y:ToDouble(call.arguments[@"y"])]; + [self showAtOrigin:CGPointMake([call.arguments[@"x"] doubleValue], + [call.arguments[@"y"] doubleValue])]; result(nil); } else if ([call.method isEqualToString:@"map#hide"]) { [self hide]; result(nil); } else if ([call.method isEqualToString:@"camera#animate"]) { - [self animateWithCameraUpdate:ToCameraUpdate(call.arguments[@"cameraUpdate"])]; + [self + animateWithCameraUpdate:[FLTGoogleMapJSONConversions + cameraUpdateFromChannelValue:call.arguments[@"cameraUpdate"]]]; result(nil); } else if ([call.method isEqualToString:@"camera#move"]) { - [self moveWithCameraUpdate:ToCameraUpdate(call.arguments[@"cameraUpdate"])]; + [self moveWithCameraUpdate:[FLTGoogleMapJSONConversions + cameraUpdateFromChannelValue:call.arguments[@"cameraUpdate"]]]; result(nil); } else if ([call.method isEqualToString:@"map#update"]) { - InterpretMapOptions(call.arguments[@"options"], self); - result(PositionToJson([self cameraPosition])); + [self interpretMapOptions:call.arguments[@"options"]]; + result([FLTGoogleMapJSONConversions dictionaryFromPosition:[self cameraPosition]]); } else if ([call.method isEqualToString:@"map#getVisibleRegion"]) { - if (_mapView != nil) { - GMSVisibleRegion visibleRegion = _mapView.projection.visibleRegion; + if (self.mapView != nil) { + GMSVisibleRegion visibleRegion = self.mapView.projection.visibleRegion; GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] initWithRegion:visibleRegion]; - - result(GMSCoordinateBoundsToJson(bounds)); + result([FLTGoogleMapJSONConversions dictionaryFromCoordinateBounds:bounds]); } else { result([FlutterError errorWithCode:@"GoogleMap uninitialized" message:@"getVisibleRegion called prior to map initialization" details:nil]); } } else if ([call.method isEqualToString:@"map#getScreenCoordinate"]) { - if (_mapView != nil) { - CLLocationCoordinate2D location = [FLTGoogleMapJsonConversions toLocation:call.arguments]; - CGPoint point = [_mapView.projection pointForCoordinate:location]; - result(PointToJson(point)); + if (self.mapView != nil) { + CLLocationCoordinate2D location = + [FLTGoogleMapJSONConversions locationFromLatLong:call.arguments]; + CGPoint point = [self.mapView.projection pointForCoordinate:location]; + result([FLTGoogleMapJSONConversions dictionaryFromPoint:point]); } else { result([FlutterError errorWithCode:@"GoogleMap uninitialized" message:@"getScreenCoordinate called prior to map initialization" details:nil]); } } else if ([call.method isEqualToString:@"map#getLatLng"]) { - if (_mapView != nil && call.arguments) { - CGPoint point = ToCGPoint(call.arguments); - CLLocationCoordinate2D latlng = [_mapView.projection coordinateForPoint:point]; - result(LocationToJson(latlng)); + if (self.mapView != nil && call.arguments) { + CGPoint point = [FLTGoogleMapJSONConversions pointFromDictionary:call.arguments]; + CLLocationCoordinate2D latlng = [self.mapView.projection coordinateForPoint:point]; + result([FLTGoogleMapJSONConversions arrayFromLocation:latlng]); } else { result([FlutterError errorWithCode:@"GoogleMap uninitialized" message:@"getLatLng called prior to map initialization" @@ -207,14 +208,14 @@ - (void)onMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { result(nil); } else if ([call.method isEqualToString:@"map#takeSnapshot"]) { if (@available(iOS 10.0, *)) { - if (_mapView != nil) { + if (self.mapView != nil) { UIGraphicsImageRendererFormat *format = [UIGraphicsImageRendererFormat defaultFormat]; format.scale = [[UIScreen mainScreen] scale]; UIGraphicsImageRenderer *renderer = - [[UIGraphicsImageRenderer alloc] initWithSize:_mapView.frame.size format:format]; + [[UIGraphicsImageRenderer alloc] initWithSize:self.mapView.frame.size format:format]; UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext *context) { - [_mapView.layer renderInContext:context.CGContext]; + [self.mapView.layer renderInContext:context.CGContext]; }]; result([FlutterStandardTypedData typedDataWithBytes:UIImagePNGRepresentation(image)]); } else { @@ -229,21 +230,21 @@ - (void)onMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { } else if ([call.method isEqualToString:@"markers#update"]) { id markersToAdd = call.arguments[@"markersToAdd"]; if ([markersToAdd isKindOfClass:[NSArray class]]) { - [_markersController addMarkers:markersToAdd]; + [self.markersController addMarkers:markersToAdd]; } id markersToChange = call.arguments[@"markersToChange"]; if ([markersToChange isKindOfClass:[NSArray class]]) { - [_markersController changeMarkers:markersToChange]; + [self.markersController changeMarkers:markersToChange]; } id markerIdsToRemove = call.arguments[@"markerIdsToRemove"]; if ([markerIdsToRemove isKindOfClass:[NSArray class]]) { - [_markersController removeMarkerIds:markerIdsToRemove]; + [self.markersController removeMarkersWithIdentifiers:markerIdsToRemove]; } result(nil); } else if ([call.method isEqualToString:@"markers#showInfoWindow"]) { id markerId = call.arguments[@"markerId"]; if ([markerId isKindOfClass:[NSString class]]) { - [_markersController showMarkerInfoWindow:markerId result:result]; + [self.markersController showMarkerInfoWindowWithIdentifier:markerId result:result]; } else { result([FlutterError errorWithCode:@"Invalid markerId" message:@"showInfoWindow called with invalid markerId" @@ -252,7 +253,7 @@ - (void)onMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { } else if ([call.method isEqualToString:@"markers#hideInfoWindow"]) { id markerId = call.arguments[@"markerId"]; if ([markerId isKindOfClass:[NSString class]]) { - [_markersController hideMarkerInfoWindow:markerId result:result]; + [self.markersController hideMarkerInfoWindowWithIdentifier:markerId result:result]; } else { result([FlutterError errorWithCode:@"Invalid markerId" message:@"hideInfoWindow called with invalid markerId" @@ -261,7 +262,7 @@ - (void)onMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { } else if ([call.method isEqualToString:@"markers#isInfoWindowShown"]) { id markerId = call.arguments[@"markerId"]; if ([markerId isKindOfClass:[NSString class]]) { - [_markersController isMarkerInfoWindowShown:markerId result:result]; + [self.markersController isInfoWindowShownForMarkerWithIdentifier:markerId result:result]; } else { result([FlutterError errorWithCode:@"Invalid markerId" message:@"isInfoWindowShown called with invalid markerId" @@ -270,97 +271,97 @@ - (void)onMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { } else if ([call.method isEqualToString:@"polygons#update"]) { id polygonsToAdd = call.arguments[@"polygonsToAdd"]; if ([polygonsToAdd isKindOfClass:[NSArray class]]) { - [_polygonsController addPolygons:polygonsToAdd]; + [self.polygonsController addPolygons:polygonsToAdd]; } id polygonsToChange = call.arguments[@"polygonsToChange"]; if ([polygonsToChange isKindOfClass:[NSArray class]]) { - [_polygonsController changePolygons:polygonsToChange]; + [self.polygonsController changePolygons:polygonsToChange]; } id polygonIdsToRemove = call.arguments[@"polygonIdsToRemove"]; if ([polygonIdsToRemove isKindOfClass:[NSArray class]]) { - [_polygonsController removePolygonIds:polygonIdsToRemove]; + [self.polygonsController removePolygonWithIdentifiers:polygonIdsToRemove]; } result(nil); } else if ([call.method isEqualToString:@"polylines#update"]) { id polylinesToAdd = call.arguments[@"polylinesToAdd"]; if ([polylinesToAdd isKindOfClass:[NSArray class]]) { - [_polylinesController addPolylines:polylinesToAdd]; + [self.polylinesController addPolylines:polylinesToAdd]; } id polylinesToChange = call.arguments[@"polylinesToChange"]; if ([polylinesToChange isKindOfClass:[NSArray class]]) { - [_polylinesController changePolylines:polylinesToChange]; + [self.polylinesController changePolylines:polylinesToChange]; } id polylineIdsToRemove = call.arguments[@"polylineIdsToRemove"]; if ([polylineIdsToRemove isKindOfClass:[NSArray class]]) { - [_polylinesController removePolylineIds:polylineIdsToRemove]; + [self.polylinesController removePolylineWithIdentifiers:polylineIdsToRemove]; } result(nil); } else if ([call.method isEqualToString:@"circles#update"]) { id circlesToAdd = call.arguments[@"circlesToAdd"]; if ([circlesToAdd isKindOfClass:[NSArray class]]) { - [_circlesController addCircles:circlesToAdd]; + [self.circlesController addCircles:circlesToAdd]; } id circlesToChange = call.arguments[@"circlesToChange"]; if ([circlesToChange isKindOfClass:[NSArray class]]) { - [_circlesController changeCircles:circlesToChange]; + [self.circlesController changeCircles:circlesToChange]; } id circleIdsToRemove = call.arguments[@"circleIdsToRemove"]; if ([circleIdsToRemove isKindOfClass:[NSArray class]]) { - [_circlesController removeCircleIds:circleIdsToRemove]; + [self.circlesController removeCircleWithIdentifiers:circleIdsToRemove]; } result(nil); } else if ([call.method isEqualToString:@"tileOverlays#update"]) { id tileOverlaysToAdd = call.arguments[@"tileOverlaysToAdd"]; if ([tileOverlaysToAdd isKindOfClass:[NSArray class]]) { - [_tileOverlaysController addTileOverlays:tileOverlaysToAdd]; + [self.tileOverlaysController addTileOverlays:tileOverlaysToAdd]; } id tileOverlaysToChange = call.arguments[@"tileOverlaysToChange"]; if ([tileOverlaysToChange isKindOfClass:[NSArray class]]) { - [_tileOverlaysController changeTileOverlays:tileOverlaysToChange]; + [self.tileOverlaysController changeTileOverlays:tileOverlaysToChange]; } id tileOverlayIdsToRemove = call.arguments[@"tileOverlayIdsToRemove"]; if ([tileOverlayIdsToRemove isKindOfClass:[NSArray class]]) { - [_tileOverlaysController removeTileOverlayIds:tileOverlayIdsToRemove]; + [self.tileOverlaysController removeTileOverlayWithIdentifiers:tileOverlayIdsToRemove]; } result(nil); } else if ([call.method isEqualToString:@"tileOverlays#clearTileCache"]) { id rawTileOverlayId = call.arguments[@"tileOverlayId"]; - [_tileOverlaysController clearTileCache:rawTileOverlayId]; + [self.tileOverlaysController clearTileCacheWithIdentifier:rawTileOverlayId]; result(nil); } else if ([call.method isEqualToString:@"map#isCompassEnabled"]) { - NSNumber *isCompassEnabled = @(_mapView.settings.compassButton); + NSNumber *isCompassEnabled = @(self.mapView.settings.compassButton); result(isCompassEnabled); } else if ([call.method isEqualToString:@"map#isMapToolbarEnabled"]) { NSNumber *isMapToolbarEnabled = @NO; result(isMapToolbarEnabled); } else if ([call.method isEqualToString:@"map#getMinMaxZoomLevels"]) { - NSArray *zoomLevels = @[ @(_mapView.minZoom), @(_mapView.maxZoom) ]; + NSArray *zoomLevels = @[ @(self.mapView.minZoom), @(self.mapView.maxZoom) ]; result(zoomLevels); } else if ([call.method isEqualToString:@"map#getZoomLevel"]) { - result(@(_mapView.camera.zoom)); + result(@(self.mapView.camera.zoom)); } else if ([call.method isEqualToString:@"map#isZoomGesturesEnabled"]) { - NSNumber *isZoomGesturesEnabled = @(_mapView.settings.zoomGestures); + NSNumber *isZoomGesturesEnabled = @(self.mapView.settings.zoomGestures); result(isZoomGesturesEnabled); } else if ([call.method isEqualToString:@"map#isZoomControlsEnabled"]) { NSNumber *isZoomControlsEnabled = @NO; result(isZoomControlsEnabled); } else if ([call.method isEqualToString:@"map#isTiltGesturesEnabled"]) { - NSNumber *isTiltGesturesEnabled = @(_mapView.settings.tiltGestures); + NSNumber *isTiltGesturesEnabled = @(self.mapView.settings.tiltGestures); result(isTiltGesturesEnabled); } else if ([call.method isEqualToString:@"map#isRotateGesturesEnabled"]) { - NSNumber *isRotateGesturesEnabled = @(_mapView.settings.rotateGestures); + NSNumber *isRotateGesturesEnabled = @(self.mapView.settings.rotateGestures); result(isRotateGesturesEnabled); } else if ([call.method isEqualToString:@"map#isScrollGesturesEnabled"]) { - NSNumber *isScrollGesturesEnabled = @(_mapView.settings.scrollGestures); + NSNumber *isScrollGesturesEnabled = @(self.mapView.settings.scrollGestures); result(isScrollGesturesEnabled); } else if ([call.method isEqualToString:@"map#isMyLocationButtonEnabled"]) { - NSNumber *isMyLocationButtonEnabled = @(_mapView.settings.myLocationButton); + NSNumber *isMyLocationButtonEnabled = @(self.mapView.settings.myLocationButton); result(isMyLocationButtonEnabled); } else if ([call.method isEqualToString:@"map#isTrafficEnabled"]) { - NSNumber *isTrafficEnabled = @(_mapView.trafficEnabled); + NSNumber *isTrafficEnabled = @(self.mapView.trafficEnabled); result(isTrafficEnabled); } else if ([call.method isEqualToString:@"map#isBuildingsEnabled"]) { - NSNumber *isBuildingsEnabled = @(_mapView.buildingsEnabled); + NSNumber *isBuildingsEnabled = @(self.mapView.buildingsEnabled); result(isBuildingsEnabled); } else if ([call.method isEqualToString:@"map#setStyle"]) { NSString *mapStyle = [call arguments]; @@ -372,86 +373,84 @@ - (void)onMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { } } else if ([call.method isEqualToString:@"map#getTileOverlayInfo"]) { NSString *rawTileOverlayId = call.arguments[@"tileOverlayId"]; - result([_tileOverlaysController getTileOverlayInfo:rawTileOverlayId]); + result([self.tileOverlaysController tileOverlayInfoWithIdentifier:rawTileOverlayId]); } else { result(FlutterMethodNotImplemented); } } -- (void)showAtX:(CGFloat)x Y:(CGFloat)y { - _mapView.frame = - CGRectMake(x, y, CGRectGetWidth(_mapView.frame), CGRectGetHeight(_mapView.frame)); - _mapView.hidden = NO; +- (void)showAtOrigin:(CGPoint)origin { + CGRect frame = {origin, self.mapView.frame.size}; + self.mapView.frame = frame; + self.mapView.hidden = NO; } - (void)hide { - _mapView.hidden = YES; + self.mapView.hidden = YES; } - (void)animateWithCameraUpdate:(GMSCameraUpdate *)cameraUpdate { - [_mapView animateWithCameraUpdate:cameraUpdate]; + [self.mapView animateWithCameraUpdate:cameraUpdate]; } - (void)moveWithCameraUpdate:(GMSCameraUpdate *)cameraUpdate { - [_mapView moveCamera:cameraUpdate]; + [self.mapView moveCamera:cameraUpdate]; } - (GMSCameraPosition *)cameraPosition { - if (_trackCameraPosition) { - return _mapView.camera; + if (self.trackCameraPosition) { + return self.mapView.camera; } else { return nil; } } -#pragma mark - FLTGoogleMapOptionsSink methods - - (void)setCamera:(GMSCameraPosition *)camera { - _mapView.camera = camera; + self.mapView.camera = camera; } - (void)setCameraTargetBounds:(GMSCoordinateBounds *)bounds { - _mapView.cameraTargetBounds = bounds; + self.mapView.cameraTargetBounds = bounds; } - (void)setCompassEnabled:(BOOL)enabled { - _mapView.settings.compassButton = enabled; + self.mapView.settings.compassButton = enabled; } - (void)setIndoorEnabled:(BOOL)enabled { - _mapView.indoorEnabled = enabled; + self.mapView.indoorEnabled = enabled; } - (void)setTrafficEnabled:(BOOL)enabled { - _mapView.trafficEnabled = enabled; + self.mapView.trafficEnabled = enabled; } - (void)setBuildingsEnabled:(BOOL)enabled { - _mapView.buildingsEnabled = enabled; + self.mapView.buildingsEnabled = enabled; } - (void)setMapType:(GMSMapViewType)mapType { - _mapView.mapType = mapType; + self.mapView.mapType = mapType; } - (void)setMinZoom:(float)minZoom maxZoom:(float)maxZoom { - [_mapView setMinZoom:minZoom maxZoom:maxZoom]; + [self.mapView setMinZoom:minZoom maxZoom:maxZoom]; } - (void)setPaddingTop:(float)top left:(float)left bottom:(float)bottom right:(float)right { - _mapView.padding = UIEdgeInsetsMake(top, left, bottom, right); + self.mapView.padding = UIEdgeInsetsMake(top, left, bottom, right); } - (void)setRotateGesturesEnabled:(BOOL)enabled { - _mapView.settings.rotateGestures = enabled; + self.mapView.settings.rotateGestures = enabled; } - (void)setScrollGesturesEnabled:(BOOL)enabled { - _mapView.settings.scrollGestures = enabled; + self.mapView.settings.scrollGestures = enabled; } - (void)setTiltGesturesEnabled:(BOOL)enabled { - _mapView.settings.tiltGestures = enabled; + self.mapView.settings.tiltGestures = enabled; } - (void)setTrackCameraPosition:(BOOL)enabled { @@ -459,20 +458,20 @@ - (void)setTrackCameraPosition:(BOOL)enabled { } - (void)setZoomGesturesEnabled:(BOOL)enabled { - _mapView.settings.zoomGestures = enabled; + self.mapView.settings.zoomGestures = enabled; } - (void)setMyLocationEnabled:(BOOL)enabled { - _mapView.myLocationEnabled = enabled; + self.mapView.myLocationEnabled = enabled; } - (void)setMyLocationButtonEnabled:(BOOL)enabled { - _mapView.settings.myLocationButton = enabled; + self.mapView.settings.myLocationButton = enabled; } - (NSString *)setMapStyle:(NSString *)mapStyle { if (mapStyle == (id)[NSNull null] || mapStyle.length == 0) { - _mapView.mapStyle = nil; + self.mapView.mapStyle = nil; return nil; } NSError *error; @@ -480,7 +479,7 @@ - (NSString *)setMapStyle:(NSString *)mapStyle { if (!style) { return [error localizedDescription]; } else { - _mapView.mapStyle = style; + self.mapView.mapStyle = style; return nil; } } @@ -488,236 +487,141 @@ - (NSString *)setMapStyle:(NSString *)mapStyle { #pragma mark - GMSMapViewDelegate methods - (void)mapView:(GMSMapView *)mapView willMove:(BOOL)gesture { - [_channel invokeMethod:@"camera#onMoveStarted" arguments:@{@"isGesture" : @(gesture)}]; + [self.channel invokeMethod:@"camera#onMoveStarted" arguments:@{@"isGesture" : @(gesture)}]; } - (void)mapView:(GMSMapView *)mapView didChangeCameraPosition:(GMSCameraPosition *)position { - if (_trackCameraPosition) { - [_channel invokeMethod:@"camera#onMove" arguments:@{@"position" : PositionToJson(position)}]; + if (self.trackCameraPosition) { + [self.channel invokeMethod:@"camera#onMove" + arguments:@{ + @"position" : [FLTGoogleMapJSONConversions dictionaryFromPosition:position] + }]; } } - (void)mapView:(GMSMapView *)mapView idleAtCameraPosition:(GMSCameraPosition *)position { - [_channel invokeMethod:@"camera#onIdle" arguments:@{}]; + [self.channel invokeMethod:@"camera#onIdle" arguments:@{}]; } - (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker { NSString *markerId = marker.userData[0]; - return [_markersController onMarkerTap:markerId]; + return [self.markersController didTapMarkerWithIdentifier:markerId]; } - (void)mapView:(GMSMapView *)mapView didEndDraggingMarker:(GMSMarker *)marker { NSString *markerId = marker.userData[0]; - [_markersController onMarkerDragEnd:markerId coordinate:marker.position]; + [self.markersController didEndDraggingMarkerWithIdentifier:markerId location:marker.position]; } - (void)mapView:(GMSMapView *)mapView didStartDraggingMarker:(GMSMarker *)marker { NSString *markerId = marker.userData[0]; - [_markersController onMarkerDragStart:markerId coordinate:marker.position]; + [self.markersController didStartDraggingMarkerWithIdentifier:markerId location:marker.position]; } - (void)mapView:(GMSMapView *)mapView didDragMarker:(GMSMarker *)marker { NSString *markerId = marker.userData[0]; - [_markersController onMarkerDrag:markerId coordinate:marker.position]; + [self.markersController didDragMarkerWithIdentifier:markerId location:marker.position]; } - (void)mapView:(GMSMapView *)mapView didTapInfoWindowOfMarker:(GMSMarker *)marker { NSString *markerId = marker.userData[0]; - [_markersController onInfoWindowTap:markerId]; + [self.markersController didTapInfoWindowOfMarkerWithIdentifier:markerId]; } - (void)mapView:(GMSMapView *)mapView didTapOverlay:(GMSOverlay *)overlay { NSString *overlayId = overlay.userData[0]; - if ([_polylinesController hasPolylineWithId:overlayId]) { - [_polylinesController onPolylineTap:overlayId]; - } else if ([_polygonsController hasPolygonWithId:overlayId]) { - [_polygonsController onPolygonTap:overlayId]; - } else if ([_circlesController hasCircleWithId:overlayId]) { - [_circlesController onCircleTap:overlayId]; + if ([self.polylinesController hasPolylineWithIdentifier:overlayId]) { + [self.polylinesController didTapPolylineWithIdentifier:overlayId]; + } else if ([self.polygonsController hasPolygonWithIdentifier:overlayId]) { + [self.polygonsController didTapPolygonWithIdentifier:overlayId]; + } else if ([self.circlesController hasCircleWithIdentifier:overlayId]) { + [self.circlesController didTapCircleWithIdentifier:overlayId]; } } - (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate { - [_channel invokeMethod:@"map#onTap" arguments:@{@"position" : LocationToJson(coordinate)}]; + [self.channel + invokeMethod:@"map#onTap" + arguments:@{@"position" : [FLTGoogleMapJSONConversions arrayFromLocation:coordinate]}]; } - (void)mapView:(GMSMapView *)mapView didLongPressAtCoordinate:(CLLocationCoordinate2D)coordinate { - [_channel invokeMethod:@"map#onLongPress" arguments:@{@"position" : LocationToJson(coordinate)}]; -} - -@end - -#pragma mark - Implementations of JSON conversion functions. - -static NSArray *LocationToJson(CLLocationCoordinate2D position) { - return @[ @(position.latitude), @(position.longitude) ]; -} - -static NSDictionary *PositionToJson(GMSCameraPosition *position) { - if (!position) { - return nil; - } - return @{ - @"target" : LocationToJson([position target]), - @"zoom" : @([position zoom]), - @"bearing" : @([position bearing]), - @"tilt" : @([position viewingAngle]), - }; -} - -static NSDictionary *PointToJson(CGPoint point) { - return @{ - @"x" : @(lroundf(point.x)), - @"y" : @(lroundf(point.y)), - }; + [self.channel + invokeMethod:@"map#onLongPress" + arguments:@{@"position" : [FLTGoogleMapJSONConversions arrayFromLocation:coordinate]}]; } -static NSDictionary *GMSCoordinateBoundsToJson(GMSCoordinateBounds *bounds) { - if (!bounds) { - return nil; - } - return @{ - @"southwest" : LocationToJson([bounds southWest]), - @"northeast" : LocationToJson([bounds northEast]), - }; -} - -static float ToFloat(NSNumber *data) { return [FLTGoogleMapJsonConversions toFloat:data]; } - -static CLLocationCoordinate2D ToLocation(NSArray *data) { - return [FLTGoogleMapJsonConversions toLocation:data]; -} - -static int ToInt(NSNumber *data) { return [FLTGoogleMapJsonConversions toInt:data]; } - -static BOOL ToBool(NSNumber *data) { return [FLTGoogleMapJsonConversions toBool:data]; } - -static CGPoint ToPoint(NSArray *data) { return [FLTGoogleMapJsonConversions toPoint:data]; } - -static GMSCameraPosition *ToCameraPosition(NSDictionary *data) { - return [GMSCameraPosition cameraWithTarget:ToLocation(data[@"target"]) - zoom:ToFloat(data[@"zoom"]) - bearing:ToDouble(data[@"bearing"]) - viewingAngle:ToDouble(data[@"tilt"])]; -} - -static GMSCameraPosition *ToOptionalCameraPosition(NSDictionary *json) { - return json ? ToCameraPosition(json) : nil; -} - -static CGPoint ToCGPoint(NSDictionary *json) { - double x = ToDouble(json[@"x"]); - double y = ToDouble(json[@"y"]); - return CGPointMake(x, y); -} - -static GMSCoordinateBounds *ToBounds(NSArray *data) { - return [[GMSCoordinateBounds alloc] initWithCoordinate:ToLocation(data[0]) - coordinate:ToLocation(data[1])]; -} - -static GMSCoordinateBounds *ToOptionalBounds(NSArray *data) { - return (data[0] == [NSNull null]) ? nil : ToBounds(data[0]); -} - -static GMSMapViewType ToMapViewType(NSNumber *json) { - int value = ToInt(json); - return (GMSMapViewType)(value == 0 ? 5 : value); -} - -static GMSCameraUpdate *ToCameraUpdate(NSArray *data) { - NSString *update = data[0]; - if ([update isEqualToString:@"newCameraPosition"]) { - return [GMSCameraUpdate setCamera:ToCameraPosition(data[1])]; - } else if ([update isEqualToString:@"newLatLng"]) { - return [GMSCameraUpdate setTarget:ToLocation(data[1])]; - } else if ([update isEqualToString:@"newLatLngBounds"]) { - return [GMSCameraUpdate fitBounds:ToBounds(data[1]) withPadding:ToDouble(data[2])]; - } else if ([update isEqualToString:@"newLatLngZoom"]) { - return [GMSCameraUpdate setTarget:ToLocation(data[1]) zoom:ToFloat(data[2])]; - } else if ([update isEqualToString:@"scrollBy"]) { - return [GMSCameraUpdate scrollByX:ToDouble(data[1]) Y:ToDouble(data[2])]; - } else if ([update isEqualToString:@"zoomBy"]) { - if (data.count == 2) { - return [GMSCameraUpdate zoomBy:ToFloat(data[1])]; - } else { - return [GMSCameraUpdate zoomBy:ToFloat(data[1]) atPoint:ToPoint(data[2])]; - } - } else if ([update isEqualToString:@"zoomIn"]) { - return [GMSCameraUpdate zoomIn]; - } else if ([update isEqualToString:@"zoomOut"]) { - return [GMSCameraUpdate zoomOut]; - } else if ([update isEqualToString:@"zoomTo"]) { - return [GMSCameraUpdate zoomTo:ToFloat(data[1])]; - } - return nil; -} - -static void InterpretMapOptions(NSDictionary *data, id sink) { +- (void)interpretMapOptions:(NSDictionary *)data { NSArray *cameraTargetBounds = data[@"cameraTargetBounds"]; - if (cameraTargetBounds) { - [sink setCameraTargetBounds:ToOptionalBounds(cameraTargetBounds)]; + if (cameraTargetBounds && cameraTargetBounds != (id)[NSNull null]) { + [self + setCameraTargetBounds:cameraTargetBounds.count > 0 && cameraTargetBounds[0] != [NSNull null] + ? [FLTGoogleMapJSONConversions + coordinateBoundsFromLatLongs:cameraTargetBounds.firstObject] + : nil]; } NSNumber *compassEnabled = data[@"compassEnabled"]; - if (compassEnabled != nil) { - [sink setCompassEnabled:ToBool(compassEnabled)]; + if (compassEnabled && compassEnabled != (id)[NSNull null]) { + [self setCompassEnabled:[compassEnabled boolValue]]; } id indoorEnabled = data[@"indoorEnabled"]; - if (indoorEnabled) { - [sink setIndoorEnabled:ToBool(indoorEnabled)]; + if (indoorEnabled && indoorEnabled != [NSNull null]) { + [self setIndoorEnabled:[indoorEnabled boolValue]]; } id trafficEnabled = data[@"trafficEnabled"]; - if (trafficEnabled) { - [sink setTrafficEnabled:ToBool(trafficEnabled)]; + if (trafficEnabled && trafficEnabled != [NSNull null]) { + [self setTrafficEnabled:[trafficEnabled boolValue]]; } id buildingsEnabled = data[@"buildingsEnabled"]; - if (buildingsEnabled) { - [sink setBuildingsEnabled:ToBool(buildingsEnabled)]; + if (buildingsEnabled && buildingsEnabled != [NSNull null]) { + [self setBuildingsEnabled:[buildingsEnabled boolValue]]; } id mapType = data[@"mapType"]; - if (mapType) { - [sink setMapType:ToMapViewType(mapType)]; + if (mapType && mapType != [NSNull null]) { + [self setMapType:[FLTGoogleMapJSONConversions mapViewTypeFromTypeValue:mapType]]; } NSArray *zoomData = data[@"minMaxZoomPreference"]; - if (zoomData) { - float minZoom = (zoomData[0] == [NSNull null]) ? kGMSMinZoomLevel : ToFloat(zoomData[0]); - float maxZoom = (zoomData[1] == [NSNull null]) ? kGMSMaxZoomLevel : ToFloat(zoomData[1]); - [sink setMinZoom:minZoom maxZoom:maxZoom]; + if (zoomData && zoomData != (id)[NSNull null]) { + float minZoom = (zoomData[0] == [NSNull null]) ? kGMSMinZoomLevel : [zoomData[0] floatValue]; + float maxZoom = (zoomData[1] == [NSNull null]) ? kGMSMaxZoomLevel : [zoomData[1] floatValue]; + [self setMinZoom:minZoom maxZoom:maxZoom]; } NSArray *paddingData = data[@"padding"]; if (paddingData) { - float top = (paddingData[0] == [NSNull null]) ? 0 : ToFloat(paddingData[0]); - float left = (paddingData[1] == [NSNull null]) ? 0 : ToFloat(paddingData[1]); - float bottom = (paddingData[2] == [NSNull null]) ? 0 : ToFloat(paddingData[2]); - float right = (paddingData[3] == [NSNull null]) ? 0 : ToFloat(paddingData[3]); - [sink setPaddingTop:top left:left bottom:bottom right:right]; + float top = (paddingData[0] == [NSNull null]) ? 0 : [paddingData[0] floatValue]; + float left = (paddingData[1] == [NSNull null]) ? 0 : [paddingData[1] floatValue]; + float bottom = (paddingData[2] == [NSNull null]) ? 0 : [paddingData[2] floatValue]; + float right = (paddingData[3] == [NSNull null]) ? 0 : [paddingData[3] floatValue]; + [self setPaddingTop:top left:left bottom:bottom right:right]; } NSNumber *rotateGesturesEnabled = data[@"rotateGesturesEnabled"]; - if (rotateGesturesEnabled != nil) { - [sink setRotateGesturesEnabled:ToBool(rotateGesturesEnabled)]; + if (rotateGesturesEnabled && rotateGesturesEnabled != (id)[NSNull null]) { + [self setRotateGesturesEnabled:[rotateGesturesEnabled boolValue]]; } NSNumber *scrollGesturesEnabled = data[@"scrollGesturesEnabled"]; - if (scrollGesturesEnabled != nil) { - [sink setScrollGesturesEnabled:ToBool(scrollGesturesEnabled)]; + if (scrollGesturesEnabled && scrollGesturesEnabled != (id)[NSNull null]) { + [self setScrollGesturesEnabled:[scrollGesturesEnabled boolValue]]; } NSNumber *tiltGesturesEnabled = data[@"tiltGesturesEnabled"]; - if (tiltGesturesEnabled != nil) { - [sink setTiltGesturesEnabled:ToBool(tiltGesturesEnabled)]; + if (tiltGesturesEnabled && tiltGesturesEnabled != (id)[NSNull null]) { + [self setTiltGesturesEnabled:[tiltGesturesEnabled boolValue]]; } NSNumber *trackCameraPosition = data[@"trackCameraPosition"]; - if (trackCameraPosition != nil) { - [sink setTrackCameraPosition:ToBool(trackCameraPosition)]; + if (trackCameraPosition && trackCameraPosition != (id)[NSNull null]) { + [self setTrackCameraPosition:[trackCameraPosition boolValue]]; } NSNumber *zoomGesturesEnabled = data[@"zoomGesturesEnabled"]; - if (zoomGesturesEnabled != nil) { - [sink setZoomGesturesEnabled:ToBool(zoomGesturesEnabled)]; + if (zoomGesturesEnabled && zoomGesturesEnabled != (id)[NSNull null]) { + [self setZoomGesturesEnabled:[zoomGesturesEnabled boolValue]]; } NSNumber *myLocationEnabled = data[@"myLocationEnabled"]; - if (myLocationEnabled != nil) { - [sink setMyLocationEnabled:ToBool(myLocationEnabled)]; + if (myLocationEnabled && myLocationEnabled != (id)[NSNull null]) { + [self setMyLocationEnabled:[myLocationEnabled boolValue]]; } NSNumber *myLocationButtonEnabled = data[@"myLocationButtonEnabled"]; - if (myLocationButtonEnabled != nil) { - [sink setMyLocationButtonEnabled:ToBool(myLocationButtonEnabled)]; + if (myLocationButtonEnabled && myLocationButtonEnabled != (id)[NSNull null]) { + [self setMyLocationButtonEnabled:[myLocationButtonEnabled boolValue]]; } } + +@end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.h b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.h index 8734c06fe929..a33d48073dd2 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.h +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.h @@ -8,50 +8,37 @@ NS_ASSUME_NONNULL_BEGIN -// Defines marker UI options writable from Flutter. -@protocol FLTGoogleMapMarkerOptionsSink -- (void)setAlpha:(float)alpha; -- (void)setAnchor:(CGPoint)anchor; -- (void)setConsumeTapEvents:(BOOL)consume; -- (void)setDraggable:(BOOL)draggable; -- (void)setFlat:(BOOL)flat; -- (void)setIcon:(UIImage *)icon; -- (void)setInfoWindowAnchor:(CGPoint)anchor; -- (void)setInfoWindowTitle:(NSString *)title snippet:(NSString *)snippet; -- (void)setPosition:(CLLocationCoordinate2D)position; -- (void)setRotation:(CLLocationDegrees)rotation; -- (void)setVisible:(BOOL)visible; -- (void)setZIndex:(int)zIndex; -@end - // Defines marker controllable by Flutter. -@interface FLTGoogleMapMarkerController : NSObject -@property(atomic, readonly) NSString *markerId; +@interface FLTGoogleMapMarkerController : NSObject +@property(assign, nonatomic, readonly) BOOL consumeTapEvents; - (instancetype)initMarkerWithPosition:(CLLocationCoordinate2D)position - markerId:(NSString *)markerId + identifier:(NSString *)identifier mapView:(GMSMapView *)mapView; - (void)showInfoWindow; - (void)hideInfoWindow; - (BOOL)isInfoWindowShown; -- (BOOL)consumeTapEvents; - (void)removeMarker; @end @interface FLTMarkersController : NSObject -- (instancetype)init:(FlutterMethodChannel *)methodChannel - mapView:(GMSMapView *)mapView - registrar:(NSObject *)registrar; +- (instancetype)initWithMethodChannel:(FlutterMethodChannel *)methodChannel + mapView:(GMSMapView *)mapView + registrar:(NSObject *)registrar; - (void)addMarkers:(NSArray *)markersToAdd; - (void)changeMarkers:(NSArray *)markersToChange; -- (void)removeMarkerIds:(NSArray *)markerIdsToRemove; -- (BOOL)onMarkerTap:(NSString *)markerId; -- (void)onMarkerDragStart:(NSString *)markerId coordinate:(CLLocationCoordinate2D)coordinate; -- (void)onMarkerDragEnd:(NSString *)markerId coordinate:(CLLocationCoordinate2D)coordinate; -- (void)onMarkerDrag:(NSString *)markerId coordinate:(CLLocationCoordinate2D)coordinate; -- (void)onInfoWindowTap:(NSString *)markerId; -- (void)showMarkerInfoWindow:(NSString *)markerId result:(FlutterResult)result; -- (void)hideMarkerInfoWindow:(NSString *)markerId result:(FlutterResult)result; -- (void)isMarkerInfoWindowShown:(NSString *)markerId result:(FlutterResult)result; +- (void)removeMarkersWithIdentifiers:(NSArray *)identifiers; +- (BOOL)didTapMarkerWithIdentifier:(NSString *)identifier; +- (void)didStartDraggingMarkerWithIdentifier:(NSString *)identifier + location:(CLLocationCoordinate2D)coordinate; +- (void)didEndDraggingMarkerWithIdentifier:(NSString *)identifier + location:(CLLocationCoordinate2D)coordinate; +- (void)didDragMarkerWithIdentifier:(NSString *)identifier + location:(CLLocationCoordinate2D)coordinate; +- (void)didTapInfoWindowOfMarkerWithIdentifier:(NSString *)identifier; +- (void)showMarkerInfoWindowWithIdentifier:(NSString *)identifier result:(FlutterResult)result; +- (void)hideMarkerInfoWindowWithIdentifier:(NSString *)identifier result:(FlutterResult)result; +- (void)isInfoWindowShownForMarkerWithIdentifier:(NSString *)identifier + result:(FlutterResult)result; @end NS_ASSUME_NONNULL_END diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.m index c2877e2bd78f..dd07e791a888 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.m +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.m @@ -3,184 +3,159 @@ // found in the LICENSE file. #import "GoogleMapMarkerController.h" -#import "JsonConversions.h" +#import "FLTGoogleMapJSONConversions.h" -static UIImage *ExtractIcon(NSObject *registrar, NSArray *icon); -static void InterpretInfoWindow(id sink, NSDictionary *data); +@interface FLTGoogleMapMarkerController () + +@property(strong, nonatomic) GMSMarker *marker; +@property(weak, nonatomic) GMSMapView *mapView; +@property(assign, nonatomic, readwrite) BOOL consumeTapEvents; + +@end + +@implementation FLTGoogleMapMarkerController -@implementation FLTGoogleMapMarkerController { - GMSMarker *_marker; - GMSMapView *_mapView; - BOOL _consumeTapEvents; -} - (instancetype)initMarkerWithPosition:(CLLocationCoordinate2D)position - markerId:(NSString *)markerId + identifier:(NSString *)identifier mapView:(GMSMapView *)mapView { self = [super init]; if (self) { _marker = [GMSMarker markerWithPosition:position]; _mapView = mapView; - _markerId = markerId; - _marker.userData = @[ _markerId ]; - _consumeTapEvents = NO; + _marker.userData = @[ identifier ]; } return self; } + - (void)showInfoWindow { - _mapView.selectedMarker = _marker; + self.mapView.selectedMarker = self.marker; } + - (void)hideInfoWindow { - if (_mapView.selectedMarker == _marker) { - _mapView.selectedMarker = nil; + if (self.mapView.selectedMarker == self.marker) { + self.mapView.selectedMarker = nil; } } + - (BOOL)isInfoWindowShown { - return _mapView.selectedMarker == _marker; -} -- (BOOL)consumeTapEvents { - return _consumeTapEvents; + return self.mapView.selectedMarker == self.marker; } + - (void)removeMarker { - _marker.map = nil; + self.marker.map = nil; } -#pragma mark - FLTGoogleMapMarkerOptionsSink methods - - (void)setAlpha:(float)alpha { - _marker.opacity = alpha; + self.marker.opacity = alpha; } + - (void)setAnchor:(CGPoint)anchor { - _marker.groundAnchor = anchor; -} -- (void)setConsumeTapEvents:(BOOL)consumes { - _consumeTapEvents = consumes; + self.marker.groundAnchor = anchor; } + - (void)setDraggable:(BOOL)draggable { - _marker.draggable = draggable; + self.marker.draggable = draggable; } + - (void)setFlat:(BOOL)flat { - _marker.flat = flat; + self.marker.flat = flat; } + - (void)setIcon:(UIImage *)icon { - _marker.icon = icon; + self.marker.icon = icon; } + - (void)setInfoWindowAnchor:(CGPoint)anchor { - _marker.infoWindowAnchor = anchor; + self.marker.infoWindowAnchor = anchor; } + - (void)setInfoWindowTitle:(NSString *)title snippet:(NSString *)snippet { - _marker.title = title; - _marker.snippet = snippet; + self.marker.title = title; + self.marker.snippet = snippet; } + - (void)setPosition:(CLLocationCoordinate2D)position { - _marker.position = position; + self.marker.position = position; } + - (void)setRotation:(CLLocationDegrees)rotation { - _marker.rotation = rotation; + self.marker.rotation = rotation; } -- (void)setVisible:(BOOL)visible { - _marker.map = visible ? _mapView : nil; -} -- (void)setZIndex:(int)zIndex { - _marker.zIndex = zIndex; -} -@end -static double ToDouble(NSNumber *data) { return [FLTGoogleMapJsonConversions toDouble:data]; } - -static float ToFloat(NSNumber *data) { return [FLTGoogleMapJsonConversions toFloat:data]; } - -static CLLocationCoordinate2D ToLocation(NSArray *data) { - return [FLTGoogleMapJsonConversions toLocation:data]; +- (void)setVisible:(BOOL)visible { + self.marker.map = visible ? self.mapView : nil; } -static int ToInt(NSNumber *data) { return [FLTGoogleMapJsonConversions toInt:data]; } - -static BOOL ToBool(NSNumber *data) { return [FLTGoogleMapJsonConversions toBool:data]; } - -static CGPoint ToPoint(NSArray *data) { return [FLTGoogleMapJsonConversions toPoint:data]; } - -static NSArray *PositionToJson(CLLocationCoordinate2D data) { - return [FLTGoogleMapJsonConversions positionToJson:data]; +- (void)setZIndex:(int)zIndex { + self.marker.zIndex = zIndex; } -static void InterpretMarkerOptions(NSDictionary *data, id sink, - NSObject *registrar) { +- (void)interpretMarkerOptions:(NSDictionary *)data + registrar:(NSObject *)registrar { NSNumber *alpha = data[@"alpha"]; - if (alpha != nil) { - [sink setAlpha:ToFloat(alpha)]; + if (alpha && alpha != (id)[NSNull null]) { + [self setAlpha:[alpha floatValue]]; } NSArray *anchor = data[@"anchor"]; - if (anchor) { - [sink setAnchor:ToPoint(anchor)]; + if (anchor && anchor != (id)[NSNull null]) { + [self setAnchor:[FLTGoogleMapJSONConversions pointFromArray:anchor]]; } NSNumber *draggable = data[@"draggable"]; - if (draggable != nil) { - [sink setDraggable:ToBool(draggable)]; + if (draggable && draggable != (id)[NSNull null]) { + [self setDraggable:[draggable boolValue]]; } NSArray *icon = data[@"icon"]; - if (icon) { - UIImage *image = ExtractIcon(registrar, icon); - [sink setIcon:image]; + if (icon && icon != (id)[NSNull null]) { + UIImage *image = [self extractIconFromData:icon registrar:registrar]; + [self setIcon:image]; } NSNumber *flat = data[@"flat"]; - if (flat != nil) { - [sink setFlat:ToBool(flat)]; + if (flat && flat != (id)[NSNull null]) { + [self setFlat:[flat boolValue]]; } NSNumber *consumeTapEvents = data[@"consumeTapEvents"]; - if (consumeTapEvents != nil) { - [sink setConsumeTapEvents:ToBool(consumeTapEvents)]; + if (consumeTapEvents && consumeTapEvents != (id)[NSNull null]) { + [self setConsumeTapEvents:[consumeTapEvents boolValue]]; } - InterpretInfoWindow(sink, data); + [self interpretInfoWindow:data]; NSArray *position = data[@"position"]; - if (position) { - [sink setPosition:ToLocation(position)]; + if (position && position != (id)[NSNull null]) { + [self setPosition:[FLTGoogleMapJSONConversions locationFromLatLong:position]]; } NSNumber *rotation = data[@"rotation"]; - if (rotation != nil) { - [sink setRotation:ToDouble(rotation)]; + if (rotation && rotation != (id)[NSNull null]) { + [self setRotation:[rotation doubleValue]]; } NSNumber *visible = data[@"visible"]; - if (visible != nil) { - [sink setVisible:ToBool(visible)]; + if (visible && visible != (id)[NSNull null]) { + [self setVisible:[visible boolValue]]; } NSNumber *zIndex = data[@"zIndex"]; - if (zIndex != nil) { - [sink setZIndex:ToInt(zIndex)]; + if (zIndex && zIndex != (id)[NSNull null]) { + [self setZIndex:[zIndex intValue]]; } } -static void InterpretInfoWindow(id sink, NSDictionary *data) { +- (void)interpretInfoWindow:(NSDictionary *)data { NSDictionary *infoWindow = data[@"infoWindow"]; - if (infoWindow) { + if (infoWindow && infoWindow != (id)[NSNull null]) { NSString *title = infoWindow[@"title"]; NSString *snippet = infoWindow[@"snippet"]; - if (title) { - [sink setInfoWindowTitle:title snippet:snippet]; + if (title && title != (id)[NSNull null]) { + [self setInfoWindowTitle:title snippet:snippet]; } NSArray *infoWindowAnchor = infoWindow[@"infoWindowAnchor"]; - if (infoWindowAnchor) { - [sink setInfoWindowAnchor:ToPoint(infoWindowAnchor)]; + if (infoWindowAnchor && infoWindowAnchor != (id)[NSNull null]) { + [self setInfoWindowAnchor:[FLTGoogleMapJSONConversions pointFromArray:infoWindowAnchor]]; } } } -static UIImage *scaleImage(UIImage *image, NSNumber *scaleParam) { - double scale = 1.0; - if ([scaleParam isKindOfClass:[NSNumber class]]) { - scale = scaleParam.doubleValue; - } - if (fabs(scale - 1) > 1e-3) { - return [UIImage imageWithCGImage:[image CGImage] - scale:(image.scale * scale) - orientation:(image.imageOrientation)]; - } - return image; -} - -static UIImage *ExtractIcon(NSObject *registrar, NSArray *iconData) { +- (UIImage *)extractIconFromData:(NSArray *)iconData + registrar:(NSObject *)registrar { UIImage *image; if ([iconData.firstObject isEqualToString:@"defaultMarker"]) { - CGFloat hue = (iconData.count == 1) ? 0.0f : ToDouble(iconData[1]); + CGFloat hue = (iconData.count == 1) ? 0.0f : [iconData[1] doubleValue]; image = [GMSMarker markerImageWithColor:[UIColor colorWithHue:hue / 360.0 saturation:1.0 brightness:0.7 @@ -195,8 +170,8 @@ static void InterpretInfoWindow(id sink, NSDictio } else if ([iconData.firstObject isEqualToString:@"fromAssetImage"]) { if (iconData.count == 3) { image = [UIImage imageNamed:[registrar lookupKeyForAsset:iconData[1]]]; - NSNumber *scaleParam = iconData[2]; - image = scaleImage(image, scaleParam); + id scaleParam = iconData[2]; + image = [self scaleImage:image by:scaleParam]; } else { NSString *error = [NSString stringWithFormat:@"'fromAssetImage' should have exactly 3 arguments. Got: %lu", @@ -231,110 +206,145 @@ static void InterpretInfoWindow(id sink, NSDictio return image; } -@implementation FLTMarkersController { - NSMutableDictionary *_markerIdToController; - FlutterMethodChannel *_methodChannel; - NSObject *_registrar; - GMSMapView *_mapView; +- (UIImage *)scaleImage:(UIImage *)image by:(id)scaleParam { + double scale = 1.0; + if ([scaleParam isKindOfClass:[NSNumber class]]) { + scale = [scaleParam doubleValue]; + } + if (fabs(scale - 1) > 1e-3) { + return [UIImage imageWithCGImage:[image CGImage] + scale:(image.scale * scale) + orientation:(image.imageOrientation)]; + } + return image; } -- (instancetype)init:(FlutterMethodChannel *)methodChannel - mapView:(GMSMapView *)mapView - registrar:(NSObject *)registrar { + +@end + +@interface FLTMarkersController () + +@property(strong, nonatomic) NSMutableDictionary *markerIdentifierToController; +@property(strong, nonatomic) FlutterMethodChannel *methodChannel; +@property(weak, nonatomic) NSObject *registrar; +@property(weak, nonatomic) GMSMapView *mapView; + +@end + +@implementation FLTMarkersController + +- (instancetype)initWithMethodChannel:(FlutterMethodChannel *)methodChannel + mapView:(GMSMapView *)mapView + registrar:(NSObject *)registrar { self = [super init]; if (self) { _methodChannel = methodChannel; _mapView = mapView; - _markerIdToController = [NSMutableDictionary dictionaryWithCapacity:1]; + _markerIdentifierToController = [[NSMutableDictionary alloc] init]; _registrar = registrar; } return self; } + - (void)addMarkers:(NSArray *)markersToAdd { for (NSDictionary *marker in markersToAdd) { CLLocationCoordinate2D position = [FLTMarkersController getPosition:marker]; - NSString *markerId = [FLTMarkersController getMarkerId:marker]; + NSString *identifier = marker[@"markerId"]; FLTGoogleMapMarkerController *controller = [[FLTGoogleMapMarkerController alloc] initMarkerWithPosition:position - markerId:markerId - mapView:_mapView]; - InterpretMarkerOptions(marker, controller, _registrar); - _markerIdToController[markerId] = controller; + identifier:identifier + mapView:self.mapView]; + [controller interpretMarkerOptions:marker registrar:self.registrar]; + self.markerIdentifierToController[identifier] = controller; } } + - (void)changeMarkers:(NSArray *)markersToChange { for (NSDictionary *marker in markersToChange) { - NSString *markerId = [FLTMarkersController getMarkerId:marker]; - FLTGoogleMapMarkerController *controller = _markerIdToController[markerId]; + NSString *identifier = marker[@"markerId"]; + FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[identifier]; if (!controller) { continue; } - InterpretMarkerOptions(marker, controller, _registrar); + [controller interpretMarkerOptions:marker registrar:self.registrar]; } } -- (void)removeMarkerIds:(NSArray *)markerIdsToRemove { - for (NSString *markerId in markerIdsToRemove) { - if (!markerId) { - continue; - } - FLTGoogleMapMarkerController *controller = _markerIdToController[markerId]; + +- (void)removeMarkersWithIdentifiers:(NSArray *)identifiers { + for (NSString *identifier in identifiers) { + FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[identifier]; if (!controller) { continue; } [controller removeMarker]; - [_markerIdToController removeObjectForKey:markerId]; + [self.markerIdentifierToController removeObjectForKey:identifier]; } } -- (BOOL)onMarkerTap:(NSString *)markerId { - if (!markerId) { + +- (BOOL)didTapMarkerWithIdentifier:(NSString *)identifier { + if (!identifier) { return NO; } - FLTGoogleMapMarkerController *controller = _markerIdToController[markerId]; + FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[identifier]; if (!controller) { return NO; } - [_methodChannel invokeMethod:@"marker#onTap" arguments:@{@"markerId" : markerId}]; + [self.methodChannel invokeMethod:@"marker#onTap" arguments:@{@"markerId" : identifier}]; return controller.consumeTapEvents; } -- (void)onMarkerDragStart:(NSString *)markerId coordinate:(CLLocationCoordinate2D)coordinate { - if (!markerId) { + +- (void)didStartDraggingMarkerWithIdentifier:(NSString *)identifier + location:(CLLocationCoordinate2D)location { + if (!identifier) { return; } - FLTGoogleMapMarkerController *controller = _markerIdToController[markerId]; + FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[identifier]; if (!controller) { return; } - [_methodChannel invokeMethod:@"marker#onDragStart" - arguments:@{@"markerId" : markerId, @"position" : PositionToJson(coordinate)}]; + [self.methodChannel invokeMethod:@"marker#onDragStart" + arguments:@{ + @"markerId" : identifier, + @"position" : [FLTGoogleMapJSONConversions arrayFromLocation:location] + }]; } -- (void)onMarkerDrag:(NSString *)markerId coordinate:(CLLocationCoordinate2D)coordinate { - if (!markerId) { + +- (void)didDragMarkerWithIdentifier:(NSString *)identifier + location:(CLLocationCoordinate2D)location { + if (!identifier) { return; } - FLTGoogleMapMarkerController *controller = _markerIdToController[markerId]; + FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[identifier]; if (!controller) { return; } - [_methodChannel invokeMethod:@"marker#onDrag" - arguments:@{@"markerId" : markerId, @"position" : PositionToJson(coordinate)}]; + [self.methodChannel invokeMethod:@"marker#onDrag" + arguments:@{ + @"markerId" : identifier, + @"position" : [FLTGoogleMapJSONConversions arrayFromLocation:location] + }]; } -- (void)onMarkerDragEnd:(NSString *)markerId coordinate:(CLLocationCoordinate2D)coordinate { - if (!markerId) { - return; - } - FLTGoogleMapMarkerController *controller = _markerIdToController[markerId]; + +- (void)didEndDraggingMarkerWithIdentifier:(NSString *)identifier + location:(CLLocationCoordinate2D)location { + FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[identifier]; if (!controller) { return; } - [_methodChannel invokeMethod:@"marker#onDragEnd" - arguments:@{@"markerId" : markerId, @"position" : PositionToJson(coordinate)}]; + [self.methodChannel invokeMethod:@"marker#onDragEnd" + arguments:@{ + @"markerId" : identifier, + @"position" : [FLTGoogleMapJSONConversions arrayFromLocation:location] + }]; } -- (void)onInfoWindowTap:(NSString *)markerId { - if (markerId && _markerIdToController[markerId]) { - [_methodChannel invokeMethod:@"infoWindow#onTap" arguments:@{@"markerId" : markerId}]; + +- (void)didTapInfoWindowOfMarkerWithIdentifier:(NSString *)identifier { + if (identifier && self.markerIdentifierToController[identifier]) { + [self.methodChannel invokeMethod:@"infoWindow#onTap" arguments:@{@"markerId" : identifier}]; } } -- (void)showMarkerInfoWindow:(NSString *)markerId result:(FlutterResult)result { - FLTGoogleMapMarkerController *controller = _markerIdToController[markerId]; + +- (void)showMarkerInfoWindowWithIdentifier:(NSString *)identifier result:(FlutterResult)result { + FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[identifier]; if (controller) { [controller showInfoWindow]; result(nil); @@ -344,8 +354,9 @@ - (void)showMarkerInfoWindow:(NSString *)markerId result:(FlutterResult)result { details:nil]); } } -- (void)hideMarkerInfoWindow:(NSString *)markerId result:(FlutterResult)result { - FLTGoogleMapMarkerController *controller = _markerIdToController[markerId]; + +- (void)hideMarkerInfoWindowWithIdentifier:(NSString *)identifier result:(FlutterResult)result { + FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[identifier]; if (controller) { [controller hideInfoWindow]; result(nil); @@ -355,8 +366,10 @@ - (void)hideMarkerInfoWindow:(NSString *)markerId result:(FlutterResult)result { details:nil]); } } -- (void)isMarkerInfoWindowShown:(NSString *)markerId result:(FlutterResult)result { - FLTGoogleMapMarkerController *controller = _markerIdToController[markerId]; + +- (void)isInfoWindowShownForMarkerWithIdentifier:(NSString *)identifier + result:(FlutterResult)result { + FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[identifier]; if (controller) { result(@([controller isInfoWindowShown])); } else { @@ -368,9 +381,7 @@ - (void)isMarkerInfoWindowShown:(NSString *)markerId result:(FlutterResult)resul + (CLLocationCoordinate2D)getPosition:(NSDictionary *)marker { NSArray *position = marker[@"position"]; - return ToLocation(position); -} -+ (NSString *)getMarkerId:(NSDictionary *)marker { - return marker[@"markerId"]; + return [FLTGoogleMapJSONConversions locationFromLatLong:position]; } + @end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.h b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.h index bdc5dd4bf850..bd0c9110200e 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.h +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.h @@ -5,23 +5,10 @@ #import #import -// Defines polygon UI options writable from Flutter. -@protocol FLTGoogleMapPolygonOptionsSink -- (void)setConsumeTapEvents:(BOOL)consume; -- (void)setVisible:(BOOL)visible; -- (void)setFillColor:(UIColor *)color; -- (void)setStrokeColor:(UIColor *)color; -- (void)setStrokeWidth:(CGFloat)width; -- (void)setPoints:(NSArray *)points; -- (void)setHoles:(NSArray *> *)holes; -- (void)setZIndex:(int)zIndex; -@end - // Defines polygon controllable by Flutter. -@interface FLTGoogleMapPolygonController : NSObject -@property(atomic, readonly) NSString *polygonId; +@interface FLTGoogleMapPolygonController : NSObject - (instancetype)initPolygonWithPath:(GMSMutablePath *)path - polygonId:(NSString *)polygonId + identifier:(NSString *)identifier mapView:(GMSMapView *)mapView; - (void)removePolygon; @end @@ -32,7 +19,7 @@ registrar:(NSObject *)registrar; - (void)addPolygons:(NSArray *)polygonsToAdd; - (void)changePolygons:(NSArray *)polygonsToChange; -- (void)removePolygonIds:(NSArray *)polygonIdsToRemove; -- (void)onPolygonTap:(NSString *)polygonId; -- (bool)hasPolygonWithId:(NSString *)polygonId; +- (void)removePolygonWithIdentifiers:(NSArray *)identifiers; +- (void)didTapPolygonWithIdentifier:(NSString *)identifier; +- (bool)hasPolygonWithIdentifier:(NSString *)identifier; @end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.m index 649ba98bca13..398adfcacecb 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.m +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.m @@ -3,39 +3,41 @@ // found in the LICENSE file. #import "GoogleMapPolygonController.h" -#import "JsonConversions.h" +#import "FLTGoogleMapJSONConversions.h" + +@interface FLTGoogleMapPolygonController () + +@property(strong, nonatomic) GMSPolygon *polygon; +@property(weak, nonatomic) GMSMapView *mapView; + +@end + +@implementation FLTGoogleMapPolygonController -@implementation FLTGoogleMapPolygonController { - GMSPolygon *_polygon; - GMSMapView *_mapView; -} - (instancetype)initPolygonWithPath:(GMSMutablePath *)path - polygonId:(NSString *)polygonId + identifier:(NSString *)identifier mapView:(GMSMapView *)mapView { self = [super init]; if (self) { _polygon = [GMSPolygon polygonWithPath:path]; _mapView = mapView; - _polygonId = polygonId; - _polygon.userData = @[ polygonId ]; + _polygon.userData = @[ identifier ]; } return self; } - (void)removePolygon { - _polygon.map = nil; + self.polygon.map = nil; } -#pragma mark - FLTGoogleMapPolygonOptionsSink methods - - (void)setConsumeTapEvents:(BOOL)consumes { - _polygon.tappable = consumes; + self.polygon.tappable = consumes; } - (void)setVisible:(BOOL)visible { - _polygon.map = visible ? _mapView : nil; + self.polygon.map = visible ? self.mapView : nil; } - (void)setZIndex:(int)zIndex { - _polygon.zIndex = zIndex; + self.polygon.zIndex = zIndex; } - (void)setPoints:(NSArray *)points { GMSMutablePath *path = [GMSMutablePath path]; @@ -43,7 +45,7 @@ - (void)setPoints:(NSArray *)points { for (CLLocation *location in points) { [path addCoordinate:location.coordinate]; } - _polygon.path = path; + self.polygon.path = path; } - (void)setHoles:(NSArray *> *)rawHoles { NSMutableArray *holes = [[NSMutableArray alloc] init]; @@ -56,83 +58,75 @@ - (void)setHoles:(NSArray *> *)rawHoles { [holes addObject:path]; } - _polygon.holes = holes; + self.polygon.holes = holes; } - (void)setFillColor:(UIColor *)color { - _polygon.fillColor = color; + self.polygon.fillColor = color; } - (void)setStrokeColor:(UIColor *)color { - _polygon.strokeColor = color; + self.polygon.strokeColor = color; } - (void)setStrokeWidth:(CGFloat)width { - _polygon.strokeWidth = width; + self.polygon.strokeWidth = width; } -@end - -static int ToInt(NSNumber *data) { return [FLTGoogleMapJsonConversions toInt:data]; } -static BOOL ToBool(NSNumber *data) { return [FLTGoogleMapJsonConversions toBool:data]; } - -static NSArray *ToPoints(NSArray *data) { - return [FLTGoogleMapJsonConversions toPoints:data]; -} - -static NSArray *> *ToHoles(NSArray *data) { - return [FLTGoogleMapJsonConversions toHoles:data]; -} - -static UIColor *ToColor(NSNumber *data) { return [FLTGoogleMapJsonConversions toColor:data]; } - -static void InterpretPolygonOptions(NSDictionary *data, id sink, - NSObject *registrar) { +- (void)interpretPolygonOptions:(NSDictionary *)data + registrar:(NSObject *)registrar { NSNumber *consumeTapEvents = data[@"consumeTapEvents"]; - if (consumeTapEvents != nil) { - [sink setConsumeTapEvents:ToBool(consumeTapEvents)]; + if (consumeTapEvents && consumeTapEvents != (id)[NSNull null]) { + [self setConsumeTapEvents:[consumeTapEvents boolValue]]; } NSNumber *visible = data[@"visible"]; - if (visible != nil) { - [sink setVisible:ToBool(visible)]; + if (visible && visible != (id)[NSNull null]) { + [self setVisible:[visible boolValue]]; } NSNumber *zIndex = data[@"zIndex"]; - if (zIndex != nil) { - [sink setZIndex:ToInt(zIndex)]; + if (zIndex && zIndex != (id)[NSNull null]) { + [self setZIndex:[zIndex intValue]]; } NSArray *points = data[@"points"]; - if (points) { - [sink setPoints:ToPoints(points)]; + if (points && points != (id)[NSNull null]) { + [self setPoints:[FLTGoogleMapJSONConversions pointsFromLatLongs:points]]; } NSArray *holes = data[@"holes"]; - if (holes) { - [sink setHoles:ToHoles(holes)]; + if (holes && holes != (id)[NSNull null]) { + [self setHoles:[FLTGoogleMapJSONConversions holesFromPointsArray:holes]]; } NSNumber *fillColor = data[@"fillColor"]; - if (fillColor != nil) { - [sink setFillColor:ToColor(fillColor)]; + if (fillColor && fillColor != (id)[NSNull null]) { + [self setFillColor:[FLTGoogleMapJSONConversions colorFromRGBA:fillColor]]; } NSNumber *strokeColor = data[@"strokeColor"]; - if (strokeColor != nil) { - [sink setStrokeColor:ToColor(strokeColor)]; + if (strokeColor && strokeColor != (id)[NSNull null]) { + [self setStrokeColor:[FLTGoogleMapJSONConversions colorFromRGBA:strokeColor]]; } NSNumber *strokeWidth = data[@"strokeWidth"]; - if (strokeWidth != nil) { - [sink setStrokeWidth:ToInt(strokeWidth)]; + if (strokeWidth && strokeWidth != (id)[NSNull null]) { + [self setStrokeWidth:[strokeWidth intValue]]; } } -@implementation FLTPolygonsController { - NSMutableDictionary *_polygonIdToController; - FlutterMethodChannel *_methodChannel; - NSObject *_registrar; - GMSMapView *_mapView; -} +@end + +@interface FLTPolygonsController () + +@property(strong, nonatomic) NSMutableDictionary *polygonIdentifierToController; +@property(strong, nonatomic) FlutterMethodChannel *methodChannel; +@property(weak, nonatomic) NSObject *registrar; +@property(weak, nonatomic) GMSMapView *mapView; + +@end + +@implementation FLTPolygonsController + - (instancetype)init:(FlutterMethodChannel *)methodChannel mapView:(GMSMapView *)mapView registrar:(NSObject *)registrar { @@ -140,72 +134,73 @@ - (instancetype)init:(FlutterMethodChannel *)methodChannel if (self) { _methodChannel = methodChannel; _mapView = mapView; - _polygonIdToController = [NSMutableDictionary dictionaryWithCapacity:1]; + _polygonIdentifierToController = [NSMutableDictionary dictionaryWithCapacity:1]; _registrar = registrar; } return self; } + - (void)addPolygons:(NSArray *)polygonsToAdd { for (NSDictionary *polygon in polygonsToAdd) { GMSMutablePath *path = [FLTPolygonsController getPath:polygon]; - NSString *polygonId = [FLTPolygonsController getPolygonId:polygon]; + NSString *identifier = polygon[@"polygonId"]; FLTGoogleMapPolygonController *controller = [[FLTGoogleMapPolygonController alloc] initPolygonWithPath:path - polygonId:polygonId - mapView:_mapView]; - InterpretPolygonOptions(polygon, controller, _registrar); - _polygonIdToController[polygonId] = controller; + identifier:identifier + mapView:self.mapView]; + [controller interpretPolygonOptions:polygon registrar:self.registrar]; + self.polygonIdentifierToController[identifier] = controller; } } + - (void)changePolygons:(NSArray *)polygonsToChange { for (NSDictionary *polygon in polygonsToChange) { - NSString *polygonId = [FLTPolygonsController getPolygonId:polygon]; - FLTGoogleMapPolygonController *controller = _polygonIdToController[polygonId]; + NSString *identifier = polygon[@"polygonId"]; + FLTGoogleMapPolygonController *controller = self.polygonIdentifierToController[identifier]; if (!controller) { continue; } - InterpretPolygonOptions(polygon, controller, _registrar); + [controller interpretPolygonOptions:polygon registrar:self.registrar]; } } -- (void)removePolygonIds:(NSArray *)polygonIdsToRemove { - for (NSString *polygonId in polygonIdsToRemove) { - if (!polygonId) { - continue; - } - FLTGoogleMapPolygonController *controller = _polygonIdToController[polygonId]; + +- (void)removePolygonWithIdentifiers:(NSArray *)identifiers { + for (NSString *identifier in identifiers) { + FLTGoogleMapPolygonController *controller = self.polygonIdentifierToController[identifier]; if (!controller) { continue; } [controller removePolygon]; - [_polygonIdToController removeObjectForKey:polygonId]; + [self.polygonIdentifierToController removeObjectForKey:identifier]; } } -- (void)onPolygonTap:(NSString *)polygonId { - if (!polygonId) { + +- (void)didTapPolygonWithIdentifier:(NSString *)identifier { + if (!identifier) { return; } - FLTGoogleMapPolygonController *controller = _polygonIdToController[polygonId]; + FLTGoogleMapPolygonController *controller = self.polygonIdentifierToController[identifier]; if (!controller) { return; } - [_methodChannel invokeMethod:@"polygon#onTap" arguments:@{@"polygonId" : polygonId}]; + [self.methodChannel invokeMethod:@"polygon#onTap" arguments:@{@"polygonId" : identifier}]; } -- (bool)hasPolygonWithId:(NSString *)polygonId { - if (!polygonId) { + +- (bool)hasPolygonWithIdentifier:(NSString *)identifier { + if (!identifier) { return false; } - return _polygonIdToController[polygonId] != nil; + return self.polygonIdentifierToController[identifier] != nil; } + + (GMSMutablePath *)getPath:(NSDictionary *)polygon { NSArray *pointArray = polygon[@"points"]; - NSArray *points = ToPoints(pointArray); + NSArray *points = [FLTGoogleMapJSONConversions pointsFromLatLongs:pointArray]; GMSMutablePath *path = [GMSMutablePath path]; for (CLLocation *location in points) { [path addCoordinate:location.coordinate]; } return path; } -+ (NSString *)getPolygonId:(NSDictionary *)polygon { - return polygon[@"polygonId"]; -} + @end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.h b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.h index 0e614eeb62ab..f85d1a3896fa 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.h +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.h @@ -5,22 +5,10 @@ #import #import -// Defines polyline UI options writable from Flutter. -@protocol FLTGoogleMapPolylineOptionsSink -- (void)setConsumeTapEvents:(BOOL)consume; -- (void)setVisible:(BOOL)visible; -- (void)setColor:(UIColor *)color; -- (void)setStrokeWidth:(CGFloat)width; -- (void)setPoints:(NSArray *)points; -- (void)setZIndex:(int)zIndex; -- (void)setGeodesic:(BOOL)isGeodesic; -@end - // Defines polyline controllable by Flutter. -@interface FLTGoogleMapPolylineController : NSObject -@property(atomic, readonly) NSString *polylineId; +@interface FLTGoogleMapPolylineController : NSObject - (instancetype)initPolylineWithPath:(GMSMutablePath *)path - polylineId:(NSString *)polylineId + identifier:(NSString *)identifier mapView:(GMSMapView *)mapView; - (void)removePolyline; @end @@ -31,7 +19,7 @@ registrar:(NSObject *)registrar; - (void)addPolylines:(NSArray *)polylinesToAdd; - (void)changePolylines:(NSArray *)polylinesToChange; -- (void)removePolylineIds:(NSArray *)polylineIdsToRemove; -- (void)onPolylineTap:(NSString *)polylineId; -- (bool)hasPolylineWithId:(NSString *)polylineId; +- (void)removePolylineWithIdentifiers:(NSArray *)identifiers; +- (void)didTapPolylineWithIdentifier:(NSString *)identifier; +- (bool)hasPolylineWithIdentifier:(NSString *)identifier; @end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.m index f366051b4af2..77601d4a1bb5 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.m +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.m @@ -3,39 +3,41 @@ // found in the LICENSE file. #import "GoogleMapPolylineController.h" -#import "JsonConversions.h" +#import "FLTGoogleMapJSONConversions.h" + +@interface FLTGoogleMapPolylineController () + +@property(strong, nonatomic) GMSPolyline *polyline; +@property(weak, nonatomic) GMSMapView *mapView; + +@end + +@implementation FLTGoogleMapPolylineController -@implementation FLTGoogleMapPolylineController { - GMSPolyline *_polyline; - GMSMapView *_mapView; -} - (instancetype)initPolylineWithPath:(GMSMutablePath *)path - polylineId:(NSString *)polylineId + identifier:(NSString *)identifier mapView:(GMSMapView *)mapView { self = [super init]; if (self) { _polyline = [GMSPolyline polylineWithPath:path]; _mapView = mapView; - _polylineId = polylineId; - _polyline.userData = @[ polylineId ]; + _polyline.userData = @[ identifier ]; } return self; } - (void)removePolyline { - _polyline.map = nil; + self.polyline.map = nil; } -#pragma mark - FLTGoogleMapPolylineOptionsSink methods - - (void)setConsumeTapEvents:(BOOL)consumes { - _polyline.tappable = consumes; + self.polyline.tappable = consumes; } - (void)setVisible:(BOOL)visible { - _polyline.map = visible ? _mapView : nil; + self.polyline.map = visible ? self.mapView : nil; } - (void)setZIndex:(int)zIndex { - _polyline.zIndex = zIndex; + self.polyline.zIndex = zIndex; } - (void)setPoints:(NSArray *)points { GMSMutablePath *path = [GMSMutablePath path]; @@ -43,75 +45,72 @@ - (void)setPoints:(NSArray *)points { for (CLLocation *location in points) { [path addCoordinate:location.coordinate]; } - _polyline.path = path; + self.polyline.path = path; } - (void)setColor:(UIColor *)color { - _polyline.strokeColor = color; + self.polyline.strokeColor = color; } - (void)setStrokeWidth:(CGFloat)width { - _polyline.strokeWidth = width; + self.polyline.strokeWidth = width; } - (void)setGeodesic:(BOOL)isGeodesic { - _polyline.geodesic = isGeodesic; + self.polyline.geodesic = isGeodesic; } -@end - -static int ToInt(NSNumber *data) { return [FLTGoogleMapJsonConversions toInt:data]; } - -static BOOL ToBool(NSNumber *data) { return [FLTGoogleMapJsonConversions toBool:data]; } - -static NSArray *ToPoints(NSArray *data) { - return [FLTGoogleMapJsonConversions toPoints:data]; -} - -static UIColor *ToColor(NSNumber *data) { return [FLTGoogleMapJsonConversions toColor:data]; } -static void InterpretPolylineOptions(NSDictionary *data, id sink, - NSObject *registrar) { +- (void)interpretPolylineOptions:(NSDictionary *)data + registrar:(NSObject *)registrar { NSNumber *consumeTapEvents = data[@"consumeTapEvents"]; - if (consumeTapEvents != nil) { - [sink setConsumeTapEvents:ToBool(consumeTapEvents)]; + if (consumeTapEvents && consumeTapEvents != (id)[NSNull null]) { + [self setConsumeTapEvents:[consumeTapEvents boolValue]]; } NSNumber *visible = data[@"visible"]; - if (visible != nil) { - [sink setVisible:ToBool(visible)]; + if (visible && visible != (id)[NSNull null]) { + [self setVisible:[visible boolValue]]; } NSNumber *zIndex = data[@"zIndex"]; - if (zIndex != nil) { - [sink setZIndex:ToInt(zIndex)]; + if (zIndex && zIndex != (id)[NSNull null]) { + [self setZIndex:[zIndex intValue]]; } NSArray *points = data[@"points"]; - if (points) { - [sink setPoints:ToPoints(points)]; + if (points && points != (id)[NSNull null]) { + [self setPoints:[FLTGoogleMapJSONConversions pointsFromLatLongs:points]]; } NSNumber *strokeColor = data[@"color"]; - if (strokeColor != nil) { - [sink setColor:ToColor(strokeColor)]; + if (strokeColor && strokeColor != (id)[NSNull null]) { + [self setColor:[FLTGoogleMapJSONConversions colorFromRGBA:strokeColor]]; } NSNumber *strokeWidth = data[@"width"]; - if (strokeWidth != nil) { - [sink setStrokeWidth:ToInt(strokeWidth)]; + if (strokeWidth && strokeWidth != (id)[NSNull null]) { + [self setStrokeWidth:[strokeWidth intValue]]; } NSNumber *geodesic = data[@"geodesic"]; - if (geodesic != nil) { - [sink setGeodesic:geodesic.boolValue]; + if (geodesic && geodesic != (id)[NSNull null]) { + [self setGeodesic:geodesic.boolValue]; } } -@implementation FLTPolylinesController { - NSMutableDictionary *_polylineIdToController; - FlutterMethodChannel *_methodChannel; - NSObject *_registrar; - GMSMapView *_mapView; -} +@end + +@interface FLTPolylinesController () + +@property(strong, nonatomic) NSMutableDictionary *polylineIdentifierToController; +@property(strong, nonatomic) FlutterMethodChannel *methodChannel; +@property(weak, nonatomic) NSObject *registrar; +@property(weak, nonatomic) GMSMapView *mapView; + +@end +; + +@implementation FLTPolylinesController + - (instancetype)init:(FlutterMethodChannel *)methodChannel mapView:(GMSMapView *)mapView registrar:(NSObject *)registrar { @@ -119,7 +118,7 @@ - (instancetype)init:(FlutterMethodChannel *)methodChannel if (self) { _methodChannel = methodChannel; _mapView = mapView; - _polylineIdToController = [NSMutableDictionary dictionaryWithCapacity:1]; + _polylineIdentifierToController = [NSMutableDictionary dictionaryWithCapacity:1]; _registrar = registrar; } return self; @@ -127,64 +126,59 @@ - (instancetype)init:(FlutterMethodChannel *)methodChannel - (void)addPolylines:(NSArray *)polylinesToAdd { for (NSDictionary *polyline in polylinesToAdd) { GMSMutablePath *path = [FLTPolylinesController getPath:polyline]; - NSString *polylineId = [FLTPolylinesController getPolylineId:polyline]; + NSString *identifier = polyline[@"polylineId"]; FLTGoogleMapPolylineController *controller = [[FLTGoogleMapPolylineController alloc] initPolylineWithPath:path - polylineId:polylineId - mapView:_mapView]; - InterpretPolylineOptions(polyline, controller, _registrar); - _polylineIdToController[polylineId] = controller; + identifier:identifier + mapView:self.mapView]; + [controller interpretPolylineOptions:polyline registrar:self.registrar]; + self.polylineIdentifierToController[identifier] = controller; } } - (void)changePolylines:(NSArray *)polylinesToChange { for (NSDictionary *polyline in polylinesToChange) { - NSString *polylineId = [FLTPolylinesController getPolylineId:polyline]; - FLTGoogleMapPolylineController *controller = _polylineIdToController[polylineId]; + NSString *identifier = polyline[@"polylineId"]; + FLTGoogleMapPolylineController *controller = self.polylineIdentifierToController[identifier]; if (!controller) { continue; } - InterpretPolylineOptions(polyline, controller, _registrar); + [controller interpretPolylineOptions:polyline registrar:self.registrar]; } } -- (void)removePolylineIds:(NSArray *)polylineIdsToRemove { - for (NSString *polylineId in polylineIdsToRemove) { - if (!polylineId) { - continue; - } - FLTGoogleMapPolylineController *controller = _polylineIdToController[polylineId]; +- (void)removePolylineWithIdentifiers:(NSArray *)identifiers { + for (NSString *identifier in identifiers) { + FLTGoogleMapPolylineController *controller = self.polylineIdentifierToController[identifier]; if (!controller) { continue; } [controller removePolyline]; - [_polylineIdToController removeObjectForKey:polylineId]; + [self.polylineIdentifierToController removeObjectForKey:identifier]; } } -- (void)onPolylineTap:(NSString *)polylineId { - if (!polylineId) { +- (void)didTapPolylineWithIdentifier:(NSString *)identifier { + if (!identifier) { return; } - FLTGoogleMapPolylineController *controller = _polylineIdToController[polylineId]; + FLTGoogleMapPolylineController *controller = self.polylineIdentifierToController[identifier]; if (!controller) { return; } - [_methodChannel invokeMethod:@"polyline#onTap" arguments:@{@"polylineId" : polylineId}]; + [self.methodChannel invokeMethod:@"polyline#onTap" arguments:@{@"polylineId" : identifier}]; } -- (bool)hasPolylineWithId:(NSString *)polylineId { - if (!polylineId) { +- (bool)hasPolylineWithIdentifier:(NSString *)identifier { + if (!identifier) { return false; } - return _polylineIdToController[polylineId] != nil; + return self.polylineIdentifierToController[identifier] != nil; } + (GMSMutablePath *)getPath:(NSDictionary *)polyline { NSArray *pointArray = polyline[@"points"]; - NSArray *points = ToPoints(pointArray); + NSArray *points = [FLTGoogleMapJSONConversions pointsFromLatLongs:pointArray]; GMSMutablePath *path = [GMSMutablePath path]; for (CLLocation *location in points) { [path addCoordinate:location.coordinate]; } return path; } -+ (NSString *)getPolylineId:(NSDictionary *)polyline { - return polyline[@"polylineId"]; -} + @end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/JsonConversions.h b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/JsonConversions.h deleted file mode 100644 index c0f673ecd025..000000000000 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/JsonConversions.h +++ /dev/null @@ -1,19 +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 -#import - -@interface FLTGoogleMapJsonConversions : NSObject -+ (bool)toBool:(NSNumber *)data; -+ (int)toInt:(NSNumber *)data; -+ (double)toDouble:(NSNumber *)data; -+ (float)toFloat:(NSNumber *)data; -+ (CLLocationCoordinate2D)toLocation:(NSArray *)data; -+ (CGPoint)toPoint:(NSArray *)data; -+ (NSArray *)positionToJson:(CLLocationCoordinate2D)position; -+ (UIColor *)toColor:(NSNumber *)data; -+ (NSArray *)toPoints:(NSArray *)data; -+ (NSArray *> *)toHoles:(NSArray *)data; -@end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/JsonConversions.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/JsonConversions.m deleted file mode 100644 index 0e88d4707489..000000000000 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/JsonConversions.m +++ /dev/null @@ -1,71 +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 "JsonConversions.h" - -@implementation FLTGoogleMapJsonConversions - -+ (bool)toBool:(NSNumber *)data { - return data.boolValue; -} - -+ (int)toInt:(NSNumber *)data { - return data.intValue; -} - -+ (double)toDouble:(NSNumber *)data { - return data.doubleValue; -} - -+ (float)toFloat:(NSNumber *)data { - return data.floatValue; -} - -+ (CLLocationCoordinate2D)toLocation:(NSArray *)data { - return CLLocationCoordinate2DMake([FLTGoogleMapJsonConversions toDouble:data[0]], - [FLTGoogleMapJsonConversions toDouble:data[1]]); -} - -+ (CGPoint)toPoint:(NSArray *)data { - return CGPointMake([FLTGoogleMapJsonConversions toDouble:data[0]], - [FLTGoogleMapJsonConversions toDouble:data[1]]); -} - -+ (NSArray *)positionToJson:(CLLocationCoordinate2D)position { - return @[ @(position.latitude), @(position.longitude) ]; -} - -+ (UIColor *)toColor:(NSNumber *)numberColor { - unsigned long value = [numberColor unsignedLongValue]; - return [UIColor colorWithRed:((float)((value & 0xFF0000) >> 16)) / 255.0 - green:((float)((value & 0xFF00) >> 8)) / 255.0 - blue:((float)(value & 0xFF)) / 255.0 - alpha:((float)((value & 0xFF000000) >> 24)) / 255.0]; -} - -+ (NSArray *)toPoints:(NSArray *)data { - NSMutableArray *points = [[NSMutableArray alloc] init]; - for (unsigned i = 0; i < [data count]; i++) { - NSNumber *latitude = data[i][0]; - NSNumber *longitude = data[i][1]; - CLLocation *point = - [[CLLocation alloc] initWithLatitude:[FLTGoogleMapJsonConversions toDouble:latitude] - longitude:[FLTGoogleMapJsonConversions toDouble:longitude]]; - [points addObject:point]; - } - - return points; -} - -+ (NSArray *> *)toHoles:(NSArray *)data { - NSMutableArray *> *holes = [[[NSMutableArray alloc] init] init]; - for (unsigned i = 0; i < [data count]; i++) { - NSArray *points = [FLTGoogleMapJsonConversions toPoints:data[i]]; - [holes addObject:points]; - } - - return holes; -} - -@end diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter-umbrella.h b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter-umbrella.h index 50880a2b9e9d..9969e716c26b 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter-umbrella.h +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter-umbrella.h @@ -3,9 +3,9 @@ // found in the LICENSE file. #import +#import #import #import -#import FOUNDATION_EXPORT double google_maps_flutterVersionNumber; FOUNDATION_EXPORT const unsigned char google_maps_flutterVersionString[]; diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index 59ee23d0b260..215a930c91b9 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/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.1.6 +version: 2.1.7 environment: sdk: ">=2.14.0 <3.0.0" From 7cad5998706043997a6bb046061e818ccbaf3cf6 Mon Sep 17 00:00:00 2001 From: godofredoc Date: Wed, 1 Jun 2022 13:48:16 -0700 Subject: [PATCH 375/844] All the workflows have been migrated to use main. (#5874) Master branch is getting archived. Bug: flutter/flutter#90476 --- .github/workflows/mirror.yml | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 .github/workflows/mirror.yml diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml deleted file mode 100644 index 9cfaf20d6cf0..000000000000 --- a/.github/workflows/mirror.yml +++ /dev/null @@ -1,28 +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. - -# Mirror master to main branches in the plugins repository. -on: - push: - branches: - - 'main' - -# Declare default permissions as read only. -permissions: read-all - -jobs: - mirror_job: - permissions: - pull-requests: write - runs-on: ubuntu-latest - if: ${{ github.repository == 'flutter/plugins' }} - name: Mirror main branch to master branch - steps: - - name: Mirror action step - id: mirror - uses: google/mirror-branch-action@c6b07e441a7ffc5ae15860c1d0a8107a3a151db8 - with: - github-token: ${{ secrets.FLUTTERMIRRORINGBOT_TOKEN }} - source: 'main' - dest: 'master' From 5ed15e5ceba01f32422d0941f0b7f8ac5a92d24c Mon Sep 17 00:00:00 2001 From: keyonghan <54558023+keyonghan@users.noreply.github.com> Date: Thu, 2 Jun 2022 10:22:46 -0700 Subject: [PATCH 376/844] update key (#5882) --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index a81f24a31035..c786a4a14418 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -232,7 +232,7 @@ task: CHANNEL: "master" CHANNEL: "stable" MAPS_API_KEY: ENCRYPTED[596a9f6bca436694625ac50851dc5da6b4d34cba8025f7db5bc9465142e8cd44e15f69e3507787753accebfc4910d550] - GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[4c11f1a80a5741d51e92ab609bc7214ab2aa015e68a490e4d6777ebdf84f9c899b97c0ded2f4b2e6adf2c8b5ead1e3c5] + GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[c84a06b85f9c906705732aea6142ef6f63ff1a6f07372dc36880a9d0c2c4b9cb35b2e35cd19edc6285167c2a5cc075ec] build_script: # Unsetting CIRRUS_CHANGE_MESSAGE and CIRRUS_COMMIT_MESSAGE as they # might include non-ASCII characters which makes Gradle crash. From f4ea8e9c26e452402152ee16e215b3a99ea4de8a Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 2 Jun 2022 16:50:14 -0700 Subject: [PATCH 377/844] [image_picker_android] Remove `jetifier` and `enableUnitTestBinaryResources` from gradle properties (#5889) --- .../image_picker_android/example/android/gradle.properties | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/image_picker/image_picker_android/example/android/gradle.properties b/packages/image_picker/image_picker_android/example/android/gradle.properties index 6effed032590..d12b9a8297e5 100755 --- a/packages/image_picker/image_picker_android/example/android/gradle.properties +++ b/packages/image_picker/image_picker_android/example/android/gradle.properties @@ -1,5 +1,3 @@ org.gradle.jvmargs=-Xmx1536M android.enableR8=true android.useAndroidX=true -android.enableJetifier=true -android.enableUnitTestBinaryResources=true From f98c9c72492d9fb4809eea21cda36798f10cf0c3 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 2 Jun 2022 17:38:12 -0700 Subject: [PATCH 378/844] [webview_flutter_wkwebview] Implement one callback method for review of the design (#5700) --- .../ios/RunnerTests/FWFInstanceManagerTests.m | 28 +- .../FWFNavigationDelegateHostApiTests.m | 35 +- .../ios/RunnerTests/FWFObjectHostApiTests.m | 5 +- .../ios/Classes/FWFGeneratedWebKitApis.h | 13 +- .../ios/Classes/FWFGeneratedWebKitApis.m | 67 +-- .../ios/Classes/FWFInstanceManager.h | 65 +- .../ios/Classes/FWFInstanceManager.m | 143 ++++- .../ios/Classes/FWFInstanceManager_Test.h | 26 + .../Classes/FWFNavigationDelegateHostApi.h | 18 +- .../Classes/FWFNavigationDelegateHostApi.m | 70 ++- .../ios/Classes/FWFObjectHostApi.h | 19 + .../ios/Classes/FWFObjectHostApi.m | 31 +- .../ios/Classes/FlutterWebView.modulemap | 1 + .../common/function_flutter_api_impls.dart | 25 - .../lib/src/common/instance_manager.dart | 204 ++++++- .../lib/src/common/web_kit.pigeon.dart | 69 +-- .../lib/src/foundation/foundation.dart | 50 +- .../src/foundation/foundation_api_impls.dart | 50 +- .../lib/src/ui_kit/ui_kit.dart | 4 + .../lib/src/ui_kit/ui_kit_api_impls.dart | 6 +- .../lib/src/web_kit/web_kit.dart | 69 ++- .../lib/src/web_kit/web_kit_api_impls.dart | 83 +-- .../lib/src/web_kit_webview_widget.dart | 19 +- .../pigeons/web_kit.dart | 18 +- .../src/common/function_flutter_api_test.dart | 34 -- .../src/common/instance_manager_test.dart | 149 ++++- .../test/src/common/test_web_kit.pigeon.dart | 22 - .../test/src/foundation/foundation_test.dart | 17 +- .../test/src/ui_kit/ui_kit_test.dart | 2 +- .../test/src/web_kit/web_kit_test.dart | 30 +- .../test/src/web_kit/web_kit_test.mocks.dart | 10 +- .../web_kit_cookie_manager_test.mocks.dart | 81 ++- .../test/src/web_kit_webview_widget_test.dart | 11 +- .../web_kit_webview_widget_test.mocks.dart | 556 +++++++++++------- 34 files changed, 1343 insertions(+), 687 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager_Test.h delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/function_flutter_api_impls.dart delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m index 264b623dd8cf..2ad4bd48b2e8 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m @@ -3,38 +3,40 @@ // found in the LICENSE file. #import + @import webview_flutter_wkwebview; +@import webview_flutter_wkwebview.Test; @interface FWFInstanceManagerTests : XCTestCase @end @implementation FWFInstanceManagerTests -- (void)testAddInstanceCreatedFromDart { +- (void)testAddDartCreatedInstance { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; NSObject *object = [[NSObject alloc] init]; - [instanceManager addDartCreatedInstance:object withIdentifier:5]; - XCTAssertEqualObjects([instanceManager instanceForIdentifier:5], object); - XCTAssertEqual([instanceManager identifierForInstance:object], 5); + [instanceManager addDartCreatedInstance:object withIdentifier:0]; + XCTAssertEqualObjects([instanceManager instanceForIdentifier:0], object); + XCTAssertEqual([instanceManager identifierWithStrongReferenceForInstance:object], 0); } -- (void)testRemoveInstance { +- (void)testAddHostCreatedInstance { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; NSObject *object = [[NSObject alloc] init]; - [instanceManager addDartCreatedInstance:object withIdentifier:5]; + [instanceManager addHostCreatedInstance:object]; - [instanceManager removeInstance:object]; - XCTAssertNil([instanceManager instanceForIdentifier:5]); - XCTAssertEqual([instanceManager identifierForInstance:object], NSNotFound); + long identifier = [instanceManager identifierWithStrongReferenceForInstance:object]; + XCTAssertNotEqual(identifier, NSNotFound); + XCTAssertEqualObjects([instanceManager instanceForIdentifier:identifier], object); } - (void)testRemoveInstanceWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; NSObject *object = [[NSObject alloc] init]; - [instanceManager addDartCreatedInstance:object withIdentifier:5]; - [instanceManager removeInstanceWithIdentifier:5]; - XCTAssertNil([instanceManager instanceForIdentifier:5]); - XCTAssertEqual([instanceManager identifierForInstance:object], NSNotFound); + [instanceManager addDartCreatedInstance:object withIdentifier:0]; + + XCTAssertEqualObjects([instanceManager removeInstanceWithIdentifier:0], object); + XCTAssertEqual([instanceManager strongInstanceCount], 0); } @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 9025b2e5ce43..206137e301f5 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 @@ -14,8 +14,9 @@ @interface FWFNavigationDelegateHostApiTests : XCTestCase @implementation FWFNavigationDelegateHostApiTests - (void)testCreateWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - FWFNavigationDelegateHostApiImpl *hostAPI = - [[FWFNavigationDelegateHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFNavigationDelegateHostApiImpl *hostAPI = [[FWFNavigationDelegateHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; [hostAPI createWithIdentifier:@0 error:&error]; @@ -25,4 +26,34 @@ - (void)testCreateWithIdentifier { XCTAssertTrue([navigationDelegate conformsToProtocol:@protocol(WKNavigationDelegate)]); XCTAssertNil(error); } + +- (void)testDidFinishNavigation { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFNavigationDelegateHostApiImpl *hostAPI = [[FWFNavigationDelegateHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; + + FlutterError *error; + [hostAPI createWithIdentifier:@0 error:&error]; + FWFNavigationDelegate *navigationDelegate = + (FWFNavigationDelegate *)[instanceManager instanceForIdentifier:0]; + id mockDelegate = OCMPartialMock(navigationDelegate); + + FWFNavigationDelegateFlutterApiImpl *flutterAPI = [[FWFNavigationDelegateFlutterApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; + id mockFlutterApi = OCMPartialMock(flutterAPI); + + OCMStub([mockDelegate navigationDelegateAPI]).andReturn(mockFlutterApi); + + WKWebView *mockWebView = OCMClassMock([WKWebView class]); + OCMStub([mockWebView URL]).andReturn([NSURL URLWithString:@"https://flutter.dev/"]); + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:1]; + + [mockDelegate webView:mockWebView didFinishNavigation:OCMClassMock([WKNavigation class])]; + OCMVerify([mockFlutterApi didFinishNavigationForDelegateWithIdentifier:@0 + webViewIdentifier:@1 + URL:@"https://flutter.dev/" + completion:OCMOCK_ANY]); +} @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m index 271a1d0eb696..bdaeae4c09dc 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m @@ -76,7 +76,10 @@ - (void)testDispose { FlutterError *error; [hostAPI disposeObjectWithIdentifier:@0 error:&error]; - XCTAssertEqual([instanceManager identifierForInstance:object], NSNotFound); + // Only the strong reference is removed, so the weak reference will remain until object is set to + // nil. + object = nil; + XCTAssertFalse([instanceManager containsInstance:object]); XCTAssertNil(error); } @end 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 3617b4ecc731..b291f4167725 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h @@ -303,9 +303,6 @@ NSObject *FWFWKNavigationDelegateHostApiGetCodec(void); @protocol FWFWKNavigationDelegateHostApi - (void)createWithIdentifier:(NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error; -- (void)setDidFinishNavigationForDelegateWithIdentifier:(NSNumber *)identifier - functionIdentifier:(nullable NSNumber *)functionIdentifier - error:(FlutterError *_Nullable *_Nonnull)error; @end extern void FWFWKNavigationDelegateHostApiSetup( @@ -317,7 +314,7 @@ NSObject *FWFWKNavigationDelegateFlutterApiGetCodec(void); @interface FWFWKNavigationDelegateFlutterApi : NSObject - (instancetype)initWithBinaryMessenger:(id)binaryMessenger; -- (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)functionIdentifier +- (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)identifier webViewIdentifier:(NSNumber *)webViewIdentifier URL:(nullable NSString *)url completion:(void (^)(NSError *_Nullable))completion; @@ -343,13 +340,11 @@ NSObject *FWFNSObjectHostApiGetCodec(void); extern void FWFNSObjectHostApiSetup(id binaryMessenger, NSObject *_Nullable api); -/// The codec used by FWFFunctionFlutterApi. -NSObject *FWFFunctionFlutterApiGetCodec(void); +/// The codec used by FWFNSObjectFlutterApi. +NSObject *FWFNSObjectFlutterApiGetCodec(void); -@interface FWFFunctionFlutterApi : NSObject +@interface FWFNSObjectFlutterApi : NSObject - (instancetype)initWithBinaryMessenger:(id)binaryMessenger; -- (void)disposeFunctionWithIdentifier:(NSNumber *)identifier - completion:(void (^)(NSError *_Nullable))completion; @end /// The codec used by FWFWKWebViewHostApi. NSObject *FWFWKWebViewHostApiGetCodec(void); 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 1d20cb350175..f936d151ddd3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m @@ -1150,32 +1150,6 @@ void FWFWKNavigationDelegateHostApiSetup(id binaryMessen [channel setMessageHandler:nil]; } } - { - FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation" - binaryMessenger:binaryMessenger - codec:FWFWKNavigationDelegateHostApiGetCodec()]; - if (api) { - NSCAssert( - [api respondsToSelector:@selector - (setDidFinishNavigationForDelegateWithIdentifier:functionIdentifier:error:)], - @"FWFWKNavigationDelegateHostApi api (%@) doesn't respond to " - @"@selector(setDidFinishNavigationForDelegateWithIdentifier:functionIdentifier:error:)", - api); - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; - NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); - NSNumber *arg_functionIdentifier = GetNullableObjectAtIndex(args, 1); - FlutterError *error; - [api setDidFinishNavigationForDelegateWithIdentifier:arg_identifier - functionIdentifier:arg_functionIdentifier - error:&error]; - callback(wrapResult(nil, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } } @interface FWFWKNavigationDelegateFlutterApiCodecReader : FlutterStandardReader @end @@ -1222,7 +1196,7 @@ - (instancetype)initWithBinaryMessenger:(NSObject *)bina } return self; } -- (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)arg_functionIdentifier +- (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)arg_identifier webViewIdentifier:(NSNumber *)arg_webViewIdentifier URL:(nullable NSString *)arg_url completion:(void (^)(NSError *_Nullable))completion { @@ -1232,7 +1206,7 @@ - (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)arg_functionIde binaryMessenger:self.binaryMessenger codec:FWFWKNavigationDelegateFlutterApiGetCodec()]; [channel sendMessage:@[ - arg_functionIdentifier ?: [NSNull null], arg_webViewIdentifier ?: [NSNull null], + arg_identifier ?: [NSNull null], arg_webViewIdentifier ?: [NSNull null], arg_url ?: [NSNull null] ] reply:^(id reply) { @@ -1373,43 +1347,43 @@ void FWFNSObjectHostApiSetup(id binaryMessenger, } } } -@interface FWFFunctionFlutterApiCodecReader : FlutterStandardReader +@interface FWFNSObjectFlutterApiCodecReader : FlutterStandardReader @end -@implementation FWFFunctionFlutterApiCodecReader +@implementation FWFNSObjectFlutterApiCodecReader @end -@interface FWFFunctionFlutterApiCodecWriter : FlutterStandardWriter +@interface FWFNSObjectFlutterApiCodecWriter : FlutterStandardWriter @end -@implementation FWFFunctionFlutterApiCodecWriter +@implementation FWFNSObjectFlutterApiCodecWriter @end -@interface FWFFunctionFlutterApiCodecReaderWriter : FlutterStandardReaderWriter +@interface FWFNSObjectFlutterApiCodecReaderWriter : FlutterStandardReaderWriter @end -@implementation FWFFunctionFlutterApiCodecReaderWriter +@implementation FWFNSObjectFlutterApiCodecReaderWriter - (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { - return [[FWFFunctionFlutterApiCodecWriter alloc] initWithData:data]; + return [[FWFNSObjectFlutterApiCodecWriter alloc] initWithData:data]; } - (FlutterStandardReader *)readerWithData:(NSData *)data { - return [[FWFFunctionFlutterApiCodecReader alloc] initWithData:data]; + return [[FWFNSObjectFlutterApiCodecReader alloc] initWithData:data]; } @end -NSObject *FWFFunctionFlutterApiGetCodec() { +NSObject *FWFNSObjectFlutterApiGetCodec() { static dispatch_once_t sPred = 0; static FlutterStandardMessageCodec *sSharedObject = nil; dispatch_once(&sPred, ^{ - FWFFunctionFlutterApiCodecReaderWriter *readerWriter = - [[FWFFunctionFlutterApiCodecReaderWriter alloc] init]; + FWFNSObjectFlutterApiCodecReaderWriter *readerWriter = + [[FWFNSObjectFlutterApiCodecReaderWriter alloc] init]; sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; }); return sSharedObject; } -@interface FWFFunctionFlutterApi () +@interface FWFNSObjectFlutterApi () @property(nonatomic, strong) NSObject *binaryMessenger; @end -@implementation FWFFunctionFlutterApi +@implementation FWFNSObjectFlutterApi - (instancetype)initWithBinaryMessenger:(NSObject *)binaryMessenger { self = [super init]; @@ -1418,17 +1392,6 @@ - (instancetype)initWithBinaryMessenger:(NSObject *)bina } return self; } -- (void)disposeFunctionWithIdentifier:(NSNumber *)arg_identifier - completion:(void (^)(NSError *_Nullable))completion { - FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel - messageChannelWithName:@"dev.flutter.pigeon.FunctionFlutterApi.dispose" - binaryMessenger:self.binaryMessenger - codec:FWFFunctionFlutterApiGetCodec()]; - [channel sendMessage:@[ arg_identifier ?: [NSNull null] ] - reply:^(id reply) { - completion(nil); - }]; -} @end @interface FWFWKWebViewHostApiCodecReader : FlutterStandardReader @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 cd84d705aaef..5dec08055ce5 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.h @@ -6,17 +6,30 @@ NS_ASSUME_NONNULL_BEGIN +typedef void (^FWFOnDeallocCallback)(long identifier); + /** - * Maintains instances to intercommunicate with Dart objects. + * 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 -// instead. +// should be replaced with a call to clear the manager in the event of a hot restart. /** - * Adds a new instance to the manager that was instantiated by Dart. + * 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 @@ -28,31 +41,30 @@ NS_ASSUME_NONNULL_BEGIN - (void)addDartCreatedInstance:(NSObject *)instance withIdentifier:(long)instanceIdentifier; /** - * Removes the instance paired with a given identifier from the manager. - * - * @param instanceIdentifier The identifier paired to an instance. + * Adds a new instance that was instantiated from the host platform. * - * @return The removed instance if the manager contains the given instanceIdentifier, otherwise - * nil. + * @param instance The instance to be stored. + * @return The unique identifier stored with instance. */ -- (nullable NSObject *)removeInstanceWithIdentifier:(long)instanceIdentifier; +- (long)addHostCreatedInstance:(nonnull NSObject *)instance; /** - * Removes the instance from the manager. + * Removes `instanceIdentifier` and its associated strongly referenced instance, if present, from + * the manager. * - * @param instance The instance to be removed from the manager. + * @param instanceIdentifier The identifier paired to an instance. * - * @return The identifier of the removed instance if the manager contains the given instance, - * otherwise NSNotFound. + * @return The removed instance if the manager contains the given instanceIdentifier, otherwise + * nil. */ -- (long)removeInstance:(NSObject *)instance; +- (nullable NSObject *)removeInstanceWithIdentifier:(long)instanceIdentifier; /** - * Retrieves the instance paired with identifier. + * Retrieves the instance associated with identifier. * * @param instanceIdentifier The identifier paired to an instance. * - * @return The paired instance if the manager contains the given instanceIdentifier, + * @return The instance associated with `instanceIdentifier` if the manager contains the value, * otherwise nil. */ - (nullable NSObject *)instanceForIdentifier:(long)instanceIdentifier; @@ -60,11 +72,26 @@ NS_ASSUME_NONNULL_BEGIN /** * 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 paired identifer if the manager contains the given instance, otherwise NSNotFound. + * @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`. */ -- (long)identifierForInstance:(NSObject *)instance; +- (BOOL)containsInstance:(nonnull NSObject *)instance; @end NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m index 995e37424bb9..1fe04a39503f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m @@ -3,71 +3,162 @@ // found in the LICENSE file. #import "FWFInstanceManager.h" +#import "FWFInstanceManager_Test.h" + +#import + +// Attaches to an object to receive a callback when the object is deallocated. +@interface FWFFinalizer : NSObject +@end + +// Attaches to an object to receive a callback when the object is deallocated. +@implementation FWFFinalizer { + long _identifier; + // Callbacks are no longer made once FWFInstanceManager is inaccessible. + FWFOnDeallocCallback __weak _callback; +} + +- (instancetype)initWithIdentifier:(long)identifier callback:(FWFOnDeallocCallback)callback { + self = [self init]; + if (self) { + _identifier = identifier; + _callback = callback; + } + return self; +} + ++ (void)attachToInstance:(NSObject *)instance + withIdentifier:(long)identifier + callback:(FWFOnDeallocCallback)callback { + FWFFinalizer *finalizer = [[FWFFinalizer alloc] initWithIdentifier:identifier callback:callback]; + objc_setAssociatedObject(instance, _cmd, finalizer, OBJC_ASSOCIATION_RETAIN); +} + ++ (void)detachFromInstance:(NSObject *)instance { + objc_setAssociatedObject(instance, @selector(attachToInstance:withIdentifier:callback:), nil, + OBJC_ASSOCIATION_ASSIGN); +} + +- (void)dealloc { + _callback(_identifier); +} +@end @interface FWFInstanceManager () @property dispatch_queue_t lockQueue; -@property NSMapTable *identifiersToInstances; -@property NSMapTable *instancesToIdentifiers; +@property NSMapTable *identifiers; +@property NSMapTable *weakInstances; +@property NSMapTable *strongInstances; +@property long nextIdentifier; @end @implementation FWFInstanceManager +// Identifiers are locked to a specific range to avoid collisions with objects +// created simultaneously from Dart. +// Host uses identifiers >= 2^16 and Dart is expected to use values n where, +// 0 <= n < 2^16. +static long const FWFMinHostCreatedIdentifier = 65536; + - (instancetype)init { + self = [super init]; if (self) { + _deallocCallback = _deallocCallback ? _deallocCallback : ^(long identifier) { + }; _lockQueue = dispatch_queue_create("FWFInstanceManager", DISPATCH_QUEUE_SERIAL); - _identifiersToInstances = [NSMapTable strongToStrongObjectsMapTable]; - _instancesToIdentifiers = [NSMapTable strongToStrongObjectsMapTable]; + _identifiers = [NSMapTable weakToStrongObjectsMapTable]; + _weakInstances = [NSMapTable strongToWeakObjectsMapTable]; + _strongInstances = [NSMapTable strongToStrongObjectsMapTable]; + _nextIdentifier = FWFMinHostCreatedIdentifier; + } + return self; +} + +- (instancetype)initWithDeallocCallback:(FWFOnDeallocCallback)callback { + self = [self init]; + if (self) { + _deallocCallback = callback; } return self; } -- (void)addDartCreatedInstance:(nonnull NSObject *)instance - withIdentifier:(long)instanceIdentifier { - NSAssert(instance && instanceIdentifier >= 0, - @"Instance must be nonnull and identifier must be >= 0."); +- (void)addDartCreatedInstance:(NSObject *)instance withIdentifier:(long)instanceIdentifier { + NSParameterAssert(instance); + NSParameterAssert(instanceIdentifier >= 0); dispatch_async(_lockQueue, ^{ - [self.instancesToIdentifiers setObject:@(instanceIdentifier) forKey:instance]; - [self.identifiersToInstances setObject:instance forKey:@(instanceIdentifier)]; + [self addInstance:instance withIdentifier:instanceIdentifier]; }); } +- (long)addHostCreatedInstance:(nonnull NSObject *)instance { + NSParameterAssert(instance); + long __block identifier = -1; + dispatch_sync(_lockQueue, ^{ + identifier = self.nextIdentifier++; + [self addInstance:instance withIdentifier:identifier]; + }); + return identifier; +} + - (nullable NSObject *)removeInstanceWithIdentifier:(long)instanceIdentifier { NSObject *__block instance = nil; dispatch_sync(_lockQueue, ^{ - instance = [self.identifiersToInstances objectForKey:@(instanceIdentifier)]; + instance = [self.strongInstances objectForKey:@(instanceIdentifier)]; if (instance) { - [self.identifiersToInstances removeObjectForKey:@(instanceIdentifier)]; - [self.instancesToIdentifiers removeObjectForKey:instance]; + [self.strongInstances removeObjectForKey:@(instanceIdentifier)]; } }); return instance; } -- (long)removeInstance:(NSObject *)instance { - NSAssert(instance, @"Instance must be nonnull."); +- (nullable NSObject *)instanceForIdentifier:(long)instanceIdentifier { + NSObject *__block instance = nil; + dispatch_sync(_lockQueue, ^{ + instance = [self.weakInstances objectForKey:@(instanceIdentifier)]; + }); + return instance; +} + +- (void)addInstance:(nonnull NSObject *)instance withIdentifier:(long)instanceIdentifier { + [self.identifiers setObject:@(instanceIdentifier) forKey:instance]; + [self.weakInstances setObject:instance forKey:@(instanceIdentifier)]; + [self.strongInstances setObject:instance forKey:@(instanceIdentifier)]; + [FWFFinalizer attachToInstance:instance + withIdentifier:instanceIdentifier + callback:self.deallocCallback]; +} + +- (long)identifierWithStrongReferenceForInstance:(nonnull NSObject *)instance { NSNumber *__block identifierNumber = nil; dispatch_sync(_lockQueue, ^{ - identifierNumber = [self.instancesToIdentifiers objectForKey:instance]; + identifierNumber = [self.identifiers objectForKey:instance]; if (identifierNumber) { - [self.identifiersToInstances removeObjectForKey:identifierNumber]; - [self.instancesToIdentifiers removeObjectForKey:instance]; + [self.strongInstances setObject:instance forKey:identifierNumber]; } }); return identifierNumber ? identifierNumber.longValue : NSNotFound; } -- (nullable NSObject *)instanceForIdentifier:(long)instanceIdentifier { - NSObject *__block instance = nil; +- (BOOL)containsInstance:(nonnull NSObject *)instance { + BOOL __block containsInstance; dispatch_sync(_lockQueue, ^{ - instance = [self.identifiersToInstances objectForKey:@(instanceIdentifier)]; + containsInstance = [self.identifiers objectForKey:instance]; }); - return instance; + return containsInstance; } -- (long)identifierForInstance:(nonnull NSObject *)instance { - NSNumber *__block identifierNumber = nil; +- (NSUInteger)strongInstanceCount { + NSUInteger __block count = -1; dispatch_sync(_lockQueue, ^{ - identifierNumber = [self.instancesToIdentifiers objectForKey:instance]; + count = self.strongInstances.count; }); - return identifierNumber ? identifierNumber.longValue : NSNotFound; + return count; +} + +- (NSUInteger)weakInstanceCount { + NSUInteger __block count = -1; + dispatch_sync(_lockQueue, ^{ + count = self.weakInstances.count; + }); + return count; } @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 new file mode 100644 index 000000000000..4f609049de0e --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager_Test.h @@ -0,0 +1,26 @@ +// 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 + +NS_ASSUME_NONNULL_BEGIN + +@interface FWFInstanceManager () +/** + * 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. + */ +- (NSUInteger)weakInstanceCount; +@end + +NS_ASSUME_NONNULL_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 fca1e83c86e0..2ac599603152 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.h @@ -3,17 +3,30 @@ // found in the LICENSE file. #import +#import #import #import "FWFGeneratedWebKitApis.h" #import "FWFInstanceManager.h" +#import "FWFObjectHostApi.h" NS_ASSUME_NONNULL_BEGIN +/** + * 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. */ -@interface FWFNavigationDelegate : NSObject +@interface FWFNavigationDelegate : FWFObject +@property(readonly, nonnull) FWFNavigationDelegateFlutterApiImpl *navigationDelegateAPI; @end /** @@ -22,7 +35,8 @@ NS_ASSUME_NONNULL_BEGIN * Handles creating WKNavigationDelegate that intercommunicate with a paired Dart object. */ @interface FWFNavigationDelegateHostApiImpl : NSObject -- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager; @end NS_ASSUME_NONNULL_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 836349b5f29b..be651c942c0e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m @@ -5,17 +5,70 @@ #import "FWFNavigationDelegateHostApi.h" #import "FWFWebViewConfigurationHostApi.h" +@interface FWFNavigationDelegateFlutterApiImpl () +// This reference must be weak to prevent a circular reference with the objects it stores. +@property(nonatomic, weak) FWFInstanceManager *instanceManager; +@end + +@implementation FWFNavigationDelegateFlutterApiImpl +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager { + self = [self initWithBinaryMessenger:binaryMessenger]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (long)identifierForDelegate:(FWFNavigationDelegate *)instance { + return [self.instanceManager identifierWithStrongReferenceForInstance:instance]; +} + +- (void)didFinishNavigationForDelegate:(FWFNavigationDelegate *)instance + webView:(WKWebView *)webView + URL:(NSString *)URL { + [self didFinishNavigationForDelegateWithIdentifier:@([self identifierForDelegate:instance]) + webViewIdentifier: + @([self.instanceManager + identifierWithStrongReferenceForInstance:webView]) + URL:URL + completion:^(NSError *error) { + NSAssert(!error, @"%@", error); + }]; +} +@end + @implementation FWFNavigationDelegate +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager { + self = [super initWithBinaryMessenger:binaryMessenger instanceManager:instanceManager]; + if (self) { + _navigationDelegateAPI = + [[FWFNavigationDelegateFlutterApiImpl alloc] initWithBinaryMessenger:binaryMessenger + instanceManager:instanceManager]; + } + return self; +} + +- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { + [self.navigationDelegateAPI didFinishNavigationForDelegate:self + webView:webView + URL:webView.URL.absoluteString]; +} @end @interface FWFNavigationDelegateHostApiImpl () -@property(nonatomic) FWFInstanceManager *instanceManager; +@property(weak) id binaryMessenger; +// This reference must be weak to prevent a circular reference with the objects it stores. +@property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @implementation FWFNavigationDelegateHostApiImpl -- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager { self = [self init]; if (self) { + _binaryMessenger = binaryMessenger; _instanceManager = instanceManager; } return self; @@ -26,16 +79,11 @@ - (FWFNavigationDelegate *)navigationDelegateForIdentifier:(NSNumber *)identifie } - (void)createWithIdentifier:(nonnull NSNumber *)identifier - error:(FlutterError *_Nullable *_Nonnull)error { - FWFNavigationDelegate *navigationDelegate = [[FWFNavigationDelegate alloc] init]; + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + FWFNavigationDelegate *navigationDelegate = + [[FWFNavigationDelegate alloc] initWithBinaryMessenger:self.binaryMessenger + instanceManager:self.instanceManager]; [self.instanceManager addDartCreatedInstance:navigationDelegate withIdentifier:identifier.longValue]; } - -- (void)setDidFinishNavigationForDelegateWithIdentifier:(nonnull NSNumber *)identifier - functionIdentifier:(nullable NSNumber *)functionIdentifier - error:(FlutterError *_Nullable __autoreleasing - *_Nonnull)error { - // TODO(bparrishMines): Implement when callback method design is finalized. -} @end 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 23c38fbe482d..f1dbdbb20776 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.h @@ -9,6 +9,25 @@ NS_ASSUME_NONNULL_BEGIN +/** + * Flutter api implementation for NSObject. + * + * Handles making callbacks to Dart for an NSObject. + */ +@interface FWFObjectFlutterApi : FWFNSObjectFlutterApi +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager; +@end + +/** + * Implementation of NSObject for FWFObjectHostApiImpl. + */ +@interface FWFObject : NSObject +@property(readonly, nonnull) FWFObjectFlutterApi *objectApi; +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager; +@end + /** * Host api implementation for NSObject. * diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m index 38417f9297f0..0cc4c5693342 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m @@ -5,8 +5,37 @@ #import "FWFObjectHostApi.h" #import "FWFDataConverters.h" +@interface FWFObjectFlutterApi () +// This reference must be weak to prevent a circular reference with the objects it stores. +@property(nonatomic, weak) FWFInstanceManager *instanceManager; +@end + +@implementation FWFObjectFlutterApi +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager { + self = [self initWithBinaryMessenger:binaryMessenger]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} +@end + +@implementation FWFObject +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _objectApi = [[FWFObjectFlutterApi alloc] initWithBinaryMessenger:binaryMessenger + instanceManager:instanceManager]; + } + return self; +} +@end + @interface FWFObjectHostApiImpl () -@property(nonatomic) FWFInstanceManager *instanceManager; +// This reference must be weak to prevent a circular reference with the objects it stores. +@property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @implementation FWFObjectHostApiImpl diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.modulemap b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.modulemap index 096507557688..639d89498d00 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.modulemap +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.modulemap @@ -7,5 +7,6 @@ framework module webview_flutter_wkwebview { explicit module Test { header "FlutterWebView_Test.h" header "FLTCookieManager_Test.h" + header "FWFInstanceManager_Test.h" } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/function_flutter_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/function_flutter_api_impls.dart deleted file mode 100644 index ebbf032aa673..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/function_flutter_api_impls.dart +++ /dev/null @@ -1,25 +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 'instance_manager.dart'; -import 'web_kit.pigeon.dart'; - -/// Flutter api to dispose functions. -class FunctionFlutterApiImpl extends FunctionFlutterApi { - /// Constructs a [FunctionFlutterApiImpl]. - FunctionFlutterApiImpl({InstanceManager? instanceManager}) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } - - /// Maintains instances stored to communicate with native language objects. - late final InstanceManager instanceManager; - - @override - void dispose(int identifier) { - final Function? function = instanceManager.getInstance(identifier); - if (function != null) { - instanceManager.removeWeakReference(function); - } - } -} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.dart index b855022d57e5..c9c8c2324cc6 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.dart @@ -2,49 +2,199 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/// Maintains instances stored to communicate with Objective-C objects. +import 'package:flutter/foundation.dart'; + +/// An immutable object that can provide functional copies of itself. +/// +/// All implementers are expected to be immutable as defined by the annotation. +@immutable +mixin Copyable { + /// Instantiates and returns a functionally identical object to oneself. + /// + /// Outside of tests, this method should only ever be called by + /// [InstanceManager]. + /// + /// Subclasses should always override their parent's implementation of this + /// method. + @protected + Copyable 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 InstanceManager { - final Map _strongInstances = {}; - final Map _identifiers = {}; + /// Constructs an [InstanceManager]. + InstanceManager({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; + // 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; - /// Global instance of [InstanceManager]. - static final InstanceManager instance = InstanceManager(); + /// Called when a weak referenced instance is removed by [removeWeakReference] + /// or becomes inaccessible. + late final void Function(int) onWeakReferenceRemoved; - /// Attempt to add a new instance. + /// 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 new if [instance] has already been added. Otherwise, it is added - /// with a new instance id. - int addDartCreatedInstance(Object instance) { + /// Returns the randomly generated id of the [instance] added. + int addDartCreatedInstance(Copyable instance) { assert(getIdentifier(instance) == null); - final int instanceId = _nextIdentifier++; - _identifiers[instance] = instanceId; - _strongInstances[instanceId] = instance; - return instanceId; + final int identifier = _nextUniqueIdentifier(); + _addInstanceWithIdentifier(instance, identifier); + return identifier; } - /// Remove the instance from the manager. + /// Removes the instance, if present, and call [onWeakReferenceRemoved] with + /// its identifier. /// - /// Returns null if the instance is removed. Otherwise, return the instanceId - /// of the removed instance. - int? removeWeakReference(T instance) { - final int? instanceId = _identifiers[instance]; - if (instanceId != null) { - _identifiers.remove(instance); - _strongInstances.remove(instanceId); + /// Returns the identifier associated with the removed instance. Otherwise, + /// `null` if the instance was not found in this manager. + /// + /// This does not remove the the strong referenced instance associated with + /// [instance]. This can be done with [remove]. + int? removeWeakReference(Copyable instance) { + final int? identifier = getIdentifier(instance); + if (identifier == null) { + return null; } - return instanceId; + + _identifiers[instance] = null; + _finalizer.detach(instance); + onWeakReferenceRemoved(identifier); + + return identifier; } - /// Retrieve the Object paired with instanceId. - T? getInstance(int identifier) { - return _strongInstances[identifier] as T?; + /// 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 the weak referenced instance associtated 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 Copyable? weakInstance = _weakInstances[identifier]?.target; + + if (weakInstance == null) { + final Copyable? strongInstance = _strongInstances[identifier]; + if (strongInstance != null) { + final Copyable copy = strongInstance.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; } - /// Retrieve the instanceId paired with instance. - int? getIdentifier(Object instance) { + /// Retrieves the identifier associated with instance. + int? getIdentifier(Copyable 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(Copyable instance, int identifier) { + assert(!containsIdentifier(identifier)); + assert(getIdentifier(instance) == null); + assert(identifier >= 0); + _addInstanceWithIdentifier(instance, identifier); + } + + void _addInstanceWithIdentifier(Copyable instance, int identifier) { + _identifiers[instance] = identifier; + _weakInstances[identifier] = WeakReference(instance); + _finalizer.attach(instance, identifier, detach: instance); + + final Copyable copy = instance.copy(); + _identifiers[copy] = identifier; + _strongInstances[identifier] = copy; + + assert(instance == 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; + } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart index 3072cb1cd0e7..9d928a38a2ef 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart @@ -1074,33 +1074,6 @@ class WKNavigationDelegateHostApi { return; } } - - Future setDidFinishNavigation( - int arg_identifier, int? arg_functionIdentifier) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation', - codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_identifier, arg_functionIdentifier]) - as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; - throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], - ); - } else { - return; - } - } } class _WKNavigationDelegateFlutterApiCodec extends StandardMessageCodec { @@ -1111,8 +1084,7 @@ abstract class WKNavigationDelegateFlutterApi { static const MessageCodec codec = _WKNavigationDelegateFlutterApiCodec(); - void didFinishNavigation( - int functionIdentifier, int webViewIdentifier, String? url); + void didFinishNavigation(int identifier, int webViewIdentifier, String? url); static void setup(WKNavigationDelegateFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1127,15 +1099,15 @@ abstract class WKNavigationDelegateFlutterApi { assert(message != null, 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null.'); final List args = (message as List?)!; - final int? arg_functionIdentifier = (args[0] as int?); - assert(arg_functionIdentifier != null, + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null int.'); final int? arg_webViewIdentifier = (args[1] as int?); assert(arg_webViewIdentifier != null, 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFinishNavigation was null, expected non-null int.'); final String? arg_url = (args[2] as String?); api.didFinishNavigation( - arg_functionIdentifier!, arg_webViewIdentifier!, arg_url); + arg_identifier!, arg_webViewIdentifier!, arg_url); return; }); } @@ -1261,36 +1233,15 @@ class NSObjectHostApi { } } -class _FunctionFlutterApiCodec extends StandardMessageCodec { - const _FunctionFlutterApiCodec(); +class _NSObjectFlutterApiCodec extends StandardMessageCodec { + const _NSObjectFlutterApiCodec(); } -abstract class FunctionFlutterApi { - static const MessageCodec codec = _FunctionFlutterApiCodec(); +abstract class NSObjectFlutterApi { + static const MessageCodec codec = _NSObjectFlutterApiCodec(); - void dispose(int identifier); - static void setup(FunctionFlutterApi? api, - {BinaryMessenger? binaryMessenger}) { - { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.FunctionFlutterApi.dispose', codec, - binaryMessenger: binaryMessenger); - if (api == null) { - channel.setMessageHandler(null); - } else { - channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.FunctionFlutterApi.dispose 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.FunctionFlutterApi.dispose was null, expected non-null int.'); - api.dispose(arg_identifier!); - return; - }); - } - } - } + static void setup(NSObjectFlutterApi? api, + {BinaryMessenger? binaryMessenger}) {} } class _WKWebViewHostApiCodec extends StandardMessageCodec { 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 5c127c5654c6..ca3ba780435c 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 @@ -233,18 +233,31 @@ class NSHttpCookie { } /// The root class of most Objective-C class hierarchies. -class NSObject { - /// Constructs an [NSObject]. - NSObject({BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) - : _api = NSObjectHostApiImpl( +@immutable +class NSObject with Copyable { + // TODO(bparrishMines): Change constructor name to `detached`. + /// Constructs a [NSObject] without creating the associated + /// Objective-C object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies. + NSObject({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _api = NSObjectHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, ) { - // Ensures FlutterApis for the Foundation library and FunctionFlutterApi are - // set up. + // Ensures FlutterApis for the Foundation library are set up. FoundationFlutterApis.instance.ensureSetUp(); } + /// Global instance of [InstanceManager]. + static final InstanceManager globalInstanceManager = + InstanceManager(onWeakReferenceRemoved: (int instanceId) { + NSObjectHostApiImpl().dispose(instanceId); + }); + final NSObjectHostApiImpl _api; /// Registers the observer object to receive KVO notifications. @@ -268,8 +281,8 @@ class NSObject { } /// Release the reference to the Objective-C object. - Future dispose() { - return _api.disposeForInstances(this); + static void dispose(NSObject instance) { + instance._api.instanceManager.removeWeakReference(instance); } /// Informs the observing object when the value at the specified key path has changed. @@ -283,4 +296,25 @@ class NSObject { ) { throw UnimplementedError(); } + + @override + Copyable copy() { + return NSObject( + binaryMessenger: _api.binaryMessenger, + instanceManager: _api.instanceManager, + ); + } + + @override + int get hashCode { + return Object.hash(_api, _api.instanceManager.getIdentifier(this)); + } + + @override + bool operator ==(Object other) { + return other is NSObject && + _api == other._api && + _api.instanceManager.getIdentifier(this) == + other._api.instanceManager.getIdentifier(other); + } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart index 5e5e577a4f41..9575cb51c648 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart @@ -5,7 +5,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import '../common/function_flutter_api_impls.dart'; import '../common/instance_manager.dart'; import '../common/web_kit.pigeon.dart'; import 'foundation.dart'; @@ -38,16 +37,16 @@ Iterable } /// Handles initialization of Flutter APIs for the Foundation library. +// TODO(bparrishMines): Add NSObjectFlutterApiImpl once the callback methods +// are added. class FoundationFlutterApis { /// Constructs a [FoundationFlutterApis]. @visibleForTesting FoundationFlutterApis({ BinaryMessenger? binaryMessenger, + // ignore: avoid_unused_constructor_parameters InstanceManager? instanceManager, - }) : _binaryMessenger = binaryMessenger { - functionFlutterApi = - FunctionFlutterApiImpl(instanceManager: instanceManager); - } + }) : _binaryMessenger = binaryMessenger; static FoundationFlutterApis _instance = FoundationFlutterApis(); @@ -62,36 +61,33 @@ class FoundationFlutterApis { return _instance; } + // ignore: unused_field final BinaryMessenger? _binaryMessenger; bool _hasBeenSetUp = false; - /// Flutter Api for disposing functions. - /// - /// This FlutterApi is placed here because [FoundationFlutterApis.ensureSetUp] - /// is called inside [NSObject] and [NSObject] is the parent class of all - /// objects. - @visibleForTesting - late final FunctionFlutterApiImpl functionFlutterApi; - /// Ensures all the Flutter APIs have been set up to receive calls from native code. void ensureSetUp() { if (!_hasBeenSetUp) { - FunctionFlutterApi.setup( - functionFlutterApi, - binaryMessenger: _binaryMessenger, - ); _hasBeenSetUp = true; } } } /// Host api implementation for [NSObject]. +@immutable class NSObjectHostApiImpl extends NSObjectHostApi { /// Constructs an [NSObjectHostApiImpl]. NSObjectHostApiImpl({ - super.binaryMessenger, + this.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); + + /// Sends binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -124,11 +120,15 @@ class NSObjectHostApiImpl extends NSObjectHostApi { ); } - /// Calls [dispose] with the ids of the provided object instances. - Future disposeForInstances(NSObject instance) async { - final int? identifier = instanceManager.removeWeakReference(instance); - if (identifier != null) { - await dispose(identifier); - } + @override + int get hashCode { + return Object.hash(binaryMessenger, instanceManager); + } + + @override + bool operator ==(Object other) { + return other is NSObjectHostApiImpl && + binaryMessenger == other.binaryMessenger && + instanceManager == other.instanceManager; } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart index 7c1bdd01724c..1d89b18c9f05 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart @@ -12,6 +12,10 @@ import '../foundation/foundation.dart'; import '../web_kit/web_kit.dart'; import 'ui_kit_api_impls.dart'; +// TODO(bparrishMines): All subclasses of NSObject need to pass their +// InstanceManager and BinaryMessenger to its parent. They also need to +// override copy(); + /// A view that allows the scrolling and zooming of its contained views. /// /// Wraps [UIScrollView](https://developer.apple.com/documentation/uikit/uiscrollview?language=objc). diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart index 1d962ee788db..e921c4bad15a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart @@ -7,6 +7,8 @@ import 'dart:math'; import 'package:flutter/painting.dart' show Color; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; + import '../common/instance_manager.dart'; import '../common/web_kit.pigeon.dart'; import '../web_kit/web_kit.dart'; @@ -18,7 +20,7 @@ class UIScrollViewHostApiImpl extends UIScrollViewHostApi { UIScrollViewHostApiImpl({ super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -75,7 +77,7 @@ class UIViewHostApiImpl extends UIViewHostApi { UIViewHostApiImpl({ super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; 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 16490a24d474..2b887e97adcf 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 @@ -10,6 +10,10 @@ import '../foundation/foundation.dart'; import '../ui_kit/ui_kit.dart'; import 'web_kit_api_impls.dart'; +// TODO(bparrishMines): All subclasses of NSObject need to pass their +// InstanceManager and BinaryMessenger to its parent. They also need to +// override copy(): https://github.com/flutter/flutter/issues/105245 + /// Times at which to inject script content into a webpage. /// /// Wraps [WKUserScriptInjectionTime](https://developer.apple.com/documentation/webkit/wkuserscriptinjectiontime?language=objc). @@ -210,7 +214,7 @@ class WKScriptMessage { /// Encapsulates the standard behaviors to apply to websites. /// /// Wraps [WKPreferences](https://developer.apple.com/documentation/webkit/wkpreferences?language=objc). -class WKPreferences { +class WKPreferences extends NSObject { /// Constructs a [WKPreferences] that is owned by [configuration]. @visibleForTesting WKPreferences.fromWebViewConfiguration( @@ -241,7 +245,7 @@ class WKPreferences { /// Manages cookies, disk and memory caches, and other types of data for a web view. /// /// Wraps [WKWebsiteDataStore](https://developer.apple.com/documentation/webkit/wkwebsitedatastore?language=objc). -class WKWebsiteDataStore { +class WKWebsiteDataStore extends NSObject { WKWebsiteDataStore._({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, @@ -343,7 +347,7 @@ class WKHttpCookieStore extends NSObject { /// An interface for receiving messages from JavaScript code running in a webpage. /// /// Wraps [WKScriptMessageHandler](https://developer.apple.com/documentation/webkit/wkscriptmessagehandler?language=objc) -class WKScriptMessageHandler { +class WKScriptMessageHandler extends NSObject { /// Constructs a [WKScriptMessageHandler]. WKScriptMessageHandler({ BinaryMessenger? binaryMessenger, @@ -382,7 +386,7 @@ class WKScriptMessageHandler { /// code. /// /// Wraps [WKUserContentController](https://developer.apple.com/documentation/webkit/wkusercontentcontroller?language=objc). -class WKUserContentController { +class WKUserContentController extends NSObject { /// Constructs a [WKUserContentController] that is owned by [configuration]. @visibleForTesting WKUserContentController.fromWebViewConfiguration( @@ -465,7 +469,7 @@ class WKUserContentController { /// A collection of properties that you use to initialize a web view. /// /// Wraps [WKWebViewConfiguration](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration?language=objc). -class WKWebViewConfiguration { +class WKWebViewConfiguration extends NSObject { /// Constructs a [WKWebViewConfiguration]. factory WKWebViewConfiguration({ BinaryMessenger? binaryMessenger, @@ -565,7 +569,7 @@ class WKWebViewConfiguration { /// The methods for presenting native user interface elements on behalf of a webpage. /// /// Wraps [WKUIDelegate](https://developer.apple.com/documentation/webkit/wkuidelegate?language=objc). -class WKUIDelegate { +class WKUIDelegate extends NSObject { /// Constructs a [WKUIDelegate]. WKUIDelegate({ BinaryMessenger? binaryMessenger, @@ -597,9 +601,11 @@ class WKUIDelegate { /// coordinate changes in your web view’s main frame. /// /// Wraps [WKNavigationDelegate](https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc). +@immutable class WKNavigationDelegate extends NSObject { /// Constructs a [WKNavigationDelegate]. WKNavigationDelegate({ + this.didFinishNavigation, super.binaryMessenger, super.instanceManager, }) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl( @@ -610,8 +616,25 @@ class WKNavigationDelegate extends NSObject { _navigationDelegateApi.createForInstances(this); } + /// Constructs a [WKNavigationDelegate] without creating the associated + /// Objective-C object. + /// + /// This should only be used outside of tests by subclasses created by this + /// library or to create a copy for an InstanceManager. + WKNavigationDelegate.detached({ + this.didFinishNavigation, + super.binaryMessenger, + super.instanceManager, + }) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + final WKNavigationDelegateHostApiImpl _navigationDelegateApi; + /// Called when navigation is complete. + final void Function(WKWebView webView, String? url)? didFinishNavigation; + /// Called when navigation from the main frame has started. Future setDidStartProvisionalNavigation( void Function(WKWebView webView, String? url)? @@ -620,16 +643,6 @@ class WKNavigationDelegate extends NSObject { throw UnimplementedError(); } - /// Called when navigation is complete. - Future setDidFinishNavigation( - void Function(WKWebView webView, String? url)? didFinishNavigation, - ) { - return _navigationDelegateApi.setDidFinishNavigationFromInstance( - this, - didFinishNavigation, - ); - } - /// Called when permission is needed to navigate to new content. Future setDecidePolicyForNavigationAction( Future Function( @@ -661,6 +674,30 @@ class WKNavigationDelegate extends NSObject { ) { throw UnimplementedError(); } + + @override + Copyable copy() { + return WKNavigationDelegate.detached( + didFinishNavigation: didFinishNavigation, + binaryMessenger: _navigationDelegateApi.binaryMessenger, + instanceManager: _navigationDelegateApi.instanceManager, + ); + } + + @override + int get hashCode { + return Object.hash(didFinishNavigation, _navigationDelegateApi, + _navigationDelegateApi.instanceManager.getIdentifier(this)); + } + + @override + bool operator ==(Object other) { + return other is WKNavigationDelegate && + didFinishNavigation == other.didFinishNavigation && + _navigationDelegateApi == other._navigationDelegateApi && + _navigationDelegateApi.instanceManager.getIdentifier(this) == + other._navigationDelegateApi.instanceManager.getIdentifier(other); + } } /// Object that displays interactive web content, such as for an in-app browser. 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 fe4d7d85d2d2..85b9b492eb87 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 @@ -226,7 +226,7 @@ class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi { WKWebsiteDataStoreHostApiImpl({ super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -247,7 +247,8 @@ class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi { WKWebsiteDataStore instance, ) { return createDefaultDataStore( - instanceManager.addDartCreatedInstance(instance)); + instanceManager.addDartCreatedInstance(instance), + ); } /// Calls [removeDataOfTypes] with the ids of the provided object instances. @@ -270,7 +271,7 @@ class WKScriptMessageHandlerHostApiImpl extends WKScriptMessageHandlerHostApi { WKScriptMessageHandlerHostApiImpl({ super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -287,7 +288,7 @@ class WKPreferencesHostApiImpl extends WKPreferencesHostApi { WKPreferencesHostApiImpl({ super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -321,7 +322,7 @@ class WKHttpCookieStoreHostApiImpl extends WKHttpCookieStoreHostApi { WKHttpCookieStoreHostApiImpl({ super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -356,7 +357,7 @@ class WKUserContentControllerHostApiImpl WKUserContentControllerHostApiImpl({ super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -430,7 +431,7 @@ class WKWebViewConfigurationHostApiImpl extends WKWebViewConfigurationHostApi { WKWebViewConfigurationHostApiImpl({ super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -480,47 +481,51 @@ class WKUIDelegateHostApiImpl extends WKUIDelegateHostApi { WKUIDelegateHostApiImpl({ super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; /// Calls [create] with the ids of the provided object instances. - Future createForInstances(WKUIDelegate instance) { + Future createForInstances(WKUIDelegate instance) async { return create(instanceManager.addDartCreatedInstance(instance)); } } /// Host api implementation for [WKNavigationDelegate]. +@immutable class WKNavigationDelegateHostApiImpl extends WKNavigationDelegateHostApi { /// Constructs a [WKNavigationDelegateHostApiImpl]. WKNavigationDelegateHostApiImpl({ - super.binaryMessenger, + this.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); + + /// Sends binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; /// Calls [create] with the ids of the provided object instances. - Future createForInstances(WKNavigationDelegate instance) { + Future createForInstances(WKNavigationDelegate instance) async { return create(instanceManager.addDartCreatedInstance(instance)); } - /// Calls [setDidFinishNavigation] with the ids of the provided object instances. - Future setDidFinishNavigationFromInstance( - WKNavigationDelegate instance, - void Function(WKWebView, String?)? didFinishNavigation, - ) { - int? functionInstanceId; - if (didFinishNavigation != null) { - functionInstanceId = instanceManager.getIdentifier(didFinishNavigation) ?? - instanceManager.addDartCreatedInstance(didFinishNavigation); - } - return setDidFinishNavigation( - instanceManager.getIdentifier(instance)!, - functionInstanceId, - ); + @override + int get hashCode { + return Object.hash(binaryMessenger, instanceManager); + } + + @override + bool operator ==(Object other) { + return other is WKNavigationDelegateHostApiImpl && + binaryMessenger == other.binaryMessenger && + instanceManager == other.instanceManager; } } @@ -529,23 +534,29 @@ class WKNavigationDelegateFlutterApiImpl extends WKNavigationDelegateFlutterApi { /// Constructs a [WKNavigationDelegateFlutterApiImpl]. WKNavigationDelegateFlutterApiImpl({InstanceManager? instanceManager}) { - this.instanceManager = instanceManager ?? InstanceManager.instance; + this.instanceManager = instanceManager ?? NSObject.globalInstanceManager; } /// Maintains instances stored to communicate with native language objects. late final InstanceManager instanceManager; + WKNavigationDelegate _getDelegate(int identifier) { + return instanceManager.getInstanceWithWeakReference(identifier)!; + } + @override void didFinishNavigation( - int functionIdentifier, + int identifier, int webViewIdentifier, String? url, ) { - final void Function( - WKWebView webView, - String? url, - ) function = instanceManager.getInstance(functionIdentifier)!; - function(instanceManager.getInstance(webViewIdentifier)!, url); + final void Function(WKWebView, String?)? function = + _getDelegate(identifier).didFinishNavigation; + function?.call( + instanceManager.getInstanceWithWeakReference(webViewIdentifier)! + as WKWebView, + url, + ); } } @@ -555,7 +566,7 @@ class WKWebViewHostApiImpl extends WKWebViewHostApi { WKWebViewHostApiImpl({ super.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? InstanceManager.instance; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -573,7 +584,9 @@ class WKWebViewHostApiImpl extends WKWebViewHostApi { /// Calls [loadRequest] with the ids of the provided object instances. Future loadRequestForInstances( - WKWebView webView, NSUrlRequest request) { + WKWebView webView, + NSUrlRequest request, + ) { return loadRequest( instanceManager.getIdentifier(webView)!, request.toNSUrlRequestData(), diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 7001c3131147..90f1554bf99b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -117,13 +117,14 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { /// Methods for handling navigation changes and tracking navigation requests. @visibleForTesting late final WKNavigationDelegate navigationDelegate = - webViewProxy.createNavigationDelegate() + webViewProxy.createNavigationDelegate( + didFinishNavigation: (WKWebView webView, String? url) { + callbacksHandler.onPageFinished(url ?? ''); + }, + ) ..setDidStartProvisionalNavigation((WKWebView webView, String? url) { callbacksHandler.onPageStarted(url ?? ''); }) - ..setDidFinishNavigation((WKWebView webView, String? url) { - callbacksHandler.onPageFinished(url ?? ''); - }) ..setDidFailNavigation((WKWebView webView, NSError error) { callbacksHandler.onWebResourceError(_toWebResourceError(error)); }) @@ -619,7 +620,13 @@ class WebViewWidgetProxy { } /// Constructs a [WKNavigationDelegate]. - WKNavigationDelegate createNavigationDelegate() { - return WKNavigationDelegate(); + WKNavigationDelegate createNavigationDelegate({ + void Function( + WKWebView webView, + String? url, + )? + didFinishNavigation, + }) { + return WKNavigationDelegate(didFinishNavigation: didFinishNavigation); } } 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 41806d3f4601..91541e8e741b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -380,14 +380,9 @@ abstract class WKScriptMessageHandlerHostApi { abstract class WKNavigationDelegateHostApi { @ObjCSelector('createWithIdentifier:') void create(int identifier); - - @ObjCSelector( - 'setDidFinishNavigationForDelegateWithIdentifier:functionIdentifier:', - ) - void setDidFinishNavigation(int identifier, int? functionIdentifier); } -/// Mirror of WKNavigationDelegate. +/// Handles callbacks from an WKNavigationDelegate instance. /// /// See https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc. @FlutterApi() @@ -396,7 +391,7 @@ abstract class WKNavigationDelegateFlutterApi { 'didFinishNavigationForDelegateWithIdentifier:webViewIdentifier:URL:', ) void didFinishNavigation( - int functionIdentifier, + int identifier, int webViewIdentifier, String? url, ); @@ -426,12 +421,11 @@ abstract class NSObjectHostApi { void removeObserver(int identifier, int observerIdentifier, String keyPath); } -/// Disposes references to functions. +/// Handles callbacks from an NSObject instance. +/// +/// See https://developer.apple.com/documentation/objectivec/nsobject. @FlutterApi() -abstract class FunctionFlutterApi { - @ObjCSelector('disposeFunctionWithIdentifier:') - void dispose(int identifier); -} +abstract class NSObjectFlutterApi {} /// Mirror of WKWebView. /// diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.dart deleted file mode 100644 index 74264bf7e577..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/function_flutter_api_test.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_test/flutter_test.dart'; -import 'package:webview_flutter_wkwebview/src/common/instance_manager.dart'; -import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart'; -import 'package:webview_flutter_wkwebview/src/foundation/foundation_api_impls.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - group('$FunctionFlutterApi', () { - late InstanceManager instanceManager; - - setUp(() { - instanceManager = InstanceManager(); - }); - - test('dispose', () { - void function() {} - final int functionInstanceId = - instanceManager.addDartCreatedInstance(function); - - FoundationFlutterApis.instance = FoundationFlutterApis( - instanceManager: instanceManager, - )..ensureSetUp(); - - FoundationFlutterApis.instance.functionFlutterApi - .dispose(functionInstanceId); - expect(instanceManager.getIdentifier(function), isNull); - }); - }); -} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/instance_manager_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/instance_manager_test.dart index dad44f9cd535..2fc68a489b6a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/instance_manager_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/instance_manager_test.dart @@ -7,28 +7,147 @@ import 'package:webview_flutter_wkwebview/src/common/instance_manager.dart'; void main() { group('InstanceManager', () { - late InstanceManager testInstanceManager; + test('addHostCreatedInstance', () { + final CopyableObject object = CopyableObject(); - setUp(() { - testInstanceManager = InstanceManager(); + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance(object, 0); + + expect(instanceManager.getIdentifier(object), 0); + expect( + instanceManager.getInstanceWithWeakReference(0), + object, + ); + }); + + test('addHostCreatedInstance prevents already used objects and ids', () { + final CopyableObject object = CopyableObject(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance(object, 0); + + expect( + () => instanceManager.addHostCreatedInstance(object, 0), + throwsAssertionError, + ); + + expect( + () => instanceManager.addHostCreatedInstance(CopyableObject(), 0), + throwsAssertionError, + ); + }); + + test('addFlutterCreatedInstance', () { + final CopyableObject object = CopyableObject(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addDartCreatedInstance(object); + + final int? instanceId = instanceManager.getIdentifier(object); + expect(instanceId, isNotNull); + expect( + instanceManager.getInstanceWithWeakReference(instanceId!), + object, + ); + }); + + test('removeWeakReference', () { + final CopyableObject object = CopyableObject(); + + int? weakInstanceId; + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (int instanceId) { + weakInstanceId = instanceId; + }); + + instanceManager.addHostCreatedInstance(object, 0); + + expect(instanceManager.removeWeakReference(object), 0); + expect( + instanceManager.getInstanceWithWeakReference(0), + isA(), + ); + expect(weakInstanceId, 0); }); - test('tryAddInstance', () { - final Object object = Object(); + test('removeWeakReference removes only weak reference', () { + final CopyableObject object = CopyableObject(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance(object, 0); - expect(testInstanceManager.addDartCreatedInstance(object), 0); - expect(testInstanceManager.getIdentifier(object), 0); - expect(testInstanceManager.getInstance(0), object); + expect(instanceManager.removeWeakReference(object), 0); + final CopyableObject copy = instanceManager.getInstanceWithWeakReference( + 0, + )!; + expect(identical(object, copy), isFalse); }); - test('removeInstance', () { - final Object object = Object(); - testInstanceManager.addDartCreatedInstance(object); + test('removeStrongReference', () { + final CopyableObject object = CopyableObject(); - expect(testInstanceManager.removeWeakReference(object), 0); - expect(testInstanceManager.getIdentifier(object), null); - expect(testInstanceManager.getInstance(0), null); - expect(testInstanceManager.removeWeakReference(object), null); + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance(object, 0); + instanceManager.removeWeakReference(object); + expect(instanceManager.remove(0), isA()); + expect(instanceManager.containsIdentifier(0), isFalse); + }); + + test('removeStrongReference removes only strong reference', () { + final CopyableObject object = CopyableObject(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance(object, 0); + expect(instanceManager.remove(0), isA()); + expect( + instanceManager.getInstanceWithWeakReference(0), + object, + ); + }); + + test('getInstance can add a new weak reference', () { + final CopyableObject object = CopyableObject(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance(object, 0); + instanceManager.removeWeakReference(object); + + final CopyableObject newWeakCopy = + instanceManager.getInstanceWithWeakReference( + 0, + )!; + expect(identical(object, newWeakCopy), isFalse); }); }); } + +class CopyableObject with Copyable { + @override + Copyable copy() { + return CopyableObject(); + } + + @override + int get hashCode { + return 0; + } + + @override + bool operator ==(Object other) { + return other is CopyableObject; + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart index 4a0002569058..00865d66f2bc 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart @@ -707,7 +707,6 @@ abstract class TestWKNavigationDelegateHostApi { _TestWKNavigationDelegateHostApiCodec(); void create(int identifier); - void setDidFinishNavigation(int identifier, int? functionIdentifier); static void setup(TestWKNavigationDelegateHostApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -729,27 +728,6 @@ abstract class TestWKNavigationDelegateHostApi { }); } } - { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation', - codec, - binaryMessenger: binaryMessenger); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WKNavigationDelegateHostApi.setDidFinishNavigation 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.WKNavigationDelegateHostApi.setDidFinishNavigation was null, expected non-null int.'); - final int? arg_functionIdentifier = (args[1] as int?); - api.setDidFinishNavigation(arg_identifier!, arg_functionIdentifier); - return {}; - }); - } - } } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart index 03bb7747a048..ca84369c9789 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart @@ -22,7 +22,7 @@ void main() { late InstanceManager instanceManager; setUp(() { - instanceManager = InstanceManager(); + instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {}); }); group('NSObject', () { @@ -88,12 +88,17 @@ void main() { }); test('dispose', () async { - final int instanceId = instanceManager.getIdentifier(object)!; + int? callbackIdentifier; + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (int identifier) { + callbackIdentifier = identifier; + }); - await object.dispose(); - verify( - mockPlatformHostApi.dispose(instanceId), - ); + final NSObject object = NSObject(instanceManager: instanceManager); + final int identifier = instanceManager.addDartCreatedInstance(object); + + NSObject.dispose(object); + expect(callbackIdentifier, identifier); }); }); }); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart index ef68bbb41809..aeb4e1cc54ce 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart @@ -28,7 +28,7 @@ void main() { late InstanceManager instanceManager; setUp(() { - instanceManager = InstanceManager(); + instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {}); }); group('UIScrollView', () { 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 da92ab71999b..f486474b4cfb 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 @@ -35,7 +35,7 @@ void main() { late WebKitFlutterApis flutterApis; setUp(() { - instanceManager = InstanceManager(); + instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {}); flutterApis = WebKitFlutterApis(instanceManager: instanceManager); WebKitFlutterApis.instance = flutterApis; }); @@ -81,7 +81,7 @@ void main() { WKWebsiteDataStore.defaultDataStore; verify( mockPlatformHostApi.createDefaultDataStore( - InstanceManager.instance.getIdentifier(defaultDataStore), + NSObject.globalInstanceManager.getIdentifier(defaultDataStore), ), ); }); @@ -438,29 +438,33 @@ void main() { }); test('create', () async { + navigationDelegate = WKNavigationDelegate( + instanceManager: instanceManager, + ); + verify(mockPlatformHostApi.create( instanceManager.getIdentifier(navigationDelegate), )); }); - test('setDidFinishNavigation', () async { + test('didFinishNavigation', () async { final Completer> argsCompleter = Completer>(); - navigationDelegate.setDidFinishNavigation( - (WKWebView webView, String? url) { + WebKitFlutterApis.instance = WebKitFlutterApis( + instanceManager: instanceManager, + ); + + navigationDelegate = WKNavigationDelegate( + instanceManager: instanceManager, + didFinishNavigation: (WKWebView webView, String? url) { argsCompleter.complete([webView, url]); }, ); - final int functionInstanceId = - verify(mockPlatformHostApi.setDidFinishNavigation( - instanceManager.getIdentifier(navigationDelegate), - captureAny, - )).captured.single as int; - - flutterApis.navigationDelegateFlutterApi.didFinishNavigation( - functionInstanceId, + WebKitFlutterApis.instance.navigationDelegateFlutterApi + .didFinishNavigation( + instanceManager.getIdentifier(navigationDelegate)!, instanceManager.getIdentifier(webView)!, 'url', ); 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 4ffb7d4c19d3..16d80b22c22b 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 @@ -56,12 +56,6 @@ class MockTestWKNavigationDelegateHostApi extends _i1.Mock void create(int? instanceId) => super.noSuchMethod(Invocation.method(#create, [instanceId]), returnValueForMissingStub: null); - @override - void setDidFinishNavigation(int? instanceId, int? functionInstanceId) => - super.noSuchMethod( - Invocation.method( - #setDidFinishNavigation, [instanceId, functionInstanceId]), - returnValueForMissingStub: null); } /// A class which mocks [TestWKPreferencesHostApi]. @@ -311,9 +305,9 @@ class MockTestWKWebsiteDataStoreHostApi extends _i1.Mock _i3.Future removeDataOfTypes( int? instanceId, List<_i4.WKWebsiteDataTypeEnumData?>? dataTypes, - double? secondsModifiedSinceEpoch) => + double? modificationTimeInSecondsSinceEpoch) => (super.noSuchMethod( Invocation.method(#removeDataOfTypes, - [instanceId, dataTypes, secondsModifiedSinceEpoch]), + [instanceId, dataTypes, modificationTimeInSecondsSinceEpoch]), returnValue: Future.value(false)) as _i3.Future); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart index 8d7751da785f..5989c138ff25 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart @@ -2,12 +2,14 @@ // in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart. // Do not manually edit this file. -import 'dart:async' as _i3; +import 'dart:async' as _i4; import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_wkwebview/src/common/instance_manager.dart' + as _i2; import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart' - as _i4; -import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i2; + as _i5; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i3; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -19,68 +21,93 @@ import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i2; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types -class _FakeWKHttpCookieStore_0 extends _i1.Fake - implements _i2.WKHttpCookieStore {} +class _FakeCopyable_0 extends _i1.Fake implements _i2.Copyable {} + +class _FakeWKHttpCookieStore_1 extends _i1.Fake + implements _i3.WKHttpCookieStore {} /// A class which mocks [WKHttpCookieStore]. /// /// See the documentation for Mockito's code generation for more information. -class MockWKHttpCookieStore extends _i1.Mock implements _i2.WKHttpCookieStore { +class MockWKHttpCookieStore extends _i1.Mock implements _i3.WKHttpCookieStore { MockWKHttpCookieStore() { _i1.throwOnMissingStub(this); } @override - _i3.Future setCookie(_i4.NSHttpCookie? cookie) => + _i4.Future setCookie(_i5.NSHttpCookie? cookie) => (super.noSuchMethod(Invocation.method(#setCookie, [cookie]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i3.Future addObserver(_i4.NSObject? observer, - {String? keyPath, Set<_i4.NSKeyValueObservingOptions>? options}) => + _i4.Future addObserver(_i5.NSObject? observer, + {String? keyPath, Set<_i5.NSKeyValueObservingOptions>? options}) => (super.noSuchMethod( Invocation.method( #addObserver, [observer], {#keyPath: keyPath, #options: options}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i3.Future removeObserver(_i4.NSObject? observer, {String? keyPath}) => + _i4.Future removeObserver(_i5.NSObject? observer, {String? keyPath}) => (super.noSuchMethod( Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); - @override - _i3.Future dispose() => - (super.noSuchMethod(Invocation.method(#dispose, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i3.Future setObserveValue( + _i4.Future setObserveValue( void Function( - String, _i4.NSObject, Map<_i4.NSKeyValueChangeKey, Object?>)? + String, _i5.NSObject, Map<_i5.NSKeyValueChangeKey, Object?>)? observeValue) => (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i2.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeCopyable_0()) as _i2.Copyable); } /// A class which mocks [WKWebsiteDataStore]. /// /// See the documentation for Mockito's code generation for more information. class MockWKWebsiteDataStore extends _i1.Mock - implements _i2.WKWebsiteDataStore { + implements _i3.WKWebsiteDataStore { MockWKWebsiteDataStore() { _i1.throwOnMissingStub(this); } @override - _i2.WKHttpCookieStore get httpCookieStore => + _i3.WKHttpCookieStore get httpCookieStore => (super.noSuchMethod(Invocation.getter(#httpCookieStore), - returnValue: _FakeWKHttpCookieStore_0()) as _i2.WKHttpCookieStore); + returnValue: _FakeWKHttpCookieStore_1()) as _i3.WKHttpCookieStore); @override - _i3.Future removeDataOfTypes( - Set<_i2.WKWebsiteDataType>? dataTypes, DateTime? since) => + _i4.Future removeDataOfTypes( + Set<_i3.WKWebsiteDataType>? dataTypes, DateTime? since) => (super.noSuchMethod( Invocation.method(#removeDataOfTypes, [dataTypes, since]), - returnValue: Future.value(false)) as _i3.Future); + returnValue: Future.value(false)) as _i4.Future); + @override + _i4.Future addObserver(_i5.NSObject? observer, + {String? keyPath, Set<_i5.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future removeObserver(_i5.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future setObserveValue( + void Function( + String, _i5.NSObject, Map<_i5.NSKeyValueChangeKey, Object?>)? + observeValue) => + (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i2.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeCopyable_0()) as _i2.Copyable); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 271fd5c062e2..6af5f7d16279 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -64,8 +64,9 @@ void main() { when(mockWebViewWidgetProxy.createWebView(any)).thenReturn(mockWebView); when(mockWebViewWidgetProxy.createUIDelgate()).thenReturn(mockUIDelegate); - when(mockWebViewWidgetProxy.createNavigationDelegate()) - .thenReturn(mockNavigationDelegate); + when(mockWebViewWidgetProxy.createNavigationDelegate( + didFinishNavigation: anyNamed('didFinishNavigation'), + )).thenReturn(mockNavigationDelegate); when(mockWebView.configuration).thenReturn(mockWebViewConfiguration); when(mockWebViewConfiguration.userContentController).thenReturn( mockUserContentController, @@ -919,9 +920,9 @@ void main() { await buildWidget(tester); final dynamic didFinishNavigation = - verify(mockNavigationDelegate.setDidFinishNavigation(captureAny)) - .captured - .single as void Function(WKWebView, String); + verify(mockWebViewWidgetProxy.createNavigationDelegate( + didFinishNavigation: captureAnyNamed('didFinishNavigation'), + )).captured.single as void Function(WKWebView, String); didFinishNavigation(mockWebView, 'https://google.com'); verify(mockCallbacksHandler.onPageFinished('https://google.com')); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index 066f33a9774e..f2a9876a71e4 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -2,23 +2,25 @@ // in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. // Do not manually edit this file. -import 'dart:async' as _i5; +import 'dart:async' as _i6; import 'dart:math' as _i2; -import 'dart:ui' as _i6; +import 'dart:ui' as _i7; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_platform_interface/src/types/javascript_channel.dart' - as _i9; -import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i10; +import 'package:webview_flutter_platform_interface/src/types/types.dart' + as _i11; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart' - as _i8; + as _i9; +import 'package:webview_flutter_wkwebview/src/common/instance_manager.dart' + as _i3; import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart' - as _i7; -import 'package:webview_flutter_wkwebview/src/ui_kit/ui_kit.dart' as _i4; -import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i3; + as _i8; +import 'package:webview_flutter_wkwebview/src/ui_kit/ui_kit.dart' as _i5; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i4; import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart' - as _i11; + as _i12; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -32,491 +34,631 @@ import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart' class _FakePoint_0 extends _i1.Fake implements _i2.Point {} -class _FakeWKWebViewConfiguration_1 extends _i1.Fake - implements _i3.WKWebViewConfiguration {} +class _FakeCopyable_1 extends _i1.Fake implements _i3.Copyable {} + +class _FakeWKWebViewConfiguration_2 extends _i1.Fake + implements _i4.WKWebViewConfiguration {} -class _FakeUIScrollView_2 extends _i1.Fake implements _i4.UIScrollView {} +class _FakeUIScrollView_3 extends _i1.Fake implements _i5.UIScrollView {} -class _FakeWKUserContentController_3 extends _i1.Fake - implements _i3.WKUserContentController {} +class _FakeWKUserContentController_4 extends _i1.Fake + implements _i4.WKUserContentController {} -class _FakeWKPreferences_4 extends _i1.Fake implements _i3.WKPreferences {} +class _FakeWKPreferences_5 extends _i1.Fake implements _i4.WKPreferences {} -class _FakeWKWebsiteDataStore_5 extends _i1.Fake - implements _i3.WKWebsiteDataStore {} +class _FakeWKWebsiteDataStore_6 extends _i1.Fake + implements _i4.WKWebsiteDataStore {} -class _FakeWKHttpCookieStore_6 extends _i1.Fake - implements _i3.WKHttpCookieStore {} +class _FakeWKHttpCookieStore_7 extends _i1.Fake + implements _i4.WKHttpCookieStore {} -class _FakeWKWebView_7 extends _i1.Fake implements _i3.WKWebView {} +class _FakeWKWebView_8 extends _i1.Fake implements _i4.WKWebView {} -class _FakeWKScriptMessageHandler_8 extends _i1.Fake - implements _i3.WKScriptMessageHandler {} +class _FakeWKScriptMessageHandler_9 extends _i1.Fake + implements _i4.WKScriptMessageHandler {} -class _FakeWKUIDelegate_9 extends _i1.Fake implements _i3.WKUIDelegate {} +class _FakeWKUIDelegate_10 extends _i1.Fake implements _i4.WKUIDelegate {} -class _FakeWKNavigationDelegate_10 extends _i1.Fake - implements _i3.WKNavigationDelegate {} +class _FakeWKNavigationDelegate_11 extends _i1.Fake + implements _i4.WKNavigationDelegate {} /// A class which mocks [UIScrollView]. /// /// See the documentation for Mockito's code generation for more information. -class MockUIScrollView extends _i1.Mock implements _i4.UIScrollView { +class MockUIScrollView extends _i1.Mock implements _i5.UIScrollView { MockUIScrollView() { _i1.throwOnMissingStub(this); } @override - _i5.Future<_i2.Point> getContentOffset() => (super.noSuchMethod( + _i6.Future<_i2.Point> getContentOffset() => (super.noSuchMethod( Invocation.method(#getContentOffset, []), returnValue: Future<_i2.Point>.value(_FakePoint_0())) - as _i5.Future<_i2.Point>); + as _i6.Future<_i2.Point>); @override - _i5.Future scrollBy(_i2.Point? offset) => + _i6.Future scrollBy(_i2.Point? offset) => (super.noSuchMethod(Invocation.method(#scrollBy, [offset]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future setContentOffset(_i2.Point? offset) => + _i6.Future setContentOffset(_i2.Point? offset) => (super.noSuchMethod(Invocation.method(#setContentOffset, [offset]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future setBackgroundColor(_i6.Color? color) => + _i6.Future setBackgroundColor(_i7.Color? color) => (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future setOpaque(bool? opaque) => + _i6.Future setOpaque(bool? opaque) => (super.noSuchMethod(Invocation.method(#setOpaque, [opaque]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future addObserver(_i7.NSObject? observer, - {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => + _i6.Future addObserver(_i8.NSObject? observer, + {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => (super.noSuchMethod( Invocation.method( #addObserver, [observer], {#keyPath: keyPath, #options: options}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => + _i6.Future removeObserver(_i8.NSObject? observer, {String? keyPath}) => (super.noSuchMethod( Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future dispose() => - (super.noSuchMethod(Invocation.method(#dispose, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future setObserveValue( + _i6.Future setObserveValue( void Function( - String, _i7.NSObject, Map<_i7.NSKeyValueChangeKey, Object?>)? + String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? observeValue) => (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeCopyable_1()) as _i3.Copyable); } /// A class which mocks [WKNavigationDelegate]. /// /// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable class MockWKNavigationDelegate extends _i1.Mock - implements _i3.WKNavigationDelegate { + implements _i4.WKNavigationDelegate { MockWKNavigationDelegate() { _i1.throwOnMissingStub(this); } @override - _i5.Future setDidStartProvisionalNavigation( - void Function(_i3.WKWebView, String?)? + _i6.Future setDidStartProvisionalNavigation( + void Function(_i4.WKWebView, String?)? didStartProvisionalNavigation) => (super.noSuchMethod( Invocation.method(#setDidStartProvisionalNavigation, [didStartProvisionalNavigation]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future setDidFinishNavigation( - void Function(_i3.WKWebView, String?)? didFinishNavigation) => - (super.noSuchMethod( - Invocation.method(#setDidFinishNavigation, [didFinishNavigation]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future setDecidePolicyForNavigationAction( - _i5.Future<_i3.WKNavigationActionPolicy> Function( - _i3.WKWebView, _i3.WKNavigationAction)? + _i6.Future setDecidePolicyForNavigationAction( + _i6.Future<_i4.WKNavigationActionPolicy> Function( + _i4.WKWebView, _i4.WKNavigationAction)? decidePolicyForNavigationAction) => (super.noSuchMethod( Invocation.method(#setDecidePolicyForNavigationAction, [decidePolicyForNavigationAction]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future setDidFailNavigation( - void Function(_i3.WKWebView, _i7.NSError)? didFailNavigation) => + _i6.Future setDidFailNavigation( + void Function(_i4.WKWebView, _i8.NSError)? didFailNavigation) => (super.noSuchMethod( Invocation.method(#setDidFailNavigation, [didFailNavigation]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future setDidFailProvisionalNavigation( - void Function(_i3.WKWebView, _i7.NSError)? + _i6.Future setDidFailProvisionalNavigation( + void Function(_i4.WKWebView, _i8.NSError)? didFailProvisionalNavigation) => (super.noSuchMethod( Invocation.method( #setDidFailProvisionalNavigation, [didFailProvisionalNavigation]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future setWebViewWebContentProcessDidTerminate( - void Function(_i3.WKWebView)? webViewWebContentProcessDidTerminate) => + _i6.Future setWebViewWebContentProcessDidTerminate( + void Function(_i4.WKWebView)? webViewWebContentProcessDidTerminate) => (super.noSuchMethod( Invocation.method(#setWebViewWebContentProcessDidTerminate, [webViewWebContentProcessDidTerminate]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeCopyable_1()) as _i3.Copyable); @override - _i5.Future addObserver(_i7.NSObject? observer, - {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => + _i6.Future addObserver(_i8.NSObject? observer, + {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => (super.noSuchMethod( Invocation.method( #addObserver, [observer], {#keyPath: keyPath, #options: options}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => + _i6.Future removeObserver(_i8.NSObject? observer, {String? keyPath}) => (super.noSuchMethod( Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future dispose() => - (super.noSuchMethod(Invocation.method(#dispose, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future setObserveValue( + _i6.Future setObserveValue( void Function( - String, _i7.NSObject, Map<_i7.NSKeyValueChangeKey, Object?>)? + String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? observeValue) => (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); } /// A class which mocks [WKPreferences]. /// /// See the documentation for Mockito's code generation for more information. -class MockWKPreferences extends _i1.Mock implements _i3.WKPreferences { +class MockWKPreferences extends _i1.Mock implements _i4.WKPreferences { MockWKPreferences() { _i1.throwOnMissingStub(this); } @override - _i5.Future setJavaScriptEnabled(bool? enabled) => + _i6.Future setJavaScriptEnabled(bool? enabled) => (super.noSuchMethod(Invocation.method(#setJavaScriptEnabled, [enabled]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future addObserver(_i8.NSObject? observer, + {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future removeObserver(_i8.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future setObserveValue( + void Function( + String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? + observeValue) => + (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeCopyable_1()) as _i3.Copyable); } /// A class which mocks [WKScriptMessageHandler]. /// /// See the documentation for Mockito's code generation for more information. class MockWKScriptMessageHandler extends _i1.Mock - implements _i3.WKScriptMessageHandler { + implements _i4.WKScriptMessageHandler { MockWKScriptMessageHandler() { _i1.throwOnMissingStub(this); } @override - _i5.Future setDidReceiveScriptMessage( - void Function(_i3.WKUserContentController, _i3.WKScriptMessage)? + _i6.Future setDidReceiveScriptMessage( + void Function(_i4.WKUserContentController, _i4.WKScriptMessage)? didReceiveScriptMessage) => (super.noSuchMethod( Invocation.method( #setDidReceiveScriptMessage, [didReceiveScriptMessage]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future addObserver(_i8.NSObject? observer, + {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future removeObserver(_i8.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future setObserveValue( + void Function( + String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? + observeValue) => + (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeCopyable_1()) as _i3.Copyable); } /// A class which mocks [WKWebView]. /// /// See the documentation for Mockito's code generation for more information. -class MockWKWebView extends _i1.Mock implements _i3.WKWebView { +class MockWKWebView extends _i1.Mock implements _i4.WKWebView { MockWKWebView() { _i1.throwOnMissingStub(this); } @override - _i3.WKWebViewConfiguration get configuration => + _i4.WKWebViewConfiguration get configuration => (super.noSuchMethod(Invocation.getter(#configuration), - returnValue: _FakeWKWebViewConfiguration_1()) - as _i3.WKWebViewConfiguration); + returnValue: _FakeWKWebViewConfiguration_2()) + as _i4.WKWebViewConfiguration); @override - _i4.UIScrollView get scrollView => + _i5.UIScrollView get scrollView => (super.noSuchMethod(Invocation.getter(#scrollView), - returnValue: _FakeUIScrollView_2()) as _i4.UIScrollView); + returnValue: _FakeUIScrollView_3()) as _i5.UIScrollView); @override - _i5.Future setUIDelegate(_i3.WKUIDelegate? delegate) => + _i6.Future setUIDelegate(_i4.WKUIDelegate? delegate) => (super.noSuchMethod(Invocation.method(#setUIDelegate, [delegate]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future setNavigationDelegate(_i3.WKNavigationDelegate? delegate) => + _i6.Future setNavigationDelegate(_i4.WKNavigationDelegate? delegate) => (super.noSuchMethod(Invocation.method(#setNavigationDelegate, [delegate]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future getUrl() => + _i6.Future getUrl() => (super.noSuchMethod(Invocation.method(#getUrl, []), - returnValue: Future.value()) as _i5.Future); + returnValue: Future.value()) as _i6.Future); @override - _i5.Future getEstimatedProgress() => + _i6.Future getEstimatedProgress() => (super.noSuchMethod(Invocation.method(#getEstimatedProgress, []), - returnValue: Future.value(0.0)) as _i5.Future); + returnValue: Future.value(0.0)) as _i6.Future); @override - _i5.Future loadRequest(_i7.NSUrlRequest? request) => + _i6.Future loadRequest(_i8.NSUrlRequest? request) => (super.noSuchMethod(Invocation.method(#loadRequest, [request]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future loadHtmlString(String? string, {String? baseUrl}) => + _i6.Future loadHtmlString(String? string, {String? baseUrl}) => (super.noSuchMethod( Invocation.method(#loadHtmlString, [string], {#baseUrl: baseUrl}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future loadFileUrl(String? url, {String? readAccessUrl}) => + _i6.Future loadFileUrl(String? url, {String? readAccessUrl}) => (super.noSuchMethod( Invocation.method( #loadFileUrl, [url], {#readAccessUrl: readAccessUrl}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future loadFlutterAsset(String? key) => + _i6.Future loadFlutterAsset(String? key) => (super.noSuchMethod(Invocation.method(#loadFlutterAsset, [key]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future canGoBack() => + _i6.Future canGoBack() => (super.noSuchMethod(Invocation.method(#canGoBack, []), - returnValue: Future.value(false)) as _i5.Future); + returnValue: Future.value(false)) as _i6.Future); @override - _i5.Future canGoForward() => + _i6.Future canGoForward() => (super.noSuchMethod(Invocation.method(#canGoForward, []), - returnValue: Future.value(false)) as _i5.Future); + returnValue: Future.value(false)) as _i6.Future); @override - _i5.Future goBack() => + _i6.Future goBack() => (super.noSuchMethod(Invocation.method(#goBack, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future goForward() => + _i6.Future goForward() => (super.noSuchMethod(Invocation.method(#goForward, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future reload() => + _i6.Future reload() => (super.noSuchMethod(Invocation.method(#reload, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future getTitle() => + _i6.Future getTitle() => (super.noSuchMethod(Invocation.method(#getTitle, []), - returnValue: Future.value()) as _i5.Future); + returnValue: Future.value()) as _i6.Future); @override - _i5.Future setAllowsBackForwardNavigationGestures(bool? allow) => + _i6.Future setAllowsBackForwardNavigationGestures(bool? allow) => (super.noSuchMethod( Invocation.method(#setAllowsBackForwardNavigationGestures, [allow]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future setCustomUserAgent(String? userAgent) => + _i6.Future setCustomUserAgent(String? userAgent) => (super.noSuchMethod(Invocation.method(#setCustomUserAgent, [userAgent]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future evaluateJavaScript(String? javaScriptString) => (super + _i6.Future evaluateJavaScript(String? javaScriptString) => (super .noSuchMethod(Invocation.method(#evaluateJavaScript, [javaScriptString]), - returnValue: Future.value()) as _i5.Future); + returnValue: Future.value()) as _i6.Future); @override - _i5.Future setBackgroundColor(_i6.Color? color) => + _i6.Future setBackgroundColor(_i7.Color? color) => (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future setOpaque(bool? opaque) => + _i6.Future setOpaque(bool? opaque) => (super.noSuchMethod(Invocation.method(#setOpaque, [opaque]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future addObserver(_i7.NSObject? observer, - {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => + _i6.Future addObserver(_i8.NSObject? observer, + {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => (super.noSuchMethod( Invocation.method( #addObserver, [observer], {#keyPath: keyPath, #options: options}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => + _i6.Future removeObserver(_i8.NSObject? observer, {String? keyPath}) => (super.noSuchMethod( Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future dispose() => - (super.noSuchMethod(Invocation.method(#dispose, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future setObserveValue( + _i6.Future setObserveValue( void Function( - String, _i7.NSObject, Map<_i7.NSKeyValueChangeKey, Object?>)? + String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? observeValue) => (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeCopyable_1()) as _i3.Copyable); } /// A class which mocks [WKWebViewConfiguration]. /// /// See the documentation for Mockito's code generation for more information. class MockWKWebViewConfiguration extends _i1.Mock - implements _i3.WKWebViewConfiguration { + implements _i4.WKWebViewConfiguration { MockWKWebViewConfiguration() { _i1.throwOnMissingStub(this); } @override - _i3.WKUserContentController get userContentController => + _i4.WKUserContentController get userContentController => (super.noSuchMethod(Invocation.getter(#userContentController), - returnValue: _FakeWKUserContentController_3()) - as _i3.WKUserContentController); + returnValue: _FakeWKUserContentController_4()) + as _i4.WKUserContentController); @override - _i3.WKPreferences get preferences => + _i4.WKPreferences get preferences => (super.noSuchMethod(Invocation.getter(#preferences), - returnValue: _FakeWKPreferences_4()) as _i3.WKPreferences); + returnValue: _FakeWKPreferences_5()) as _i4.WKPreferences); @override - _i3.WKWebsiteDataStore get websiteDataStore => + _i4.WKWebsiteDataStore get websiteDataStore => (super.noSuchMethod(Invocation.getter(#websiteDataStore), - returnValue: _FakeWKWebsiteDataStore_5()) as _i3.WKWebsiteDataStore); + returnValue: _FakeWKWebsiteDataStore_6()) as _i4.WKWebsiteDataStore); @override - _i5.Future setAllowsInlineMediaPlayback(bool? allow) => (super + _i6.Future setAllowsInlineMediaPlayback(bool? allow) => (super .noSuchMethod(Invocation.method(#setAllowsInlineMediaPlayback, [allow]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future setMediaTypesRequiringUserActionForPlayback( - Set<_i3.WKAudiovisualMediaType>? types) => + _i6.Future setMediaTypesRequiringUserActionForPlayback( + Set<_i4.WKAudiovisualMediaType>? types) => (super.noSuchMethod( Invocation.method( #setMediaTypesRequiringUserActionForPlayback, [types]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future addObserver(_i8.NSObject? observer, + {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future removeObserver(_i8.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future setObserveValue( + void Function( + String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? + observeValue) => + (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeCopyable_1()) as _i3.Copyable); } /// A class which mocks [WKWebsiteDataStore]. /// /// See the documentation for Mockito's code generation for more information. class MockWKWebsiteDataStore extends _i1.Mock - implements _i3.WKWebsiteDataStore { + implements _i4.WKWebsiteDataStore { MockWKWebsiteDataStore() { _i1.throwOnMissingStub(this); } @override - _i3.WKHttpCookieStore get httpCookieStore => + _i4.WKHttpCookieStore get httpCookieStore => (super.noSuchMethod(Invocation.getter(#httpCookieStore), - returnValue: _FakeWKHttpCookieStore_6()) as _i3.WKHttpCookieStore); + returnValue: _FakeWKHttpCookieStore_7()) as _i4.WKHttpCookieStore); @override - _i5.Future removeDataOfTypes( - Set<_i3.WKWebsiteDataType>? dataTypes, DateTime? since) => + _i6.Future removeDataOfTypes( + Set<_i4.WKWebsiteDataType>? dataTypes, DateTime? since) => (super.noSuchMethod( Invocation.method(#removeDataOfTypes, [dataTypes, since]), - returnValue: Future.value(false)) as _i5.Future); + returnValue: Future.value(false)) as _i6.Future); + @override + _i6.Future addObserver(_i8.NSObject? observer, + {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future removeObserver(_i8.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future setObserveValue( + void Function( + String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? + observeValue) => + (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeCopyable_1()) as _i3.Copyable); } /// A class which mocks [WKUIDelegate]. /// /// See the documentation for Mockito's code generation for more information. -class MockWKUIDelegate extends _i1.Mock implements _i3.WKUIDelegate { +class MockWKUIDelegate extends _i1.Mock implements _i4.WKUIDelegate { MockWKUIDelegate() { _i1.throwOnMissingStub(this); } @override - _i5.Future setOnCreateWebView( - void Function(_i3.WKWebViewConfiguration, _i3.WKNavigationAction)? + _i6.Future setOnCreateWebView( + void Function(_i4.WKWebViewConfiguration, _i4.WKNavigationAction)? onCreateWebView) => (super.noSuchMethod( Invocation.method(#setOnCreateWebView, [onCreateWebView]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future addObserver(_i8.NSObject? observer, + {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future removeObserver(_i8.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future setObserveValue( + void Function( + String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? + observeValue) => + (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeCopyable_1()) as _i3.Copyable); } /// A class which mocks [WKUserContentController]. /// /// See the documentation for Mockito's code generation for more information. class MockWKUserContentController extends _i1.Mock - implements _i3.WKUserContentController { + implements _i4.WKUserContentController { MockWKUserContentController() { _i1.throwOnMissingStub(this); } @override - _i5.Future addScriptMessageHandler( - _i3.WKScriptMessageHandler? handler, String? name) => + _i6.Future addScriptMessageHandler( + _i4.WKScriptMessageHandler? handler, String? name) => (super.noSuchMethod( Invocation.method(#addScriptMessageHandler, [handler, name]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future removeScriptMessageHandler(String? name) => (super + _i6.Future removeScriptMessageHandler(String? name) => (super .noSuchMethod(Invocation.method(#removeScriptMessageHandler, [name]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future removeAllScriptMessageHandlers() => (super.noSuchMethod( + _i6.Future removeAllScriptMessageHandlers() => (super.noSuchMethod( Invocation.method(#removeAllScriptMessageHandlers, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future addUserScript(_i3.WKUserScript? userScript) => + _i6.Future addUserScript(_i4.WKUserScript? userScript) => (super.noSuchMethod(Invocation.method(#addUserScript, [userScript]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i5.Future removeAllUserScripts() => + _i6.Future removeAllUserScripts() => (super.noSuchMethod(Invocation.method(#removeAllUserScripts, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future addObserver(_i8.NSObject? observer, + {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future removeObserver(_i8.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i6.Future setObserveValue( + void Function( + String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? + observeValue) => + (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i6.Future); + @override + _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeCopyable_1()) as _i3.Copyable); } /// A class which mocks [JavascriptChannelRegistry]. /// /// See the documentation for Mockito's code generation for more information. class MockJavascriptChannelRegistry extends _i1.Mock - implements _i8.JavascriptChannelRegistry { + implements _i9.JavascriptChannelRegistry { MockJavascriptChannelRegistry() { _i1.throwOnMissingStub(this); } @override - Map get channels => + Map get channels => (super.noSuchMethod(Invocation.getter(#channels), - returnValue: {}) - as Map); + returnValue: {}) + as Map); @override void onJavascriptChannelMessage(String? channel, String? message) => super.noSuchMethod( Invocation.method(#onJavascriptChannelMessage, [channel, message]), returnValueForMissingStub: null); @override - void updateJavascriptChannelsFromSet(Set<_i9.JavascriptChannel>? channels) => + void updateJavascriptChannelsFromSet(Set<_i10.JavascriptChannel>? channels) => super.noSuchMethod( Invocation.method(#updateJavascriptChannelsFromSet, [channels]), returnValueForMissingStub: null); @@ -526,17 +668,17 @@ class MockJavascriptChannelRegistry extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewPlatformCallbacksHandler extends _i1.Mock - implements _i8.WebViewPlatformCallbacksHandler { + implements _i9.WebViewPlatformCallbacksHandler { MockWebViewPlatformCallbacksHandler() { _i1.throwOnMissingStub(this); } @override - _i5.FutureOr onNavigationRequest({String? url, bool? isForMainFrame}) => + _i6.FutureOr onNavigationRequest({String? url, bool? isForMainFrame}) => (super.noSuchMethod( Invocation.method(#onNavigationRequest, [], {#url: url, #isForMainFrame: isForMainFrame}), - returnValue: Future.value(false)) as _i5.FutureOr); + returnValue: Future.value(false)) as _i6.FutureOr); @override void onPageStarted(String? url) => super.noSuchMethod(Invocation.method(#onPageStarted, [url]), @@ -550,7 +692,7 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock super.noSuchMethod(Invocation.method(#onProgress, [progress]), returnValueForMissingStub: null); @override - void onWebResourceError(_i10.WebResourceError? error) => + void onWebResourceError(_i11.WebResourceError? error) => super.noSuchMethod(Invocation.method(#onWebResourceError, [error]), returnValueForMissingStub: null); } @@ -559,26 +701,30 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewWidgetProxy extends _i1.Mock - implements _i11.WebViewWidgetProxy { + implements _i12.WebViewWidgetProxy { MockWebViewWidgetProxy() { _i1.throwOnMissingStub(this); } @override - _i3.WKWebView createWebView(_i3.WKWebViewConfiguration? configuration) => + _i4.WKWebView createWebView(_i4.WKWebViewConfiguration? configuration) => (super.noSuchMethod(Invocation.method(#createWebView, [configuration]), - returnValue: _FakeWKWebView_7()) as _i3.WKWebView); + returnValue: _FakeWKWebView_8()) as _i4.WKWebView); @override - _i3.WKScriptMessageHandler createScriptMessageHandler() => + _i4.WKScriptMessageHandler createScriptMessageHandler() => (super.noSuchMethod(Invocation.method(#createScriptMessageHandler, []), - returnValue: _FakeWKScriptMessageHandler_8()) - as _i3.WKScriptMessageHandler); + returnValue: _FakeWKScriptMessageHandler_9()) + as _i4.WKScriptMessageHandler); @override - _i3.WKUIDelegate createUIDelgate() => + _i4.WKUIDelegate createUIDelgate() => (super.noSuchMethod(Invocation.method(#createUIDelgate, []), - returnValue: _FakeWKUIDelegate_9()) as _i3.WKUIDelegate); + returnValue: _FakeWKUIDelegate_10()) as _i4.WKUIDelegate); @override - _i3.WKNavigationDelegate createNavigationDelegate() => (super.noSuchMethod( - Invocation.method(#createNavigationDelegate, []), - returnValue: _FakeWKNavigationDelegate_10()) as _i3.WKNavigationDelegate); + _i4.WKNavigationDelegate createNavigationDelegate( + {void Function(_i4.WKWebView, String?)? didFinishNavigation}) => + (super.noSuchMethod( + Invocation.method(#createNavigationDelegate, [], + {#didFinishNavigation: didFinishNavigation}), + returnValue: _FakeWKNavigationDelegate_11()) + as _i4.WKNavigationDelegate); } From 08e4ecba815892d3c805224a7d27a058b2ff845e Mon Sep 17 00:00:00 2001 From: Alexandre Ardhuin Date: Fri, 3 Jun 2022 14:46:11 +0200 Subject: [PATCH 379/844] ignore upcoming warnings in webview_flutter_platform_interface (#5855) --- .../webview_flutter_platform_interface/CHANGELOG.md | 4 ++++ .../lib/v4/src/types/load_request_params.dart | 2 ++ .../webview_flutter_platform_interface/pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index 8f6c413ce0cc..86b79335d7ba 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.9.1 + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). + ## 1.9.0 * Adds the first iteration of the v4 webview_flutter interface implementation. diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/load_request_params.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/load_request_params.dart index 2da51f8dc19f..a0d1c8821798 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/load_request_params.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/load_request_params.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. +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import import 'dart:typed_data'; import 'package:flutter/foundation.dart'; diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml index b3a20f8d029d..c14df52371a8 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutte issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview_flutter%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.9.0 +version: 1.9.1 environment: sdk: ">=2.12.0 <3.0.0" From d5fd7e437e1a574aac7da3728c9c1fb5ef3a65a9 Mon Sep 17 00:00:00 2001 From: Alexandre Ardhuin Date: Fri, 3 Jun 2022 14:46:41 +0200 Subject: [PATCH 380/844] ignore upcoming warnings in webview_flutter_wkwebview (#5856) --- .../webview_flutter/webview_flutter_wkwebview/CHANGELOG.md | 4 ++++ .../example/integration_test/webview_flutter_test.dart | 2 ++ .../lib/src/foundation/foundation.dart | 2 ++ .../webview_flutter/webview_flutter_wkwebview/pubspec.yaml | 2 +- 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 64a2b0974faf..f1cfcbc25375 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.1 + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). + ## 2.8.0 * Raises minimum Dart version to 2.17 and Flutter version to 3.0.0. 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 40018eee8d98..9687c7d56cbe 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 @@ -9,6 +9,8 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import import 'dart:typed_data'; import 'package:flutter/foundation.dart'; 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 ca3ba780435c..5dfb78c267de 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 @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import import 'dart:typed_data'; import 'package:flutter/foundation.dart'; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 5a8be07cbb91..c69d0d51b622 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/plugins/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: 2.8.0 +version: 2.8.1 environment: sdk: ">=2.17.0 <3.0.0" From c92e020481d72873727e631af36b95ca3cc16767 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 3 Jun 2022 14:43:14 -0400 Subject: [PATCH 381/844] Roll Flutter from e8995736bf55 to 889a15eb9df6 (176 revisions) (#5905) --- .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 da8d2239a3a1..72783c5d7072 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e8995736bf556458db4e908d918b48cae0fa1992 +889a15eb9df6961b9a4dc3bf751ff67cb54a2065 From eda3e80e7d101936f8923883bc43b657a965c429 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Fri, 3 Jun 2022 15:48:12 -0700 Subject: [PATCH 382/844] [image_picker_android] Add jetifier back with gradle and androidx upgrades (#5890) --- .../image_picker/image_picker_android/CHANGELOG.md | 4 ++++ .../image_picker_android/android/build.gradle | 12 ++++++------ .../example/android/app/build.gradle | 2 +- .../example/android/build.gradle | 2 +- .../example/android/gradle.properties | 2 +- .../android/gradle/wrapper/gradle-wrapper.properties | 2 +- .../image_picker/image_picker_android/pubspec.yaml | 2 +- 7 files changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index eede63435026..a5cc6139bf0f 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.5 + +* Updates gradle to 7.1.2. + ## 0.8.4+13 * Minor fixes for new analysis options. diff --git a/packages/image_picker/image_picker_android/android/build.gradle b/packages/image_picker/image_picker_android/android/build.gradle index 928c7cda8b03..516c7ae9770c 100755 --- a/packages/image_picker/image_picker_android/android/build.gradle +++ b/packages/image_picker/image_picker_android/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:7.1.2' } } @@ -33,14 +33,14 @@ android { disable 'GradleDependency' } dependencies { - implementation 'androidx.core:core:1.0.2' - implementation 'androidx.annotation:annotation:1.0.0' - implementation 'androidx.exifinterface:exifinterface:1.3.0' + implementation 'androidx.core:core:1.8.0' + implementation 'androidx.annotation:annotation:1.3.0' + implementation 'androidx.exifinterface:exifinterface:1.3.3' testImplementation 'junit:junit:4.12' testImplementation 'org.mockito:mockito-core:3.10.0' - testImplementation 'androidx.test:core:1.2.0' - testImplementation "org.robolectric:robolectric:4.3.1" + testImplementation 'androidx.test:core:1.4.0' + testImplementation "org.robolectric:robolectric:4.8.1" } compileOptions { diff --git a/packages/image_picker/image_picker_android/example/android/app/build.gradle b/packages/image_picker/image_picker_android/example/android/app/build.gradle index e73e3fe01003..c8e8f3f5b90d 100755 --- a/packages/image_picker/image_picker_android/example/android/app/build.gradle +++ b/packages/image_picker/image_picker_android/example/android/app/build.gradle @@ -63,5 +63,5 @@ dependencies { testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - api 'androidx.test:core:1.2.0' + api 'androidx.test:core:1.4.0' } diff --git a/packages/image_picker/image_picker_android/example/android/build.gradle b/packages/image_picker/image_picker_android/example/android/build.gradle index e101ac08df55..e29a4431f2ae 100755 --- a/packages/image_picker/image_picker_android/example/android/build.gradle +++ b/packages/image_picker/image_picker_android/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:7.1.2' } } diff --git a/packages/image_picker/image_picker_android/example/android/gradle.properties b/packages/image_picker/image_picker_android/example/android/gradle.properties index d12b9a8297e5..94adc3a3f97a 100755 --- a/packages/image_picker/image_picker_android/example/android/gradle.properties +++ b/packages/image_picker/image_picker_android/example/android/gradle.properties @@ -1,3 +1,3 @@ org.gradle.jvmargs=-Xmx1536M -android.enableR8=true android.useAndroidX=true +android.enableJetifier=true diff --git a/packages/image_picker/image_picker_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/image_picker/image_picker_android/example/android/gradle/wrapper/gradle-wrapper.properties index 019065d1d650..cb24abda10ae 100644 --- a/packages/image_picker/image_picker_android/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/image_picker/image_picker_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index 6accfb0eb4ac..3d17e8ae7745 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/plugins/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.4+13 +version: 0.8.5 environment: sdk: ">=2.14.0 <3.0.0" From 12dcd85a1eb2e382673671ff1d19ceb69db15914 Mon Sep 17 00:00:00 2001 From: godofredoc Date: Fri, 3 Jun 2022 16:33:11 -0700 Subject: [PATCH 383/844] Update filter to run tasks only on PRs and main branch. (#5883) --- .cirrus.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index c786a4a14418..3f4be974b4d2 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,9 +1,7 @@ gcp_credentials: ENCRYPTED[!ebad0a1f4f7a446b77944c33651460a7ab010b4617273cb016cf354eb8fc22aa92e37a3c58bfa4a0c40a799351e027a6!] -# Don't run on release tags since it creates O(n^2) tasks where n is the -# number of plugins. -# Don't run on 'master' since it's a mirror of 'main'. -only_if: $CIRRUS_TAG == '' && $CIRRUS_BRANCH != 'master' +# Run on PRs and main branch post submit only. +only_if: $CIRRUS_PR != '' || $CIRRUS_BRANCH == 'main' env: CHANNEL: "master" # Default to master when not explicitly set by a task. PLUGIN_TOOL: "./script/tool/bin/flutter_plugin_tools.dart" From fc3f1cc73aa221ff78444ef0ccf7515c27b4ea24 Mon Sep 17 00:00:00 2001 From: godofredoc Date: Fri, 3 Jun 2022 18:13:09 -0700 Subject: [PATCH 384/844] Revert "All the workflows have been migrated to use main. (#5874)" (#5907) --- .github/workflows/mirror.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/mirror.yml diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml new file mode 100644 index 000000000000..9cfaf20d6cf0 --- /dev/null +++ b/.github/workflows/mirror.yml @@ -0,0 +1,28 @@ +# 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. + +# Mirror master to main branches in the plugins repository. +on: + push: + branches: + - 'main' + +# Declare default permissions as read only. +permissions: read-all + +jobs: + mirror_job: + permissions: + pull-requests: write + runs-on: ubuntu-latest + if: ${{ github.repository == 'flutter/plugins' }} + name: Mirror main branch to master branch + steps: + - name: Mirror action step + id: mirror + uses: google/mirror-branch-action@c6b07e441a7ffc5ae15860c1d0a8107a3a151db8 + with: + github-token: ${{ secrets.FLUTTERMIRRORINGBOT_TOKEN }} + source: 'main' + dest: 'master' From 1037c73aa73ead76a4fca04e46b27df67c58c776 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 4 Jun 2022 12:03:08 -0400 Subject: [PATCH 385/844] Roll Flutter from 889a15eb9df6 to 2e1ebd83b3f6 (6 revisions) (#5910) --- .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 72783c5d7072..79e5568d5887 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -889a15eb9df6961b9a4dc3bf751ff67cb54a2065 +2e1ebd83b3f6d7c38f8ead3a4ae498369273d4cf From c9e3482cd48e3cc127e9b529c8e7ade73cbfd285 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 5 Jun 2022 13:18:10 -0400 Subject: [PATCH 386/844] Roll Flutter from 2e1ebd83b3f6 to 52c47e96a33d (9 revisions) (#5911) --- .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 79e5568d5887..01e3344219df 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2e1ebd83b3f6d7c38f8ead3a4ae498369273d4cf +52c47e96a33df14a5f43216fa88cf2165ea4d659 From f781b036c830952fc84a85ffa37f2f51328fa0a4 Mon Sep 17 00:00:00 2001 From: Drew Roen <102626803+drewroengoogle@users.noreply.github.com> Date: Mon, 6 Jun 2022 14:16:39 +0000 Subject: [PATCH 387/844] Add automatic updating of github-actions (#5880) --- .github/dependabot.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 676ac1da5930..e24ee2371c24 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -287,3 +287,11 @@ updates: schedule: interval: "daily" open-pull-requests-limit: 10 + + - package-ecosystem: "github-actions" + directory: "/" + commit-message: + prefix: "[github_actions]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 From 8ded25d1b674e1461baf0ae236d208bdce78ac0f Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 6 Jun 2022 12:56:12 -0400 Subject: [PATCH 388/844] Revert "Add automatic updating of github-actions (#5880)" (#5917) This reverts commit f781b036c830952fc84a85ffa37f2f51328fa0a4. --- .github/dependabot.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e24ee2371c24..676ac1da5930 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -287,11 +287,3 @@ updates: schedule: interval: "daily" open-pull-requests-limit: 10 - - - package-ecosystem: "github-actions" - directory: "/" - commit-message: - prefix: "[github_actions]" - schedule: - interval: "daily" - open-pull-requests-limit: 10 From ee2476612731f7074e4e5a663a42cddd7a579ba3 Mon Sep 17 00:00:00 2001 From: Kyle Finlinson Date: Mon, 6 Jun 2022 11:43:09 -0600 Subject: [PATCH 389/844] [video_player] Android: Rotate videos recorded in landscapeRight (#3820) --- .../video_player/video_player/CHANGELOG.md | 3 +- .../video_player/lib/video_player.dart | 31 +++++++++++++++++-- .../video_player/video_player/pubspec.yaml | 6 ++-- .../video_player/test/video_player_test.dart | 24 ++++++++++++++ 4 files changed, 58 insertions(+), 6 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index bfefb9902768..8dca057f5a08 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.4.3 +* Fixes Android to correctly display videos recorded in landscapeRight (https://github.com/flutter/flutter/issues/60327). * Fixes order-dependent unit tests. ## 2.4.2 diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index a6ec51015c33..95bdc07dc86c 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'dart:io'; +import 'dart:math' as math; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -48,6 +49,7 @@ class VideoPlayerValue { this.isBuffering = false, this.volume = 1.0, this.playbackSpeed = 1.0, + this.rotationCorrection = 0, this.errorDescription, }); @@ -111,6 +113,9 @@ class VideoPlayerValue { /// The [size] of the currently loaded video. final Size size; + /// Degrees to rotate the video (clockwise) so it is displayed correctly. + final int rotationCorrection; + /// Indicates whether or not the video has been loaded and is ready to play. final bool isInitialized; @@ -136,7 +141,7 @@ class VideoPlayerValue { } /// Returns a new instance that has the same values as this current instance, - /// except for any overrides passed in as arguments to [copyWidth]. + /// except for any overrides passed in as arguments to [copyWith]. VideoPlayerValue copyWith({ Duration? duration, Size? size, @@ -150,6 +155,7 @@ class VideoPlayerValue { bool? isBuffering, double? volume, double? playbackSpeed, + int? rotationCorrection, String? errorDescription = _defaultErrorDescription, }) { return VideoPlayerValue( @@ -165,6 +171,7 @@ class VideoPlayerValue { isBuffering: isBuffering ?? this.isBuffering, volume: volume ?? this.volume, playbackSpeed: playbackSpeed ?? this.playbackSpeed, + rotationCorrection: rotationCorrection ?? this.rotationCorrection, errorDescription: errorDescription != _defaultErrorDescription ? errorDescription : this.errorDescription, @@ -368,6 +375,7 @@ class VideoPlayerController extends ValueNotifier { value = value.copyWith( duration: event.duration, size: event.size, + rotationCorrection: event.rotationCorrection, isInitialized: event.duration != null, errorDescription: null, ); @@ -761,10 +769,29 @@ class _VideoPlayerState extends State { Widget build(BuildContext context) { return _textureId == VideoPlayerController.kUninitializedTextureId ? Container() - : _videoPlayerPlatform.buildView(_textureId); + : _VideoPlayerWithRotation( + rotation: widget.controller.value.rotationCorrection, + child: _videoPlayerPlatform.buildView(_textureId), + ); } } +class _VideoPlayerWithRotation extends StatelessWidget { + const _VideoPlayerWithRotation( + {Key? key, required this.rotation, required this.child}) + : super(key: key); + final int rotation; + final Widget child; + + @override + Widget build(BuildContext context) => rotation == 0 + ? child + : Transform.rotate( + angle: rotation * math.pi / 180, + child: child, + ); +} + /// Used to configure the [VideoProgressIndicator] widget's colors for how it /// describes the video's status. /// diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 05cfcf154f88..8b2fc5780e3d 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/plugins/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.4.2 +version: 2.4.3 environment: sdk: ">=2.14.0 <3.0.0" @@ -23,9 +23,9 @@ dependencies: flutter: sdk: flutter html: ^0.15.0 - video_player_android: ^2.2.17 + video_player_android: ^2.3.5 video_player_avfoundation: ^2.2.17 - video_player_platform_interface: ^5.1.0 + video_player_platform_interface: ^5.1.1 video_player_web: ^2.0.0 dev_dependencies: diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index 83a49882ee9a..f4eda111fb92 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'dart:io'; +import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -15,6 +16,8 @@ class FakeController extends ValueNotifier implements VideoPlayerController { FakeController() : super(VideoPlayerValue(duration: Duration.zero)); + FakeController.value(VideoPlayerValue value) : super(value); + @override Future dispose() async { super.dispose(); @@ -149,6 +152,27 @@ void main() { findsOneWidget); }); + testWidgets('non-zero rotationCorrection value is used', + (WidgetTester tester) async { + final FakeController controller = FakeController.value( + VideoPlayerValue(duration: Duration.zero, rotationCorrection: 180)); + controller.textureId = 1; + await tester.pumpWidget(VideoPlayer(controller)); + final Transform actualRotationCorrection = + find.byType(Transform).evaluate().single.widget as Transform; + expect( + actualRotationCorrection.transform, equals(Matrix4.rotationZ(math.pi))); + }); + + testWidgets('no transform when rotationCorrection is zero', + (WidgetTester tester) async { + final FakeController controller = FakeController.value( + VideoPlayerValue(duration: Duration.zero, rotationCorrection: 0)); + controller.textureId = 1; + await tester.pumpWidget(VideoPlayer(controller)); + expect(find.byType(Transform), findsNothing); + }); + group('ClosedCaption widget', () { testWidgets('uses a default text style', (WidgetTester tester) async { const String text = 'foo'; From 05aa4f4ea49a337b405a89ff0d55e02360a8b14a Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 6 Jun 2022 13:46:51 -0400 Subject: [PATCH 390/844] Re-add tag filter (#5918) Re-adding filter removed in https://github.com/flutter/plugins/pull/5883 per [discussion](https://github.com/flutter/plugins/pull/5883/files#r887442326) --- .cirrus.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 3f4be974b4d2..73d26b55a51d 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,7 +1,7 @@ gcp_credentials: ENCRYPTED[!ebad0a1f4f7a446b77944c33651460a7ab010b4617273cb016cf354eb8fc22aa92e37a3c58bfa4a0c40a799351e027a6!] -# Run on PRs and main branch post submit only. -only_if: $CIRRUS_PR != '' || $CIRRUS_BRANCH == 'main' +# Run on PRs and main branch post submit only. Don't run tests when tagging. +only_if: $CIRRUS_TAG == '' && ($CIRRUS_PR != '' || $CIRRUS_BRANCH == 'main') env: CHANNEL: "master" # Default to master when not explicitly set by a task. PLUGIN_TOOL: "./script/tool/bin/flutter_plugin_tools.dart" From e79709cc0c2e89faba66332059890559d8c2b8c9 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 6 Jun 2022 15:23:10 -0400 Subject: [PATCH 391/844] [various] Clean up obsolete references to "master" (#5912) --- .ci/scripts/prepare_tool.sh | 2 +- .cirrus.yml | 3 - .github/PULL_REQUEST_TEMPLATE.md | 6 +- CONTRIBUTING.md | 4 +- README.md | 4 +- packages/camera/camera/README.md | 2 +- .../lib/src/shims/dart_ui_fake.dart | 8 +- packages/camera/camera_windows/CHANGELOG.md | 4 + packages/camera/camera_windows/pubspec.yaml | 4 +- packages/e2e/README.md | 2 +- .../internal/protocol/impl/DartVmService.java | 2 +- .../internal/protocol/impl/GetVmResponse.java | 2 +- .../file_selector_macos/CHANGELOG.md | 4 + .../file_selector_macos/pubspec.yaml | 4 +- .../file_selector_windows/CHANGELOG.md | 4 + .../file_selector_windows/pubspec.yaml | 4 +- .../lib/src/shims/dart_ui_fake.dart | 8 +- .../google_sign_in/CHANGELOG.md | 4 + .../google_sign_in/google_sign_in/README.md | 2 +- .../google_sign_in/pubspec.yaml | 2 +- .../ios/google_sign_in_ios.podspec | 2 +- .../google_sign_in_web/CHANGELOG.md | 4 + .../google_sign_in_web/README.md | 8 +- .../google_sign_in_web/pubspec.yaml | 2 +- .../in_app_purchase/CHANGELOG.md | 4 + .../in_app_purchase/in_app_purchase/README.md | 8 +- .../in_app_purchase/pubspec.yaml | 2 +- .../in_app_purchase_android/CHANGELOG.md | 4 + .../in_app_purchase_android/README.md | 2 +- .../inapppurchase/MethodCallHandlerImpl.java | 2 +- .../in_app_purchase_android/pubspec.yaml | 2 +- .../in_app_purchase_storekit/CHANGELOG.md | 4 + .../in_app_purchase_storekit/README.md | 2 +- .../in_app_purchase_storekit/pubspec.yaml | 2 +- packages/integration_test/README.md | 253 ------------------ .../local_auth_android/CHANGELOG.md | 4 + .../local_auth_android/pubspec.yaml | 4 +- .../local_auth/local_auth_ios/CHANGELOG.md | 4 + .../local_auth/local_auth_ios/pubspec.yaml | 4 +- .../CHANGELOG.md | 3 +- .../pubspec.yaml | 4 +- .../local_auth_windows/CHANGELOG.md | 4 + .../local_auth_windows/pubspec.yaml | 6 +- .../path_provider/path_provider/CHANGELOG.md | 3 +- .../path_provider/path_provider/README.md | 2 +- .../path_provider/path_provider/pubspec.yaml | 2 +- .../macos/path_provider_macos.podspec | 2 +- .../quick_actions_android/CHANGELOG.md | 6 +- .../quick_actions_android/README.md | 2 +- .../quick_actions_android/pubspec.yaml | 2 +- .../quick_actions_ios/CHANGELOG.md | 6 +- .../quick_actions/quick_actions_ios/README.md | 2 +- .../quick_actions_ios/pubspec.yaml | 2 +- .../macos/shared_preferences_macos.podspec | 2 +- .../ios/url_launcher_ios.podspec | 2 +- .../macos/url_launcher_macos.podspec | 2 +- .../lib/src/shims/dart_ui_fake.dart | 8 +- .../video_player/video_player/CHANGELOG.md | 4 + packages/video_player/video_player/README.md | 2 +- .../video_player/video_player/pubspec.yaml | 2 +- .../video_player_android/CHANGELOG.md | 4 + .../video_player_android/CONTRIBUTING.md | 2 +- .../video_player_android/pubspec.yaml | 4 +- .../video_player_avfoundation/CHANGELOG.md | 4 + .../video_player_avfoundation/CONTRIBUTING.md | 2 +- .../video_player_avfoundation/pubspec.yaml | 4 +- .../CHANGELOG.md | 3 +- .../CONTRIBUTING.md | 2 +- .../pubspec.yaml | 2 +- .../lib/src/shims/dart_ui_fake.dart | 8 +- .../webview_flutter/CHANGELOG.md | 6 +- .../webview_flutter_android/CHANGELOG.md | 4 + .../webview_flutter_android/README.md | 2 +- .../webview_flutter_android/pubspec.yaml | 2 +- .../lib/shims/dart_ui_fake.dart | 8 +- script/install_chromium.sh | 2 +- script/tool/README.md | 2 +- .../tool/lib/src/pubspec_check_command.dart | 5 + .../tool/test/pubspec_check_command_test.dart | 32 ++- 79 files changed, 205 insertions(+), 353 deletions(-) diff --git a/.ci/scripts/prepare_tool.sh b/.ci/scripts/prepare_tool.sh index 1095e2189a36..f93694bf1ff6 100644 --- a/.ci/scripts/prepare_tool.sh +++ b/.ci/scripts/prepare_tool.sh @@ -4,7 +4,7 @@ # found in the LICENSE file. # To set FETCH_HEAD for "git merge-base" to work -git fetch origin master +git fetch origin main cd script/tool dart pub get diff --git a/.cirrus.yml b/.cirrus.yml index 73d26b55a51d..699ec264db77 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -86,9 +86,6 @@ task: - dart pub run test - name: publishable env: - # TODO (mvanbeusekom): Temporary override to "stable" because of failure on "master". - # Remove override once https://github.com/dart-lang/pub/issues/3152 is resolved. - CHANNEL: stable CHANGE_DESC: "$TMPDIR/change-description.txt" version_check_script: # For pre-submit, pass the PR description to the script to allow for diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index e19d8e1d80cd..9fe5a37a4fa8 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -21,9 +21,9 @@ If you need help, consider asking for advice on the #hackers-new channel on [Discord]. -[Contributor Guide]: https://github.com/flutter/plugins/blob/master/CONTRIBUTING.md +[Contributor Guide]: https://github.com/flutter/plugins/blob/main/CONTRIBUTING.md [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene -[relevant style guides]: https://github.com/flutter/plugins/blob/master/CONTRIBUTING.md#style +[relevant style guides]: https://github.com/flutter/plugins/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 @@ -31,5 +31,5 @@ If you need help, consider asking for advice on the #hackers-new channel on [Dis [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 -[the auto-formatter]: https://github.com/flutter/plugins/blob/master/script/tool/README.md#format-code +[the auto-formatter]: https://github.com/flutter/plugins/blob/main/script/tool/README.md#format-code [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 52518202fa84..c2d44d50049b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing to Flutter Plugins -[![Build Status](https://api.cirrus-ci.com/github/flutter/plugins.svg)](https://cirrus-ci.com/github/flutter/plugins/master) +[![Build Status](https://api.cirrus-ci.com/github/flutter/plugins.svg)](https://cirrus-ci.com/github/flutter/plugins/main) _See also: [Flutter's code of conduct](https://github.com/flutter/flutter/blob/master/CODE_OF_CONDUCT.md)_ @@ -35,7 +35,7 @@ use, and use auto-formatters: - [C++](https://google.github.io/styleguide/cppguide.html) formatted with `clang-format` - **Note**: The Linux plugins generally follow idiomatic GObject-based C style. See [the engine style - notes](https://github.com/flutter/engine/blob/master/CONTRIBUTING.md#style) + notes](https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style) for more details, and exceptions. - [Java](https://google.github.io/styleguide/javaguide.html) formatted with `google-java-format` diff --git a/README.md b/README.md index cc6543d4ecee..edcffe208cad 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Flutter plugins -[![Build Status](https://api.cirrus-ci.com/github/flutter/plugins.svg)](https://cirrus-ci.com/github/flutter/plugins/master) +[![Build Status](https://api.cirrus-ci.com/github/flutter/plugins.svg)](https://cirrus-ci.com/github/flutter/plugins/main) [![Release Status](https://github.com/flutter/plugins/actions/workflows/release.yml/badge.svg)](https://github.com/flutter/plugins/actions/workflows/release.yml) This repo is a companion repo to the main [flutter @@ -34,7 +34,7 @@ is ready, you can [publish](https://flutter.dev/developing-packages/#publish) it to the [pub repository](https://pub.dev/). If you wish to contribute a change to any of the existing plugins in this repo, -please review our [contribution guide](https://github.com/flutter/plugins/blob/master/CONTRIBUTING.md), +please review our [contribution guide](https://github.com/flutter/plugins/blob/main/CONTRIBUTING.md), and send a [pull request](https://github.com/flutter/plugins/pulls). ## Plugins diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index ec9d7379c60b..610b7175b533 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -59,7 +59,7 @@ For web integration details, see the ### Handling Lifecycle states -As of version [0.5.0](https://github.com/flutter/plugins/blob/master/packages/camera/CHANGELOG.md#050) of the camera plugin, lifecycle changes are no longer handled by the plugin. This means developers are now responsible to control camera resources when the lifecycle state is updated. Failure to do so might lead to unexpected behavior (for example as described in issue [#39109](https://github.com/flutter/flutter/issues/39109)). Handling lifecycle changes can be done by overriding the `didChangeAppLifecycleState` method like so: +As of version [0.5.0](https://github.com/flutter/plugins/blob/main/packages/camera/CHANGELOG.md#050) of the camera plugin, lifecycle changes are no longer handled by the plugin. This means developers are now responsible to control camera resources when the lifecycle state is updated. Failure to do so might lead to unexpected behavior (for example as described in issue [#39109](https://github.com/flutter/flutter/issues/39109)). Handling lifecycle changes can be done by overriding the `didChangeAppLifecycleState` method like so: ```dart diff --git a/packages/camera/camera_web/lib/src/shims/dart_ui_fake.dart b/packages/camera/camera_web/lib/src/shims/dart_ui_fake.dart index 8757ca22be17..40d8f1903111 100644 --- a/packages/camera/camera_web/lib/src/shims/dart_ui_fake.dart +++ b/packages/camera/camera_web/lib/src/shims/dart_ui_fake.dart @@ -11,10 +11,10 @@ import 'dart:html' as html; // ignore_for_file: camel_case_types /// Shim for web_ui engine.PlatformViewRegistry -/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L62 +/// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/ui.dart#L62 class platformViewRegistry { /// Shim for registerViewFactory - /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L72 + /// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/ui.dart#L72 static bool registerViewFactory( String viewTypeId, html.Element Function(int viewId) viewFactory) { return false; @@ -22,10 +22,10 @@ class platformViewRegistry { } /// Shim for web_ui engine.AssetManager. -/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L12 +/// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/src/engine/assets.dart#L12 class webOnlyAssetManager { /// Shim for getAssetUrl. - /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L45 + /// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/src/engine/assets.dart#L45 static String getAssetUrl(String asset) => ''; } diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index 0f3bf441b05a..f84e442c68da 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.0+2 + +* Updates references to the obsolete master branch. + ## 0.1.0+1 * Removes unnecessary imports. diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index fe655b04e8c8..b519668c431a 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -1,8 +1,8 @@ name: camera_windows description: A Flutter plugin for getting information about and controlling the camera on Windows. -repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera_windows +repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.1.0+1 +version: 0.1.0+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/e2e/README.md b/packages/e2e/README.md index e86126e4cc56..89c81f8c6e27 100644 --- a/packages/e2e/README.md +++ b/packages/e2e/README.md @@ -1,3 +1,3 @@ # e2e (deprecated) -This package has been moved to [integration_test](https://github.com/flutter/plugins/tree/master/packages/integration_test). +This package has been moved to [`integration_test` in the Flutter SDK](https://github.com/flutter/flutter/tree/master/packages/integration_test). diff --git a/packages/espresso/android/src/main/java/androidx/test/espresso/flutter/internal/protocol/impl/DartVmService.java b/packages/espresso/android/src/main/java/androidx/test/espresso/flutter/internal/protocol/impl/DartVmService.java index a1cdd977066c..a8ddfc6bb5eb 100644 --- a/packages/espresso/android/src/main/java/androidx/test/espresso/flutter/internal/protocol/impl/DartVmService.java +++ b/packages/espresso/android/src/main/java/androidx/test/espresso/flutter/internal/protocol/impl/DartVmService.java @@ -42,7 +42,7 @@ * An implementation of the Espresso-Flutter testing protocol by using the testing APIs exposed by * Dart VM service protocol. * - * @see Dart VM + * @see Dart VM * Service Protocol. */ public final class DartVmService implements FlutterTestingProtocol { diff --git a/packages/espresso/android/src/main/java/androidx/test/espresso/flutter/internal/protocol/impl/GetVmResponse.java b/packages/espresso/android/src/main/java/androidx/test/espresso/flutter/internal/protocol/impl/GetVmResponse.java index 94cac364ddc7..0f4815cd2571 100644 --- a/packages/espresso/android/src/main/java/androidx/test/espresso/flutter/internal/protocol/impl/GetVmResponse.java +++ b/packages/espresso/android/src/main/java/androidx/test/espresso/flutter/internal/protocol/impl/GetVmResponse.java @@ -15,7 +15,7 @@ /** * Represents a response of a getVM() + * href="https://github.com/dart-lang/sdk/blob/main/runtime/vm/service/service.md#getvm">getVM() * request. */ public class GetVmResponse { diff --git a/packages/file_selector/file_selector_macos/CHANGELOG.md b/packages/file_selector/file_selector_macos/CHANGELOG.md index 19724a513a9e..e909fe5a4377 100644 --- a/packages/file_selector/file_selector_macos/CHANGELOG.md +++ b/packages/file_selector/file_selector_macos/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.2+2 + +* Updates references to the obsolete master branch. + ## 0.8.2+1 * Removes unnecessary imports. diff --git a/packages/file_selector/file_selector_macos/pubspec.yaml b/packages/file_selector/file_selector_macos/pubspec.yaml index e6f8e9b3c212..8c6d1f7c81ce 100644 --- a/packages/file_selector/file_selector_macos/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/pubspec.yaml @@ -1,8 +1,8 @@ name: file_selector_macos description: macOS implementation of the file_selector plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_macos +repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.2+1 +version: 0.8.2+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_windows/CHANGELOG.md b/packages/file_selector/file_selector_windows/CHANGELOG.md index c242717c3267..d3278a534154 100644 --- a/packages/file_selector/file_selector_windows/CHANGELOG.md +++ b/packages/file_selector/file_selector_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.2+2 + +* Updates references to the obsolete master branch. + ## 0.8.2+1 * Removes unnecessary imports. diff --git a/packages/file_selector/file_selector_windows/pubspec.yaml b/packages/file_selector/file_selector_windows/pubspec.yaml index 3ca568004ca9..7c933b2778d8 100644 --- a/packages/file_selector/file_selector_windows/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/pubspec.yaml @@ -1,8 +1,8 @@ name: file_selector_windows description: Windows implementation of the file_selector plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/file_selector/file_selector_windows +repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.2+1 +version: 0.8.2+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui_fake.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui_fake.dart index 8757ca22be17..40d8f1903111 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui_fake.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/shims/dart_ui_fake.dart @@ -11,10 +11,10 @@ import 'dart:html' as html; // ignore_for_file: camel_case_types /// Shim for web_ui engine.PlatformViewRegistry -/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L62 +/// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/ui.dart#L62 class platformViewRegistry { /// Shim for registerViewFactory - /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L72 + /// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/ui.dart#L72 static bool registerViewFactory( String viewTypeId, html.Element Function(int viewId) viewFactory) { return false; @@ -22,10 +22,10 @@ class platformViewRegistry { } /// Shim for web_ui engine.AssetManager. -/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L12 +/// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/src/engine/assets.dart#L12 class webOnlyAssetManager { /// Shim for getAssetUrl. - /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L45 + /// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/src/engine/assets.dart#L45 static String getAssetUrl(String asset) => ''; } diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index ad21ee245359..d3f0bdaea17e 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.3.3 + +* Updates references to the obsolete master branch. + ## 5.3.2 * Enables mocking models by changing overridden operator == parameter type from `dynamic` to `Object`. diff --git a/packages/google_sign_in/google_sign_in/README.md b/packages/google_sign_in/google_sign_in/README.md index 2d6fa7cb6a12..5ede3be051b2 100644 --- a/packages/google_sign_in/google_sign_in/README.md +++ b/packages/google_sign_in/google_sign_in/README.md @@ -126,4 +126,4 @@ Future _handleSignIn() async { ## Example Find the example wiring in the -[Google sign-in example application](https://github.com/flutter/plugins/blob/master/packages/google_sign_in/google_sign_in/example/lib/main.dart). +[Google sign-in example application](https://github.com/flutter/plugins/blob/main/packages/google_sign_in/google_sign_in/example/lib/main.dart). diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 6862a5560f83..9ea09dd1aeb4 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/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 and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.3.2 +version: 5.3.3 environment: diff --git a/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec b/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec index 18a213579a23..4e307098fd6d 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec +++ b/packages/google_sign_in/google_sign_in_ios/ios/google_sign_in_ios.podspec @@ -11,7 +11,7 @@ Enables Google Sign-In in Flutter apps. s.homepage = 'https://github.com/flutter/plugins/tree/main/packages/google_sign_in' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/google_sign_in/google_sign_in_ios' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in_ios' } s.source_files = 'Classes/**/*.{h,m}' s.public_header_files = 'Classes/**/*.h' s.module_map = 'Classes/FLTGoogleSignInPlugin.modulemap' 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 9a6b07650c64..672b1b2ca857 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.10.1+3 + +* Updates references to the obsolete master branch. + ## 0.10.1+2 * Minor fixes for new analysis options. diff --git a/packages/google_sign_in/google_sign_in_web/README.md b/packages/google_sign_in/google_sign_in_web/README.md index 463603e73e54..6f6dcf2dbf15 100644 --- a/packages/google_sign_in/google_sign_in_web/README.md +++ b/packages/google_sign_in/google_sign_in_web/README.md @@ -79,19 +79,19 @@ Future _handleSignIn() async { ## Example -Find the example wiring in the [Google sign-in example application](https://github.com/flutter/plugins/blob/master/packages/google_sign_in/google_sign_in/example/lib/main.dart). +Find the example wiring in the [Google sign-in example application](https://github.com/flutter/plugins/blob/main/packages/google_sign_in/google_sign_in/example/lib/main.dart). ## API details -See the [google_sign_in.dart](https://github.com/flutter/plugins/blob/master/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart) for more API details. +See the [google_sign_in.dart](https://github.com/flutter/plugins/blob/main/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart) for more API details. ## Contributions and Testing Tests are crucial for contributions to this package. All new contributions should be reasonably tested. -**Check the [`test/README.md` file](https://github.com/flutter/plugins/blob/master/packages/google_sign_in/google_sign_in_web/test/README.md)** for more information on how to run tests on this package. +**Check the [`test/README.md` file](https://github.com/flutter/plugins/blob/main/packages/google_sign_in/google_sign_in_web/test/README.md)** for more information on how to run tests on this package. -Contributions to this package are welcome. Read the [Contributing to Flutter Plugins](https://github.com/flutter/plugins/blob/master/CONTRIBUTING.md) guide to get started. +Contributions to this package are welcome. Read the [Contributing to Flutter Plugins](https://github.com/flutter/plugins/blob/main/CONTRIBUTING.md) guide to get started. ## Issues and feedback 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 a270af985ea7..907cc90c81eb 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/plugins/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.10.1+2 +version: 0.10.1+3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 6f0d4877f8a3..61fd745f7871 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.0.5 + +* Updates references to the obsolete master branch. + ## 3.0.4 * Minor fixes for new analysis options. diff --git a/packages/in_app_purchase/in_app_purchase/README.md b/packages/in_app_purchase/in_app_purchase/README.md index 258eba7f50f4..8986b9dea809 100644 --- a/packages/in_app_purchase/in_app_purchase/README.md +++ b/packages/in_app_purchase/in_app_purchase/README.md @@ -10,10 +10,10 @@ which can be the App Store (on iOS) or Google Play (on Android). | **Support** | SDK 16+ | 9.0+ |

- An animated image of the iOS in-app purchase UI      - An animated image of the Android in-app purchase UI

@@ -41,7 +41,7 @@ your app with each store. Both stores have extensive guides: > to a particular store. For a list of steps for configuring in-app purchases in both stores, see the -[example app README](https://github.com/flutter/plugins/blob/master/packages/in_app_purchase/in_app_purchase/example/README.md). +[example app README](https://github.com/flutter/plugins/blob/main/packages/in_app_purchase/in_app_purchase/example/README.md). Once you've configured your in-app purchases in their respective stores, you can start using the plugin. Two basic options are available: @@ -426,4 +426,4 @@ iosPlatformAddition.presentCodeRedemptionSheet(); ## Contributing to this plugin If you would like to contribute to the plugin, check out our -[contribution guide](https://github.com/flutter/plugins/blob/master/CONTRIBUTING.md). +[contribution guide](https://github.com/flutter/plugins/blob/main/CONTRIBUTING.md). diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index 23d771c68afb..8503b22aa299 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 3.0.4 +version: 3.0.5 environment: sdk: ">=2.12.0 <3.0.0" 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 05c2952fc615..b7069cf40b04 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.2+7 + +* Updates references to the obsolete master branch. + ## 0.2.2+6 * Enables mocking models by changing overridden operator == parameter type from `dynamic` to `Object`. diff --git a/packages/in_app_purchase/in_app_purchase_android/README.md b/packages/in_app_purchase/in_app_purchase_android/README.md index d64fbfb8c49a..423c07577ca4 100644 --- a/packages/in_app_purchase/in_app_purchase_android/README.md +++ b/packages/in_app_purchase/in_app_purchase_android/README.md @@ -21,7 +21,7 @@ editing any of the serialized data structs, rebuild the serializers by running watch the filesystem for changes. If you would like to contribute to the plugin, check out our -[contribution guide](https://github.com/flutter/plugins/blob/master/CONTRIBUTING.md). +[contribution guide](https://github.com/flutter/plugins/blob/main/CONTRIBUTING.md). [1]: https://pub.dev/packages/in_app_purchase 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 23b9cb6ecda2..adad84b39e1d 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 @@ -42,7 +42,7 @@ class MethodCallHandlerImpl private static final String TAG = "InAppPurchasePlugin"; private static final String LOAD_SKU_DOC_URL = - "https://github.com/flutter/plugins/blob/master/packages/in_app_purchase/in_app_purchase/README.md#loading-products-for-sale"; + "https://github.com/flutter/plugins/blob/main/packages/in_app_purchase/in_app_purchase/README.md#loading-products-for-sale"; @Nullable private BillingClient billingClient; private final BillingClientFactory billingClientFactory; 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 1099c3a12bfa..b9a2b86b96c2 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/plugins/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.2.2+6 +version: 0.2.2+7 environment: sdk: ">=2.14.0 <3.0.0" 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 365c7133864b..30448aef0c84 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.0+9 + +* Updates references to the obsolete master branch. + ## 0.3.0+8 * Fixes a memory leak on iOS. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/README.md b/packages/in_app_purchase/in_app_purchase_storekit/README.md index e9585f324331..76e2854c26e1 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/README.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/README.md @@ -21,7 +21,7 @@ editing any of the serialized data structs, rebuild the serializers by running watch the filesystem for changes. If you would like to contribute to the plugin, check out our -[contribution guide](https://github.com/flutter/plugins/blob/master/CONTRIBUTING.md). +[contribution guide](https://github.com/flutter/plugins/blob/main/CONTRIBUTING.md). [1]: ../in_app_purchase 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 b154ff304a98..fab5218c34de 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 platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/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.0+8 +version: 0.3.0+9 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/integration_test/README.md b/packages/integration_test/README.md index 6bf388131680..83c4adb500f0 100644 --- a/packages/integration_test/README.md +++ b/packages/integration_test/README.md @@ -16,256 +16,3 @@ dev_dependencies: For the latest documentation, see [Integration testing](https://flutter.dev/docs/testing/integration-tests). - -## Old instructions - -This package enables self-driving testing of Flutter code on devices and emulators. -It adapts flutter_test results into a format that is compatible with `flutter drive` -and native Android instrumentation testing. - -## Usage - -Add a dependency on the `integration_test` and `flutter_test` package in the -`dev_dependencies` section of `pubspec.yaml`. For plugins, do this in the -`pubspec.yaml` of the example app. - -Create a `integration_test/` directory for your package. In this directory, -create a `_test.dart`, using the following as a starting point to make -assertions. - -Note: You should only use `testWidgets` to declare your tests, or errors will not be reported correctly. - -```dart -import 'package:flutter_test/flutter_test.dart'; -import 'package:integration_test/integration_test.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - testWidgets("failing test example", (WidgetTester tester) async { - expect(2 + 2, equals(5)); - }); -} -``` - -### Driver Entrypoint - -An accompanying driver script will be needed that can be shared across all -integration tests. Create a file named `integration_test.dart` in the -`test_driver/` directory with the following contents: - -```dart -import 'package:integration_test/integration_test_driver.dart'; - -Future main() => integrationDriver(); -``` - -You can also use different driver scripts to customize the behavior of the app -under test. For example, `FlutterDriver` can also be parameterized with -different [options](https://api.flutter.dev/flutter/flutter_driver/FlutterDriver/connect.html). -See the [extended driver](https://github.com/flutter/flutter/blob/master/packages/integration_test/example/test_driver/extended_integration_test.dart) for an example. - -### Package Structure - -Your package should have a structure that looks like this: - -``` -lib/ - ... -integration_test/ - foo_test.dart - bar_test.dart -test/ - # Other unit tests go here. -test_driver/ - integration_test.dart -``` - -[Example](https://github.com/flutter/plugins/tree/master/packages/integration_test/example) - -## Using Flutter Driver to Run Tests - -These tests can be launched with the `flutter drive` command. - -To run the `integration_test/foo_test.dart` test with the -`test_driver/integration_test.dart` driver, use the following command: - -```sh -flutter drive \ - --driver=test_driver/integration_test.dart \ - --target=integration_test/foo_test.dart -``` - -### Web - -Make sure you have [enabled web support](https://flutter.dev/docs/get-started/web#set-up) -then [download and run](https://flutter.dev/docs/cookbook/testing/integration/introduction#6b-web) -the web driver in another process. - -Use following command to execute the tests: - -```sh -flutter drive \ - --driver=test_driver/integration_test.dart \ - --target=integration_test/foo_test.dart \ - -d web-server -``` - -## Android Device Testing - -Create an instrumentation test file in your application's -**android/app/src/androidTest/java/com/example/myapp/** directory (replacing -com, example, and myapp with values from your app's package name). You can name -this test file `MainActivityTest.java` or another name of your choice. - -```java -package com.example.myapp; - -import androidx.test.rule.ActivityTestRule; -import dev.flutter.plugins.integration_test.FlutterTestRunner; -import org.junit.Rule; -import org.junit.runner.RunWith; - -@RunWith(FlutterTestRunner.class) -public class MainActivityTest { - @Rule - public ActivityTestRule rule = new ActivityTestRule<>(MainActivity.class, true, false); -} -``` - -Update your application's **myapp/android/app/build.gradle** to make sure it -uses androidx's version of `AndroidJUnitRunner` and has androidx libraries as a -dependency. - -```gradle -android { - ... - defaultConfig { - ... - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } -} - -dependencies { - testImplementation 'junit:junit:4.12' - - // https://developer.android.com/jetpack/androidx/releases/test/#1.2.0 - androidTestImplementation 'androidx.test:runner:1.2.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' -} -``` - -To run `integration_test/foo_test.dart` on a local Android device (emulated or -physical): - -```sh -./gradlew app:connectedAndroidTest -Ptarget=`pwd`/../integration_test/foo_test.dart -``` - -## Firebase Test Lab - -If this is your first time testing with Firebase Test Lab, you'll need to follow -the guides in the [Firebase test lab -documentation](https://firebase.google.com/docs/test-lab/?gclid=EAIaIQobChMIs5qVwqW25QIV8iCtBh3DrwyUEAAYASAAEgLFU_D_BwE) -to set up a project. - -To run a test on Android devices using Firebase Test Lab, use gradle commands to build an -instrumentation test for Android, after creating `androidTest` as suggested in the last section. - -```bash -pushd android -# flutter build generates files in android/ for building the app -flutter build apk -./gradlew app:assembleAndroidTest -./gradlew app:assembleDebug -Ptarget=.dart -popd -``` - -Upload the build apks Firebase Test Lab, making sure to replace , -, , and with your values. - -```bash -gcloud auth activate-service-account --key-file= -gcloud --quiet config set project -gcloud firebase test android run --type instrumentation \ - --app build/app/outputs/apk/debug/app-debug.apk \ - --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk\ - --timeout 2m \ - --results-bucket= \ - --results-dir= -``` - -You can pass additional parameters on the command line, such as the -devices you want to test on. See -[gcloud firebase test android run](https://cloud.google.com/sdk/gcloud/reference/firebase/test/android/run). - -## iOS Device Testing - -Open `ios/Runner.xcworkspace` in Xcode. Create a test target if you -do not already have one via `File > New > Target...` and select `Unit Testing Bundle`. -Change the `Product Name` to `RunnerTests`. Make sure `Target to be Tested` is set to `Runner` and language is set to `Objective-C`. -Select `Finish`. -Make sure that the **iOS Deployment Target** of `RunnerTests` within the **Build Settings** section is the same as `Runner`. - -Add the new test target to `ios/Podfile` by embedding in the existing `Runner` target. - -```ruby -target 'Runner' do - # Do not change existing lines. - ... - - target 'RunnerTests' do - inherit! :search_paths - end -end -``` - -To build `integration_test/foo_test.dart` from the command line, run: -```sh -flutter build ios --config-only integration_test/foo_test.dart -``` - -In Xcode, add a test file called `RunnerTests.m` (or any name of your choice) to the new target and -replace the file: - -```objective-c -@import XCTest; -@import integration_test; - -INTEGRATION_TEST_IOS_RUNNER(RunnerTests) -``` - -Run `Product > Test` to run the integration tests on your selected device. - -To deploy it to Firebase Test Lab you can follow these steps: - -Execute this script at the root of your Flutter app: - -```sh -output="../build/ios_integ" -product="build/ios_integ/Build/Products" -dev_target="14.3" - -# Pass --simulator if building for the simulator. -flutter build ios integration_test/foo_test.dart --release - -pushd ios -xcodebuild -workspace Runner.xcworkspace -scheme Runner -config Flutter/Release.xcconfig -derivedDataPath $output -sdk iphoneos build-for-testing -popd - -pushd $product -zip -r "ios_tests.zip" "Release-iphoneos" "Runner_iphoneos$dev_target-arm64.xctestrun" -popd -``` - -You can verify locally that your tests are successful by running the following command: - -```sh -xcodebuild test-without-building -xctestrun "build/ios_integ/Build/Products/Runner_iphoneos14.3-arm64.xctestrun" -destination id= -``` - -Once everything is ok, you can upload the resulting zip to Firebase Test Lab (change the model with your values): - -```sh -gcloud firebase test ios run --test "build/ios_integ/ios_tests.zip" --device model=iphone11pro,version=14.1,locale=fr_FR,orientation=portrait -``` diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index f18e76bf1156..df9c8b87f3e6 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.5 + +* Updates references to the obsolete master branch. + ## 1.0.4 * Minor fixes for new analysis options. diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index 0feea23256a2..ec4eaab829f2 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -1,8 +1,8 @@ name: local_auth_android description: Android implementation of the local_auth plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_android +repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.4 +version: 1.0.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index d44836788ce3..4b8e0653a7ad 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.7 + +* Updates references to the obsolete master branch. + ## 1.0.6 * Suppresses warnings for pre-iOS-11 codepaths. diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index f491dfb49679..043d84eb4a2c 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -1,8 +1,8 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_ios +repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.6 +version: 1.0.7 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md index 10020be3391f..387a20050ed8 100644 --- a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md +++ b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 1.0.4 +* Updates references to the obsolete master branch. * Removes unnecessary imports. ## 1.0.3 diff --git a/packages/local_auth/local_auth_platform_interface/pubspec.yaml b/packages/local_auth/local_auth_platform_interface/pubspec.yaml index ee6c1e9d6577..a4ad682b363d 100644 --- a/packages/local_auth/local_auth_platform_interface/pubspec.yaml +++ b/packages/local_auth/local_auth_platform_interface/pubspec.yaml @@ -1,10 +1,10 @@ name: local_auth_platform_interface description: A common platform interface for the local_auth plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_platform_interface +repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_platform_interface issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%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: 1.0.3 +version: 1.0.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_windows/CHANGELOG.md b/packages/local_auth/local_auth_windows/CHANGELOG.md index 7cf171f305de..f6c5e909b31e 100644 --- a/packages/local_auth/local_auth_windows/CHANGELOG.md +++ b/packages/local_auth/local_auth_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.1 + +* Updates references to the obsolete master branch. + ## 1.0.0 * Initial release of Windows support. diff --git a/packages/local_auth/local_auth_windows/pubspec.yaml b/packages/local_auth/local_auth_windows/pubspec.yaml index 9edeffbb6530..b42a4f846cc3 100644 --- a/packages/local_auth/local_auth_windows/pubspec.yaml +++ b/packages/local_auth/local_auth_windows/pubspec.yaml @@ -1,8 +1,8 @@ name: local_auth_windows description: Windows implementation of the local_auth plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/local_auth/local_auth_windows +repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.0 +version: 1.0.1 environment: sdk: ">=2.14.0 <3.0.0" @@ -23,4 +23,4 @@ dependencies: dev_dependencies: flutter_test: - sdk: flutter \ No newline at end of file + sdk: flutter diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index 0567b7e7ae33..6f345f8c3755 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.0.11 +* Updates references to the obsolete master branch. * Fixes integration test permission issue on recent versions of macOS. ## 2.0.10 diff --git a/packages/path_provider/path_provider/README.md b/packages/path_provider/path_provider/README.md index e79234ffc07c..3a52e3e72050 100644 --- a/packages/path_provider/path_provider/README.md +++ b/packages/path_provider/path_provider/README.md @@ -43,4 +43,4 @@ Directories support by platform: `path_provider` now uses a `PlatformInterface`, meaning that not all platforms share a single `PlatformChannel`-based implementation. With that change, tests should be updated to mock `PathProviderPlatform` rather than `PlatformChannel`. -See this `path_provider` [test](https://github.com/flutter/plugins/blob/master/packages/path_provider/path_provider/test/path_provider_test.dart) for an example. +See this `path_provider` [test](https://github.com/flutter/plugins/blob/main/packages/path_provider/path_provider/test/path_provider_test.dart) for an example. diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml index 1e73497a39ff..272cb44c5617 100644 --- a/packages/path_provider/path_provider/pubspec.yaml +++ b/packages/path_provider/path_provider/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider description: Flutter plugin for getting commonly used locations on host platform file systems, such as the temp and app data directories. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.10 +version: 2.0.11 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_macos/macos/path_provider_macos.podspec b/packages/path_provider/path_provider_macos/macos/path_provider_macos.podspec index 1f28f2bf7cf0..14c468231f8c 100644 --- a/packages/path_provider/path_provider_macos/macos/path_provider_macos.podspec +++ b/packages/path_provider/path_provider_macos/macos/path_provider_macos.podspec @@ -11,7 +11,7 @@ Pod::Spec.new do |s| s.homepage = 'https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_macos' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider_macos' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_macos' } s.source_files = 'Classes/**/*' s.dependency 'FlutterMacOS' diff --git a/packages/quick_actions/quick_actions_android/CHANGELOG.md b/packages/quick_actions/quick_actions_android/CHANGELOG.md index 56accc9d044c..35f9c9fd51d9 100644 --- a/packages/quick_actions/quick_actions_android/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.0+11 + +* Updates references to the obsolete master branch. + ## 0.6.0+10 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors @@ -5,4 +9,4 @@ ## 0.6.0+9 -* Switches to a package-internal implementation of the platform interface. \ No newline at end of file +* Switches to a package-internal implementation of the platform interface. diff --git a/packages/quick_actions/quick_actions_android/README.md b/packages/quick_actions/quick_actions_android/README.md index caeb94374398..8b7fc8895212 100644 --- a/packages/quick_actions/quick_actions_android/README.md +++ b/packages/quick_actions/quick_actions_android/README.md @@ -13,5 +13,5 @@ If you would like to contribute to the plugin, check out our [contribution guide [1]: https://pub.dev/packages/quick_actions [2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin -[3]: https://github.com/flutter/plugins/blob/master/CONTRIBUTING.md +[3]: https://github.com/flutter/plugins/blob/main/CONTRIBUTING.md diff --git a/packages/quick_actions/quick_actions_android/pubspec.yaml b/packages/quick_actions/quick_actions_android/pubspec.yaml index 4ddbc79ee5e9..fa39e36b4fab 100644 --- a/packages/quick_actions/quick_actions_android/pubspec.yaml +++ b/packages/quick_actions/quick_actions_android/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_android description: An implementation for the Android platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.6.0+10 +version: 0.6.0+11 environment: sdk: ">=2.15.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions_ios/CHANGELOG.md b/packages/quick_actions/quick_actions_ios/CHANGELOG.md index 56accc9d044c..35f9c9fd51d9 100644 --- a/packages/quick_actions/quick_actions_ios/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.0+11 + +* Updates references to the obsolete master branch. + ## 0.6.0+10 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors @@ -5,4 +9,4 @@ ## 0.6.0+9 -* Switches to a package-internal implementation of the platform interface. \ No newline at end of file +* Switches to a package-internal implementation of the platform interface. diff --git a/packages/quick_actions/quick_actions_ios/README.md b/packages/quick_actions/quick_actions_ios/README.md index a0c369813a4d..e33b9ec3ab14 100644 --- a/packages/quick_actions/quick_actions_ios/README.md +++ b/packages/quick_actions/quick_actions_ios/README.md @@ -13,4 +13,4 @@ If you would like to contribute to the plugin, check out our [contribution guide [1]: https://pub.dev/packages/quick_actions [2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin -[3]: https://github.com/flutter/plugins/blob/master/CONTRIBUTING.md \ No newline at end of file +[3]: https://github.com/flutter/plugins/blob/main/CONTRIBUTING.md diff --git a/packages/quick_actions/quick_actions_ios/pubspec.yaml b/packages/quick_actions/quick_actions_ios/pubspec.yaml index 47748b9789ad..4dc91f4a5fe7 100644 --- a/packages/quick_actions/quick_actions_ios/pubspec.yaml +++ b/packages/quick_actions/quick_actions_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_ios description: An implementation for the iOS platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.6.0+10 +version: 0.6.0+11 environment: sdk: ">=2.15.0 <3.0.0" diff --git a/packages/shared_preferences/shared_preferences_macos/macos/shared_preferences_macos.podspec b/packages/shared_preferences/shared_preferences_macos/macos/shared_preferences_macos.podspec index df140fbb1eec..590b0c34adcf 100644 --- a/packages/shared_preferences/shared_preferences_macos/macos/shared_preferences_macos.podspec +++ b/packages/shared_preferences/shared_preferences_macos/macos/shared_preferences_macos.podspec @@ -11,7 +11,7 @@ Wraps NSUserDefaults, providing a persistent store for simple key-value pairs. s.homepage = 'https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_macos' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_macos' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_macos' } s.source_files = 'Classes/**/*' s.dependency 'FlutterMacOS' diff --git a/packages/url_launcher/url_launcher_ios/ios/url_launcher_ios.podspec b/packages/url_launcher/url_launcher_ios/ios/url_launcher_ios.podspec index e22ba3db9854..1c0e81964252 100644 --- a/packages/url_launcher/url_launcher_ios/ios/url_launcher_ios.podspec +++ b/packages/url_launcher/url_launcher_ios/ios/url_launcher_ios.podspec @@ -11,7 +11,7 @@ A Flutter plugin for making the underlying platform (Android or iOS) launch a UR s.homepage = 'https://github.com/flutter/plugins/tree/main/packages/url_launcher' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_ios' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_ios' } s.documentation_url = 'https://pub.dev/packages/url_launcher' s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' diff --git a/packages/url_launcher/url_launcher_macos/macos/url_launcher_macos.podspec b/packages/url_launcher/url_launcher_macos/macos/url_launcher_macos.podspec index 408df1f9ef45..270adc60b81f 100644 --- a/packages/url_launcher/url_launcher_macos/macos/url_launcher_macos.podspec +++ b/packages/url_launcher/url_launcher_macos/macos/url_launcher_macos.podspec @@ -11,7 +11,7 @@ Pod::Spec.new do |s| s.homepage = 'https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_macos' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_macos' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_macos' } s.source_files = 'Classes/**/*' s.dependency 'FlutterMacOS' diff --git a/packages/url_launcher/url_launcher_web/lib/src/shims/dart_ui_fake.dart b/packages/url_launcher/url_launcher_web/lib/src/shims/dart_ui_fake.dart index f51dce946acc..ec46f2789ab5 100644 --- a/packages/url_launcher/url_launcher_web/lib/src/shims/dart_ui_fake.dart +++ b/packages/url_launcher/url_launcher_web/lib/src/shims/dart_ui_fake.dart @@ -11,10 +11,10 @@ import 'dart:html' as html; // ignore_for_file: camel_case_types /// Shim for web_ui engine.PlatformViewRegistry -/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L62 +/// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/ui.dart#L62 class platformViewRegistry { /// Shim for registerViewFactory - /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L72 + /// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/ui.dart#L72 static bool registerViewFactory( String viewTypeId, html.Element Function(int viewId) viewFactory, {bool isVisible = true}) { @@ -23,10 +23,10 @@ class platformViewRegistry { } /// Shim for web_ui engine.AssetManager. -/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L12 +/// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/src/engine/assets.dart#L12 class webOnlyAssetManager { /// Shim for getAssetUrl. - /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L45 + /// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/src/engine/assets.dart#L45 static String getAssetUrl(String asset) => ''; } diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 8dca057f5a08..02ff3128c6a6 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.4.4 + +* Updates references to the obsolete master branch. + ## 2.4.3 * Fixes Android to correctly display videos recorded in landscapeRight (https://github.com/flutter/flutter/issues/60327). diff --git a/packages/video_player/video_player/README.md b/packages/video_player/video_player/README.md index 91c1bdeb7773..c870688a4fee 100644 --- a/packages/video_player/video_player/README.md +++ b/packages/video_player/video_player/README.md @@ -8,7 +8,7 @@ A Flutter plugin for iOS, Android and Web for playing back video on a Widget sur |-------------|---------|------|-------| | **Support** | SDK 16+ | 9.0+ | Any\* | -![The example app running in iOS](https://github.com/flutter/plugins/blob/master/packages/video_player/video_player/doc/demo_ipod.gif?raw=true) +![The example app running in iOS](https://github.com/flutter/plugins/blob/main/packages/video_player/video_player/doc/demo_ipod.gif?raw=true) ## Installation diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 8b2fc5780e3d..26723992628f 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/plugins/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.4.3 +version: 2.4.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index d5acda4cce22..beabb2344524 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.6 + +* Updates references to the obsolete master branch. + ## 2.3.5 * Sets rotationCorrection for videos recorded in landscapeRight (https://github.com/flutter/flutter/issues/60327). diff --git a/packages/video_player/video_player_android/CONTRIBUTING.md b/packages/video_player/video_player_android/CONTRIBUTING.md index 8dfec9faf809..e06f2233278b 100644 --- a/packages/video_player/video_player_android/CONTRIBUTING.md +++ b/packages/video_player/video_player_android/CONTRIBUTING.md @@ -25,7 +25,7 @@ Then, run the commands above. When you run `pub get` it should warn you that you're using an override. If you do this, you will need to publish pigeon before you can land the updates to this package, since the CI tests run the analysis using latest published version of -pigeon, not your version or the version on master. +pigeon, not your version or the version on `main`. In either case, the configuration will be obtained automatically from the `pigeons/messages.dart` file (see `ConfigurePigeon` at the top of that file). diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index 367b95ea5a60..0b630467ac05 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -1,8 +1,8 @@ name: video_player_android description: Android implementation of the video_player plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_android +repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.5 +version: 2.3.6 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 6ab5398f7013..90d8ee61819e 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.5 + +* Updates references to the obsolete master branch. + ## 2.3.4 * Removes unnecessary imports. diff --git a/packages/video_player/video_player_avfoundation/CONTRIBUTING.md b/packages/video_player/video_player_avfoundation/CONTRIBUTING.md index 8dfec9faf809..e06f2233278b 100644 --- a/packages/video_player/video_player_avfoundation/CONTRIBUTING.md +++ b/packages/video_player/video_player_avfoundation/CONTRIBUTING.md @@ -25,7 +25,7 @@ Then, run the commands above. When you run `pub get` it should warn you that you're using an override. If you do this, you will need to publish pigeon before you can land the updates to this package, since the CI tests run the analysis using latest published version of -pigeon, not your version or the version on master. +pigeon, not your version or the version on `main`. In either case, the configuration will be obtained automatically from the `pigeons/messages.dart` file (see `ConfigurePigeon` at the top of that file). diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index 380d8343c024..c00f4baa1f0a 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -1,8 +1,8 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. -repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_avfoundation +repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.4 +version: 2.3.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index 4304fd470ba2..2229bf3bbfd4 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 5.1.3 +* Updates references to the obsolete master branch. * Removes unnecessary imports. ## 5.1.2 diff --git a/packages/video_player/video_player_platform_interface/CONTRIBUTING.md b/packages/video_player/video_player_platform_interface/CONTRIBUTING.md index dbbfbf66c9a1..4108ae0d0030 100644 --- a/packages/video_player/video_player_platform_interface/CONTRIBUTING.md +++ b/packages/video_player/video_player_platform_interface/CONTRIBUTING.md @@ -39,7 +39,7 @@ Then, run the commands above. When you run `pub get` it should warn you that you're using an override. If you do this, you will need to publish pigeon before you can land the updates to this package, since the CI tests run the analysis using latest published version of -pigeon, not your version or the version on master. +pigeon, not your version or the version on `main`. In either case, the configuration will be obtained automatically from the `pigeons/messages.dart` file (see `configurePigeon` at the bottom diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml index 7a18568f22b5..8644c45b9e93 100644 --- a/packages/video_player/video_player_platform_interface/pubspec.yaml +++ b/packages/video_player/video_player_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/video_player/v issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%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: 5.1.2 +version: 5.1.3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/video_player/video_player_web/lib/src/shims/dart_ui_fake.dart b/packages/video_player/video_player_web/lib/src/shims/dart_ui_fake.dart index 8757ca22be17..40d8f1903111 100644 --- a/packages/video_player/video_player_web/lib/src/shims/dart_ui_fake.dart +++ b/packages/video_player/video_player_web/lib/src/shims/dart_ui_fake.dart @@ -11,10 +11,10 @@ import 'dart:html' as html; // ignore_for_file: camel_case_types /// Shim for web_ui engine.PlatformViewRegistry -/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L62 +/// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/ui.dart#L62 class platformViewRegistry { /// Shim for registerViewFactory - /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L72 + /// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/ui.dart#L72 static bool registerViewFactory( String viewTypeId, html.Element Function(int viewId) viewFactory) { return false; @@ -22,10 +22,10 @@ class platformViewRegistry { } /// Shim for web_ui engine.AssetManager. -/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L12 +/// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/src/engine/assets.dart#L12 class webOnlyAssetManager { /// Shim for getAssetUrl. - /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L45 + /// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/src/engine/assets.dart#L45 static String getAssetUrl(String asset) => ''; } diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 31c16da8807b..f7500e569992 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates references to the obsolete master branch. + ## 3.0.4 * Minor fixes for new analysis options. @@ -186,7 +190,7 @@ when hybrid composition is used [flutter/issues/75667](https://github.com/flutte performing better on iOS. Flutter 1.22 no longer requires adding the `io.flutter.embedded_views_preview` flag to `Info.plist`. -* Added support for Hybrid Composition on Android (see opt-in instructions in [README](https://github.com/flutter/plugins/blob/master/packages/webview_flutter/README.md#android)) +* Added support for Hybrid Composition on Android (see opt-in instructions in [README](https://github.com/flutter/plugins/blob/main/packages/webview_flutter/README.md#android)) * Lowered the required Android API to 19 (was previously 20): [#23728](https://github.com/flutter/flutter/issues/23728). * Fixed the following issues: * 🎹 Keyboard: [#41089](https://github.com/flutter/flutter/issues/41089), [#36478](https://github.com/flutter/flutter/issues/36478), [#51254](https://github.com/flutter/flutter/issues/51254), [#50716](https://github.com/flutter/flutter/issues/50716), [#55724](https://github.com/flutter/flutter/issues/55724), [#56513](https://github.com/flutter/flutter/issues/56513), [#56515](https://github.com/flutter/flutter/issues/56515), [#61085](https://github.com/flutter/flutter/issues/61085), [#62205](https://github.com/flutter/flutter/issues/62205), [#62547](https://github.com/flutter/flutter/issues/62547), [#58943](https://github.com/flutter/flutter/issues/58943), [#56361](https://github.com/flutter/flutter/issues/56361), [#56361](https://github.com/flutter/flutter/issues/42902), [#40716](https://github.com/flutter/flutter/issues/40716), [#37989](https://github.com/flutter/flutter/issues/37989), [#27924](https://github.com/flutter/flutter/issues/27924). diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 70e93697bb96..ee1186a29d79 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.10 + +* Updates references to the obsolete master branch. + ## 2.8.9 * Updates Gradle to 7.2.1. diff --git a/packages/webview_flutter/webview_flutter_android/README.md b/packages/webview_flutter/webview_flutter_android/README.md index 04cbde292356..80f1f6e0b9cb 100644 --- a/packages/webview_flutter/webview_flutter_android/README.md +++ b/packages/webview_flutter/webview_flutter_android/README.md @@ -41,5 +41,5 @@ If you would like to contribute to the plugin, check out our [contribution guide [2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin [3]: https://pub.dev/packages/pigeon [4]: https://pub.dev/packages/mockito -[5]: https://github.com/flutter/plugins/blob/master/CONTRIBUTING.md +[5]: https://github.com/flutter/plugins/blob/main/CONTRIBUTING.md diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 873d3f7cbf51..2a32a837d0d3 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/plugins/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: 2.8.9 +version: 2.8.10 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui_fake.dart b/packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui_fake.dart index 8757ca22be17..40d8f1903111 100644 --- a/packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui_fake.dart +++ b/packages/webview_flutter/webview_flutter_web/lib/shims/dart_ui_fake.dart @@ -11,10 +11,10 @@ import 'dart:html' as html; // ignore_for_file: camel_case_types /// Shim for web_ui engine.PlatformViewRegistry -/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L62 +/// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/ui.dart#L62 class platformViewRegistry { /// Shim for registerViewFactory - /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/ui.dart#L72 + /// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/ui.dart#L72 static bool registerViewFactory( String viewTypeId, html.Element Function(int viewId) viewFactory) { return false; @@ -22,10 +22,10 @@ class platformViewRegistry { } /// Shim for web_ui engine.AssetManager. -/// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L12 +/// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/src/engine/assets.dart#L12 class webOnlyAssetManager { /// Shim for getAssetUrl. - /// https://github.com/flutter/engine/blob/master/lib/web_ui/lib/src/engine/assets.dart#L45 + /// https://github.com/flutter/engine/blob/main/lib/web_ui/lib/src/engine/assets.dart#L45 static String getAssetUrl(String asset) => ''; } diff --git a/script/install_chromium.sh b/script/install_chromium.sh index b7d787626d55..0d360fe98cfe 100755 --- a/script/install_chromium.sh +++ b/script/install_chromium.sh @@ -11,7 +11,7 @@ readonly TARGET_DIR=$1 # # Chromium builds can be located here: https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Linux_x64/ # -# Check: https://github.com/flutter/engine/blob/master/lib/web_ui/dev/browser_lock.yaml +# Check: https://github.com/flutter/engine/blob/main/lib/web_ui/dev/browser_lock.yaml readonly CHROMIUM_BUILD=929514 # The correct ChromeDriver is distributed alongside the chromium build above, as diff --git a/script/tool/README.md b/script/tool/README.md index cc9621f44086..d3beb53a1103 100644 --- a/script/tool/README.md +++ b/script/tool/README.md @@ -178,4 +178,4 @@ For changes that are relevant to flutter/packages, you will also need to: - Update the tool's pubspec.yaml and CHANGELOG - Publish the tool - Update the pinned version in - [flutter/packages](https://github.com/flutter/packages/blob/master/.cirrus.yml) + [flutter/packages](https://github.com/flutter/packages/blob/main/.cirrus.yml) diff --git a/script/tool/lib/src/pubspec_check_command.dart b/script/tool/lib/src/pubspec_check_command.dart index 3598a39da960..79ef1e1d3e5e 100644 --- a/script/tool/lib/src/pubspec_check_command.dart +++ b/script/tool/lib/src/pubspec_check_command.dart @@ -191,6 +191,11 @@ class PubspecCheckCommand extends PackageLoopingCommand { errorMessages .add('The "repository" link should end with the package path.'); } + + if (pubspec.repository!.path.contains('/master/')) { + errorMessages + .add('The "repository" link should use "main", not "master".'); + } } if (pubspec.homepage != null) { diff --git a/script/tool/test/pubspec_check_command_test.dart b/script/tool/test/pubspec_check_command_test.dart index fbe31c72bc2b..2c254ca94984 100644 --- a/script/tool/test/pubspec_check_command_test.dart +++ b/script/tool/test/pubspec_check_command_test.dart @@ -27,6 +27,7 @@ String _headerSection( String name, { bool isPlugin = false, bool includeRepository = true, + String repositoryBranch = 'main', String? repositoryPackagesDirRelativePath, bool includeHomepage = false, bool includeIssueTracker = true, @@ -38,7 +39,7 @@ String _headerSection( 'flutter', if (isPlugin) 'plugins' else 'packages', 'tree', - 'main', + repositoryBranch, 'packages', repositoryPath, ]; @@ -328,7 +329,7 @@ ${_devDependenciesSection()} ); }); - test('fails when repository is incorrect', () async { + test('fails when repository package name is incorrect', () async { final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); @@ -355,6 +356,33 @@ ${_devDependenciesSection()} ); }); + test('fails when repository uses master instead of main', () async { + final RepositoryPackage plugin = + createFakePlugin('plugin', packagesDir, examples: []); + + plugin.pubspecFile.writeAsStringSync(''' +${_headerSection('plugin', isPlugin: true, repositoryBranch: 'master')} +${_environmentSection()} +${_flutterSection(isPlugin: true)} +${_dependenciesSection()} +${_devDependenciesSection()} +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['pubspec-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The "repository" link should use "main", not "master".'), + ]), + ); + }); + test('fails when issue tracker is missing', () async { final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, examples: []); From 1b5386d1b16feca98ff7c3bb7ae6280146228eee Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 6 Jun 2022 16:08:07 -0400 Subject: [PATCH 392/844] Roll Flutter from 52c47e96a33d to 1b2ee411aa13 (8 revisions) (#5916) --- .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 01e3344219df..44e3b16c1950 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -52c47e96a33df14a5f43216fa88cf2165ea4d659 +1b2ee411aa1389e74c7c2aada90bc468781532d2 From 395dc14b27e0ba1bb02a65168b510e55fa7fc09d Mon Sep 17 00:00:00 2001 From: Ricardo Amador <32242716+ricardoamador@users.noreply.github.com> Date: Mon, 6 Jun 2022 15:19:00 -0700 Subject: [PATCH 393/844] Add versions to all the dependencies so they are pinned (#5887) * Pinning dependency versions * Update gradle version. * Revert "Update gradle version." This reverts commit 7bf787f2dcb1c2d2ce6a626104f91b5fd13ca1c1. --- .ci.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.ci.yaml b/.ci.yaml index a8fa7a5a1577..7504373ac61a 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -13,7 +13,7 @@ platform_properties: properties: dependencies: > [ - {"dependency": "curl"} + {"dependency": "curl", "version": "version:7.64.0"} ] device_type: none os: Linux @@ -21,7 +21,7 @@ platform_properties: properties: dependencies: > [ - {"dependency": "certs"} + {"dependency": "certs", "version": "version:9563bb"} ] device_type: none os: Windows @@ -37,7 +37,7 @@ targets: version_file: flutter_master.version dependencies: > [ - {"dependency": "vs_build"} + {"dependency": "vs_build", "version": "version:vs2019"} ] - name: Windows win32-platform_tests stable @@ -49,7 +49,7 @@ targets: channel: stable dependencies: > [ - {"dependency": "vs_build"} + {"dependency": "vs_build", "version": "version:vs2019"} ] - name: Windows windows-build_all_plugins master @@ -62,7 +62,7 @@ targets: version_file: flutter_master.version dependencies: > [ - {"dependency": "vs_build"} + {"dependency": "vs_build", "version": "version:vs2019"} ] - name: Windows windows-build_all_plugins stable @@ -74,7 +74,7 @@ targets: channel: stable dependencies: > [ - {"dependency": "vs_build"} + {"dependency": "vs_build", "version": "version:vs2019"} ] - name: Windows plugin_tools_tests From c5e7e0681c3d1e707dbee0537681463c3d8988d6 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 6 Jun 2022 20:43:11 -0400 Subject: [PATCH 394/844] [ci] Re-disable mirroring to master (#5920) --- .github/workflows/mirror.yml | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 .github/workflows/mirror.yml diff --git a/.github/workflows/mirror.yml b/.github/workflows/mirror.yml deleted file mode 100644 index 9cfaf20d6cf0..000000000000 --- a/.github/workflows/mirror.yml +++ /dev/null @@ -1,28 +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. - -# Mirror master to main branches in the plugins repository. -on: - push: - branches: - - 'main' - -# Declare default permissions as read only. -permissions: read-all - -jobs: - mirror_job: - permissions: - pull-requests: write - runs-on: ubuntu-latest - if: ${{ github.repository == 'flutter/plugins' }} - name: Mirror main branch to master branch - steps: - - name: Mirror action step - id: mirror - uses: google/mirror-branch-action@c6b07e441a7ffc5ae15860c1d0a8107a3a151db8 - with: - github-token: ${{ secrets.FLUTTERMIRRORINGBOT_TOKEN }} - source: 'main' - dest: 'master' From 63c52c129390a3b90b0b118a9dc5f01f8d1dc778 Mon Sep 17 00:00:00 2001 From: Alexandre Ardhuin Date: Tue, 7 Jun 2022 11:33:00 +0200 Subject: [PATCH 395/844] Ignore upcoming warnings webview_flutter_android (#5922) * ignore upcoming warnings in webview_flutter_android * update-release-info --- packages/webview_flutter/webview_flutter_android/CHANGELOG.md | 4 ++++ .../example/integration_test/webview_flutter_test.dart | 2 ++ .../webview_flutter_android/lib/src/android_webview.dart | 2 ++ packages/webview_flutter/webview_flutter_android/pubspec.yaml | 2 +- 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index ee1186a29d79..8e715ccedbf5 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.11 + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). + ## 2.8.10 * Updates references to the obsolete master branch. 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 383fe4508081..c5bf76d2c6cb 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 @@ -9,6 +9,8 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import import 'dart:typed_data'; import 'package:flutter/foundation.dart'; 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 f858fb39a943..7fdcf4b2871f 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 @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import import 'dart:typed_data'; import 'dart:ui'; diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 2a32a837d0d3..6195e4335d10 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/plugins/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: 2.8.10 +version: 2.8.11 environment: sdk: ">=2.14.0 <3.0.0" From 6a146d66cb40462e2528b670d68ee244ed429b6e Mon Sep 17 00:00:00 2001 From: Drew Roen <102626803+drewroengoogle@users.noreply.github.com> Date: Tue, 7 Jun 2022 14:16:13 +0000 Subject: [PATCH 396/844] [github_actions] Add automatic updating of github-actions (#5919) * Revert "Revert "Add automatic updating of github-actions (#5880)" (#5917)" This reverts commit 8ded25d1b674e1461baf0ae236d208bdce78ac0f. * Update dependabot.yml Shortened prefix from "github_actions" to "gh_actions" in order to meet the 15 character max length requirement --- .github/dependabot.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 676ac1da5930..02bfe34d9608 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -287,3 +287,11 @@ updates: schedule: interval: "daily" open-pull-requests-limit: 10 + + - package-ecosystem: "github-actions" + directory: "/" + commit-message: + prefix: "[gh_actions]" + schedule: + interval: "daily" + open-pull-requests-limit: 10 From 9ea278f3ca8ac0372a8c8532181e1e7206ec91e6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 7 Jun 2022 12:13:12 -0400 Subject: [PATCH 397/844] Roll Flutter from 1b2ee411aa13 to be0c1bd0f0ce (23 revisions) (#5929) --- .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 44e3b16c1950..b5daa841e966 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1b2ee411aa1389e74c7c2aada90bc468781532d2 +be0c1bd0f0cec3aa97c6be279b7beae9d0e8d6be From 044f5ce1948536bd044ef2a44df4986a35954bf2 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 7 Jun 2022 12:53:10 -0400 Subject: [PATCH 398/844] [url_launcher] Update README discussion of permissions (#5424) --- .../url_launcher/url_launcher/CHANGELOG.md | 5 +++ packages/url_launcher/url_launcher/README.md | 40 ++++++++----------- .../android/app/src/main/AndroidManifest.xml | 6 +-- .../url_launcher/url_launcher/pubspec.yaml | 2 +- 4 files changed, 25 insertions(+), 28 deletions(-) diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 043edeb3d8e2..d2c4206718ec 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,8 @@ +## 6.1.3 + +* Updates README section about query permissions to better reflect changes to + `canLaunchUrl` recommendations. + ## 6.1.2 * Minor fixes for new analysis options. diff --git a/packages/url_launcher/url_launcher/README.md b/packages/url_launcher/url_launcher/README.md index 9c9f0b57e667..9ef6b5aac5c7 100644 --- a/packages/url_launcher/url_launcher/README.md +++ b/packages/url_launcher/url_launcher/README.md @@ -43,14 +43,15 @@ See the example app for more complex examples. ## Configuration ### iOS -Add any URL schemes passed to `canLaunchUrl` as `LSApplicationQueriesSchemes` entries in your Info.plist file. +Add any URL schemes passed to `canLaunchUrl` as `LSApplicationQueriesSchemes` +entries in your Info.plist file, otherwise it will return false. Example: ```xml LSApplicationQueriesSchemes - https - http + sms + tel ``` @@ -58,40 +59,31 @@ See [`-[UIApplication canOpenURL:]`](https://developer.apple.com/documentation/u ### Android -Starting from API 30 Android requires package visibility configuration in your -`AndroidManifest.xml` otherwise `canLaunchUrl` will return `false`. A `` +Add any URL schemes passed to `canLaunchUrl` as `` entries in your +`AndroidManifest.xml`, otherwise it will return false in most cases starting +on Android 11 (API 30) or higher. A `` element must be added to your manifest as a child of the root element. -The snippet below shows an example for an application that uses `https`, `tel`, -and `mailto` URLs with `url_launcher`. See -[the Android documentation](https://developer.android.com/training/package-visibility/use-cases) -for examples of other queries. - +Example: ``` xml - + - + - + - + - - - - - - - - - - ``` +See +[the Android documentation](https://developer.android.com/training/package-visibility/use-cases) +for examples of other queries. + ## Supported URL schemes The provided URL is passed directly to the host platform for handling. The diff --git a/packages/url_launcher/url_launcher/example/android/app/src/main/AndroidManifest.xml b/packages/url_launcher/url_launcher/example/android/app/src/main/AndroidManifest.xml index fa149f94adf0..5c0d0afa0fe4 100644 --- a/packages/url_launcher/url_launcher/example/android/app/src/main/AndroidManifest.xml +++ b/packages/url_launcher/url_launcher/example/android/app/src/main/AndroidManifest.xml @@ -14,12 +14,12 @@ - + - - + + diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 319b6bfd3e0b..2a7ddcdb1dbe 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/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.1.2 +version: 6.1.3 environment: sdk: ">=2.14.0 <3.0.0" From 39d99d97ba640c0a1b50097d0be616662bafec0a Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 7 Jun 2022 11:48:10 -0700 Subject: [PATCH 399/844] [webview_flutter_wkwebview] Change callbacks setters to anonymous functions (#5921) --- .../lib/src/foundation/foundation.dart | 21 +- .../lib/src/ui_kit/ui_kit.dart | 7 +- .../lib/src/web_kit/web_kit.dart | 86 +++---- .../lib/src/web_kit_webview_widget.dart | 189 ++++++++------ .../src/foundation/foundation_test.mocks.dart | 16 +- .../test/src/ui_kit/ui_kit_test.mocks.dart | 116 ++++----- .../test/src/web_kit/web_kit_test.dart | 2 + .../test/src/web_kit/web_kit_test.mocks.dart | 156 ++++++------ .../web_kit_cookie_manager_test.mocks.dart | 18 +- .../test/src/web_kit_webview_widget_test.dart | 209 ++++++++++----- .../web_kit_webview_widget_test.mocks.dart | 238 ++++++------------ 11 files changed, 529 insertions(+), 529 deletions(-) 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 5dfb78c267de..0a1dd347f8a2 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 @@ -244,6 +244,7 @@ class NSObject with Copyable { /// This should only be used by subclasses created by this library or to /// create copies. NSObject({ + this.observeValue, BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, }) : _api = NSObjectHostApiImpl( @@ -262,6 +263,13 @@ class NSObject with Copyable { final NSObjectHostApiImpl _api; + /// Informs the observing object when the value at the specified key path has changed. + final void Function( + String keyPath, + NSObject object, + Map change, + )? observeValue; + /// Registers the observer object to receive KVO notifications. Future addObserver( NSObject observer, { @@ -287,21 +295,10 @@ class NSObject with Copyable { instance._api.instanceManager.removeWeakReference(instance); } - /// Informs the observing object when the value at the specified key path has changed. - Future setObserveValue( - void Function( - String keyPath, - NSObject object, - Map change, - )? - observeValue, - ) { - throw UnimplementedError(); - } - @override Copyable copy() { return NSObject( + observeValue: observeValue, binaryMessenger: _api.binaryMessenger, instanceManager: _api.instanceManager, ); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart index 1d89b18c9f05..2ed1602b9ac9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart @@ -64,8 +64,11 @@ class UIScrollView extends UIView { /// Wraps [UIView](https://developer.apple.com/documentation/uikit/uiview?language=objc). class UIView extends NSObject { /// Constructs an [NSObject]. - UIView({BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) - : _viewApi = UIViewHostApiImpl( + UIView({ + super.observeValue, + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _viewApi = UIViewHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, ); 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 2b887e97adcf..f2efd665298b 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 @@ -350,6 +350,8 @@ class WKHttpCookieStore extends NSObject { class WKScriptMessageHandler extends NSObject { /// Constructs a [WKScriptMessageHandler]. WKScriptMessageHandler({ + required this.didReceiveScriptMessage, + super.observeValue, BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, }) : _scriptMessageHandlerApi = WKScriptMessageHandlerHostApiImpl( @@ -366,15 +368,10 @@ class WKScriptMessageHandler extends NSObject { /// Use this method to respond to a message sent from the webpage’s /// JavaScript code. Use the [message] parameter to get the message contents and /// to determine the originating web view. - Future setDidReceiveScriptMessage( - void Function( - WKUserContentController userContentController, - WKScriptMessage message, - )? - didReceiveScriptMessage, - ) { - throw UnimplementedError(); - } + final void Function( + WKUserContentController userContentController, + WKScriptMessage message, + ) didReceiveScriptMessage; } /// Manages interactions between JavaScript code and your web view. @@ -572,6 +569,8 @@ class WKWebViewConfiguration extends NSObject { class WKUIDelegate extends NSObject { /// Constructs a [WKUIDelegate]. WKUIDelegate({ + this.onCreateWebView, + super.observeValue, BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, }) : _uiDelegateApi = WKUIDelegateHostApiImpl( @@ -584,15 +583,10 @@ class WKUIDelegate extends NSObject { final WKUIDelegateHostApiImpl _uiDelegateApi; /// Indicates a new [WKWebView] was requested to be created with [configuration]. - Future setOnCreateWebView( - void Function( - WKWebViewConfiguration configuration, - WKNavigationAction navigationAction, - )? - onCreateWebView, - ) { - throw UnimplementedError(); - } + final void Function( + WKWebViewConfiguration configuration, + WKNavigationAction navigationAction, + )? onCreateWebView; } /// Methods for handling navigation changes and tracking navigation requests. @@ -606,6 +600,12 @@ class WKNavigationDelegate extends NSObject { /// Constructs a [WKNavigationDelegate]. WKNavigationDelegate({ this.didFinishNavigation, + this.didStartProvisionalNavigation, + this.decidePolicyForNavigationAction, + this.didFailNavigation, + this.didFailProvisionalNavigation, + this.webViewWebContentProcessDidTerminate, + super.observeValue, super.binaryMessenger, super.instanceManager, }) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl( @@ -623,6 +623,12 @@ class WKNavigationDelegate extends NSObject { /// library or to create a copy for an InstanceManager. WKNavigationDelegate.detached({ this.didFinishNavigation, + this.didStartProvisionalNavigation, + this.decidePolicyForNavigationAction, + this.didFailNavigation, + this.didFailProvisionalNavigation, + this.webViewWebContentProcessDidTerminate, + super.observeValue, super.binaryMessenger, super.instanceManager, }) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl( @@ -636,49 +642,36 @@ class WKNavigationDelegate extends NSObject { final void Function(WKWebView webView, String? url)? didFinishNavigation; /// Called when navigation from the main frame has started. - Future setDidStartProvisionalNavigation( - void Function(WKWebView webView, String? url)? - didStartProvisionalNavigation, - ) { - throw UnimplementedError(); - } + final void Function(WKWebView webView, String? url)? + didStartProvisionalNavigation; /// Called when permission is needed to navigate to new content. - Future setDecidePolicyForNavigationAction( - Future Function( + final Future Function( WKWebView webView, WKNavigationAction navigationAction, - )? - decidePolicyForNavigationAction) { - throw UnimplementedError(); - } + )? decidePolicyForNavigationAction; /// Called when an error occurred during navigation. - Future setDidFailNavigation( - void Function(WKWebView webView, NSError error)? didFailNavigation, - ) { - throw UnimplementedError(); - } + final void Function(WKWebView webView, NSError error)? didFailNavigation; /// Called when an error occurred during the early navigation process. - Future setDidFailProvisionalNavigation( - void Function(WKWebView webView, NSError error)? - didFailProvisionalNavigation, - ) { - throw UnimplementedError(); - } + final void Function(WKWebView webView, NSError error)? + didFailProvisionalNavigation; /// Called when the web view’s content process was terminated. - Future setWebViewWebContentProcessDidTerminate( - void Function(WKWebView webView)? webViewWebContentProcessDidTerminate, - ) { - throw UnimplementedError(); - } + final void Function(WKWebView webView)? webViewWebContentProcessDidTerminate; @override Copyable copy() { return WKNavigationDelegate.detached( didFinishNavigation: didFinishNavigation, + didStartProvisionalNavigation: didStartProvisionalNavigation, + decidePolicyForNavigationAction: decidePolicyForNavigationAction, + didFailNavigation: didFailNavigation, + didFailProvisionalNavigation: didFailProvisionalNavigation, + webViewWebContentProcessDidTerminate: + webViewWebContentProcessDidTerminate, + observeValue: observeValue, binaryMessenger: _navigationDelegateApi.binaryMessenger, instanceManager: _navigationDelegateApi.instanceManager, ); @@ -715,6 +708,7 @@ class WKWebView extends UIView { /// configuration object. WKWebView( WKWebViewConfiguration configuration, { + super.observeValue, super.binaryMessenger, super.instanceManager, }) : _binaryMessenger = binaryMessenger, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 90f1554bf99b..1cfbc6edb458 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -92,6 +92,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { } bool _zoomEnabled = true; + bool _hasNavigationDelegate = false; final Map _scriptMessageHandlers = {}; @@ -112,7 +113,15 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { /// Used to integrate custom user interface elements into web view interactions. @visibleForTesting - late final WKUIDelegate uiDelegate = webViewProxy.createUIDelgate(); + late final WKUIDelegate uiDelegate = + webViewProxy.createUIDelgate(onCreateWebView: ( + WKWebViewConfiguration configuration, + WKNavigationAction navigationAction, + ) { + if (!navigationAction.targetFrame.isMainFrame) { + webView.loadRequest(navigationAction.request); + } + }); /// Methods for handling navigation changes and tracking navigation requests. @visibleForTesting @@ -121,25 +130,42 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { didFinishNavigation: (WKWebView webView, String? url) { callbacksHandler.onPageFinished(url ?? ''); }, - ) - ..setDidStartProvisionalNavigation((WKWebView webView, String? url) { - callbacksHandler.onPageStarted(url ?? ''); - }) - ..setDidFailNavigation((WKWebView webView, NSError error) { - callbacksHandler.onWebResourceError(_toWebResourceError(error)); - }) - ..setDidFailProvisionalNavigation((WKWebView webView, NSError error) { - callbacksHandler.onWebResourceError(_toWebResourceError(error)); - }) - ..setWebViewWebContentProcessDidTerminate((WKWebView webView) { - callbacksHandler.onWebResourceError(WebResourceError( - errorCode: WKErrorCode.webContentProcessTerminated, - // Value from https://developer.apple.com/documentation/webkit/wkerrordomain?language=objc. - domain: 'WKErrorDomain', - description: '', - errorType: WebResourceErrorType.webContentProcessTerminated, - )); - }); + didStartProvisionalNavigation: (WKWebView webView, String? url) { + callbacksHandler.onPageStarted(url ?? ''); + }, + decidePolicyForNavigationAction: ( + WKWebView webView, + WKNavigationAction action, + ) async { + if (!_hasNavigationDelegate) { + return WKNavigationActionPolicy.allow; + } + + final bool allow = await callbacksHandler.onNavigationRequest( + url: action.request.url, + isForMainFrame: action.targetFrame.isMainFrame, + ); + + return allow + ? WKNavigationActionPolicy.allow + : WKNavigationActionPolicy.cancel; + }, + didFailNavigation: (WKWebView webView, NSError error) { + callbacksHandler.onWebResourceError(_toWebResourceError(error)); + }, + didFailProvisionalNavigation: (WKWebView webView, NSError error) { + callbacksHandler.onWebResourceError(_toWebResourceError(error)); + }, + webViewWebContentProcessDidTerminate: (WKWebView webView) { + callbacksHandler.onWebResourceError(WebResourceError( + errorCode: WKErrorCode.webContentProcessTerminated, + // Value from https://developer.apple.com/documentation/webkit/wkerrordomain?language=objc. + domain: 'WKErrorDomain', + description: '', + errorType: WebResourceErrorType.webContentProcessTerminated, + )); + }, + ); Future _setCreationParams( CreationParams params, { @@ -151,18 +177,17 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { autoMediaPlaybackPolicy: params.autoMediaPlaybackPolicy, ); - webView = webViewProxy.createWebView(configuration); - - webView.setUIDelegate(uiDelegate); - uiDelegate.setOnCreateWebView(( - WKWebViewConfiguration configuration, - WKNavigationAction navigationAction, + webView = webViewProxy.createWebView(configuration, observeValue: ( + String keyPath, + NSObject object, + Map change, ) { - if (!navigationAction.targetFrame.isMainFrame) { - webView.loadRequest(navigationAction.request); - } + final double progress = change[NSKeyValueChangeKey.newValue]! as double; + callbacksHandler.onProgress((progress * 100).round()); }); + webView.setUIDelegate(uiDelegate); + await addJavascriptChannels(params.javascriptChannelNames); webView.setNavigationDelegate(navigationDelegate); @@ -358,10 +383,11 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { @override Future updateSettings(WebSettings setting) async { + if (setting.hasNavigationDelegate != null) { + _hasNavigationDelegate = setting.hasNavigationDelegate!; + } await Future.wait(>[ _setUserAgent(setting.userAgent), - if (setting.hasNavigationDelegate != null) - _setHasNavigationDelegate(setting.hasNavigationDelegate!), if (setting.hasProgressTracking != null) _setHasProgressTracking(setting.hasProgressTracking!), if (setting.javascriptMode != null) @@ -384,16 +410,15 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { ).map>( (String channelName) { final WKScriptMessageHandler handler = - webViewProxy.createScriptMessageHandler() - ..setDidReceiveScriptMessage(( - WKUserContentController userContentController, - WKScriptMessage message, - ) { - javascriptChannelRegistry.onJavascriptChannelMessage( - message.name, - message.body!.toString(), - ); - }); + webViewProxy.createScriptMessageHandler(didReceiveScriptMessage: ( + WKUserContentController userContentController, + WKScriptMessage message, + ) { + javascriptChannelRegistry.onJavascriptChannelMessage( + message.name, + message.body!.toString(), + ); + }); _scriptMessageHandlers[channelName] = handler; final String wrapperSource = @@ -426,34 +451,8 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { await _resetUserScripts(removedJavaScriptChannels: javascriptChannelNames); } - Future _setHasNavigationDelegate(bool hasNavigationDelegate) { - if (hasNavigationDelegate) { - return navigationDelegate.setDecidePolicyForNavigationAction( - (WKWebView webView, WKNavigationAction action) async { - final bool allow = await callbacksHandler.onNavigationRequest( - url: action.request.url, - isForMainFrame: action.targetFrame.isMainFrame, - ); - - return allow - ? WKNavigationActionPolicy.allow - : WKNavigationActionPolicy.cancel; - }); - } else { - return navigationDelegate.setDecidePolicyForNavigationAction(null); - } - } - Future _setHasProgressTracking(bool hasProgressTracking) { if (hasProgressTracking) { - webView.setObserveValue(( - String keyPath, - NSObject object, - Map change, - ) { - final double progress = change[NSKeyValueChangeKey.newValue]! as double; - callbacksHandler.onProgress((progress * 100).round()); - }); return webView.addObserver( webView, keyPath: 'estimatedProgress', @@ -462,7 +461,6 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { }, ); } else { - webView.setObserveValue(null); return webView.removeObserver(webView, keyPath: 'estimatedProgress'); } } @@ -605,18 +603,40 @@ class WebViewWidgetProxy { const WebViewWidgetProxy(); /// Constructs a [WKWebView]. - WKWebView createWebView(WKWebViewConfiguration configuration) { - return WKWebView(configuration); + WKWebView createWebView( + WKWebViewConfiguration configuration, { + void Function( + String keyPath, + NSObject object, + Map change, + )? + observeValue, + }) { + return WKWebView(configuration, observeValue: observeValue); } /// Constructs a [WKScriptMessageHandler]. - WKScriptMessageHandler createScriptMessageHandler() { - return WKScriptMessageHandler(); + WKScriptMessageHandler createScriptMessageHandler({ + required void Function( + WKUserContentController userContentController, + WKScriptMessage message, + ) + didReceiveScriptMessage, + }) { + return WKScriptMessageHandler( + didReceiveScriptMessage: didReceiveScriptMessage, + ); } /// Constructs a [WKUIDelegate]. - WKUIDelegate createUIDelgate() { - return WKUIDelegate(); + WKUIDelegate createUIDelgate({ + void Function( + WKWebViewConfiguration configuration, + WKNavigationAction navigationAction, + )? + onCreateWebView, + }) { + return WKUIDelegate(onCreateWebView: onCreateWebView); } /// Constructs a [WKNavigationDelegate]. @@ -626,7 +646,26 @@ class WebViewWidgetProxy { String? url, )? didFinishNavigation, + void Function(WKWebView webView, String? url)? + didStartProvisionalNavigation, + Future Function( + WKWebView webView, + WKNavigationAction navigationAction, + )? + decidePolicyForNavigationAction, + void Function(WKWebView webView, NSError error)? didFailNavigation, + void Function(WKWebView webView, NSError error)? + didFailProvisionalNavigation, + void Function(WKWebView webView)? webViewWebContentProcessDidTerminate, }) { - return WKNavigationDelegate(didFinishNavigation: didFinishNavigation); + return WKNavigationDelegate( + didFinishNavigation: didFinishNavigation, + didStartProvisionalNavigation: didStartProvisionalNavigation, + decidePolicyForNavigationAction: decidePolicyForNavigationAction, + didFailNavigation: didFailNavigation, + didFailProvisionalNavigation: didFailProvisionalNavigation, + webViewWebContentProcessDidTerminate: + webViewWebContentProcessDidTerminate, + ); } } 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 7bd208eeac05..e328a292fcbe 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 @@ -1,5 +1,5 @@ -// Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart. +// Mocks generated by Mockito 5.2.0 from annotations +// in webview_flutter_wkwebview/test/src/foundation/foundation_test.dart. // Do not manually edit this file. import 'package:mockito/mockito.dart' as _i1; @@ -28,21 +28,21 @@ class MockTestNSObjectHostApi extends _i1.Mock } @override - void dispose(int? instanceId) => - super.noSuchMethod(Invocation.method(#dispose, [instanceId]), + void dispose(int? identifier) => + super.noSuchMethod(Invocation.method(#dispose, [identifier]), returnValueForMissingStub: null); @override - void addObserver(int? instanceId, int? observerInstanceId, String? keyPath, + void addObserver(int? identifier, int? observerIdentifier, String? keyPath, List<_i3.NSKeyValueObservingOptionsEnumData?>? options) => super.noSuchMethod( Invocation.method( - #addObserver, [instanceId, observerInstanceId, keyPath, options]), + #addObserver, [identifier, observerIdentifier, keyPath, options]), returnValueForMissingStub: null); @override void removeObserver( - int? instanceId, int? observerInstanceId, String? keyPath) => + int? identifier, int? observerIdentifier, String? keyPath) => super.noSuchMethod( Invocation.method( - #removeObserver, [instanceId, observerInstanceId, keyPath]), + #removeObserver, [identifier, observerIdentifier, keyPath]), returnValueForMissingStub: null); } 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 a9f9b2c322c7..58939f5b8829 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 @@ -1,5 +1,5 @@ -// Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart. +// Mocks generated by Mockito 5.2.0 from annotations +// in webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart. // Do not manually edit this file. import 'dart:async' as _i4; @@ -30,26 +30,26 @@ class MockTestWKWebViewConfigurationHostApi extends _i1.Mock } @override - void create(int? instanceId) => - super.noSuchMethod(Invocation.method(#create, [instanceId]), + void create(int? identifier) => + super.noSuchMethod(Invocation.method(#create, [identifier]), returnValueForMissingStub: null); @override - void createFromWebView(int? instanceId, int? webViewInstanceId) => + void createFromWebView(int? identifier, int? webViewIdentifier) => super.noSuchMethod( Invocation.method( - #createFromWebView, [instanceId, webViewInstanceId]), + #createFromWebView, [identifier, webViewIdentifier]), returnValueForMissingStub: null); @override - void setAllowsInlineMediaPlayback(int? instanceId, bool? allow) => + void setAllowsInlineMediaPlayback(int? identifier, bool? allow) => super.noSuchMethod( - Invocation.method(#setAllowsInlineMediaPlayback, [instanceId, allow]), + Invocation.method(#setAllowsInlineMediaPlayback, [identifier, allow]), returnValueForMissingStub: null); @override void setMediaTypesRequiringUserActionForPlayback( - int? instanceId, List<_i3.WKAudiovisualMediaTypeEnumData?>? types) => + int? identifier, List<_i3.WKAudiovisualMediaTypeEnumData?>? types) => super.noSuchMethod( Invocation.method(#setMediaTypesRequiringUserActionForPlayback, - [instanceId, types]), + [identifier, types]), returnValueForMissingStub: null); } @@ -63,88 +63,88 @@ class MockTestWKWebViewHostApi extends _i1.Mock } @override - void create(int? instanceId, int? configurationInstanceId) => + void create(int? identifier, int? configurationIdentifier) => super.noSuchMethod( - Invocation.method(#create, [instanceId, configurationInstanceId]), + Invocation.method(#create, [identifier, configurationIdentifier]), returnValueForMissingStub: null); @override - void setUIDelegate(int? instanceId, int? uiDelegateInstanceId) => + void setUIDelegate(int? identifier, int? uiDelegateIdentifier) => super.noSuchMethod( - Invocation.method(#setUIDelegate, [instanceId, uiDelegateInstanceId]), + Invocation.method(#setUIDelegate, [identifier, uiDelegateIdentifier]), returnValueForMissingStub: null); @override void setNavigationDelegate( - int? instanceId, int? navigationDelegateInstanceId) => + int? identifier, int? navigationDelegateIdentifier) => super.noSuchMethod( Invocation.method(#setNavigationDelegate, - [instanceId, navigationDelegateInstanceId]), + [identifier, navigationDelegateIdentifier]), returnValueForMissingStub: null); @override - String? getUrl(int? instanceId) => - (super.noSuchMethod(Invocation.method(#getUrl, [instanceId])) as String?); + String? getUrl(int? identifier) => + (super.noSuchMethod(Invocation.method(#getUrl, [identifier])) as String?); @override - double getEstimatedProgress(int? instanceId) => (super.noSuchMethod( - Invocation.method(#getEstimatedProgress, [instanceId]), + double getEstimatedProgress(int? identifier) => (super.noSuchMethod( + Invocation.method(#getEstimatedProgress, [identifier]), returnValue: 0.0) as double); @override - void loadRequest(int? instanceId, _i3.NSUrlRequestData? request) => - super.noSuchMethod(Invocation.method(#loadRequest, [instanceId, request]), + void loadRequest(int? identifier, _i3.NSUrlRequestData? request) => + super.noSuchMethod(Invocation.method(#loadRequest, [identifier, request]), returnValueForMissingStub: null); @override - void loadHtmlString(int? instanceId, String? string, String? baseUrl) => + void loadHtmlString(int? identifier, String? string, String? baseUrl) => super.noSuchMethod( - Invocation.method(#loadHtmlString, [instanceId, string, baseUrl]), + Invocation.method(#loadHtmlString, [identifier, string, baseUrl]), returnValueForMissingStub: null); @override - void loadFileUrl(int? instanceId, String? url, String? readAccessUrl) => + void loadFileUrl(int? identifier, String? url, String? readAccessUrl) => super.noSuchMethod( - Invocation.method(#loadFileUrl, [instanceId, url, readAccessUrl]), + Invocation.method(#loadFileUrl, [identifier, url, readAccessUrl]), returnValueForMissingStub: null); @override - void loadFlutterAsset(int? instanceId, String? key) => super.noSuchMethod( - Invocation.method(#loadFlutterAsset, [instanceId, key]), + void loadFlutterAsset(int? identifier, String? key) => super.noSuchMethod( + Invocation.method(#loadFlutterAsset, [identifier, key]), returnValueForMissingStub: null); @override - bool canGoBack(int? instanceId) => - (super.noSuchMethod(Invocation.method(#canGoBack, [instanceId]), + bool canGoBack(int? identifier) => + (super.noSuchMethod(Invocation.method(#canGoBack, [identifier]), returnValue: false) as bool); @override - bool canGoForward(int? instanceId) => - (super.noSuchMethod(Invocation.method(#canGoForward, [instanceId]), + bool canGoForward(int? identifier) => + (super.noSuchMethod(Invocation.method(#canGoForward, [identifier]), returnValue: false) as bool); @override - void goBack(int? instanceId) => - super.noSuchMethod(Invocation.method(#goBack, [instanceId]), + void goBack(int? identifier) => + super.noSuchMethod(Invocation.method(#goBack, [identifier]), returnValueForMissingStub: null); @override - void goForward(int? instanceId) => - super.noSuchMethod(Invocation.method(#goForward, [instanceId]), + void goForward(int? identifier) => + super.noSuchMethod(Invocation.method(#goForward, [identifier]), returnValueForMissingStub: null); @override - void reload(int? instanceId) => - super.noSuchMethod(Invocation.method(#reload, [instanceId]), + void reload(int? identifier) => + super.noSuchMethod(Invocation.method(#reload, [identifier]), returnValueForMissingStub: null); @override - String? getTitle(int? instanceId) => - (super.noSuchMethod(Invocation.method(#getTitle, [instanceId])) + String? getTitle(int? identifier) => + (super.noSuchMethod(Invocation.method(#getTitle, [identifier])) as String?); @override - void setAllowsBackForwardNavigationGestures(int? instanceId, bool? allow) => + void setAllowsBackForwardNavigationGestures(int? identifier, bool? allow) => super.noSuchMethod( Invocation.method( - #setAllowsBackForwardNavigationGestures, [instanceId, allow]), + #setAllowsBackForwardNavigationGestures, [identifier, allow]), returnValueForMissingStub: null); @override - void setCustomUserAgent(int? instanceId, String? userAgent) => + void setCustomUserAgent(int? identifier, String? userAgent) => super.noSuchMethod( - Invocation.method(#setCustomUserAgent, [instanceId, userAgent]), + Invocation.method(#setCustomUserAgent, [identifier, userAgent]), returnValueForMissingStub: null); @override _i4.Future evaluateJavaScript( - int? instanceId, String? javaScriptString) => + int? identifier, String? javaScriptString) => (super.noSuchMethod( Invocation.method( - #evaluateJavaScript, [instanceId, javaScriptString]), + #evaluateJavaScript, [identifier, javaScriptString]), returnValue: Future.value()) as _i4.Future); } @@ -158,22 +158,22 @@ class MockTestUIScrollViewHostApi extends _i1.Mock } @override - void createFromWebView(int? instanceId, int? webViewInstanceId) => + void createFromWebView(int? identifier, int? webViewIdentifier) => super.noSuchMethod( Invocation.method( - #createFromWebView, [instanceId, webViewInstanceId]), + #createFromWebView, [identifier, webViewIdentifier]), returnValueForMissingStub: null); @override - List getContentOffset(int? instanceId) => - (super.noSuchMethod(Invocation.method(#getContentOffset, [instanceId]), + List getContentOffset(int? identifier) => + (super.noSuchMethod(Invocation.method(#getContentOffset, [identifier]), returnValue: []) as List); @override - void scrollBy(int? instanceId, double? x, double? y) => - super.noSuchMethod(Invocation.method(#scrollBy, [instanceId, x, y]), + void scrollBy(int? identifier, double? x, double? y) => + super.noSuchMethod(Invocation.method(#scrollBy, [identifier, x, y]), returnValueForMissingStub: null); @override - void setContentOffset(int? instanceId, double? x, double? y) => super - .noSuchMethod(Invocation.method(#setContentOffset, [instanceId, x, y]), + void setContentOffset(int? identifier, double? x, double? y) => super + .noSuchMethod(Invocation.method(#setContentOffset, [identifier, x, y]), returnValueForMissingStub: null); } @@ -186,11 +186,11 @@ class MockTestUIViewHostApi extends _i1.Mock implements _i2.TestUIViewHostApi { } @override - void setBackgroundColor(int? instanceId, int? value) => super.noSuchMethod( - Invocation.method(#setBackgroundColor, [instanceId, value]), + void setBackgroundColor(int? identifier, int? value) => super.noSuchMethod( + Invocation.method(#setBackgroundColor, [identifier, value]), returnValueForMissingStub: null); @override - void setOpaque(int? instanceId, bool? opaque) => - super.noSuchMethod(Invocation.method(#setOpaque, [instanceId, opaque]), + void setOpaque(int? identifier, bool? opaque) => + super.noSuchMethod(Invocation.method(#setOpaque, [identifier, opaque]), returnValueForMissingStub: null); } 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 f486474b4cfb..cf417c4e0ca2 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 @@ -186,6 +186,7 @@ void main() { TestWKScriptMessageHandlerHostApi.setup(mockPlatformHostApi); scriptMessageHandler = WKScriptMessageHandler( + didReceiveScriptMessage: (_, __) {}, instanceManager: instanceManager, ); }); @@ -288,6 +289,7 @@ void main() { MockTestWKScriptMessageHandlerHostApi(), ); final WKScriptMessageHandler handler = WKScriptMessageHandler( + didReceiveScriptMessage: (_, __) {}, instanceManager: instanceManager, ); 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 16d80b22c22b..39ba08f3aa93 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 @@ -1,5 +1,5 @@ -// Mocks generated by Mockito 5.1.0 from annotations -// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. +// Mocks generated by Mockito 5.2.0 from annotations +// in webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. // Do not manually edit this file. import 'dart:async' as _i3; @@ -31,14 +31,14 @@ class MockTestWKHttpCookieStoreHostApi extends _i1.Mock @override void createFromWebsiteDataStore( - int? instanceId, int? websiteDataStoreInstanceId) => + int? identifier, int? websiteDataStoreIdentifier) => super.noSuchMethod( Invocation.method(#createFromWebsiteDataStore, - [instanceId, websiteDataStoreInstanceId]), + [identifier, websiteDataStoreIdentifier]), returnValueForMissingStub: null); @override - _i3.Future setCookie(int? instanceId, _i4.NSHttpCookieData? cookie) => - (super.noSuchMethod(Invocation.method(#setCookie, [instanceId, cookie]), + _i3.Future setCookie(int? identifier, _i4.NSHttpCookieData? cookie) => + (super.noSuchMethod(Invocation.method(#setCookie, [identifier, cookie]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i3.Future); } @@ -53,8 +53,8 @@ class MockTestWKNavigationDelegateHostApi extends _i1.Mock } @override - void create(int? instanceId) => - super.noSuchMethod(Invocation.method(#create, [instanceId]), + void create(int? identifier) => + super.noSuchMethod(Invocation.method(#create, [identifier]), returnValueForMissingStub: null); } @@ -69,15 +69,15 @@ class MockTestWKPreferencesHostApi extends _i1.Mock @override void createFromWebViewConfiguration( - int? instanceId, int? configurationInstanceId) => + int? identifier, int? configurationIdentifier) => super.noSuchMethod( Invocation.method(#createFromWebViewConfiguration, - [instanceId, configurationInstanceId]), + [identifier, configurationIdentifier]), returnValueForMissingStub: null); @override - void setJavaScriptEnabled(int? instanceId, bool? enabled) => + void setJavaScriptEnabled(int? identifier, bool? enabled) => super.noSuchMethod( - Invocation.method(#setJavaScriptEnabled, [instanceId, enabled]), + Invocation.method(#setJavaScriptEnabled, [identifier, enabled]), returnValueForMissingStub: null); } @@ -91,8 +91,8 @@ class MockTestWKScriptMessageHandlerHostApi extends _i1.Mock } @override - void create(int? instanceId) => - super.noSuchMethod(Invocation.method(#create, [instanceId]), + void create(int? identifier) => + super.noSuchMethod(Invocation.method(#create, [identifier]), returnValueForMissingStub: null); } @@ -106,8 +106,8 @@ class MockTestWKUIDelegateHostApi extends _i1.Mock } @override - void create(int? instanceId) => - super.noSuchMethod(Invocation.method(#create, [instanceId]), + void create(int? identifier) => + super.noSuchMethod(Invocation.method(#create, [identifier]), returnValueForMissingStub: null); } @@ -122,34 +122,34 @@ class MockTestWKUserContentControllerHostApi extends _i1.Mock @override void createFromWebViewConfiguration( - int? instanceId, int? configurationInstanceId) => + int? identifier, int? configurationIdentifier) => super.noSuchMethod( Invocation.method(#createFromWebViewConfiguration, - [instanceId, configurationInstanceId]), + [identifier, configurationIdentifier]), returnValueForMissingStub: null); @override void addScriptMessageHandler( - int? instanceId, int? handlerInstanceid, String? name) => + int? identifier, int? handlerIdentifier, String? name) => super.noSuchMethod( Invocation.method( - #addScriptMessageHandler, [instanceId, handlerInstanceid, name]), + #addScriptMessageHandler, [identifier, handlerIdentifier, name]), returnValueForMissingStub: null); @override - void removeScriptMessageHandler(int? instanceId, String? name) => + void removeScriptMessageHandler(int? identifier, String? name) => super.noSuchMethod( - Invocation.method(#removeScriptMessageHandler, [instanceId, name]), + Invocation.method(#removeScriptMessageHandler, [identifier, name]), returnValueForMissingStub: null); @override - void removeAllScriptMessageHandlers(int? instanceId) => super.noSuchMethod( - Invocation.method(#removeAllScriptMessageHandlers, [instanceId]), + void removeAllScriptMessageHandlers(int? identifier) => super.noSuchMethod( + Invocation.method(#removeAllScriptMessageHandlers, [identifier]), returnValueForMissingStub: null); @override - void addUserScript(int? instanceId, _i4.WKUserScriptData? userScript) => super - .noSuchMethod(Invocation.method(#addUserScript, [instanceId, userScript]), + void addUserScript(int? identifier, _i4.WKUserScriptData? userScript) => super + .noSuchMethod(Invocation.method(#addUserScript, [identifier, userScript]), returnValueForMissingStub: null); @override - void removeAllUserScripts(int? instanceId) => - super.noSuchMethod(Invocation.method(#removeAllUserScripts, [instanceId]), + void removeAllUserScripts(int? identifier) => + super.noSuchMethod(Invocation.method(#removeAllUserScripts, [identifier]), returnValueForMissingStub: null); } @@ -163,26 +163,26 @@ class MockTestWKWebViewConfigurationHostApi extends _i1.Mock } @override - void create(int? instanceId) => - super.noSuchMethod(Invocation.method(#create, [instanceId]), + void create(int? identifier) => + super.noSuchMethod(Invocation.method(#create, [identifier]), returnValueForMissingStub: null); @override - void createFromWebView(int? instanceId, int? webViewInstanceId) => + void createFromWebView(int? identifier, int? webViewIdentifier) => super.noSuchMethod( Invocation.method( - #createFromWebView, [instanceId, webViewInstanceId]), + #createFromWebView, [identifier, webViewIdentifier]), returnValueForMissingStub: null); @override - void setAllowsInlineMediaPlayback(int? instanceId, bool? allow) => + void setAllowsInlineMediaPlayback(int? identifier, bool? allow) => super.noSuchMethod( - Invocation.method(#setAllowsInlineMediaPlayback, [instanceId, allow]), + Invocation.method(#setAllowsInlineMediaPlayback, [identifier, allow]), returnValueForMissingStub: null); @override void setMediaTypesRequiringUserActionForPlayback( - int? instanceId, List<_i4.WKAudiovisualMediaTypeEnumData?>? types) => + int? identifier, List<_i4.WKAudiovisualMediaTypeEnumData?>? types) => super.noSuchMethod( Invocation.method(#setMediaTypesRequiringUserActionForPlayback, - [instanceId, types]), + [identifier, types]), returnValueForMissingStub: null); } @@ -196,88 +196,88 @@ class MockTestWKWebViewHostApi extends _i1.Mock } @override - void create(int? instanceId, int? configurationInstanceId) => + void create(int? identifier, int? configurationIdentifier) => super.noSuchMethod( - Invocation.method(#create, [instanceId, configurationInstanceId]), + Invocation.method(#create, [identifier, configurationIdentifier]), returnValueForMissingStub: null); @override - void setUIDelegate(int? instanceId, int? uiDelegateInstanceId) => + void setUIDelegate(int? identifier, int? uiDelegateIdentifier) => super.noSuchMethod( - Invocation.method(#setUIDelegate, [instanceId, uiDelegateInstanceId]), + Invocation.method(#setUIDelegate, [identifier, uiDelegateIdentifier]), returnValueForMissingStub: null); @override void setNavigationDelegate( - int? instanceId, int? navigationDelegateInstanceId) => + int? identifier, int? navigationDelegateIdentifier) => super.noSuchMethod( Invocation.method(#setNavigationDelegate, - [instanceId, navigationDelegateInstanceId]), + [identifier, navigationDelegateIdentifier]), returnValueForMissingStub: null); @override - String? getUrl(int? instanceId) => - (super.noSuchMethod(Invocation.method(#getUrl, [instanceId])) as String?); + String? getUrl(int? identifier) => + (super.noSuchMethod(Invocation.method(#getUrl, [identifier])) as String?); @override - double getEstimatedProgress(int? instanceId) => (super.noSuchMethod( - Invocation.method(#getEstimatedProgress, [instanceId]), + double getEstimatedProgress(int? identifier) => (super.noSuchMethod( + Invocation.method(#getEstimatedProgress, [identifier]), returnValue: 0.0) as double); @override - void loadRequest(int? instanceId, _i4.NSUrlRequestData? request) => - super.noSuchMethod(Invocation.method(#loadRequest, [instanceId, request]), + void loadRequest(int? identifier, _i4.NSUrlRequestData? request) => + super.noSuchMethod(Invocation.method(#loadRequest, [identifier, request]), returnValueForMissingStub: null); @override - void loadHtmlString(int? instanceId, String? string, String? baseUrl) => + void loadHtmlString(int? identifier, String? string, String? baseUrl) => super.noSuchMethod( - Invocation.method(#loadHtmlString, [instanceId, string, baseUrl]), + Invocation.method(#loadHtmlString, [identifier, string, baseUrl]), returnValueForMissingStub: null); @override - void loadFileUrl(int? instanceId, String? url, String? readAccessUrl) => + void loadFileUrl(int? identifier, String? url, String? readAccessUrl) => super.noSuchMethod( - Invocation.method(#loadFileUrl, [instanceId, url, readAccessUrl]), + Invocation.method(#loadFileUrl, [identifier, url, readAccessUrl]), returnValueForMissingStub: null); @override - void loadFlutterAsset(int? instanceId, String? key) => super.noSuchMethod( - Invocation.method(#loadFlutterAsset, [instanceId, key]), + void loadFlutterAsset(int? identifier, String? key) => super.noSuchMethod( + Invocation.method(#loadFlutterAsset, [identifier, key]), returnValueForMissingStub: null); @override - bool canGoBack(int? instanceId) => - (super.noSuchMethod(Invocation.method(#canGoBack, [instanceId]), + bool canGoBack(int? identifier) => + (super.noSuchMethod(Invocation.method(#canGoBack, [identifier]), returnValue: false) as bool); @override - bool canGoForward(int? instanceId) => - (super.noSuchMethod(Invocation.method(#canGoForward, [instanceId]), + bool canGoForward(int? identifier) => + (super.noSuchMethod(Invocation.method(#canGoForward, [identifier]), returnValue: false) as bool); @override - void goBack(int? instanceId) => - super.noSuchMethod(Invocation.method(#goBack, [instanceId]), + void goBack(int? identifier) => + super.noSuchMethod(Invocation.method(#goBack, [identifier]), returnValueForMissingStub: null); @override - void goForward(int? instanceId) => - super.noSuchMethod(Invocation.method(#goForward, [instanceId]), + void goForward(int? identifier) => + super.noSuchMethod(Invocation.method(#goForward, [identifier]), returnValueForMissingStub: null); @override - void reload(int? instanceId) => - super.noSuchMethod(Invocation.method(#reload, [instanceId]), + void reload(int? identifier) => + super.noSuchMethod(Invocation.method(#reload, [identifier]), returnValueForMissingStub: null); @override - String? getTitle(int? instanceId) => - (super.noSuchMethod(Invocation.method(#getTitle, [instanceId])) + String? getTitle(int? identifier) => + (super.noSuchMethod(Invocation.method(#getTitle, [identifier])) as String?); @override - void setAllowsBackForwardNavigationGestures(int? instanceId, bool? allow) => + void setAllowsBackForwardNavigationGestures(int? identifier, bool? allow) => super.noSuchMethod( Invocation.method( - #setAllowsBackForwardNavigationGestures, [instanceId, allow]), + #setAllowsBackForwardNavigationGestures, [identifier, allow]), returnValueForMissingStub: null); @override - void setCustomUserAgent(int? instanceId, String? userAgent) => + void setCustomUserAgent(int? identifier, String? userAgent) => super.noSuchMethod( - Invocation.method(#setCustomUserAgent, [instanceId, userAgent]), + Invocation.method(#setCustomUserAgent, [identifier, userAgent]), returnValueForMissingStub: null); @override _i3.Future evaluateJavaScript( - int? instanceId, String? javaScriptString) => + int? identifier, String? javaScriptString) => (super.noSuchMethod( Invocation.method( - #evaluateJavaScript, [instanceId, javaScriptString]), + #evaluateJavaScript, [identifier, javaScriptString]), returnValue: Future.value()) as _i3.Future); } @@ -292,22 +292,22 @@ class MockTestWKWebsiteDataStoreHostApi extends _i1.Mock @override void createFromWebViewConfiguration( - int? instanceId, int? configurationInstanceId) => + int? identifier, int? configurationIdentifier) => super.noSuchMethod( Invocation.method(#createFromWebViewConfiguration, - [instanceId, configurationInstanceId]), + [identifier, configurationIdentifier]), returnValueForMissingStub: null); @override - void createDefaultDataStore(int? instanceId) => super.noSuchMethod( - Invocation.method(#createDefaultDataStore, [instanceId]), + void createDefaultDataStore(int? identifier) => super.noSuchMethod( + Invocation.method(#createDefaultDataStore, [identifier]), returnValueForMissingStub: null); @override _i3.Future removeDataOfTypes( - int? instanceId, + int? identifier, List<_i4.WKWebsiteDataTypeEnumData?>? dataTypes, double? modificationTimeInSecondsSinceEpoch) => (super.noSuchMethod( Invocation.method(#removeDataOfTypes, - [instanceId, dataTypes, modificationTimeInSecondsSinceEpoch]), + [identifier, dataTypes, modificationTimeInSecondsSinceEpoch]), returnValue: Future.value(false)) as _i3.Future); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart index 5989c138ff25..b0c63b663066 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.1.0 from annotations +// Mocks generated by Mockito 5.2.0 from annotations // in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart. // Do not manually edit this file. @@ -54,14 +54,6 @@ class MockWKHttpCookieStore extends _i1.Mock implements _i3.WKHttpCookieStore { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i4.Future setObserveValue( - void Function( - String, _i5.NSObject, Map<_i5.NSKeyValueChangeKey, Object?>)? - observeValue) => - (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); - @override _i2.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), returnValue: _FakeCopyable_0()) as _i2.Copyable); } @@ -100,14 +92,6 @@ class MockWKWebsiteDataStore extends _i1.Mock returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); @override - _i4.Future setObserveValue( - void Function( - String, _i5.NSObject, Map<_i5.NSKeyValueChangeKey, Object?>)? - observeValue) => - (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); - @override _i2.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), returnValue: _FakeCopyable_0()) as _i2.Copyable); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 6af5f7d16279..c1cdfc503a59 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -62,10 +62,27 @@ void main() { mockNavigationDelegate = MockWKNavigationDelegate(); mockWebViewWidgetProxy = MockWebViewWidgetProxy(); - when(mockWebViewWidgetProxy.createWebView(any)).thenReturn(mockWebView); - when(mockWebViewWidgetProxy.createUIDelgate()).thenReturn(mockUIDelegate); + when( + mockWebViewWidgetProxy.createWebView( + any, + observeValue: anyNamed('observeValue'), + ), + ).thenReturn(mockWebView); + when( + mockWebViewWidgetProxy.createUIDelgate( + onCreateWebView: captureAnyNamed('onCreateWebView'), + ), + ).thenReturn(mockUIDelegate); when(mockWebViewWidgetProxy.createNavigationDelegate( didFinishNavigation: anyNamed('didFinishNavigation'), + didStartProvisionalNavigation: + anyNamed('didStartProvisionalNavigation'), + decidePolicyForNavigationAction: + anyNamed('decidePolicyForNavigationAction'), + didFailNavigation: anyNamed('didFailNavigation'), + didFailProvisionalNavigation: anyNamed('didFailProvisionalNavigation'), + webViewWebContentProcessDidTerminate: + anyNamed('webViewWebContentProcessDidTerminate'), )).thenReturn(mockNavigationDelegate); when(mockWebView.configuration).thenReturn(mockWebViewConfiguration); when(mockWebViewConfiguration.userContentController).thenReturn( @@ -118,9 +135,11 @@ void main() { (WidgetTester tester) async { await buildWidget(tester); - final dynamic onCreateWebView = - verify(mockUIDelegate.setOnCreateWebView(captureAny)).captured.single - as void Function(WKWebViewConfiguration, WKNavigationAction); + final dynamic onCreateWebView = verify( + mockWebViewWidgetProxy.createUIDelgate( + onCreateWebView: captureAnyNamed('onCreateWebView'))) + .captured + .single as void Function(WKWebViewConfiguration, WKNavigationAction); const NSUrlRequest request = NSUrlRequest(url: 'https://google.com'); onCreateWebView( @@ -224,7 +243,11 @@ void main() { }); testWidgets('javascriptChannelNames', (WidgetTester tester) async { - when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn( + when( + mockWebViewWidgetProxy.createScriptMessageHandler( + didReceiveScriptMessage: anyNamed('didReceiveScriptMessage'), + ), + ).thenReturn( MockWKScriptMessageHandler(), ); @@ -273,21 +296,6 @@ void main() { verify(mockPreferences.setJavaScriptEnabled(true)); }); - testWidgets('hasNavigationDelegate', (WidgetTester tester) async { - await buildWidget( - tester, - creationParams: CreationParams( - webSettings: WebSettings( - userAgent: const WebSetting.absent(), - hasNavigationDelegate: true, - ), - ), - ); - - verify(mockNavigationDelegate - .setDecidePolicyForNavigationAction(argThat(isNotNull))); - }); - testWidgets('userAgent', (WidgetTester tester) async { await buildWidget( tester, @@ -305,8 +313,11 @@ void main() { testWidgets( 'enabling zoom re-adds JavaScript channels', (WidgetTester tester) async { - when(mockWebViewWidgetProxy.createScriptMessageHandler()) - .thenReturn( + when( + mockWebViewWidgetProxy.createScriptMessageHandler( + didReceiveScriptMessage: anyNamed('didReceiveScriptMessage'), + ), + ).thenReturn( MockWKScriptMessageHandler(), ); @@ -783,7 +794,11 @@ void main() { }); testWidgets('addJavascriptChannels', (WidgetTester tester) async { - when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn( + when( + mockWebViewWidgetProxy.createScriptMessageHandler( + didReceiveScriptMessage: anyNamed('didReceiveScriptMessage'), + ), + ).thenReturn( MockWKScriptMessageHandler(), ); @@ -824,7 +839,11 @@ void main() { }); testWidgets('removeJavascriptChannels', (WidgetTester tester) async { - when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn( + when( + mockWebViewWidgetProxy.createScriptMessageHandler( + didReceiveScriptMessage: anyNamed('didReceiveScriptMessage'), + ), + ).thenReturn( MockWKScriptMessageHandler(), ); @@ -865,7 +884,11 @@ void main() { testWidgets('removeJavascriptChannels with zoom disabled', (WidgetTester tester) async { - when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn( + when( + mockWebViewWidgetProxy.createScriptMessageHandler( + didReceiveScriptMessage: anyNamed('didReceiveScriptMessage'), + ), + ).thenReturn( MockWKScriptMessageHandler(), ); @@ -906,11 +929,19 @@ void main() { testWidgets('onPageStarted', (WidgetTester tester) async { await buildWidget(tester); - final dynamic didStartProvisionalNavigation = verify( - mockNavigationDelegate - .setDidStartProvisionalNavigation(captureAny)) - .captured - .single as void Function(WKWebView, String); + final dynamic didStartProvisionalNavigation = + verify(mockWebViewWidgetProxy.createNavigationDelegate( + didFinishNavigation: anyNamed('didFinishNavigation'), + didStartProvisionalNavigation: + captureAnyNamed('didStartProvisionalNavigation'), + decidePolicyForNavigationAction: + anyNamed('decidePolicyForNavigationAction'), + didFailNavigation: anyNamed('didFailNavigation'), + didFailProvisionalNavigation: + anyNamed('didFailProvisionalNavigation'), + webViewWebContentProcessDidTerminate: + anyNamed('webViewWebContentProcessDidTerminate'), + )).captured.single as void Function(WKWebView, String); didStartProvisionalNavigation(mockWebView, 'https://google.com'); verify(mockCallbacksHandler.onPageStarted('https://google.com')); @@ -922,6 +953,15 @@ void main() { final dynamic didFinishNavigation = verify(mockWebViewWidgetProxy.createNavigationDelegate( didFinishNavigation: captureAnyNamed('didFinishNavigation'), + didStartProvisionalNavigation: + anyNamed('didStartProvisionalNavigation'), + decidePolicyForNavigationAction: + anyNamed('decidePolicyForNavigationAction'), + didFailNavigation: anyNamed('didFailNavigation'), + didFailProvisionalNavigation: + anyNamed('didFailProvisionalNavigation'), + webViewWebContentProcessDidTerminate: + anyNamed('webViewWebContentProcessDidTerminate'), )).captured.single as void Function(WKWebView, String); didFinishNavigation(mockWebView, 'https://google.com'); @@ -933,9 +973,18 @@ void main() { await buildWidget(tester); final dynamic didFailNavigation = - verify(mockNavigationDelegate.setDidFailNavigation(captureAny)) - .captured - .single as void Function(WKWebView, NSError); + verify(mockWebViewWidgetProxy.createNavigationDelegate( + didFinishNavigation: anyNamed('didFinishNavigation'), + didStartProvisionalNavigation: + anyNamed('didStartProvisionalNavigation'), + decidePolicyForNavigationAction: + anyNamed('decidePolicyForNavigationAction'), + didFailNavigation: captureAnyNamed('didFailNavigation'), + didFailProvisionalNavigation: + anyNamed('didFailProvisionalNavigation'), + webViewWebContentProcessDidTerminate: + anyNamed('webViewWebContentProcessDidTerminate'), + )).captured.single as void Function(WKWebView, NSError); didFailNavigation( mockWebView, @@ -960,11 +1009,19 @@ void main() { (WidgetTester tester) async { await buildWidget(tester); - final dynamic didFailProvisionalNavigation = verify( - mockNavigationDelegate - .setDidFailProvisionalNavigation(captureAny)) - .captured - .single as void Function(WKWebView, NSError); + final dynamic didFailProvisionalNavigation = + verify(mockWebViewWidgetProxy.createNavigationDelegate( + didFinishNavigation: anyNamed('didFinishNavigation'), + didStartProvisionalNavigation: + anyNamed('didStartProvisionalNavigation'), + decidePolicyForNavigationAction: + anyNamed('decidePolicyForNavigationAction'), + didFailNavigation: anyNamed('didFailNavigation'), + didFailProvisionalNavigation: + captureAnyNamed('didFailProvisionalNavigation'), + webViewWebContentProcessDidTerminate: + anyNamed('webViewWebContentProcessDidTerminate'), + )).captured.single as void Function(WKWebView, NSError); didFailProvisionalNavigation( mockWebView, @@ -993,11 +1050,19 @@ void main() { (WidgetTester tester) async { await buildWidget(tester); - final dynamic webViewWebContentProcessDidTerminate = verify( - mockNavigationDelegate - .setWebViewWebContentProcessDidTerminate(captureAny)) - .captured - .single as void Function(WKWebView); + final dynamic webViewWebContentProcessDidTerminate = + verify(mockWebViewWidgetProxy.createNavigationDelegate( + didFinishNavigation: anyNamed('didFinishNavigation'), + didStartProvisionalNavigation: + anyNamed('didStartProvisionalNavigation'), + decidePolicyForNavigationAction: + anyNamed('decidePolicyForNavigationAction'), + didFailNavigation: anyNamed('didFailNavigation'), + didFailProvisionalNavigation: + anyNamed('didFailProvisionalNavigation'), + webViewWebContentProcessDidTerminate: + captureAnyNamed('webViewWebContentProcessDidTerminate'), + )).captured.single as void Function(WKWebView); webViewWebContentProcessDidTerminate(mockWebView); final WebResourceError error = @@ -1017,12 +1082,19 @@ void main() { (WidgetTester tester) async { await buildWidget(tester, hasNavigationDelegate: true); - final dynamic decidePolicyForNavigationAction = verify( - mockNavigationDelegate - .setDecidePolicyForNavigationAction(captureAny)) - .captured - .single - as Future Function( + final dynamic decidePolicyForNavigationAction = + verify(mockWebViewWidgetProxy.createNavigationDelegate( + didFinishNavigation: anyNamed('didFinishNavigation'), + didStartProvisionalNavigation: + anyNamed('didStartProvisionalNavigation'), + decidePolicyForNavigationAction: + captureAnyNamed('decidePolicyForNavigationAction'), + didFailNavigation: anyNamed('didFailNavigation'), + didFailProvisionalNavigation: + anyNamed('didFailProvisionalNavigation'), + webViewWebContentProcessDidTerminate: + anyNamed('webViewWebContentProcessDidTerminate'), + )).captured.single as Future Function( WKWebView, WKNavigationAction); when(mockCallbacksHandler.onNavigationRequest( @@ -1049,13 +1121,6 @@ void main() { testWidgets('onProgress', (WidgetTester tester) async { await buildWidget(tester, hasProgressTracking: true); - final dynamic observeValue = - verify(mockWebView.setObserveValue(captureAny)).captured.single - as void Function( - String keyPath, - NSObject object, - Map change, - ); verify(mockWebView.addObserver( mockWebView, @@ -1065,6 +1130,16 @@ void main() { }, )); + final dynamic observeValue = verify( + mockWebViewWidgetProxy.createWebView(any, + observeValue: captureAnyNamed('observeValue'))) + .captured + .single as void Function( + String keyPath, + NSObject object, + Map change, + ); + observeValue( 'estimatedProgress', mockWebView, @@ -1077,23 +1152,23 @@ void main() { group('JavascriptChannelRegistry', () { testWidgets('onJavascriptChannelMessage', (WidgetTester tester) async { - when(mockWebViewWidgetProxy.createScriptMessageHandler()).thenReturn( + when( + mockWebViewWidgetProxy.createScriptMessageHandler( + didReceiveScriptMessage: anyNamed('didReceiveScriptMessage'), + ), + ).thenReturn( MockWKScriptMessageHandler(), ); await buildWidget(tester); await testController.addJavascriptChannels({'hello'}); - final MockWKScriptMessageHandler messageHandler = verify( - mockUserContentController.addScriptMessageHandler( - captureAny, 'hello')) + final dynamic didReceiveScriptMessage = verify( + mockWebViewWidgetProxy.createScriptMessageHandler( + didReceiveScriptMessage: + captureAnyNamed('didReceiveScriptMessage'))) .captured - .single as MockWKScriptMessageHandler; - - final dynamic didReceiveScriptMessage = - verify(messageHandler.setDidReceiveScriptMessage(captureAny)) - .captured - .single as void Function( + .single as void Function( WKUserContentController userContentController, WKScriptMessage message, ); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index f2a9876a71e4..728c526ca929 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.1.0 from annotations +// Mocks generated by Mockito 5.2.0 from annotations // in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. // Do not manually edit this file. @@ -36,31 +36,31 @@ class _FakePoint_0 extends _i1.Fake implements _i2.Point {} class _FakeCopyable_1 extends _i1.Fake implements _i3.Copyable {} -class _FakeWKWebViewConfiguration_2 extends _i1.Fake +class _FakeWKNavigationDelegate_2 extends _i1.Fake + implements _i4.WKNavigationDelegate {} + +class _FakeWKWebViewConfiguration_3 extends _i1.Fake implements _i4.WKWebViewConfiguration {} -class _FakeUIScrollView_3 extends _i1.Fake implements _i5.UIScrollView {} +class _FakeUIScrollView_4 extends _i1.Fake implements _i5.UIScrollView {} -class _FakeWKUserContentController_4 extends _i1.Fake +class _FakeWKUserContentController_5 extends _i1.Fake implements _i4.WKUserContentController {} -class _FakeWKPreferences_5 extends _i1.Fake implements _i4.WKPreferences {} +class _FakeWKPreferences_6 extends _i1.Fake implements _i4.WKPreferences {} -class _FakeWKWebsiteDataStore_6 extends _i1.Fake +class _FakeWKWebsiteDataStore_7 extends _i1.Fake implements _i4.WKWebsiteDataStore {} -class _FakeWKHttpCookieStore_7 extends _i1.Fake +class _FakeWKHttpCookieStore_8 extends _i1.Fake implements _i4.WKHttpCookieStore {} -class _FakeWKWebView_8 extends _i1.Fake implements _i4.WKWebView {} +class _FakeWKWebView_9 extends _i1.Fake implements _i4.WKWebView {} -class _FakeWKScriptMessageHandler_9 extends _i1.Fake +class _FakeWKScriptMessageHandler_10 extends _i1.Fake implements _i4.WKScriptMessageHandler {} -class _FakeWKUIDelegate_10 extends _i1.Fake implements _i4.WKUIDelegate {} - -class _FakeWKNavigationDelegate_11 extends _i1.Fake - implements _i4.WKNavigationDelegate {} +class _FakeWKUIDelegate_11 extends _i1.Fake implements _i4.WKUIDelegate {} /// A class which mocks [UIScrollView]. /// @@ -110,14 +110,6 @@ class MockUIScrollView extends _i1.Mock implements _i5.UIScrollView { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i6.Future setObserveValue( - void Function( - String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? - observeValue) => - (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), returnValue: _FakeCopyable_1()) as _i3.Copyable); } @@ -133,51 +125,9 @@ class MockWKNavigationDelegate extends _i1.Mock } @override - _i6.Future setDidStartProvisionalNavigation( - void Function(_i4.WKWebView, String?)? - didStartProvisionalNavigation) => - (super.noSuchMethod( - Invocation.method(#setDidStartProvisionalNavigation, - [didStartProvisionalNavigation]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override - _i6.Future setDecidePolicyForNavigationAction( - _i6.Future<_i4.WKNavigationActionPolicy> Function( - _i4.WKWebView, _i4.WKNavigationAction)? - decidePolicyForNavigationAction) => - (super.noSuchMethod( - Invocation.method(#setDecidePolicyForNavigationAction, - [decidePolicyForNavigationAction]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override - _i6.Future setDidFailNavigation( - void Function(_i4.WKWebView, _i8.NSError)? didFailNavigation) => - (super.noSuchMethod( - Invocation.method(#setDidFailNavigation, [didFailNavigation]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override - _i6.Future setDidFailProvisionalNavigation( - void Function(_i4.WKWebView, _i8.NSError)? - didFailProvisionalNavigation) => - (super.noSuchMethod( - Invocation.method( - #setDidFailProvisionalNavigation, [didFailProvisionalNavigation]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override - _i6.Future setWebViewWebContentProcessDidTerminate( - void Function(_i4.WKWebView)? webViewWebContentProcessDidTerminate) => - (super.noSuchMethod( - Invocation.method(#setWebViewWebContentProcessDidTerminate, - [webViewWebContentProcessDidTerminate]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override - _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeCopyable_1()) as _i3.Copyable); + _i4.WKNavigationDelegate copy() => (super.noSuchMethod( + Invocation.method(#copy, []), + returnValue: _FakeWKNavigationDelegate_2()) as _i4.WKNavigationDelegate); @override _i6.Future addObserver(_i8.NSObject? observer, {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => @@ -192,14 +142,6 @@ class MockWKNavigationDelegate extends _i1.Mock Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i6.Future); - @override - _i6.Future setObserveValue( - void Function( - String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? - observeValue) => - (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); } /// A class which mocks [WKPreferences]. @@ -230,14 +172,6 @@ class MockWKPreferences extends _i1.Mock implements _i4.WKPreferences { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i6.Future setObserveValue( - void Function( - String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? - observeValue) => - (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), returnValue: _FakeCopyable_1()) as _i3.Copyable); } @@ -252,14 +186,12 @@ class MockWKScriptMessageHandler extends _i1.Mock } @override - _i6.Future setDidReceiveScriptMessage( - void Function(_i4.WKUserContentController, _i4.WKScriptMessage)? - didReceiveScriptMessage) => - (super.noSuchMethod( - Invocation.method( - #setDidReceiveScriptMessage, [didReceiveScriptMessage]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + void Function(_i4.WKUserContentController, _i4.WKScriptMessage) + get didReceiveScriptMessage => + (super.noSuchMethod(Invocation.getter(#didReceiveScriptMessage), + returnValue: (_i4.WKUserContentController userContentController, + _i4.WKScriptMessage message) {}) as void Function( + _i4.WKUserContentController, _i4.WKScriptMessage)); @override _i6.Future addObserver(_i8.NSObject? observer, {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => @@ -275,14 +207,6 @@ class MockWKScriptMessageHandler extends _i1.Mock returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i6.Future setObserveValue( - void Function( - String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? - observeValue) => - (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), returnValue: _FakeCopyable_1()) as _i3.Copyable); } @@ -298,12 +222,12 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { @override _i4.WKWebViewConfiguration get configuration => (super.noSuchMethod(Invocation.getter(#configuration), - returnValue: _FakeWKWebViewConfiguration_2()) + returnValue: _FakeWKWebViewConfiguration_3()) as _i4.WKWebViewConfiguration); @override _i5.UIScrollView get scrollView => (super.noSuchMethod(Invocation.getter(#scrollView), - returnValue: _FakeUIScrollView_3()) as _i5.UIScrollView); + returnValue: _FakeUIScrollView_4()) as _i5.UIScrollView); @override _i6.Future setUIDelegate(_i4.WKUIDelegate? delegate) => (super.noSuchMethod(Invocation.method(#setUIDelegate, [delegate]), @@ -412,14 +336,6 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i6.Future setObserveValue( - void Function( - String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? - observeValue) => - (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), returnValue: _FakeCopyable_1()) as _i3.Copyable); } @@ -436,16 +352,16 @@ class MockWKWebViewConfiguration extends _i1.Mock @override _i4.WKUserContentController get userContentController => (super.noSuchMethod(Invocation.getter(#userContentController), - returnValue: _FakeWKUserContentController_4()) + returnValue: _FakeWKUserContentController_5()) as _i4.WKUserContentController); @override _i4.WKPreferences get preferences => (super.noSuchMethod(Invocation.getter(#preferences), - returnValue: _FakeWKPreferences_5()) as _i4.WKPreferences); + returnValue: _FakeWKPreferences_6()) as _i4.WKPreferences); @override _i4.WKWebsiteDataStore get websiteDataStore => (super.noSuchMethod(Invocation.getter(#websiteDataStore), - returnValue: _FakeWKWebsiteDataStore_6()) as _i4.WKWebsiteDataStore); + returnValue: _FakeWKWebsiteDataStore_7()) as _i4.WKWebsiteDataStore); @override _i6.Future setAllowsInlineMediaPlayback(bool? allow) => (super .noSuchMethod(Invocation.method(#setAllowsInlineMediaPlayback, [allow]), @@ -474,14 +390,6 @@ class MockWKWebViewConfiguration extends _i1.Mock returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i6.Future setObserveValue( - void Function( - String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? - observeValue) => - (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), returnValue: _FakeCopyable_1()) as _i3.Copyable); } @@ -498,7 +406,7 @@ class MockWKWebsiteDataStore extends _i1.Mock @override _i4.WKHttpCookieStore get httpCookieStore => (super.noSuchMethod(Invocation.getter(#httpCookieStore), - returnValue: _FakeWKHttpCookieStore_7()) as _i4.WKHttpCookieStore); + returnValue: _FakeWKHttpCookieStore_8()) as _i4.WKHttpCookieStore); @override _i6.Future removeDataOfTypes( Set<_i4.WKWebsiteDataType>? dataTypes, DateTime? since) => @@ -520,14 +428,6 @@ class MockWKWebsiteDataStore extends _i1.Mock returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i6.Future setObserveValue( - void Function( - String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? - observeValue) => - (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), returnValue: _FakeCopyable_1()) as _i3.Copyable); } @@ -540,14 +440,6 @@ class MockWKUIDelegate extends _i1.Mock implements _i4.WKUIDelegate { _i1.throwOnMissingStub(this); } - @override - _i6.Future setOnCreateWebView( - void Function(_i4.WKWebViewConfiguration, _i4.WKNavigationAction)? - onCreateWebView) => - (super.noSuchMethod( - Invocation.method(#setOnCreateWebView, [onCreateWebView]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); @override _i6.Future addObserver(_i8.NSObject? observer, {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => @@ -563,14 +455,6 @@ class MockWKUIDelegate extends _i1.Mock implements _i4.WKUIDelegate { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i6.Future setObserveValue( - void Function( - String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? - observeValue) => - (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), returnValue: _FakeCopyable_1()) as _i3.Copyable); } @@ -626,14 +510,6 @@ class MockWKUserContentController extends _i1.Mock returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i6.Future); @override - _i6.Future setObserveValue( - void Function( - String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? - observeValue) => - (super.noSuchMethod(Invocation.method(#setObserveValue, [observeValue]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), returnValue: _FakeCopyable_1()) as _i3.Copyable); } @@ -707,24 +583,54 @@ class MockWebViewWidgetProxy extends _i1.Mock } @override - _i4.WKWebView createWebView(_i4.WKWebViewConfiguration? configuration) => - (super.noSuchMethod(Invocation.method(#createWebView, [configuration]), - returnValue: _FakeWKWebView_8()) as _i4.WKWebView); + _i4.WKWebView createWebView(_i4.WKWebViewConfiguration? configuration, + {void Function( + String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? + observeValue}) => + (super.noSuchMethod( + Invocation.method( + #createWebView, [configuration], {#observeValue: observeValue}), + returnValue: _FakeWKWebView_9()) as _i4.WKWebView); @override - _i4.WKScriptMessageHandler createScriptMessageHandler() => - (super.noSuchMethod(Invocation.method(#createScriptMessageHandler, []), - returnValue: _FakeWKScriptMessageHandler_9()) + _i4.WKScriptMessageHandler createScriptMessageHandler( + {void Function(_i4.WKUserContentController, _i4.WKScriptMessage)? + didReceiveScriptMessage}) => + (super.noSuchMethod( + Invocation.method(#createScriptMessageHandler, [], + {#didReceiveScriptMessage: didReceiveScriptMessage}), + returnValue: _FakeWKScriptMessageHandler_10()) as _i4.WKScriptMessageHandler); @override - _i4.WKUIDelegate createUIDelgate() => - (super.noSuchMethod(Invocation.method(#createUIDelgate, []), - returnValue: _FakeWKUIDelegate_10()) as _i4.WKUIDelegate); + _i4.WKUIDelegate createUIDelgate( + {void Function(_i4.WKWebViewConfiguration, _i4.WKNavigationAction)? + onCreateWebView}) => + (super.noSuchMethod( + Invocation.method( + #createUIDelgate, [], {#onCreateWebView: onCreateWebView}), + returnValue: _FakeWKUIDelegate_11()) as _i4.WKUIDelegate); @override _i4.WKNavigationDelegate createNavigationDelegate( - {void Function(_i4.WKWebView, String?)? didFinishNavigation}) => - (super.noSuchMethod( - Invocation.method(#createNavigationDelegate, [], - {#didFinishNavigation: didFinishNavigation}), - returnValue: _FakeWKNavigationDelegate_11()) + {void Function(_i4.WKWebView, String?)? didFinishNavigation, + void Function(_i4.WKWebView, String?)? didStartProvisionalNavigation, + _i6.Future<_i4.WKNavigationActionPolicy> Function( + _i4.WKWebView, _i4.WKNavigationAction)? + decidePolicyForNavigationAction, + void Function(_i4.WKWebView, _i8.NSError)? didFailNavigation, + void Function(_i4.WKWebView, _i8.NSError)? + didFailProvisionalNavigation, + void Function(_i4.WKWebView)? + webViewWebContentProcessDidTerminate}) => + (super.noSuchMethod( + Invocation.method(#createNavigationDelegate, [], { + #didFinishNavigation: didFinishNavigation, + #didStartProvisionalNavigation: didStartProvisionalNavigation, + #decidePolicyForNavigationAction: + decidePolicyForNavigationAction, + #didFailNavigation: didFailNavigation, + #didFailProvisionalNavigation: didFailProvisionalNavigation, + #webViewWebContentProcessDidTerminate: + webViewWebContentProcessDidTerminate + }), + returnValue: _FakeWKNavigationDelegate_2()) as _i4.WKNavigationDelegate); } From 2726dab108813fa2510fb95a41f42e302c2e9e95 Mon Sep 17 00:00:00 2001 From: Alexandre Ardhuin Date: Wed, 8 Jun 2022 13:59:02 +0200 Subject: [PATCH 400/844] Ignore upcoming warnings (#5931) * ignore upcoming warnings * update-release-info --- packages/camera/camera/CHANGELOG.md | 4 ++++ packages/camera/camera/lib/src/camera_image.dart | 2 ++ packages/camera/camera/test/camera_image_test.dart | 2 ++ packages/camera/camera_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/method_channel/type_conversion.dart | 2 ++ .../lib/src/types/camera_image_data.dart | 2 ++ .../test/method_channel/type_conversion_test.dart | 2 ++ .../test/types/camera_image_data_test.dart | 2 ++ packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md | 4 ++++ .../google_maps_flutter/lib/google_maps_flutter.dart | 2 ++ .../google_maps_flutter/test/map_creation_test.dart | 2 ++ .../google_maps_flutter_platform_interface/CHANGELOG.md | 4 ++++ .../method_channel/method_channel_google_maps_flutter.dart | 2 ++ .../src/platform_interface/google_maps_flutter_platform.dart | 2 ++ packages/video_player/video_player/CHANGELOG.md | 4 ++++ .../example/integration_test/video_player_test.dart | 2 ++ packages/webview_flutter/webview_flutter/CHANGELOG.md | 1 + .../example/integration_test/webview_flutter_test.dart | 2 ++ 18 files changed, 45 insertions(+) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 72af38a9f9de..3a8bf2ee0c1a 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). + ## 0.9.7+1 * Moves streaming implementation to the platform interface package. diff --git a/packages/camera/camera/lib/src/camera_image.dart b/packages/camera/camera/lib/src/camera_image.dart index cb3d306eaf6e..bfcad6626dd6 100644 --- a/packages/camera/camera/lib/src/camera_image.dart +++ b/packages/camera/camera/lib/src/camera_image.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. +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import import 'dart:typed_data'; import 'package:camera_platform_interface/camera_platform_interface.dart'; diff --git a/packages/camera/camera/test/camera_image_test.dart b/packages/camera/camera/test/camera_image_test.dart index c964e7acd97b..ecf4b509e2e4 100644 --- a/packages/camera/camera/test/camera_image_test.dart +++ b/packages/camera/camera/test/camera_image_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. +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import import 'dart:typed_data'; import 'package:camera/camera.dart'; diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index 5ecd8891fe20..a46b758d8053 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). + ## 2.2.0 * Adds image streaming to the platform interface. diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/type_conversion.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/type_conversion.dart index 9dffbbf6ae3a..8b360077305c 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/type_conversion.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/type_conversion.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. +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import import 'dart:typed_data'; import 'package:flutter/foundation.dart'; diff --git a/packages/camera/camera_platform_interface/lib/src/types/camera_image_data.dart b/packages/camera/camera_platform_interface/lib/src/types/camera_image_data.dart index 6971dbb39737..4bafe270fa49 100644 --- a/packages/camera/camera_platform_interface/lib/src/types/camera_image_data.dart +++ b/packages/camera/camera_platform_interface/lib/src/types/camera_image_data.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. +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import import 'dart:typed_data'; import 'package:flutter/foundation.dart'; diff --git a/packages/camera/camera_platform_interface/test/method_channel/type_conversion_test.dart b/packages/camera/camera_platform_interface/test/method_channel/type_conversion_test.dart index a8ca45eca43b..4818074ec767 100644 --- a/packages/camera/camera_platform_interface/test/method_channel/type_conversion_test.dart +++ b/packages/camera/camera_platform_interface/test/method_channel/type_conversion_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. +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import import 'dart:typed_data'; import 'package:camera_platform_interface/camera_platform_interface.dart'; diff --git a/packages/camera/camera_platform_interface/test/types/camera_image_data_test.dart b/packages/camera/camera_platform_interface/test/types/camera_image_data_test.dart index f06213e2b0e4..d8c582d74844 100644 --- a/packages/camera/camera_platform_interface/test/types/camera_image_data_test.dart +++ b/packages/camera/camera_platform_interface/test/types/camera_image_data_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. +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import import 'dart:typed_data'; import 'package:camera_platform_interface/camera_platform_interface.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 8d78ec38b057..d09aa4d8bd07 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). + ## 2.1.7 * Objective-C code cleanup. diff --git a/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart b/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart index 9736b8b5e06f..4eeb8572413c 100644 --- a/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart +++ b/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart @@ -6,6 +6,8 @@ library google_maps_flutter; import 'dart:async'; import 'dart:io'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import import 'dart:typed_data'; import 'package:flutter/foundation.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter/test/map_creation_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/map_creation_test.dart index 73e1e77646cd..2b03d785953b 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/map_creation_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/map_creation_test.dart @@ -3,6 +3,8 @@ // found in the LICENSE file. import 'dart:async'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import import 'dart:typed_data'; import 'package:flutter/foundation.dart'; 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 0359bab0b7c5..016ec9e968e3 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 @@ +## NEXT + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). + ## 2.1.7 * Updates code for stricter analysis options. 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 365da757e435..60df6f52499f 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 @@ -3,6 +3,8 @@ // found in the LICENSE file. import 'dart:async'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import import 'dart:typed_data'; import 'package:flutter/foundation.dart'; 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 6386ed2523f2..8c36ebe4f6ef 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 @@ -3,6 +3,8 @@ // found in the LICENSE file. import 'dart:async'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import import 'dart:typed_data'; import 'package:flutter/foundation.dart'; diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 02ff3128c6a6..379909b86e42 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). + ## 2.4.4 * Updates references to the obsolete master branch. diff --git a/packages/video_player/video_player/example/integration_test/video_player_test.dart b/packages/video_player/video_player/example/integration_test/video_player_test.dart index 633d636c7c69..d20f47fd69ed 100644 --- a/packages/video_player/video_player/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player/example/integration_test/video_player_test.dart @@ -4,6 +4,8 @@ import 'dart:async'; import 'dart:io'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import import 'dart:typed_data'; import 'package:flutter/foundation.dart'; diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index f7500e569992..98ce9d263415 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). * Updates references to the obsolete master branch. ## 3.0.4 diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index 066ac030595f..17548901bcb8 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -9,6 +9,8 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import import 'dart:typed_data'; import 'package:flutter/foundation.dart'; From b83d7d8e033763881cb8fe3dee8718ca7e7cc308 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Jun 2022 05:18:10 -0700 Subject: [PATCH 401/844] [gh_actions]: Bump lewagon/wait-on-check-action from 5e937358caba2c7876a2ee06e4a48d0664fe4967 to 1.1.1 (#5926) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a67f8f1575e4..cec0a29e9701 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,7 +36,7 @@ jobs: # This workflow should be the last to run. So wait for all the other tests to succeed. - name: Wait on all tests - uses: lewagon/wait-on-check-action@5e937358caba2c7876a2ee06e4a48d0664fe4967 + uses: lewagon/wait-on-check-action@752bfae19aef55dab12a00bc36d48acc46b77e9d with: ref: ${{ github.sha }} running-workflow-name: 'release' From 768272671eee26b7b780525bc52843fd50547ea5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Jun 2022 05:23:09 -0700 Subject: [PATCH 402/844] [gh_actions]: Bump actions/upload-artifact from 3.0.0 to 3.1.0 (#5925) --- .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 32c80cd247a2..8815da22805d 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -40,7 +40,7 @@ jobs: # Upload the results as artifacts (optional). - name: "Upload artifact" - uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 + uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 with: name: SARIF file path: results.sarif From f7d99fa5dbacc10cc8af7e44145d9cb7d41b82c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Jun 2022 06:03:11 -0700 Subject: [PATCH 403/844] [gh_actions]: Bump actions/labeler from 3.0.0 to 4 (#5927) --- .github/workflows/pull_request_label.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull_request_label.yml b/.github/workflows/pull_request_label.yml index 54cae2deca98..dff3fbd411d6 100644 --- a/.github/workflows/pull_request_label.yml +++ b/.github/workflows/pull_request_label.yml @@ -21,7 +21,7 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@9794b1493b6f1fa7b006c5f8635a19c76c98be95 + - uses: actions/labeler@9fd24f1f9d6ceb64ba34d181b329ee72f99978a0 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" sync-labels: true From 4d194a4eeace000bde3085c7cd33ef22e49a8904 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Jun 2022 09:18:13 -0700 Subject: [PATCH 404/844] [gh_actions]: Bump actions/checkout from 2.3.4 to 3.0.2 (#5923) --- .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 cec0a29e9701..d4e2b11ec5f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,7 @@ jobs: cd $GITHUB_WORKSPACE # Checks out a copy of the repo. - name: Check out code - uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f + uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b 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 8815da22805d..52c11472b953 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -20,7 +20,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 + uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b with: persist-credentials: false From 533eb3f22097fcde279a0471f4ba06d3b936d6b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Jun 2022 10:03:11 -0700 Subject: [PATCH 405/844] [gh_actions]: Bump ossf/scorecard-action from 1.0.4 to 1.1.1 (#5928) --- .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 52c11472b953..5e695cd86e25 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -25,7 +25,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@c1aec4ac820532bab364f02a81873c555a0ba3a1 + uses: ossf/scorecard-action@3e15ea8318eee9b333819ec77a36aca8d39df13e with: results_file: results.sarif results_format: sarif From d50cab703dd96398bc202b88777c39929917dc3b Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 8 Jun 2022 12:33:14 -0700 Subject: [PATCH 406/844] [webview_flutter_wkwebview] Implement Dart side of Flutter Apis (#5933) --- .../ios/Classes/FWFGeneratedWebKitApis.h | 111 ++- .../ios/Classes/FWFGeneratedWebKitApis.m | 643 ++++++++++++++++- .../lib/src/common/web_kit.pigeon.dart | 678 +++++++++++++++++- .../src/foundation/foundation_api_impls.dart | 62 +- .../lib/src/web_kit/web_kit.dart | 43 +- .../lib/src/web_kit/web_kit_api_impls.dart | 237 +++++- .../lib/src/web_kit_webview_widget.dart | 2 + .../pigeons/web_kit.dart | 96 ++- .../test/src/common/test_web_kit.pigeon.dart | 68 +- .../test/src/foundation/foundation_test.dart | 45 ++ .../test/src/web_kit/web_kit_test.dart | 240 ++++++- .../web_kit_cookie_manager_test.mocks.dart | 2 +- .../test/src/web_kit_webview_widget_test.dart | 11 +- .../web_kit_webview_widget_test.mocks.dart | 54 +- 14 files changed, 2187 insertions(+), 105 deletions(-) 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 b291f4167725..ebd29f5cacad 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 (v3.1.2), do not edit directly. +// Autogenerated from Pigeon (v3.1.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #import @protocol FlutterBinaryMessenger; @@ -79,12 +79,18 @@ typedef NS_ENUM(NSUInteger, FWFNSHttpCookiePropertyKeyEnum) { }; @class FWFNSKeyValueObservingOptionsEnumData; +@class FWFNSKeyValueChangeKeyEnumData; @class FWFWKUserScriptInjectionTimeEnumData; @class FWFWKAudiovisualMediaTypeEnumData; @class FWFWKWebsiteDataTypeEnumData; +@class FWFWKNavigationActionPolicyEnumData; @class FWFNSHttpCookiePropertyKeyEnumData; @class FWFNSUrlRequestData; @class FWFWKUserScriptData; +@class FWFWKNavigationActionData; +@class FWFWKFrameInfoData; +@class FWFNSErrorData; +@class FWFWKScriptMessageData; @class FWFNSHttpCookieData; @interface FWFNSKeyValueObservingOptionsEnumData : NSObject @@ -94,6 +100,13 @@ typedef NS_ENUM(NSUInteger, FWFNSHttpCookiePropertyKeyEnum) { @property(nonatomic, assign) FWFNSKeyValueObservingOptionsEnum value; @end +@interface FWFNSKeyValueChangeKeyEnumData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithValue:(FWFNSKeyValueChangeKeyEnum)value; +@property(nonatomic, assign) FWFNSKeyValueChangeKeyEnum value; +@end + @interface FWFWKUserScriptInjectionTimeEnumData : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; @@ -115,6 +128,13 @@ typedef NS_ENUM(NSUInteger, FWFNSHttpCookiePropertyKeyEnum) { @property(nonatomic, assign) FWFWKWebsiteDataTypeEnum value; @end +@interface FWFWKNavigationActionPolicyEnumData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithValue:(FWFWKNavigationActionPolicyEnum)value; +@property(nonatomic, assign) FWFWKNavigationActionPolicyEnum value; +@end + @interface FWFNSHttpCookiePropertyKeyEnumData : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; @@ -146,6 +166,41 @@ typedef NS_ENUM(NSUInteger, FWFNSHttpCookiePropertyKeyEnum) { @property(nonatomic, strong) NSNumber *isMainFrameOnly; @end +@interface FWFWKNavigationActionData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithRequest:(FWFNSUrlRequestData *)request + targetFrame:(FWFWKFrameInfoData *)targetFrame; +@property(nonatomic, strong) FWFNSUrlRequestData *request; +@property(nonatomic, strong) FWFWKFrameInfoData *targetFrame; +@end + +@interface FWFWKFrameInfoData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithIsMainFrame:(NSNumber *)isMainFrame; +@property(nonatomic, strong) NSNumber *isMainFrame; +@end + +@interface FWFNSErrorData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithCode:(NSNumber *)code + domain:(NSString *)domain + localizedDescription:(NSString *)localizedDescription; +@property(nonatomic, strong) NSNumber *code; +@property(nonatomic, copy) NSString *domain; +@property(nonatomic, copy) NSString *localizedDescription; +@end + +@interface FWFWKScriptMessageData : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithName:(NSString *)name body:(id)body; +@property(nonatomic, copy) NSString *name; +@property(nonatomic, strong) id body; +@end + @interface FWFNSHttpCookieData : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; @@ -298,6 +353,16 @@ extern void FWFWKScriptMessageHandlerHostApiSetup( id binaryMessenger, NSObject *_Nullable api); +/// The codec used by FWFWKScriptMessageHandlerFlutterApi. +NSObject *FWFWKScriptMessageHandlerFlutterApiGetCodec(void); + +@interface FWFWKScriptMessageHandlerFlutterApi : NSObject +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger; +- (void)didReceiveScriptMessageForHandlerWithIdentifier:(NSNumber *)identifier + userContentControllerIdentifier:(NSNumber *)userContentControllerIdentifier + message:(FWFWKScriptMessageData *)message + completion:(void (^)(NSError *_Nullable))completion; +@end /// The codec used by FWFWKNavigationDelegateHostApi. NSObject *FWFWKNavigationDelegateHostApiGetCodec(void); @@ -318,6 +383,33 @@ NSObject *FWFWKNavigationDelegateFlutterApiGetCodec(void); webViewIdentifier:(NSNumber *)webViewIdentifier URL:(nullable NSString *)url completion:(void (^)(NSError *_Nullable))completion; +- (void)didStartProvisionalNavigationForDelegateWithIdentifier:(NSNumber *)identifier + webViewIdentifier:(NSNumber *)webViewIdentifier + URL:(nullable NSString *)url + completion: + (void (^)(NSError *_Nullable))completion; +- (void) + decidePolicyForNavigationActionForDelegateWithIdentifier:(NSNumber *)identifier + webViewIdentifier:(NSNumber *)webViewIdentifier + navigationAction: + (FWFWKNavigationActionData *)navigationAction + completion: + (void (^)(FWFWKNavigationActionPolicyEnumData + *_Nullable, + NSError *_Nullable))completion; +- (void)didFailNavigationForDelegateWithIdentifier:(NSNumber *)identifier + webViewIdentifier:(NSNumber *)webViewIdentifier + error:(FWFNSErrorData *)error + completion:(void (^)(NSError *_Nullable))completion; +- (void)didFailProvisionalNavigationForDelegateWithIdentifier:(NSNumber *)identifier + webViewIdentifier:(NSNumber *)webViewIdentifier + error:(FWFNSErrorData *)error + completion: + (void (^)(NSError *_Nullable))completion; +- (void)webViewWebContentProcessDidTerminateForDelegateWithIdentifier:(NSNumber *)identifier + webViewIdentifier:(NSNumber *)webViewIdentifier + completion:(void (^)(NSError *_Nullable)) + completion; @end /// The codec used by FWFNSObjectHostApi. NSObject *FWFNSObjectHostApiGetCodec(void); @@ -345,6 +437,12 @@ NSObject *FWFNSObjectFlutterApiGetCodec(void); @interface FWFNSObjectFlutterApi : NSObject - (instancetype)initWithBinaryMessenger:(id)binaryMessenger; +- (void)observeValueForObjectWithIdentifier:(NSNumber *)identifier + keyPath:(NSString *)keyPath + objectIdentifier:(NSNumber *)objectIdentifier + changeKeys:(NSArray *)changeKeys + changeValues:(NSArray *)changeValues + completion:(void (^)(NSError *_Nullable))completion; @end /// The codec used by FWFWKWebViewHostApi. NSObject *FWFWKWebViewHostApiGetCodec(void); @@ -420,6 +518,17 @@ NSObject *FWFWKUIDelegateHostApiGetCodec(void); extern void FWFWKUIDelegateHostApiSetup(id binaryMessenger, NSObject *_Nullable api); +/// The codec used by FWFWKUIDelegateFlutterApi. +NSObject *FWFWKUIDelegateFlutterApiGetCodec(void); + +@interface FWFWKUIDelegateFlutterApi : NSObject +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger; +- (void)onCreateWebViewForDelegateWithIdentifier:(NSNumber *)identifier + webViewIdentifier:(NSNumber *)webViewIdentifier + configurationIdentifier:(NSNumber *)configurationIdentifier + navigationAction:(FWFWKNavigationActionData *)navigationAction + completion:(void (^)(NSError *_Nullable))completion; +@end /// The codec used by FWFWKHttpCookieStoreHostApi. NSObject *FWFWKHttpCookieStoreHostApiGetCodec(void); 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 f936d151ddd3..592fa87ac9d1 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 (v3.1.2), do not edit directly. +// Autogenerated from Pigeon (v3.1.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "FWFGeneratedWebKitApis.h" #import @@ -37,6 +37,10 @@ @interface FWFNSKeyValueObservingOptionsEnumData () + (FWFNSKeyValueObservingOptionsEnumData *)fromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end +@interface FWFNSKeyValueChangeKeyEnumData () ++ (FWFNSKeyValueChangeKeyEnumData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end @interface FWFWKUserScriptInjectionTimeEnumData () + (FWFWKUserScriptInjectionTimeEnumData *)fromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @@ -49,6 +53,10 @@ @interface FWFWKWebsiteDataTypeEnumData () + (FWFWKWebsiteDataTypeEnumData *)fromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end +@interface FWFWKNavigationActionPolicyEnumData () ++ (FWFWKNavigationActionPolicyEnumData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end @interface FWFNSHttpCookiePropertyKeyEnumData () + (FWFNSHttpCookiePropertyKeyEnumData *)fromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @@ -61,6 +69,22 @@ @interface FWFWKUserScriptData () + (FWFWKUserScriptData *)fromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end +@interface FWFWKNavigationActionData () ++ (FWFWKNavigationActionData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FWFWKFrameInfoData () ++ (FWFWKFrameInfoData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FWFNSErrorData () ++ (FWFNSErrorData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end +@interface FWFWKScriptMessageData () ++ (FWFWKScriptMessageData *)fromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end @interface FWFNSHttpCookieData () + (FWFNSHttpCookieData *)fromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @@ -86,6 +110,24 @@ - (NSDictionary *)toMap { } @end +@implementation FWFNSKeyValueChangeKeyEnumData ++ (instancetype)makeWithValue:(FWFNSKeyValueChangeKeyEnum)value { + FWFNSKeyValueChangeKeyEnumData *pigeonResult = [[FWFNSKeyValueChangeKeyEnumData alloc] init]; + pigeonResult.value = value; + return pigeonResult; +} ++ (FWFNSKeyValueChangeKeyEnumData *)fromMap:(NSDictionary *)dict { + FWFNSKeyValueChangeKeyEnumData *pigeonResult = [[FWFNSKeyValueChangeKeyEnumData alloc] init]; + pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; + return pigeonResult; +} +- (NSDictionary *)toMap { + return @{ + @"value" : @(self.value), + }; +} +@end + @implementation FWFWKUserScriptInjectionTimeEnumData + (instancetype)makeWithValue:(FWFWKUserScriptInjectionTimeEnum)value { FWFWKUserScriptInjectionTimeEnumData *pigeonResult = @@ -144,6 +186,26 @@ - (NSDictionary *)toMap { } @end +@implementation FWFWKNavigationActionPolicyEnumData ++ (instancetype)makeWithValue:(FWFWKNavigationActionPolicyEnum)value { + FWFWKNavigationActionPolicyEnumData *pigeonResult = + [[FWFWKNavigationActionPolicyEnumData alloc] init]; + pigeonResult.value = value; + return pigeonResult; +} ++ (FWFWKNavigationActionPolicyEnumData *)fromMap:(NSDictionary *)dict { + FWFWKNavigationActionPolicyEnumData *pigeonResult = + [[FWFWKNavigationActionPolicyEnumData alloc] init]; + pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; + return pigeonResult; +} +- (NSDictionary *)toMap { + return @{ + @"value" : @(self.value), + }; +} +@end + @implementation FWFNSHttpCookiePropertyKeyEnumData + (instancetype)makeWithValue:(FWFNSHttpCookiePropertyKeyEnum)value { FWFNSHttpCookiePropertyKeyEnumData *pigeonResult = @@ -225,6 +287,100 @@ - (NSDictionary *)toMap { } @end +@implementation FWFWKNavigationActionData ++ (instancetype)makeWithRequest:(FWFNSUrlRequestData *)request + targetFrame:(FWFWKFrameInfoData *)targetFrame { + FWFWKNavigationActionData *pigeonResult = [[FWFWKNavigationActionData alloc] init]; + pigeonResult.request = request; + pigeonResult.targetFrame = targetFrame; + return pigeonResult; +} ++ (FWFWKNavigationActionData *)fromMap:(NSDictionary *)dict { + FWFWKNavigationActionData *pigeonResult = [[FWFWKNavigationActionData alloc] init]; + pigeonResult.request = [FWFNSUrlRequestData fromMap:GetNullableObject(dict, @"request")]; + NSAssert(pigeonResult.request != nil, @""); + pigeonResult.targetFrame = [FWFWKFrameInfoData fromMap:GetNullableObject(dict, @"targetFrame")]; + NSAssert(pigeonResult.targetFrame != nil, @""); + return pigeonResult; +} +- (NSDictionary *)toMap { + return @{ + @"request" : (self.request ? [self.request toMap] : [NSNull null]), + @"targetFrame" : (self.targetFrame ? [self.targetFrame toMap] : [NSNull null]), + }; +} +@end + +@implementation FWFWKFrameInfoData ++ (instancetype)makeWithIsMainFrame:(NSNumber *)isMainFrame { + FWFWKFrameInfoData *pigeonResult = [[FWFWKFrameInfoData alloc] init]; + pigeonResult.isMainFrame = isMainFrame; + return pigeonResult; +} ++ (FWFWKFrameInfoData *)fromMap:(NSDictionary *)dict { + FWFWKFrameInfoData *pigeonResult = [[FWFWKFrameInfoData alloc] init]; + pigeonResult.isMainFrame = GetNullableObject(dict, @"isMainFrame"); + NSAssert(pigeonResult.isMainFrame != nil, @""); + return pigeonResult; +} +- (NSDictionary *)toMap { + return @{ + @"isMainFrame" : (self.isMainFrame ?: [NSNull null]), + }; +} +@end + +@implementation FWFNSErrorData ++ (instancetype)makeWithCode:(NSNumber *)code + domain:(NSString *)domain + localizedDescription:(NSString *)localizedDescription { + FWFNSErrorData *pigeonResult = [[FWFNSErrorData alloc] init]; + pigeonResult.code = code; + pigeonResult.domain = domain; + pigeonResult.localizedDescription = localizedDescription; + return pigeonResult; +} ++ (FWFNSErrorData *)fromMap:(NSDictionary *)dict { + FWFNSErrorData *pigeonResult = [[FWFNSErrorData alloc] init]; + pigeonResult.code = GetNullableObject(dict, @"code"); + NSAssert(pigeonResult.code != nil, @""); + pigeonResult.domain = GetNullableObject(dict, @"domain"); + NSAssert(pigeonResult.domain != nil, @""); + pigeonResult.localizedDescription = GetNullableObject(dict, @"localizedDescription"); + NSAssert(pigeonResult.localizedDescription != nil, @""); + return pigeonResult; +} +- (NSDictionary *)toMap { + return @{ + @"code" : (self.code ?: [NSNull null]), + @"domain" : (self.domain ?: [NSNull null]), + @"localizedDescription" : (self.localizedDescription ?: [NSNull null]), + }; +} +@end + +@implementation FWFWKScriptMessageData ++ (instancetype)makeWithName:(NSString *)name body:(id)body { + FWFWKScriptMessageData *pigeonResult = [[FWFWKScriptMessageData alloc] init]; + pigeonResult.name = name; + pigeonResult.body = body; + return pigeonResult; +} ++ (FWFWKScriptMessageData *)fromMap:(NSDictionary *)dict { + FWFWKScriptMessageData *pigeonResult = [[FWFWKScriptMessageData alloc] init]; + pigeonResult.name = GetNullableObject(dict, @"name"); + NSAssert(pigeonResult.name != nil, @""); + pigeonResult.body = GetNullableObject(dict, @"body"); + return pigeonResult; +} +- (NSDictionary *)toMap { + return @{ + @"name" : (self.name ?: [NSNull null]), + @"body" : (self.body ?: [NSNull null]), + }; +} +@end + @implementation FWFNSHttpCookieData + (instancetype)makeWithPropertyKeys:(NSArray *)propertyKeys propertyValues:(NSArray *)propertyValues { @@ -1095,6 +1251,87 @@ void FWFWKScriptMessageHandlerHostApiSetup(id binaryMess } } } +@interface FWFWKScriptMessageHandlerFlutterApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKScriptMessageHandlerFlutterApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FWFWKScriptMessageData fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} +@end + +@interface FWFWKScriptMessageHandlerFlutterApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKScriptMessageHandlerFlutterApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FWFWKScriptMessageData class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface FWFWKScriptMessageHandlerFlutterApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKScriptMessageHandlerFlutterApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKScriptMessageHandlerFlutterApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKScriptMessageHandlerFlutterApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKScriptMessageHandlerFlutterApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKScriptMessageHandlerFlutterApiCodecReaderWriter *readerWriter = + [[FWFWKScriptMessageHandlerFlutterApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +@interface FWFWKScriptMessageHandlerFlutterApi () +@property(nonatomic, strong) NSObject *binaryMessenger; +@end + +@implementation FWFWKScriptMessageHandlerFlutterApi + +- (instancetype)initWithBinaryMessenger:(NSObject *)binaryMessenger { + self = [super init]; + if (self) { + _binaryMessenger = binaryMessenger; + } + return self; +} +- (void)didReceiveScriptMessageForHandlerWithIdentifier:(NSNumber *)arg_identifier + userContentControllerIdentifier: + (NSNumber *)arg_userContentControllerIdentifier + message:(FWFWKScriptMessageData *)arg_message + completion:(void (^)(NSError *_Nullable))completion { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName: + @"dev.flutter.pigeon.WKScriptMessageHandlerFlutterApi.didReceiveScriptMessage" + binaryMessenger:self.binaryMessenger + codec:FWFWKScriptMessageHandlerFlutterApiGetCodec()]; + [channel sendMessage:@[ + arg_identifier ?: [NSNull null], arg_userContentControllerIdentifier ?: [NSNull null], + arg_message ?: [NSNull null] + ] + reply:^(id reply) { + completion(nil); + }]; +} +@end @interface FWFWKNavigationDelegateHostApiCodecReader : FlutterStandardReader @end @implementation FWFWKNavigationDelegateHostApiCodecReader @@ -1154,11 +1391,52 @@ void FWFWKNavigationDelegateHostApiSetup(id binaryMessen @interface FWFWKNavigationDelegateFlutterApiCodecReader : FlutterStandardReader @end @implementation FWFWKNavigationDelegateFlutterApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FWFNSErrorData fromMap:[self readValue]]; + + case 129: + return [FWFNSUrlRequestData fromMap:[self readValue]]; + + case 130: + return [FWFWKFrameInfoData fromMap:[self readValue]]; + + case 131: + return [FWFWKNavigationActionData fromMap:[self readValue]]; + + case 132: + return [FWFWKNavigationActionPolicyEnumData fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} @end @interface FWFWKNavigationDelegateFlutterApiCodecWriter : FlutterStandardWriter @end @implementation FWFWKNavigationDelegateFlutterApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FWFNSErrorData class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFNSUrlRequestData class]]) { + [self writeByte:129]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKFrameInfoData class]]) { + [self writeByte:130]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKNavigationActionData class]]) { + [self writeByte:131]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKNavigationActionPolicyEnumData class]]) { + [self writeByte:132]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} @end @interface FWFWKNavigationDelegateFlutterApiCodecReaderWriter : FlutterStandardReaderWriter @@ -1213,6 +1491,96 @@ - (void)didFinishNavigationForDelegateWithIdentifier:(NSNumber *)arg_identifier completion(nil); }]; } +- (void)didStartProvisionalNavigationForDelegateWithIdentifier:(NSNumber *)arg_identifier + webViewIdentifier:(NSNumber *)arg_webViewIdentifier + URL:(nullable NSString *)arg_url + completion: + (void (^)(NSError *_Nullable))completion { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName: + @"dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didStartProvisionalNavigation" + binaryMessenger:self.binaryMessenger + codec:FWFWKNavigationDelegateFlutterApiGetCodec()]; + [channel sendMessage:@[ + arg_identifier ?: [NSNull null], arg_webViewIdentifier ?: [NSNull null], + arg_url ?: [NSNull null] + ] + reply:^(id reply) { + completion(nil); + }]; +} +- (void) + decidePolicyForNavigationActionForDelegateWithIdentifier:(NSNumber *)arg_identifier + webViewIdentifier:(NSNumber *)arg_webViewIdentifier + navigationAction: + (FWFWKNavigationActionData *)arg_navigationAction + completion: + (void (^)(FWFWKNavigationActionPolicyEnumData + *_Nullable, + NSError *_Nullable))completion { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName: + @"dev.flutter.pigeon.WKNavigationDelegateFlutterApi.decidePolicyForNavigationAction" + binaryMessenger:self.binaryMessenger + codec:FWFWKNavigationDelegateFlutterApiGetCodec()]; + [channel sendMessage:@[ + arg_identifier ?: [NSNull null], arg_webViewIdentifier ?: [NSNull null], + arg_navigationAction ?: [NSNull null] + ] + reply:^(id reply) { + FWFWKNavigationActionPolicyEnumData *output = reply; + completion(output, nil); + }]; +} +- (void)didFailNavigationForDelegateWithIdentifier:(NSNumber *)arg_identifier + webViewIdentifier:(NSNumber *)arg_webViewIdentifier + error:(FWFNSErrorData *)arg_error + completion:(void (^)(NSError *_Nullable))completion { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFailNavigation" + binaryMessenger:self.binaryMessenger + codec:FWFWKNavigationDelegateFlutterApiGetCodec()]; + [channel sendMessage:@[ + arg_identifier ?: [NSNull null], arg_webViewIdentifier ?: [NSNull null], + arg_error ?: [NSNull null] + ] + reply:^(id reply) { + completion(nil); + }]; +} +- (void)didFailProvisionalNavigationForDelegateWithIdentifier:(NSNumber *)arg_identifier + webViewIdentifier:(NSNumber *)arg_webViewIdentifier + error:(FWFNSErrorData *)arg_error + completion: + (void (^)(NSError *_Nullable))completion { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName: + @"dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFailProvisionalNavigation" + binaryMessenger:self.binaryMessenger + codec:FWFWKNavigationDelegateFlutterApiGetCodec()]; + [channel sendMessage:@[ + arg_identifier ?: [NSNull null], arg_webViewIdentifier ?: [NSNull null], + arg_error ?: [NSNull null] + ] + reply:^(id reply) { + completion(nil); + }]; +} +- (void)webViewWebContentProcessDidTerminateForDelegateWithIdentifier:(NSNumber *)arg_identifier + webViewIdentifier: + (NSNumber *)arg_webViewIdentifier + completion:(void (^)(NSError *_Nullable)) + completion { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName: + @"dev.flutter.pigeon.WKNavigationDelegateFlutterApi.webViewWebContentProcessDidTerminate" + binaryMessenger:self.binaryMessenger + codec:FWFWKNavigationDelegateFlutterApiGetCodec()]; + [channel sendMessage:@[ arg_identifier ?: [NSNull null], arg_webViewIdentifier ?: [NSNull null] ] + reply:^(id reply) { + completion(nil); + }]; +} @end @interface FWFNSObjectHostApiCodecReader : FlutterStandardReader @end @@ -1350,11 +1718,106 @@ void FWFNSObjectHostApiSetup(id binaryMessenger, @interface FWFNSObjectFlutterApiCodecReader : FlutterStandardReader @end @implementation FWFNSObjectFlutterApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FWFNSErrorData fromMap:[self readValue]]; + + case 129: + return [FWFNSHttpCookieData fromMap:[self readValue]]; + + case 130: + return [FWFNSHttpCookiePropertyKeyEnumData fromMap:[self readValue]]; + + case 131: + return [FWFNSKeyValueChangeKeyEnumData fromMap:[self readValue]]; + + case 132: + return [FWFNSKeyValueObservingOptionsEnumData fromMap:[self readValue]]; + + case 133: + return [FWFNSUrlRequestData fromMap:[self readValue]]; + + case 134: + return [FWFWKAudiovisualMediaTypeEnumData fromMap:[self readValue]]; + + case 135: + return [FWFWKFrameInfoData fromMap:[self readValue]]; + + case 136: + return [FWFWKNavigationActionData fromMap:[self readValue]]; + + case 137: + return [FWFWKNavigationActionPolicyEnumData fromMap:[self readValue]]; + + case 138: + return [FWFWKScriptMessageData fromMap:[self readValue]]; + + case 139: + return [FWFWKUserScriptData fromMap:[self readValue]]; + + case 140: + return [FWFWKUserScriptInjectionTimeEnumData fromMap:[self readValue]]; + + case 141: + return [FWFWKWebsiteDataTypeEnumData fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} @end @interface FWFNSObjectFlutterApiCodecWriter : FlutterStandardWriter @end @implementation FWFNSObjectFlutterApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FWFNSErrorData class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFNSHttpCookieData class]]) { + [self writeByte:129]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFNSHttpCookiePropertyKeyEnumData class]]) { + [self writeByte:130]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFNSKeyValueChangeKeyEnumData class]]) { + [self writeByte:131]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFNSKeyValueObservingOptionsEnumData class]]) { + [self writeByte:132]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFNSUrlRequestData class]]) { + [self writeByte:133]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKAudiovisualMediaTypeEnumData class]]) { + [self writeByte:134]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKFrameInfoData class]]) { + [self writeByte:135]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKNavigationActionData class]]) { + [self writeByte:136]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKNavigationActionPolicyEnumData class]]) { + [self writeByte:137]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKScriptMessageData class]]) { + [self writeByte:138]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKUserScriptData class]]) { + [self writeByte:139]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKUserScriptInjectionTimeEnumData class]]) { + [self writeByte:140]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKWebsiteDataTypeEnumData class]]) { + [self writeByte:141]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} @end @interface FWFNSObjectFlutterApiCodecReaderWriter : FlutterStandardReaderWriter @@ -1392,6 +1855,26 @@ - (instancetype)initWithBinaryMessenger:(NSObject *)bina } return self; } +- (void)observeValueForObjectWithIdentifier:(NSNumber *)arg_identifier + keyPath:(NSString *)arg_keyPath + objectIdentifier:(NSNumber *)arg_objectIdentifier + changeKeys: + (NSArray *)arg_changeKeys + changeValues:(NSArray *)arg_changeValues + completion:(void (^)(NSError *_Nullable))completion { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.NSObjectFlutterApi.observeValue" + binaryMessenger:self.binaryMessenger + codec:FWFNSObjectFlutterApiGetCodec()]; + [channel sendMessage:@[ + arg_identifier ?: [NSNull null], arg_keyPath ?: [NSNull null], + arg_objectIdentifier ?: [NSNull null], arg_changeKeys ?: [NSNull null], + arg_changeValues ?: [NSNull null] + ] + reply:^(id reply) { + completion(nil); + }]; +} @end @interface FWFWKWebViewHostApiCodecReader : FlutterStandardReader @end @@ -1399,27 +1882,45 @@ @implementation FWFWKWebViewHostApiCodecReader - (nullable id)readValueOfType:(UInt8)type { switch (type) { case 128: - return [FWFNSHttpCookieData fromMap:[self readValue]]; + return [FWFNSErrorData fromMap:[self readValue]]; case 129: - return [FWFNSHttpCookiePropertyKeyEnumData fromMap:[self readValue]]; + return [FWFNSHttpCookieData fromMap:[self readValue]]; case 130: - return [FWFNSKeyValueObservingOptionsEnumData fromMap:[self readValue]]; + return [FWFNSHttpCookiePropertyKeyEnumData fromMap:[self readValue]]; case 131: - return [FWFNSUrlRequestData fromMap:[self readValue]]; + return [FWFNSKeyValueChangeKeyEnumData fromMap:[self readValue]]; case 132: - return [FWFWKAudiovisualMediaTypeEnumData fromMap:[self readValue]]; + return [FWFNSKeyValueObservingOptionsEnumData fromMap:[self readValue]]; case 133: - return [FWFWKUserScriptData fromMap:[self readValue]]; + return [FWFNSUrlRequestData fromMap:[self readValue]]; case 134: - return [FWFWKUserScriptInjectionTimeEnumData fromMap:[self readValue]]; + return [FWFWKAudiovisualMediaTypeEnumData fromMap:[self readValue]]; case 135: + return [FWFWKFrameInfoData fromMap:[self readValue]]; + + case 136: + return [FWFWKNavigationActionData fromMap:[self readValue]]; + + case 137: + return [FWFWKNavigationActionPolicyEnumData fromMap:[self readValue]]; + + case 138: + return [FWFWKScriptMessageData fromMap:[self readValue]]; + + case 139: + return [FWFWKUserScriptData fromMap:[self readValue]]; + + case 140: + return [FWFWKUserScriptInjectionTimeEnumData fromMap:[self readValue]]; + + case 141: return [FWFWKWebsiteDataTypeEnumData fromMap:[self readValue]]; default: @@ -1432,30 +1933,48 @@ @interface FWFWKWebViewHostApiCodecWriter : FlutterStandardWriter @end @implementation FWFWKWebViewHostApiCodecWriter - (void)writeValue:(id)value { - if ([value isKindOfClass:[FWFNSHttpCookieData class]]) { + if ([value isKindOfClass:[FWFNSErrorData class]]) { [self writeByte:128]; [self writeValue:[value toMap]]; - } else if ([value isKindOfClass:[FWFNSHttpCookiePropertyKeyEnumData class]]) { + } else if ([value isKindOfClass:[FWFNSHttpCookieData class]]) { [self writeByte:129]; [self writeValue:[value toMap]]; - } else if ([value isKindOfClass:[FWFNSKeyValueObservingOptionsEnumData class]]) { + } else if ([value isKindOfClass:[FWFNSHttpCookiePropertyKeyEnumData class]]) { [self writeByte:130]; [self writeValue:[value toMap]]; - } else if ([value isKindOfClass:[FWFNSUrlRequestData class]]) { + } else if ([value isKindOfClass:[FWFNSKeyValueChangeKeyEnumData class]]) { [self writeByte:131]; [self writeValue:[value toMap]]; - } else if ([value isKindOfClass:[FWFWKAudiovisualMediaTypeEnumData class]]) { + } else if ([value isKindOfClass:[FWFNSKeyValueObservingOptionsEnumData class]]) { [self writeByte:132]; [self writeValue:[value toMap]]; - } else if ([value isKindOfClass:[FWFWKUserScriptData class]]) { + } else if ([value isKindOfClass:[FWFNSUrlRequestData class]]) { [self writeByte:133]; [self writeValue:[value toMap]]; - } else if ([value isKindOfClass:[FWFWKUserScriptInjectionTimeEnumData class]]) { + } else if ([value isKindOfClass:[FWFWKAudiovisualMediaTypeEnumData class]]) { [self writeByte:134]; [self writeValue:[value toMap]]; - } else if ([value isKindOfClass:[FWFWKWebsiteDataTypeEnumData class]]) { + } else if ([value isKindOfClass:[FWFWKFrameInfoData class]]) { [self writeByte:135]; [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKNavigationActionData class]]) { + [self writeByte:136]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKNavigationActionPolicyEnumData class]]) { + [self writeByte:137]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKScriptMessageData class]]) { + [self writeByte:138]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKUserScriptData class]]) { + [self writeByte:139]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKUserScriptInjectionTimeEnumData class]]) { + [self writeByte:140]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKWebsiteDataTypeEnumData class]]) { + [self writeByte:141]; + [self writeValue:[value toMap]]; } else { [super writeValue:value]; } @@ -1967,6 +2486,98 @@ void FWFWKUIDelegateHostApiSetup(id binaryMessenger, } } } +@interface FWFWKUIDelegateFlutterApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKUIDelegateFlutterApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FWFNSUrlRequestData fromMap:[self readValue]]; + + case 129: + return [FWFWKFrameInfoData fromMap:[self readValue]]; + + case 130: + return [FWFWKNavigationActionData fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} +@end + +@interface FWFWKUIDelegateFlutterApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKUIDelegateFlutterApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FWFNSUrlRequestData class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKFrameInfoData class]]) { + [self writeByte:129]; + [self writeValue:[value toMap]]; + } else if ([value isKindOfClass:[FWFWKNavigationActionData class]]) { + [self writeByte:130]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface FWFWKUIDelegateFlutterApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKUIDelegateFlutterApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKUIDelegateFlutterApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKUIDelegateFlutterApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKUIDelegateFlutterApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKUIDelegateFlutterApiCodecReaderWriter *readerWriter = + [[FWFWKUIDelegateFlutterApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +@interface FWFWKUIDelegateFlutterApi () +@property(nonatomic, strong) NSObject *binaryMessenger; +@end + +@implementation FWFWKUIDelegateFlutterApi + +- (instancetype)initWithBinaryMessenger:(NSObject *)binaryMessenger { + self = [super init]; + if (self) { + _binaryMessenger = binaryMessenger; + } + return self; +} +- (void)onCreateWebViewForDelegateWithIdentifier:(NSNumber *)arg_identifier + webViewIdentifier:(NSNumber *)arg_webViewIdentifier + configurationIdentifier:(NSNumber *)arg_configurationIdentifier + navigationAction:(FWFWKNavigationActionData *)arg_navigationAction + completion:(void (^)(NSError *_Nullable))completion { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.WKUIDelegateFlutterApi.onCreateWebView" + binaryMessenger:self.binaryMessenger + codec:FWFWKUIDelegateFlutterApiGetCodec()]; + [channel sendMessage:@[ + arg_identifier ?: [NSNull null], arg_webViewIdentifier ?: [NSNull null], + arg_configurationIdentifier ?: [NSNull null], arg_navigationAction ?: [NSNull null] + ] + reply:^(id reply) { + completion(nil); + }]; +} +@end @interface FWFWKHttpCookieStoreHostApiCodecReader : FlutterStandardReader @end @implementation FWFWKHttpCookieStoreHostApiCodecReader diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart index 9d928a38a2ef..c1971f09d068 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.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 (v3.1.2), do not edit directly. +// Autogenerated from Pigeon (v3.1.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, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name // @dart = 2.12 @@ -99,6 +99,27 @@ class NSKeyValueObservingOptionsEnumData { } } +class NSKeyValueChangeKeyEnumData { + NSKeyValueChangeKeyEnumData({ + required this.value, + }); + + NSKeyValueChangeKeyEnum value; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['value'] = value.index; + return pigeonMap; + } + + static NSKeyValueChangeKeyEnumData decode(Object message) { + final Map pigeonMap = message as Map; + return NSKeyValueChangeKeyEnumData( + value: NSKeyValueChangeKeyEnum.values[pigeonMap['value']! as int], + ); + } +} + class WKUserScriptInjectionTimeEnumData { WKUserScriptInjectionTimeEnumData({ required this.value, @@ -162,6 +183,27 @@ class WKWebsiteDataTypeEnumData { } } +class WKNavigationActionPolicyEnumData { + WKNavigationActionPolicyEnumData({ + required this.value, + }); + + WKNavigationActionPolicyEnum value; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['value'] = value.index; + return pigeonMap; + } + + static WKNavigationActionPolicyEnumData decode(Object message) { + final Map pigeonMap = message as Map; + return WKNavigationActionPolicyEnumData( + value: WKNavigationActionPolicyEnum.values[pigeonMap['value']! as int], + ); + } +} + class NSHttpCookiePropertyKeyEnumData { NSHttpCookiePropertyKeyEnumData({ required this.value, @@ -250,6 +292,106 @@ class WKUserScriptData { } } +class WKNavigationActionData { + WKNavigationActionData({ + required this.request, + required this.targetFrame, + }); + + NSUrlRequestData request; + WKFrameInfoData targetFrame; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['request'] = request.encode(); + pigeonMap['targetFrame'] = targetFrame.encode(); + return pigeonMap; + } + + static WKNavigationActionData decode(Object message) { + final Map pigeonMap = message as Map; + return WKNavigationActionData( + request: NSUrlRequestData.decode(pigeonMap['request']!), + targetFrame: WKFrameInfoData.decode(pigeonMap['targetFrame']!), + ); + } +} + +class WKFrameInfoData { + WKFrameInfoData({ + required this.isMainFrame, + }); + + bool isMainFrame; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['isMainFrame'] = isMainFrame; + return pigeonMap; + } + + static WKFrameInfoData decode(Object message) { + final Map pigeonMap = message as Map; + return WKFrameInfoData( + isMainFrame: pigeonMap['isMainFrame']! as bool, + ); + } +} + +class NSErrorData { + NSErrorData({ + required this.code, + required this.domain, + required this.localizedDescription, + }); + + int code; + String domain; + String localizedDescription; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['code'] = code; + pigeonMap['domain'] = domain; + pigeonMap['localizedDescription'] = localizedDescription; + return pigeonMap; + } + + static NSErrorData decode(Object message) { + final Map pigeonMap = message as Map; + return NSErrorData( + code: pigeonMap['code']! as int, + domain: pigeonMap['domain']! as String, + localizedDescription: pigeonMap['localizedDescription']! as String, + ); + } +} + +class WKScriptMessageData { + WKScriptMessageData({ + required this.name, + this.body, + }); + + String name; + Object? body; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['name'] = name; + pigeonMap['body'] = body; + return pigeonMap; + } + + static WKScriptMessageData decode(Object message) { + final Map pigeonMap = message as Map; + return WKScriptMessageData( + name: pigeonMap['name']! as String, + body: pigeonMap['body'] as Object?, + ); + } +} + class NSHttpCookieData { NSHttpCookieData({ required this.propertyKeys, @@ -1035,6 +1177,69 @@ class WKScriptMessageHandlerHostApi { } } +class _WKScriptMessageHandlerFlutterApiCodec extends StandardMessageCodec { + const _WKScriptMessageHandlerFlutterApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is WKScriptMessageData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return WKScriptMessageData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class WKScriptMessageHandlerFlutterApi { + static const MessageCodec codec = + _WKScriptMessageHandlerFlutterApiCodec(); + + void didReceiveScriptMessage(int identifier, + int userContentControllerIdentifier, WKScriptMessageData message); + static void setup(WKScriptMessageHandlerFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKScriptMessageHandlerFlutterApi.didReceiveScriptMessage', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKScriptMessageHandlerFlutterApi.didReceiveScriptMessage 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.WKScriptMessageHandlerFlutterApi.didReceiveScriptMessage was null, expected non-null int.'); + final int? arg_userContentControllerIdentifier = (args[1] as int?); + assert(arg_userContentControllerIdentifier != null, + 'Argument for dev.flutter.pigeon.WKScriptMessageHandlerFlutterApi.didReceiveScriptMessage was null, expected non-null int.'); + final WKScriptMessageData? arg_message = + (args[2] as WKScriptMessageData?); + assert(arg_message != null, + 'Argument for dev.flutter.pigeon.WKScriptMessageHandlerFlutterApi.didReceiveScriptMessage was null, expected non-null WKScriptMessageData.'); + api.didReceiveScriptMessage(arg_identifier!, + arg_userContentControllerIdentifier!, arg_message!); + return; + }); + } + } + } +} + class _WKNavigationDelegateHostApiCodec extends StandardMessageCodec { const _WKNavigationDelegateHostApiCodec(); } @@ -1078,6 +1283,50 @@ class WKNavigationDelegateHostApi { class _WKNavigationDelegateFlutterApiCodec extends StandardMessageCodec { const _WKNavigationDelegateFlutterApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is NSErrorData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is NSUrlRequestData) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is WKFrameInfoData) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is WKNavigationActionData) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is WKNavigationActionPolicyEnumData) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return NSErrorData.decode(readValue(buffer)!); + + case 129: + return NSUrlRequestData.decode(readValue(buffer)!); + + case 130: + return WKFrameInfoData.decode(readValue(buffer)!); + + case 131: + return WKNavigationActionData.decode(readValue(buffer)!); + + case 132: + return WKNavigationActionPolicyEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } } abstract class WKNavigationDelegateFlutterApi { @@ -1085,6 +1334,18 @@ abstract class WKNavigationDelegateFlutterApi { _WKNavigationDelegateFlutterApiCodec(); void didFinishNavigation(int identifier, int webViewIdentifier, String? url); + void didStartProvisionalNavigation( + int identifier, int webViewIdentifier, String? url); + Future decidePolicyForNavigationAction( + int identifier, + int webViewIdentifier, + WKNavigationActionData navigationAction); + void didFailNavigation( + int identifier, int webViewIdentifier, NSErrorData error); + void didFailProvisionalNavigation( + int identifier, int webViewIdentifier, NSErrorData error); + void webViewWebContentProcessDidTerminate( + int identifier, int webViewIdentifier); static void setup(WKNavigationDelegateFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1112,6 +1373,138 @@ abstract class WKNavigationDelegateFlutterApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didStartProvisionalNavigation', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didStartProvisionalNavigation 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.WKNavigationDelegateFlutterApi.didStartProvisionalNavigation was null, expected non-null int.'); + final int? arg_webViewIdentifier = (args[1] as int?); + assert(arg_webViewIdentifier != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didStartProvisionalNavigation was null, expected non-null int.'); + final String? arg_url = (args[2] as String?); + api.didStartProvisionalNavigation( + arg_identifier!, arg_webViewIdentifier!, arg_url); + return; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKNavigationDelegateFlutterApi.decidePolicyForNavigationAction', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.decidePolicyForNavigationAction 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.WKNavigationDelegateFlutterApi.decidePolicyForNavigationAction was null, expected non-null int.'); + final int? arg_webViewIdentifier = (args[1] as int?); + assert(arg_webViewIdentifier != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.decidePolicyForNavigationAction was null, expected non-null int.'); + final WKNavigationActionData? arg_navigationAction = + (args[2] as WKNavigationActionData?); + assert(arg_navigationAction != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.decidePolicyForNavigationAction was null, expected non-null WKNavigationActionData.'); + final WKNavigationActionPolicyEnumData output = + await api.decidePolicyForNavigationAction(arg_identifier!, + arg_webViewIdentifier!, arg_navigationAction!); + return output; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFailNavigation', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFailNavigation 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.WKNavigationDelegateFlutterApi.didFailNavigation was null, expected non-null int.'); + final int? arg_webViewIdentifier = (args[1] as int?); + assert(arg_webViewIdentifier != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFailNavigation was null, expected non-null int.'); + final NSErrorData? arg_error = (args[2] as NSErrorData?); + assert(arg_error != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFailNavigation was null, expected non-null NSErrorData.'); + api.didFailNavigation( + arg_identifier!, arg_webViewIdentifier!, arg_error!); + return; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFailProvisionalNavigation', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFailProvisionalNavigation 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.WKNavigationDelegateFlutterApi.didFailProvisionalNavigation was null, expected non-null int.'); + final int? arg_webViewIdentifier = (args[1] as int?); + assert(arg_webViewIdentifier != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFailProvisionalNavigation was null, expected non-null int.'); + final NSErrorData? arg_error = (args[2] as NSErrorData?); + assert(arg_error != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.didFailProvisionalNavigation was null, expected non-null NSErrorData.'); + api.didFailProvisionalNavigation( + arg_identifier!, arg_webViewIdentifier!, arg_error!); + return; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKNavigationDelegateFlutterApi.webViewWebContentProcessDidTerminate', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.webViewWebContentProcessDidTerminate 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.WKNavigationDelegateFlutterApi.webViewWebContentProcessDidTerminate was null, expected non-null int.'); + final int? arg_webViewIdentifier = (args[1] as int?); + assert(arg_webViewIdentifier != null, + 'Argument for dev.flutter.pigeon.WKNavigationDelegateFlutterApi.webViewWebContentProcessDidTerminate was null, expected non-null int.'); + api.webViewWebContentProcessDidTerminate( + arg_identifier!, arg_webViewIdentifier!); + return; + }); + } + } } } @@ -1235,43 +1628,200 @@ class NSObjectHostApi { class _NSObjectFlutterApiCodec extends StandardMessageCodec { const _NSObjectFlutterApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is NSErrorData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is NSHttpCookieData) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is NSHttpCookiePropertyKeyEnumData) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is NSKeyValueChangeKeyEnumData) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is NSKeyValueObservingOptionsEnumData) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else if (value is NSUrlRequestData) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); + } else if (value is WKAudiovisualMediaTypeEnumData) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else if (value is WKFrameInfoData) { + buffer.putUint8(135); + writeValue(buffer, value.encode()); + } else if (value is WKNavigationActionData) { + buffer.putUint8(136); + writeValue(buffer, value.encode()); + } else if (value is WKNavigationActionPolicyEnumData) { + buffer.putUint8(137); + writeValue(buffer, value.encode()); + } else if (value is WKScriptMessageData) { + buffer.putUint8(138); + writeValue(buffer, value.encode()); + } else if (value is WKUserScriptData) { + buffer.putUint8(139); + writeValue(buffer, value.encode()); + } else if (value is WKUserScriptInjectionTimeEnumData) { + buffer.putUint8(140); + writeValue(buffer, value.encode()); + } else if (value is WKWebsiteDataTypeEnumData) { + buffer.putUint8(141); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return NSErrorData.decode(readValue(buffer)!); + + case 129: + return NSHttpCookieData.decode(readValue(buffer)!); + + case 130: + return NSHttpCookiePropertyKeyEnumData.decode(readValue(buffer)!); + + case 131: + return NSKeyValueChangeKeyEnumData.decode(readValue(buffer)!); + + case 132: + return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!); + + case 133: + return NSUrlRequestData.decode(readValue(buffer)!); + + case 134: + return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!); + + case 135: + return WKFrameInfoData.decode(readValue(buffer)!); + + case 136: + return WKNavigationActionData.decode(readValue(buffer)!); + + case 137: + return WKNavigationActionPolicyEnumData.decode(readValue(buffer)!); + + case 138: + return WKScriptMessageData.decode(readValue(buffer)!); + + case 139: + return WKUserScriptData.decode(readValue(buffer)!); + + case 140: + return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); + + case 141: + return WKWebsiteDataTypeEnumData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } } abstract class NSObjectFlutterApi { static const MessageCodec codec = _NSObjectFlutterApiCodec(); + void observeValue( + int identifier, + String keyPath, + int objectIdentifier, + List changeKeys, + List changeValues); static void setup(NSObjectFlutterApi? api, - {BinaryMessenger? binaryMessenger}) {} + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NSObjectFlutterApi.observeValue', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NSObjectFlutterApi.observeValue 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.NSObjectFlutterApi.observeValue was null, expected non-null int.'); + final String? arg_keyPath = (args[1] as String?); + assert(arg_keyPath != null, + 'Argument for dev.flutter.pigeon.NSObjectFlutterApi.observeValue was null, expected non-null String.'); + final int? arg_objectIdentifier = (args[2] as int?); + assert(arg_objectIdentifier != null, + 'Argument for dev.flutter.pigeon.NSObjectFlutterApi.observeValue was null, expected non-null int.'); + final List? arg_changeKeys = + (args[3] as List?)?.cast(); + assert(arg_changeKeys != null, + 'Argument for dev.flutter.pigeon.NSObjectFlutterApi.observeValue was null, expected non-null List.'); + final List? arg_changeValues = + (args[4] as List?)?.cast(); + assert(arg_changeValues != null, + 'Argument for dev.flutter.pigeon.NSObjectFlutterApi.observeValue was null, expected non-null List.'); + api.observeValue(arg_identifier!, arg_keyPath!, arg_objectIdentifier!, + arg_changeKeys!, arg_changeValues!); + return; + }); + } + } + } } class _WKWebViewHostApiCodec extends StandardMessageCodec { const _WKWebViewHostApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is NSHttpCookieData) { + if (value is NSErrorData) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else if (value is NSHttpCookiePropertyKeyEnumData) { + } else if (value is NSHttpCookieData) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is NSKeyValueObservingOptionsEnumData) { + } else if (value is NSHttpCookiePropertyKeyEnumData) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is NSUrlRequestData) { + } else if (value is NSKeyValueChangeKeyEnumData) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is WKAudiovisualMediaTypeEnumData) { + } else if (value is NSKeyValueObservingOptionsEnumData) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is WKUserScriptData) { + } else if (value is NSUrlRequestData) { buffer.putUint8(133); writeValue(buffer, value.encode()); - } else if (value is WKUserScriptInjectionTimeEnumData) { + } else if (value is WKAudiovisualMediaTypeEnumData) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is WKWebsiteDataTypeEnumData) { + } else if (value is WKFrameInfoData) { buffer.putUint8(135); writeValue(buffer, value.encode()); + } else if (value is WKNavigationActionData) { + buffer.putUint8(136); + writeValue(buffer, value.encode()); + } else if (value is WKNavigationActionPolicyEnumData) { + buffer.putUint8(137); + writeValue(buffer, value.encode()); + } else if (value is WKScriptMessageData) { + buffer.putUint8(138); + writeValue(buffer, value.encode()); + } else if (value is WKUserScriptData) { + buffer.putUint8(139); + writeValue(buffer, value.encode()); + } else if (value is WKUserScriptInjectionTimeEnumData) { + buffer.putUint8(140); + writeValue(buffer, value.encode()); + } else if (value is WKWebsiteDataTypeEnumData) { + buffer.putUint8(141); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -1281,27 +1831,45 @@ class _WKWebViewHostApiCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 128: - return NSHttpCookieData.decode(readValue(buffer)!); + return NSErrorData.decode(readValue(buffer)!); case 129: - return NSHttpCookiePropertyKeyEnumData.decode(readValue(buffer)!); + return NSHttpCookieData.decode(readValue(buffer)!); case 130: - return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!); + return NSHttpCookiePropertyKeyEnumData.decode(readValue(buffer)!); case 131: - return NSUrlRequestData.decode(readValue(buffer)!); + return NSKeyValueChangeKeyEnumData.decode(readValue(buffer)!); case 132: - return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!); + return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!); case 133: - return WKUserScriptData.decode(readValue(buffer)!); + return NSUrlRequestData.decode(readValue(buffer)!); case 134: - return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); + return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!); case 135: + return WKFrameInfoData.decode(readValue(buffer)!); + + case 136: + return WKNavigationActionData.decode(readValue(buffer)!); + + case 137: + return WKNavigationActionPolicyEnumData.decode(readValue(buffer)!); + + case 138: + return WKScriptMessageData.decode(readValue(buffer)!); + + case 139: + return WKUserScriptData.decode(readValue(buffer)!); + + case 140: + return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); + + case 141: return WKWebsiteDataTypeEnumData.decode(readValue(buffer)!); default: @@ -1826,6 +2394,82 @@ class WKUIDelegateHostApi { } } +class _WKUIDelegateFlutterApiCodec extends StandardMessageCodec { + const _WKUIDelegateFlutterApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is NSUrlRequestData) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is WKFrameInfoData) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is WKNavigationActionData) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return NSUrlRequestData.decode(readValue(buffer)!); + + case 129: + return WKFrameInfoData.decode(readValue(buffer)!); + + case 130: + return WKNavigationActionData.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class WKUIDelegateFlutterApi { + static const MessageCodec codec = _WKUIDelegateFlutterApiCodec(); + + void onCreateWebView(int identifier, int webViewIdentifier, + int configurationIdentifier, WKNavigationActionData navigationAction); + static void setup(WKUIDelegateFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKUIDelegateFlutterApi.onCreateWebView', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKUIDelegateFlutterApi.onCreateWebView 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.WKUIDelegateFlutterApi.onCreateWebView was null, expected non-null int.'); + final int? arg_webViewIdentifier = (args[1] as int?); + assert(arg_webViewIdentifier != null, + 'Argument for dev.flutter.pigeon.WKUIDelegateFlutterApi.onCreateWebView was null, expected non-null int.'); + final int? arg_configurationIdentifier = (args[2] as int?); + assert(arg_configurationIdentifier != null, + 'Argument for dev.flutter.pigeon.WKUIDelegateFlutterApi.onCreateWebView was null, expected non-null int.'); + final WKNavigationActionData? arg_navigationAction = + (args[3] as WKNavigationActionData?); + assert(arg_navigationAction != null, + 'Argument for dev.flutter.pigeon.WKUIDelegateFlutterApi.onCreateWebView was null, expected non-null WKNavigationActionData.'); + api.onCreateWebView(arg_identifier!, arg_webViewIdentifier!, + arg_configurationIdentifier!, arg_navigationAction!); + return; + }); + } + } + } +} + class _WKHttpCookieStoreHostApiCodec extends StandardMessageCodec { const _WKHttpCookieStoreHostApiCodec(); @override diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart index 9575cb51c648..f9efe3616ca7 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart @@ -36,17 +36,25 @@ Iterable }); } +extension _NSKeyValueChangeKeyEnumDataConverter on NSKeyValueChangeKeyEnumData { + NSKeyValueChangeKey toNSKeyValueChangeKey() { + return NSKeyValueChangeKey.values.firstWhere( + (NSKeyValueChangeKey element) => element.name == value.name, + ); + } +} + /// Handles initialization of Flutter APIs for the Foundation library. -// TODO(bparrishMines): Add NSObjectFlutterApiImpl once the callback methods -// are added. class FoundationFlutterApis { /// Constructs a [FoundationFlutterApis]. @visibleForTesting FoundationFlutterApis({ BinaryMessenger? binaryMessenger, - // ignore: avoid_unused_constructor_parameters InstanceManager? instanceManager, - }) : _binaryMessenger = binaryMessenger; + }) : _binaryMessenger = binaryMessenger, + object = NSObjectFlutterApiImpl( + instanceManager: instanceManager, + ); static FoundationFlutterApis _instance = FoundationFlutterApis(); @@ -61,13 +69,20 @@ class FoundationFlutterApis { return _instance; } - // ignore: unused_field final BinaryMessenger? _binaryMessenger; bool _hasBeenSetUp = false; + /// Flutter Api for [NSObject]. + @visibleForTesting + final NSObjectFlutterApiImpl object; + /// Ensures all the Flutter APIs have been set up to receive calls from native code. void ensureSetUp() { if (!_hasBeenSetUp) { + NSObjectFlutterApi.setup( + object, + binaryMessenger: _binaryMessenger, + ); _hasBeenSetUp = true; } } @@ -132,3 +147,40 @@ class NSObjectHostApiImpl extends NSObjectHostApi { instanceManager == other.instanceManager; } } + +/// Flutter api implementation for [NSObject]. +class NSObjectFlutterApiImpl extends NSObjectFlutterApi { + /// Constructs a [NSObjectFlutterApiImpl]. + NSObjectFlutterApiImpl({InstanceManager? instanceManager}) + : instanceManager = instanceManager ?? NSObject.globalInstanceManager; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; + + NSObject _getObject(int identifier) { + return instanceManager.getInstanceWithWeakReference(identifier)!; + } + + @override + void observeValue( + int identifier, + String keyPath, + int objectIdentifier, + List changeKeys, + List changeValues, + ) { + final void Function(String, NSObject, Map)? + function = _getObject(identifier).observeValue; + function?.call( + keyPath, + instanceManager.getInstanceWithWeakReference(objectIdentifier)! + as NSObject, + Map.fromIterables( + changeKeys.map( + (NSKeyValueChangeKeyEnumData? data) { + return data!.toNSKeyValueChangeKey(); + }, + ), changeValues), + ); + } +} 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 f2efd665298b..b2945293e624 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 @@ -400,6 +400,19 @@ class WKUserContentController extends NSObject { ); } + /// Constructs a [WKUserContentController] without creating the associated + /// Objective-C object. + /// + /// This should only be used outside of tests by subclasses created by this + /// library or to create a copy for an InstanceManager. + WKUserContentController.detached({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _userContentControllerApi = WKUserContentControllerHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + final WKUserContentControllerHostApiImpl _userContentControllerApi; /// Installs a message handler that you can call from your JavaScript code. @@ -472,7 +485,8 @@ class WKWebViewConfiguration extends NSObject { BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, }) { - final WKWebViewConfiguration configuration = WKWebViewConfiguration._( + final WKWebViewConfiguration configuration = + WKWebViewConfiguration.detached( binaryMessenger: binaryMessenger, instanceManager: instanceManager, ); @@ -487,7 +501,8 @@ class WKWebViewConfiguration extends NSObject { BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, }) { - final WKWebViewConfiguration configuration = WKWebViewConfiguration._( + final WKWebViewConfiguration configuration = + WKWebViewConfiguration.detached( binaryMessenger: binaryMessenger, instanceManager: instanceManager, ); @@ -498,7 +513,12 @@ class WKWebViewConfiguration extends NSObject { return configuration; } - WKWebViewConfiguration._({ + /// Constructs a [WKWebViewConfiguration] without creating the associated + /// Objective-C object. + /// + /// This should only be used outside of tests by subclasses created by this + /// library or to create a copy for an InstanceManager. + WKWebViewConfiguration.detached({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, }) : _binaryMessenger = binaryMessenger, @@ -584,6 +604,7 @@ class WKUIDelegate extends NSObject { /// Indicates a new [WKWebView] was requested to be created with [configuration]. final void Function( + WKWebView webView, WKWebViewConfiguration configuration, WKNavigationAction navigationAction, )? onCreateWebView; @@ -720,6 +741,22 @@ class WKWebView extends UIView { _webViewApi.createForInstances(this, configuration); } + /// Constructs a [WKWebView] without creating the associated + /// Objective-C object. + /// + /// This should only be used outside of tests by subclasses created by this + /// library or to create a copy for an InstanceManager. + WKWebView.detached({ + super.observeValue, + super.binaryMessenger, + super.instanceManager, + }) : _binaryMessenger = binaryMessenger, + _instanceManager = instanceManager, + _webViewApi = WKWebViewHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + final BinaryMessenger? _binaryMessenger; final InstanceManager? _instanceManager; 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 85b9b492eb87..c27fc3e73aee 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 @@ -61,6 +61,16 @@ extension _NSHttpCookieConverter on NSHttpCookie { } } +extension _WKNavigationActionPolicyConverter on WKNavigationActionPolicy { + WKNavigationActionPolicyEnumData toWKNavigationActionPolicyEnumData() { + return WKNavigationActionPolicyEnumData( + value: WKNavigationActionPolicyEnum.values.firstWhere( + (WKNavigationActionPolicyEnum element) => element.name == name, + ), + ); + } +} + extension _NSHttpCookiePropertyKeyConverter on NSHttpCookiePropertyKey { NSHttpCookiePropertyKeyEnumData toNSHttpCookiePropertyKeyEnumData() { late final NSHttpCookiePropertyKeyEnum value; @@ -154,6 +164,48 @@ Iterable _toWKAudiovisualMediaTypeEnumData( }); } +extension _NavigationActionDataConverter on WKNavigationActionData { + WKNavigationAction toNavigationAction() { + return WKNavigationAction( + request: request.toNSUrlRequest(), + targetFrame: targetFrame.toWKFrameInfo(), + ); + } +} + +extension _WKFrameInfoDataConverter on WKFrameInfoData { + WKFrameInfo toWKFrameInfo() { + return WKFrameInfo(isMainFrame: isMainFrame); + } +} + +extension _NSUrlRequestDataConverter on NSUrlRequestData { + NSUrlRequest toNSUrlRequest() { + return NSUrlRequest( + url: url, + httpBody: httpBody, + httpMethod: httpMethod, + allHttpHeaderFields: allHttpHeaderFields.cast(), + ); + } +} + +extension _WKNSErrorDataConverter on NSErrorData { + NSError toNSError() { + return NSError( + domain: domain, + code: code, + localizedDescription: localizedDescription, + ); + } +} + +extension _WKScriptMessageDataConverter on WKScriptMessageData { + WKScriptMessage toWKScriptMessage() { + return WKScriptMessage(name: name, body: body); + } +} + extension _WKUserScriptConverter on WKUserScript { WKUserScriptData toWKUserScriptData() { return WKUserScriptData( @@ -182,11 +234,15 @@ class WebKitFlutterApis { WebKitFlutterApis({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : _binaryMessenger = binaryMessenger { - navigationDelegateFlutterApi = WKNavigationDelegateFlutterApiImpl( - instanceManager: instanceManager, - ); - } + }) : _binaryMessenger = binaryMessenger, + navigationDelegate = WKNavigationDelegateFlutterApiImpl( + instanceManager: instanceManager, + ), + scriptMessageHandler = WKScriptMessageHandlerFlutterApiImpl( + instanceManager: instanceManager), + uiDelegate = WKUIDelegateFlutterApiImpl( + instanceManager: instanceManager, + ); static WebKitFlutterApis _instance = WebKitFlutterApis(); @@ -206,13 +262,29 @@ class WebKitFlutterApis { /// Flutter Api for [WKNavigationDelegate]. @visibleForTesting - late final WKNavigationDelegateFlutterApiImpl navigationDelegateFlutterApi; + final WKNavigationDelegateFlutterApiImpl navigationDelegate; + + /// Flutter Api for [WKScriptMessageHandler]. + @visibleForTesting + final WKScriptMessageHandlerFlutterApiImpl scriptMessageHandler; + + /// Flutter Api for [WKUIDelegate]. + @visibleForTesting + final WKUIDelegateFlutterApiImpl uiDelegate; /// Ensures all the Flutter APIs have been set up to receive calls from native code. void ensureSetUp() { if (!_hasBeenSetUp) { WKNavigationDelegateFlutterApi.setup( - navigationDelegateFlutterApi, + navigationDelegate, + binaryMessenger: _binaryMessenger, + ); + WKScriptMessageHandlerFlutterApi.setup( + scriptMessageHandler, + binaryMessenger: _binaryMessenger, + ); + WKUIDelegateFlutterApi.setup( + uiDelegate, binaryMessenger: _binaryMessenger, ); _hasBeenSetUp = true; @@ -282,6 +354,35 @@ class WKScriptMessageHandlerHostApiImpl extends WKScriptMessageHandlerHostApi { } } +/// Flutter api implementation for [WKScriptMessageHandler]. +class WKScriptMessageHandlerFlutterApiImpl + extends WKScriptMessageHandlerFlutterApi { + /// Constructs a [WKScriptMessageHandlerFlutterApiImpl]. + WKScriptMessageHandlerFlutterApiImpl({InstanceManager? instanceManager}) + : instanceManager = instanceManager ?? NSObject.globalInstanceManager; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; + + WKScriptMessageHandler _getHandler(int identifier) { + return instanceManager.getInstanceWithWeakReference(identifier)!; + } + + @override + void didReceiveScriptMessage( + int identifier, + int userContentControllerIdentifier, + WKScriptMessageData message, + ) { + _getHandler(identifier).didReceiveScriptMessage( + instanceManager.getInstanceWithWeakReference( + userContentControllerIdentifier, + )! as WKUserContentController, + message.toWKScriptMessage(), + ); + } +} + /// Host api implementation for [WKPreferences]. class WKPreferencesHostApiImpl extends WKPreferencesHostApi { /// Constructs a [WKPreferencesHostApiImpl]. @@ -492,6 +593,38 @@ class WKUIDelegateHostApiImpl extends WKUIDelegateHostApi { } } +/// Flutter api implementation for [WKUIDelegate]. +class WKUIDelegateFlutterApiImpl extends WKUIDelegateFlutterApi { + /// Constructs a [WKUIDelegateFlutterApiImpl]. + WKUIDelegateFlutterApiImpl({InstanceManager? instanceManager}) + : instanceManager = instanceManager ?? NSObject.globalInstanceManager; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; + + WKUIDelegate _getDelegate(int identifier) { + return instanceManager.getInstanceWithWeakReference(identifier)!; + } + + @override + void onCreateWebView( + int identifier, + int webViewIdentifier, + int configurationIdentifier, + WKNavigationActionData navigationAction, + ) { + final void Function(WKWebView, WKWebViewConfiguration, WKNavigationAction)? + function = _getDelegate(identifier).onCreateWebView; + function?.call( + instanceManager.getInstanceWithWeakReference(webViewIdentifier)! + as WKWebView, + instanceManager.getInstanceWithWeakReference(configurationIdentifier)! + as WKWebViewConfiguration, + navigationAction.toNavigationAction(), + ); + } +} + /// Host api implementation for [WKNavigationDelegate]. @immutable class WKNavigationDelegateHostApiImpl extends WKNavigationDelegateHostApi { @@ -533,12 +666,11 @@ class WKNavigationDelegateHostApiImpl extends WKNavigationDelegateHostApi { class WKNavigationDelegateFlutterApiImpl extends WKNavigationDelegateFlutterApi { /// Constructs a [WKNavigationDelegateFlutterApiImpl]. - WKNavigationDelegateFlutterApiImpl({InstanceManager? instanceManager}) { - this.instanceManager = instanceManager ?? NSObject.globalInstanceManager; - } + WKNavigationDelegateFlutterApiImpl({InstanceManager? instanceManager}) + : instanceManager = instanceManager ?? NSObject.globalInstanceManager; /// Maintains instances stored to communicate with native language objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; WKNavigationDelegate _getDelegate(int identifier) { return instanceManager.getInstanceWithWeakReference(identifier)!; @@ -558,6 +690,89 @@ class WKNavigationDelegateFlutterApiImpl url, ); } + + @override + Future decidePolicyForNavigationAction( + int identifier, + int webViewIdentifier, + WKNavigationActionData navigationAction, + ) async { + final Future Function( + WKWebView, + WKNavigationAction navigationAction, + )? function = _getDelegate(identifier).decidePolicyForNavigationAction; + + if (function == null) { + return WKNavigationActionPolicyEnumData( + value: WKNavigationActionPolicyEnum.allow, + ); + } + + final WKNavigationActionPolicy policy = await function( + instanceManager.getInstanceWithWeakReference(webViewIdentifier)! + as WKWebView, + navigationAction.toNavigationAction(), + ); + return policy.toWKNavigationActionPolicyEnumData(); + } + + @override + void didFailNavigation( + int identifier, + int webViewIdentifier, + NSErrorData error, + ) { + final void Function(WKWebView, NSError)? function = + _getDelegate(identifier).didFailNavigation; + function?.call( + instanceManager.getInstanceWithWeakReference(webViewIdentifier)! + as WKWebView, + error.toNSError(), + ); + } + + @override + void didFailProvisionalNavigation( + int identifier, + int webViewIdentifier, + NSErrorData error, + ) { + final void Function(WKWebView, NSError)? function = + _getDelegate(identifier).didFailProvisionalNavigation; + function?.call( + instanceManager.getInstanceWithWeakReference(webViewIdentifier)! + as WKWebView, + error.toNSError(), + ); + } + + @override + void didStartProvisionalNavigation( + int identifier, + int webViewIdentifier, + String? url, + ) { + final void Function(WKWebView, String?)? function = + _getDelegate(identifier).didStartProvisionalNavigation; + function?.call( + instanceManager.getInstanceWithWeakReference(webViewIdentifier)! + as WKWebView, + url, + ); + } + + @override + void webViewWebContentProcessDidTerminate( + int identifier, + int webViewIdentifier, + ) { + final void Function(WKWebView)? function = + _getDelegate(identifier).webViewWebContentProcessDidTerminate; + function?.call( + instanceManager.getInstanceWithWeakReference(webViewIdentifier)! + as WKWebView, + ); + } } /// Host api implementation for [WKWebView]. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 1cfbc6edb458..05a77f56f851 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -115,6 +115,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { @visibleForTesting late final WKUIDelegate uiDelegate = webViewProxy.createUIDelgate(onCreateWebView: ( + WKWebView webView, WKWebViewConfiguration configuration, WKNavigationAction navigationAction, ) { @@ -631,6 +632,7 @@ class WebViewWidgetProxy { /// Constructs a [WKUIDelegate]. WKUIDelegate createUIDelgate({ void Function( + WKWebView webView, WKWebViewConfiguration configuration, WKNavigationAction navigationAction, )? 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 91541e8e741b..e46dafa01a62 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -206,7 +206,7 @@ class WKFrameInfoData { class NSErrorData { late int code; late String domain; - late String localiziedDescription; + late String localizedDescription; } /// Mirror of WKScriptMessage. @@ -373,6 +373,21 @@ abstract class WKScriptMessageHandlerHostApi { void create(int identifier); } +/// Handles callbacks from an WKScriptMessageHandler instance. +/// +/// See https://developer.apple.com/documentation/webkit/wkscriptmessagehandler?language=objc. +@FlutterApi() +abstract class WKScriptMessageHandlerFlutterApi { + @ObjCSelector( + 'didReceiveScriptMessageForHandlerWithIdentifier:userContentControllerIdentifier:message:', + ) + void didReceiveScriptMessage( + int identifier, + int userContentControllerIdentifier, + WKScriptMessageData message, + ); +} + /// Mirror of WKNavigationDelegate. /// /// See https://developer.apple.com/documentation/webkit/wknavigationdelegate?language=objc. @@ -395,6 +410,51 @@ abstract class WKNavigationDelegateFlutterApi { int webViewIdentifier, String? url, ); + + @ObjCSelector( + 'didStartProvisionalNavigationForDelegateWithIdentifier:webViewIdentifier:URL:', + ) + void didStartProvisionalNavigation( + int identifier, + int webViewIdentifier, + String? url, + ); + + @ObjCSelector( + 'decidePolicyForNavigationActionForDelegateWithIdentifier:webViewIdentifier:navigationAction:', + ) + @async + WKNavigationActionPolicyEnumData decidePolicyForNavigationAction( + int identifier, + int webViewIdentifier, + WKNavigationActionData navigationAction, + ); + + @ObjCSelector( + 'didFailNavigationForDelegateWithIdentifier:webViewIdentifier:error:', + ) + void didFailNavigation( + int identifier, + int webViewIdentifier, + NSErrorData error, + ); + + @ObjCSelector( + 'didFailProvisionalNavigationForDelegateWithIdentifier:webViewIdentifier:error:', + ) + void didFailProvisionalNavigation( + int identifier, + int webViewIdentifier, + NSErrorData error, + ); + + @ObjCSelector( + 'webViewWebContentProcessDidTerminateForDelegateWithIdentifier:webViewIdentifier:', + ) + void webViewWebContentProcessDidTerminate( + int identifier, + int webViewIdentifier, + ); } /// Mirror of NSObject. @@ -425,7 +485,23 @@ abstract class NSObjectHostApi { /// /// See https://developer.apple.com/documentation/objectivec/nsobject. @FlutterApi() -abstract class NSObjectFlutterApi {} +abstract class NSObjectFlutterApi { + @ObjCSelector( + 'observeValueForObjectWithIdentifier:keyPath:objectIdentifier:changeKeys:changeValues:', + ) + void observeValue( + int identifier, + String keyPath, + int objectIdentifier, + // TODO(bparrishMines): Change to a map when Objective-C data classes conform + // to `NSCopying`. See https://github.com/flutter/flutter/issues/103383. + // `NSDictionary`s are unable to use data classes as keys because they don't + // conform to `NSCopying`. This splits the map of properties into a list of + // keys and values with the ordered maintained. + List changeKeys, + List changeValues, + ); +} /// Mirror of WKWebView. /// @@ -499,6 +575,22 @@ abstract class WKUIDelegateHostApi { void create(int identifier); } +/// Handles callbacks from an WKUIDelegate instance. +/// +/// See https://developer.apple.com/documentation/webkit/wkuidelegate?language=objc. +@FlutterApi() +abstract class WKUIDelegateFlutterApi { + @ObjCSelector( + 'onCreateWebViewForDelegateWithIdentifier:webViewIdentifier:configurationIdentifier:navigationAction:', + ) + void onCreateWebView( + int identifier, + int webViewIdentifier, + int configurationIdentifier, + WKNavigationActionData navigationAction, + ); +} + /// Mirror of WKHttpCookieStore. /// /// See https://developer.apple.com/documentation/webkit/wkhttpcookiestore?language=objc. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart index 00865d66f2bc..5bf50e2b9850 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.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 (v3.1.2), do not edit directly. +// Autogenerated from Pigeon (v3.1.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 // ignore_for_file: avoid_relative_lib_imports @@ -847,30 +847,48 @@ class _TestWKWebViewHostApiCodec extends StandardMessageCodec { const _TestWKWebViewHostApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is NSHttpCookieData) { + if (value is NSErrorData) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else if (value is NSHttpCookiePropertyKeyEnumData) { + } else if (value is NSHttpCookieData) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is NSKeyValueObservingOptionsEnumData) { + } else if (value is NSHttpCookiePropertyKeyEnumData) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is NSUrlRequestData) { + } else if (value is NSKeyValueChangeKeyEnumData) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is WKAudiovisualMediaTypeEnumData) { + } else if (value is NSKeyValueObservingOptionsEnumData) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is WKUserScriptData) { + } else if (value is NSUrlRequestData) { buffer.putUint8(133); writeValue(buffer, value.encode()); - } else if (value is WKUserScriptInjectionTimeEnumData) { + } else if (value is WKAudiovisualMediaTypeEnumData) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is WKWebsiteDataTypeEnumData) { + } else if (value is WKFrameInfoData) { buffer.putUint8(135); writeValue(buffer, value.encode()); + } else if (value is WKNavigationActionData) { + buffer.putUint8(136); + writeValue(buffer, value.encode()); + } else if (value is WKNavigationActionPolicyEnumData) { + buffer.putUint8(137); + writeValue(buffer, value.encode()); + } else if (value is WKScriptMessageData) { + buffer.putUint8(138); + writeValue(buffer, value.encode()); + } else if (value is WKUserScriptData) { + buffer.putUint8(139); + writeValue(buffer, value.encode()); + } else if (value is WKUserScriptInjectionTimeEnumData) { + buffer.putUint8(140); + writeValue(buffer, value.encode()); + } else if (value is WKWebsiteDataTypeEnumData) { + buffer.putUint8(141); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -880,27 +898,45 @@ class _TestWKWebViewHostApiCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 128: - return NSHttpCookieData.decode(readValue(buffer)!); + return NSErrorData.decode(readValue(buffer)!); case 129: - return NSHttpCookiePropertyKeyEnumData.decode(readValue(buffer)!); + return NSHttpCookieData.decode(readValue(buffer)!); case 130: - return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!); + return NSHttpCookiePropertyKeyEnumData.decode(readValue(buffer)!); case 131: - return NSUrlRequestData.decode(readValue(buffer)!); + return NSKeyValueChangeKeyEnumData.decode(readValue(buffer)!); case 132: - return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!); + return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!); case 133: - return WKUserScriptData.decode(readValue(buffer)!); + return NSUrlRequestData.decode(readValue(buffer)!); case 134: - return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); + return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!); case 135: + return WKFrameInfoData.decode(readValue(buffer)!); + + case 136: + return WKNavigationActionData.decode(readValue(buffer)!); + + case 137: + return WKNavigationActionPolicyEnumData.decode(readValue(buffer)!); + + case 138: + return WKScriptMessageData.decode(readValue(buffer)!); + + case 139: + return WKUserScriptData.decode(readValue(buffer)!); + + case 140: + return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!); + + case 141: return WKWebsiteDataTypeEnumData.decode(readValue(buffer)!); default: diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart index ca84369c9789..8d15d65f3598 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart @@ -2,12 +2,15 @@ // 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_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:webview_flutter_wkwebview/src/common/instance_manager.dart'; import 'package:webview_flutter_wkwebview/src/common/web_kit.pigeon.dart'; import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; +import 'package:webview_flutter_wkwebview/src/foundation/foundation_api_impls.dart'; import '../common/test_web_kit.pigeon.dart'; import 'foundation_test.mocks.dart'; @@ -100,6 +103,48 @@ void main() { NSObject.dispose(object); expect(callbackIdentifier, identifier); }); + + test('observeValue', () async { + final Completer> argsCompleter = + Completer>(); + + FoundationFlutterApis.instance = FoundationFlutterApis( + instanceManager: instanceManager, + ); + + object = NSObject( + instanceManager: instanceManager, + observeValue: ( + String keyPath, + NSObject object, + Map change, + ) { + argsCompleter.complete([keyPath, object, change]); + }, + ); + instanceManager.addHostCreatedInstance(object, 1); + + FoundationFlutterApis.instance.object.observeValue( + 1, + 'keyPath', + 1, + [ + NSKeyValueChangeKeyEnumData(value: NSKeyValueChangeKeyEnum.oldValue) + ], + ['value'], + ); + + expect( + argsCompleter.future, + completion([ + 'keyPath', + object, + { + NSKeyValueChangeKey.oldValue: 'value', + }, + ]), + ); + }); }); }); } 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 cf417c4e0ca2..d71a6c5e0838 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 @@ -200,6 +200,42 @@ void main() { instanceManager.getIdentifier(scriptMessageHandler), )); }); + + test('didReceiveScriptMessage', () async { + final Completer> argsCompleter = + Completer>(); + + WebKitFlutterApis.instance = WebKitFlutterApis( + instanceManager: instanceManager, + ); + + scriptMessageHandler = WKScriptMessageHandler( + instanceManager: instanceManager, + didReceiveScriptMessage: ( + WKUserContentController userContentController, + WKScriptMessage message, + ) { + argsCompleter.complete([userContentController, message]); + }, + ); + + final WKUserContentController userContentController = + WKUserContentController.detached( + instanceManager: instanceManager, + ); + instanceManager.addHostCreatedInstance(userContentController, 2); + + WebKitFlutterApis.instance.scriptMessageHandler.didReceiveScriptMessage( + instanceManager.getIdentifier(scriptMessageHandler)!, + 2, + WKScriptMessageData(name: 'name'), + ); + + expect( + argsCompleter.future, + completion([userContentController, isA()]), + ); + }); }); group('WKPreferences', () { @@ -464,8 +500,7 @@ void main() { }, ); - WebKitFlutterApis.instance.navigationDelegateFlutterApi - .didFinishNavigation( + WebKitFlutterApis.instance.navigationDelegate.didFinishNavigation( instanceManager.getIdentifier(navigationDelegate)!, instanceManager.getIdentifier(webView)!, 'url', @@ -473,6 +508,150 @@ void main() { expect(argsCompleter.future, completion([webView, 'url'])); }); + + test('didStartProvisionalNavigation', () async { + final Completer> argsCompleter = + Completer>(); + + WebKitFlutterApis.instance = WebKitFlutterApis( + instanceManager: instanceManager, + ); + + navigationDelegate = WKNavigationDelegate( + instanceManager: instanceManager, + didStartProvisionalNavigation: (WKWebView webView, String? url) { + argsCompleter.complete([webView, url]); + }, + ); + + WebKitFlutterApis.instance.navigationDelegate + .didStartProvisionalNavigation( + instanceManager.getIdentifier(navigationDelegate)!, + instanceManager.getIdentifier(webView)!, + 'url', + ); + + expect(argsCompleter.future, completion([webView, 'url'])); + }); + + test('decidePolicyForNavigationAction', () async { + WebKitFlutterApis.instance = WebKitFlutterApis( + instanceManager: instanceManager, + ); + + navigationDelegate = WKNavigationDelegate( + instanceManager: instanceManager, + decidePolicyForNavigationAction: ( + WKWebView webView, + WKNavigationAction navigationAction, + ) async { + return WKNavigationActionPolicy.cancel; + }, + ); + + final WKNavigationActionPolicyEnumData policyData = + await WebKitFlutterApis.instance.navigationDelegate + .decidePolicyForNavigationAction( + instanceManager.getIdentifier(navigationDelegate)!, + instanceManager.getIdentifier(webView)!, + WKNavigationActionData( + request: NSUrlRequestData( + url: 'url', + allHttpHeaderFields: {}, + ), + targetFrame: WKFrameInfoData(isMainFrame: false), + ), + ); + + expect(policyData.value, WKNavigationActionPolicyEnum.cancel); + }); + + test('didFailNavigation', () async { + final Completer> argsCompleter = + Completer>(); + + WebKitFlutterApis.instance = WebKitFlutterApis( + instanceManager: instanceManager, + ); + + navigationDelegate = WKNavigationDelegate( + instanceManager: instanceManager, + didFailNavigation: (WKWebView webView, NSError error) { + argsCompleter.complete([webView, error]); + }, + ); + + WebKitFlutterApis.instance.navigationDelegate.didFailNavigation( + instanceManager.getIdentifier(navigationDelegate)!, + instanceManager.getIdentifier(webView)!, + NSErrorData( + code: 23, + domain: 'Hello', + localizedDescription: 'localiziedDescription', + ), + ); + + expect( + argsCompleter.future, + completion([webView, isA()]), + ); + }); + + test('didFailProvisionalNavigation', () async { + final Completer> argsCompleter = + Completer>(); + + WebKitFlutterApis.instance = WebKitFlutterApis( + instanceManager: instanceManager, + ); + + navigationDelegate = WKNavigationDelegate( + instanceManager: instanceManager, + didFailProvisionalNavigation: (WKWebView webView, NSError error) { + argsCompleter.complete([webView, error]); + }, + ); + + WebKitFlutterApis.instance.navigationDelegate + .didFailProvisionalNavigation( + instanceManager.getIdentifier(navigationDelegate)!, + instanceManager.getIdentifier(webView)!, + NSErrorData( + code: 23, + domain: 'Hello', + localizedDescription: 'localiziedDescription', + ), + ); + + expect( + argsCompleter.future, + completion([webView, isA()]), + ); + }); + + test('webViewWebContentProcessDidTerminate', () async { + final Completer> argsCompleter = + Completer>(); + + WebKitFlutterApis.instance = WebKitFlutterApis( + instanceManager: instanceManager, + ); + + navigationDelegate = WKNavigationDelegate( + instanceManager: instanceManager, + webViewWebContentProcessDidTerminate: (WKWebView webView) { + argsCompleter.complete([webView]); + }, + ); + + WebKitFlutterApis.instance.navigationDelegate + .webViewWebContentProcessDidTerminate( + instanceManager.getIdentifier(navigationDelegate)!, + instanceManager.getIdentifier(webView)!, + ); + + expect(argsCompleter.future, completion([webView])); + }); }); group('WKWebView', () { @@ -659,6 +838,63 @@ void main() { instanceManager.getIdentifier(uiDelegate), )); }); + + test('onCreateWebView', () async { + final Completer> argsCompleter = + Completer>(); + + WebKitFlutterApis.instance = WebKitFlutterApis( + instanceManager: instanceManager, + ); + + uiDelegate = WKUIDelegate( + instanceManager: instanceManager, + onCreateWebView: ( + WKWebView webView, + WKWebViewConfiguration configuration, + WKNavigationAction navigationAction, + ) { + argsCompleter.complete([ + webView, + configuration, + navigationAction, + ]); + }, + ); + + final WKWebView webView = WKWebView.detached( + instanceManager: instanceManager, + ); + instanceManager.addHostCreatedInstance(webView, 2); + + final WKWebViewConfiguration configuration = + WKWebViewConfiguration.detached( + instanceManager: instanceManager, + ); + instanceManager.addHostCreatedInstance(configuration, 3); + + WebKitFlutterApis.instance.uiDelegate.onCreateWebView( + instanceManager.getIdentifier(uiDelegate)!, + 2, + 3, + WKNavigationActionData( + request: NSUrlRequestData( + url: 'url', + allHttpHeaderFields: {}, + ), + targetFrame: WKFrameInfoData(isMainFrame: false), + ), + ); + + expect( + argsCompleter.future, + completion([ + webView, + configuration, + isA(), + ]), + ); + }); }); }); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart index b0c63b663066..8289831fc4e4 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.2.0 from annotations -// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart. +// in webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart. // Do not manually edit this file. import 'dart:async' as _i4; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index c1cdfc503a59..24155d2a05e1 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -136,13 +136,16 @@ void main() { await buildWidget(tester); final dynamic onCreateWebView = verify( - mockWebViewWidgetProxy.createUIDelgate( - onCreateWebView: captureAnyNamed('onCreateWebView'))) - .captured - .single as void Function(WKWebViewConfiguration, WKNavigationAction); + mockWebViewWidgetProxy.createUIDelgate( + onCreateWebView: captureAnyNamed('onCreateWebView'))) + .captured + .single + as void Function( + WKWebView, WKWebViewConfiguration, WKNavigationAction); const NSUrlRequest request = NSUrlRequest(url: 'https://google.com'); onCreateWebView( + mockWebView, mockWebViewConfiguration, const WKNavigationAction( request: request, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index 728c526ca929..c8ed4c856dcb 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.2.0 from annotations -// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. +// in webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. // Do not manually edit this file. import 'dart:async' as _i6; @@ -36,31 +36,31 @@ class _FakePoint_0 extends _i1.Fake implements _i2.Point {} class _FakeCopyable_1 extends _i1.Fake implements _i3.Copyable {} -class _FakeWKNavigationDelegate_2 extends _i1.Fake - implements _i4.WKNavigationDelegate {} - -class _FakeWKWebViewConfiguration_3 extends _i1.Fake +class _FakeWKWebViewConfiguration_2 extends _i1.Fake implements _i4.WKWebViewConfiguration {} -class _FakeUIScrollView_4 extends _i1.Fake implements _i5.UIScrollView {} +class _FakeUIScrollView_3 extends _i1.Fake implements _i5.UIScrollView {} -class _FakeWKUserContentController_5 extends _i1.Fake +class _FakeWKUserContentController_4 extends _i1.Fake implements _i4.WKUserContentController {} -class _FakeWKPreferences_6 extends _i1.Fake implements _i4.WKPreferences {} +class _FakeWKPreferences_5 extends _i1.Fake implements _i4.WKPreferences {} -class _FakeWKWebsiteDataStore_7 extends _i1.Fake +class _FakeWKWebsiteDataStore_6 extends _i1.Fake implements _i4.WKWebsiteDataStore {} -class _FakeWKHttpCookieStore_8 extends _i1.Fake +class _FakeWKHttpCookieStore_7 extends _i1.Fake implements _i4.WKHttpCookieStore {} -class _FakeWKWebView_9 extends _i1.Fake implements _i4.WKWebView {} +class _FakeWKWebView_8 extends _i1.Fake implements _i4.WKWebView {} -class _FakeWKScriptMessageHandler_10 extends _i1.Fake +class _FakeWKScriptMessageHandler_9 extends _i1.Fake implements _i4.WKScriptMessageHandler {} -class _FakeWKUIDelegate_11 extends _i1.Fake implements _i4.WKUIDelegate {} +class _FakeWKUIDelegate_10 extends _i1.Fake implements _i4.WKUIDelegate {} + +class _FakeWKNavigationDelegate_11 extends _i1.Fake + implements _i4.WKNavigationDelegate {} /// A class which mocks [UIScrollView]. /// @@ -125,9 +125,8 @@ class MockWKNavigationDelegate extends _i1.Mock } @override - _i4.WKNavigationDelegate copy() => (super.noSuchMethod( - Invocation.method(#copy, []), - returnValue: _FakeWKNavigationDelegate_2()) as _i4.WKNavigationDelegate); + _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeCopyable_1()) as _i3.Copyable); @override _i6.Future addObserver(_i8.NSObject? observer, {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => @@ -222,12 +221,12 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { @override _i4.WKWebViewConfiguration get configuration => (super.noSuchMethod(Invocation.getter(#configuration), - returnValue: _FakeWKWebViewConfiguration_3()) + returnValue: _FakeWKWebViewConfiguration_2()) as _i4.WKWebViewConfiguration); @override _i5.UIScrollView get scrollView => (super.noSuchMethod(Invocation.getter(#scrollView), - returnValue: _FakeUIScrollView_4()) as _i5.UIScrollView); + returnValue: _FakeUIScrollView_3()) as _i5.UIScrollView); @override _i6.Future setUIDelegate(_i4.WKUIDelegate? delegate) => (super.noSuchMethod(Invocation.method(#setUIDelegate, [delegate]), @@ -352,16 +351,16 @@ class MockWKWebViewConfiguration extends _i1.Mock @override _i4.WKUserContentController get userContentController => (super.noSuchMethod(Invocation.getter(#userContentController), - returnValue: _FakeWKUserContentController_5()) + returnValue: _FakeWKUserContentController_4()) as _i4.WKUserContentController); @override _i4.WKPreferences get preferences => (super.noSuchMethod(Invocation.getter(#preferences), - returnValue: _FakeWKPreferences_6()) as _i4.WKPreferences); + returnValue: _FakeWKPreferences_5()) as _i4.WKPreferences); @override _i4.WKWebsiteDataStore get websiteDataStore => (super.noSuchMethod(Invocation.getter(#websiteDataStore), - returnValue: _FakeWKWebsiteDataStore_7()) as _i4.WKWebsiteDataStore); + returnValue: _FakeWKWebsiteDataStore_6()) as _i4.WKWebsiteDataStore); @override _i6.Future setAllowsInlineMediaPlayback(bool? allow) => (super .noSuchMethod(Invocation.method(#setAllowsInlineMediaPlayback, [allow]), @@ -406,7 +405,7 @@ class MockWKWebsiteDataStore extends _i1.Mock @override _i4.WKHttpCookieStore get httpCookieStore => (super.noSuchMethod(Invocation.getter(#httpCookieStore), - returnValue: _FakeWKHttpCookieStore_8()) as _i4.WKHttpCookieStore); + returnValue: _FakeWKHttpCookieStore_7()) as _i4.WKHttpCookieStore); @override _i6.Future removeDataOfTypes( Set<_i4.WKWebsiteDataType>? dataTypes, DateTime? since) => @@ -590,7 +589,7 @@ class MockWebViewWidgetProxy extends _i1.Mock (super.noSuchMethod( Invocation.method( #createWebView, [configuration], {#observeValue: observeValue}), - returnValue: _FakeWKWebView_9()) as _i4.WKWebView); + returnValue: _FakeWKWebView_8()) as _i4.WKWebView); @override _i4.WKScriptMessageHandler createScriptMessageHandler( {void Function(_i4.WKUserContentController, _i4.WKScriptMessage)? @@ -598,16 +597,17 @@ class MockWebViewWidgetProxy extends _i1.Mock (super.noSuchMethod( Invocation.method(#createScriptMessageHandler, [], {#didReceiveScriptMessage: didReceiveScriptMessage}), - returnValue: _FakeWKScriptMessageHandler_10()) + returnValue: _FakeWKScriptMessageHandler_9()) as _i4.WKScriptMessageHandler); @override _i4.WKUIDelegate createUIDelgate( - {void Function(_i4.WKWebViewConfiguration, _i4.WKNavigationAction)? + {void Function(_i4.WKWebView, _i4.WKWebViewConfiguration, + _i4.WKNavigationAction)? onCreateWebView}) => (super.noSuchMethod( Invocation.method( #createUIDelgate, [], {#onCreateWebView: onCreateWebView}), - returnValue: _FakeWKUIDelegate_11()) as _i4.WKUIDelegate); + returnValue: _FakeWKUIDelegate_10()) as _i4.WKUIDelegate); @override _i4.WKNavigationDelegate createNavigationDelegate( {void Function(_i4.WKWebView, String?)? didFinishNavigation, @@ -631,6 +631,6 @@ class MockWebViewWidgetProxy extends _i1.Mock #webViewWebContentProcessDidTerminate: webViewWebContentProcessDidTerminate }), - returnValue: _FakeWKNavigationDelegate_2()) + returnValue: _FakeWKNavigationDelegate_11()) as _i4.WKNavigationDelegate); } From f894b9807058b28f8d7195423dd3f3f292d05998 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 8 Jun 2022 13:23:14 -0700 Subject: [PATCH 407/844] [webview_flutter_wkwebview] Implement Objc side of Flutter Apis (#5934) --- .../ios/Flutter/AppFrameworkInfo.plist | 2 +- .../example/ios/Podfile | 2 +- .../ios/Runner.xcodeproj/project.pbxproj | 4 +- .../example/ios/Runner/Info.plist | 2 + .../ios/RunnerTests/FWFDataConvertersTests.m | 61 ++++++ .../FWFNavigationDelegateHostApiTests.m | 187 ++++++++++++++++-- .../ios/RunnerTests/FWFObjectHostApiTests.m | 61 ++++++ .../FWFScriptMessageHandlerHostApiTests.m | 57 ++++++ .../RunnerTests/FWFUIDelegateHostApiTests.m | 66 +++++++ .../ios/Classes/FWFDataConverters.h | 66 +++++++ .../ios/Classes/FWFDataConverters.m | 61 ++++++ .../Classes/FWFNavigationDelegateHostApi.h | 4 +- .../Classes/FWFNavigationDelegateHostApi.m | 141 ++++++++++++- .../ios/Classes/FWFObjectHostApi.h | 4 +- .../ios/Classes/FWFObjectHostApi.m | 48 ++++- .../Classes/FWFScriptMessageHandlerHostApi.h | 17 +- .../Classes/FWFScriptMessageHandlerHostApi.m | 52 ++++- .../ios/Classes/FWFUIDelegateHostApi.h | 16 +- .../ios/Classes/FWFUIDelegateHostApi.m | 64 +++++- 19 files changed, 877 insertions(+), 38 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Flutter/AppFrameworkInfo.plist b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Flutter/AppFrameworkInfo.plist index 8d4492f977ad..9625e105df39 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Flutter/AppFrameworkInfo.plist +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 11.0 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Podfile b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Podfile index 66509fcae284..d01e899e347b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Podfile +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' +# platform :ios, '11.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj index e7519af18e7c..4ed8769ea518 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj @@ -614,7 +614,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -664,7 +664,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner/Info.plist b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner/Info.plist index a810c5a172c0..bea41604e8aa 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner/Info.plist +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner/Info.plist @@ -41,5 +41,7 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + 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 57d90f6c6814..1fc5a95398bd 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 @@ -6,6 +6,8 @@ @import XCTest; @import webview_flutter_wkwebview; +#import + @interface FWFDataConvertersTests : XCTestCase @end @@ -43,4 +45,63 @@ - (void)testFWFWKUserScriptFromScriptData { XCTAssertEqual(userScript.injectionTime, WKUserScriptInjectionTimeAtDocumentStart); XCTAssertEqual(userScript.isForMainFrameOnly, NO); } + +- (void)testFWFWKNavigationActionDataFromNavigationAction { + WKNavigationAction *mockNavigationAction = OCMClassMock([WKNavigationAction class]); + + NSURLRequest *request = + [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.flutter.dev/"]]; + OCMStub([mockNavigationAction request]).andReturn(request); + + WKFrameInfo *mockFrameInfo = OCMClassMock([WKFrameInfo class]); + OCMStub([mockFrameInfo isMainFrame]).andReturn(YES); + OCMStub([mockNavigationAction targetFrame]).andReturn(mockFrameInfo); + + FWFWKNavigationActionData *data = + FWFWKNavigationActionDataFromNavigationAction(mockNavigationAction); + XCTAssertNotNil(data); +} + +- (void)testFWFNSUrlRequestDataFromNSURLRequest { + NSMutableURLRequest *request = + [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://www.flutter.dev/"]]; + request.HTTPMethod = @"POST"; + request.HTTPBody = [@"aString" dataUsingEncoding:NSUTF8StringEncoding]; + request.allHTTPHeaderFields = @{@"a" : @"field"}; + + FWFNSUrlRequestData *data = FWFNSUrlRequestDataFromNSURLRequest(request); + XCTAssertEqualObjects(data.url, @"https://www.flutter.dev/"); + XCTAssertEqualObjects(data.httpMethod, @"POST"); + XCTAssertEqualObjects(data.httpBody.data, [@"aString" dataUsingEncoding:NSUTF8StringEncoding]); + XCTAssertEqualObjects(data.allHttpHeaderFields, @{@"a" : @"field"}); +} + +- (void)testFWFWKFrameInfoDataFromWKFrameInfo { + WKFrameInfo *mockFrameInfo = OCMClassMock([WKFrameInfo class]); + OCMStub([mockFrameInfo isMainFrame]).andReturn(YES); + + FWFWKFrameInfoData *targetFrameData = FWFWKFrameInfoDataFromWKFrameInfo(mockFrameInfo); + XCTAssertEqualObjects(targetFrameData.isMainFrame, @YES); +} + +- (void)testFWFNSErrorDataFromNSError { + NSError *error = [NSError errorWithDomain:@"domain" + code:23 + userInfo:@{NSLocalizedDescriptionKey : @"description"}]; + + FWFNSErrorData *data = FWFNSErrorDataFromNSError(error); + XCTAssertEqualObjects(data.code, @23); + XCTAssertEqualObjects(data.domain, @"domain"); + XCTAssertEqualObjects(data.localizedDescription, @"description"); +} + +- (void)testFWFWKScriptMessageDataFromWKScriptMessage { + WKScriptMessage *mockScriptMessage = OCMClassMock([WKScriptMessage class]); + OCMStub([mockScriptMessage name]).andReturn(@"name"); + OCMStub([mockScriptMessage body]).andReturn(@"message"); + + FWFWKScriptMessageData *data = FWFWKScriptMessageDataFromWKScriptMessage(mockScriptMessage); + XCTAssertEqualObjects(data.name, @"name"); + XCTAssertEqualObjects(data.body, @"message"); +} @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 206137e301f5..570a1f73ad9b 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 @@ -12,6 +12,38 @@ @interface FWFNavigationDelegateHostApiTests : XCTestCase @end @implementation FWFNavigationDelegateHostApiTests +/** + * Creates a partially mocked FWFNavigationDelegate and adds it to instanceManager. + * + * @param instanceManager Instance manager to add the delegate to. + * @param identifier Identifier for the delegate added to the instanceManager. + * + * @return A mock FWFNavigationDelegate. + */ +- (id)mockNavigationDelegateWithManager:(FWFInstanceManager *)instanceManager + identifier:(long)identifier { + FWFNavigationDelegate *navigationDelegate = [[FWFNavigationDelegate alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; + + [instanceManager addDartCreatedInstance:navigationDelegate withIdentifier:0]; + return OCMPartialMock(navigationDelegate); +} + +/** + * Creates a mock FWFNavigationDelegateFlutterApiImpl with instanceManager. + * + * @param instanceManager Instance manager passed to the Flutter API. + * + * @return A mock FWFNavigationDelegateFlutterApiImpl. + */ +- (id)mockFlutterApiWithManager:(FWFInstanceManager *)instanceManager { + FWFNavigationDelegateFlutterApiImpl *flutterAPI = [[FWFNavigationDelegateFlutterApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; + return OCMPartialMock(flutterAPI); +} + - (void)testCreateWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; FWFNavigationDelegateHostApiImpl *hostAPI = [[FWFNavigationDelegateHostApiImpl alloc] @@ -29,31 +61,156 @@ - (void)testCreateWithIdentifier { - (void)testDidFinishNavigation { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - FWFNavigationDelegateHostApiImpl *hostAPI = [[FWFNavigationDelegateHostApiImpl alloc] - initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) - instanceManager:instanceManager]; - FlutterError *error; - [hostAPI createWithIdentifier:@0 error:&error]; - FWFNavigationDelegate *navigationDelegate = - (FWFNavigationDelegate *)[instanceManager instanceForIdentifier:0]; - id mockDelegate = OCMPartialMock(navigationDelegate); + FWFNavigationDelegate *mockDelegate = [self mockNavigationDelegateWithManager:instanceManager + identifier:0]; + FWFNavigationDelegateFlutterApiImpl *mockFlutterAPI = + [self mockFlutterApiWithManager:instanceManager]; - FWFNavigationDelegateFlutterApiImpl *flutterAPI = [[FWFNavigationDelegateFlutterApiImpl alloc] - initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) - instanceManager:instanceManager]; - id mockFlutterApi = OCMPartialMock(flutterAPI); - - OCMStub([mockDelegate navigationDelegateAPI]).andReturn(mockFlutterApi); + OCMStub([mockDelegate navigationDelegateAPI]).andReturn(mockFlutterAPI); WKWebView *mockWebView = OCMClassMock([WKWebView class]); OCMStub([mockWebView URL]).andReturn([NSURL URLWithString:@"https://flutter.dev/"]); [instanceManager addDartCreatedInstance:mockWebView withIdentifier:1]; [mockDelegate webView:mockWebView didFinishNavigation:OCMClassMock([WKNavigation class])]; - OCMVerify([mockFlutterApi didFinishNavigationForDelegateWithIdentifier:@0 + OCMVerify([mockFlutterAPI didFinishNavigationForDelegateWithIdentifier:@0 webViewIdentifier:@1 URL:@"https://flutter.dev/" completion:OCMOCK_ANY]); } + +- (void)testDidStartProvisionalNavigation { + 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]); + OCMStub([mockWebView URL]).andReturn([NSURL URLWithString:@"https://flutter.dev/"]); + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:1]; + + [mockDelegate webView:mockWebView + didStartProvisionalNavigation:OCMClassMock([WKNavigation class])]; + OCMVerify([mockFlutterAPI + didStartProvisionalNavigationForDelegateWithIdentifier:@0 + webViewIdentifier:@1 + URL:@"https://flutter.dev/" + completion:OCMOCK_ANY]); +} + +- (void)testDecidePolicyForNavigationAction { + 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]; + + WKNavigationAction *mockNavigationAction = OCMClassMock([WKNavigationAction class]); + OCMStub([mockNavigationAction request]) + .andReturn([NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.flutter.dev"]]); + + WKFrameInfo *mockFrameInfo = OCMClassMock([WKFrameInfo class]); + OCMStub([mockFrameInfo isMainFrame]).andReturn(YES); + OCMStub([mockNavigationAction targetFrame]).andReturn(mockFrameInfo); + + OCMStub([mockFlutterAPI + decidePolicyForNavigationActionForDelegateWithIdentifier:@0 + webViewIdentifier:@1 + navigationAction: + [OCMArg isKindOfClass:[FWFWKNavigationActionData + class]] + completion: + ([OCMArg + invokeBlockWithArgs: + [FWFWKNavigationActionPolicyEnumData + makeWithValue: + FWFWKNavigationActionPolicyEnumCancel], + [NSNull null], nil])]); + + WKNavigationActionPolicy __block callbackPolicy = -1; + [mockDelegate webView:mockWebView + decidePolicyForNavigationAction:mockNavigationAction + decisionHandler:^(WKNavigationActionPolicy policy) { + callbackPolicy = policy; + }]; + XCTAssertEqual(callbackPolicy, WKNavigationActionPolicyCancel); +} + +- (void)testDidFailNavigation { + 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]; + + [mockDelegate webView:mockWebView + didFailNavigation:OCMClassMock([WKNavigation class]) + withError:[NSError errorWithDomain:@"domain" code:0 userInfo:nil]]; + OCMVerify([mockFlutterAPI + didFailNavigationForDelegateWithIdentifier:@0 + webViewIdentifier:@1 + error:[OCMArg isKindOfClass:[FWFNSErrorData class]] + completion:OCMOCK_ANY]); +} + +- (void)testDidFailProvisionalNavigation { + 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]; + + [mockDelegate webView:mockWebView + didFailProvisionalNavigation:OCMClassMock([WKNavigation class]) + withError:[NSError errorWithDomain:@"domain" code:0 userInfo:nil]]; + OCMVerify([mockFlutterAPI + didFailProvisionalNavigationForDelegateWithIdentifier:@0 + webViewIdentifier:@1 + error:[OCMArg isKindOfClass:[FWFNSErrorData + class]] + completion:OCMOCK_ANY]); +} + +- (void)testWebViewWebContentProcessDidTerminate { + 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]; + + [mockDelegate webViewWebContentProcessDidTerminate:mockWebView]; + OCMVerify([mockFlutterAPI + webViewWebContentProcessDidTerminateForDelegateWithIdentifier:@0 + webViewIdentifier:@1 + completion:OCMOCK_ANY]); +} @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m index bdaeae4c09dc..b8e41d142331 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFObjectHostApiTests.m @@ -12,6 +12,37 @@ @interface FWFObjectHostApiTests : XCTestCase @end @implementation FWFObjectHostApiTests +/** + * Creates a partially mocked FWFObject and adds it to instanceManager. + * + * @param instanceManager Instance manager to add the delegate to. + * @param identifier Identifier for the delegate added to the instanceManager. + * + * @return A mock FWFObject. + */ +- (id)mockObjectWithManager:(FWFInstanceManager *)instanceManager identifier:(long)identifier { + FWFObject *object = + [[FWFObject alloc] initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; + + [instanceManager addDartCreatedInstance:object withIdentifier:0]; + return OCMPartialMock(object); +} + +/** + * Creates a mock FWFObjectFlutterApiImpl with instanceManager. + * + * @param instanceManager Instance manager passed to the Flutter API. + * + * @return A mock FWFObjectFlutterApiImpl. + */ +- (id)mockFlutterApiWithManager:(FWFInstanceManager *)instanceManager { + FWFObjectFlutterApiImpl *flutterAPI = [[FWFObjectFlutterApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; + return OCMPartialMock(flutterAPI); +} + - (void)testAddObserver { NSObject *mockObject = OCMClassMock([NSObject class]); @@ -82,4 +113,34 @@ - (void)testDispose { XCTAssertFalse([instanceManager containsInstance:object]); XCTAssertNil(error); } + +- (void)testObserveValueForKeyPath { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + + FWFObject *mockObject = [self mockObjectWithManager:instanceManager identifier:0]; + FWFObjectFlutterApiImpl *mockFlutterAPI = [self mockFlutterApiWithManager:instanceManager]; + + OCMStub([mockObject objectApi]).andReturn(mockFlutterAPI); + + NSObject *object = [[NSObject alloc] init]; + [instanceManager addDartCreatedInstance:object withIdentifier:1]; + + [mockObject observeValueForKeyPath:@"keyPath" + ofObject:object + change:@{NSKeyValueChangeOldKey : @"key"} + context:nil]; + OCMVerify([mockFlutterAPI + observeValueForObjectWithIdentifier:@0 + keyPath:@"keyPath" + objectIdentifier:@1 + changeKeys:[OCMArg checkWithBlock:^BOOL( + NSArray + *value) { + return value[0].value == FWFNSKeyValueChangeKeyEnumOldValue; + }] + changeValues:[OCMArg checkWithBlock:^BOOL(id value) { + return [@"key" isEqual:value[0]]; + }] + completion:OCMOCK_ANY]); +} @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m index b74d21114cc4..5a51e7b980f8 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m @@ -12,6 +12,37 @@ @interface FWFScriptMessageHandlerHostApiTests : XCTestCase @end @implementation FWFScriptMessageHandlerHostApiTests +/** + * Creates a partially mocked FWFScriptMessageHandler and adds it to instanceManager. + * + * @param instanceManager Instance manager to add the delegate to. + * @param identifier Identifier for the delegate added to the instanceManager. + * + * @return A mock FWFScriptMessageHandler. + */ +- (id)mockHandlerWithManager:(FWFInstanceManager *)instanceManager identifier:(long)identifier { + FWFScriptMessageHandler *handler = [[FWFScriptMessageHandler alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; + + [instanceManager addDartCreatedInstance:handler withIdentifier:0]; + return OCMPartialMock(handler); +} + +/** + * Creates a mock FWFScriptMessageHandlerFlutterApiImpl with instanceManager. + * + * @param instanceManager Instance manager passed to the Flutter API. + * + * @return A mock FWFScriptMessageHandlerFlutterApiImpl. + */ +- (id)mockFlutterApiWithManager:(FWFInstanceManager *)instanceManager { + FWFScriptMessageHandlerFlutterApiImpl *flutterAPI = [[FWFScriptMessageHandlerFlutterApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; + return OCMPartialMock(flutterAPI); +} + - (void)testCreateWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; FWFScriptMessageHandlerHostApiImpl *hostAPI = @@ -26,4 +57,30 @@ - (void)testCreateWithIdentifier { XCTAssertTrue([scriptMessageHandler conformsToProtocol:@protocol(WKScriptMessageHandler)]); XCTAssertNil(error); } + +- (void)testDidReceiveScriptMessageForHandler { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + + FWFScriptMessageHandler *mockHandler = [self mockHandlerWithManager:instanceManager identifier:0]; + FWFScriptMessageHandlerFlutterApiImpl *mockFlutterAPI = + [self mockFlutterApiWithManager:instanceManager]; + + OCMStub([mockHandler scriptMessageHandlerAPI]).andReturn(mockFlutterAPI); + + WKUserContentController *userContentController = [[WKUserContentController alloc] init]; + [instanceManager addDartCreatedInstance:userContentController withIdentifier:1]; + + WKScriptMessage *mockScriptMessage = OCMClassMock([WKScriptMessage class]); + OCMStub([mockScriptMessage name]).andReturn(@"name"); + OCMStub([mockScriptMessage body]).andReturn(@"message"); + + [mockHandler userContentController:userContentController + didReceiveScriptMessage:mockScriptMessage]; + OCMVerify([mockFlutterAPI + didReceiveScriptMessageForHandlerWithIdentifier:@0 + userContentControllerIdentifier:@1 + message:[OCMArg isKindOfClass:[FWFWKScriptMessageData + class]] + completion:OCMOCK_ANY]); +} @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m index 4ee36ae7c492..17cb4367b3aa 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m @@ -12,6 +12,37 @@ @interface FWFUIDelegateHostApiTests : XCTestCase @end @implementation FWFUIDelegateHostApiTests +/** + * Creates a partially mocked FWFUIDelegate and adds it to instanceManager. + * + * @param instanceManager Instance manager to add the delegate to. + * @param identifier Identifier for the delegate added to the instanceManager. + * + * @return A mock FWFUIDelegate. + */ +- (id)mockDelegateWithManager:(FWFInstanceManager *)instanceManager identifier:(long)identifier { + FWFUIDelegate *delegate = [[FWFUIDelegate alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; + + [instanceManager addDartCreatedInstance:delegate withIdentifier:0]; + return OCMPartialMock(delegate); +} + +/** + * Creates a mock FWFUIDelegateFlutterApiImpl with instanceManager. + * + * @param instanceManager Instance manager passed to the Flutter API. + * + * @return A mock FWFUIDelegateFlutterApiImpl. + */ +- (id)mockFlutterApiWithManager:(FWFInstanceManager *)instanceManager { + FWFUIDelegateFlutterApiImpl *flutterAPI = [[FWFUIDelegateFlutterApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; + return OCMPartialMock(flutterAPI); +} + - (void)testCreateWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; FWFUIDelegateHostApiImpl *hostAPI = @@ -24,4 +55,39 @@ - (void)testCreateWithIdentifier { XCTAssertTrue([delegate conformsToProtocol:@protocol(WKUIDelegate)]); XCTAssertNil(error); } + +- (void)testOnCreateWebViewForDelegateWithIdentifier { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + + FWFUIDelegate *mockDelegate = [self mockDelegateWithManager:instanceManager identifier:0]; + FWFUIDelegateFlutterApiImpl *mockFlutterAPI = [self mockFlutterApiWithManager:instanceManager]; + + OCMStub([mockDelegate UIDelegateAPI]).andReturn(mockFlutterAPI); + + WKWebView *mockWebView = OCMClassMock([WKWebView class]); + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:1]; + + WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init]; + [instanceManager addDartCreatedInstance:configuration withIdentifier:2]; + + WKNavigationAction *mockNavigationAction = OCMClassMock([WKNavigationAction class]); + OCMStub([mockNavigationAction request]) + .andReturn([NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.flutter.dev"]]); + + WKFrameInfo *mockFrameInfo = OCMClassMock([WKFrameInfo class]); + OCMStub([mockFrameInfo isMainFrame]).andReturn(YES); + OCMStub([mockNavigationAction targetFrame]).andReturn(mockFrameInfo); + + [mockDelegate webView:mockWebView + createWebViewWithConfiguration:configuration + forNavigationAction:mockNavigationAction + windowFeatures:OCMClassMock([WKWindowFeatures class])]; + OCMVerify([mockFlutterAPI + onCreateWebViewForDelegateWithIdentifier:@0 + webViewIdentifier:@1 + configurationIdentifier:@2 + navigationAction:[OCMArg + isKindOfClass:[FWFWKNavigationActionData class]] + completion:OCMOCK_ANY]); +} @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 4cf24c67ac95..2863048726a9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h @@ -85,4 +85,70 @@ extern WKAudiovisualMediaTypes FWFWKAudiovisualMediaTypeFromEnumData( */ extern NSString *_Nullable FWFWKWebsiteDataTypeFromEnumData(FWFWKWebsiteDataTypeEnumData *data); +/** + * Converts a WKNavigationAction to an FWFWKNavigationActionData. + * + * @param action The object containing information to create a WKNavigationActionData. + * + * @return A FWFWKNavigationActionData. + */ +extern FWFWKNavigationActionData *FWFWKNavigationActionDataFromNavigationAction( + WKNavigationAction *action); + +/** + * Converts a NSURLRequest to an FWFNSUrlRequestData. + * + * @param request The object containing information to create a WKNavigationActionData. + * + * @return A FWFNSUrlRequestData. + */ +extern FWFNSUrlRequestData *FWFNSUrlRequestDataFromNSURLRequest(NSURLRequest *request); + +/** + * Converts a WKFrameInfo to an FWFWKFrameInfoData. + * + * @param info The object containing information to create a FWFWKFrameInfoData. + * + * @return A FWFWKFrameInfoData. + */ +extern FWFWKFrameInfoData *FWFWKFrameInfoDataFromWKFrameInfo(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. + */ +extern WKNavigationActionPolicy FWFWKNavigationActionPolicyFromEnumData( + FWFWKNavigationActionPolicyEnumData *data); + +/** + * Converts a NSError to an FWFNSErrorData. + * + * @param error The object containing information to create a FWFNSErrorData. + * + * @return A FWFNSErrorData. + */ +extern FWFNSErrorData *FWFNSErrorDataFromNSError(NSError *error); + +/** + * Converts an NSKeyValueChangeKey to a FWFNSKeyValueChangeKeyEnumData. + * + * @param key The data object containing information to create a FWFNSKeyValueChangeKeyEnumData. + * + * @return A FWFNSKeyValueChangeKeyEnumData or nil if data could not be converted. + */ +extern FWFNSKeyValueChangeKeyEnumData *FWFNSKeyValueChangeKeyEnumDataFromNSKeyValueChangeKey( + NSKeyValueChangeKey key); + +/** + * Converts a WKScriptMessage to an FWFWKScriptMessageData. + * + * @param message The object containing information to create a FWFWKScriptMessageData. + * + * @return A FWFWKScriptMessageData. + */ +extern FWFWKScriptMessageData *FWFWKScriptMessageDataFromWKScriptMessage(WKScriptMessage *message); + NS_ASSUME_NONNULL_END 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 a06b3d79b38c..759bfedc1621 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m @@ -153,3 +153,64 @@ WKAudiovisualMediaTypes FWFWKAudiovisualMediaTypeFromEnumData( return nil; } + +FWFWKNavigationActionData *FWFWKNavigationActionDataFromNavigationAction( + WKNavigationAction *action) { + return [FWFWKNavigationActionData + makeWithRequest:FWFNSUrlRequestDataFromNSURLRequest(action.request) + targetFrame:FWFWKFrameInfoDataFromWKFrameInfo(action.targetFrame)]; +} + +FWFNSUrlRequestData *FWFNSUrlRequestDataFromNSURLRequest(NSURLRequest *request) { + return [FWFNSUrlRequestData + makeWithUrl:request.URL.absoluteString + httpMethod:request.HTTPMethod + httpBody:request.HTTPBody + ? [FlutterStandardTypedData typedDataWithBytes:request.HTTPBody] + : nil + allHttpHeaderFields:request.allHTTPHeaderFields ? request.allHTTPHeaderFields : @{}]; +} + +FWFWKFrameInfoData *FWFWKFrameInfoDataFromWKFrameInfo(WKFrameInfo *info) { + return [FWFWKFrameInfoData makeWithIsMainFrame:@(info.isMainFrame)]; +} + +WKNavigationActionPolicy FWFWKNavigationActionPolicyFromEnumData( + FWFWKNavigationActionPolicyEnumData *data) { + switch (data.value) { + case FWFWKNavigationActionPolicyEnumAllow: + return WKNavigationActionPolicyAllow; + case FWFWKNavigationActionPolicyEnumCancel: + return WKNavigationActionPolicyCancel; + } + + return -1; +} + +FWFNSErrorData *FWFNSErrorDataFromNSError(NSError *error) { + return [FWFNSErrorData makeWithCode:@(error.code) + domain:error.domain + localizedDescription:error.localizedDescription]; +} + +FWFNSKeyValueChangeKeyEnumData *FWFNSKeyValueChangeKeyEnumDataFromNSKeyValueChangeKey( + NSKeyValueChangeKey key) { + if ([key isEqualToString:NSKeyValueChangeIndexesKey]) { + return [FWFNSKeyValueChangeKeyEnumData makeWithValue:FWFNSKeyValueChangeKeyEnumIndexes]; + } else if ([key isEqualToString:NSKeyValueChangeKindKey]) { + return [FWFNSKeyValueChangeKeyEnumData makeWithValue:FWFNSKeyValueChangeKeyEnumKind]; + } else if ([key isEqualToString:NSKeyValueChangeNewKey]) { + return [FWFNSKeyValueChangeKeyEnumData makeWithValue:FWFNSKeyValueChangeKeyEnumNewValue]; + } else if ([key isEqualToString:NSKeyValueChangeNotificationIsPriorKey]) { + return [FWFNSKeyValueChangeKeyEnumData + makeWithValue:FWFNSKeyValueChangeKeyEnumNotificationIsPrior]; + } else if ([key isEqualToString:NSKeyValueChangeOldKey]) { + return [FWFNSKeyValueChangeKeyEnumData makeWithValue:FWFNSKeyValueChangeKeyEnumOldValue]; + } + + return nil; +} + +FWFWKScriptMessageData *FWFWKScriptMessageDataFromWKScriptMessage(WKScriptMessage *message) { + return [FWFWKScriptMessageData makeWithName:message.name body:message.body]; +} 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 2ac599603152..da5939a3abed 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.h @@ -26,7 +26,9 @@ NS_ASSUME_NONNULL_BEGIN * Implementation of WKNavigationDelegate for FWFNavigationDelegateHostApiImpl. */ @interface FWFNavigationDelegate : FWFObject -@property(readonly, nonnull) FWFNavigationDelegateFlutterApiImpl *navigationDelegateAPI; +@property(readonly, nonnull, nonatomic) FWFNavigationDelegateFlutterApiImpl *navigationDelegateAPI; +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager; @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 be651c942c0e..6946783088d5 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m @@ -3,6 +3,7 @@ // found in the LICENSE file. #import "FWFNavigationDelegateHostApi.h" +#import "FWFDataConverters.h" #import "FWFWebViewConfigurationHostApi.h" @interface FWFNavigationDelegateFlutterApiImpl () @@ -26,15 +27,82 @@ - (long)identifierForDelegate:(FWFNavigationDelegate *)instance { - (void)didFinishNavigationForDelegate:(FWFNavigationDelegate *)instance webView:(WKWebView *)webView - URL:(NSString *)URL { + URL:(NSString *)URL + completion:(void (^)(NSError *_Nullable))completion { + NSNumber *webViewIdentifier = + @([self.instanceManager identifierWithStrongReferenceForInstance:webView]); [self didFinishNavigationForDelegateWithIdentifier:@([self identifierForDelegate:instance]) - webViewIdentifier: - @([self.instanceManager - identifierWithStrongReferenceForInstance:webView]) + webViewIdentifier:webViewIdentifier URL:URL - completion:^(NSError *error) { - NSAssert(!error, @"%@", error); - }]; + completion:completion]; +} + +- (void)didStartProvisionalNavigationForDelegate:(FWFNavigationDelegate *)instance + webView:(WKWebView *)webView + URL:(NSString *)URL + completion:(void (^)(NSError *_Nullable))completion { + NSNumber *webViewIdentifier = + @([self.instanceManager identifierWithStrongReferenceForInstance:webView]); + [self didStartProvisionalNavigationForDelegateWithIdentifier:@([self + identifierForDelegate:instance]) + webViewIdentifier:webViewIdentifier + URL:URL + completion:completion]; +} + +- (void) + decidePolicyForNavigationActionForDelegate:(FWFNavigationDelegate *)instance + webView:(WKWebView *)webView + navigationAction:(WKNavigationAction *)navigationAction + completion: + (void (^)(FWFWKNavigationActionPolicyEnumData *_Nullable, + NSError *_Nullable))completion { + NSNumber *webViewIdentifier = + @([self.instanceManager identifierWithStrongReferenceForInstance:webView]); + FWFWKNavigationActionData *navigationActionData = + FWFWKNavigationActionDataFromNavigationAction(navigationAction); + [self + decidePolicyForNavigationActionForDelegateWithIdentifier:@([self + identifierForDelegate:instance]) + webViewIdentifier:webViewIdentifier + navigationAction:navigationActionData + completion:completion]; +} + +- (void)didFailNavigationForDelegate:(FWFNavigationDelegate *)instance + webView:(WKWebView *)webView + error:(NSError *)error + completion:(void (^)(NSError *_Nullable))completion { + NSNumber *webViewIdentifier = + @([self.instanceManager identifierWithStrongReferenceForInstance:webView]); + [self didFailNavigationForDelegateWithIdentifier:@([self identifierForDelegate:instance]) + webViewIdentifier:webViewIdentifier + error:FWFNSErrorDataFromNSError(error) + completion:completion]; +} + +- (void)didFailProvisionalNavigationForDelegate:(FWFNavigationDelegate *)instance + webView:(WKWebView *)webView + error:(NSError *)error + completion:(void (^)(NSError *_Nullable))completion { + NSNumber *webViewIdentifier = + @([self.instanceManager identifierWithStrongReferenceForInstance:webView]); + [self + didFailProvisionalNavigationForDelegateWithIdentifier:@([self identifierForDelegate:instance]) + webViewIdentifier:webViewIdentifier + error:FWFNSErrorDataFromNSError(error) + completion:completion]; +} + +- (void)webViewWebContentProcessDidTerminateForDelegate:(FWFNavigationDelegate *)instance + webView:(WKWebView *)webView + completion:(void (^)(NSError *_Nullable))completion { + NSNumber *webViewIdentifier = + @([self.instanceManager identifierWithStrongReferenceForInstance:webView]); + [self webViewWebContentProcessDidTerminateForDelegateWithIdentifier: + @([self identifierForDelegate:instance]) + webViewIdentifier:webViewIdentifier + completion:completion]; } @end @@ -53,7 +121,64 @@ - (instancetype)initWithBinaryMessenger:(id)binaryMessen - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { [self.navigationDelegateAPI didFinishNavigationForDelegate:self webView:webView - URL:webView.URL.absoluteString]; + URL:webView.URL.absoluteString + completion:^(NSError *error) { + NSAssert(!error, @"%@", error); + }]; +} + +- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation { + [self.navigationDelegateAPI didStartProvisionalNavigationForDelegate:self + webView:webView + URL:webView.URL.absoluteString + completion:^(NSError *error) { + NSAssert(!error, @"%@", error); + }]; +} + +- (void)webView:(WKWebView *)webView + decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction + decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { + [self.navigationDelegateAPI + decidePolicyForNavigationActionForDelegate:self + webView:webView + navigationAction:navigationAction + completion:^(FWFWKNavigationActionPolicyEnumData *policy, + NSError *error) { + NSAssert(!error, @"%@", error); + decisionHandler( + FWFWKNavigationActionPolicyFromEnumData(policy)); + }]; +} + +- (void)webView:(WKWebView *)webView + didFailNavigation:(WKNavigation *)navigation + withError:(NSError *)error { + [self.navigationDelegateAPI didFailNavigationForDelegate:self + webView:webView + error:error + completion:^(NSError *error) { + NSAssert(!error, @"%@", error); + }]; +} + +- (void)webView:(WKWebView *)webView + didFailProvisionalNavigation:(WKNavigation *)navigation + withError:(NSError *)error { + [self.navigationDelegateAPI didFailProvisionalNavigationForDelegate:self + webView:webView + error:error + completion:^(NSError *error) { + NSAssert(!error, @"%@", error); + }]; +} + +- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView { + [self.navigationDelegateAPI webViewWebContentProcessDidTerminateForDelegate:self + webView:webView + completion:^(NSError *error) { + NSAssert(!error, @"%@", error); + }]; } @end 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 f1dbdbb20776..1f7b2943974e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.h @@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN * * Handles making callbacks to Dart for an NSObject. */ -@interface FWFObjectFlutterApi : FWFNSObjectFlutterApi +@interface FWFObjectFlutterApiImpl : FWFNSObjectFlutterApi - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; @end @@ -23,7 +23,7 @@ NS_ASSUME_NONNULL_BEGIN * Implementation of NSObject for FWFObjectHostApiImpl. */ @interface FWFObject : NSObject -@property(readonly, nonnull) FWFObjectFlutterApi *objectApi; +@property(readonly, nonnull, nonatomic) FWFObjectFlutterApiImpl *objectApi; - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m index 0cc4c5693342..f88b91750493 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m @@ -5,12 +5,12 @@ #import "FWFObjectHostApi.h" #import "FWFDataConverters.h" -@interface FWFObjectFlutterApi () +@interface FWFObjectFlutterApiImpl () // This reference must be weak to prevent a circular reference with the objects it stores. @property(nonatomic, weak) FWFInstanceManager *instanceManager; @end -@implementation FWFObjectFlutterApi +@implementation FWFObjectFlutterApiImpl - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager { self = [self initWithBinaryMessenger:binaryMessenger]; @@ -19,6 +19,33 @@ - (instancetype)initWithBinaryMessenger:(id)binaryMessen } return self; } + +- (long)identifierForObject:(NSObject *)instance { + return [self.instanceManager identifierWithStrongReferenceForInstance:instance]; +} + +- (void)observeValueForObject:(NSObject *)instance + keyPath:(NSString *)keyPath + object:(NSObject *)object + change:(NSDictionary *)change + completion:(void (^)(NSError *_Nullable))completion { + NSMutableArray *changeKeys = [NSMutableArray array]; + NSMutableArray *changeValues = [NSMutableArray array]; + + [change enumerateKeysAndObjectsUsingBlock:^(NSKeyValueChangeKey key, id value, BOOL *stop) { + [changeKeys addObject:FWFNSKeyValueChangeKeyEnumDataFromNSKeyValueChangeKey(key)]; + [changeValues addObject:value]; + }]; + + NSNumber *objectIdentifier = + @([self.instanceManager identifierWithStrongReferenceForInstance:object]); + [self observeValueForObjectWithIdentifier:@([self identifierForObject:instance]) + keyPath:keyPath + objectIdentifier:objectIdentifier + changeKeys:changeKeys + changeValues:changeValues + completion:completion]; +} @end @implementation FWFObject @@ -26,11 +53,24 @@ - (instancetype)initWithBinaryMessenger:(id)binaryMessen instanceManager:(FWFInstanceManager *)instanceManager { self = [self init]; if (self) { - _objectApi = [[FWFObjectFlutterApi alloc] initWithBinaryMessenger:binaryMessenger - instanceManager:instanceManager]; + _objectApi = [[FWFObjectFlutterApiImpl alloc] initWithBinaryMessenger:binaryMessenger + instanceManager:instanceManager]; } return self; } + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context { + [self.objectApi observeValueForObject:self + keyPath:keyPath + object:object + change:change + completion:^(NSError *error) { + NSAssert(!error, @"%@", error); + }]; +} @end @interface FWFObjectHostApiImpl () 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 7440862ce4e4..36f12feac3fe 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.h @@ -7,13 +7,28 @@ #import "FWFGeneratedWebKitApis.h" #import "FWFInstanceManager.h" +#import "FWFObjectHostApi.h" NS_ASSUME_NONNULL_BEGIN +/** + * 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. */ -@interface FWFScriptMessageHandler : NSObject +@interface FWFScriptMessageHandler : FWFObject +@property(readonly, nonnull, nonatomic) + FWFScriptMessageHandlerFlutterApiImpl *scriptMessageHandlerAPI; +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager; @end /** diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m index 9e4b5f003abc..69ace0202824 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m @@ -5,14 +5,64 @@ #import "FWFScriptMessageHandlerHostApi.h" #import "FWFDataConverters.h" +@interface FWFScriptMessageHandlerFlutterApiImpl () +// This reference must be weak to prevent a circular reference with the objects it stores. +@property(nonatomic, weak) FWFInstanceManager *instanceManager; +@end + +@implementation FWFScriptMessageHandlerFlutterApiImpl +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager { + self = [self initWithBinaryMessenger:binaryMessenger]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (long)identifierForHandler:(FWFScriptMessageHandler *)instance { + return [self.instanceManager identifierWithStrongReferenceForInstance:instance]; +} + +- (void)didReceiveScriptMessageForHandler:(FWFScriptMessageHandler *)instance + userContentController:(WKUserContentController *)userContentController + message:(WKScriptMessage *)message + completion:(void (^)(NSError *_Nullable))completion { + NSNumber *userContentControllerIdentifier = + @([self.instanceManager identifierWithStrongReferenceForInstance:userContentController]); + FWFWKScriptMessageData *messageData = FWFWKScriptMessageDataFromWKScriptMessage(message); + [self didReceiveScriptMessageForHandlerWithIdentifier:@([self identifierForHandler:instance]) + userContentControllerIdentifier:userContentControllerIdentifier + message:messageData + completion:completion]; +} +@end + @implementation FWFScriptMessageHandler +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager { + self = [super initWithBinaryMessenger:binaryMessenger instanceManager:instanceManager]; + if (self) { + _scriptMessageHandlerAPI = + [[FWFScriptMessageHandlerFlutterApiImpl alloc] initWithBinaryMessenger:binaryMessenger + instanceManager:instanceManager]; + } + return self; +} + - (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message { + [self.scriptMessageHandlerAPI didReceiveScriptMessageForHandler:self + userContentController:userContentController + message:message + completion:^(NSError *error) { + NSAssert(!error, @"%@", error); + }]; } @end @interface FWFScriptMessageHandlerHostApiImpl () -@property(nonatomic) FWFInstanceManager *instanceManager; +@property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @implementation FWFScriptMessageHandlerHostApiImpl 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 8795b00e9001..65c148e6b0b1 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h @@ -7,13 +7,27 @@ #import "FWFGeneratedWebKitApis.h" #import "FWFInstanceManager.h" +#import "FWFObjectHostApi.h" NS_ASSUME_NONNULL_BEGIN +/** + * Flutter api implementation for WKUIDelegate. + * + * Handles making callbacks to Dart for a WKUIDelegate. + */ +@interface FWFUIDelegateFlutterApiImpl : FWFWKUIDelegateFlutterApi +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager; +@end + /** * Implementation of WKUIDelegate for FWFUIDelegateHostApiImpl. */ -@interface FWFUIDelegate : NSObject +@interface FWFUIDelegate : FWFObject +@property(readonly, nonnull, nonatomic) FWFUIDelegateFlutterApiImpl *UIDelegateAPI; +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager; @end /** diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m index 28d0ab53aaad..4ec8b583b1f8 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m @@ -3,13 +3,75 @@ // found in the LICENSE file. #import "FWFUIDelegateHostApi.h" +#import "FWFDataConverters.h" #import "FWFWebViewConfigurationHostApi.h" +@interface FWFUIDelegateFlutterApiImpl () +// This reference must be weak to prevent a circular reference with the objects it stores. +@property(nonatomic, weak) FWFInstanceManager *instanceManager; +@end + +@implementation FWFUIDelegateFlutterApiImpl +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager { + self = [self initWithBinaryMessenger:binaryMessenger]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (long)identifierForDelegate:(FWFUIDelegate *)instance { + return [self.instanceManager identifierWithStrongReferenceForInstance:instance]; +} + +- (void)onCreateWebViewForDelegate:(FWFUIDelegate *)instance + webView:(WKWebView *)webView + configuration:(WKWebViewConfiguration *)configuration + navigationAction:(WKNavigationAction *)navigationAction + completion:(void (^)(NSError *_Nullable))completion { + NSNumber *configurationIdentifier = + @([self.instanceManager identifierWithStrongReferenceForInstance:configuration]); + FWFWKNavigationActionData *navigationActionData = + FWFWKNavigationActionDataFromNavigationAction(navigationAction); + [self onCreateWebViewForDelegateWithIdentifier:@([self identifierForDelegate:instance]) + webViewIdentifier: + @([self.instanceManager + identifierWithStrongReferenceForInstance:webView]) + configurationIdentifier:configurationIdentifier + navigationAction:navigationActionData + completion:completion]; +} +@end + @implementation FWFUIDelegate +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager { + self = [super initWithBinaryMessenger:binaryMessenger instanceManager:instanceManager]; + if (self) { + _UIDelegateAPI = [[FWFUIDelegateFlutterApiImpl alloc] initWithBinaryMessenger:binaryMessenger + instanceManager:instanceManager]; + } + return self; +} + +- (WKWebView *)webView:(WKWebView *)webView + createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration + forNavigationAction:(WKNavigationAction *)navigationAction + windowFeatures:(WKWindowFeatures *)windowFeatures { + [self.UIDelegateAPI onCreateWebViewForDelegate:self + webView:webView + configuration:configuration + navigationAction:navigationAction + completion:^(NSError *error) { + NSAssert(!error, @"%@", error); + }]; + return nil; +} @end @interface FWFUIDelegateHostApiImpl () -@property(nonatomic) FWFInstanceManager *instanceManager; +@property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @implementation FWFUIDelegateHostApiImpl From 3605dc71aeb39326411beed16dc39aa6498f74e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Jun 2022 14:03:10 -0700 Subject: [PATCH 408/844] [webview]: Bump mockito-inline from 3.11.1 to 4.6.1 in /packages/webview_flutter/webview_flutter_android/android (#5893) --- packages/webview_flutter/webview_flutter_android/CHANGELOG.md | 4 ++++ .../webview_flutter_android/android/build.gradle | 2 +- packages/webview_flutter/webview_flutter_android/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 8e715ccedbf5..66393c88cbb3 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.12 + +* Bumps mockito-inline from 3.11.1 to 4.6.1. + ## 2.8.11 * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). diff --git a/packages/webview_flutter/webview_flutter_android/android/build.gradle b/packages/webview_flutter/webview_flutter_android/android/build.gradle index 09579c4abc70..c7c6d992c823 100644 --- a/packages/webview_flutter/webview_flutter_android/android/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/android/build.gradle @@ -38,7 +38,7 @@ android { implementation 'androidx.annotation:annotation:1.0.0' implementation 'androidx.webkit:webkit:1.0.0' testImplementation 'junit:junit:4.12' - testImplementation 'org.mockito:mockito-inline:3.11.1' + testImplementation 'org.mockito:mockito-inline:4.6.1' testImplementation 'androidx.test:core:1.3.0' } diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 6195e4335d10..53f25c723fda 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/plugins/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: 2.8.11 +version: 2.8.12 environment: sdk: ">=2.14.0 <3.0.0" From 3d7d7af926537bc2bd7f90a3484b251db824f6b5 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 8 Jun 2022 17:08:10 -0400 Subject: [PATCH 409/844] Roll Flutter from be0c1bd0f0ce to 4ec296513cb2 (24 revisions) (#5935) --- .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 b5daa841e966..ea859f1df8f9 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -be0c1bd0f0cec3aa97c6be279b7beae9d0e8d6be +4ec296513cb23b089c27b383214dad2c7cfc025f From 7723022cdc6dfb4d5e502ec07ae8444bc9305c90 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 8 Jun 2022 17:53:09 -0400 Subject: [PATCH 410/844] [tools] Check integration tests for `test` (#5936) --- .../integration_test/path_provider_test.dart | 3 +- .../integration_test/path_provider_test.dart | 3 +- script/tool/CHANGELOG.md | 2 + .../tool/lib/src/drive_examples_command.dart | 30 +++++++++++++- .../test/drive_examples_command_test.dart | 41 +++++++++++++++++++ 5 files changed, 76 insertions(+), 3 deletions(-) diff --git a/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart b/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart index 4f5a1873bfb8..6b3dd65fcb14 100644 --- a/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart +++ b/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart @@ -70,7 +70,8 @@ void main() { ]; for (final StorageDirectory? type in _allDirs) { - test('getExternalStorageDirectories (type: $type)', () async { + testWidgets('getExternalStorageDirectories (type: $type)', + (WidgetTester tester) async { if (Platform.isIOS) { final Future?> result = getExternalStorageDirectories(type: null); diff --git a/packages/path_provider/path_provider_android/example/integration_test/path_provider_test.dart b/packages/path_provider/path_provider_android/example/integration_test/path_provider_test.dart index 426b07abc7c1..0538738ade7e 100644 --- a/packages/path_provider/path_provider_android/example/integration_test/path_provider_test.dart +++ b/packages/path_provider/path_provider_android/example/integration_test/path_provider_test.dart @@ -61,7 +61,8 @@ void main() { ]; for (final StorageDirectory? type in _allDirs) { - test('getExternalStorageDirectories (type: $type)', () async { + testWidgets('getExternalStorageDirectories (type: $type)', + (WidgetTester tester) async { final PathProviderPlatform provider = PathProviderPlatform.instance; final List? directories = diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index adc7bfcd29ae..36d8d23eb753 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,6 +1,8 @@ ## NEXT - Supports empty custom analysis allow list files. +- `drive-examples` now validates files to ensure that they don't accidentally + use `test(...)`. ## 0.8.6 diff --git a/script/tool/lib/src/drive_examples_command.dart b/script/tool/lib/src/drive_examples_command.dart index 15366e17ae85..45e20c0f13cf 100644 --- a/script/tool/lib/src/drive_examples_command.dart +++ b/script/tool/lib/src/drive_examples_command.dart @@ -182,7 +182,16 @@ class DriveExamplesCommand extends PackageLoopingCommand { if (legacyTestFile != null) { testTargets.add(legacyTestFile); } else { - (await _getIntegrationTests(example)).forEach(testTargets.add); + for (final File testFile in await _getIntegrationTests(example)) { + // Check files for known problematic patterns. + final bool passesValidation = _validateIntegrationTest(testFile); + if (!passesValidation) { + // Report the issue, but continue with the test as the validation + // errors don't prevent running. + errors.add('${testFile.basename} failed validation'); + } + testTargets.add(testFile); + } } if (testTargets.isEmpty) { @@ -310,6 +319,25 @@ class DriveExamplesCommand extends PackageLoopingCommand { return tests; } + /// Checks [testFile] for known bad patterns in integration tests, logging + /// any issues. + /// + /// Returns true if the file passes validation without issues. + bool _validateIntegrationTest(File testFile) { + final List lines = testFile.readAsLinesSync(); + + final RegExp badTestPattern = RegExp(r'\s*test\('); + if (lines.any((String line) => line.startsWith(badTestPattern))) { + final String filename = testFile.basename; + printError( + '$filename uses "test", which will not report failures correctly. ' + 'Use testWidgets instead.'); + return false; + } + + return true; + } + /// For each file in [targets], uses /// `flutter drive --driver [driver] --target ` /// to drive [example], returning a list of any failing test targets. diff --git a/script/tool/test/drive_examples_command_test.dart b/script/tool/test/drive_examples_command_test.dart index 23318f7cd604..0b6082098ae8 100644 --- a/script/tool/test/drive_examples_command_test.dart +++ b/script/tool/test/drive_examples_command_test.dart @@ -307,6 +307,47 @@ void main() { ); }); + test('integration tests using test(...) fail validation', () async { + setMockFlutterDevicesOutput(); + final RepositoryPackage package = createFakePlugin( + 'plugin', + packagesDir, + extraFiles: [ + 'example/test_driver/integration_test.dart', + 'example/integration_test/foo_test.dart', + 'example/android/android.java', + ], + platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline), + platformIOS: const PlatformDetails(PlatformSupport.inline), + }, + ); + package.directory + .childDirectory('example') + .childDirectory('integration_test') + .childFile('foo_test.dart') + .writeAsStringSync(''' + test('this is the wrong kind of test!'), () { + ... + } +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['drive-examples', '--android'], + errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('foo_test.dart failed validation'), + ]), + ); + }); + test( 'driving under folder "test_driver" when targets are under "integration_test"', () async { From 2be688d4871458af5d636b71c3c7ea4c291e66bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Jun 2022 04:02:03 -0700 Subject: [PATCH 411/844] [gh_actions]: Bump github/codeql-action from 1.1.4 to 2.1.12 (#5924) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 1.1.4 to 2.1.12. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/f5d822707ee6e8fb81b04a5c0040b736da22e587...27ea8f8fe5977c00f5b37e076ab846c5bd783b96) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .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 5e695cd86e25..7f2a4d8ec44e 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -48,6 +48,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@f5d822707ee6e8fb81b04a5c0040b736da22e587 + uses: github/codeql-action/upload-sarif@27ea8f8fe5977c00f5b37e076ab846c5bd783b96 with: sarif_file: results.sarif From aa6c864d2acb274f5bacaff67a0595db9c7f1986 Mon Sep 17 00:00:00 2001 From: Alexandre Ardhuin Date: Thu, 9 Jun 2022 13:43:08 +0200 Subject: [PATCH 412/844] Ignore upcoming warnings (#5939) --- packages/url_launcher/url_launcher/CHANGELOG.md | 4 ++++ .../url_launcher/url_launcher/test/src/legacy_api_test.dart | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index d2c4206718ec..182119345ccc 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/105648). + ## 6.1.3 * Updates README section about query permissions to better reflect changes to 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 e94f1847ef51..11d7d8f17c09 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 @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:ui'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#105648) +// ignore: unnecessary_import +import 'dart:ui' show Brightness; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart' show PlatformException; From 1b26fb1d76cd51e4cf6cdb341e2127385e6567b1 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 9 Jun 2022 11:23:14 -0400 Subject: [PATCH 413/844] [camera] Federate mobile implementations (#5937) --- packages/camera/camera/CHANGELOG.md | 1 + .../camera/camera/android/settings.gradle | 1 - packages/camera/camera/example/ios/Podfile | 7 - .../ios/Runner.xcodeproj/project.pbxproj | 240 ---- packages/camera/camera/pubspec.yaml | 13 +- packages/camera/camera_android/AUTHORS | 66 + packages/camera/camera_android/CHANGELOG.md | 3 + packages/camera/camera_android/LICENSE | 25 + packages/camera/camera_android/README.md | 11 + .../android/build.gradle | 0 .../android/lint-baseline.xml | 0 .../camera_android/android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 0 .../io/flutter/plugins/camera/Camera.java | 0 .../plugins/camera/CameraCaptureCallback.java | 0 .../plugins/camera/CameraPermissions.java | 0 .../flutter/plugins/camera/CameraPlugin.java | 0 .../plugins/camera/CameraProperties.java | 0 .../plugins/camera/CameraRegionUtils.java | 0 .../flutter/plugins/camera/CameraState.java | 0 .../flutter/plugins/camera/CameraUtils.java | 0 .../io/flutter/plugins/camera/CameraZoom.java | 0 .../flutter/plugins/camera/DartMessenger.java | 0 .../io/flutter/plugins/camera/ImageSaver.java | 0 .../plugins/camera/MethodCallHandlerImpl.java | 0 .../camera/features/CameraFeature.java | 0 .../camera/features/CameraFeatureFactory.java | 0 .../features/CameraFeatureFactoryImpl.java | 0 .../camera/features/CameraFeatures.java | 0 .../plugins/camera/features/Point.java | 0 .../features/autofocus/AutoFocusFeature.java | 0 .../camera/features/autofocus/FocusMode.java | 0 .../exposurelock/ExposureLockFeature.java | 0 .../features/exposurelock/ExposureMode.java | 0 .../exposureoffset/ExposureOffsetFeature.java | 0 .../exposurepoint/ExposurePointFeature.java | 0 .../camera/features/flash/FlashFeature.java | 0 .../camera/features/flash/FlashMode.java | 0 .../focuspoint/FocusPointFeature.java | 0 .../features/fpsrange/FpsRangeFeature.java | 0 .../noisereduction/NoiseReductionFeature.java | 0 .../noisereduction/NoiseReductionMode.java | 0 .../resolution/ResolutionFeature.java | 0 .../features/resolution/ResolutionPreset.java | 0 .../DeviceOrientationManager.java | 0 .../SensorOrientationFeature.java | 0 .../features/zoomlevel/ZoomLevelFeature.java | 0 .../camera/features/zoomlevel/ZoomUtils.java | 0 .../camera/media/MediaRecorderBuilder.java | 0 .../camera/types/CameraCaptureProperties.java | 0 .../camera/types/CaptureTimeoutsWrapper.java | 0 .../plugins/camera/types/ExposureMode.java | 0 .../plugins/camera/types/FlashMode.java | 0 .../plugins/camera/types/FocusMode.java | 0 .../camera/types/ResolutionPreset.java | 0 .../flutter/plugins/camera/types/Timeout.java | 0 .../CameraCaptureCallbackStatesTest.java | 0 .../camera/CameraCaptureCallbackTest.java | 0 .../plugins/camera/CameraPermissionsTest.java | 0 .../camera/CameraPropertiesImplTest.java | 0 ...s_convertPointToMeteringRectangleTest.java | 0 ...raRegionUtils_getCameraBoundariesTest.java | 0 .../io/flutter/plugins/camera/CameraTest.java | 0 .../CameraTest_getRecordingProfileTest.java | 0 .../plugins/camera/CameraUtilsTest.java | 0 .../plugins/camera/CameraZoomTest.java | 0 .../plugins/camera/DartMessengerTest.java | 0 .../plugins/camera/ImageSaverTests.java | 0 .../camera/MethodCallHandlerImplTest.java | 0 .../autofocus/AutoFocusFeatureTest.java | 0 .../features/autofocus/FocusModeTest.java | 0 .../exposurelock/ExposureLockFeatureTest.java | 0 .../exposurelock/ExposureModeTest.java | 0 .../ExposureOffsetFeatureTest.java | 0 .../ExposurePointFeatureTest.java | 0 .../features/flash/FlashFeatureTest.java | 0 .../focuspoint/FocusPointFeatureTest.java | 0 .../fpsrange/FpsRangeFeaturePixel4aTest.java | 0 .../fpsrange/FpsRangeFeatureTest.java | 0 .../NoiseReductionFeatureTest.java | 0 .../resolution/ResolutionFeatureTest.java | 0 .../DeviceOrientationManagerTest.java | 0 .../SensorOrientationFeatureTest.java | 0 .../zoomlevel/ZoomLevelFeatureTest.java | 0 .../features/zoomlevel/ZoomUtilsTest.java | 0 .../media/MediaRecorderBuilderTest.java | 0 .../camera/types/ExposureModeTest.java | 0 .../plugins/camera/types/FlashModeTest.java | 0 .../plugins/camera/types/FocusModeTest.java | 0 .../plugins/camera/utils/TestUtils.java | 0 .../src/test/resources/robolectric.properties | 0 .../example/android/app/build.gradle | 64 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../flutter/plugins/DartIntegrationTest.java | 14 + .../cameraexample/FlutterActivityTest.java | 19 + .../android/app/src/main/AndroidManifest.xml | 28 + .../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/styles.xml | 8 + .../example/android/build.gradle | 29 + .../example/android/gradle.properties | 4 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../example/android/settings.gradle | 15 + .../example/integration_test/camera_test.dart | 246 ++++ .../example/lib/camera_controller.dart | 437 +++++++ .../example/lib/camera_preview.dart | 85 ++ .../camera_android/example/lib/main.dart | 1095 +++++++++++++++++ .../camera_android/example/pubspec.yaml | 33 + .../example/test_driver/integration_test.dart | 64 + packages/camera/camera_android/pubspec.yaml | 29 + packages/camera/camera_avfoundation/AUTHORS | 66 + .../camera/camera_avfoundation/CHANGELOG.md | 3 + packages/camera/camera_avfoundation/LICENSE | 25 + packages/camera/camera_avfoundation/README.md | 11 + .../example/integration_test/camera_test.dart | 254 ++++ .../ios/Flutter/AppFrameworkInfo.plist | 30 + .../example/ios/Flutter/Debug.xcconfig | 3 + .../example/ios/Flutter/Release.xcconfig | 3 + .../camera_avfoundation/example/ios/Podfile | 45 + .../ios/Runner.xcodeproj/project.pbxproj | 712 +++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/xcschemes/Runner.xcscheme | 104 ++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/ios/Runner/AppDelegate.h | 10 + .../example/ios/Runner/AppDelegate.m | 17 + .../AppIcon.appiconset/Contents.json | 121 ++ .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 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 .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../example/ios/Runner/Info.plist | 56 + .../example/ios/Runner/main.m | 19 + .../ios/RunnerTests/AvailableCamerasTest.m | 4 +- ...eraCaptureSessionQueueRaceConditionTests.m | 4 +- .../ios/RunnerTests/CameraExposureTests.m | 2 +- .../ios/RunnerTests/CameraFocusTests.m | 4 +- .../RunnerTests/CameraMethodChannelTests.m | 4 +- .../ios/RunnerTests/CameraOrientationTests.m | 4 +- .../ios/RunnerTests/CameraPermissionTests.m | 4 +- .../ios/RunnerTests/CameraPreviewPauseTests.m | 4 +- .../ios/RunnerTests/CameraPropertiesTests.m | 2 +- .../example/ios/RunnerTests/CameraTestUtils.h | 2 +- .../example/ios/RunnerTests/CameraTestUtils.m | 0 .../example/ios/RunnerTests/CameraUtilTests.m | 2 +- .../ios/RunnerTests/FLTCamPhotoCaptureTests.m | 4 +- .../ios/RunnerTests/FLTCamSampleBufferTests.m | 4 +- .../RunnerTests/FLTSavePhotoDelegateTests.m | 4 +- .../example/ios/RunnerTests/Info.plist | 0 .../MockFLTThreadSafeFlutterResult.h | 0 .../MockFLTThreadSafeFlutterResult.m | 2 +- .../example/ios/RunnerTests/QueueUtilsTests.m | 2 +- .../example/ios/RunnerTests/StreamingTest.m | 4 +- .../RunnerTests/ThreadSafeEventChannelTests.m | 2 +- .../ThreadSafeFlutterResultTests.m | 2 +- .../ThreadSafeMethodChannelTests.m | 2 +- .../ThreadSafeTextureRegistryTests.m | 2 +- .../example/lib/camera_controller.dart | 437 +++++++ .../example/lib/camera_preview.dart | 85 ++ .../camera_avfoundation/example/lib/main.dart | 1095 +++++++++++++++++ .../camera_avfoundation/example/pubspec.yaml | 33 + .../example/test_driver/integration_test.dart | 64 + .../ios/Assets/.gitkeep | 0 .../ios/Classes/CameraPermissionUtils.h | 0 .../ios/Classes/CameraPermissionUtils.m | 0 .../ios/Classes/CameraPlugin.h | 0 .../ios/Classes/CameraPlugin.m | 0 .../ios/Classes/CameraPlugin.modulemap | 4 +- .../ios/Classes/CameraPlugin_Test.h | 8 +- .../ios/Classes/CameraProperties.h | 0 .../ios/Classes/CameraProperties.m | 0 .../ios/Classes/FLTCam.h | 0 .../ios/Classes/FLTCam.m | 0 .../ios/Classes/FLTCam_Test.h | 0 .../ios/Classes/FLTSavePhotoDelegate.h | 0 .../ios/Classes/FLTSavePhotoDelegate.m | 0 .../ios/Classes/FLTSavePhotoDelegate_Test.h | 0 .../ios/Classes/FLTThreadSafeEventChannel.h | 0 .../ios/Classes/FLTThreadSafeEventChannel.m | 0 .../ios/Classes/FLTThreadSafeFlutterResult.h | 0 .../ios/Classes/FLTThreadSafeFlutterResult.m | 0 .../ios/Classes/FLTThreadSafeMethodChannel.h | 0 .../ios/Classes/FLTThreadSafeMethodChannel.m | 0 .../Classes/FLTThreadSafeTextureRegistry.h | 0 .../Classes/FLTThreadSafeTextureRegistry.m | 0 .../ios/Classes/QueueUtils.h | 0 .../ios/Classes/QueueUtils.m | 0 .../Classes/camera_avfoundation-umbrella.h} | 2 +- .../ios/camera_avfoundation.podspec} | 8 +- .../camera/camera_avfoundation/pubspec.yaml | 27 + 211 files changed, 5688 insertions(+), 294 deletions(-) delete mode 100644 packages/camera/camera/android/settings.gradle create mode 100644 packages/camera/camera_android/AUTHORS create mode 100644 packages/camera/camera_android/CHANGELOG.md create mode 100644 packages/camera/camera_android/LICENSE create mode 100644 packages/camera/camera_android/README.md rename packages/camera/{camera => camera_android}/android/build.gradle (100%) rename packages/camera/{camera => camera_android}/android/lint-baseline.xml (100%) create mode 100644 packages/camera/camera_android/android/settings.gradle rename packages/camera/{camera => camera_android}/android/src/main/AndroidManifest.xml (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/Camera.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/CameraCaptureCallback.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/CameraState.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/CameraZoom.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/ImageSaver.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/CameraFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactory.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactoryImpl.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatures.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/Point.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/autofocus/FocusMode.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureMode.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashMode.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionMode.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionPreset.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManager.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeature.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtils.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/media/MediaRecorderBuilder.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/types/CameraCaptureProperties.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/types/CaptureTimeoutsWrapper.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/types/ExposureMode.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/types/FlashMode.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/types/FocusMode.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/types/ResolutionPreset.java (100%) rename packages/camera/{camera => camera_android}/android/src/main/java/io/flutter/plugins/camera/types/Timeout.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackStatesTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraPermissionsTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraPropertiesImplTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_convertPointToMeteringRectangleTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_getCameraBoundariesTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraUtilsTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/CameraZoomTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/DartMessengerTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/ImageSaverTests.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/MethodCallHandlerImplTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/autofocus/FocusModeTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureModeTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/flash/FlashFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeaturePixel4aTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/resolution/ResolutionFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeatureTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtilsTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/media/MediaRecorderBuilderTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/types/ExposureModeTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/types/FlashModeTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/types/FocusModeTest.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/java/io/flutter/plugins/camera/utils/TestUtils.java (100%) rename packages/camera/{camera => camera_android}/android/src/test/resources/robolectric.properties (100%) create mode 100644 packages/camera/camera_android/example/android/app/build.gradle create mode 100644 packages/camera/camera_android/example/android/app/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/camera/camera_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java create mode 100644 packages/camera/camera_android/example/android/app/src/androidTest/java/io/flutter/plugins/cameraexample/FlutterActivityTest.java create mode 100644 packages/camera/camera_android/example/android/app/src/main/AndroidManifest.xml create mode 100644 packages/camera/camera_android/example/android/app/src/main/res/drawable/launch_background.xml create mode 100644 packages/camera/camera_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 packages/camera/camera_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 packages/camera/camera_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 packages/camera/camera_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 packages/camera/camera_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 packages/camera/camera_android/example/android/app/src/main/res/values/styles.xml create mode 100644 packages/camera/camera_android/example/android/build.gradle create mode 100644 packages/camera/camera_android/example/android/gradle.properties create mode 100644 packages/camera/camera_android/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/camera/camera_android/example/android/settings.gradle create mode 100644 packages/camera/camera_android/example/integration_test/camera_test.dart create mode 100644 packages/camera/camera_android/example/lib/camera_controller.dart create mode 100644 packages/camera/camera_android/example/lib/camera_preview.dart create mode 100644 packages/camera/camera_android/example/lib/main.dart create mode 100644 packages/camera/camera_android/example/pubspec.yaml create mode 100644 packages/camera/camera_android/example/test_driver/integration_test.dart create mode 100644 packages/camera/camera_android/pubspec.yaml create mode 100644 packages/camera/camera_avfoundation/AUTHORS create mode 100644 packages/camera/camera_avfoundation/CHANGELOG.md create mode 100644 packages/camera/camera_avfoundation/LICENSE create mode 100644 packages/camera/camera_avfoundation/README.md create mode 100644 packages/camera/camera_avfoundation/example/integration_test/camera_test.dart create mode 100644 packages/camera/camera_avfoundation/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 packages/camera/camera_avfoundation/example/ios/Flutter/Debug.xcconfig create mode 100644 packages/camera/camera_avfoundation/example/ios/Flutter/Release.xcconfig create mode 100644 packages/camera/camera_avfoundation/example/ios/Podfile create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/AppDelegate.h create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/AppDelegate.m create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/Info.plist create mode 100644 packages/camera/camera_avfoundation/example/ios/Runner/main.m rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/AvailableCamerasTest.m (98%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m (97%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraExposureTests.m (98%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraFocusTests.m (98%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraMethodChannelTests.m (96%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraOrientationTests.m (98%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraPermissionTests.m (99%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraPreviewPauseTests.m (93%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraPropertiesTests.m (99%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraTestUtils.h (95%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraTestUtils.m (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/CameraUtilTests.m (98%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m (98%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/FLTCamSampleBufferTests.m (96%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m (98%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/Info.plist (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.h (100%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.m (95%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/QueueUtilsTests.m (97%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/StreamingTest.m (98%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/ThreadSafeEventChannelTests.m (98%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m (99%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m (98%) rename packages/camera/{camera => camera_avfoundation}/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m (99%) create mode 100644 packages/camera/camera_avfoundation/example/lib/camera_controller.dart create mode 100644 packages/camera/camera_avfoundation/example/lib/camera_preview.dart create mode 100644 packages/camera/camera_avfoundation/example/lib/main.dart create mode 100644 packages/camera/camera_avfoundation/example/pubspec.yaml create mode 100644 packages/camera/camera_avfoundation/example/test_driver/integration_test.dart rename packages/camera/{camera => camera_avfoundation}/ios/Assets/.gitkeep (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/CameraPermissionUtils.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/CameraPermissionUtils.m (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/CameraPlugin.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/CameraPlugin.m (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/CameraPlugin.modulemap (83%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/CameraPlugin_Test.h (94%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/CameraProperties.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/CameraProperties.m (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTCam.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTCam.m (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTCam_Test.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTSavePhotoDelegate.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTSavePhotoDelegate.m (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTSavePhotoDelegate_Test.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTThreadSafeEventChannel.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTThreadSafeEventChannel.m (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTThreadSafeFlutterResult.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTThreadSafeFlutterResult.m (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTThreadSafeMethodChannel.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTThreadSafeMethodChannel.m (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTThreadSafeTextureRegistry.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/FLTThreadSafeTextureRegistry.m (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/QueueUtils.h (100%) rename packages/camera/{camera => camera_avfoundation}/ios/Classes/QueueUtils.m (100%) rename packages/camera/{camera/ios/Classes/camera-umbrella.h => camera_avfoundation/ios/Classes/camera_avfoundation-umbrella.h} (87%) rename packages/camera/{camera/ios/camera.podspec => camera_avfoundation/ios/camera_avfoundation.podspec} (76%) create mode 100644 packages/camera/camera_avfoundation/pubspec.yaml diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 3a8bf2ee0c1a..8f13ff38f47f 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Moves Android and iOS implementations to federated packages. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). ## 0.9.7+1 diff --git a/packages/camera/camera/android/settings.gradle b/packages/camera/camera/android/settings.gradle deleted file mode 100644 index 5222c9172f70..000000000000 --- a/packages/camera/camera/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'camera' diff --git a/packages/camera/camera/example/ios/Podfile b/packages/camera/camera/example/ios/Podfile index 5bc7b7e85717..f7d6a5e68c3a 100644 --- a/packages/camera/camera/example/ios/Podfile +++ b/packages/camera/camera/example/ios/Podfile @@ -29,13 +29,6 @@ flutter_ios_podfile_setup target 'Runner' do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) - - target 'RunnerTests' do - platform :ios, '9.0' - inherit! :search_paths - # Pods for testing - pod 'OCMock', '~> 3.8.1' - end end post_install do |installer| diff --git a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj index 03c80d79c578..99433b084f27 100644 --- a/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/camera/camera/example/ios/Runner.xcodeproj/project.pbxproj @@ -7,46 +7,16 @@ objects = { /* Begin PBXBuildFile section */ - 033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */; }; - 03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB766A2665316900CE5A93 /* CameraFocusTests.m */; }; - 03F6F8B226CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03F6F8B126CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 236906D1621AE863A5B2E770 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 89D82918721FABF772705DB0 /* libPods-Runner.a */; }; - 25C3919135C3D981E6F800D0 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */; }; - 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 43ED1537282570DE00EB00DE /* AvailableCamerasTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */; }; - 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 788A065927B0E02900533D74 /* StreamingTest.m */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 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 */; }; - 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 */; }; - E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */; }; - E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */; }; - E0B0D2BB27DFF2AF00E71E4B /* CameraPermissionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */; }; - E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */; }; - E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */; }; - E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; - E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */; }; - E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */; }; - E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */; }; - F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */ = {isa = PBXBuildFile; fileRef = F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - 03BB766D2665316900CE5A93 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; @@ -61,20 +31,12 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraMethodChannelTests.m; sourceTree = ""; }; - 03BB76682665316900CE5A93 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 03BB766A2665316900CE5A93 /* CameraFocusTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraFocusTests.m; sourceTree = ""; }; - 03BB766C2665316900CE5A93 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraOrientationTests.m; sourceTree = ""; }; - 03F6F8B126CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeFlutterResultTests.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 = ""; }; 14AE82C910C2A12F2ECB2094 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AvailableCamerasTest.m; sourceTree = ""; }; 59848A7CA98C1FADF8840207 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 788A065927B0E02900533D74 /* StreamingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StreamingTest.m; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -89,32 +51,9 @@ 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 = ""; }; - 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 = ""; }; - E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTCamPhotoCaptureTests.m; sourceTree = ""; }; - E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTCamSampleBufferTests.m; sourceTree = ""; }; - E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPermissionTests.m; sourceTree = ""; }; - E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeMethodChannelTests.m; sourceTree = ""; }; - E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeTextureRegistryTests.m; sourceTree = ""; }; - E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = ""; }; - E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CameraTestUtils.h; sourceTree = ""; }; - E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraTestUtils.m; sourceTree = ""; }; - E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPropertiesTests.m; sourceTree = ""; }; - E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPreviewPauseTests.m; sourceTree = ""; }; - F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockFLTThreadSafeFlutterResult.h; sourceTree = ""; }; - F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockFLTThreadSafeFlutterResult.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 03BB76652665316900CE5A93 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 25C3919135C3D981E6F800D0 /* libPods-RunnerTests.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -126,35 +65,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 03BB76692665316900CE5A93 /* RunnerTests */ = { - isa = PBXGroup; - children = ( - 03BB766A2665316900CE5A93 /* CameraFocusTests.m */, - 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */, - 03BB766C2665316900CE5A93 /* Info.plist */, - 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */, - 03F6F8B126CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m */, - E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */, - E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */, - E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */, - E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */, - E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */, - E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */, - E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */, - E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */, - E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */, - E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */, - E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */, - F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */, - F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */, - E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */, - E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */, - 788A065927B0E02900533D74 /* StreamingTest.m */, - 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */, - ); - path = RunnerTests; - sourceTree = ""; - }; 3242FD2B467C15C62200632F /* Frameworks */ = { isa = PBXGroup; children = ( @@ -180,7 +90,6 @@ children = ( 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, - 03BB76692665316900CE5A93 /* RunnerTests */, 97C146EF1CF9000F007C117D /* Products */, FD386F00E98D73419C929072 /* Pods */, 3242FD2B467C15C62200632F /* Frameworks */, @@ -191,7 +100,6 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, - 03BB76682665316900CE5A93 /* RunnerTests.xctest */, ); name = Products; sourceTree = ""; @@ -234,25 +142,6 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 03BB76672665316900CE5A93 /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 03BB76712665316900CE5A93 /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - 422786A96136AA9087A2041B /* [CP] Check Pods Manifest.lock */, - 03BB76642665316900CE5A93 /* Sources */, - 03BB76652665316900CE5A93 /* Frameworks */, - 03BB76662665316900CE5A93 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 03BB766E2665316900CE5A93 /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = camera_exampleTests; - productReference = 03BB76682665316900CE5A93 /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; 97C146ED1CF9000F007C117D /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; @@ -283,11 +172,6 @@ LastUpgradeCheck = 1300; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { - 03BB76672665316900CE5A93 = { - CreatedOnToolsVersion = 12.5; - ProvisioningStyle = Automatic; - TestTargetID = 97C146ED1CF9000F007C117D; - }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; }; @@ -307,19 +191,11 @@ projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, - 03BB76672665316900CE5A93 /* RunnerTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 03BB76662665316900CE5A93 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EC1CF9000F007C117D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -348,28 +224,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 422786A96136AA9087A2041B /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -409,32 +263,6 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 03BB76642665316900CE5A93 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 03F6F8B226CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m in Sources */, - 033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */, - E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */, - E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */, - 03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */, - E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */, - E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */, - E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */, - 43ED1537282570DE00EB00DE /* AvailableCamerasTest.m in Sources */, - F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */, - E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */, - 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */, - E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */, - 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */, - E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, - E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */, - E0B0D2BB27DFF2AF00E71E4B /* CameraPermissionTests.m in Sources */, - E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */, - E01EE4A82799F3A5008C1950 /* QueueUtilsTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EA1CF9000F007C117D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -447,14 +275,6 @@ }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 03BB766E2665316900CE5A93 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = 03BB766D2665316900CE5A93 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -475,57 +295,6 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - 03BB766F2665316900CE5A93 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9C5CC6CAD53AD388B2694F3A /* Pods-RunnerTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = ""; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = RunnerTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "dev.flutter.plugins.cameraExample.camera-exampleTests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Debug; - }; - 03BB76702665316900CE5A93 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = ""; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = RunnerTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "dev.flutter.plugins.cameraExample.camera-exampleTests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; - }; - name = Release; - }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -679,15 +448,6 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 03BB76712665316900CE5A93 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 03BB766F2665316900CE5A93 /* Debug */, - 03BB76702665316900CE5A93 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index d1f70d906626..dd2ce04d2d61 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -5,6 +5,9 @@ description: A Flutter plugin for controlling the camera. Supports previewing repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 version: 0.9.7+1 +# Temporarily disable publishing to allow moving Android and iOS +# implementations. +publish_to: none environment: sdk: ">=2.14.0 <3.0.0" @@ -14,14 +17,18 @@ flutter: plugin: platforms: android: - package: io.flutter.plugins.camera - pluginClass: CameraPlugin + default_package: camera_android ios: - pluginClass: CameraPlugin + default_package: camera_avfoundation web: default_package: camera_web dependencies: + # Temporary path dependencies to allow moving Android and iOS implementations. + camera_android: + path: ../camera_android + camera_avfoundation: + path: ../camera_avfoundation camera_platform_interface: ^2.2.0 camera_web: ^0.2.1 flutter: diff --git a/packages/camera/camera_android/AUTHORS b/packages/camera/camera_android/AUTHORS new file mode 100644 index 000000000000..493a0b4ef9c2 --- /dev/null +++ b/packages/camera/camera_android/AUTHORS @@ -0,0 +1,66 @@ +# 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. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md new file mode 100644 index 000000000000..c57f301fae95 --- /dev/null +++ b/packages/camera/camera_android/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.9.7+1 + +* Splits from `camera` as a federated implementation. diff --git a/packages/camera/camera_android/LICENSE b/packages/camera/camera_android/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/camera/camera_android/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/camera/camera_android/README.md b/packages/camera/camera_android/README.md new file mode 100644 index 000000000000..de8897c1727a --- /dev/null +++ b/packages/camera/camera_android/README.md @@ -0,0 +1,11 @@ +# camera\_android + +The Android implementation of [`camera`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `camera` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/camera +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/camera/camera/android/build.gradle b/packages/camera/camera_android/android/build.gradle similarity index 100% rename from packages/camera/camera/android/build.gradle rename to packages/camera/camera_android/android/build.gradle diff --git a/packages/camera/camera/android/lint-baseline.xml b/packages/camera/camera_android/android/lint-baseline.xml similarity index 100% rename from packages/camera/camera/android/lint-baseline.xml rename to packages/camera/camera_android/android/lint-baseline.xml diff --git a/packages/camera/camera_android/android/settings.gradle b/packages/camera/camera_android/android/settings.gradle new file mode 100644 index 000000000000..94a1bae9d6cd --- /dev/null +++ b/packages/camera/camera_android/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'camera_android' diff --git a/packages/camera/camera/android/src/main/AndroidManifest.xml b/packages/camera/camera_android/android/src/main/AndroidManifest.xml similarity index 100% rename from packages/camera/camera/android/src/main/AndroidManifest.xml rename to packages/camera/camera_android/android/src/main/AndroidManifest.xml diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/Camera.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraCaptureCallback.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraCaptureCallback.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraCaptureCallback.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraCaptureCallback.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraState.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraState.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraState.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraState.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraUtils.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraZoom.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraZoom.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraZoom.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraZoom.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/ImageSaver.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/ImageSaver.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/ImageSaver.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/ImageSaver.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/CameraFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/CameraFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactory.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactory.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactory.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactory.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactoryImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactoryImpl.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactoryImpl.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatureFactoryImpl.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatures.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatures.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatures.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/CameraFeatures.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/Point.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/Point.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/Point.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/Point.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/autofocus/FocusMode.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/autofocus/FocusMode.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/autofocus/FocusMode.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/autofocus/FocusMode.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureMode.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureMode.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureMode.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposurelock/ExposureMode.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashMode.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashMode.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashMode.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/flash/FlashMode.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionMode.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionMode.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionMode.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionMode.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionPreset.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionPreset.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionPreset.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/resolution/ResolutionPreset.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManager.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManager.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManager.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManager.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeature.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeature.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeature.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtils.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtils.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtils.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtils.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/media/MediaRecorderBuilder.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/MediaRecorderBuilder.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/media/MediaRecorderBuilder.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/media/MediaRecorderBuilder.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/CameraCaptureProperties.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CameraCaptureProperties.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/CameraCaptureProperties.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CameraCaptureProperties.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/CaptureTimeoutsWrapper.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CaptureTimeoutsWrapper.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/CaptureTimeoutsWrapper.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/CaptureTimeoutsWrapper.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/ExposureMode.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/ExposureMode.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/ExposureMode.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/ExposureMode.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/FlashMode.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/FlashMode.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/FlashMode.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/FlashMode.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/FocusMode.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/FocusMode.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/FocusMode.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/FocusMode.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/ResolutionPreset.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/ResolutionPreset.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/ResolutionPreset.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/ResolutionPreset.java diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/Timeout.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/Timeout.java similarity index 100% rename from packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/Timeout.java rename to packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/types/Timeout.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackStatesTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackStatesTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackStatesTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackStatesTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraCaptureCallbackTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraPermissionsTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraPermissionsTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraPermissionsTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraPermissionsTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraPropertiesImplTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraPropertiesImplTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraPropertiesImplTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraPropertiesImplTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_convertPointToMeteringRectangleTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_convertPointToMeteringRectangleTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_convertPointToMeteringRectangleTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_convertPointToMeteringRectangleTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_getCameraBoundariesTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_getCameraBoundariesTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_getCameraBoundariesTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtils_getCameraBoundariesTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest_getRecordingProfileTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraUtilsTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraUtilsTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraUtilsTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraUtilsTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraZoomTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraZoomTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraZoomTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraZoomTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/DartMessengerTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/DartMessengerTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/DartMessengerTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/DartMessengerTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/ImageSaverTests.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/ImageSaverTests.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/ImageSaverTests.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/ImageSaverTests.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/MethodCallHandlerImplTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/MethodCallHandlerImplTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/MethodCallHandlerImplTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/MethodCallHandlerImplTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/autofocus/AutoFocusFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/autofocus/FocusModeTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/autofocus/FocusModeTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/autofocus/FocusModeTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/autofocus/FocusModeTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureLockFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureModeTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureModeTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureModeTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposurelock/ExposureModeTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposureoffset/ExposureOffsetFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/flash/FlashFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/flash/FlashFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/flash/FlashFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/flash/FlashFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeaturePixel4aTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeaturePixel4aTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeaturePixel4aTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeaturePixel4aTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/fpsrange/FpsRangeFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/noisereduction/NoiseReductionFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/resolution/ResolutionFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/resolution/ResolutionFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/resolution/ResolutionFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/resolution/ResolutionFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/DeviceOrientationManagerTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/sensororientation/SensorOrientationFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeatureTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeatureTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomLevelFeatureTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtilsTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtilsTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtilsTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/zoomlevel/ZoomUtilsTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/media/MediaRecorderBuilderTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/media/MediaRecorderBuilderTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/media/MediaRecorderBuilderTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/media/MediaRecorderBuilderTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/ExposureModeTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/types/ExposureModeTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/ExposureModeTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/types/ExposureModeTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/FlashModeTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/types/FlashModeTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/FlashModeTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/types/FlashModeTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/FocusModeTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/types/FocusModeTest.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/FocusModeTest.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/types/FocusModeTest.java diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/utils/TestUtils.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/utils/TestUtils.java similarity index 100% rename from packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/utils/TestUtils.java rename to packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/utils/TestUtils.java diff --git a/packages/camera/camera/android/src/test/resources/robolectric.properties b/packages/camera/camera_android/android/src/test/resources/robolectric.properties similarity index 100% rename from packages/camera/camera/android/src/test/resources/robolectric.properties rename to packages/camera/camera_android/android/src/test/resources/robolectric.properties diff --git a/packages/camera/camera_android/example/android/app/build.gradle b/packages/camera/camera_android/example/android/app/build.gradle new file mode 100644 index 000000000000..476d65373723 --- /dev/null +++ b/packages/camera/camera_android/example/android/app/build.gradle @@ -0,0 +1,64 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 31 + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + applicationId "io.flutter.plugins.cameraexample" + minSdkVersion 21 + targetSdkVersion 28 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + profile { + matchingFallbacks = ['debug', 'release'] + } + } +} + +flutter { + source '../..' +} + +dependencies { + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test:rules:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' +} diff --git a/packages/camera/camera_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/camera/camera_android/example/android/app/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..9a4163a4f5ee --- /dev/null +++ b/packages/camera/camera_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/camera/camera_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/camera/camera_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java new file mode 100644 index 000000000000..0f4298dca155 --- /dev/null +++ b/packages/camera/camera_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java @@ -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. + +package io.flutter.plugins; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DartIntegrationTest {} diff --git a/packages/camera/camera_android/example/android/app/src/androidTest/java/io/flutter/plugins/cameraexample/FlutterActivityTest.java b/packages/camera/camera_android/example/android/app/src/androidTest/java/io/flutter/plugins/cameraexample/FlutterActivityTest.java new file mode 100644 index 000000000000..39cae489d9fa --- /dev/null +++ b/packages/camera/camera_android/example/android/app/src/androidTest/java/io/flutter/plugins/cameraexample/FlutterActivityTest.java @@ -0,0 +1,19 @@ +// 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.cameraexample; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.integration_test.FlutterTestRunner; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.plugins.DartIntegrationTest; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@DartIntegrationTest +@RunWith(FlutterTestRunner.class) +public class FlutterActivityTest { + @Rule + public ActivityTestRule rule = new ActivityTestRule<>(FlutterActivity.class); +} diff --git a/packages/camera/camera_android/example/android/app/src/main/AndroidManifest.xml b/packages/camera/camera_android/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..cef23162ddb6 --- /dev/null +++ b/packages/camera/camera_android/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + diff --git a/packages/camera/camera_android/example/android/app/src/main/res/drawable/launch_background.xml b/packages/camera/camera_android/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 000000000000..304732f88420 --- /dev/null +++ b/packages/camera/camera_android/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/camera/camera_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/camera/camera_android/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/camera/camera_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/camera/camera_android/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/camera/camera_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/camera/camera_android/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/camera/camera_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/camera/camera_android/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/camera/camera_android/example/android/app/src/main/res/values/styles.xml b/packages/camera/camera_android/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000000..00fa4417cfbe --- /dev/null +++ b/packages/camera/camera_android/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + diff --git a/packages/camera/camera_android/example/android/build.gradle b/packages/camera/camera_android/example/android/build.gradle new file mode 100644 index 000000000000..456d020f6e2c --- /dev/null +++ b/packages/camera/camera_android/example/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.5.0' + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/packages/camera/camera_android/example/android/gradle.properties b/packages/camera/camera_android/example/android/gradle.properties new file mode 100644 index 000000000000..b253d8e5f746 --- /dev/null +++ b/packages/camera/camera_android/example/android/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=false +android.enableR8=true diff --git a/packages/camera/camera_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/camera/camera_android/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..01a286e96a21 --- /dev/null +++ b/packages/camera/camera_android/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-5.6.2-all.zip diff --git a/packages/camera/camera_android/example/android/settings.gradle b/packages/camera/camera_android/example/android/settings.gradle new file mode 100644 index 000000000000..115da6cb4f4d --- /dev/null +++ b/packages/camera/camera_android/example/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withInputStream { stream -> plugins.load(stream) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/packages/camera/camera_android/example/integration_test/camera_test.dart b/packages/camera/camera_android/example/integration_test/camera_test.dart new file mode 100644 index 000000000000..05c669307dcc --- /dev/null +++ b/packages/camera/camera_android/example/integration_test/camera_test.dart @@ -0,0 +1,246 @@ +// 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:io'; +import 'dart:ui'; + +import 'package:camera_example/camera_controller.dart'; +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/painting.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:video_player/video_player.dart'; + +void main() { + late Directory testDir; + + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + setUpAll(() async { + final Directory extDir = await getTemporaryDirectory(); + testDir = await Directory('${extDir.path}/test').create(recursive: true); + }); + + tearDownAll(() async { + await testDir.delete(recursive: true); + }); + + final Map presetExpectedSizes = + { + ResolutionPreset.low: const Size(240, 320), + ResolutionPreset.medium: const Size(480, 720), + ResolutionPreset.high: const Size(720, 1280), + ResolutionPreset.veryHigh: const Size(1080, 1920), + ResolutionPreset.ultraHigh: const Size(2160, 3840), + // Don't bother checking for max here since it could be anything. + }; + + /// Verify that [actual] has dimensions that are at least as large as + /// [expectedSize]. Allows for a mismatch in portrait vs landscape. Returns + /// whether the dimensions exactly match. + bool assertExpectedDimensions(Size expectedSize, Size actual) { + expect(actual.shortestSide, lessThanOrEqualTo(expectedSize.shortestSide)); + expect(actual.longestSide, lessThanOrEqualTo(expectedSize.longestSide)); + return actual.shortestSide == expectedSize.shortestSide && + actual.longestSide == expectedSize.longestSide; + } + + // This tests that the capture is no bigger than the preset, since we have + // automatic code to fall back to smaller sizes when we need to. Returns + // whether the image is exactly the desired resolution. + Future testCaptureImageResolution( + CameraController controller, ResolutionPreset preset) async { + final Size expectedSize = presetExpectedSizes[preset]!; + print( + 'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); + + // Take Picture + final XFile file = await controller.takePicture(); + + // Load picture + final File fileImage = File(file.path); + final Image image = await decodeImageFromList(fileImage.readAsBytesSync()); + + // Verify image dimensions are as expected + expect(image, isNotNull); + return assertExpectedDimensions( + expectedSize, Size(image.height.toDouble(), image.width.toDouble())); + } + + testWidgets( + 'Capture specific image resolutions', + (WidgetTester tester) async { + final List cameras = + await CameraPlatform.instance.availableCameras(); + if (cameras.isEmpty) { + return; + } + for (final CameraDescription cameraDescription in cameras) { + bool previousPresetExactlySupported = true; + for (final MapEntry preset + in presetExpectedSizes.entries) { + final CameraController controller = + CameraController(cameraDescription, preset.key); + await controller.initialize(); + final bool presetExactlySupported = + await testCaptureImageResolution(controller, preset.key); + assert(!(!previousPresetExactlySupported && presetExactlySupported), + 'The camera took higher resolution pictures at a lower resolution.'); + previousPresetExactlySupported = presetExactlySupported; + await controller.dispose(); + } + } + }, + // TODO(egarciad): Fix https://github.com/flutter/flutter/issues/93686. + skip: true, + ); + + // This tests that the capture is no bigger than the preset, since we have + // automatic code to fall back to smaller sizes when we need to. Returns + // whether the image is exactly the desired resolution. + Future testCaptureVideoResolution( + CameraController controller, ResolutionPreset preset) async { + final Size expectedSize = presetExpectedSizes[preset]!; + print( + 'Capturing video at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); + + // Take Video + await controller.startVideoRecording(); + sleep(const Duration(milliseconds: 300)); + final XFile file = await controller.stopVideoRecording(); + + // Load video metadata + final File videoFile = File(file.path); + final VideoPlayerController videoController = + VideoPlayerController.file(videoFile); + await videoController.initialize(); + final Size video = videoController.value.size; + + // Verify image dimensions are as expected + expect(video, isNotNull); + return assertExpectedDimensions( + expectedSize, Size(video.height, video.width)); + } + + testWidgets( + 'Capture specific video resolutions', + (WidgetTester tester) async { + final List cameras = + await CameraPlatform.instance.availableCameras(); + if (cameras.isEmpty) { + return; + } + for (final CameraDescription cameraDescription in cameras) { + bool previousPresetExactlySupported = true; + for (final MapEntry preset + in presetExpectedSizes.entries) { + final CameraController controller = + CameraController(cameraDescription, preset.key); + await controller.initialize(); + await controller.prepareForVideoRecording(); + final bool presetExactlySupported = + await testCaptureVideoResolution(controller, preset.key); + assert(!(!previousPresetExactlySupported && presetExactlySupported), + 'The camera took higher resolution pictures at a lower resolution.'); + previousPresetExactlySupported = presetExactlySupported; + await controller.dispose(); + } + } + }, + // TODO(egarciad): Fix https://github.com/flutter/flutter/issues/93686. + skip: true, + ); + + testWidgets('Pause and resume video recording', (WidgetTester tester) async { + final List cameras = + await CameraPlatform.instance.availableCameras(); + if (cameras.isEmpty) { + return; + } + + final CameraController controller = CameraController( + cameras[0], + ResolutionPreset.low, + enableAudio: false, + ); + + await controller.initialize(); + await controller.prepareForVideoRecording(); + + int startPause; + int timePaused = 0; + + await controller.startVideoRecording(); + final int recordingStart = DateTime.now().millisecondsSinceEpoch; + sleep(const Duration(milliseconds: 500)); + + await controller.pauseVideoRecording(); + startPause = DateTime.now().millisecondsSinceEpoch; + sleep(const Duration(milliseconds: 500)); + await controller.resumeVideoRecording(); + timePaused += DateTime.now().millisecondsSinceEpoch - startPause; + + sleep(const Duration(milliseconds: 500)); + + await controller.pauseVideoRecording(); + startPause = DateTime.now().millisecondsSinceEpoch; + sleep(const Duration(milliseconds: 500)); + await controller.resumeVideoRecording(); + timePaused += DateTime.now().millisecondsSinceEpoch - startPause; + + sleep(const Duration(milliseconds: 500)); + + final XFile file = await controller.stopVideoRecording(); + final int recordingTime = + DateTime.now().millisecondsSinceEpoch - recordingStart; + + final File videoFile = File(file.path); + final VideoPlayerController videoController = VideoPlayerController.file( + videoFile, + ); + await videoController.initialize(); + final int duration = videoController.value.duration.inMilliseconds; + await videoController.dispose(); + + expect(duration, lessThan(recordingTime - timePaused)); + }); + + testWidgets( + 'image streaming', + (WidgetTester tester) async { + final List cameras = + await CameraPlatform.instance.availableCameras(); + if (cameras.isEmpty) { + return; + } + + final CameraController controller = CameraController( + cameras[0], + ResolutionPreset.low, + enableAudio: false, + ); + + await controller.initialize(); + bool _isDetecting = false; + + await controller.startImageStream((CameraImageData image) { + if (_isDetecting) { + return; + } + + _isDetecting = true; + + expectLater(image, isNotNull).whenComplete(() => _isDetecting = false); + }); + + expect(controller.value.isStreamingImages, true); + + sleep(const Duration(milliseconds: 500)); + + await controller.stopImageStream(); + await controller.dispose(); + }, + ); +} diff --git a/packages/camera/camera_android/example/lib/camera_controller.dart b/packages/camera/camera_android/example/lib/camera_controller.dart new file mode 100644 index 000000000000..5a7a79c8d96c --- /dev/null +++ b/packages/camera/camera_android/example/lib/camera_controller.dart @@ -0,0 +1,437 @@ +// 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:async'; + +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:quiver/core.dart'; + +/// The state of a [CameraController]. +class CameraValue { + /// Creates a new camera controller state. + const CameraValue({ + required this.isInitialized, + this.previewSize, + required this.isRecordingVideo, + required this.isTakingPicture, + required this.isStreamingImages, + required this.isRecordingPaused, + required this.flashMode, + required this.exposureMode, + required this.focusMode, + required this.deviceOrientation, + this.lockedCaptureOrientation, + this.recordingOrientation, + this.isPreviewPaused = false, + this.previewPauseOrientation, + }); + + /// Creates a new camera controller state for an uninitialized controller. + const CameraValue.uninitialized() + : this( + isInitialized: false, + isRecordingVideo: false, + isTakingPicture: false, + isStreamingImages: false, + isRecordingPaused: false, + flashMode: FlashMode.auto, + exposureMode: ExposureMode.auto, + focusMode: FocusMode.auto, + deviceOrientation: DeviceOrientation.portraitUp, + isPreviewPaused: false, + ); + + /// True after [CameraController.initialize] has completed successfully. + final bool isInitialized; + + /// True when a picture capture request has been sent but as not yet returned. + final bool isTakingPicture; + + /// True when the camera is recording (not the same as previewing). + final bool isRecordingVideo; + + /// True when images from the camera are being streamed. + final bool isStreamingImages; + + /// True when video recording is paused. + final bool isRecordingPaused; + + /// True when the preview widget has been paused manually. + final bool isPreviewPaused; + + /// Set to the orientation the preview was paused in, if it is currently paused. + final DeviceOrientation? previewPauseOrientation; + + /// The size of the preview in pixels. + /// + /// Is `null` until [isInitialized] is `true`. + final Size? previewSize; + + /// The flash mode the camera is currently set to. + final FlashMode flashMode; + + /// The exposure mode the camera is currently set to. + final ExposureMode exposureMode; + + /// The focus mode the camera is currently set to. + final FocusMode focusMode; + + /// The current device UI orientation. + final DeviceOrientation deviceOrientation; + + /// The currently locked capture orientation. + final DeviceOrientation? lockedCaptureOrientation; + + /// Whether the capture orientation is currently locked. + bool get isCaptureOrientationLocked => lockedCaptureOrientation != null; + + /// The orientation of the currently running video recording. + final DeviceOrientation? recordingOrientation; + + /// Creates a modified copy of the object. + /// + /// Explicitly specified fields get the specified value, all other fields get + /// the same value of the current object. + CameraValue copyWith({ + bool? isInitialized, + bool? isRecordingVideo, + bool? isTakingPicture, + bool? isStreamingImages, + Size? previewSize, + bool? isRecordingPaused, + FlashMode? flashMode, + ExposureMode? exposureMode, + FocusMode? focusMode, + bool? exposurePointSupported, + bool? focusPointSupported, + DeviceOrientation? deviceOrientation, + Optional? lockedCaptureOrientation, + Optional? recordingOrientation, + bool? isPreviewPaused, + Optional? previewPauseOrientation, + }) { + return CameraValue( + isInitialized: isInitialized ?? this.isInitialized, + previewSize: previewSize ?? this.previewSize, + isRecordingVideo: isRecordingVideo ?? this.isRecordingVideo, + isTakingPicture: isTakingPicture ?? this.isTakingPicture, + isStreamingImages: isStreamingImages ?? this.isStreamingImages, + isRecordingPaused: isRecordingPaused ?? this.isRecordingPaused, + flashMode: flashMode ?? this.flashMode, + exposureMode: exposureMode ?? this.exposureMode, + focusMode: focusMode ?? this.focusMode, + deviceOrientation: deviceOrientation ?? this.deviceOrientation, + lockedCaptureOrientation: lockedCaptureOrientation == null + ? this.lockedCaptureOrientation + : lockedCaptureOrientation.orNull, + recordingOrientation: recordingOrientation == null + ? this.recordingOrientation + : recordingOrientation.orNull, + isPreviewPaused: isPreviewPaused ?? this.isPreviewPaused, + previewPauseOrientation: previewPauseOrientation == null + ? this.previewPauseOrientation + : previewPauseOrientation.orNull, + ); + } + + @override + String toString() { + return '${objectRuntimeType(this, 'CameraValue')}(' + 'isRecordingVideo: $isRecordingVideo, ' + 'isInitialized: $isInitialized, ' + 'previewSize: $previewSize, ' + 'isStreamingImages: $isStreamingImages, ' + 'flashMode: $flashMode, ' + 'exposureMode: $exposureMode, ' + 'focusMode: $focusMode, ' + 'deviceOrientation: $deviceOrientation, ' + 'lockedCaptureOrientation: $lockedCaptureOrientation, ' + 'recordingOrientation: $recordingOrientation, ' + 'isPreviewPaused: $isPreviewPaused, ' + 'previewPausedOrientation: $previewPauseOrientation)'; + } +} + +/// Controls a device camera. +/// +/// This is a stripped-down version of the app-facing controller to serve as a +/// utility for the example and integration tests. It wraps only the calls that +/// have state associated with them, to consolidate tracking of camera state +/// outside of the overall example code. +class CameraController extends ValueNotifier { + /// Creates a new camera controller in an uninitialized state. + CameraController( + this.description, + this.resolutionPreset, { + this.enableAudio = true, + this.imageFormatGroup, + }) : super(const CameraValue.uninitialized()); + + /// The properties of the camera device controlled by this controller. + final CameraDescription description; + + /// The resolution this controller is targeting. + /// + /// This resolution preset is not guaranteed to be available on the device, + /// if unavailable a lower resolution will be used. + /// + /// See also: [ResolutionPreset]. + final ResolutionPreset resolutionPreset; + + /// Whether to include audio when recording a video. + final bool enableAudio; + + /// The [ImageFormatGroup] describes the output of the raw image format. + /// + /// When null the imageFormat will fallback to the platforms default. + final ImageFormatGroup? imageFormatGroup; + + late int _cameraId; + + bool _isDisposed = false; + StreamSubscription? _imageStreamSubscription; + FutureOr? _initCalled; + StreamSubscription? + _deviceOrientationSubscription; + + /// The camera identifier with which the controller is associated. + int get cameraId => _cameraId; + + /// Initializes the camera on the device. + Future initialize() async { + final Completer _initializeCompleter = + Completer(); + + _deviceOrientationSubscription = CameraPlatform.instance + .onDeviceOrientationChanged() + .listen((DeviceOrientationChangedEvent event) { + value = value.copyWith( + deviceOrientation: event.orientation, + ); + }); + + _cameraId = await CameraPlatform.instance.createCamera( + description, + resolutionPreset, + enableAudio: enableAudio, + ); + + CameraPlatform.instance + .onCameraInitialized(_cameraId) + .first + .then((CameraInitializedEvent event) { + _initializeCompleter.complete(event); + }); + + await CameraPlatform.instance.initializeCamera( + _cameraId, + imageFormatGroup: imageFormatGroup ?? ImageFormatGroup.unknown, + ); + + value = value.copyWith( + isInitialized: true, + previewSize: await _initializeCompleter.future + .then((CameraInitializedEvent event) => Size( + event.previewWidth, + event.previewHeight, + )), + exposureMode: await _initializeCompleter.future + .then((CameraInitializedEvent event) => event.exposureMode), + focusMode: await _initializeCompleter.future + .then((CameraInitializedEvent event) => event.focusMode), + exposurePointSupported: await _initializeCompleter.future + .then((CameraInitializedEvent event) => event.exposurePointSupported), + focusPointSupported: await _initializeCompleter.future + .then((CameraInitializedEvent event) => event.focusPointSupported), + ); + + _initCalled = true; + } + + /// Prepare the capture session for video recording. + Future prepareForVideoRecording() async { + await CameraPlatform.instance.prepareForVideoRecording(); + } + + /// Pauses the current camera preview + Future pausePreview() async { + await CameraPlatform.instance.pausePreview(_cameraId); + value = value.copyWith( + isPreviewPaused: true, + previewPauseOrientation: Optional.of( + value.lockedCaptureOrientation ?? value.deviceOrientation)); + } + + /// Resumes the current camera preview + Future resumePreview() async { + await CameraPlatform.instance.resumePreview(_cameraId); + value = value.copyWith( + isPreviewPaused: false, + previewPauseOrientation: const Optional.absent()); + } + + /// Captures an image and returns the file where it was saved. + /// + /// Throws a [CameraException] if the capture fails. + Future takePicture() async { + value = value.copyWith(isTakingPicture: true); + final XFile file = await CameraPlatform.instance.takePicture(_cameraId); + value = value.copyWith(isTakingPicture: false); + return file; + } + + /// Start streaming images from platform camera. + Future startImageStream( + Function(CameraImageData image) onAvailable) async { + _imageStreamSubscription = CameraPlatform.instance + .onStreamedFrameAvailable(_cameraId) + .listen((CameraImageData imageData) { + onAvailable(imageData); + }); + value = value.copyWith(isStreamingImages: true); + } + + /// Stop streaming images from platform camera. + Future stopImageStream() async { + value = value.copyWith(isStreamingImages: false); + await _imageStreamSubscription?.cancel(); + _imageStreamSubscription = null; + } + + /// Start a video recording. + /// + /// The video is returned as a [XFile] after calling [stopVideoRecording]. + /// Throws a [CameraException] if the capture fails. + Future startVideoRecording() async { + await CameraPlatform.instance.startVideoRecording(_cameraId); + value = value.copyWith( + isRecordingVideo: true, + isRecordingPaused: false, + recordingOrientation: Optional.of( + value.lockedCaptureOrientation ?? value.deviceOrientation)); + } + + /// Stops the video recording and returns the file where it was saved. + /// + /// Throws a [CameraException] if the capture failed. + Future stopVideoRecording() async { + final XFile file = + await CameraPlatform.instance.stopVideoRecording(_cameraId); + value = value.copyWith( + isRecordingVideo: false, + recordingOrientation: const Optional.absent(), + ); + return file; + } + + /// Pause video recording. + /// + /// This feature is only available on iOS and Android sdk 24+. + Future pauseVideoRecording() async { + await CameraPlatform.instance.pauseVideoRecording(_cameraId); + value = value.copyWith(isRecordingPaused: true); + } + + /// Resume video recording after pausing. + /// + /// This feature is only available on iOS and Android sdk 24+. + Future resumeVideoRecording() async { + await CameraPlatform.instance.resumeVideoRecording(_cameraId); + value = value.copyWith(isRecordingPaused: false); + } + + /// Returns a widget showing a live camera preview. + Widget buildPreview() { + return CameraPlatform.instance.buildPreview(_cameraId); + } + + /// Sets the flash mode for taking pictures. + Future setFlashMode(FlashMode mode) async { + await CameraPlatform.instance.setFlashMode(_cameraId, mode); + value = value.copyWith(flashMode: mode); + } + + /// Sets the exposure mode for taking pictures. + Future setExposureMode(ExposureMode mode) async { + await CameraPlatform.instance.setExposureMode(_cameraId, mode); + value = value.copyWith(exposureMode: mode); + } + + /// Sets the exposure offset for the selected camera. + Future setExposureOffset(double offset) async { + // Check if offset is in range + final List range = await Future.wait(>[ + CameraPlatform.instance.getMinExposureOffset(_cameraId), + CameraPlatform.instance.getMaxExposureOffset(_cameraId) + ]); + + // Round to the closest step if needed + final double stepSize = + await CameraPlatform.instance.getExposureOffsetStepSize(_cameraId); + if (stepSize > 0) { + final double inv = 1.0 / stepSize; + double roundedOffset = (offset * inv).roundToDouble() / inv; + if (roundedOffset > range[1]) { + roundedOffset = (offset * inv).floorToDouble() / inv; + } else if (roundedOffset < range[0]) { + roundedOffset = (offset * inv).ceilToDouble() / inv; + } + offset = roundedOffset; + } + + return CameraPlatform.instance.setExposureOffset(_cameraId, offset); + } + + /// Locks the capture orientation. + /// + /// If [orientation] is omitted, the current device orientation is used. + Future lockCaptureOrientation() async { + await CameraPlatform.instance + .lockCaptureOrientation(_cameraId, value.deviceOrientation); + value = value.copyWith( + lockedCaptureOrientation: + Optional.of(value.deviceOrientation)); + } + + /// Unlocks the capture orientation. + Future unlockCaptureOrientation() async { + await CameraPlatform.instance.unlockCaptureOrientation(_cameraId); + value = value.copyWith( + lockedCaptureOrientation: const Optional.absent()); + } + + /// Sets the focus mode for taking pictures. + Future setFocusMode(FocusMode mode) async { + await CameraPlatform.instance.setFocusMode(_cameraId, mode); + value = value.copyWith(focusMode: mode); + } + + /// Releases the resources of this camera. + @override + Future dispose() async { + if (_isDisposed) { + return; + } + _deviceOrientationSubscription?.cancel(); + _isDisposed = true; + super.dispose(); + if (_initCalled != null) { + await _initCalled; + await CameraPlatform.instance.dispose(_cameraId); + } + } + + @override + void removeListener(VoidCallback listener) { + // Prevent ValueListenableBuilder in CameraPreview widget from causing an + // exception to be thrown by attempting to remove its own listener after + // the controller has already been disposed. + if (!_isDisposed) { + super.removeListener(listener); + } + } +} diff --git a/packages/camera/camera_android/example/lib/camera_preview.dart b/packages/camera/camera_android/example/lib/camera_preview.dart new file mode 100644 index 000000000000..5e8f64cb2fbd --- /dev/null +++ b/packages/camera/camera_android/example/lib/camera_preview.dart @@ -0,0 +1,85 @@ +// 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/services.dart'; + +import 'camera_controller.dart'; + +/// A widget showing a live camera preview. +class CameraPreview extends StatelessWidget { + /// Creates a preview widget for the given camera controller. + const CameraPreview(this.controller, {Key? key, this.child}) + : super(key: key); + + /// The controller for the camera that the preview is shown for. + final CameraController controller; + + /// A widget to overlay on top of the camera preview + final Widget? child; + + @override + Widget build(BuildContext context) { + return controller.value.isInitialized + ? ValueListenableBuilder( + valueListenable: controller, + builder: (BuildContext context, Object? value, Widget? child) { + final double cameraAspectRatio = + controller.value.previewSize!.width / + controller.value.previewSize!.height; + return AspectRatio( + aspectRatio: _isLandscape() + ? cameraAspectRatio + : (1 / cameraAspectRatio), + child: Stack( + fit: StackFit.expand, + children: [ + _wrapInRotatedBox(child: controller.buildPreview()), + child ?? Container(), + ], + ), + ); + }, + child: child, + ) + : Container(); + } + + Widget _wrapInRotatedBox({required Widget child}) { + if (kIsWeb || defaultTargetPlatform != TargetPlatform.android) { + return child; + } + + return RotatedBox( + quarterTurns: _getQuarterTurns(), + child: child, + ); + } + + bool _isLandscape() { + return [ + DeviceOrientation.landscapeLeft, + DeviceOrientation.landscapeRight + ].contains(_getApplicableOrientation()); + } + + int _getQuarterTurns() { + final Map turns = { + DeviceOrientation.portraitUp: 0, + DeviceOrientation.landscapeRight: 1, + DeviceOrientation.portraitDown: 2, + DeviceOrientation.landscapeLeft: 3, + }; + return turns[_getApplicableOrientation()]!; + } + + DeviceOrientation _getApplicableOrientation() { + return controller.value.isRecordingVideo + ? controller.value.recordingOrientation! + : (controller.value.previewPauseOrientation ?? + controller.value.lockedCaptureOrientation ?? + controller.value.deviceOrientation); + } +} diff --git a/packages/camera/camera_android/example/lib/main.dart b/packages/camera/camera_android/example/lib/main.dart new file mode 100644 index 000000000000..1f3e06d33b9b --- /dev/null +++ b/packages/camera/camera_android/example/lib/main.dart @@ -0,0 +1,1095 @@ +// 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:async'; +import 'dart:io'; +import 'dart:math'; + +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:video_player/video_player.dart'; + +import 'camera_controller.dart'; +import 'camera_preview.dart'; + +/// Camera example home widget. +class CameraExampleHome extends StatefulWidget { + /// Default Constructor + const CameraExampleHome({Key? key}) : super(key: key); + + @override + State createState() { + return _CameraExampleHomeState(); + } +} + +/// Returns a suitable camera icon for [direction]. +IconData getCameraLensIcon(CameraLensDirection direction) { + switch (direction) { + case CameraLensDirection.back: + return Icons.camera_rear; + case CameraLensDirection.front: + return Icons.camera_front; + case CameraLensDirection.external: + return Icons.camera; + default: + throw ArgumentError('Unknown lens direction'); + } +} + +void _logError(String code, String? message) { + if (message != null) { + print('Error: $code\nError Message: $message'); + } else { + print('Error: $code'); + } +} + +class _CameraExampleHomeState extends State + with WidgetsBindingObserver, TickerProviderStateMixin { + CameraController? controller; + XFile? imageFile; + XFile? videoFile; + VideoPlayerController? videoController; + VoidCallback? videoPlayerListener; + bool enableAudio = true; + double _minAvailableExposureOffset = 0.0; + double _maxAvailableExposureOffset = 0.0; + double _currentExposureOffset = 0.0; + late AnimationController _flashModeControlRowAnimationController; + late Animation _flashModeControlRowAnimation; + late AnimationController _exposureModeControlRowAnimationController; + late Animation _exposureModeControlRowAnimation; + late AnimationController _focusModeControlRowAnimationController; + late Animation _focusModeControlRowAnimation; + double _minAvailableZoom = 1.0; + double _maxAvailableZoom = 1.0; + double _currentScale = 1.0; + double _baseScale = 1.0; + + // Counting pointers (number of user fingers on screen) + int _pointers = 0; + + @override + void initState() { + super.initState(); + _ambiguate(WidgetsBinding.instance)?.addObserver(this); + + _flashModeControlRowAnimationController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + _flashModeControlRowAnimation = CurvedAnimation( + parent: _flashModeControlRowAnimationController, + curve: Curves.easeInCubic, + ); + _exposureModeControlRowAnimationController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + _exposureModeControlRowAnimation = CurvedAnimation( + parent: _exposureModeControlRowAnimationController, + curve: Curves.easeInCubic, + ); + _focusModeControlRowAnimationController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + _focusModeControlRowAnimation = CurvedAnimation( + parent: _focusModeControlRowAnimationController, + curve: Curves.easeInCubic, + ); + } + + @override + void dispose() { + _ambiguate(WidgetsBinding.instance)?.removeObserver(this); + _flashModeControlRowAnimationController.dispose(); + _exposureModeControlRowAnimationController.dispose(); + super.dispose(); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + final CameraController? cameraController = controller; + + // App state changed before we got the chance to initialize. + if (cameraController == null || !cameraController.value.isInitialized) { + return; + } + + if (state == AppLifecycleState.inactive) { + cameraController.dispose(); + } else if (state == AppLifecycleState.resumed) { + onNewCameraSelected(cameraController.description); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Camera example'), + ), + body: Column( + children: [ + Expanded( + child: Container( + decoration: BoxDecoration( + color: Colors.black, + border: Border.all( + color: + controller != null && controller!.value.isRecordingVideo + ? Colors.redAccent + : Colors.grey, + width: 3.0, + ), + ), + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Center( + child: _cameraPreviewWidget(), + ), + ), + ), + ), + _captureControlRowWidget(), + _modeControlRowWidget(), + Padding( + padding: const EdgeInsets.all(5.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + _cameraTogglesRowWidget(), + _thumbnailWidget(), + ], + ), + ), + ], + ), + ); + } + + /// Display the preview from the camera (or a message if the preview is not available). + Widget _cameraPreviewWidget() { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isInitialized) { + return const Text( + 'Tap a camera', + style: TextStyle( + color: Colors.white, + fontSize: 24.0, + fontWeight: FontWeight.w900, + ), + ); + } else { + return Listener( + onPointerDown: (_) => _pointers++, + onPointerUp: (_) => _pointers--, + child: CameraPreview( + controller!, + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onScaleStart: _handleScaleStart, + onScaleUpdate: _handleScaleUpdate, + onTapDown: (TapDownDetails details) => + onViewFinderTap(details, constraints), + ); + }), + ), + ); + } + } + + void _handleScaleStart(ScaleStartDetails details) { + _baseScale = _currentScale; + } + + Future _handleScaleUpdate(ScaleUpdateDetails details) async { + // When there are not exactly two fingers on screen don't scale + if (controller == null || _pointers != 2) { + return; + } + + _currentScale = (_baseScale * details.scale) + .clamp(_minAvailableZoom, _maxAvailableZoom); + + await CameraPlatform.instance + .setZoomLevel(controller!.cameraId, _currentScale); + } + + /// Display the thumbnail of the captured image or video. + Widget _thumbnailWidget() { + final VideoPlayerController? localVideoController = videoController; + + return Expanded( + child: Align( + alignment: Alignment.centerRight, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (localVideoController == null && imageFile == null) + Container() + else + SizedBox( + width: 64.0, + height: 64.0, + child: (localVideoController == null) + ? ( + // The captured image on the web contains a network-accessible URL + // pointing to a location within the browser. It may be displayed + // either with Image.network or Image.memory after loading the image + // bytes to memory. + kIsWeb + ? Image.network(imageFile!.path) + : Image.file(File(imageFile!.path))) + : Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.pink)), + child: Center( + child: AspectRatio( + aspectRatio: + localVideoController.value.size != null + ? localVideoController.value.aspectRatio + : 1.0, + child: VideoPlayer(localVideoController)), + ), + ), + ), + ], + ), + ), + ); + } + + /// Display a bar with buttons to change the flash and exposure modes + Widget _modeControlRowWidget() { + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + IconButton( + icon: const Icon(Icons.flash_on), + color: Colors.blue, + onPressed: controller != null ? onFlashModeButtonPressed : null, + ), + // The exposure and focus mode are currently not supported on the web. + ...!kIsWeb + ? [ + IconButton( + icon: const Icon(Icons.exposure), + color: Colors.blue, + onPressed: controller != null + ? onExposureModeButtonPressed + : null, + ), + IconButton( + icon: const Icon(Icons.filter_center_focus), + color: Colors.blue, + onPressed: + controller != null ? onFocusModeButtonPressed : null, + ) + ] + : [], + IconButton( + icon: Icon(enableAudio ? Icons.volume_up : Icons.volume_mute), + color: Colors.blue, + onPressed: controller != null ? onAudioModeButtonPressed : null, + ), + IconButton( + icon: Icon(controller?.value.isCaptureOrientationLocked ?? false + ? Icons.screen_lock_rotation + : Icons.screen_rotation), + color: Colors.blue, + onPressed: controller != null + ? onCaptureOrientationLockButtonPressed + : null, + ), + ], + ), + _flashModeControlRowWidget(), + _exposureModeControlRowWidget(), + _focusModeControlRowWidget(), + ], + ); + } + + Widget _flashModeControlRowWidget() { + return SizeTransition( + sizeFactor: _flashModeControlRowAnimation, + child: ClipRect( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + IconButton( + icon: const Icon(Icons.flash_off), + color: controller?.value.flashMode == FlashMode.off + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.off) + : null, + ), + IconButton( + icon: const Icon(Icons.flash_auto), + color: controller?.value.flashMode == FlashMode.auto + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.auto) + : null, + ), + IconButton( + icon: const Icon(Icons.flash_on), + color: controller?.value.flashMode == FlashMode.always + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.always) + : null, + ), + IconButton( + icon: const Icon(Icons.highlight), + color: controller?.value.flashMode == FlashMode.torch + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.torch) + : null, + ), + ], + ), + ), + ); + } + + Widget _exposureModeControlRowWidget() { + final ButtonStyle styleAuto = TextButton.styleFrom( + primary: controller?.value.exposureMode == ExposureMode.auto + ? Colors.orange + : Colors.blue, + ); + final ButtonStyle styleLocked = TextButton.styleFrom( + primary: controller?.value.exposureMode == ExposureMode.locked + ? Colors.orange + : Colors.blue, + ); + + return SizeTransition( + sizeFactor: _exposureModeControlRowAnimation, + child: ClipRect( + child: Container( + color: Colors.grey.shade50, + child: Column( + children: [ + const Center( + child: Text('Exposure Mode'), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + TextButton( + style: styleAuto, + onPressed: controller != null + ? () => + onSetExposureModeButtonPressed(ExposureMode.auto) + : null, + onLongPress: () { + if (controller != null) { + CameraPlatform.instance + .setExposurePoint(controller!.cameraId, null); + showInSnackBar('Resetting exposure point'); + } + }, + child: const Text('AUTO'), + ), + TextButton( + style: styleLocked, + onPressed: controller != null + ? () => + onSetExposureModeButtonPressed(ExposureMode.locked) + : null, + child: const Text('LOCKED'), + ), + TextButton( + style: styleLocked, + onPressed: controller != null + ? () => controller!.setExposureOffset(0.0) + : null, + child: const Text('RESET OFFSET'), + ), + ], + ), + const Center( + child: Text('Exposure Offset'), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + Text(_minAvailableExposureOffset.toString()), + Slider( + value: _currentExposureOffset, + min: _minAvailableExposureOffset, + max: _maxAvailableExposureOffset, + label: _currentExposureOffset.toString(), + onChanged: _minAvailableExposureOffset == + _maxAvailableExposureOffset + ? null + : setExposureOffset, + ), + Text(_maxAvailableExposureOffset.toString()), + ], + ), + ], + ), + ), + ), + ); + } + + Widget _focusModeControlRowWidget() { + final ButtonStyle styleAuto = TextButton.styleFrom( + primary: controller?.value.focusMode == FocusMode.auto + ? Colors.orange + : Colors.blue, + ); + final ButtonStyle styleLocked = TextButton.styleFrom( + primary: controller?.value.focusMode == FocusMode.locked + ? Colors.orange + : Colors.blue, + ); + + return SizeTransition( + sizeFactor: _focusModeControlRowAnimation, + child: ClipRect( + child: Container( + color: Colors.grey.shade50, + child: Column( + children: [ + const Center( + child: Text('Focus Mode'), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + TextButton( + style: styleAuto, + onPressed: controller != null + ? () => onSetFocusModeButtonPressed(FocusMode.auto) + : null, + onLongPress: () { + if (controller != null) { + CameraPlatform.instance + .setFocusPoint(controller!.cameraId, null); + } + showInSnackBar('Resetting focus point'); + }, + child: const Text('AUTO'), + ), + TextButton( + style: styleLocked, + onPressed: controller != null + ? () => onSetFocusModeButtonPressed(FocusMode.locked) + : null, + child: const Text('LOCKED'), + ), + ], + ), + ], + ), + ), + ), + ); + } + + /// Display the control bar with buttons to take pictures and record videos. + Widget _captureControlRowWidget() { + final CameraController? cameraController = controller; + + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + IconButton( + icon: const Icon(Icons.camera_alt), + color: Colors.blue, + onPressed: cameraController != null && + cameraController.value.isInitialized && + !cameraController.value.isRecordingVideo + ? onTakePictureButtonPressed + : null, + ), + IconButton( + icon: const Icon(Icons.videocam), + color: Colors.blue, + onPressed: cameraController != null && + cameraController.value.isInitialized && + !cameraController.value.isRecordingVideo + ? onVideoRecordButtonPressed + : null, + ), + IconButton( + icon: cameraController != null && + (!cameraController.value.isRecordingVideo || + cameraController.value.isRecordingPaused) + ? const Icon(Icons.play_arrow) + : const Icon(Icons.pause), + color: Colors.blue, + onPressed: cameraController != null && + cameraController.value.isInitialized && + cameraController.value.isRecordingVideo + ? (cameraController.value.isRecordingPaused) + ? onResumeButtonPressed + : onPauseButtonPressed + : null, + ), + IconButton( + icon: const Icon(Icons.stop), + color: Colors.red, + onPressed: cameraController != null && + cameraController.value.isInitialized && + cameraController.value.isRecordingVideo + ? onStopButtonPressed + : null, + ), + IconButton( + icon: const Icon(Icons.pause_presentation), + color: + cameraController != null && cameraController.value.isPreviewPaused + ? Colors.red + : Colors.blue, + onPressed: + cameraController == null ? null : onPausePreviewButtonPressed, + ), + ], + ); + } + + /// Display a row of toggle to select the camera (or a message if no camera is available). + Widget _cameraTogglesRowWidget() { + final List toggles = []; + + void onChanged(CameraDescription? description) { + if (description == null) { + return; + } + + onNewCameraSelected(description); + } + + if (_cameras.isEmpty) { + _ambiguate(SchedulerBinding.instance)?.addPostFrameCallback((_) async { + showInSnackBar('No camera found.'); + }); + return const Text('None'); + } else { + for (final CameraDescription cameraDescription in _cameras) { + toggles.add( + SizedBox( + width: 90.0, + child: RadioListTile( + title: Icon(getCameraLensIcon(cameraDescription.lensDirection)), + groupValue: controller?.description, + value: cameraDescription, + onChanged: + controller != null && controller!.value.isRecordingVideo + ? null + : onChanged, + ), + ), + ); + } + } + + return Row(children: toggles); + } + + String timestamp() => DateTime.now().millisecondsSinceEpoch.toString(); + + void showInSnackBar(String message) { + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text(message))); + } + + void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) { + if (controller == null) { + return; + } + + final CameraController cameraController = controller!; + + final Point point = Point( + details.localPosition.dx / constraints.maxWidth, + details.localPosition.dy / constraints.maxHeight, + ); + CameraPlatform.instance.setExposurePoint(cameraController.cameraId, point); + CameraPlatform.instance.setFocusPoint(cameraController.cameraId, point); + } + + Future onNewCameraSelected(CameraDescription cameraDescription) async { + final CameraController? oldController = controller; + if (oldController != null) { + // `controller` needs to be set to null before getting disposed, + // to avoid a race condition when we use the controller that is being + // disposed. This happens when camera permission dialog shows up, + // which triggers `didChangeAppLifecycleState`, which disposes and + // re-creates the controller. + controller = null; + await oldController.dispose(); + } + + final CameraController cameraController = CameraController( + cameraDescription, + kIsWeb ? ResolutionPreset.max : ResolutionPreset.medium, + enableAudio: enableAudio, + imageFormatGroup: ImageFormatGroup.jpeg, + ); + + controller = cameraController; + + // If the controller is updated then update the UI. + cameraController.addListener(() { + if (mounted) { + setState(() {}); + } + }); + + try { + await cameraController.initialize(); + await Future.wait(>[ + // The exposure mode is currently not supported on the web. + ...!kIsWeb + ? >[ + CameraPlatform.instance + .getMinExposureOffset(cameraController.cameraId) + .then( + (double value) => _minAvailableExposureOffset = value), + CameraPlatform.instance + .getMaxExposureOffset(cameraController.cameraId) + .then((double value) => _maxAvailableExposureOffset = value) + ] + : >[], + CameraPlatform.instance + .getMaxZoomLevel(cameraController.cameraId) + .then((double value) => _maxAvailableZoom = value), + CameraPlatform.instance + .getMinZoomLevel(cameraController.cameraId) + .then((double value) => _minAvailableZoom = value), + ]); + } on CameraException catch (e) { + switch (e.code) { + case 'CameraAccessDenied': + showInSnackBar('You have denied camera access.'); + break; + case 'CameraAccessDeniedWithoutPrompt': + // iOS only + showInSnackBar('Please go to Settings app to enable camera access.'); + break; + case 'CameraAccessRestricted': + // iOS only + showInSnackBar('Camera access is restricted.'); + break; + case 'AudioAccessDenied': + showInSnackBar('You have denied audio access.'); + break; + case 'AudioAccessDeniedWithoutPrompt': + // iOS only + showInSnackBar('Please go to Settings app to enable audio access.'); + break; + case 'AudioAccessRestricted': + // iOS only + showInSnackBar('Audio access is restricted.'); + break; + case 'cameraPermission': + // Android & web only + showInSnackBar('Unknown permission error.'); + break; + default: + _showCameraException(e); + break; + } + } + + if (mounted) { + setState(() {}); + } + } + + void onTakePictureButtonPressed() { + takePicture().then((XFile? file) { + if (mounted) { + setState(() { + imageFile = file; + videoController?.dispose(); + videoController = null; + }); + if (file != null) { + showInSnackBar('Picture saved to ${file.path}'); + } + } + }); + } + + void onFlashModeButtonPressed() { + if (_flashModeControlRowAnimationController.value == 1) { + _flashModeControlRowAnimationController.reverse(); + } else { + _flashModeControlRowAnimationController.forward(); + _exposureModeControlRowAnimationController.reverse(); + _focusModeControlRowAnimationController.reverse(); + } + } + + void onExposureModeButtonPressed() { + if (_exposureModeControlRowAnimationController.value == 1) { + _exposureModeControlRowAnimationController.reverse(); + } else { + _exposureModeControlRowAnimationController.forward(); + _flashModeControlRowAnimationController.reverse(); + _focusModeControlRowAnimationController.reverse(); + } + } + + void onFocusModeButtonPressed() { + if (_focusModeControlRowAnimationController.value == 1) { + _focusModeControlRowAnimationController.reverse(); + } else { + _focusModeControlRowAnimationController.forward(); + _flashModeControlRowAnimationController.reverse(); + _exposureModeControlRowAnimationController.reverse(); + } + } + + void onAudioModeButtonPressed() { + enableAudio = !enableAudio; + if (controller != null) { + onNewCameraSelected(controller!.description); + } + } + + Future onCaptureOrientationLockButtonPressed() async { + try { + if (controller != null) { + final CameraController cameraController = controller!; + if (cameraController.value.isCaptureOrientationLocked) { + await cameraController.unlockCaptureOrientation(); + showInSnackBar('Capture orientation unlocked'); + } else { + await cameraController.lockCaptureOrientation(); + showInSnackBar( + 'Capture orientation locked to ${cameraController.value.lockedCaptureOrientation.toString().split('.').last}'); + } + } + } on CameraException catch (e) { + _showCameraException(e); + } + } + + void onSetFlashModeButtonPressed(FlashMode mode) { + setFlashMode(mode).then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Flash mode set to ${mode.toString().split('.').last}'); + }); + } + + void onSetExposureModeButtonPressed(ExposureMode mode) { + setExposureMode(mode).then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Exposure mode set to ${mode.toString().split('.').last}'); + }); + } + + void onSetFocusModeButtonPressed(FocusMode mode) { + setFocusMode(mode).then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Focus mode set to ${mode.toString().split('.').last}'); + }); + } + + void onVideoRecordButtonPressed() { + startVideoRecording().then((_) { + if (mounted) { + setState(() {}); + } + }); + } + + void onStopButtonPressed() { + stopVideoRecording().then((XFile? file) { + if (mounted) { + setState(() {}); + } + if (file != null) { + showInSnackBar('Video recorded to ${file.path}'); + videoFile = file; + _startVideoPlayer(); + } + }); + } + + Future onPausePreviewButtonPressed() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isInitialized) { + showInSnackBar('Error: select a camera first.'); + return; + } + + if (cameraController.value.isPreviewPaused) { + await cameraController.resumePreview(); + } else { + await cameraController.pausePreview(); + } + + if (mounted) { + setState(() {}); + } + } + + void onPauseButtonPressed() { + pauseVideoRecording().then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Video recording paused'); + }); + } + + void onResumeButtonPressed() { + resumeVideoRecording().then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Video recording resumed'); + }); + } + + Future startVideoRecording() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isInitialized) { + showInSnackBar('Error: select a camera first.'); + return; + } + + if (cameraController.value.isRecordingVideo) { + // A recording is already started, do nothing. + return; + } + + try { + await cameraController.startVideoRecording(); + } on CameraException catch (e) { + _showCameraException(e); + return; + } + } + + Future stopVideoRecording() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isRecordingVideo) { + return null; + } + + try { + return cameraController.stopVideoRecording(); + } on CameraException catch (e) { + _showCameraException(e); + return null; + } + } + + Future pauseVideoRecording() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isRecordingVideo) { + return; + } + + try { + await cameraController.pauseVideoRecording(); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future resumeVideoRecording() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isRecordingVideo) { + return; + } + + try { + await cameraController.resumeVideoRecording(); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future setFlashMode(FlashMode mode) async { + if (controller == null) { + return; + } + + try { + await controller!.setFlashMode(mode); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future setExposureMode(ExposureMode mode) async { + if (controller == null) { + return; + } + + try { + await controller!.setExposureMode(mode); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future setExposureOffset(double offset) async { + if (controller == null) { + return; + } + + setState(() { + _currentExposureOffset = offset; + }); + try { + offset = await controller!.setExposureOffset(offset); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future setFocusMode(FocusMode mode) async { + if (controller == null) { + return; + } + + try { + await controller!.setFocusMode(mode); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future _startVideoPlayer() async { + if (videoFile == null) { + return; + } + + final VideoPlayerController vController = kIsWeb + ? VideoPlayerController.network(videoFile!.path) + : VideoPlayerController.file(File(videoFile!.path)); + + videoPlayerListener = () { + if (videoController != null && videoController!.value.size != null) { + // Refreshing the state to update video player with the correct ratio. + if (mounted) { + setState(() {}); + } + videoController!.removeListener(videoPlayerListener!); + } + }; + vController.addListener(videoPlayerListener!); + await vController.setLooping(true); + await vController.initialize(); + await videoController?.dispose(); + if (mounted) { + setState(() { + imageFile = null; + videoController = vController; + }); + } + await vController.play(); + } + + Future takePicture() async { + final CameraController? cameraController = controller; + if (cameraController == null || !cameraController.value.isInitialized) { + showInSnackBar('Error: select a camera first.'); + return null; + } + + if (cameraController.value.isTakingPicture) { + // A capture is already pending, do nothing. + return null; + } + + try { + final XFile file = await cameraController.takePicture(); + return file; + } on CameraException catch (e) { + _showCameraException(e); + return null; + } + } + + void _showCameraException(CameraException e) { + _logError(e.code, e.description); + showInSnackBar('Error: ${e.code}\n${e.description}'); + } +} + +/// CameraApp is the Main Application. +class CameraApp extends StatelessWidget { + /// Default Constructor + const CameraApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return const MaterialApp( + home: CameraExampleHome(), + ); + } +} + +List _cameras = []; + +Future main() async { + // Fetch the available cameras before initializing the app. + try { + WidgetsFlutterBinding.ensureInitialized(); + _cameras = await CameraPlatform.instance.availableCameras(); + } on CameraException catch (e) { + _logError(e.code, e.description); + } + runApp(const CameraApp()); +} + +/// This allows a value of type T or T? to be treated as a value of type T?. +/// +/// We use this so that APIs that have become non-nullable can still be used +/// with `!` and `?` on the stable branch. +// TODO(ianh): Remove this once we roll stable in late 2021. +T? _ambiguate(T? value) => value; diff --git a/packages/camera/camera_android/example/pubspec.yaml b/packages/camera/camera_android/example/pubspec.yaml new file mode 100644 index 000000000000..64e69405041c --- /dev/null +++ b/packages/camera/camera_android/example/pubspec.yaml @@ -0,0 +1,33 @@ +name: camera_example +description: Demonstrates how to use the camera plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +dependencies: + camera_android: + # When depending on this package from a real application you should use: + # camera_android: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # 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: ../ + flutter: + sdk: flutter + path_provider: ^2.0.0 + quiver: ^3.0.0 + video_player: ^2.1.4 + +dev_dependencies: + build_runner: ^2.1.10 + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/camera/camera_android/example/test_driver/integration_test.dart b/packages/camera/camera_android/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4ec97e66d36c --- /dev/null +++ b/packages/camera/camera_android/example/test_driver/integration_test.dart @@ -0,0 +1,64 @@ +// 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:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter_driver/flutter_driver.dart'; + +const String _examplePackage = 'io.flutter.plugins.cameraexample'; + +Future main() async { + if (!(Platform.isLinux || Platform.isMacOS)) { + print('This test must be run on a POSIX host. Skipping...'); + exit(0); + } + final bool adbExists = + Process.runSync('which', ['adb']).exitCode == 0; + if (!adbExists) { + print(r'This test needs ADB to exist on the $PATH. Skipping...'); + exit(0); + } + print('Granting camera permissions...'); + Process.runSync('adb', [ + 'shell', + 'pm', + 'grant', + _examplePackage, + 'android.permission.CAMERA' + ]); + Process.runSync('adb', [ + 'shell', + 'pm', + 'grant', + _examplePackage, + 'android.permission.RECORD_AUDIO' + ]); + print('Starting test.'); + final FlutterDriver driver = await FlutterDriver.connect(); + final String data = await driver.requestData( + null, + timeout: const Duration(minutes: 1), + ); + await driver.close(); + print('Test finished. Revoking camera permissions...'); + Process.runSync('adb', [ + 'shell', + 'pm', + 'revoke', + _examplePackage, + 'android.permission.CAMERA' + ]); + Process.runSync('adb', [ + 'shell', + 'pm', + 'revoke', + _examplePackage, + 'android.permission.RECORD_AUDIO' + ]); + + final Map result = jsonDecode(data) as Map; + exit(result['result'] == 'true' ? 0 : 1); +} diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml new file mode 100644 index 000000000000..908d55f9c50e --- /dev/null +++ b/packages/camera/camera_android/pubspec.yaml @@ -0,0 +1,29 @@ +name: camera_android +description: Android implementation of the camera plugin. +repository: https://github.com/flutter/plugins/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.9.7+1 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +flutter: + plugin: + implements: camera + platforms: + android: + package: io.flutter.plugins.camera + pluginClass: CameraPlugin + +dependencies: + camera_platform_interface: ^2.2.0 + flutter: + sdk: flutter + flutter_plugin_android_lifecycle: ^2.0.2 + +dev_dependencies: + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter diff --git a/packages/camera/camera_avfoundation/AUTHORS b/packages/camera/camera_avfoundation/AUTHORS new file mode 100644 index 000000000000..493a0b4ef9c2 --- /dev/null +++ b/packages/camera/camera_avfoundation/AUTHORS @@ -0,0 +1,66 @@ +# 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. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md new file mode 100644 index 000000000000..c57f301fae95 --- /dev/null +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.9.7+1 + +* Splits from `camera` as a federated implementation. diff --git a/packages/camera/camera_avfoundation/LICENSE b/packages/camera/camera_avfoundation/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/camera/camera_avfoundation/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/camera/camera_avfoundation/README.md b/packages/camera/camera_avfoundation/README.md new file mode 100644 index 000000000000..a063492e6c15 --- /dev/null +++ b/packages/camera/camera_avfoundation/README.md @@ -0,0 +1,11 @@ +# camera\_avfoundation + +The iOS implementation of [`camera`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `camera` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/camera +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart new file mode 100644 index 000000000000..6ac7a6860dd9 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart @@ -0,0 +1,254 @@ +// 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:async'; +import 'dart:io'; +import 'dart:ui'; + +import 'package:camera_example/camera_controller.dart'; +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/painting.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:video_player/video_player.dart'; + +void main() { + late Directory testDir; + + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + setUpAll(() async { + final Directory extDir = await getTemporaryDirectory(); + testDir = await Directory('${extDir.path}/test').create(recursive: true); + }); + + tearDownAll(() async { + await testDir.delete(recursive: true); + }); + + final Map presetExpectedSizes = + { + ResolutionPreset.low: const Size(288, 352), + ResolutionPreset.medium: const Size(480, 640), + ResolutionPreset.high: const Size(720, 1280), + ResolutionPreset.veryHigh: const Size(1080, 1920), + ResolutionPreset.ultraHigh: const Size(2160, 3840), + // Don't bother checking for max here since it could be anything. + }; + + /// Verify that [actual] has dimensions that are at least as large as + /// [expectedSize]. Allows for a mismatch in portrait vs landscape. Returns + /// whether the dimensions exactly match. + bool assertExpectedDimensions(Size expectedSize, Size actual) { + expect(actual.shortestSide, lessThanOrEqualTo(expectedSize.shortestSide)); + expect(actual.longestSide, lessThanOrEqualTo(expectedSize.longestSide)); + return actual.shortestSide == expectedSize.shortestSide && + actual.longestSide == expectedSize.longestSide; + } + + // This tests that the capture is no bigger than the preset, since we have + // automatic code to fall back to smaller sizes when we need to. Returns + // whether the image is exactly the desired resolution. + Future testCaptureImageResolution( + CameraController controller, ResolutionPreset preset) async { + final Size expectedSize = presetExpectedSizes[preset]!; + print( + 'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); + + // Take Picture + final XFile file = await controller.takePicture(); + + // Load picture + final File fileImage = File(file.path); + final Image image = await decodeImageFromList(fileImage.readAsBytesSync()); + + // Verify image dimensions are as expected + expect(image, isNotNull); + return assertExpectedDimensions( + expectedSize, Size(image.height.toDouble(), image.width.toDouble())); + } + + testWidgets('Capture specific image resolutions', + (WidgetTester tester) async { + final List cameras = + await CameraPlatform.instance.availableCameras(); + if (cameras.isEmpty) { + return; + } + for (final CameraDescription cameraDescription in cameras) { + bool previousPresetExactlySupported = true; + for (final MapEntry preset + in presetExpectedSizes.entries) { + final CameraController controller = + CameraController(cameraDescription, preset.key); + await controller.initialize(); + final bool presetExactlySupported = + await testCaptureImageResolution(controller, preset.key); + assert(!(!previousPresetExactlySupported && presetExactlySupported), + 'The camera took higher resolution pictures at a lower resolution.'); + previousPresetExactlySupported = presetExactlySupported; + await controller.dispose(); + } + } + }); + + // This tests that the capture is no bigger than the preset, since we have + // automatic code to fall back to smaller sizes when we need to. Returns + // whether the image is exactly the desired resolution. + Future testCaptureVideoResolution( + CameraController controller, ResolutionPreset preset) async { + final Size expectedSize = presetExpectedSizes[preset]!; + print( + 'Capturing video at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); + + // Take Video + await controller.startVideoRecording(); + sleep(const Duration(milliseconds: 300)); + final XFile file = await controller.stopVideoRecording(); + + // Load video metadata + final File videoFile = File(file.path); + final VideoPlayerController videoController = + VideoPlayerController.file(videoFile); + await videoController.initialize(); + final Size video = videoController.value.size; + + // Verify image dimensions are as expected + expect(video, isNotNull); + return assertExpectedDimensions( + expectedSize, Size(video.height, video.width)); + } + + testWidgets('Capture specific video resolutions', + (WidgetTester tester) async { + final List cameras = + await CameraPlatform.instance.availableCameras(); + if (cameras.isEmpty) { + return; + } + for (final CameraDescription cameraDescription in cameras) { + bool previousPresetExactlySupported = true; + for (final MapEntry preset + in presetExpectedSizes.entries) { + final CameraController controller = + CameraController(cameraDescription, preset.key); + await controller.initialize(); + await controller.prepareForVideoRecording(); + final bool presetExactlySupported = + await testCaptureVideoResolution(controller, preset.key); + assert(!(!previousPresetExactlySupported && presetExactlySupported), + 'The camera took higher resolution pictures at a lower resolution.'); + previousPresetExactlySupported = presetExactlySupported; + await controller.dispose(); + } + } + }); + + testWidgets('Pause and resume video recording', (WidgetTester tester) async { + final List cameras = + await CameraPlatform.instance.availableCameras(); + if (cameras.isEmpty) { + return; + } + + final CameraController controller = CameraController( + cameras[0], + ResolutionPreset.low, + enableAudio: false, + ); + + await controller.initialize(); + await controller.prepareForVideoRecording(); + + int startPause; + int timePaused = 0; + + await controller.startVideoRecording(); + final int recordingStart = DateTime.now().millisecondsSinceEpoch; + sleep(const Duration(milliseconds: 500)); + + await controller.pauseVideoRecording(); + startPause = DateTime.now().millisecondsSinceEpoch; + sleep(const Duration(milliseconds: 500)); + await controller.resumeVideoRecording(); + timePaused += DateTime.now().millisecondsSinceEpoch - startPause; + + sleep(const Duration(milliseconds: 500)); + + await controller.pauseVideoRecording(); + startPause = DateTime.now().millisecondsSinceEpoch; + sleep(const Duration(milliseconds: 500)); + await controller.resumeVideoRecording(); + timePaused += DateTime.now().millisecondsSinceEpoch - startPause; + + sleep(const Duration(milliseconds: 500)); + + final XFile file = await controller.stopVideoRecording(); + final int recordingTime = + DateTime.now().millisecondsSinceEpoch - recordingStart; + + final File videoFile = File(file.path); + final VideoPlayerController videoController = VideoPlayerController.file( + videoFile, + ); + await videoController.initialize(); + final int duration = videoController.value.duration.inMilliseconds; + await videoController.dispose(); + + expect(duration, lessThan(recordingTime - timePaused)); + }); + + /// Start streaming with specifying the ImageFormatGroup. + Future startStreaming(List cameras, + ImageFormatGroup? imageFormatGroup) async { + final CameraController controller = CameraController( + cameras.first, + ResolutionPreset.low, + enableAudio: false, + imageFormatGroup: imageFormatGroup, + ); + + await controller.initialize(); + final Completer _completer = Completer(); + + await controller.startImageStream((CameraImageData image) { + if (!_completer.isCompleted) { + Future(() async { + await controller.stopImageStream(); + await controller.dispose(); + }).then((Object? value) { + _completer.complete(image); + }); + } + }); + return _completer.future; + } + + testWidgets( + 'image streaming with imageFormatGroup', + (WidgetTester tester) async { + final List cameras = + await CameraPlatform.instance.availableCameras(); + if (cameras.isEmpty) { + return; + } + + CameraImageData _image = await startStreaming(cameras, null); + expect(_image, isNotNull); + expect(_image.format.group, ImageFormatGroup.bgra8888); + expect(_image.planes.length, 1); + + _image = await startStreaming(cameras, ImageFormatGroup.yuv420); + expect(_image, isNotNull); + expect(_image.format.group, ImageFormatGroup.yuv420); + expect(_image.planes.length, 2); + + _image = await startStreaming(cameras, ImageFormatGroup.bgra8888); + expect(_image, isNotNull); + expect(_image.format.group, ImageFormatGroup.bgra8888); + expect(_image.planes.length, 1); + }, + ); +} diff --git a/packages/camera/camera_avfoundation/example/ios/Flutter/AppFrameworkInfo.plist b/packages/camera/camera_avfoundation/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000000..3a9c234f96d4 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + UIRequiredDeviceCapabilities + + arm64 + + MinimumOSVersion + 9.0 + + diff --git a/packages/camera/camera_avfoundation/example/ios/Flutter/Debug.xcconfig b/packages/camera/camera_avfoundation/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000000..b2f5fae9c254 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,3 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/camera/camera_avfoundation/example/ios/Flutter/Release.xcconfig b/packages/camera/camera_avfoundation/example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000000..88c29144c836 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,3 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/packages/camera/camera_avfoundation/example/ios/Podfile b/packages/camera/camera_avfoundation/example/ios/Podfile new file mode 100644 index 000000000000..5bc7b7e85717 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Podfile @@ -0,0 +1,45 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.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 + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + + target 'RunnerTests' do + platform :ios, '9.0' + inherit! :search_paths + # Pods for testing + pod 'OCMock', '~> 3.8.1' + 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/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..03c80d79c578 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,712 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */; }; + 03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB766A2665316900CE5A93 /* CameraFocusTests.m */; }; + 03F6F8B226CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03F6F8B126CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m */; }; + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 236906D1621AE863A5B2E770 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 89D82918721FABF772705DB0 /* libPods-Runner.a */; }; + 25C3919135C3D981E6F800D0 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */; }; + 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 43ED1537282570DE00EB00DE /* AvailableCamerasTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */; }; + 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 788A065927B0E02900533D74 /* StreamingTest.m */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 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 */; }; + 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 */; }; + E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */; }; + E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */; }; + E0B0D2BB27DFF2AF00E71E4B /* CameraPermissionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */; }; + E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */; }; + E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */; }; + E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */; }; + E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */; }; + E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */; }; + E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */; }; + F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */ = {isa = PBXBuildFile; fileRef = F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 03BB766D2665316900CE5A93 /* 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 */ + 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraMethodChannelTests.m; sourceTree = ""; }; + 03BB76682665316900CE5A93 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 03BB766A2665316900CE5A93 /* CameraFocusTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraFocusTests.m; sourceTree = ""; }; + 03BB766C2665316900CE5A93 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CameraOrientationTests.m; sourceTree = ""; }; + 03F6F8B126CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeFlutterResultTests.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 = ""; }; + 14AE82C910C2A12F2ECB2094 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AvailableCamerasTest.m; sourceTree = ""; }; + 59848A7CA98C1FADF8840207 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 788A065927B0E02900533D74 /* StreamingTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StreamingTest.m; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 89D82918721FABF772705DB0 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 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; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTCamPhotoCaptureTests.m; sourceTree = ""; }; + E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTCamSampleBufferTests.m; sourceTree = ""; }; + E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPermissionTests.m; sourceTree = ""; }; + E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeMethodChannelTests.m; sourceTree = ""; }; + E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeTextureRegistryTests.m; sourceTree = ""; }; + E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThreadSafeEventChannelTests.m; sourceTree = ""; }; + E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CameraTestUtils.h; sourceTree = ""; }; + E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraTestUtils.m; sourceTree = ""; }; + E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPropertiesTests.m; sourceTree = ""; }; + E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CameraPreviewPauseTests.m; sourceTree = ""; }; + F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockFLTThreadSafeFlutterResult.h; sourceTree = ""; }; + F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockFLTThreadSafeFlutterResult.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 03BB76652665316900CE5A93 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 25C3919135C3D981E6F800D0 /* libPods-RunnerTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 236906D1621AE863A5B2E770 /* libPods-Runner.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 03BB76692665316900CE5A93 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 03BB766A2665316900CE5A93 /* CameraFocusTests.m */, + 03BB767226653ABE00CE5A93 /* CameraOrientationTests.m */, + 03BB766C2665316900CE5A93 /* Info.plist */, + 033B94BD269C40A200B4DF97 /* CameraMethodChannelTests.m */, + 03F6F8B126CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m */, + E0C6E1FF2770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m */, + E0C6E1FD2770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m */, + E0C6E1FE2770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m */, + E04F108527A87CA600573D0C /* FLTSavePhotoDelegateTests.m */, + E071CF7127B3061B006EF3BA /* FLTCamPhotoCaptureTests.m */, + E071CF7327B31DE4006EF3BA /* FLTCamSampleBufferTests.m */, + E0B0D2BA27DFF2AF00E71E4B /* CameraPermissionTests.m */, + E01EE4A72799F3A5008C1950 /* QueueUtilsTests.m */, + E0CDBAC027CD9729002561D9 /* CameraTestUtils.h */, + E0CDBAC127CD9729002561D9 /* CameraTestUtils.m */, + E487C85F26D686A10034AC92 /* CameraPreviewPauseTests.m */, + F6EE622E2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m */, + F63F9EED27143B19002479BF /* MockFLTThreadSafeFlutterResult.h */, + E032F24F279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m */, + E0F95E3C27A32AB900699390 /* CameraPropertiesTests.m */, + 788A065927B0E02900533D74 /* StreamingTest.m */, + 43ED1536282570DE00EB00DE /* AvailableCamerasTest.m */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 3242FD2B467C15C62200632F /* Frameworks */ = { + isa = PBXGroup; + children = ( + 89D82918721FABF772705DB0 /* libPods-Runner.a */, + 1944D8072499F3B5E7653D44 /* libPods-RunnerTests.a */, + ); + name = Frameworks; + 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 */, + 03BB76692665316900CE5A93 /* RunnerTests */, + 97C146EF1CF9000F007C117D /* Products */, + FD386F00E98D73419C929072 /* Pods */, + 3242FD2B467C15C62200632F /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 03BB76682665316900CE5A93 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + FD386F00E98D73419C929072 /* Pods */ = { + isa = PBXGroup; + children = ( + 59848A7CA98C1FADF8840207 /* Pods-Runner.debug.xcconfig */, + 14AE82C910C2A12F2ECB2094 /* Pods-Runner.release.xcconfig */, + 9C5CC6CAD53AD388B2694F3A /* Pods-RunnerTests.debug.xcconfig */, + A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 03BB76672665316900CE5A93 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 03BB76712665316900CE5A93 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 422786A96136AA9087A2041B /* [CP] Check Pods Manifest.lock */, + 03BB76642665316900CE5A93 /* Sources */, + 03BB76652665316900CE5A93 /* Frameworks */, + 03BB76662665316900CE5A93 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 03BB766E2665316900CE5A93 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = camera_exampleTests; + productReference = 03BB76682665316900CE5A93 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9872F2A25E8A171A111468CD /* [CP] Check Pods Manifest.lock */, + 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 = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = "The Flutter Authors"; + TargetAttributes = { + 03BB76672665316900CE5A93 = { + CreatedOnToolsVersion = 12.5; + ProvisioningStyle = Automatic; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 03BB76672665316900CE5A93 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 03BB76662665316900CE5A93 /* 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; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 422786A96136AA9087A2041B /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + 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"; + }; + 9872F2A25E8A171A111468CD /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 03BB76642665316900CE5A93 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 03F6F8B226CBB4670024B8D3 /* ThreadSafeFlutterResultTests.m in Sources */, + 033B94BE269C40A200B4DF97 /* CameraMethodChannelTests.m in Sources */, + E071CF7227B3061B006EF3BA /* FLTCamPhotoCaptureTests.m in Sources */, + E0F95E3D27A32AB900699390 /* CameraPropertiesTests.m in Sources */, + 03BB766B2665316900CE5A93 /* CameraFocusTests.m in Sources */, + E487C86026D686A10034AC92 /* CameraPreviewPauseTests.m in Sources */, + E071CF7427B31DE4006EF3BA /* FLTCamSampleBufferTests.m in Sources */, + E04F108627A87CA600573D0C /* FLTSavePhotoDelegateTests.m in Sources */, + 43ED1537282570DE00EB00DE /* AvailableCamerasTest.m in Sources */, + F6EE622F2710A6FC00905E4A /* MockFLTThreadSafeFlutterResult.m in Sources */, + E0CDBAC227CD9729002561D9 /* CameraTestUtils.m in Sources */, + 334733EA2668111C00DCC49E /* CameraOrientationTests.m in Sources */, + E032F250279F5E94009E9028 /* CameraCaptureSessionQueueRaceConditionTests.m in Sources */, + 788A065A27B0E02900533D74 /* StreamingTest.m in Sources */, + E0C6E2022770F01A00EA6AA3 /* ThreadSafeEventChannelTests.m in Sources */, + E0C6E2012770F01A00EA6AA3 /* ThreadSafeTextureRegistryTests.m in Sources */, + E0B0D2BB27DFF2AF00E71E4B /* CameraPermissionTests.m in Sources */, + E0C6E2002770F01A00EA6AA3 /* ThreadSafeMethodChannelTests.m in Sources */, + E01EE4A82799F3A5008C1950 /* QueueUtilsTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 03BB766E2665316900CE5A93 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 03BB766D2665316900CE5A93 /* 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 */ + 03BB766F2665316900CE5A93 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9C5CC6CAD53AD388B2694F3A /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = RunnerTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "dev.flutter.plugins.cameraExample.camera-exampleTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Debug; + }; + 03BB76702665316900CE5A93 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A24F9E418BA48BCC7409B117 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = RunnerTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "dev.flutter.plugins.cameraExample.camera-exampleTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Release; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = 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; + 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 = 9.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; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = 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; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = ""; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.cameraExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = ""; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.cameraExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 03BB76712665316900CE5A93 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 03BB766F2665316900CE5A93 /* Debug */, + 03BB76702665316900CE5A93 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..919434a6254f --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..f4b3c1099001 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/camera/camera_avfoundation/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/camera/camera_avfoundation/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/camera/camera_avfoundation/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/AppDelegate.h b/packages/camera/camera_avfoundation/example/ios/Runner/AppDelegate.h new file mode 100644 index 000000000000..0681d288bb70 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner/AppDelegate.h @@ -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 +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/AppDelegate.m b/packages/camera/camera_avfoundation/example/ios/Runner/AppDelegate.m new file mode 100644 index 000000000000..30b87969f44a --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner/AppDelegate.m @@ -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. + +#include "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + // Override point for customization after application launch. + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..d225b3c2cfe2 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,121 @@ +{ + "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" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..28c6bf03016f6c994b70f38d1b7346e5831b531f GIT binary patch literal 564 zcmV-40?Yl0P)Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV7ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 zcmds$`#;kQ7{|XelZftyR5~xW7?MLxS4^|Hw3&P7^y)@A9Fj{Xm1~_CIV^XZ%SLBn zA;!r`GqGHg=7>xrB{?psZQs88ZaedDoagm^KF{a*>G|dJWRSe^I$DNW008I^+;Kjt z>9p3GNR^I;v>5_`+91i(*G;u5|L+Bu6M=(afLjtkya#yZ175|z$pU~>2#^Z_pCZ7o z1c6UNcv2B3?; zX%qdxCXQpdKRz=#b*q0P%b&o)5ZrNZt7$fiETSK_VaY=mb4GK`#~0K#~9^ zcY!`#Af+4h?UMR-gMKOmpuYeN5P*RKF!(tb`)oe0j2BH1l?=>y#S5pMqkx6i{*=V9JF%>N8`ewGhRE(|WohnD59R^$_36{4>S zDFlPC5|k?;SPsDo87!B{6*7eqmMdU|QZ84>6)Kd9wNfh90=y=TFQay-0__>=<4pk& zYDjgIhL-jQ9o>z32K)BgAH+HxamL{ZL~ozu)Qqe@a`FpH=oQRA8=L-m-1dam(Ix2V z?du;LdMO+ooBelr^_y4{|44tmgH^2hSzPFd;U^!1p>6d|o)(-01z{i&Kj@)z-yfWQ)V#3Uo!_U}q3u`(fOs`_f^ueFii1xBNUB z6MecwJN$CqV&vhc+)b(p4NzGGEgwWNs z@*lUV6LaduZH)4_g!cE<2G6#+hJrWd5(|p1Z;YJ7ifVHv+n49btR}dq?HHDjl{m$T z!jLZcGkb&XS2OG~u%&R$(X+Z`CWec%QKt>NGYvd5g20)PU(dOn^7%@6kQb}C(%=vr z{?RP(z~C9DPnL{q^@pVw@|Vx~@3v!9dCaBtbh2EdtoNHm4kGxp>i#ct)7p|$QJs+U z-a3qtcPvhihub?wnJqEt>zC@)2suY?%-96cYCm$Q8R%-8$PZYsx3~QOLMDf(piXMm zB=<63yQk1AdOz#-qsEDX>>c)EES%$owHKue;?B3)8aRd}m~_)>SL3h2(9X;|+2#7X z+#2)NpD%qJvCQ0a-uzZLmz*ms+l*N}w)3LRQ*6>|Ub-fyptY(keUxw+)jfwF5K{L9 z|Cl_w=`!l_o><384d&?)$6Nh(GAm=4p_;{qVn#hI8lqewW7~wUlyBM-4Z|)cZr?Rh z=xZ&Ol>4(CU85ea(CZ^aO@2N18K>ftl8>2MqetAR53_JA>Fal`^)1Y--Am~UDa4th zKfCYpcXky$XSFDWBMIl(q=Mxj$iMBX=|j9P)^fDmF(5(5$|?Cx}DKEJa&XZP%OyE`*GvvYQ4PV&!g2|L^Q z?YG}tx;sY@GzMmsY`7r$P+F_YLz)(e}% zyakqFB<6|x9R#TdoP{R$>o7y(-`$$p0NxJ6?2B8tH)4^yF(WhqGZlM3=9Ibs$%U1w zWzcss*_c0=v_+^bfb`kBFsI`d;ElwiU%frgRB%qBjn@!0U2zZehBn|{%uNIKBA7n= zzE`nnwTP85{g;8AkYxA68>#muXa!G>xH22D1I*SiD~7C?7Za+9y7j1SHiuSkKK*^O zsZ==KO(Ua#?YUpXl{ViynyT#Hzk=}5X$e04O@fsMQjb}EMuPWFO0e&8(2N(29$@Vd zn1h8Yd>6z(*p^E{c(L0Lg=wVdupg!z@WG;E0k|4a%s7Up5C0c)55XVK*|x9RQeZ1J@1v9MX;>n34(i>=YE@Iur`0Vah(inE3VUFZNqf~tSz{1fz3Fsn_x4F>o(Yo;kpqvBe-sbwH(*Y zu$JOl0b83zu$JMvy<#oH^Wl>aWL*?aDwnS0iEAwC?DK@aT)GHRLhnz2WCvf3Ba;o=aY7 z2{Asu5MEjGOY4O#Ggz@@J;q*0`kd2n8I3BeNuMmYZf{}pg=jTdTCrIIYuW~luKecn z+E-pHY%ohj@uS0%^ z&(OxwPFPD$+#~`H?fMvi9geVLci(`K?Kj|w{rZ9JgthFHV+=6vMbK~0)Ea<&WY-NC zy-PnZft_k2tfeQ*SuC=nUj4H%SQ&Y$gbH4#2sT0cU0SdFs=*W*4hKGpuR1{)mV;Qf5pw4? zfiQgy0w3fC*w&Bj#{&=7033qFR*<*61B4f9K%CQvxEn&bsWJ{&winp;FP!KBj=(P6 z4Z_n4L7cS;ao2)ax?Tm|I1pH|uLpDSRVghkA_UtFFuZ0b2#>!8;>-_0ELjQSD-DRd z4im;599VHDZYtnWZGAB25W-e(2VrzEh|etsv2YoP#VbIZ{aFkwPrzJ#JvCvA*mXS& z`}Q^v9(W4GiSs}#s7BaN!WA2bniM$0J(#;MR>uIJ^uvgD3GS^%*ikdW6-!VFUU?JV zZc2)4cMsX@j z5HQ^e3BUzOdm}yC-xA%SY``k$rbfk z;CHqifhU*jfGM@DkYCecD9vl*qr58l6x<8URB=&%{!Cu3RO*MrKZ4VO}V6R0a zZw3Eg^0iKWM1dcTYZ0>N899=r6?+adUiBKPciJw}L$=1f4cs^bio&cr9baLF>6#BM z(F}EXe-`F=f_@`A7+Q&|QaZ??Txp_dB#lg!NH=t3$G8&06MFhwR=Iu*Im0s_b2B@| znW>X}sy~m#EW)&6E&!*0%}8UAS)wjt+A(io#wGI@Z2S+Ms1Cxl%YVE800007ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 zcmVjJBgitF5mAp-i>4+KS_oR{|13AP->1TD4=w)g|)JHOx|a2Wk1Va z!k)vP$UcQ#mdj%wNQoaJ!w>jv_6&JPyutpQps?s5dmDQ>`%?Bvj>o<%kYG!YW6H-z zu`g$@mp`;qDR!51QaS}|ZToSuAGcJ7$2HF0z`ln4t!#Yg46>;vGG9N9{V@9z#}6v* zfP?}r6b{*-C*)(S>NECI_E~{QYzN5SXRmVnP<=gzP+_Sp(Aza_hKlZ{C1D&l*(7IKXxQC1Z9#6wx}YrGcn~g%;icdw>T0Rf^w0{ z$_wn1J+C0@!jCV<%Go5LA45e{5gY9PvZp8uM$=1}XDI+9m7!A95L>q>>oe0$nC->i zeexUIvq%Uk<-$>DiDb?!In)lAmtuMWxvWlk`2>4lNuhSsjAf2*2tjT`y;@d}($o)S zn(+W&hJ1p0xy@oxP%AM15->wPLp{H!k)BdBD$toBpJh+crWdsNV)qsHaqLg2_s|Ih z`8E9z{E3sA!}5aKu?T!#enD(wLw?IT?k-yWVHZ8Akz4k5(TZJN^zZgm&zM28sfTD2BYJ|Fde3Xzh;;S` z=GXTnY4Xc)8nYoz6&vF;P7{xRF-{|2Xs5>a5)@BrnQ}I(_x7Cgpx#5&Td^4Q9_FnQ zX5so*;#8-J8#c$OlA&JyPp$LKUhC~-e~Ij!L%uSMu!-VZG7Hx-L{m2DVR2i=GR(_% zCVD!4N`I)&Q5S`?P&fQZ=4#Dgt_v2-DzkT}K(9gF0L(owe-Id$Rc2qZVLqI_M_DyO z9@LC#U28_LU{;wGZ&))}0R2P4MhajKCd^K#D+JJ&JIXZ_p#@+7J9A&P<0kdRujtQ_ zOy>3=C$kgi6$0pW06KaLz!21oOryKM3ZUOWqppndxfH}QpgjEJ`j7Tzn5bk6K&@RA?vl##y z$?V~1E(!wB5rH`>3nc&@)|#<1dN2cMzzm=PGhQ|Yppne(C-Vlt450IXc`J4R0W@I7 zd1e5uW6juvO%ni(WX7BsKx3MLngO7rHO;^R5I~0^nE^9^E_eYLgiR9&KnJ)pBbfno zSVnW$0R+&6jOOsZ82}nJ126+c|%svPo;TeUku<2G7%?$oft zyaO;tVo}(W)VsTUhq^XmFi#2z%-W9a{7mXn{uzivYQ_d6b7VJG{77naW(vHt-uhnY zVN#d!JTqVh(7r-lhtXVU6o})aZbDt_;&wJVGl2FKYFBFpU-#9U)z#(A%=IVnqytR$SY-sO( z($oNE09{D^@OuYPz&w~?9>Fl5`g9u&ecFGhqX=^#fmR=we0CJw+5xna*@oHnkahk+ z9aWeE3v|An+O5%?4fA&$Fgu~H_YmqR!yIU!bFCk4!#pAj%(lI(A5n)n@Id#M)O9Yx zJU9oKy{sRAIV3=5>(s8n{8ryJ!;ho}%pn6hZKTKbqk=&m=f*UnK$zW3YQP*)pw$O* zIfLA^!-bmBl6%d_n$#tP8Zd_(XdA*z*WH|E_yILwjtI~;jK#v-6jMl^?<%Y%`gvpwv&cFb$||^v4D&V=aNy?NGo620jL3VZnA%s zH~I|qPzB~e(;p;b^gJr7Ure#7?8%F0m4vzzPy^^(q4q1OdthF}Fi*RmVZN1OwTsAP zn9CZP`FazX3^kG(KodIZ=Kty8DLTy--UKfa1$6XugS zk%6v$Kmxt6U!YMx0JQ)0qX*{CXwZZk$vEROidEc7=J-1;peNat!vS<3P-FT5po>iE z!l3R+<`#x|+_hw!HjQGV=8!q|76y8L7N8gP3$%0kfush|u0uU^?dKBaeRSBUpOZ0c z62;D&Mdn2}N}xHRFTRI?zRv=>=AjHgH}`2k4WK=#AHB)UFrR-J87GgX*x5fL^W2#d z=(%K8-oZfMO=i{aWRDg=FX}UubM4eotRDcn;OR#{3q=*?3mE3_oJ-~prjhxh%PgQT zyn)Qozaq0@o&|LEgS{Ind4Swsr;b`u185hZPOBLL<`d2%^Yp1?oL)=jnLi;Zo0ZDliTtQ^b5SmfIMe{T==zZkbvn$KTQGlbG8w}s@M3TZnde;1Am46P3juKb zl9GU&3F=q`>j!`?SyH#r@O59%@aMX^rx}Nxe<>NqpUp5=lX1ojGDIR*-D^SDuvCKF z?3$xG(gVUsBERef_YjPFl^rU9EtD{pt z0CXwpN7BN3!8>hajGaTVk-wl=9rxmfWtIhC{mheHgStLi^+Nz12a?4r(fz)?3A%at zMlvQmL<2-R)-@G1wJ0^zQK%mR=r4d{Y3fHp){nWXUL#|CqXl(+v+qDh>FkF9`eWrW zfr^D%LNfOcTNvtx0JXR35J0~Jpi2#P3Q&80w+nqNfc}&G0A~*)lGHKv=^FE+b(37|)zL;KLF>oiGfb(?&1 zV3XRu!Sw>@quKiab%g6jun#oZ%!>V#A%+lNc?q>6+VvyAn=kf_6z^(TZUa4Eelh{{ zqFX-#dY(EV@7l$NE&kv9u9BR8&Ojd#ZGJ6l8_BW}^r?DIS_rU2(XaGOK z225E@kH5Opf+CgD^{y29jD4gHbGf{1MD6ggQ&%>UG4WyPh5q_tb`{@_34B?xfSO*| zZv8!)q;^o-bz`MuxXk*G^}(6)ACb@=Lfs`Hxoh>`Y0NE8QRQ!*p|SH@{r8=%RKd4p z+#Ty^-0kb=-H-O`nAA3_6>2z(D=~Tbs(n8LHxD0`R0_ATFqp-SdY3(bZ3;VUM?J=O zKCNsxsgt@|&nKMC=*+ZqmLHhX1KHbAJs{nGVMs6~TiF%Q)P@>!koa$%oS zjXa=!5>P`vC-a}ln!uH1ooeI&v?=?v7?1n~P(wZ~0>xWxd_Aw;+}9#eULM7M8&E?Y zC-ZLhi3RoM92SXUb-5i-Lmt5_rfjE{6y^+24`y$1lywLyHO!)Boa7438K4#iLe?rh z2O~YGSgFUBH?og*6=r9rme=peP~ah`(8Zt7V)j5!V0KPFf_mebo3z95U8(up$-+EA^9dTRLq>Yl)YMBuch9%=e5B`Vnb>o zt03=kq;k2TgGe4|lGne&zJa~h(UGutjP_zr?a7~#b)@15XNA>Dj(m=gg2Q5V4-$)D|Q9}R#002ovPDHLkV1o7DH3k3x literal 0 HcmV?d00001 diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 zcmV-m2cP(fP)x~L`~4d)Rspd&<9kFh{hn*KP1LP0~$;u(LfAu zp%fx&qLBcRHx$G|3q(bv@+b;o0*D|jwD-Q9uQR(l*ST}s+uPgQ-MeFwZ#GS?b332? z&Tk$&_miXn3IGq)AmQ)3sisq{raD4(k*bHvpCe-TdWq^NRTEVM)i9xbgQ&ccnUVx* zEY%vS%gDcSg=!tuIK8$Th2_((_h^+7;R|G{n06&O2#6%LK`a}n?h_fL18btz<@lFG za}xS}u?#DBMB> zw^b($1Z)`9G?eP95EKi&$eOy@K%h;ryrR3la%;>|o*>CgB(s>dDcNOXg}CK9SPmD? zmr-s{0wRmxUnbDrYfRvnZ@d z6johZ2sMX{YkGSKWd}m|@V7`Degt-43=2M?+jR%8{(H$&MLLmS;-|JxnX2pnz;el1jsvqQz}pGSF<`mqEXRQ5sC4#BbwnB_4` zc5bFE-Gb#JV3tox9fp-vVEN{(tOCpRse`S+@)?%pz+zVJXSooTrNCUg`R6`hxwb{) zC@{O6MKY8tfZ5@!yy=p5Y|#+myRL=^{tc(6YgAnkg3I(Cd!r5l;|;l-MQ8B`;*SCE z{u)uP^C$lOPM z5d~UhKhRRmvv{LIa^|oavk1$QiEApSrP@~Jjbg`<*dW4TO?4qG%a%sTPUFz(QtW5( zM)lA+5)0TvH~aBaOAs|}?u2FO;yc-CZ1gNM1dAxJ?%m?YsGR`}-xk2*dxC}r5j$d* zE!#Vtbo69h>V4V`BL%_&$} z+oJAo@jQ^Tk`;%xw-4G>hhb&)B?##U+(6Fi7nno`C<|#PVA%$Y{}N-?(Gc$1%tr4Pc}}hm~yY#fTOe!@v9s-ik$dX~|ygArPhByaXn8 zpI^FUjNWMsTFKTP3X7m?UK)3m zp6rI^_zxRYrx6_QmhoWoDR`fp4R7gu6;gdO)!KexaoO2D88F9x#TM1(9Bn7g;|?|o z)~$n&Lh#hCP6_LOPD>a)NmhW})LADx2kq=X7}7wYRj-0?dXr&bHaRWCfSqvzFa=sn z-8^gSyn-RmH=BZ{AJZ~!8n5621GbUJV7Qvs%JNv&$%Q17s_X%s-41vAPfIR>;x0Wlqr5?09S>x#%Qkt>?(&XjFRY}*L6BeQ3 z<6XEBh^S7>AbwGm@XP{RkeEKj6@_o%oV?hDuUpUJ+r#JZO?!IUc;r0R?>mi)*ZpQ) z#((dn=A#i_&EQn|hd)N$#A*fjBFuiHcYvo?@y1 z5|fV=a^a~d!c-%ZbMNqkMKiSzM{Yq=7_c&1H!mXk60Uv32dV;vMg&-kQ)Q{+PFtwc zj|-uQ;b^gts??J*9VxxOro}W~Q9j4Em|zSRv)(WSO9$F$s=Ydu%Q+5DOid~lwk&we zY%W(Z@ofdwPHncEZzZgmqS|!gTj3wQq9rxQy+^eNYKr1mj&?tm@wkO*9@UtnRMG>c aR{jt9+;fr}hV%pg00001^@s67{VYS000c7NklQEG_j zup^)eW&WUIApqy$=APz8jE@awGp)!bsTjDbrJO`$x^ZR^dr;>)LW>{ zs70vpsD38v)19rI=GNk1b(0?Js9~rjsQsu*K;@SD40RB-3^gKU-MYC7G!Bw{fZsqp zih4iIi;Hr_xZ033Iu{sQxLS=}yBXgLMn40d++>aQ0#%8D1EbGZp7+ z5=mK?t31BkVYbGOxE9`i748x`YgCMwL$qMsChbSGSE1`p{nSmadR zcQ#R)(?!~dmtD0+D2!K zR9%!Xp1oOJzm(vbLvT^$IKp@+W2=-}qTzTgVtQ!#Y7Gxz}stUIm<1;oBQ^Sh2X{F4ibaOOx;5ZGSNK z0maF^@(UtV$=p6DXLgRURwF95C=|U8?osGhgOED*b z7woJ_PWXBD>V-NjQAm{~T%sjyJ{5tn2f{G%?J!KRSrrGvQ1(^`YLA5B!~eycY(e5_ z*%aa{at13SxC(=7JT7$IQF~R3sy`Nn%EMv!$-8ZEAryB*yB1k&stni)=)8-ODo41g zkJu~roIgAih94tb=YsL%iH5@^b~kU9M-=aqgXIrbtxMpFy5mekFm#edF9z7RQ6V}R zBIhbXs~pMzt0VWy1Fi$^fh+1xxLDoK09&5&MJl(q#THjPm(0=z2H2Yfm^a&E)V+a5 zbi>08u;bJsDRUKR9(INSc7XyuWv(JsD+BB*0hS)FO&l&7MdViuur@-<-EHw>kHRGY zqoT}3fDv2-m{NhBG8X}+rgOEZ;amh*DqN?jEfQdqxdj08`Sr=C-KmT)qU1 z+9Cl)a1mgXxhQiHVB}l`m;-RpmKy?0*|yl?FXvJkFxuu!fKlcmz$kN(a}i*saM3nr z0!;a~_%Xqy24IxA2rz<+08=B-Q|2PT)O4;EaxP^6qixOv7-cRh?*T?zZU`{nIM-at zTKYWr9rJ=tppQ9I#Z#mLgINVB!pO-^FOcvFw6NhV0gztuO?g ztoA*C-52Q-Z-P#xB4HAY3KQVd%dz1S4PA3vHp0aa=zAO?FCt zC_GaTyVBg2F!bBr3U@Zy2iJgIAt>1sf$JWA9kh{;L+P*HfUBX1Zy{4MgNbDfBV_ly z!y#+753arsZUt@366jIC0klaC@ckuk!qu=pAyf7&QmiBUT^L1&tOHzsK)4n|pmrVT zs2($4=?s~VejTFHbFdDOwG;_58LkIj1Fh@{glkO#F1>a==ymJS$z;gdedT1zPx4Kj ztjS`y_C}%af-RtpehdQDt3a<=W5C4$)9W@QAse;WUry$WYmr51ml9lkeunUrE`-3e zmq1SgSOPNEE-Mf+AGJ$g0M;3@w!$Ej;hMh=v=I+Lpz^n%Pg^MgwyqOkNyu2c^of)C z1~ALor3}}+RiF*K4+4{(1%1j3pif1>sv0r^mTZ?5Jd-It!tfPfiG_p$AY*Vfak%FG z4z#;wLtw&E&?}w+eKG^=#jF7HQzr8rV0mY<1YAJ_uGz~$E13p?F^fPSzXSn$8UcI$ z8er9{5w5iv0qf8%70zV71T1IBB1N}R5Kp%NO0=5wJalZt8;xYp;b{1K) zHY>2wW-`Sl{=NpR%iu3(u6l&)rc%%cSA#aV7WCowfbFR4wcc{LQZv~o1u_`}EJA3>ki`?9CKYTA!rhO)if*zRdd}Kn zEPfYbhoVE~!FI_2YbC5qAj1kq;xP6%J8+?2PAs?`V3}nyFVD#sV3+uP`pi}{$l9U^ zSz}_M9f7RgnnRhaoIJgT8us!1aB&4!*vYF07Hp&}L zCRlop0oK4DL@ISz{2_BPlezc;xj2|I z23RlDNpi9LgTG_#(w%cMaS)%N`e>~1&a3<{Xy}>?WbF>OOLuO+j&hc^YohQ$4F&ze z+hwnro1puQjnKm;vFG~o>`kCeUIlkA-2tI?WBKCFLMBY=J{hpSsQ=PDtU$=duS_hq zHpymHt^uuV1q@uc4bFb{MdG*|VoW@15Osrqt2@8ll0qO=j*uOXn{M0UJX#SUztui9FN4)K3{9!y8PC-AHHvpVTU;x|-7P+taAtyglk#rjlH2 z5Gq8ik}BPaGiM{#Woyg;*&N9R2{J0V+WGB69cEtH7F?U~Kbi6ksi*`CFXsi931q7Y zGO82?whBhN%w1iDetv%~wM*Y;E^)@Vl?VDj-f*RX>{;o_=$fU!&KAXbuadYZ46Zbg z&6jMF=49$uL^73y;;N5jaHYv)BTyfh&`qVLYn?`o6BCA_z-0niZz=qPG!vonK3MW_ zo$V96zM!+kJRs{P-5-rQVse0VBH*n6A58)4uc&gfHMa{gIhV2fGf{st>E8sKyP-$8zp~wJX^A*@DI&-;8>gANXZj zU)R+Y)PB?=)a|Kj>8NXEu^S_h^7R`~Q&7*Kn!xyvzVv&^>?^iu;S~R2e-2fJx-oUb cX)(b1KSk$MOV07*qoM6N<$f&6$jw%VRuvdN2+38CZWny1cRtlsl+0_KtW)EU14Ei(F!UtWuj4IK+3{sK@>rh zs1Z;=(DD&U6+tlyL?UnHVN^&g6QhFi2#HS+*qz;(>63G(`|jRtW|nz$Pv7qTovP!^ zP_jES{mr@O-02w%!^a?^1ZP!_KmQiz0L~jZ=W@Qt`8wzOoclQsAS<5YdH;a(4bGLE zk8s}1If(PSIgVi!XE!5kA?~z*sobvNyohr;=Q_@h2@$6Flyej3J)D-6YfheRGl`HEcPk|~huT_2-U?PfL=4BPV)f1o!%rQ!NMt_MYw-5bUSwQ9Z&zC>u zOrl~UJglJNa%f50Ok}?WB{on`Ci`p^Y!xBA?m@rcJXLxtrE0FhRF3d*ir>yzO|BD$ z3V}HpFcCh6bTzY}Nt_(W%QYd3NG)jJ4<`F<1Od) zfQblTdC&h2lCz`>y?>|9o2CdvC8qZeIZt%jN;B7Hdn2l*k4M4MFEtq`q_#5?}c$b$pf_3y{Y!cRDafZBEj-*OD|gz#PBDeu3QoueOesLzB+O zxjf2wvf6Wwz>@AiOo2mO4=TkAV+g~%_n&R;)l#!cBxjuoD$aS-`IIJv7cdX%2{WT7 zOm%5rs(wqyPE^k5SIpUZ!&Lq4<~%{*>_Hu$2|~Xa;iX*tz8~G6O3uFOS?+)tWtdi| zV2b#;zRN!m@H&jd=!$7YY6_}|=!IU@=SjvGDFtL;aCtw06U;-v^0%k0FOyESt z1Wv$={b_H&8FiRV?MrzoHWd>%v6KTRU;-v^Miiz+@q`(BoT!+<37CKhoKb)|8!+RG z6BQFU^@fRW;s8!mOf2QViKQGk0TVER6EG1`#;Nm39Do^PoT!+<37AD!%oJe86(=et zZ~|sLzU>V-qYiU6V8$0GmU7_K8|Fd0B?+9Un1BhKAz#V~Fk^`mJtlCX#{^8^M8!me z8Yg;8-~>!e<-iG;h*0B1kBKm}hItVGY6WnjVpgnTTAC$rqQ^v)4KvOtpY|sIj@WYg zyw##ZZ5AC2IKNC;^hwg9BPk0wLStlmBr;E|$5GoAo$&Ui_;S9WY62n3)i49|T%C#i017z3J=$RF|KyZWnci*@lW4 z=AKhNN6+m`Q!V3Ye68|8y@%=am>YD0nG99M)NWc20%)gwO!96j7muR}Fr&54SxKP2 zP30S~lt=a*qDlbu3+Av57=9v&vr<6g0&`!8E2fq>I|EJGKs}t|{h7+KT@)LfIV-3K zK)r_fr2?}FFyn*MYoLC>oV-J~eavL2ho4a4^r{E-8m2hi>~hA?_vIG4a*KT;2eyl1 zh_hUvUJpNCFwBvRq5BI*srSle>c6%n`#VNsyC|MGa{(P&08p=C9+WUw9Hl<1o9T4M zdD=_C0F7#o8A_bRR?sFNmU0R6tW`ElnF8p53IdHo#S9(JoZCz}fHwJ6F<&?qrpVqE zte|m%89JQD+XwaPU#%#lVs-@-OL);|MdfINd6!XwP2h(eyafTUsoRkA%&@fe?9m@jw-v(yTTiV2(*fthQH9}SqmsRPVnwwbV$1E(_lkmo&S zF-truCU914_$jpqjr(>Ha4HkM4YMT>m~NosUu&UZ>zirfHo%N6PPs9^_o$WqPA0#5 z%tG>qFCL+b*0s?sZ;Sht0nE7Kl>OVXy=gjWxxK;OJ3yGd7-pZf7JYNcZo2*1SF`u6 zHJyRRxGw9mDlOiXqVMsNe#WX`fC`vrtjSQ%KmLcl(lC>ZOQzG^%iql2w-f_K@r?OE zwCICifM#L-HJyc7Gm>Ern?+Sk3&|Khmu4(~3qa$(m6Ub^U0E5RHq49za|XklN#?kP zl;EstdW?(_4D>kwjWy2f!LM)y?F94kyU3`W!6+AyId-89v}sXJpuic^NLL7GJItl~ zsiuB98AI-(#Mnm|=A-R6&2fwJ0JVSY#Q>&3$zFh|@;#%0qeF=j5Ajq@4i0tIIW z&}sk$&fGwoJpe&u-JeGLi^r?dO`m=y(QO{@h zQqAC7$rvz&5+mo3IqE?h=a~6m>%r5Quapvzq;{y~p zJpyXOBgD9VrW7@#p6l7O?o3feml(DtSL>D^R) zZUY%T2b0-vBAFN7VB;M88!~HuOXi4KcI6aRQ&h|XQ0A?m%j2=l1f0cGP}h(oVfJ`N zz#PpmFC*ieab)zJK<4?^k=g%OjPnkANzbAbmGZHoVRk*mTfm75s_cWVa`l*f$B@xu z5E*?&@seIo#*Y~1rBm!7sF9~~u6Wrj5oICUOuz}CS)jdNIznfzCA(stJ(7$c^e5wN z?lt>eYgbA!kvAR7zYSD&*r1$b|(@;9dcZ^67R0 zXAXJKa|5Sdmj!g578Nwt6d$sXuc&MWezA0Whd`94$h{{?1IwXP4)Tx4obDK%xoFZ_Z zjjHJ_P@R_e5blG@yEjnaJb`l;s%Lb2&=8$&Ct-fV`E^4CUs)=jTk!I}2d&n!f@)bm z@ z_4Dc86+3l2*p|~;o-Sb~oXb_RuLmoifDU^&Te$*FevycC0*nE3Xws8gsWp|Rj2>SM zns)qcYj?^2sd8?N!_w~4v+f-HCF|a$TNZDoNl$I1Uq87euoNgKb6&r26TNrfkUa@o zfdiFA@p{K&mH3b8i!lcoz)V{n8Q@g(vR4ns4r6w;K z>1~ecQR0-<^J|Ndg5fvVUM9g;lbu-){#ghGw(fg>L zh)T5Ljb%lWE;V9L!;Cqk>AV1(rULYF07ZBJbGb9qbSoLAd;in9{)95YqX$J43-dY7YU*k~vrM25 zxh5_IqO0LYZW%oxQ5HOzmk4x{atE*vipUk}sh88$b2tn?!ujEHn`tQLe&vo}nMb&{ zio`xzZ&GG6&ZyN3jnaQy#iVqXE9VT(3tWY$n-)uWDQ|tc{`?fq2F`oQ{;d3aWPg4Hp-(iE{ry>MIPWL> iW8Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/camera/camera_avfoundation/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/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/camera/camera_avfoundation/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/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 000000000000..89c2725b70f1 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/camera/camera_avfoundation/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000000..f2e259c7c939 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Base.lproj/Main.storyboard b/packages/camera/camera_avfoundation/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/Info.plist b/packages/camera/camera_avfoundation/example/ios/Runner/Info.plist new file mode 100644 index 000000000000..ff2e341a1803 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner/Info.plist @@ -0,0 +1,56 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + camera_example + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSApplicationCategoryType + + LSRequiresIPhoneOS + + NSCameraUsageDescription + Can I use the camera please? Only for demo purpose of the app + NSMicrophoneUsageDescription + Only for demo purpose of the app + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/packages/camera/camera_avfoundation/example/ios/Runner/main.m b/packages/camera/camera_avfoundation/example/ios/Runner/main.m new file mode 100644 index 000000000000..d1224fea37ed --- /dev/null +++ b/packages/camera/camera_avfoundation/example/ios/Runner/main.m @@ -0,0 +1,19 @@ +// 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 +#import "AppDelegate.h" + +int main(int argc, char *argv[]) { + @autoreleasepool { + // The setup logic in `AppDelegate::didFinishLaunchingWithOptions:` eventually sends camera + // operations on the background queue, which would run concurrently with the test cases during + // unit tests, making the debugging process confusing. This setup is actually not necessary for + // the unit tests, so it is better to skip the AppDelegate when running unit tests. + BOOL isTesting = NSClassFromString(@"XCTestCase") != nil; + return UIApplicationMain(argc, argv, nil, + isTesting ? nil : NSStringFromClass([AppDelegate class])); + } +} diff --git a/packages/camera/camera/example/ios/RunnerTests/AvailableCamerasTest.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTest.m similarity index 98% rename from packages/camera/camera/example/ios/RunnerTests/AvailableCamerasTest.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTest.m index 2caac0a03891..6074b871cd02 100644 --- a/packages/camera/camera/example/ios/RunnerTests/AvailableCamerasTest.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/AvailableCamerasTest.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import XCTest; @import AVFoundation; #import diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m similarity index 97% rename from packages/camera/camera/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m index e99ce4e89a94..89f40307933c 100644 --- a/packages/camera/camera/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraCaptureSessionQueueRaceConditionTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import XCTest; @interface CameraCaptureSessionQueueRaceConditionTests : XCTestCase diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraExposureTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.m similarity index 98% rename from packages/camera/camera/example/ios/RunnerTests/CameraExposureTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.m index ee43d3f155f4..7b641a5746c0 100644 --- a/packages/camera/camera/example/ios/RunnerTests/CameraExposureTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraExposureTests.m @@ -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 camera; +@import camera_avfoundation; @import XCTest; @import AVFoundation; #import diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraFocusTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m similarity index 98% rename from packages/camera/camera/example/ios/RunnerTests/CameraFocusTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m index e0f5fdaa3c97..1b6ada564dd8 100644 --- a/packages/camera/camera/example/ios/RunnerTests/CameraFocusTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraFocusTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import XCTest; @import AVFoundation; #import diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraMethodChannelTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraMethodChannelTests.m similarity index 96% rename from packages/camera/camera/example/ios/RunnerTests/CameraMethodChannelTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraMethodChannelTests.m index 62b9cda2ef7b..bd20134db561 100644 --- a/packages/camera/camera/example/ios/RunnerTests/CameraMethodChannelTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraMethodChannelTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import XCTest; @import AVFoundation; #import diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraOrientationTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.m similarity index 98% rename from packages/camera/camera/example/ios/RunnerTests/CameraOrientationTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.m index 50f3416e7869..29fc325dffc0 100644 --- a/packages/camera/camera/example/ios/RunnerTests/CameraOrientationTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import XCTest; @import Flutter; diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.m similarity index 99% rename from packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.m index 541e0288254c..24ca5b6525c9 100644 --- a/packages/camera/camera/example/ios/RunnerTests/CameraPermissionTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPermissionTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import AVFoundation; @import XCTest; #import diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraPreviewPauseTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m similarity index 93% rename from packages/camera/camera/example/ios/RunnerTests/CameraPreviewPauseTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m index 2c1adbef468b..1dfc90b27f1b 100644 --- a/packages/camera/camera/example/ios/RunnerTests/CameraPreviewPauseTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPreviewPauseTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import XCTest; @import AVFoundation; #import diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraPropertiesTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.m similarity index 99% rename from packages/camera/camera/example/ios/RunnerTests/CameraPropertiesTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.m index 865791586382..18c01e599907 100644 --- a/packages/camera/camera/example/ios/RunnerTests/CameraPropertiesTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraPropertiesTests.m @@ -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 camera.Test; +@import camera_avfoundation.Test; @import AVFoundation; @import XCTest; diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.h b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h similarity index 95% rename from packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.h rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h index 9fe67dc650e2..f2d46114a0c5 100644 --- a/packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.h +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.h @@ -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 camera; +@import camera_avfoundation; NS_ASSUME_NONNULL_BEGIN diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/CameraTestUtils.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraTestUtils.m diff --git a/packages/camera/camera/example/ios/RunnerTests/CameraUtilTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraUtilTests.m similarity index 98% rename from packages/camera/camera/example/ios/RunnerTests/CameraUtilTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraUtilTests.m index 380f6e93de58..d1a835c36efe 100644 --- a/packages/camera/camera/example/ios/RunnerTests/CameraUtilTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraUtilTests.m @@ -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 camera; +@import camera_avfoundation; @import XCTest; @import AVFoundation; #import diff --git a/packages/camera/camera/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m similarity index 98% rename from packages/camera/camera/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m index ed3e6a9793fd..8a7c34cc2731 100644 --- a/packages/camera/camera/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamPhotoCaptureTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import AVFoundation; @import XCTest; #import diff --git a/packages/camera/camera/example/ios/RunnerTests/FLTCamSampleBufferTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m similarity index 96% rename from packages/camera/camera/example/ios/RunnerTests/FLTCamSampleBufferTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m index 8f65c4e9eaa5..94426ab3aeb8 100644 --- a/packages/camera/camera/example/ios/RunnerTests/FLTCamSampleBufferTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTCamSampleBufferTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import AVFoundation; @import XCTest; #import diff --git a/packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m similarity index 98% rename from packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m index a70a572ac0f2..f7633591ccb6 100644 --- a/packages/camera/camera/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/FLTSavePhotoDelegateTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import AVFoundation; @import XCTest; #import diff --git a/packages/camera/camera/example/ios/RunnerTests/Info.plist b/packages/camera/camera_avfoundation/example/ios/RunnerTests/Info.plist similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/Info.plist rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/Info.plist diff --git a/packages/camera/camera/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.h b/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.h similarity index 100% rename from packages/camera/camera/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.h rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.h diff --git a/packages/camera/camera/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.m similarity index 95% rename from packages/camera/camera/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.m index da2fc2d936ba..d3d7b6ac15b3 100644 --- a/packages/camera/camera/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/MockFLTThreadSafeFlutterResult.m @@ -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 camera; +@import camera_avfoundation; @import XCTest; #import "MockFLTThreadSafeFlutterResult.h" diff --git a/packages/camera/camera/example/ios/RunnerTests/QueueUtilsTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.m similarity index 97% rename from packages/camera/camera/example/ios/RunnerTests/QueueUtilsTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.m index de11b4f6961f..a9fc7396bb99 100644 --- a/packages/camera/camera/example/ios/RunnerTests/QueueUtilsTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/QueueUtilsTests.m @@ -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 camera; +@import camera_avfoundation; @import XCTest; @interface QueueUtilsTests : XCTestCase diff --git a/packages/camera/camera/example/ios/RunnerTests/StreamingTest.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTest.m similarity index 98% rename from packages/camera/camera/example/ios/RunnerTests/StreamingTest.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTest.m index 1843cce12152..14a611852dcc 100644 --- a/packages/camera/camera/example/ios/RunnerTests/StreamingTest.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/StreamingTest.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import camera; -@import camera.Test; +@import camera_avfoundation; +@import camera_avfoundation.Test; @import XCTest; @import AVFoundation; #import diff --git a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeEventChannelTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.m similarity index 98% rename from packages/camera/camera/example/ios/RunnerTests/ThreadSafeEventChannelTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.m index dd7ca39c2e98..e7460de6977e 100644 --- a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeEventChannelTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.m @@ -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 camera; +@import camera_avfoundation; @import XCTest; #import diff --git a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m similarity index 99% rename from packages/camera/camera/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m index 5fdbd49e5d40..b8de19ce4ab5 100644 --- a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeFlutterResultTests.m @@ -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 camera; +@import camera_avfoundation; @import XCTest; @interface ThreadSafeFlutterResultTests : XCTestCase diff --git a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m similarity index 98% rename from packages/camera/camera/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m index 5075be7a81e2..ce1b641cef6f 100644 --- a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeMethodChannelTests.m @@ -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 camera; +@import camera_avfoundation; @import XCTest; #import diff --git a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m similarity index 99% rename from packages/camera/camera/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m rename to packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m index 067ebab3642f..31f196ffdb9e 100644 --- a/packages/camera/camera/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeTextureRegistryTests.m @@ -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 camera; +@import camera_avfoundation; @import XCTest; #import diff --git a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart new file mode 100644 index 000000000000..5a7a79c8d96c --- /dev/null +++ b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart @@ -0,0 +1,437 @@ +// 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:async'; + +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:quiver/core.dart'; + +/// The state of a [CameraController]. +class CameraValue { + /// Creates a new camera controller state. + const CameraValue({ + required this.isInitialized, + this.previewSize, + required this.isRecordingVideo, + required this.isTakingPicture, + required this.isStreamingImages, + required this.isRecordingPaused, + required this.flashMode, + required this.exposureMode, + required this.focusMode, + required this.deviceOrientation, + this.lockedCaptureOrientation, + this.recordingOrientation, + this.isPreviewPaused = false, + this.previewPauseOrientation, + }); + + /// Creates a new camera controller state for an uninitialized controller. + const CameraValue.uninitialized() + : this( + isInitialized: false, + isRecordingVideo: false, + isTakingPicture: false, + isStreamingImages: false, + isRecordingPaused: false, + flashMode: FlashMode.auto, + exposureMode: ExposureMode.auto, + focusMode: FocusMode.auto, + deviceOrientation: DeviceOrientation.portraitUp, + isPreviewPaused: false, + ); + + /// True after [CameraController.initialize] has completed successfully. + final bool isInitialized; + + /// True when a picture capture request has been sent but as not yet returned. + final bool isTakingPicture; + + /// True when the camera is recording (not the same as previewing). + final bool isRecordingVideo; + + /// True when images from the camera are being streamed. + final bool isStreamingImages; + + /// True when video recording is paused. + final bool isRecordingPaused; + + /// True when the preview widget has been paused manually. + final bool isPreviewPaused; + + /// Set to the orientation the preview was paused in, if it is currently paused. + final DeviceOrientation? previewPauseOrientation; + + /// The size of the preview in pixels. + /// + /// Is `null` until [isInitialized] is `true`. + final Size? previewSize; + + /// The flash mode the camera is currently set to. + final FlashMode flashMode; + + /// The exposure mode the camera is currently set to. + final ExposureMode exposureMode; + + /// The focus mode the camera is currently set to. + final FocusMode focusMode; + + /// The current device UI orientation. + final DeviceOrientation deviceOrientation; + + /// The currently locked capture orientation. + final DeviceOrientation? lockedCaptureOrientation; + + /// Whether the capture orientation is currently locked. + bool get isCaptureOrientationLocked => lockedCaptureOrientation != null; + + /// The orientation of the currently running video recording. + final DeviceOrientation? recordingOrientation; + + /// Creates a modified copy of the object. + /// + /// Explicitly specified fields get the specified value, all other fields get + /// the same value of the current object. + CameraValue copyWith({ + bool? isInitialized, + bool? isRecordingVideo, + bool? isTakingPicture, + bool? isStreamingImages, + Size? previewSize, + bool? isRecordingPaused, + FlashMode? flashMode, + ExposureMode? exposureMode, + FocusMode? focusMode, + bool? exposurePointSupported, + bool? focusPointSupported, + DeviceOrientation? deviceOrientation, + Optional? lockedCaptureOrientation, + Optional? recordingOrientation, + bool? isPreviewPaused, + Optional? previewPauseOrientation, + }) { + return CameraValue( + isInitialized: isInitialized ?? this.isInitialized, + previewSize: previewSize ?? this.previewSize, + isRecordingVideo: isRecordingVideo ?? this.isRecordingVideo, + isTakingPicture: isTakingPicture ?? this.isTakingPicture, + isStreamingImages: isStreamingImages ?? this.isStreamingImages, + isRecordingPaused: isRecordingPaused ?? this.isRecordingPaused, + flashMode: flashMode ?? this.flashMode, + exposureMode: exposureMode ?? this.exposureMode, + focusMode: focusMode ?? this.focusMode, + deviceOrientation: deviceOrientation ?? this.deviceOrientation, + lockedCaptureOrientation: lockedCaptureOrientation == null + ? this.lockedCaptureOrientation + : lockedCaptureOrientation.orNull, + recordingOrientation: recordingOrientation == null + ? this.recordingOrientation + : recordingOrientation.orNull, + isPreviewPaused: isPreviewPaused ?? this.isPreviewPaused, + previewPauseOrientation: previewPauseOrientation == null + ? this.previewPauseOrientation + : previewPauseOrientation.orNull, + ); + } + + @override + String toString() { + return '${objectRuntimeType(this, 'CameraValue')}(' + 'isRecordingVideo: $isRecordingVideo, ' + 'isInitialized: $isInitialized, ' + 'previewSize: $previewSize, ' + 'isStreamingImages: $isStreamingImages, ' + 'flashMode: $flashMode, ' + 'exposureMode: $exposureMode, ' + 'focusMode: $focusMode, ' + 'deviceOrientation: $deviceOrientation, ' + 'lockedCaptureOrientation: $lockedCaptureOrientation, ' + 'recordingOrientation: $recordingOrientation, ' + 'isPreviewPaused: $isPreviewPaused, ' + 'previewPausedOrientation: $previewPauseOrientation)'; + } +} + +/// Controls a device camera. +/// +/// This is a stripped-down version of the app-facing controller to serve as a +/// utility for the example and integration tests. It wraps only the calls that +/// have state associated with them, to consolidate tracking of camera state +/// outside of the overall example code. +class CameraController extends ValueNotifier { + /// Creates a new camera controller in an uninitialized state. + CameraController( + this.description, + this.resolutionPreset, { + this.enableAudio = true, + this.imageFormatGroup, + }) : super(const CameraValue.uninitialized()); + + /// The properties of the camera device controlled by this controller. + final CameraDescription description; + + /// The resolution this controller is targeting. + /// + /// This resolution preset is not guaranteed to be available on the device, + /// if unavailable a lower resolution will be used. + /// + /// See also: [ResolutionPreset]. + final ResolutionPreset resolutionPreset; + + /// Whether to include audio when recording a video. + final bool enableAudio; + + /// The [ImageFormatGroup] describes the output of the raw image format. + /// + /// When null the imageFormat will fallback to the platforms default. + final ImageFormatGroup? imageFormatGroup; + + late int _cameraId; + + bool _isDisposed = false; + StreamSubscription? _imageStreamSubscription; + FutureOr? _initCalled; + StreamSubscription? + _deviceOrientationSubscription; + + /// The camera identifier with which the controller is associated. + int get cameraId => _cameraId; + + /// Initializes the camera on the device. + Future initialize() async { + final Completer _initializeCompleter = + Completer(); + + _deviceOrientationSubscription = CameraPlatform.instance + .onDeviceOrientationChanged() + .listen((DeviceOrientationChangedEvent event) { + value = value.copyWith( + deviceOrientation: event.orientation, + ); + }); + + _cameraId = await CameraPlatform.instance.createCamera( + description, + resolutionPreset, + enableAudio: enableAudio, + ); + + CameraPlatform.instance + .onCameraInitialized(_cameraId) + .first + .then((CameraInitializedEvent event) { + _initializeCompleter.complete(event); + }); + + await CameraPlatform.instance.initializeCamera( + _cameraId, + imageFormatGroup: imageFormatGroup ?? ImageFormatGroup.unknown, + ); + + value = value.copyWith( + isInitialized: true, + previewSize: await _initializeCompleter.future + .then((CameraInitializedEvent event) => Size( + event.previewWidth, + event.previewHeight, + )), + exposureMode: await _initializeCompleter.future + .then((CameraInitializedEvent event) => event.exposureMode), + focusMode: await _initializeCompleter.future + .then((CameraInitializedEvent event) => event.focusMode), + exposurePointSupported: await _initializeCompleter.future + .then((CameraInitializedEvent event) => event.exposurePointSupported), + focusPointSupported: await _initializeCompleter.future + .then((CameraInitializedEvent event) => event.focusPointSupported), + ); + + _initCalled = true; + } + + /// Prepare the capture session for video recording. + Future prepareForVideoRecording() async { + await CameraPlatform.instance.prepareForVideoRecording(); + } + + /// Pauses the current camera preview + Future pausePreview() async { + await CameraPlatform.instance.pausePreview(_cameraId); + value = value.copyWith( + isPreviewPaused: true, + previewPauseOrientation: Optional.of( + value.lockedCaptureOrientation ?? value.deviceOrientation)); + } + + /// Resumes the current camera preview + Future resumePreview() async { + await CameraPlatform.instance.resumePreview(_cameraId); + value = value.copyWith( + isPreviewPaused: false, + previewPauseOrientation: const Optional.absent()); + } + + /// Captures an image and returns the file where it was saved. + /// + /// Throws a [CameraException] if the capture fails. + Future takePicture() async { + value = value.copyWith(isTakingPicture: true); + final XFile file = await CameraPlatform.instance.takePicture(_cameraId); + value = value.copyWith(isTakingPicture: false); + return file; + } + + /// Start streaming images from platform camera. + Future startImageStream( + Function(CameraImageData image) onAvailable) async { + _imageStreamSubscription = CameraPlatform.instance + .onStreamedFrameAvailable(_cameraId) + .listen((CameraImageData imageData) { + onAvailable(imageData); + }); + value = value.copyWith(isStreamingImages: true); + } + + /// Stop streaming images from platform camera. + Future stopImageStream() async { + value = value.copyWith(isStreamingImages: false); + await _imageStreamSubscription?.cancel(); + _imageStreamSubscription = null; + } + + /// Start a video recording. + /// + /// The video is returned as a [XFile] after calling [stopVideoRecording]. + /// Throws a [CameraException] if the capture fails. + Future startVideoRecording() async { + await CameraPlatform.instance.startVideoRecording(_cameraId); + value = value.copyWith( + isRecordingVideo: true, + isRecordingPaused: false, + recordingOrientation: Optional.of( + value.lockedCaptureOrientation ?? value.deviceOrientation)); + } + + /// Stops the video recording and returns the file where it was saved. + /// + /// Throws a [CameraException] if the capture failed. + Future stopVideoRecording() async { + final XFile file = + await CameraPlatform.instance.stopVideoRecording(_cameraId); + value = value.copyWith( + isRecordingVideo: false, + recordingOrientation: const Optional.absent(), + ); + return file; + } + + /// Pause video recording. + /// + /// This feature is only available on iOS and Android sdk 24+. + Future pauseVideoRecording() async { + await CameraPlatform.instance.pauseVideoRecording(_cameraId); + value = value.copyWith(isRecordingPaused: true); + } + + /// Resume video recording after pausing. + /// + /// This feature is only available on iOS and Android sdk 24+. + Future resumeVideoRecording() async { + await CameraPlatform.instance.resumeVideoRecording(_cameraId); + value = value.copyWith(isRecordingPaused: false); + } + + /// Returns a widget showing a live camera preview. + Widget buildPreview() { + return CameraPlatform.instance.buildPreview(_cameraId); + } + + /// Sets the flash mode for taking pictures. + Future setFlashMode(FlashMode mode) async { + await CameraPlatform.instance.setFlashMode(_cameraId, mode); + value = value.copyWith(flashMode: mode); + } + + /// Sets the exposure mode for taking pictures. + Future setExposureMode(ExposureMode mode) async { + await CameraPlatform.instance.setExposureMode(_cameraId, mode); + value = value.copyWith(exposureMode: mode); + } + + /// Sets the exposure offset for the selected camera. + Future setExposureOffset(double offset) async { + // Check if offset is in range + final List range = await Future.wait(>[ + CameraPlatform.instance.getMinExposureOffset(_cameraId), + CameraPlatform.instance.getMaxExposureOffset(_cameraId) + ]); + + // Round to the closest step if needed + final double stepSize = + await CameraPlatform.instance.getExposureOffsetStepSize(_cameraId); + if (stepSize > 0) { + final double inv = 1.0 / stepSize; + double roundedOffset = (offset * inv).roundToDouble() / inv; + if (roundedOffset > range[1]) { + roundedOffset = (offset * inv).floorToDouble() / inv; + } else if (roundedOffset < range[0]) { + roundedOffset = (offset * inv).ceilToDouble() / inv; + } + offset = roundedOffset; + } + + return CameraPlatform.instance.setExposureOffset(_cameraId, offset); + } + + /// Locks the capture orientation. + /// + /// If [orientation] is omitted, the current device orientation is used. + Future lockCaptureOrientation() async { + await CameraPlatform.instance + .lockCaptureOrientation(_cameraId, value.deviceOrientation); + value = value.copyWith( + lockedCaptureOrientation: + Optional.of(value.deviceOrientation)); + } + + /// Unlocks the capture orientation. + Future unlockCaptureOrientation() async { + await CameraPlatform.instance.unlockCaptureOrientation(_cameraId); + value = value.copyWith( + lockedCaptureOrientation: const Optional.absent()); + } + + /// Sets the focus mode for taking pictures. + Future setFocusMode(FocusMode mode) async { + await CameraPlatform.instance.setFocusMode(_cameraId, mode); + value = value.copyWith(focusMode: mode); + } + + /// Releases the resources of this camera. + @override + Future dispose() async { + if (_isDisposed) { + return; + } + _deviceOrientationSubscription?.cancel(); + _isDisposed = true; + super.dispose(); + if (_initCalled != null) { + await _initCalled; + await CameraPlatform.instance.dispose(_cameraId); + } + } + + @override + void removeListener(VoidCallback listener) { + // Prevent ValueListenableBuilder in CameraPreview widget from causing an + // exception to be thrown by attempting to remove its own listener after + // the controller has already been disposed. + if (!_isDisposed) { + super.removeListener(listener); + } + } +} diff --git a/packages/camera/camera_avfoundation/example/lib/camera_preview.dart b/packages/camera/camera_avfoundation/example/lib/camera_preview.dart new file mode 100644 index 000000000000..5e8f64cb2fbd --- /dev/null +++ b/packages/camera/camera_avfoundation/example/lib/camera_preview.dart @@ -0,0 +1,85 @@ +// 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/services.dart'; + +import 'camera_controller.dart'; + +/// A widget showing a live camera preview. +class CameraPreview extends StatelessWidget { + /// Creates a preview widget for the given camera controller. + const CameraPreview(this.controller, {Key? key, this.child}) + : super(key: key); + + /// The controller for the camera that the preview is shown for. + final CameraController controller; + + /// A widget to overlay on top of the camera preview + final Widget? child; + + @override + Widget build(BuildContext context) { + return controller.value.isInitialized + ? ValueListenableBuilder( + valueListenable: controller, + builder: (BuildContext context, Object? value, Widget? child) { + final double cameraAspectRatio = + controller.value.previewSize!.width / + controller.value.previewSize!.height; + return AspectRatio( + aspectRatio: _isLandscape() + ? cameraAspectRatio + : (1 / cameraAspectRatio), + child: Stack( + fit: StackFit.expand, + children: [ + _wrapInRotatedBox(child: controller.buildPreview()), + child ?? Container(), + ], + ), + ); + }, + child: child, + ) + : Container(); + } + + Widget _wrapInRotatedBox({required Widget child}) { + if (kIsWeb || defaultTargetPlatform != TargetPlatform.android) { + return child; + } + + return RotatedBox( + quarterTurns: _getQuarterTurns(), + child: child, + ); + } + + bool _isLandscape() { + return [ + DeviceOrientation.landscapeLeft, + DeviceOrientation.landscapeRight + ].contains(_getApplicableOrientation()); + } + + int _getQuarterTurns() { + final Map turns = { + DeviceOrientation.portraitUp: 0, + DeviceOrientation.landscapeRight: 1, + DeviceOrientation.portraitDown: 2, + DeviceOrientation.landscapeLeft: 3, + }; + return turns[_getApplicableOrientation()]!; + } + + DeviceOrientation _getApplicableOrientation() { + return controller.value.isRecordingVideo + ? controller.value.recordingOrientation! + : (controller.value.previewPauseOrientation ?? + controller.value.lockedCaptureOrientation ?? + controller.value.deviceOrientation); + } +} diff --git a/packages/camera/camera_avfoundation/example/lib/main.dart b/packages/camera/camera_avfoundation/example/lib/main.dart new file mode 100644 index 000000000000..1f3e06d33b9b --- /dev/null +++ b/packages/camera/camera_avfoundation/example/lib/main.dart @@ -0,0 +1,1095 @@ +// 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:async'; +import 'dart:io'; +import 'dart:math'; + +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:video_player/video_player.dart'; + +import 'camera_controller.dart'; +import 'camera_preview.dart'; + +/// Camera example home widget. +class CameraExampleHome extends StatefulWidget { + /// Default Constructor + const CameraExampleHome({Key? key}) : super(key: key); + + @override + State createState() { + return _CameraExampleHomeState(); + } +} + +/// Returns a suitable camera icon for [direction]. +IconData getCameraLensIcon(CameraLensDirection direction) { + switch (direction) { + case CameraLensDirection.back: + return Icons.camera_rear; + case CameraLensDirection.front: + return Icons.camera_front; + case CameraLensDirection.external: + return Icons.camera; + default: + throw ArgumentError('Unknown lens direction'); + } +} + +void _logError(String code, String? message) { + if (message != null) { + print('Error: $code\nError Message: $message'); + } else { + print('Error: $code'); + } +} + +class _CameraExampleHomeState extends State + with WidgetsBindingObserver, TickerProviderStateMixin { + CameraController? controller; + XFile? imageFile; + XFile? videoFile; + VideoPlayerController? videoController; + VoidCallback? videoPlayerListener; + bool enableAudio = true; + double _minAvailableExposureOffset = 0.0; + double _maxAvailableExposureOffset = 0.0; + double _currentExposureOffset = 0.0; + late AnimationController _flashModeControlRowAnimationController; + late Animation _flashModeControlRowAnimation; + late AnimationController _exposureModeControlRowAnimationController; + late Animation _exposureModeControlRowAnimation; + late AnimationController _focusModeControlRowAnimationController; + late Animation _focusModeControlRowAnimation; + double _minAvailableZoom = 1.0; + double _maxAvailableZoom = 1.0; + double _currentScale = 1.0; + double _baseScale = 1.0; + + // Counting pointers (number of user fingers on screen) + int _pointers = 0; + + @override + void initState() { + super.initState(); + _ambiguate(WidgetsBinding.instance)?.addObserver(this); + + _flashModeControlRowAnimationController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + _flashModeControlRowAnimation = CurvedAnimation( + parent: _flashModeControlRowAnimationController, + curve: Curves.easeInCubic, + ); + _exposureModeControlRowAnimationController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + _exposureModeControlRowAnimation = CurvedAnimation( + parent: _exposureModeControlRowAnimationController, + curve: Curves.easeInCubic, + ); + _focusModeControlRowAnimationController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + _focusModeControlRowAnimation = CurvedAnimation( + parent: _focusModeControlRowAnimationController, + curve: Curves.easeInCubic, + ); + } + + @override + void dispose() { + _ambiguate(WidgetsBinding.instance)?.removeObserver(this); + _flashModeControlRowAnimationController.dispose(); + _exposureModeControlRowAnimationController.dispose(); + super.dispose(); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + final CameraController? cameraController = controller; + + // App state changed before we got the chance to initialize. + if (cameraController == null || !cameraController.value.isInitialized) { + return; + } + + if (state == AppLifecycleState.inactive) { + cameraController.dispose(); + } else if (state == AppLifecycleState.resumed) { + onNewCameraSelected(cameraController.description); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Camera example'), + ), + body: Column( + children: [ + Expanded( + child: Container( + decoration: BoxDecoration( + color: Colors.black, + border: Border.all( + color: + controller != null && controller!.value.isRecordingVideo + ? Colors.redAccent + : Colors.grey, + width: 3.0, + ), + ), + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Center( + child: _cameraPreviewWidget(), + ), + ), + ), + ), + _captureControlRowWidget(), + _modeControlRowWidget(), + Padding( + padding: const EdgeInsets.all(5.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + _cameraTogglesRowWidget(), + _thumbnailWidget(), + ], + ), + ), + ], + ), + ); + } + + /// Display the preview from the camera (or a message if the preview is not available). + Widget _cameraPreviewWidget() { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isInitialized) { + return const Text( + 'Tap a camera', + style: TextStyle( + color: Colors.white, + fontSize: 24.0, + fontWeight: FontWeight.w900, + ), + ); + } else { + return Listener( + onPointerDown: (_) => _pointers++, + onPointerUp: (_) => _pointers--, + child: CameraPreview( + controller!, + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onScaleStart: _handleScaleStart, + onScaleUpdate: _handleScaleUpdate, + onTapDown: (TapDownDetails details) => + onViewFinderTap(details, constraints), + ); + }), + ), + ); + } + } + + void _handleScaleStart(ScaleStartDetails details) { + _baseScale = _currentScale; + } + + Future _handleScaleUpdate(ScaleUpdateDetails details) async { + // When there are not exactly two fingers on screen don't scale + if (controller == null || _pointers != 2) { + return; + } + + _currentScale = (_baseScale * details.scale) + .clamp(_minAvailableZoom, _maxAvailableZoom); + + await CameraPlatform.instance + .setZoomLevel(controller!.cameraId, _currentScale); + } + + /// Display the thumbnail of the captured image or video. + Widget _thumbnailWidget() { + final VideoPlayerController? localVideoController = videoController; + + return Expanded( + child: Align( + alignment: Alignment.centerRight, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (localVideoController == null && imageFile == null) + Container() + else + SizedBox( + width: 64.0, + height: 64.0, + child: (localVideoController == null) + ? ( + // The captured image on the web contains a network-accessible URL + // pointing to a location within the browser. It may be displayed + // either with Image.network or Image.memory after loading the image + // bytes to memory. + kIsWeb + ? Image.network(imageFile!.path) + : Image.file(File(imageFile!.path))) + : Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.pink)), + child: Center( + child: AspectRatio( + aspectRatio: + localVideoController.value.size != null + ? localVideoController.value.aspectRatio + : 1.0, + child: VideoPlayer(localVideoController)), + ), + ), + ), + ], + ), + ), + ); + } + + /// Display a bar with buttons to change the flash and exposure modes + Widget _modeControlRowWidget() { + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + IconButton( + icon: const Icon(Icons.flash_on), + color: Colors.blue, + onPressed: controller != null ? onFlashModeButtonPressed : null, + ), + // The exposure and focus mode are currently not supported on the web. + ...!kIsWeb + ? [ + IconButton( + icon: const Icon(Icons.exposure), + color: Colors.blue, + onPressed: controller != null + ? onExposureModeButtonPressed + : null, + ), + IconButton( + icon: const Icon(Icons.filter_center_focus), + color: Colors.blue, + onPressed: + controller != null ? onFocusModeButtonPressed : null, + ) + ] + : [], + IconButton( + icon: Icon(enableAudio ? Icons.volume_up : Icons.volume_mute), + color: Colors.blue, + onPressed: controller != null ? onAudioModeButtonPressed : null, + ), + IconButton( + icon: Icon(controller?.value.isCaptureOrientationLocked ?? false + ? Icons.screen_lock_rotation + : Icons.screen_rotation), + color: Colors.blue, + onPressed: controller != null + ? onCaptureOrientationLockButtonPressed + : null, + ), + ], + ), + _flashModeControlRowWidget(), + _exposureModeControlRowWidget(), + _focusModeControlRowWidget(), + ], + ); + } + + Widget _flashModeControlRowWidget() { + return SizeTransition( + sizeFactor: _flashModeControlRowAnimation, + child: ClipRect( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + IconButton( + icon: const Icon(Icons.flash_off), + color: controller?.value.flashMode == FlashMode.off + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.off) + : null, + ), + IconButton( + icon: const Icon(Icons.flash_auto), + color: controller?.value.flashMode == FlashMode.auto + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.auto) + : null, + ), + IconButton( + icon: const Icon(Icons.flash_on), + color: controller?.value.flashMode == FlashMode.always + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.always) + : null, + ), + IconButton( + icon: const Icon(Icons.highlight), + color: controller?.value.flashMode == FlashMode.torch + ? Colors.orange + : Colors.blue, + onPressed: controller != null + ? () => onSetFlashModeButtonPressed(FlashMode.torch) + : null, + ), + ], + ), + ), + ); + } + + Widget _exposureModeControlRowWidget() { + final ButtonStyle styleAuto = TextButton.styleFrom( + primary: controller?.value.exposureMode == ExposureMode.auto + ? Colors.orange + : Colors.blue, + ); + final ButtonStyle styleLocked = TextButton.styleFrom( + primary: controller?.value.exposureMode == ExposureMode.locked + ? Colors.orange + : Colors.blue, + ); + + return SizeTransition( + sizeFactor: _exposureModeControlRowAnimation, + child: ClipRect( + child: Container( + color: Colors.grey.shade50, + child: Column( + children: [ + const Center( + child: Text('Exposure Mode'), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + TextButton( + style: styleAuto, + onPressed: controller != null + ? () => + onSetExposureModeButtonPressed(ExposureMode.auto) + : null, + onLongPress: () { + if (controller != null) { + CameraPlatform.instance + .setExposurePoint(controller!.cameraId, null); + showInSnackBar('Resetting exposure point'); + } + }, + child: const Text('AUTO'), + ), + TextButton( + style: styleLocked, + onPressed: controller != null + ? () => + onSetExposureModeButtonPressed(ExposureMode.locked) + : null, + child: const Text('LOCKED'), + ), + TextButton( + style: styleLocked, + onPressed: controller != null + ? () => controller!.setExposureOffset(0.0) + : null, + child: const Text('RESET OFFSET'), + ), + ], + ), + const Center( + child: Text('Exposure Offset'), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + Text(_minAvailableExposureOffset.toString()), + Slider( + value: _currentExposureOffset, + min: _minAvailableExposureOffset, + max: _maxAvailableExposureOffset, + label: _currentExposureOffset.toString(), + onChanged: _minAvailableExposureOffset == + _maxAvailableExposureOffset + ? null + : setExposureOffset, + ), + Text(_maxAvailableExposureOffset.toString()), + ], + ), + ], + ), + ), + ), + ); + } + + Widget _focusModeControlRowWidget() { + final ButtonStyle styleAuto = TextButton.styleFrom( + primary: controller?.value.focusMode == FocusMode.auto + ? Colors.orange + : Colors.blue, + ); + final ButtonStyle styleLocked = TextButton.styleFrom( + primary: controller?.value.focusMode == FocusMode.locked + ? Colors.orange + : Colors.blue, + ); + + return SizeTransition( + sizeFactor: _focusModeControlRowAnimation, + child: ClipRect( + child: Container( + color: Colors.grey.shade50, + child: Column( + children: [ + const Center( + child: Text('Focus Mode'), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + TextButton( + style: styleAuto, + onPressed: controller != null + ? () => onSetFocusModeButtonPressed(FocusMode.auto) + : null, + onLongPress: () { + if (controller != null) { + CameraPlatform.instance + .setFocusPoint(controller!.cameraId, null); + } + showInSnackBar('Resetting focus point'); + }, + child: const Text('AUTO'), + ), + TextButton( + style: styleLocked, + onPressed: controller != null + ? () => onSetFocusModeButtonPressed(FocusMode.locked) + : null, + child: const Text('LOCKED'), + ), + ], + ), + ], + ), + ), + ), + ); + } + + /// Display the control bar with buttons to take pictures and record videos. + Widget _captureControlRowWidget() { + final CameraController? cameraController = controller; + + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + IconButton( + icon: const Icon(Icons.camera_alt), + color: Colors.blue, + onPressed: cameraController != null && + cameraController.value.isInitialized && + !cameraController.value.isRecordingVideo + ? onTakePictureButtonPressed + : null, + ), + IconButton( + icon: const Icon(Icons.videocam), + color: Colors.blue, + onPressed: cameraController != null && + cameraController.value.isInitialized && + !cameraController.value.isRecordingVideo + ? onVideoRecordButtonPressed + : null, + ), + IconButton( + icon: cameraController != null && + (!cameraController.value.isRecordingVideo || + cameraController.value.isRecordingPaused) + ? const Icon(Icons.play_arrow) + : const Icon(Icons.pause), + color: Colors.blue, + onPressed: cameraController != null && + cameraController.value.isInitialized && + cameraController.value.isRecordingVideo + ? (cameraController.value.isRecordingPaused) + ? onResumeButtonPressed + : onPauseButtonPressed + : null, + ), + IconButton( + icon: const Icon(Icons.stop), + color: Colors.red, + onPressed: cameraController != null && + cameraController.value.isInitialized && + cameraController.value.isRecordingVideo + ? onStopButtonPressed + : null, + ), + IconButton( + icon: const Icon(Icons.pause_presentation), + color: + cameraController != null && cameraController.value.isPreviewPaused + ? Colors.red + : Colors.blue, + onPressed: + cameraController == null ? null : onPausePreviewButtonPressed, + ), + ], + ); + } + + /// Display a row of toggle to select the camera (or a message if no camera is available). + Widget _cameraTogglesRowWidget() { + final List toggles = []; + + void onChanged(CameraDescription? description) { + if (description == null) { + return; + } + + onNewCameraSelected(description); + } + + if (_cameras.isEmpty) { + _ambiguate(SchedulerBinding.instance)?.addPostFrameCallback((_) async { + showInSnackBar('No camera found.'); + }); + return const Text('None'); + } else { + for (final CameraDescription cameraDescription in _cameras) { + toggles.add( + SizedBox( + width: 90.0, + child: RadioListTile( + title: Icon(getCameraLensIcon(cameraDescription.lensDirection)), + groupValue: controller?.description, + value: cameraDescription, + onChanged: + controller != null && controller!.value.isRecordingVideo + ? null + : onChanged, + ), + ), + ); + } + } + + return Row(children: toggles); + } + + String timestamp() => DateTime.now().millisecondsSinceEpoch.toString(); + + void showInSnackBar(String message) { + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text(message))); + } + + void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) { + if (controller == null) { + return; + } + + final CameraController cameraController = controller!; + + final Point point = Point( + details.localPosition.dx / constraints.maxWidth, + details.localPosition.dy / constraints.maxHeight, + ); + CameraPlatform.instance.setExposurePoint(cameraController.cameraId, point); + CameraPlatform.instance.setFocusPoint(cameraController.cameraId, point); + } + + Future onNewCameraSelected(CameraDescription cameraDescription) async { + final CameraController? oldController = controller; + if (oldController != null) { + // `controller` needs to be set to null before getting disposed, + // to avoid a race condition when we use the controller that is being + // disposed. This happens when camera permission dialog shows up, + // which triggers `didChangeAppLifecycleState`, which disposes and + // re-creates the controller. + controller = null; + await oldController.dispose(); + } + + final CameraController cameraController = CameraController( + cameraDescription, + kIsWeb ? ResolutionPreset.max : ResolutionPreset.medium, + enableAudio: enableAudio, + imageFormatGroup: ImageFormatGroup.jpeg, + ); + + controller = cameraController; + + // If the controller is updated then update the UI. + cameraController.addListener(() { + if (mounted) { + setState(() {}); + } + }); + + try { + await cameraController.initialize(); + await Future.wait(>[ + // The exposure mode is currently not supported on the web. + ...!kIsWeb + ? >[ + CameraPlatform.instance + .getMinExposureOffset(cameraController.cameraId) + .then( + (double value) => _minAvailableExposureOffset = value), + CameraPlatform.instance + .getMaxExposureOffset(cameraController.cameraId) + .then((double value) => _maxAvailableExposureOffset = value) + ] + : >[], + CameraPlatform.instance + .getMaxZoomLevel(cameraController.cameraId) + .then((double value) => _maxAvailableZoom = value), + CameraPlatform.instance + .getMinZoomLevel(cameraController.cameraId) + .then((double value) => _minAvailableZoom = value), + ]); + } on CameraException catch (e) { + switch (e.code) { + case 'CameraAccessDenied': + showInSnackBar('You have denied camera access.'); + break; + case 'CameraAccessDeniedWithoutPrompt': + // iOS only + showInSnackBar('Please go to Settings app to enable camera access.'); + break; + case 'CameraAccessRestricted': + // iOS only + showInSnackBar('Camera access is restricted.'); + break; + case 'AudioAccessDenied': + showInSnackBar('You have denied audio access.'); + break; + case 'AudioAccessDeniedWithoutPrompt': + // iOS only + showInSnackBar('Please go to Settings app to enable audio access.'); + break; + case 'AudioAccessRestricted': + // iOS only + showInSnackBar('Audio access is restricted.'); + break; + case 'cameraPermission': + // Android & web only + showInSnackBar('Unknown permission error.'); + break; + default: + _showCameraException(e); + break; + } + } + + if (mounted) { + setState(() {}); + } + } + + void onTakePictureButtonPressed() { + takePicture().then((XFile? file) { + if (mounted) { + setState(() { + imageFile = file; + videoController?.dispose(); + videoController = null; + }); + if (file != null) { + showInSnackBar('Picture saved to ${file.path}'); + } + } + }); + } + + void onFlashModeButtonPressed() { + if (_flashModeControlRowAnimationController.value == 1) { + _flashModeControlRowAnimationController.reverse(); + } else { + _flashModeControlRowAnimationController.forward(); + _exposureModeControlRowAnimationController.reverse(); + _focusModeControlRowAnimationController.reverse(); + } + } + + void onExposureModeButtonPressed() { + if (_exposureModeControlRowAnimationController.value == 1) { + _exposureModeControlRowAnimationController.reverse(); + } else { + _exposureModeControlRowAnimationController.forward(); + _flashModeControlRowAnimationController.reverse(); + _focusModeControlRowAnimationController.reverse(); + } + } + + void onFocusModeButtonPressed() { + if (_focusModeControlRowAnimationController.value == 1) { + _focusModeControlRowAnimationController.reverse(); + } else { + _focusModeControlRowAnimationController.forward(); + _flashModeControlRowAnimationController.reverse(); + _exposureModeControlRowAnimationController.reverse(); + } + } + + void onAudioModeButtonPressed() { + enableAudio = !enableAudio; + if (controller != null) { + onNewCameraSelected(controller!.description); + } + } + + Future onCaptureOrientationLockButtonPressed() async { + try { + if (controller != null) { + final CameraController cameraController = controller!; + if (cameraController.value.isCaptureOrientationLocked) { + await cameraController.unlockCaptureOrientation(); + showInSnackBar('Capture orientation unlocked'); + } else { + await cameraController.lockCaptureOrientation(); + showInSnackBar( + 'Capture orientation locked to ${cameraController.value.lockedCaptureOrientation.toString().split('.').last}'); + } + } + } on CameraException catch (e) { + _showCameraException(e); + } + } + + void onSetFlashModeButtonPressed(FlashMode mode) { + setFlashMode(mode).then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Flash mode set to ${mode.toString().split('.').last}'); + }); + } + + void onSetExposureModeButtonPressed(ExposureMode mode) { + setExposureMode(mode).then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Exposure mode set to ${mode.toString().split('.').last}'); + }); + } + + void onSetFocusModeButtonPressed(FocusMode mode) { + setFocusMode(mode).then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Focus mode set to ${mode.toString().split('.').last}'); + }); + } + + void onVideoRecordButtonPressed() { + startVideoRecording().then((_) { + if (mounted) { + setState(() {}); + } + }); + } + + void onStopButtonPressed() { + stopVideoRecording().then((XFile? file) { + if (mounted) { + setState(() {}); + } + if (file != null) { + showInSnackBar('Video recorded to ${file.path}'); + videoFile = file; + _startVideoPlayer(); + } + }); + } + + Future onPausePreviewButtonPressed() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isInitialized) { + showInSnackBar('Error: select a camera first.'); + return; + } + + if (cameraController.value.isPreviewPaused) { + await cameraController.resumePreview(); + } else { + await cameraController.pausePreview(); + } + + if (mounted) { + setState(() {}); + } + } + + void onPauseButtonPressed() { + pauseVideoRecording().then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Video recording paused'); + }); + } + + void onResumeButtonPressed() { + resumeVideoRecording().then((_) { + if (mounted) { + setState(() {}); + } + showInSnackBar('Video recording resumed'); + }); + } + + Future startVideoRecording() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isInitialized) { + showInSnackBar('Error: select a camera first.'); + return; + } + + if (cameraController.value.isRecordingVideo) { + // A recording is already started, do nothing. + return; + } + + try { + await cameraController.startVideoRecording(); + } on CameraException catch (e) { + _showCameraException(e); + return; + } + } + + Future stopVideoRecording() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isRecordingVideo) { + return null; + } + + try { + return cameraController.stopVideoRecording(); + } on CameraException catch (e) { + _showCameraException(e); + return null; + } + } + + Future pauseVideoRecording() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isRecordingVideo) { + return; + } + + try { + await cameraController.pauseVideoRecording(); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future resumeVideoRecording() async { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isRecordingVideo) { + return; + } + + try { + await cameraController.resumeVideoRecording(); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future setFlashMode(FlashMode mode) async { + if (controller == null) { + return; + } + + try { + await controller!.setFlashMode(mode); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future setExposureMode(ExposureMode mode) async { + if (controller == null) { + return; + } + + try { + await controller!.setExposureMode(mode); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future setExposureOffset(double offset) async { + if (controller == null) { + return; + } + + setState(() { + _currentExposureOffset = offset; + }); + try { + offset = await controller!.setExposureOffset(offset); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future setFocusMode(FocusMode mode) async { + if (controller == null) { + return; + } + + try { + await controller!.setFocusMode(mode); + } on CameraException catch (e) { + _showCameraException(e); + rethrow; + } + } + + Future _startVideoPlayer() async { + if (videoFile == null) { + return; + } + + final VideoPlayerController vController = kIsWeb + ? VideoPlayerController.network(videoFile!.path) + : VideoPlayerController.file(File(videoFile!.path)); + + videoPlayerListener = () { + if (videoController != null && videoController!.value.size != null) { + // Refreshing the state to update video player with the correct ratio. + if (mounted) { + setState(() {}); + } + videoController!.removeListener(videoPlayerListener!); + } + }; + vController.addListener(videoPlayerListener!); + await vController.setLooping(true); + await vController.initialize(); + await videoController?.dispose(); + if (mounted) { + setState(() { + imageFile = null; + videoController = vController; + }); + } + await vController.play(); + } + + Future takePicture() async { + final CameraController? cameraController = controller; + if (cameraController == null || !cameraController.value.isInitialized) { + showInSnackBar('Error: select a camera first.'); + return null; + } + + if (cameraController.value.isTakingPicture) { + // A capture is already pending, do nothing. + return null; + } + + try { + final XFile file = await cameraController.takePicture(); + return file; + } on CameraException catch (e) { + _showCameraException(e); + return null; + } + } + + void _showCameraException(CameraException e) { + _logError(e.code, e.description); + showInSnackBar('Error: ${e.code}\n${e.description}'); + } +} + +/// CameraApp is the Main Application. +class CameraApp extends StatelessWidget { + /// Default Constructor + const CameraApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return const MaterialApp( + home: CameraExampleHome(), + ); + } +} + +List _cameras = []; + +Future main() async { + // Fetch the available cameras before initializing the app. + try { + WidgetsFlutterBinding.ensureInitialized(); + _cameras = await CameraPlatform.instance.availableCameras(); + } on CameraException catch (e) { + _logError(e.code, e.description); + } + runApp(const CameraApp()); +} + +/// This allows a value of type T or T? to be treated as a value of type T?. +/// +/// We use this so that APIs that have become non-nullable can still be used +/// with `!` and `?` on the stable branch. +// TODO(ianh): Remove this once we roll stable in late 2021. +T? _ambiguate(T? value) => value; diff --git a/packages/camera/camera_avfoundation/example/pubspec.yaml b/packages/camera/camera_avfoundation/example/pubspec.yaml new file mode 100644 index 000000000000..78927fc70d76 --- /dev/null +++ b/packages/camera/camera_avfoundation/example/pubspec.yaml @@ -0,0 +1,33 @@ +name: camera_example +description: Demonstrates how to use the camera plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +dependencies: + camera_avfoundation: + # When depending on this package from a real application you should use: + # camera_avfoundation: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # 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: ../ + flutter: + sdk: flutter + path_provider: ^2.0.0 + quiver: ^3.0.0 + video_player: ^2.1.4 + +dev_dependencies: + build_runner: ^2.1.10 + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/camera/camera_avfoundation/example/test_driver/integration_test.dart b/packages/camera/camera_avfoundation/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4ec97e66d36c --- /dev/null +++ b/packages/camera/camera_avfoundation/example/test_driver/integration_test.dart @@ -0,0 +1,64 @@ +// 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:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter_driver/flutter_driver.dart'; + +const String _examplePackage = 'io.flutter.plugins.cameraexample'; + +Future main() async { + if (!(Platform.isLinux || Platform.isMacOS)) { + print('This test must be run on a POSIX host. Skipping...'); + exit(0); + } + final bool adbExists = + Process.runSync('which', ['adb']).exitCode == 0; + if (!adbExists) { + print(r'This test needs ADB to exist on the $PATH. Skipping...'); + exit(0); + } + print('Granting camera permissions...'); + Process.runSync('adb', [ + 'shell', + 'pm', + 'grant', + _examplePackage, + 'android.permission.CAMERA' + ]); + Process.runSync('adb', [ + 'shell', + 'pm', + 'grant', + _examplePackage, + 'android.permission.RECORD_AUDIO' + ]); + print('Starting test.'); + final FlutterDriver driver = await FlutterDriver.connect(); + final String data = await driver.requestData( + null, + timeout: const Duration(minutes: 1), + ); + await driver.close(); + print('Test finished. Revoking camera permissions...'); + Process.runSync('adb', [ + 'shell', + 'pm', + 'revoke', + _examplePackage, + 'android.permission.CAMERA' + ]); + Process.runSync('adb', [ + 'shell', + 'pm', + 'revoke', + _examplePackage, + 'android.permission.RECORD_AUDIO' + ]); + + final Map result = jsonDecode(data) as Map; + exit(result['result'] == 'true' ? 0 : 1); +} diff --git a/packages/camera/camera/ios/Assets/.gitkeep b/packages/camera/camera_avfoundation/ios/Assets/.gitkeep similarity index 100% rename from packages/camera/camera/ios/Assets/.gitkeep rename to packages/camera/camera_avfoundation/ios/Assets/.gitkeep diff --git a/packages/camera/camera/ios/Classes/CameraPermissionUtils.h b/packages/camera/camera_avfoundation/ios/Classes/CameraPermissionUtils.h similarity index 100% rename from packages/camera/camera/ios/Classes/CameraPermissionUtils.h rename to packages/camera/camera_avfoundation/ios/Classes/CameraPermissionUtils.h diff --git a/packages/camera/camera/ios/Classes/CameraPermissionUtils.m b/packages/camera/camera_avfoundation/ios/Classes/CameraPermissionUtils.m similarity index 100% rename from packages/camera/camera/ios/Classes/CameraPermissionUtils.m rename to packages/camera/camera_avfoundation/ios/Classes/CameraPermissionUtils.m diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.h b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.h similarity index 100% rename from packages/camera/camera/ios/Classes/CameraPlugin.h rename to packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.h diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.m b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m similarity index 100% rename from packages/camera/camera/ios/Classes/CameraPlugin.m rename to packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m diff --git a/packages/camera/camera/ios/Classes/CameraPlugin.modulemap b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.modulemap similarity index 83% rename from packages/camera/camera/ios/Classes/CameraPlugin.modulemap rename to packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.modulemap index 897302799497..abdad1ab575c 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin.modulemap +++ b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.modulemap @@ -1,5 +1,5 @@ -framework module camera { - umbrella header "camera-umbrella.h" +framework module camera_avfoundation { + umbrella header "camera_avfoundation-umbrella.h" export * module * { export * } diff --git a/packages/camera/camera/ios/Classes/CameraPlugin_Test.h b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin_Test.h similarity index 94% rename from packages/camera/camera/ios/Classes/CameraPlugin_Test.h rename to packages/camera/camera_avfoundation/ios/Classes/CameraPlugin_Test.h index d1903e0829b4..77a758d8dea3 100644 --- a/packages/camera/camera/ios/Classes/CameraPlugin_Test.h +++ b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin_Test.h @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// This header is available in the Test module. Import via "@import camera.Test;" +// This header is available in the Test module. Import via "@import camera_avfoundation.Test;" -#import -#import -#import +#import "CameraPlugin.h" +#import "FLTCam.h" +#import "FLTThreadSafeFlutterResult.h" /// Methods exposed for unit testing. @interface CameraPlugin () diff --git a/packages/camera/camera/ios/Classes/CameraProperties.h b/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h similarity index 100% rename from packages/camera/camera/ios/Classes/CameraProperties.h rename to packages/camera/camera_avfoundation/ios/Classes/CameraProperties.h diff --git a/packages/camera/camera/ios/Classes/CameraProperties.m b/packages/camera/camera_avfoundation/ios/Classes/CameraProperties.m similarity index 100% rename from packages/camera/camera/ios/Classes/CameraProperties.m rename to packages/camera/camera_avfoundation/ios/Classes/CameraProperties.m diff --git a/packages/camera/camera/ios/Classes/FLTCam.h b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.h similarity index 100% rename from packages/camera/camera/ios/Classes/FLTCam.h rename to packages/camera/camera_avfoundation/ios/Classes/FLTCam.h diff --git a/packages/camera/camera/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m similarity index 100% rename from packages/camera/camera/ios/Classes/FLTCam.m rename to packages/camera/camera_avfoundation/ios/Classes/FLTCam.m diff --git a/packages/camera/camera/ios/Classes/FLTCam_Test.h b/packages/camera/camera_avfoundation/ios/Classes/FLTCam_Test.h similarity index 100% rename from packages/camera/camera/ios/Classes/FLTCam_Test.h rename to packages/camera/camera_avfoundation/ios/Classes/FLTCam_Test.h diff --git a/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.h b/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.h similarity index 100% rename from packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.h rename to packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.h diff --git a/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m b/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.m similarity index 100% rename from packages/camera/camera/ios/Classes/FLTSavePhotoDelegate.m rename to packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.m diff --git a/packages/camera/camera/ios/Classes/FLTSavePhotoDelegate_Test.h b/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate_Test.h similarity index 100% rename from packages/camera/camera/ios/Classes/FLTSavePhotoDelegate_Test.h rename to packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate_Test.h diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.h b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.h similarity index 100% rename from packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.h rename to packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.h diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.m similarity index 100% rename from packages/camera/camera/ios/Classes/FLTThreadSafeEventChannel.m rename to packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.m diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.h similarity index 100% rename from packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.h rename to packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.h diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.m similarity index 100% rename from packages/camera/camera/ios/Classes/FLTThreadSafeFlutterResult.m rename to packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.m diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.h b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeMethodChannel.h similarity index 100% rename from packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.h rename to packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeMethodChannel.h diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeMethodChannel.m similarity index 100% rename from packages/camera/camera/ios/Classes/FLTThreadSafeMethodChannel.m rename to packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeMethodChannel.m diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.h b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeTextureRegistry.h similarity index 100% rename from packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.h rename to packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeTextureRegistry.h diff --git a/packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeTextureRegistry.m similarity index 100% rename from packages/camera/camera/ios/Classes/FLTThreadSafeTextureRegistry.m rename to packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeTextureRegistry.m diff --git a/packages/camera/camera/ios/Classes/QueueUtils.h b/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h similarity index 100% rename from packages/camera/camera/ios/Classes/QueueUtils.h rename to packages/camera/camera_avfoundation/ios/Classes/QueueUtils.h diff --git a/packages/camera/camera/ios/Classes/QueueUtils.m b/packages/camera/camera_avfoundation/ios/Classes/QueueUtils.m similarity index 100% rename from packages/camera/camera/ios/Classes/QueueUtils.m rename to packages/camera/camera_avfoundation/ios/Classes/QueueUtils.m diff --git a/packages/camera/camera/ios/Classes/camera-umbrella.h b/packages/camera/camera_avfoundation/ios/Classes/camera_avfoundation-umbrella.h similarity index 87% rename from packages/camera/camera/ios/Classes/camera-umbrella.h rename to packages/camera/camera_avfoundation/ios/Classes/camera_avfoundation-umbrella.h index 5c39401e6261..f8464aaae3dc 100644 --- a/packages/camera/camera/ios/Classes/camera-umbrella.h +++ b/packages/camera/camera_avfoundation/ios/Classes/camera_avfoundation-umbrella.h @@ -3,7 +3,7 @@ // found in the LICENSE file. #import -#import +#import FOUNDATION_EXPORT double cameraVersionNumber; FOUNDATION_EXPORT const unsigned char cameraVersionString[]; diff --git a/packages/camera/camera/ios/camera.podspec b/packages/camera/camera_avfoundation/ios/camera_avfoundation.podspec similarity index 76% rename from packages/camera/camera/ios/camera.podspec rename to packages/camera/camera_avfoundation/ios/camera_avfoundation.podspec index 098b011a47a9..27f569c8b9be 100644 --- a/packages/camera/camera/ios/camera.podspec +++ b/packages/camera/camera_avfoundation/ios/camera_avfoundation.podspec @@ -2,7 +2,7 @@ # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html # Pod::Spec.new do |s| - s.name = 'camera' + s.name = 'camera_avfoundation' s.version = '0.0.1' s.summary = 'Flutter Camera' s.description = <<-DESC @@ -11,13 +11,13 @@ A Flutter plugin to use the camera from your Flutter app. s.homepage = 'https://github.com/flutter/plugins' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/camera' } - s.documentation_url = 'https://pub.dev/packages/camera' + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/camera_avfoundation' } + s.documentation_url = 'https://pub.dev/packages/camera_avfoundation' s.source_files = 'Classes/**/*.{h,m}' s.public_header_files = 'Classes/**/*.h' s.module_map = 'Classes/CameraPlugin.modulemap' s.dependency 'Flutter' s.platform = :ios, '9.0' - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } end diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml new file mode 100644 index 000000000000..237f5ad329c5 --- /dev/null +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -0,0 +1,27 @@ +name: camera_avfoundation +description: iOS implementation of the camera plugin. +repository: https://github.com/flutter/plugins/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.7+1 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +flutter: + plugin: + implements: camera + platforms: + ios: + pluginClass: CameraPlugin + +dependencies: + camera_platform_interface: ^2.2.0 + flutter: + sdk: flutter + +dev_dependencies: + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter From beb6d19f6d0b7a3a296d4ba635de2d7697ae02f7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 9 Jun 2022 13:08:11 -0400 Subject: [PATCH 414/844] Roll Flutter from 4ec296513cb2 to 25e0e290e6a4 (27 revisions) (#5941) --- .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 ea859f1df8f9..bc01325825ab 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -4ec296513cb23b089c27b383214dad2c7cfc025f +25e0e290e6a4182e030ea9a352c6124ea36ca22d From 88e6cf8b83a14e61d9135068c15de5721318da18 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 9 Jun 2022 13:58:12 -0400 Subject: [PATCH 415/844] [camera] Publish federated implementations (#5942) --- packages/camera/camera/CHANGELOG.md | 2 +- packages/camera/camera/pubspec.yaml | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 8f13ff38f47f..28032c9658fa 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,4 +1,4 @@ -## NEXT +## 0.9.8 * Moves Android and iOS implementations to federated packages. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index dd2ce04d2d61..3270889c08e3 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,10 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.7+1 -# Temporarily disable publishing to allow moving Android and iOS -# implementations. -publish_to: none +version: 0.9.8 environment: sdk: ">=2.14.0 <3.0.0" @@ -24,11 +21,8 @@ flutter: default_package: camera_web dependencies: - # Temporary path dependencies to allow moving Android and iOS implementations. - camera_android: - path: ../camera_android - camera_avfoundation: - path: ../camera_avfoundation + camera_android: ^0.9.7+1 + camera_avfoundation: ^0.9.7+1 camera_platform_interface: ^2.2.0 camera_web: ^0.2.1 flutter: From 39020ce73c7e7070e30f3ebcf345492441b477eb Mon Sep 17 00:00:00 2001 From: 8rine23 <58650422+TowaYamashita@users.noreply.github.com> Date: Fri, 10 Jun 2022 05:43:12 +0900 Subject: [PATCH 416/844] [file_selector]Improve API docs and examples (#4824) --- packages/file_selector/file_selector/AUTHORS | 1 + .../file_selector/file_selector/CHANGELOG.md | 3 +- .../file_selector/file_selector/README.md | 45 +++++++++--- .../file_selector/example/build.excerpt.yaml | 15 ++++ .../file_selector/example/lib/main.dart | 13 ++-- .../example/lib/open_image_page.dart | 9 +-- .../lib/open_multiple_images_page.dart | 2 + .../example/lib/open_text_page.dart | 12 +++- .../lib/readme_standalone_excerpts.dart | 57 +++++++++++++++ .../example/lib/save_text_page.dart | 15 +++- .../file_selector/example/pubspec.yaml | 4 +- .../windows/flutter/generated_plugins.cmake | 8 +++ .../file_selector/lib/file_selector.dart | 70 +++++++++++++++++-- .../file_selector/file_selector/pubspec.yaml | 2 +- script/configs/temp_exclude_excerpt.yaml | 1 - 15 files changed, 225 insertions(+), 32 deletions(-) create mode 100644 packages/file_selector/file_selector/example/build.excerpt.yaml create mode 100644 packages/file_selector/file_selector/example/lib/readme_standalone_excerpts.dart diff --git a/packages/file_selector/file_selector/AUTHORS b/packages/file_selector/file_selector/AUTHORS index dbf9d190931b..94743a9a64ae 100644 --- a/packages/file_selector/file_selector/AUTHORS +++ b/packages/file_selector/file_selector/AUTHORS @@ -63,3 +63,4 @@ Aleksandr Yurkovskiy Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> +TowaYamashita diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 639783361852..ed7b213b5686 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.8.4+3 +* Improves API docs and examples. * Minor fixes for new analysis options. ## 0.8.4+2 diff --git a/packages/file_selector/file_selector/README.md b/packages/file_selector/file_selector/README.md index 89cac1e6fd5f..f5c1de8afebc 100644 --- a/packages/file_selector/file_selector/README.md +++ b/packages/file_selector/file_selector/README.md @@ -1,5 +1,7 @@ # file_selector + + [![pub package](https://img.shields.io/pub/v/file_selector.svg)](https://pub.dartlang.org/packages/file_selector) A Flutter plugin that manages files and interactions with file dialogs. @@ -30,25 +32,48 @@ Here are small examples that show you how to use the API. Please also take a look at our [example][example] app. #### Open a single file + ``` dart -final typeGroup = XTypeGroup(label: 'images', extensions: ['jpg', 'png']); -final file = await openFile(acceptedTypeGroups: [typeGroup]); +final XTypeGroup typeGroup = XTypeGroup( + label: 'images', + extensions: ['jpg', 'png'], +); +final XFile? file = + await openFile(acceptedTypeGroups: [typeGroup]); ``` #### Open multiple files at once + ``` dart -final typeGroup = XTypeGroup(label: 'images', extensions: ['jpg', 'png']); -final files = await openFiles(acceptedTypeGroups: [typeGroup]); +final XTypeGroup jpgsTypeGroup = XTypeGroup( + label: 'JPEGs', + extensions: ['jpg', 'jpeg'], +); +final XTypeGroup pngTypeGroup = XTypeGroup( + label: 'PNGs', + extensions: ['png'], +); +final List files = await openFiles(acceptedTypeGroups: [ + jpgsTypeGroup, + pngTypeGroup, +]); ``` #### Saving a file + ```dart -final path = await getSavePath(); -final name = "hello_file_selector.txt"; -final data = Uint8List.fromList("Hello World!".codeUnits); -final mimeType = "text/plain"; -final file = XFile.fromData(data, name: name, mimeType: mimeType); -await file.saveTo(path); +const String fileName = 'suggested_name.txt'; +final String? path = await getSavePath(suggestedName: fileName); +if (path == null) { + // Operation was canceled by the user. + return; +} + +final Uint8List fileData = Uint8List.fromList('Hello World!'.codeUnits); +const String mimeType = 'text/plain'; +final XFile textFile = + XFile.fromData(fileData, mimeType: mimeType, name: fileName); +await textFile.saveTo(path); ``` [example]:./example diff --git a/packages/file_selector/file_selector/example/build.excerpt.yaml b/packages/file_selector/file_selector/example/build.excerpt.yaml new file mode 100644 index 000000000000..e317efa11cb3 --- /dev/null +++ b/packages/file_selector/file_selector/example/build.excerpt.yaml @@ -0,0 +1,15 @@ +targets: + $default: + sources: + include: + - lib/** + # Some default includes that aren't really used here but will prevent + # false-negative warnings: + - $package$ + - lib/$lib$ + exclude: + - '**/.*/**' + - '**/build/**' + builders: + code_excerpter|code_excerpter: + enabled: true diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index 34f5857ab0bc..d05e80f1b755 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:example/get_directory_page.dart'; -import 'package:example/home_page.dart'; -import 'package:example/open_image_page.dart'; -import 'package:example/open_multiple_images_page.dart'; -import 'package:example/open_text_page.dart'; -import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; +import 'get_directory_page.dart'; +import 'home_page.dart'; +import 'open_image_page.dart'; +import 'open_multiple_images_page.dart'; +import 'open_text_page.dart'; +import 'save_text_page.dart'; + void main() { runApp(const MyApp()); } diff --git a/packages/file_selector/file_selector/example/lib/open_image_page.dart b/packages/file_selector/file_selector/example/lib/open_image_page.dart index e520ffb402aa..4fb06864c570 100644 --- a/packages/file_selector/file_selector/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_image_page.dart @@ -14,17 +14,18 @@ class OpenImagePage extends StatelessWidget { const OpenImagePage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { + // #docregion SingleOpen final XTypeGroup typeGroup = XTypeGroup( label: 'images', extensions: ['jpg', 'png'], ); - final List files = - await openFiles(acceptedTypeGroups: [typeGroup]); - if (files.isEmpty) { + final XFile? file = + await openFile(acceptedTypeGroups: [typeGroup]); + // #enddocregion SingleOpen + if (file == null) { // Operation was canceled by the user. return; } - final XFile file = files[0]; final String fileName = file.name; final String filePath = file.path; diff --git a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart index e2d21c7f04d1..ac3d66fc955c 100644 --- a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart @@ -14,6 +14,7 @@ class OpenMultipleImagesPage extends StatelessWidget { const OpenMultipleImagesPage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { + // #docregion MultiOpen final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', extensions: ['jpg', 'jpeg'], @@ -26,6 +27,7 @@ class OpenMultipleImagesPage extends StatelessWidget { jpgsTypeGroup, pngTypeGroup, ]); + // #enddocregion MultiOpen if (files.isEmpty) { // Operation was canceled by the user. return; diff --git a/packages/file_selector/file_selector/example/lib/open_text_page.dart b/packages/file_selector/file_selector/example/lib/open_text_page.dart index be48a434282b..057925ed43c2 100644 --- a/packages/file_selector/file_selector/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_text_page.dart @@ -4,6 +4,7 @@ import 'package:file_selector/file_selector.dart'; import 'package:flutter/material.dart'; +import 'package:path_provider/path_provider.dart'; /// Screen that shows an example of openFile class OpenTextPage extends StatelessWidget { @@ -15,8 +16,15 @@ class OpenTextPage extends StatelessWidget { label: 'text', extensions: ['txt', 'json'], ); - final XFile? file = - await openFile(acceptedTypeGroups: [typeGroup]); + // This demonstrates using an initial directory for the prompt, which should + // only be done in cases where the application can likely predict where the + // file would be. In most cases, this parameter should not be provided. + final String initialDirectory = + (await getApplicationDocumentsDirectory()).path; + final XFile? file = await openFile( + acceptedTypeGroups: [typeGroup], + initialDirectory: initialDirectory, + ); if (file == null) { // Operation was canceled by the user. return; diff --git a/packages/file_selector/file_selector/example/lib/readme_standalone_excerpts.dart b/packages/file_selector/file_selector/example/lib/readme_standalone_excerpts.dart new file mode 100644 index 000000000000..c67c93fa63f2 --- /dev/null +++ b/packages/file_selector/file_selector/example/lib/readme_standalone_excerpts.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. + +// This file exists solely to host compiled excerpts for README.md, and is not +// intended for use as an actual example application. + +// ignore_for_file: public_member_api_docs + +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import +import 'dart:typed_data'; + +import 'package:file_selector/file_selector.dart'; +import 'package:flutter/material.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('README snippet app'), + ), + body: const Text('See example in main.dart'), + ), + ); + } + + Future saveFile() async { + // #docregion Save + const String fileName = 'suggested_name.txt'; + final String? path = await getSavePath(suggestedName: fileName); + if (path == null) { + // Operation was canceled by the user. + return; + } + + final Uint8List fileData = Uint8List.fromList('Hello World!'.codeUnits); + const String mimeType = 'text/plain'; + final XFile textFile = + XFile.fromData(fileData, mimeType: mimeType, name: fileName); + await textFile.saveTo(path); + // #enddocregion Save + } +} diff --git a/packages/file_selector/file_selector/example/lib/save_text_page.dart b/packages/file_selector/file_selector/example/lib/save_text_page.dart index b0317844ec36..257add5b6de8 100644 --- a/packages/file_selector/file_selector/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/save_text_page.dart @@ -5,6 +5,7 @@ import 'dart:typed_data'; import 'package:file_selector/file_selector.dart'; import 'package:flutter/material.dart'; +import 'package:path_provider/path_provider.dart'; /// Page for showing an example of saving with file_selector class SaveTextPage extends StatelessWidget { @@ -15,17 +16,27 @@ class SaveTextPage extends StatelessWidget { final TextEditingController _contentController = TextEditingController(); Future _saveFile() async { - final String? path = await getSavePath(); + final String fileName = _nameController.text; + // This demonstrates using an initial directory for the prompt, which should + // only be done in cases where the application can likely predict where the + // file will be saved. In most cases, this parameter should not be provided. + final String initialDirectory = + (await getApplicationDocumentsDirectory()).path; + final String? path = await getSavePath( + initialDirectory: initialDirectory, + suggestedName: fileName, + ); if (path == null) { // Operation was canceled by the user. return; } + final String text = _contentController.text; - final String fileName = _nameController.text; final Uint8List fileData = Uint8List.fromList(text.codeUnits); const String fileMimeType = 'text/plain'; final XFile textFile = XFile.fromData(fileData, mimeType: fileMimeType, name: fileName); + await textFile.saveTo(path); } diff --git a/packages/file_selector/file_selector/example/pubspec.yaml b/packages/file_selector/file_selector/example/pubspec.yaml index 531f4790afd0..011d95874ae4 100644 --- a/packages/file_selector/file_selector/example/pubspec.yaml +++ b/packages/file_selector/file_selector/example/pubspec.yaml @@ -1,4 +1,4 @@ -name: example +name: file_selector_example description: A new Flutter project. publish_to: none @@ -17,8 +17,10 @@ dependencies: path: ../ flutter: sdk: flutter + path_provider: ^2.0.9 dev_dependencies: + build_runner: ^2.1.10 flutter_test: sdk: flutter diff --git a/packages/file_selector/file_selector/example/windows/flutter/generated_plugins.cmake b/packages/file_selector/file_selector/example/windows/flutter/generated_plugins.cmake index 63eda9b7b59f..a423a02476a2 100644 --- a/packages/file_selector/file_selector/example/windows/flutter/generated_plugins.cmake +++ b/packages/file_selector/file_selector/example/windows/flutter/generated_plugins.cmake @@ -6,6 +6,9 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_windows ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -14,3 +17,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index c2803d60c972..322ae6cda483 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -9,7 +9,24 @@ import 'package:file_selector_platform_interface/file_selector_platform_interfac export 'package:file_selector_platform_interface/file_selector_platform_interface.dart' show XFile, XTypeGroup; -/// Open file dialog for loading files and return a file path +/// Opens a file selection dialog and returns the path chosen by the user. +/// +/// [acceptedTypeGroups] is a list of file type groups that can be selected in +/// the dialog. How this is displayed depends on the pltaform, for example: +/// - On Windows and Linux, each group will be an entry in a list of filter +/// options. +/// - On macOS, the union of all types allowed by all of the groups will be +/// allowed. +/// +/// [initialDirectory] is the full path to the directory that will be displayed +/// when the dialog is opened. When not provided, the platform will pick an +/// initial location. This is ignored on the Web platform. +/// +/// [confirmButtonText] is the text in the confirmation button of the dialog. +/// When not provided, the default OS label is used (for example, "Open"). +/// This is ignored on the Web platform. +/// +/// Returns `null` if the user cancels the operation. Future openFile({ List acceptedTypeGroups = const [], String? initialDirectory, @@ -21,7 +38,24 @@ Future openFile({ confirmButtonText: confirmButtonText); } -/// Open file dialog for loading files and return a list of file paths +/// Opens a file selection dialog and returns the list of paths chosen by the +/// user. +/// +/// [acceptedTypeGroups] is a list of file type groups that can be selected in +/// the dialog. How this is displayed depends on the pltaform, for example: +/// - On Windows and Linux, each group will be an entry in a list of filter +/// options. +/// - On macOS, the union of all types allowed by all of the groups will be +/// allowed. +/// +/// [initialDirectory] is the full path to the directory that will be displayed +/// when the dialog is opened. When not provided, the platform will pick an +/// initial location. +/// +/// [confirmButtonText] is the text in the confirmation button of the dialog. +/// When not provided, the default OS label is used (for example, "Open"). +/// +/// Returns an empty list if the user cancels the operation. Future> openFiles({ List acceptedTypeGroups = const [], String? initialDirectory, @@ -33,7 +67,25 @@ Future> openFiles({ confirmButtonText: confirmButtonText); } -/// Saves File to user's file system +/// Opens a save dialog and returns the target path chosen by the user. +/// +/// [acceptedTypeGroups] is a list of file type groups that can be selected in +/// the dialog. How this is displayed depends on the pltaform, for example: +/// - On Windows and Linux, each group will be an entry in a list of filter +/// options. +/// - On macOS, the union of all types allowed by all of the groups will be +/// allowed. +/// +/// [initialDirectory] is the full path to the directory that will be displayed +/// when the dialog is opened. When not provided, the platform will pick an +/// initial location. +/// +/// [suggestedName] is initial value of file name. +/// +/// [confirmButtonText] is the text in the confirmation button of the dialog. +/// When not provided, the default OS label is used (for example, "Save"). +/// +/// Returns `null` if the user cancels the operation. Future getSavePath({ List acceptedTypeGroups = const [], String? initialDirectory, @@ -47,7 +99,17 @@ Future getSavePath({ confirmButtonText: confirmButtonText); } -/// Gets a directory path from a user's file system +/// Opens a directory selection dialog and returns the path chosen by the user. +/// This always returns `null` on the web. +/// +/// [initialDirectory] is the full path to the directory that will be displayed +/// when the dialog is opened. When not provided, the platform will pick an +/// initial location. +/// +/// [confirmButtonText] is the text in the confirmation button of the dialog. +/// When not provided, the default OS label is used (for example, "Open"). +/// +/// Returns `null` if the user cancels the operation. Future getDirectoryPath({ String? initialDirectory, String? confirmButtonText, diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index 1c502c055c9a..12a1fbc47782 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.4+2 +version: 0.8.4+3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/script/configs/temp_exclude_excerpt.yaml b/script/configs/temp_exclude_excerpt.yaml index bd73880ee5ee..dec99ee6edd9 100644 --- a/script/configs/temp_exclude_excerpt.yaml +++ b/script/configs/temp_exclude_excerpt.yaml @@ -7,7 +7,6 @@ # https://github.com/flutter/flutter/issues/102679 - camera_web - espresso -- file_selector/file_selector - google_maps_flutter/google_maps_flutter - google_sign_in/google_sign_in - google_sign_in_web From 6c1b4feaa66ec5c084b73f56b12d2f021f531fb4 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 11 Jun 2022 12:03:05 -0400 Subject: [PATCH 417/844] Roll Flutter from 25e0e290e6a4 to bc08d85718b6 (36 revisions) (#5951) --- .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 bc01325825ab..d29c2125c417 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -25e0e290e6a4182e030ea9a352c6124ea36ca22d +bc08d85718b654b7cffcdcff81473e400f666380 From 40c991a710c5d458018753550275e5d8a578ddfa Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 12 Jun 2022 12:03:06 -0400 Subject: [PATCH 418/844] Roll Flutter from bc08d85718b6 to 689b5cc68d2f (5 revisions) (#5954) --- .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 d29c2125c417..5535e495d0d2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -bc08d85718b654b7cffcdcff81473e400f666380 +689b5cc68d2f2184dab62eb7ff4df2156257b307 From 07891d3f6c562448f1a62ac1ef3ed65315e53d32 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 13 Jun 2022 13:58:11 -0400 Subject: [PATCH 419/844] Roll Flutter from 689b5cc68d2f to 712860d33b00 (4 revisions) (#5956) --- .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 5535e495d0d2..aa3ae00b60df 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -689b5cc68d2f2184dab62eb7ff4df2156257b307 +712860d33b00bf3acf02118512901a6261ad5219 From cdb311e03f640cdb42bcd4d38da0b07c8815846f Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 13 Jun 2022 16:28:10 -0400 Subject: [PATCH 420/844] [camera] Switch to internal method channels (#5943) --- CODEOWNERS | 4 +- packages/camera/camera_android/CHANGELOG.md | 4 + .../flutter/plugins/camera/DartMessenger.java | 5 +- .../plugins/camera/MethodCallHandlerImpl.java | 5 +- .../example/integration_test/camera_test.dart | 2 + .../camera_android/lib/camera_android.dart | 5 + .../lib/src/android_camera.dart | 587 +++++++++ .../lib/src/type_conversion.dart | 49 + .../camera/camera_android/lib/src/utils.dart | 51 + packages/camera/camera_android/pubspec.yaml | 5 +- .../test/android_camera_test.dart | 1075 +++++++++++++++++ .../test/method_channel_mock.dart | 39 + .../test/type_conversion_test.dart | 60 + .../camera_android/test/utils_test.dart | 60 + .../camera/camera_avfoundation/CHANGELOG.md | 4 + .../example/integration_test/camera_test.dart | 2 + .../example/test_driver/integration_test.dart | 61 +- .../ios/Classes/CameraPlugin.m | 13 +- .../camera_avfoundation/ios/Classes/FLTCam.m | 6 +- .../lib/camera_avfoundation.dart | 5 + .../lib/src/avfoundation_camera.dart | 592 +++++++++ .../lib/src/type_conversion.dart | 50 + .../camera_avfoundation/lib/src/utils.dart | 51 + .../camera/camera_avfoundation/pubspec.yaml | 5 +- .../test/avfoundation_camera_test.dart | 1075 +++++++++++++++++ .../test/method_channel_mock.dart | 39 + .../test/type_conversion_test.dart | 60 + .../camera_avfoundation/test/utils_test.dart | 60 + 28 files changed, 3898 insertions(+), 76 deletions(-) create mode 100644 packages/camera/camera_android/lib/camera_android.dart create mode 100644 packages/camera/camera_android/lib/src/android_camera.dart create mode 100644 packages/camera/camera_android/lib/src/type_conversion.dart create mode 100644 packages/camera/camera_android/lib/src/utils.dart create mode 100644 packages/camera/camera_android/test/android_camera_test.dart create mode 100644 packages/camera/camera_android/test/method_channel_mock.dart create mode 100644 packages/camera/camera_android/test/type_conversion_test.dart create mode 100644 packages/camera/camera_android/test/utils_test.dart create mode 100644 packages/camera/camera_avfoundation/lib/camera_avfoundation.dart create mode 100644 packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart create mode 100644 packages/camera/camera_avfoundation/lib/src/type_conversion.dart create mode 100644 packages/camera/camera_avfoundation/lib/src/utils.dart create mode 100644 packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart create mode 100644 packages/camera/camera_avfoundation/test/method_channel_mock.dart create mode 100644 packages/camera/camera_avfoundation/test/type_conversion_test.dart create mode 100644 packages/camera/camera_avfoundation/test/utils_test.dart diff --git a/CODEOWNERS b/CODEOWNERS index 68e4cb768a6a..16bf98ba9e4b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -26,7 +26,7 @@ packages/webview_flutter/** @bparrishMines packages/**/*_web/** @ditman # - Android -packages/camera/camera/android/** @camsim99 +packages/camera/camera_android/** @camsim99 packages/espresso/** @blasten packages/flutter_plugin_android_lifecycle/** @blasten packages/google_maps_flutter/google_maps_flutter/android/** @GaryQian @@ -40,7 +40,7 @@ packages/url_launcher/url_launcher_android/** @GaryQian packages/video_player/video_player_android/** @blasten # - iOS -packages/camera/camera/ios/** @hellohuanlin +packages/camera/camera_avfoundation/** @hellohuanlin packages/google_maps_flutter/google_maps_flutter/ios/** @cyanglaz packages/google_sign_in/google_sign_in_ios/** @jmagman packages/image_picker/image_picker_ios/** @cyanglaz diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index c57f301fae95..5bc7c8ae8d19 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.8 + +* Switches to internal method channel implementation. + ## 0.9.7+1 * Splits from `camera` as a federated implementation. diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java index dc62fce524d3..e15078e66afc 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/DartMessenger.java @@ -64,8 +64,9 @@ enum CameraEventType { * the main thread. The handler is mainly supplied so it will be easier test this class. */ DartMessenger(BinaryMessenger messenger, long cameraId, @NonNull Handler handler) { - cameraChannel = new MethodChannel(messenger, "flutter.io/cameraPlugin/camera" + cameraId); - deviceChannel = new MethodChannel(messenger, "flutter.io/cameraPlugin/device"); + cameraChannel = + new MethodChannel(messenger, "plugins.flutter.io/camera_android/camera" + cameraId); + deviceChannel = new MethodChannel(messenger, "plugins.flutter.io/camera_android/fromPlatform"); this.handler = handler; } diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java index 35cc2b081bae..38201e1136c9 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/MethodCallHandlerImpl.java @@ -49,8 +49,9 @@ final class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler { this.permissionsRegistry = permissionsAdder; this.textureRegistry = textureRegistry; - methodChannel = new MethodChannel(messenger, "plugins.flutter.io/camera"); - imageStreamChannel = new EventChannel(messenger, "plugins.flutter.io/camera/imageStream"); + methodChannel = new MethodChannel(messenger, "plugins.flutter.io/camera_android"); + imageStreamChannel = + new EventChannel(messenger, "plugins.flutter.io/camera_android/imageStream"); methodChannel.setMethodCallHandler(this); } diff --git a/packages/camera/camera_android/example/integration_test/camera_test.dart b/packages/camera/camera_android/example/integration_test/camera_test.dart index 05c669307dcc..99029fcac605 100644 --- a/packages/camera/camera_android/example/integration_test/camera_test.dart +++ b/packages/camera/camera_android/example/integration_test/camera_test.dart @@ -5,6 +5,7 @@ import 'dart:io'; import 'dart:ui'; +import 'package:camera_android/camera_android.dart'; import 'package:camera_example/camera_controller.dart'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/painting.dart'; @@ -19,6 +20,7 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); setUpAll(() async { + CameraPlatform.instance = AndroidCamera(); final Directory extDir = await getTemporaryDirectory(); testDir = await Directory('${extDir.path}/test').create(recursive: true); }); diff --git a/packages/camera/camera_android/lib/camera_android.dart b/packages/camera/camera_android/lib/camera_android.dart new file mode 100644 index 000000000000..93e3e17290c0 --- /dev/null +++ b/packages/camera/camera_android/lib/camera_android.dart @@ -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. + +export 'src/android_camera.dart'; diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart new file mode 100644 index 000000000000..5fb3443ff91e --- /dev/null +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -0,0 +1,587 @@ +// 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:async'; +import 'dart:math'; + +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:stream_transform/stream_transform.dart'; + +import 'type_conversion.dart'; +import 'utils.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.io/camera_android'); + +/// The Android implementation of [CameraPlatform] that uses method channels. +class AndroidCamera extends CameraPlatform { + /// Construct a new method channel camera instance. + AndroidCamera() { + const MethodChannel channel = + MethodChannel('plugins.flutter.io/camera_android/fromPlatform'); + channel.setMethodCallHandler( + (MethodCall call) => handleDeviceMethodCall(call)); + } + + /// Registers this class as the default instance of [CameraPlatform]. + static void registerWith() { + CameraPlatform.instance = AndroidCamera(); + } + + final Map _channels = {}; + + /// The controller we need to broadcast the different events coming + /// from handleMethodCall, specific to camera events. + /// + /// It is a `broadcast` because multiple controllers will connect to + /// different stream views of this Controller. + /// This is only exposed for test purposes. It shouldn't be used by clients of + /// the plugin as it may break or change at any time. + @visibleForTesting + final StreamController cameraEventStreamController = + StreamController.broadcast(); + + /// The controller we need to broadcast the different events coming + /// from handleMethodCall, specific to general device events. + /// + /// It is a `broadcast` because multiple controllers will connect to + /// different stream views of this Controller. + /// This is only exposed for test purposes. It shouldn't be used by clients of + /// the plugin as it may break or change at any time. + @visibleForTesting + final StreamController deviceEventStreamController = + StreamController.broadcast(); + + // The stream to receive frames from the native code. + StreamSubscription? _platformImageStreamSubscription; + + // The stream for vending frames to platform interface clients. + StreamController? _frameStreamController; + + Stream _cameraEvents(int cameraId) => + cameraEventStreamController.stream + .where((CameraEvent event) => event.cameraId == cameraId); + + @override + Future> availableCameras() async { + try { + final List>? cameras = await _channel + .invokeListMethod>('availableCameras'); + + if (cameras == null) { + return []; + } + + return cameras.map((Map camera) { + return CameraDescription( + name: camera['name']! as String, + lensDirection: + parseCameraLensDirection(camera['lensFacing']! as String), + sensorOrientation: camera['sensorOrientation']! as int, + ); + }).toList(); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } + + @override + Future createCamera( + CameraDescription cameraDescription, + ResolutionPreset? resolutionPreset, { + bool enableAudio = false, + }) async { + try { + final Map? reply = await _channel + .invokeMapMethod('create', { + 'cameraName': cameraDescription.name, + 'resolutionPreset': resolutionPreset != null + ? _serializeResolutionPreset(resolutionPreset) + : null, + 'enableAudio': enableAudio, + }); + + return reply!['cameraId']! as int; + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } + + @override + Future initializeCamera( + int cameraId, { + ImageFormatGroup imageFormatGroup = ImageFormatGroup.unknown, + }) { + _channels.putIfAbsent(cameraId, () { + final MethodChannel channel = + MethodChannel('plugins.flutter.io/camera_android/camera$cameraId'); + channel.setMethodCallHandler( + (MethodCall call) => handleCameraMethodCall(call, cameraId)); + return channel; + }); + + final Completer _completer = Completer(); + + onCameraInitialized(cameraId).first.then((CameraInitializedEvent value) { + _completer.complete(); + }); + + _channel.invokeMapMethod( + 'initialize', + { + 'cameraId': cameraId, + 'imageFormatGroup': imageFormatGroup.name(), + }, + ).catchError( + (Object error, StackTrace stackTrace) { + if (error is! PlatformException) { + throw error; + } + _completer.completeError( + CameraException(error.code, error.message), + stackTrace, + ); + }, + ); + + return _completer.future; + } + + @override + Future dispose(int cameraId) async { + if (_channels.containsKey(cameraId)) { + final MethodChannel? cameraChannel = _channels[cameraId]; + cameraChannel?.setMethodCallHandler(null); + _channels.remove(cameraId); + } + + await _channel.invokeMethod( + 'dispose', + {'cameraId': cameraId}, + ); + } + + @override + Stream onCameraInitialized(int cameraId) { + return _cameraEvents(cameraId).whereType(); + } + + @override + Stream onCameraResolutionChanged(int cameraId) { + return _cameraEvents(cameraId).whereType(); + } + + @override + Stream onCameraClosing(int cameraId) { + return _cameraEvents(cameraId).whereType(); + } + + @override + Stream onCameraError(int cameraId) { + return _cameraEvents(cameraId).whereType(); + } + + @override + Stream onVideoRecordedEvent(int cameraId) { + return _cameraEvents(cameraId).whereType(); + } + + @override + Stream onDeviceOrientationChanged() { + return deviceEventStreamController.stream + .whereType(); + } + + @override + Future lockCaptureOrientation( + int cameraId, + DeviceOrientation orientation, + ) async { + await _channel.invokeMethod( + 'lockCaptureOrientation', + { + 'cameraId': cameraId, + 'orientation': serializeDeviceOrientation(orientation) + }, + ); + } + + @override + Future unlockCaptureOrientation(int cameraId) async { + await _channel.invokeMethod( + 'unlockCaptureOrientation', + {'cameraId': cameraId}, + ); + } + + @override + Future takePicture(int cameraId) async { + final String? path = await _channel.invokeMethod( + 'takePicture', + {'cameraId': cameraId}, + ); + + if (path == null) { + throw CameraException( + 'INVALID_PATH', + 'The platform "$defaultTargetPlatform" did not return a path while reporting success. The platform should always return a valid path or report an error.', + ); + } + + return XFile(path); + } + + @override + Future prepareForVideoRecording() => + _channel.invokeMethod('prepareForVideoRecording'); + + @override + Future startVideoRecording(int cameraId, + {Duration? maxVideoDuration}) async { + await _channel.invokeMethod( + 'startVideoRecording', + { + 'cameraId': cameraId, + 'maxVideoDuration': maxVideoDuration?.inMilliseconds, + }, + ); + } + + @override + Future stopVideoRecording(int cameraId) async { + final String? path = await _channel.invokeMethod( + 'stopVideoRecording', + {'cameraId': cameraId}, + ); + + if (path == null) { + throw CameraException( + 'INVALID_PATH', + 'The platform "$defaultTargetPlatform" did not return a path while reporting success. The platform should always return a valid path or report an error.', + ); + } + + return XFile(path); + } + + @override + Future pauseVideoRecording(int cameraId) => _channel.invokeMethod( + 'pauseVideoRecording', + {'cameraId': cameraId}, + ); + + @override + Future resumeVideoRecording(int cameraId) => + _channel.invokeMethod( + 'resumeVideoRecording', + {'cameraId': cameraId}, + ); + + @override + Stream onStreamedFrameAvailable(int cameraId, + {CameraImageStreamOptions? options}) { + _frameStreamController = StreamController( + onListen: _onFrameStreamListen, + onPause: _onFrameStreamPauseResume, + onResume: _onFrameStreamPauseResume, + onCancel: _onFrameStreamCancel, + ); + return _frameStreamController!.stream; + } + + void _onFrameStreamListen() { + _startPlatformStream(); + } + + Future _startPlatformStream() async { + await _channel.invokeMethod('startImageStream'); + const EventChannel cameraEventChannel = + EventChannel('plugins.flutter.io/camera_android/imageStream'); + _platformImageStreamSubscription = + cameraEventChannel.receiveBroadcastStream().listen((dynamic imageData) { + _frameStreamController! + .add(cameraImageFromPlatformData(imageData as Map)); + }); + } + + FutureOr _onFrameStreamCancel() async { + await _channel.invokeMethod('stopImageStream'); + await _platformImageStreamSubscription?.cancel(); + _platformImageStreamSubscription = null; + _frameStreamController = null; + } + + void _onFrameStreamPauseResume() { + throw CameraException('InvalidCall', + 'Pause and resume are not supported for onStreamedFrameAvailable'); + } + + @override + Future setFlashMode(int cameraId, FlashMode mode) => + _channel.invokeMethod( + 'setFlashMode', + { + 'cameraId': cameraId, + 'mode': _serializeFlashMode(mode), + }, + ); + + @override + Future setExposureMode(int cameraId, ExposureMode mode) => + _channel.invokeMethod( + 'setExposureMode', + { + 'cameraId': cameraId, + 'mode': serializeExposureMode(mode), + }, + ); + + @override + Future setExposurePoint(int cameraId, Point? point) { + assert(point == null || point.x >= 0 && point.x <= 1); + assert(point == null || point.y >= 0 && point.y <= 1); + + return _channel.invokeMethod( + 'setExposurePoint', + { + 'cameraId': cameraId, + 'reset': point == null, + 'x': point?.x, + 'y': point?.y, + }, + ); + } + + @override + Future getMinExposureOffset(int cameraId) async { + final double? minExposureOffset = await _channel.invokeMethod( + 'getMinExposureOffset', + {'cameraId': cameraId}, + ); + + return minExposureOffset!; + } + + @override + Future getMaxExposureOffset(int cameraId) async { + final double? maxExposureOffset = await _channel.invokeMethod( + 'getMaxExposureOffset', + {'cameraId': cameraId}, + ); + + return maxExposureOffset!; + } + + @override + Future getExposureOffsetStepSize(int cameraId) async { + final double? stepSize = await _channel.invokeMethod( + 'getExposureOffsetStepSize', + {'cameraId': cameraId}, + ); + + return stepSize!; + } + + @override + Future setExposureOffset(int cameraId, double offset) async { + final double? appliedOffset = await _channel.invokeMethod( + 'setExposureOffset', + { + 'cameraId': cameraId, + 'offset': offset, + }, + ); + + return appliedOffset!; + } + + @override + Future setFocusMode(int cameraId, FocusMode mode) => + _channel.invokeMethod( + 'setFocusMode', + { + 'cameraId': cameraId, + 'mode': serializeFocusMode(mode), + }, + ); + + @override + Future setFocusPoint(int cameraId, Point? point) { + assert(point == null || point.x >= 0 && point.x <= 1); + assert(point == null || point.y >= 0 && point.y <= 1); + + return _channel.invokeMethod( + 'setFocusPoint', + { + 'cameraId': cameraId, + 'reset': point == null, + 'x': point?.x, + 'y': point?.y, + }, + ); + } + + @override + Future getMaxZoomLevel(int cameraId) async { + final double? maxZoomLevel = await _channel.invokeMethod( + 'getMaxZoomLevel', + {'cameraId': cameraId}, + ); + + return maxZoomLevel!; + } + + @override + Future getMinZoomLevel(int cameraId) async { + final double? minZoomLevel = await _channel.invokeMethod( + 'getMinZoomLevel', + {'cameraId': cameraId}, + ); + + return minZoomLevel!; + } + + @override + Future setZoomLevel(int cameraId, double zoom) async { + try { + await _channel.invokeMethod( + 'setZoomLevel', + { + 'cameraId': cameraId, + 'zoom': zoom, + }, + ); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } + + @override + Future pausePreview(int cameraId) async { + await _channel.invokeMethod( + 'pausePreview', + {'cameraId': cameraId}, + ); + } + + @override + Future resumePreview(int cameraId) async { + await _channel.invokeMethod( + 'resumePreview', + {'cameraId': cameraId}, + ); + } + + @override + Widget buildPreview(int cameraId) { + return Texture(textureId: cameraId); + } + + /// Returns the flash mode as a String. + String _serializeFlashMode(FlashMode flashMode) { + switch (flashMode) { + case FlashMode.off: + return 'off'; + case FlashMode.auto: + return 'auto'; + case FlashMode.always: + return 'always'; + case FlashMode.torch: + return 'torch'; + default: + throw ArgumentError('Unknown FlashMode value'); + } + } + + /// Returns the resolution preset as a String. + String _serializeResolutionPreset(ResolutionPreset resolutionPreset) { + switch (resolutionPreset) { + case ResolutionPreset.max: + return 'max'; + case ResolutionPreset.ultraHigh: + return 'ultraHigh'; + case ResolutionPreset.veryHigh: + return 'veryHigh'; + case ResolutionPreset.high: + return 'high'; + case ResolutionPreset.medium: + return 'medium'; + case ResolutionPreset.low: + return 'low'; + default: + throw ArgumentError('Unknown ResolutionPreset value'); + } + } + + /// Converts messages received from the native platform into device events. + /// + /// This is only exposed for test purposes. It shouldn't be used by clients of + /// the plugin as it may break or change at any time. + @visibleForTesting + Future handleDeviceMethodCall(MethodCall call) async { + switch (call.method) { + case 'orientation_changed': + deviceEventStreamController.add(DeviceOrientationChangedEvent( + deserializeDeviceOrientation( + call.arguments['orientation']! as String))); + break; + default: + throw MissingPluginException(); + } + } + + /// Converts messages received from the native platform into camera events. + /// + /// This is only exposed for test purposes. It shouldn't be used by clients of + /// the plugin as it may break or change at any time. + @visibleForTesting + Future handleCameraMethodCall(MethodCall call, int cameraId) async { + switch (call.method) { + case 'initialized': + cameraEventStreamController.add(CameraInitializedEvent( + cameraId, + call.arguments['previewWidth']! as double, + call.arguments['previewHeight']! as double, + deserializeExposureMode(call.arguments['exposureMode']! as String), + call.arguments['exposurePointSupported']! as bool, + deserializeFocusMode(call.arguments['focusMode']! as String), + call.arguments['focusPointSupported']! as bool, + )); + break; + case 'resolution_changed': + cameraEventStreamController.add(CameraResolutionChangedEvent( + cameraId, + call.arguments['captureWidth']! as double, + call.arguments['captureHeight']! as double, + )); + break; + case 'camera_closing': + cameraEventStreamController.add(CameraClosingEvent( + cameraId, + )); + break; + case 'video_recorded': + cameraEventStreamController.add(VideoRecordedEvent( + cameraId, + XFile(call.arguments['path']! as String), + call.arguments['maxVideoDuration'] != null + ? Duration( + milliseconds: call.arguments['maxVideoDuration']! as int) + : null, + )); + break; + case 'error': + cameraEventStreamController.add(CameraErrorEvent( + cameraId, + call.arguments['description']! as String, + )); + break; + default: + throw MissingPluginException(); + } + } +} diff --git a/packages/camera/camera_android/lib/src/type_conversion.dart b/packages/camera/camera_android/lib/src/type_conversion.dart new file mode 100644 index 000000000000..754a5a032715 --- /dev/null +++ b/packages/camera/camera_android/lib/src/type_conversion.dart @@ -0,0 +1,49 @@ +// 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. + +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import +import 'dart:typed_data'; + +import 'package:camera_platform_interface/camera_platform_interface.dart'; + +/// Converts method channel call [data] for `receivedImageStreamData` to a +/// [CameraImageData]. +CameraImageData cameraImageFromPlatformData(Map data) { + return CameraImageData( + format: _cameraImageFormatFromPlatformData(data['format']), + height: data['height'] as int, + width: data['width'] as int, + lensAperture: data['lensAperture'] as double?, + sensorExposureTime: data['sensorExposureTime'] as int?, + sensorSensitivity: data['sensorSensitivity'] as double?, + planes: List.unmodifiable( + (data['planes'] as List).map( + (dynamic planeData) => _cameraImagePlaneFromPlatformData( + planeData as Map)))); +} + +CameraImageFormat _cameraImageFormatFromPlatformData(dynamic data) { + return CameraImageFormat(_imageFormatGroupFromPlatformData(data), raw: data); +} + +ImageFormatGroup _imageFormatGroupFromPlatformData(dynamic data) { + switch (data) { + case 35: // android.graphics.ImageFormat.YUV_420_888 + return ImageFormatGroup.yuv420; + case 256: // android.graphics.ImageFormat.JPEG + return ImageFormatGroup.jpeg; + } + + return ImageFormatGroup.unknown; +} + +CameraImagePlane _cameraImagePlaneFromPlatformData(Map data) { + return CameraImagePlane( + bytes: data['bytes'] as Uint8List, + bytesPerPixel: data['bytesPerPixel'] as int?, + bytesPerRow: data['bytesPerRow'] as int, + height: data['height'] as int?, + width: data['width'] as int?); +} diff --git a/packages/camera/camera_android/lib/src/utils.dart b/packages/camera/camera_android/lib/src/utils.dart new file mode 100644 index 000000000000..663ec6da7a97 --- /dev/null +++ b/packages/camera/camera_android/lib/src/utils.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:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/services.dart'; + +/// Parses a string into a corresponding CameraLensDirection. +CameraLensDirection parseCameraLensDirection(String string) { + switch (string) { + case 'front': + return CameraLensDirection.front; + case 'back': + return CameraLensDirection.back; + case 'external': + return CameraLensDirection.external; + } + throw ArgumentError('Unknown CameraLensDirection value'); +} + +/// Returns the device orientation as a String. +String serializeDeviceOrientation(DeviceOrientation orientation) { + switch (orientation) { + case DeviceOrientation.portraitUp: + return 'portraitUp'; + case DeviceOrientation.portraitDown: + return 'portraitDown'; + case DeviceOrientation.landscapeRight: + return 'landscapeRight'; + case DeviceOrientation.landscapeLeft: + return 'landscapeLeft'; + default: + throw ArgumentError('Unknown DeviceOrientation value'); + } +} + +/// Returns the device orientation for a given String. +DeviceOrientation deserializeDeviceOrientation(String str) { + switch (str) { + case 'portraitUp': + return DeviceOrientation.portraitUp; + case 'portraitDown': + return DeviceOrientation.portraitDown; + case 'landscapeRight': + return DeviceOrientation.landscapeRight; + case 'landscapeLeft': + return DeviceOrientation.landscapeLeft; + default: + throw ArgumentError('"$str" is not a valid DeviceOrientation value'); + } +} diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 908d55f9c50e..73eeaa720dbc 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android description: Android implementation of the camera plugin. repository: https://github.com/flutter/plugins/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.9.7+1 +version: 0.9.8 environment: sdk: ">=2.14.0 <3.0.0" @@ -15,14 +15,17 @@ flutter: android: package: io.flutter.plugins.camera pluginClass: CameraPlugin + dartPluginClass: AndroidCamera dependencies: camera_platform_interface: ^2.2.0 flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.2 + stream_transform: ^2.0.0 dev_dependencies: + async: ^2.5.0 flutter_driver: sdk: flutter flutter_test: diff --git a/packages/camera/camera_android/test/android_camera_test.dart b/packages/camera/camera_android/test/android_camera_test.dart new file mode 100644 index 000000000000..9674b0c5a420 --- /dev/null +++ b/packages/camera/camera_android/test/android_camera_test.dart @@ -0,0 +1,1075 @@ +// 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:async'; +import 'dart:math'; + +import 'package:async/async.dart'; +import 'package:camera_android/src/android_camera.dart'; +import 'package:camera_android/src/utils.dart'; +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'method_channel_mock.dart'; + +const String _channelName = 'plugins.flutter.io/camera_android'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + test('registers instance', () async { + AndroidCamera.registerWith(); + expect(CameraPlatform.instance, isA()); + }); + + group('Creation, Initialization & Disposal Tests', () { + test('Should send creation data and receive back a camera id', () async { + // Arrange + final MethodChannelMock cameraMockChannel = MethodChannelMock( + channelName: _channelName, + methods: { + 'create': { + 'cameraId': 1, + 'imageFormatGroup': 'unknown', + } + }); + final AndroidCamera camera = AndroidCamera(); + + // Act + final int cameraId = await camera.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0), + ResolutionPreset.high, + ); + + // Assert + expect(cameraMockChannel.log, [ + isMethodCall( + 'create', + arguments: { + 'cameraName': 'Test', + 'resolutionPreset': 'high', + 'enableAudio': false + }, + ), + ]); + expect(cameraId, 1); + }); + + test('Should throw CameraException when create throws a PlatformException', + () { + // Arrange + MethodChannelMock(channelName: _channelName, methods: { + 'create': PlatformException( + code: 'TESTING_ERROR_CODE', + message: 'Mock error message used during testing.', + ) + }); + final AndroidCamera camera = AndroidCamera(); + + // Act + expect( + () => camera.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0, + ), + ResolutionPreset.high, + ), + throwsA( + isA() + .having( + (CameraException e) => e.code, 'code', 'TESTING_ERROR_CODE') + .having((CameraException e) => e.description, 'description', + 'Mock error message used during testing.'), + ), + ); + }); + + test('Should throw CameraException when create throws a PlatformException', + () { + // Arrange + MethodChannelMock(channelName: _channelName, methods: { + 'create': PlatformException( + code: 'TESTING_ERROR_CODE', + message: 'Mock error message used during testing.', + ) + }); + final AndroidCamera camera = AndroidCamera(); + + // Act + expect( + () => camera.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0, + ), + ResolutionPreset.high, + ), + throwsA( + isA() + .having( + (CameraException e) => e.code, 'code', 'TESTING_ERROR_CODE') + .having((CameraException e) => e.description, 'description', + 'Mock error message used during testing.'), + ), + ); + }); + + test( + 'Should throw CameraException when initialize throws a PlatformException', + () { + // Arrange + MethodChannelMock( + channelName: _channelName, + methods: { + 'initialize': PlatformException( + code: 'TESTING_ERROR_CODE', + message: 'Mock error message used during testing.', + ) + }, + ); + final AndroidCamera camera = AndroidCamera(); + + // Act + expect( + () => camera.initializeCamera(0), + throwsA( + isA() + .having( + (CameraException e) => e.code, 'code', 'TESTING_ERROR_CODE') + .having( + (CameraException e) => e.description, + 'description', + 'Mock error message used during testing.', + ), + ), + ); + }, + ); + + test('Should send initialization data', () async { + // Arrange + final MethodChannelMock cameraMockChannel = MethodChannelMock( + channelName: _channelName, + methods: { + 'create': { + 'cameraId': 1, + 'imageFormatGroup': 'unknown', + }, + 'initialize': null + }); + final AndroidCamera camera = AndroidCamera(); + final int cameraId = await camera.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0, + ), + ResolutionPreset.high, + ); + + // Act + final Future initializeFuture = camera.initializeCamera(cameraId); + camera.cameraEventStreamController.add(CameraInitializedEvent( + cameraId, + 1920, + 1080, + ExposureMode.auto, + true, + FocusMode.auto, + true, + )); + await initializeFuture; + + // Assert + expect(cameraId, 1); + expect(cameraMockChannel.log, [ + anything, + isMethodCall( + 'initialize', + arguments: { + 'cameraId': 1, + 'imageFormatGroup': 'unknown', + }, + ), + ]); + }); + + test('Should send a disposal call on dispose', () async { + // Arrange + final MethodChannelMock cameraMockChannel = MethodChannelMock( + channelName: _channelName, + methods: { + 'create': {'cameraId': 1}, + 'initialize': null, + 'dispose': {'cameraId': 1} + }); + + final AndroidCamera camera = AndroidCamera(); + final int cameraId = await camera.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0, + ), + ResolutionPreset.high, + ); + final Future initializeFuture = camera.initializeCamera(cameraId); + camera.cameraEventStreamController.add(CameraInitializedEvent( + cameraId, + 1920, + 1080, + ExposureMode.auto, + true, + FocusMode.auto, + true, + )); + await initializeFuture; + + // Act + await camera.dispose(cameraId); + + // Assert + expect(cameraId, 1); + expect(cameraMockChannel.log, [ + anything, + anything, + isMethodCall( + 'dispose', + arguments: {'cameraId': 1}, + ), + ]); + }); + }); + + group('Event Tests', () { + late AndroidCamera camera; + late int cameraId; + setUp(() async { + MethodChannelMock( + channelName: _channelName, + methods: { + 'create': {'cameraId': 1}, + 'initialize': null + }, + ); + camera = AndroidCamera(); + cameraId = await camera.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0, + ), + ResolutionPreset.high, + ); + final Future initializeFuture = camera.initializeCamera(cameraId); + camera.cameraEventStreamController.add(CameraInitializedEvent( + cameraId, + 1920, + 1080, + ExposureMode.auto, + true, + FocusMode.auto, + true, + )); + await initializeFuture; + }); + + test('Should receive initialized event', () async { + // Act + final Stream eventStream = + camera.onCameraInitialized(cameraId); + final StreamQueue streamQueue = + StreamQueue(eventStream); + + // Emit test events + final CameraInitializedEvent event = CameraInitializedEvent( + cameraId, + 3840, + 2160, + ExposureMode.auto, + true, + FocusMode.auto, + true, + ); + await camera.handleCameraMethodCall( + MethodCall('initialized', event.toJson()), cameraId); + + // Assert + expect(await streamQueue.next, event); + + // Clean up + await streamQueue.cancel(); + }); + + test('Should receive resolution changes', () async { + // Act + final Stream resolutionStream = + camera.onCameraResolutionChanged(cameraId); + final StreamQueue streamQueue = + StreamQueue(resolutionStream); + + // Emit test events + final CameraResolutionChangedEvent fhdEvent = + CameraResolutionChangedEvent(cameraId, 1920, 1080); + final CameraResolutionChangedEvent uhdEvent = + CameraResolutionChangedEvent(cameraId, 3840, 2160); + await camera.handleCameraMethodCall( + MethodCall('resolution_changed', fhdEvent.toJson()), cameraId); + await camera.handleCameraMethodCall( + MethodCall('resolution_changed', uhdEvent.toJson()), cameraId); + await camera.handleCameraMethodCall( + MethodCall('resolution_changed', fhdEvent.toJson()), cameraId); + await camera.handleCameraMethodCall( + MethodCall('resolution_changed', uhdEvent.toJson()), cameraId); + + // Assert + expect(await streamQueue.next, fhdEvent); + expect(await streamQueue.next, uhdEvent); + expect(await streamQueue.next, fhdEvent); + expect(await streamQueue.next, uhdEvent); + + // Clean up + await streamQueue.cancel(); + }); + + test('Should receive camera closing events', () async { + // Act + final Stream eventStream = + camera.onCameraClosing(cameraId); + final StreamQueue streamQueue = + StreamQueue(eventStream); + + // Emit test events + final CameraClosingEvent event = CameraClosingEvent(cameraId); + await camera.handleCameraMethodCall( + MethodCall('camera_closing', event.toJson()), cameraId); + await camera.handleCameraMethodCall( + MethodCall('camera_closing', event.toJson()), cameraId); + await camera.handleCameraMethodCall( + MethodCall('camera_closing', event.toJson()), cameraId); + + // Assert + expect(await streamQueue.next, event); + expect(await streamQueue.next, event); + expect(await streamQueue.next, event); + + // Clean up + await streamQueue.cancel(); + }); + + test('Should receive camera error events', () async { + // Act + final Stream errorStream = + camera.onCameraError(cameraId); + final StreamQueue streamQueue = + StreamQueue(errorStream); + + // Emit test events + final CameraErrorEvent event = + CameraErrorEvent(cameraId, 'Error Description'); + await camera.handleCameraMethodCall( + MethodCall('error', event.toJson()), cameraId); + await camera.handleCameraMethodCall( + MethodCall('error', event.toJson()), cameraId); + await camera.handleCameraMethodCall( + MethodCall('error', event.toJson()), cameraId); + + // Assert + expect(await streamQueue.next, event); + expect(await streamQueue.next, event); + expect(await streamQueue.next, event); + + // Clean up + await streamQueue.cancel(); + }); + + test('Should receive device orientation change events', () async { + // Act + final Stream eventStream = + camera.onDeviceOrientationChanged(); + final StreamQueue streamQueue = + StreamQueue(eventStream); + + // Emit test events + const DeviceOrientationChangedEvent event = + DeviceOrientationChangedEvent(DeviceOrientation.portraitUp); + await camera.handleDeviceMethodCall( + MethodCall('orientation_changed', event.toJson())); + await camera.handleDeviceMethodCall( + MethodCall('orientation_changed', event.toJson())); + await camera.handleDeviceMethodCall( + MethodCall('orientation_changed', event.toJson())); + + // Assert + expect(await streamQueue.next, event); + expect(await streamQueue.next, event); + expect(await streamQueue.next, event); + + // Clean up + await streamQueue.cancel(); + }); + }); + + group('Function Tests', () { + late AndroidCamera camera; + late int cameraId; + + setUp(() async { + MethodChannelMock( + channelName: _channelName, + methods: { + 'create': {'cameraId': 1}, + 'initialize': null + }, + ); + camera = AndroidCamera(); + cameraId = await camera.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0, + ), + ResolutionPreset.high, + ); + final Future initializeFuture = camera.initializeCamera(cameraId); + camera.cameraEventStreamController.add( + CameraInitializedEvent( + cameraId, + 1920, + 1080, + ExposureMode.auto, + true, + FocusMode.auto, + true, + ), + ); + await initializeFuture; + }); + + test('Should fetch CameraDescription instances for available cameras', + () async { + // Arrange + final List returnData = [ + { + 'name': 'Test 1', + 'lensFacing': 'front', + 'sensorOrientation': 1 + }, + { + 'name': 'Test 2', + 'lensFacing': 'back', + 'sensorOrientation': 2 + } + ]; + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'availableCameras': returnData}, + ); + + // Act + final List cameras = await camera.availableCameras(); + + // Assert + expect(channel.log, [ + isMethodCall('availableCameras', arguments: null), + ]); + expect(cameras.length, returnData.length); + for (int i = 0; i < returnData.length; i++) { + final CameraDescription cameraDescription = CameraDescription( + name: returnData[i]['name']! as String, + lensDirection: + parseCameraLensDirection(returnData[i]['lensFacing']! as String), + sensorOrientation: returnData[i]['sensorOrientation']! as int, + ); + expect(cameras[i], cameraDescription); + } + }); + + test( + 'Should throw CameraException when availableCameras throws a PlatformException', + () { + // Arrange + MethodChannelMock(channelName: _channelName, methods: { + 'availableCameras': PlatformException( + code: 'TESTING_ERROR_CODE', + message: 'Mock error message used during testing.', + ) + }); + + // Act + expect( + camera.availableCameras, + throwsA( + isA() + .having( + (CameraException e) => e.code, 'code', 'TESTING_ERROR_CODE') + .having((CameraException e) => e.description, 'description', + 'Mock error message used during testing.'), + ), + ); + }); + + test('Should take a picture and return an XFile instance', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'takePicture': '/test/path.jpg'}); + + // Act + final XFile file = await camera.takePicture(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('takePicture', arguments: { + 'cameraId': cameraId, + }), + ]); + expect(file.path, '/test/path.jpg'); + }); + + test('Should prepare for video recording', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'prepareForVideoRecording': null}, + ); + + // Act + await camera.prepareForVideoRecording(); + + // Assert + expect(channel.log, [ + isMethodCall('prepareForVideoRecording', arguments: null), + ]); + }); + + test('Should start recording a video', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'startVideoRecording': null}, + ); + + // Act + await camera.startVideoRecording(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('startVideoRecording', arguments: { + 'cameraId': cameraId, + 'maxVideoDuration': null, + }), + ]); + }); + + test('Should pass maxVideoDuration when starting recording a video', + () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'startVideoRecording': null}, + ); + + // Act + await camera.startVideoRecording( + cameraId, + maxVideoDuration: const Duration(seconds: 10), + ); + + // Assert + expect(channel.log, [ + isMethodCall('startVideoRecording', arguments: { + 'cameraId': cameraId, + 'maxVideoDuration': 10000 + }), + ]); + }); + + test('Should stop a video recording and return the file', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'stopVideoRecording': '/test/path.mp4'}, + ); + + // Act + final XFile file = await camera.stopVideoRecording(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('stopVideoRecording', arguments: { + 'cameraId': cameraId, + }), + ]); + expect(file.path, '/test/path.mp4'); + }); + + test('Should pause a video recording', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'pauseVideoRecording': null}, + ); + + // Act + await camera.pauseVideoRecording(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('pauseVideoRecording', arguments: { + 'cameraId': cameraId, + }), + ]); + }); + + test('Should resume a video recording', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'resumeVideoRecording': null}, + ); + + // Act + await camera.resumeVideoRecording(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('resumeVideoRecording', arguments: { + 'cameraId': cameraId, + }), + ]); + }); + + test('Should set the flash mode', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'setFlashMode': null}, + ); + + // Act + await camera.setFlashMode(cameraId, FlashMode.torch); + await camera.setFlashMode(cameraId, FlashMode.always); + await camera.setFlashMode(cameraId, FlashMode.auto); + await camera.setFlashMode(cameraId, FlashMode.off); + + // Assert + expect(channel.log, [ + isMethodCall('setFlashMode', arguments: { + 'cameraId': cameraId, + 'mode': 'torch' + }), + isMethodCall('setFlashMode', arguments: { + 'cameraId': cameraId, + 'mode': 'always' + }), + isMethodCall('setFlashMode', + arguments: {'cameraId': cameraId, 'mode': 'auto'}), + isMethodCall('setFlashMode', + arguments: {'cameraId': cameraId, 'mode': 'off'}), + ]); + }); + + test('Should set the exposure mode', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'setExposureMode': null}, + ); + + // Act + await camera.setExposureMode(cameraId, ExposureMode.auto); + await camera.setExposureMode(cameraId, ExposureMode.locked); + + // Assert + expect(channel.log, [ + isMethodCall('setExposureMode', + arguments: {'cameraId': cameraId, 'mode': 'auto'}), + isMethodCall('setExposureMode', arguments: { + 'cameraId': cameraId, + 'mode': 'locked' + }), + ]); + }); + + test('Should set the exposure point', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'setExposurePoint': null}, + ); + + // Act + await camera.setExposurePoint(cameraId, const Point(0.5, 0.5)); + await camera.setExposurePoint(cameraId, null); + + // Assert + expect(channel.log, [ + isMethodCall('setExposurePoint', arguments: { + 'cameraId': cameraId, + 'x': 0.5, + 'y': 0.5, + 'reset': false + }), + isMethodCall('setExposurePoint', arguments: { + 'cameraId': cameraId, + 'x': null, + 'y': null, + 'reset': true + }), + ]); + }); + + test('Should get the min exposure offset', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'getMinExposureOffset': 2.0}, + ); + + // Act + final double minExposureOffset = + await camera.getMinExposureOffset(cameraId); + + // Assert + expect(minExposureOffset, 2.0); + expect(channel.log, [ + isMethodCall('getMinExposureOffset', arguments: { + 'cameraId': cameraId, + }), + ]); + }); + + test('Should get the max exposure offset', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'getMaxExposureOffset': 2.0}, + ); + + // Act + final double maxExposureOffset = + await camera.getMaxExposureOffset(cameraId); + + // Assert + expect(maxExposureOffset, 2.0); + expect(channel.log, [ + isMethodCall('getMaxExposureOffset', arguments: { + 'cameraId': cameraId, + }), + ]); + }); + + test('Should get the exposure offset step size', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'getExposureOffsetStepSize': 0.25}, + ); + + // Act + final double stepSize = await camera.getExposureOffsetStepSize(cameraId); + + // Assert + expect(stepSize, 0.25); + expect(channel.log, [ + isMethodCall('getExposureOffsetStepSize', arguments: { + 'cameraId': cameraId, + }), + ]); + }); + + test('Should set the exposure offset', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'setExposureOffset': 0.6}, + ); + + // Act + final double actualOffset = await camera.setExposureOffset(cameraId, 0.5); + + // Assert + expect(actualOffset, 0.6); + expect(channel.log, [ + isMethodCall('setExposureOffset', arguments: { + 'cameraId': cameraId, + 'offset': 0.5, + }), + ]); + }); + + test('Should set the focus mode', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'setFocusMode': null}, + ); + + // Act + await camera.setFocusMode(cameraId, FocusMode.auto); + await camera.setFocusMode(cameraId, FocusMode.locked); + + // Assert + expect(channel.log, [ + isMethodCall('setFocusMode', + arguments: {'cameraId': cameraId, 'mode': 'auto'}), + isMethodCall('setFocusMode', arguments: { + 'cameraId': cameraId, + 'mode': 'locked' + }), + ]); + }); + + test('Should set the exposure point', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'setFocusPoint': null}, + ); + + // Act + await camera.setFocusPoint(cameraId, const Point(0.5, 0.5)); + await camera.setFocusPoint(cameraId, null); + + // Assert + expect(channel.log, [ + isMethodCall('setFocusPoint', arguments: { + 'cameraId': cameraId, + 'x': 0.5, + 'y': 0.5, + 'reset': false + }), + isMethodCall('setFocusPoint', arguments: { + 'cameraId': cameraId, + 'x': null, + 'y': null, + 'reset': true + }), + ]); + }); + + test('Should build a texture widget as preview widget', () async { + // Act + final Widget widget = camera.buildPreview(cameraId); + + // Act + expect(widget is Texture, isTrue); + expect((widget as Texture).textureId, cameraId); + }); + + test('Should throw MissingPluginException when handling unknown method', + () { + final AndroidCamera camera = AndroidCamera(); + + expect( + () => camera.handleCameraMethodCall( + const MethodCall('unknown_method'), 1), + throwsA(isA())); + }); + + test('Should get the max zoom level', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'getMaxZoomLevel': 10.0}, + ); + + // Act + final double maxZoomLevel = await camera.getMaxZoomLevel(cameraId); + + // Assert + expect(maxZoomLevel, 10.0); + expect(channel.log, [ + isMethodCall('getMaxZoomLevel', arguments: { + 'cameraId': cameraId, + }), + ]); + }); + + test('Should get the min zoom level', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'getMinZoomLevel': 1.0}, + ); + + // Act + final double maxZoomLevel = await camera.getMinZoomLevel(cameraId); + + // Assert + expect(maxZoomLevel, 1.0); + expect(channel.log, [ + isMethodCall('getMinZoomLevel', arguments: { + 'cameraId': cameraId, + }), + ]); + }); + + test('Should set the zoom level', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'setZoomLevel': null}, + ); + + // Act + await camera.setZoomLevel(cameraId, 2.0); + + // Assert + expect(channel.log, [ + isMethodCall('setZoomLevel', + arguments: {'cameraId': cameraId, 'zoom': 2.0}), + ]); + }); + + test('Should throw CameraException when illegal zoom level is supplied', + () async { + // Arrange + MethodChannelMock( + channelName: _channelName, + methods: { + 'setZoomLevel': PlatformException( + code: 'ZOOM_ERROR', + message: 'Illegal zoom error', + details: null, + ) + }, + ); + + // Act & assert + expect( + () => camera.setZoomLevel(cameraId, -1.0), + throwsA(isA() + .having((CameraException e) => e.code, 'code', 'ZOOM_ERROR') + .having((CameraException e) => e.description, 'description', + 'Illegal zoom error'))); + }); + + test('Should lock the capture orientation', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'lockCaptureOrientation': null}, + ); + + // Act + await camera.lockCaptureOrientation( + cameraId, DeviceOrientation.portraitUp); + + // Assert + expect(channel.log, [ + isMethodCall('lockCaptureOrientation', arguments: { + 'cameraId': cameraId, + 'orientation': 'portraitUp' + }), + ]); + }); + + test('Should unlock the capture orientation', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'unlockCaptureOrientation': null}, + ); + + // Act + await camera.unlockCaptureOrientation(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('unlockCaptureOrientation', + arguments: {'cameraId': cameraId}), + ]); + }); + + test('Should pause the camera preview', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'pausePreview': null}, + ); + + // Act + await camera.pausePreview(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('pausePreview', + arguments: {'cameraId': cameraId}), + ]); + }); + + test('Should resume the camera preview', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'resumePreview': null}, + ); + + // Act + await camera.resumePreview(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('resumePreview', + arguments: {'cameraId': cameraId}), + ]); + }); + + test('Should start streaming', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: { + 'startImageStream': null, + 'stopImageStream': null, + }, + ); + + // Act + final StreamSubscription subscription = camera + .onStreamedFrameAvailable(cameraId) + .listen((CameraImageData imageData) {}); + + // Assert + expect(channel.log, [ + isMethodCall('startImageStream', arguments: null), + ]); + + subscription.cancel(); + }); + + test('Should stop streaming', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: { + 'startImageStream': null, + 'stopImageStream': null, + }, + ); + + // Act + final StreamSubscription subscription = camera + .onStreamedFrameAvailable(cameraId) + .listen((CameraImageData imageData) {}); + subscription.cancel(); + + // Assert + expect(channel.log, [ + isMethodCall('startImageStream', arguments: null), + isMethodCall('stopImageStream', arguments: null), + ]); + }); + }); +} diff --git a/packages/camera/camera_android/test/method_channel_mock.dart b/packages/camera/camera_android/test/method_channel_mock.dart new file mode 100644 index 000000000000..413c10633cc1 --- /dev/null +++ b/packages/camera/camera_android/test/method_channel_mock.dart @@ -0,0 +1,39 @@ +// 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:flutter_test/flutter_test.dart'; + +class MethodChannelMock { + MethodChannelMock({ + required String channelName, + this.delay, + required this.methods, + }) : methodChannel = MethodChannel(channelName) { + methodChannel.setMockMethodCallHandler(_handler); + } + + final Duration? delay; + final MethodChannel methodChannel; + final Map methods; + final List log = []; + + Future _handler(MethodCall methodCall) async { + log.add(methodCall); + + if (!methods.containsKey(methodCall.method)) { + throw MissingPluginException('No implementation found for method ' + '${methodCall.method} on channel ${methodChannel.name}'); + } + + return Future.delayed(delay ?? Duration.zero, () { + final dynamic result = methods[methodCall.method]; + if (result is Exception) { + throw result; + } + + return Future.value(result); + }); + } +} diff --git a/packages/camera/camera_android/test/type_conversion_test.dart b/packages/camera/camera_android/test/type_conversion_test.dart new file mode 100644 index 000000000000..b07466df791f --- /dev/null +++ b/packages/camera/camera_android/test/type_conversion_test.dart @@ -0,0 +1,60 @@ +// 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. + +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import +import 'dart:typed_data'; + +import 'package:camera_android/src/type_conversion.dart'; +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('CameraImageData can be created', () { + final CameraImageData cameraImage = + cameraImageFromPlatformData({ + 'format': 1, + 'height': 1, + 'width': 4, + 'lensAperture': 1.8, + 'sensorExposureTime': 9991324, + 'sensorSensitivity': 92.0, + 'planes': [ + { + 'bytes': Uint8List.fromList([1, 2, 3, 4]), + 'bytesPerPixel': 1, + 'bytesPerRow': 4, + 'height': 1, + 'width': 4 + } + ] + }); + expect(cameraImage.height, 1); + expect(cameraImage.width, 4); + expect(cameraImage.format.group, ImageFormatGroup.unknown); + expect(cameraImage.planes.length, 1); + }); + + test('CameraImageData has ImageFormatGroup.yuv420', () { + final CameraImageData cameraImage = + cameraImageFromPlatformData({ + 'format': 35, + 'height': 1, + 'width': 4, + 'lensAperture': 1.8, + 'sensorExposureTime': 9991324, + 'sensorSensitivity': 92.0, + 'planes': [ + { + 'bytes': Uint8List.fromList([1, 2, 3, 4]), + 'bytesPerPixel': 1, + 'bytesPerRow': 4, + 'height': 1, + 'width': 4 + } + ] + }); + expect(cameraImage.format.group, ImageFormatGroup.yuv420); + }); +} diff --git a/packages/camera/camera_android/test/utils_test.dart b/packages/camera/camera_android/test/utils_test.dart new file mode 100644 index 000000000000..6f426bc90f6f --- /dev/null +++ b/packages/camera/camera_android/test/utils_test.dart @@ -0,0 +1,60 @@ +// 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:camera_android/src/utils.dart'; +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('Utility methods', () { + test( + 'Should return CameraLensDirection when valid value is supplied when parsing camera lens direction', + () { + expect( + parseCameraLensDirection('back'), + CameraLensDirection.back, + ); + expect( + parseCameraLensDirection('front'), + CameraLensDirection.front, + ); + expect( + parseCameraLensDirection('external'), + CameraLensDirection.external, + ); + }); + + test( + 'Should throw ArgumentException when invalid value is supplied when parsing camera lens direction', + () { + expect( + () => parseCameraLensDirection('test'), + throwsA(isArgumentError), + ); + }); + + test('serializeDeviceOrientation() should serialize correctly', () { + expect(serializeDeviceOrientation(DeviceOrientation.portraitUp), + 'portraitUp'); + expect(serializeDeviceOrientation(DeviceOrientation.portraitDown), + 'portraitDown'); + expect(serializeDeviceOrientation(DeviceOrientation.landscapeRight), + 'landscapeRight'); + expect(serializeDeviceOrientation(DeviceOrientation.landscapeLeft), + 'landscapeLeft'); + }); + + test('deserializeDeviceOrientation() should deserialize correctly', () { + expect(deserializeDeviceOrientation('portraitUp'), + DeviceOrientation.portraitUp); + expect(deserializeDeviceOrientation('portraitDown'), + DeviceOrientation.portraitDown); + expect(deserializeDeviceOrientation('landscapeRight'), + DeviceOrientation.landscapeRight); + expect(deserializeDeviceOrientation('landscapeLeft'), + DeviceOrientation.landscapeLeft); + }); + }); +} diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index c57f301fae95..5bc7c8ae8d19 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.8 + +* Switches to internal method channel implementation. + ## 0.9.7+1 * Splits from `camera` as a federated implementation. diff --git a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart index 6ac7a6860dd9..51eab634c84b 100644 --- a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart +++ b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:io'; import 'dart:ui'; +import 'package:camera_avfoundation/camera_avfoundation.dart'; import 'package:camera_example/camera_controller.dart'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/painting.dart'; @@ -20,6 +21,7 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); setUpAll(() async { + CameraPlatform.instance = AVFoundationCamera(); final Directory extDir = await getTemporaryDirectory(); testDir = await Directory('${extDir.path}/test').create(recursive: true); }); diff --git a/packages/camera/camera_avfoundation/example/test_driver/integration_test.dart b/packages/camera/camera_avfoundation/example/test_driver/integration_test.dart index 4ec97e66d36c..4f10f2a522f3 100644 --- a/packages/camera/camera_avfoundation/example/test_driver/integration_test.dart +++ b/packages/camera/camera_avfoundation/example/test_driver/integration_test.dart @@ -2,63 +2,6 @@ // 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:convert'; -import 'dart:io'; +import 'package:integration_test/integration_test_driver.dart'; -import 'package:flutter_driver/flutter_driver.dart'; - -const String _examplePackage = 'io.flutter.plugins.cameraexample'; - -Future main() async { - if (!(Platform.isLinux || Platform.isMacOS)) { - print('This test must be run on a POSIX host. Skipping...'); - exit(0); - } - final bool adbExists = - Process.runSync('which', ['adb']).exitCode == 0; - if (!adbExists) { - print(r'This test needs ADB to exist on the $PATH. Skipping...'); - exit(0); - } - print('Granting camera permissions...'); - Process.runSync('adb', [ - 'shell', - 'pm', - 'grant', - _examplePackage, - 'android.permission.CAMERA' - ]); - Process.runSync('adb', [ - 'shell', - 'pm', - 'grant', - _examplePackage, - 'android.permission.RECORD_AUDIO' - ]); - print('Starting test.'); - final FlutterDriver driver = await FlutterDriver.connect(); - final String data = await driver.requestData( - null, - timeout: const Duration(minutes: 1), - ); - await driver.close(); - print('Test finished. Revoking camera permissions...'); - Process.runSync('adb', [ - 'shell', - 'pm', - 'revoke', - _examplePackage, - 'android.permission.CAMERA' - ]); - Process.runSync('adb', [ - 'shell', - 'pm', - 'revoke', - _examplePackage, - 'android.permission.RECORD_AUDIO' - ]); - - final Map result = jsonDecode(data) as Map; - exit(result['result'] == 'true' ? 0 : 1); -} +Future main() => integrationDriver(); diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m index 90327e35e187..cb19c0909158 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m @@ -26,7 +26,7 @@ @implementation CameraPlugin + (void)registerWithRegistrar:(NSObject *)registrar { FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/camera" + [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/camera_avfoundation" binaryMessenger:[registrar messenger]]; CameraPlugin *instance = [[CameraPlugin alloc] initWithRegistry:[registrar textures] messenger:[registrar messenger]]; @@ -49,9 +49,9 @@ - (instancetype)initWithRegistry:(NSObject *)registry } - (void)initDeviceEventMethodChannel { - FlutterMethodChannel *methodChannel = - [FlutterMethodChannel methodChannelWithName:@"flutter.io/cameraPlugin/device" - binaryMessenger:_messenger]; + FlutterMethodChannel *methodChannel = [FlutterMethodChannel + methodChannelWithName:@"plugins.flutter.io/camera_avfoundation/fromPlatform" + binaryMessenger:_messenger]; _deviceEventMethodChannel = [[FLTThreadSafeMethodChannel alloc] initWithMethodChannel:methodChannel]; } @@ -162,8 +162,9 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call } }; FlutterMethodChannel *methodChannel = [FlutterMethodChannel - methodChannelWithName:[NSString stringWithFormat:@"flutter.io/cameraPlugin/camera%lu", - (unsigned long)cameraId] + methodChannelWithName: + [NSString stringWithFormat:@"plugins.flutter.io/camera_avfoundation/camera%lu", + (unsigned long)cameraId] binaryMessenger:_messenger]; FLTThreadSafeMethodChannel *threadSafeMethodChannel = [[FLTThreadSafeMethodChannel alloc] initWithMethodChannel:methodChannel]; diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index 7af505b249cb..f267604af419 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -910,9 +910,9 @@ - (void)startImageStreamWithMessenger:(NSObject *)messen - (void)startImageStreamWithMessenger:(NSObject *)messenger imageStreamHandler:(FLTImageStreamHandler *)imageStreamHandler { if (!_isStreamingImages) { - FlutterEventChannel *eventChannel = - [FlutterEventChannel eventChannelWithName:@"plugins.flutter.io/camera/imageStream" - binaryMessenger:messenger]; + FlutterEventChannel *eventChannel = [FlutterEventChannel + eventChannelWithName:@"plugins.flutter.io/camera_avfoundation/imageStream" + binaryMessenger:messenger]; FLTThreadSafeEventChannel *threadSafeEventChannel = [[FLTThreadSafeEventChannel alloc] initWithEventChannel:eventChannel]; diff --git a/packages/camera/camera_avfoundation/lib/camera_avfoundation.dart b/packages/camera/camera_avfoundation/lib/camera_avfoundation.dart new file mode 100644 index 000000000000..e07a440e84f1 --- /dev/null +++ b/packages/camera/camera_avfoundation/lib/camera_avfoundation.dart @@ -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. + +export 'src/avfoundation_camera.dart'; diff --git a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart new file mode 100644 index 000000000000..1bff9011586b --- /dev/null +++ b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart @@ -0,0 +1,592 @@ +// 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:async'; +import 'dart:math'; + +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:stream_transform/stream_transform.dart'; + +import 'type_conversion.dart'; +import 'utils.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.io/camera_avfoundation'); + +/// An iOS implementation of [CameraPlatform] based on AVFoundation. +class AVFoundationCamera extends CameraPlatform { + /// Construct a new method channel camera instance. + AVFoundationCamera() { + const MethodChannel channel = + MethodChannel('plugins.flutter.io/camera_avfoundation/fromPlatform'); + channel.setMethodCallHandler( + (MethodCall call) => handleDeviceMethodCall(call)); + } + + /// Registers this class as the default instance of [CameraPlatform]. + static void registerWith() { + CameraPlatform.instance = AVFoundationCamera(); + } + + final Map _channels = {}; + + /// The controller we need to broadcast the different events coming + /// from handleMethodCall, specific to camera events. + /// + /// It is a `broadcast` because multiple controllers will connect to + /// different stream views of this Controller. + /// This is only exposed for test purposes. It shouldn't be used by clients of + /// the plugin as it may break or change at any time. + @visibleForTesting + final StreamController cameraEventStreamController = + StreamController.broadcast(); + + /// The controller we need to broadcast the different events coming + /// from handleMethodCall, specific to general device events. + /// + /// It is a `broadcast` because multiple controllers will connect to + /// different stream views of this Controller. + /// This is only exposed for test purposes. It shouldn't be used by clients of + /// the plugin as it may break or change at any time. + @visibleForTesting + final StreamController deviceEventStreamController = + StreamController.broadcast(); + + // The stream to receive frames from the native code. + StreamSubscription? _platformImageStreamSubscription; + + // The stream for vending frames to platform interface clients. + StreamController? _frameStreamController; + + Stream _cameraEvents(int cameraId) => + cameraEventStreamController.stream + .where((CameraEvent event) => event.cameraId == cameraId); + + @override + Future> availableCameras() async { + try { + final List>? cameras = await _channel + .invokeListMethod>('availableCameras'); + + if (cameras == null) { + return []; + } + + return cameras.map((Map camera) { + return CameraDescription( + name: camera['name']! as String, + lensDirection: + parseCameraLensDirection(camera['lensFacing']! as String), + sensorOrientation: camera['sensorOrientation']! as int, + ); + }).toList(); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } + + @override + Future createCamera( + CameraDescription cameraDescription, + ResolutionPreset? resolutionPreset, { + bool enableAudio = false, + }) async { + try { + final Map? reply = await _channel + .invokeMapMethod('create', { + 'cameraName': cameraDescription.name, + 'resolutionPreset': resolutionPreset != null + ? _serializeResolutionPreset(resolutionPreset) + : null, + 'enableAudio': enableAudio, + }); + + return reply!['cameraId']! as int; + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } + + @override + Future initializeCamera( + int cameraId, { + ImageFormatGroup imageFormatGroup = ImageFormatGroup.unknown, + }) { + _channels.putIfAbsent(cameraId, () { + final MethodChannel channel = MethodChannel( + 'plugins.flutter.io/camera_avfoundation/camera$cameraId'); + channel.setMethodCallHandler( + (MethodCall call) => handleCameraMethodCall(call, cameraId)); + return channel; + }); + + final Completer _completer = Completer(); + + onCameraInitialized(cameraId).first.then((CameraInitializedEvent value) { + _completer.complete(); + }); + + _channel.invokeMapMethod( + 'initialize', + { + 'cameraId': cameraId, + 'imageFormatGroup': imageFormatGroup.name(), + }, + ).catchError( + (Object error, StackTrace stackTrace) { + if (error is! PlatformException) { + throw error; + } + _completer.completeError( + CameraException(error.code, error.message), + stackTrace, + ); + }, + ); + + return _completer.future; + } + + @override + Future dispose(int cameraId) async { + if (_channels.containsKey(cameraId)) { + final MethodChannel? cameraChannel = _channels[cameraId]; + cameraChannel?.setMethodCallHandler(null); + _channels.remove(cameraId); + } + + await _channel.invokeMethod( + 'dispose', + {'cameraId': cameraId}, + ); + } + + @override + Stream onCameraInitialized(int cameraId) { + return _cameraEvents(cameraId).whereType(); + } + + @override + Stream onCameraResolutionChanged(int cameraId) { + return _cameraEvents(cameraId).whereType(); + } + + @override + Stream onCameraClosing(int cameraId) { + return _cameraEvents(cameraId).whereType(); + } + + @override + Stream onCameraError(int cameraId) { + return _cameraEvents(cameraId).whereType(); + } + + @override + Stream onVideoRecordedEvent(int cameraId) { + return _cameraEvents(cameraId).whereType(); + } + + @override + Stream onDeviceOrientationChanged() { + return deviceEventStreamController.stream + .whereType(); + } + + @override + Future lockCaptureOrientation( + int cameraId, + DeviceOrientation orientation, + ) async { + await _channel.invokeMethod( + 'lockCaptureOrientation', + { + 'cameraId': cameraId, + 'orientation': serializeDeviceOrientation(orientation) + }, + ); + } + + @override + Future unlockCaptureOrientation(int cameraId) async { + await _channel.invokeMethod( + 'unlockCaptureOrientation', + {'cameraId': cameraId}, + ); + } + + @override + Future takePicture(int cameraId) async { + final String? path = await _channel.invokeMethod( + 'takePicture', + {'cameraId': cameraId}, + ); + + if (path == null) { + throw CameraException( + 'INVALID_PATH', + 'The platform "$defaultTargetPlatform" did not return a path while reporting success. The platform should always return a valid path or report an error.', + ); + } + + return XFile(path); + } + + @override + Future prepareForVideoRecording() => + _channel.invokeMethod('prepareForVideoRecording'); + + @override + Future startVideoRecording(int cameraId, + {Duration? maxVideoDuration}) async { + await _channel.invokeMethod( + 'startVideoRecording', + { + 'cameraId': cameraId, + 'maxVideoDuration': maxVideoDuration?.inMilliseconds, + }, + ); + } + + @override + Future stopVideoRecording(int cameraId) async { + final String? path = await _channel.invokeMethod( + 'stopVideoRecording', + {'cameraId': cameraId}, + ); + + if (path == null) { + throw CameraException( + 'INVALID_PATH', + 'The platform "$defaultTargetPlatform" did not return a path while reporting success. The platform should always return a valid path or report an error.', + ); + } + + return XFile(path); + } + + @override + Future pauseVideoRecording(int cameraId) => _channel.invokeMethod( + 'pauseVideoRecording', + {'cameraId': cameraId}, + ); + + @override + Future resumeVideoRecording(int cameraId) => + _channel.invokeMethod( + 'resumeVideoRecording', + {'cameraId': cameraId}, + ); + + @override + Stream onStreamedFrameAvailable(int cameraId, + {CameraImageStreamOptions? options}) { + _frameStreamController = StreamController( + onListen: _onFrameStreamListen, + onPause: _onFrameStreamPauseResume, + onResume: _onFrameStreamPauseResume, + onCancel: _onFrameStreamCancel, + ); + return _frameStreamController!.stream; + } + + void _onFrameStreamListen() { + _startPlatformStream(); + } + + Future _startPlatformStream() async { + await _channel.invokeMethod('startImageStream'); + const EventChannel cameraEventChannel = + EventChannel('plugins.flutter.io/camera_avfoundation/imageStream'); + _platformImageStreamSubscription = + cameraEventChannel.receiveBroadcastStream().listen((dynamic imageData) { + try { + _channel.invokeMethod('receivedImageStreamData'); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + _frameStreamController! + .add(cameraImageFromPlatformData(imageData as Map)); + }); + } + + FutureOr _onFrameStreamCancel() async { + await _channel.invokeMethod('stopImageStream'); + await _platformImageStreamSubscription?.cancel(); + _platformImageStreamSubscription = null; + _frameStreamController = null; + } + + void _onFrameStreamPauseResume() { + throw CameraException('InvalidCall', + 'Pause and resume are not supported for onStreamedFrameAvailable'); + } + + @override + Future setFlashMode(int cameraId, FlashMode mode) => + _channel.invokeMethod( + 'setFlashMode', + { + 'cameraId': cameraId, + 'mode': _serializeFlashMode(mode), + }, + ); + + @override + Future setExposureMode(int cameraId, ExposureMode mode) => + _channel.invokeMethod( + 'setExposureMode', + { + 'cameraId': cameraId, + 'mode': serializeExposureMode(mode), + }, + ); + + @override + Future setExposurePoint(int cameraId, Point? point) { + assert(point == null || point.x >= 0 && point.x <= 1); + assert(point == null || point.y >= 0 && point.y <= 1); + + return _channel.invokeMethod( + 'setExposurePoint', + { + 'cameraId': cameraId, + 'reset': point == null, + 'x': point?.x, + 'y': point?.y, + }, + ); + } + + @override + Future getMinExposureOffset(int cameraId) async { + final double? minExposureOffset = await _channel.invokeMethod( + 'getMinExposureOffset', + {'cameraId': cameraId}, + ); + + return minExposureOffset!; + } + + @override + Future getMaxExposureOffset(int cameraId) async { + final double? maxExposureOffset = await _channel.invokeMethod( + 'getMaxExposureOffset', + {'cameraId': cameraId}, + ); + + return maxExposureOffset!; + } + + @override + Future getExposureOffsetStepSize(int cameraId) async { + final double? stepSize = await _channel.invokeMethod( + 'getExposureOffsetStepSize', + {'cameraId': cameraId}, + ); + + return stepSize!; + } + + @override + Future setExposureOffset(int cameraId, double offset) async { + final double? appliedOffset = await _channel.invokeMethod( + 'setExposureOffset', + { + 'cameraId': cameraId, + 'offset': offset, + }, + ); + + return appliedOffset!; + } + + @override + Future setFocusMode(int cameraId, FocusMode mode) => + _channel.invokeMethod( + 'setFocusMode', + { + 'cameraId': cameraId, + 'mode': serializeFocusMode(mode), + }, + ); + + @override + Future setFocusPoint(int cameraId, Point? point) { + assert(point == null || point.x >= 0 && point.x <= 1); + assert(point == null || point.y >= 0 && point.y <= 1); + + return _channel.invokeMethod( + 'setFocusPoint', + { + 'cameraId': cameraId, + 'reset': point == null, + 'x': point?.x, + 'y': point?.y, + }, + ); + } + + @override + Future getMaxZoomLevel(int cameraId) async { + final double? maxZoomLevel = await _channel.invokeMethod( + 'getMaxZoomLevel', + {'cameraId': cameraId}, + ); + + return maxZoomLevel!; + } + + @override + Future getMinZoomLevel(int cameraId) async { + final double? minZoomLevel = await _channel.invokeMethod( + 'getMinZoomLevel', + {'cameraId': cameraId}, + ); + + return minZoomLevel!; + } + + @override + Future setZoomLevel(int cameraId, double zoom) async { + try { + await _channel.invokeMethod( + 'setZoomLevel', + { + 'cameraId': cameraId, + 'zoom': zoom, + }, + ); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } + + @override + Future pausePreview(int cameraId) async { + await _channel.invokeMethod( + 'pausePreview', + {'cameraId': cameraId}, + ); + } + + @override + Future resumePreview(int cameraId) async { + await _channel.invokeMethod( + 'resumePreview', + {'cameraId': cameraId}, + ); + } + + @override + Widget buildPreview(int cameraId) { + return Texture(textureId: cameraId); + } + + /// Returns the flash mode as a String. + String _serializeFlashMode(FlashMode flashMode) { + switch (flashMode) { + case FlashMode.off: + return 'off'; + case FlashMode.auto: + return 'auto'; + case FlashMode.always: + return 'always'; + case FlashMode.torch: + return 'torch'; + default: + throw ArgumentError('Unknown FlashMode value'); + } + } + + /// Returns the resolution preset as a String. + String _serializeResolutionPreset(ResolutionPreset resolutionPreset) { + switch (resolutionPreset) { + case ResolutionPreset.max: + return 'max'; + case ResolutionPreset.ultraHigh: + return 'ultraHigh'; + case ResolutionPreset.veryHigh: + return 'veryHigh'; + case ResolutionPreset.high: + return 'high'; + case ResolutionPreset.medium: + return 'medium'; + case ResolutionPreset.low: + return 'low'; + default: + throw ArgumentError('Unknown ResolutionPreset value'); + } + } + + /// Converts messages received from the native platform into device events. + /// + /// This is only exposed for test purposes. It shouldn't be used by clients of + /// the plugin as it may break or change at any time. + @visibleForTesting + Future handleDeviceMethodCall(MethodCall call) async { + switch (call.method) { + case 'orientation_changed': + deviceEventStreamController.add(DeviceOrientationChangedEvent( + deserializeDeviceOrientation( + call.arguments['orientation']! as String))); + break; + default: + throw MissingPluginException(); + } + } + + /// Converts messages received from the native platform into camera events. + /// + /// This is only exposed for test purposes. It shouldn't be used by clients of + /// the plugin as it may break or change at any time. + @visibleForTesting + Future handleCameraMethodCall(MethodCall call, int cameraId) async { + switch (call.method) { + case 'initialized': + cameraEventStreamController.add(CameraInitializedEvent( + cameraId, + call.arguments['previewWidth']! as double, + call.arguments['previewHeight']! as double, + deserializeExposureMode(call.arguments['exposureMode']! as String), + call.arguments['exposurePointSupported']! as bool, + deserializeFocusMode(call.arguments['focusMode']! as String), + call.arguments['focusPointSupported']! as bool, + )); + break; + case 'resolution_changed': + cameraEventStreamController.add(CameraResolutionChangedEvent( + cameraId, + call.arguments['captureWidth']! as double, + call.arguments['captureHeight']! as double, + )); + break; + case 'camera_closing': + cameraEventStreamController.add(CameraClosingEvent( + cameraId, + )); + break; + case 'video_recorded': + cameraEventStreamController.add(VideoRecordedEvent( + cameraId, + XFile(call.arguments['path']! as String), + call.arguments['maxVideoDuration'] != null + ? Duration( + milliseconds: call.arguments['maxVideoDuration']! as int) + : null, + )); + break; + case 'error': + cameraEventStreamController.add(CameraErrorEvent( + cameraId, + call.arguments['description']! as String, + )); + break; + default: + throw MissingPluginException(); + } + } +} diff --git a/packages/camera/camera_avfoundation/lib/src/type_conversion.dart b/packages/camera/camera_avfoundation/lib/src/type_conversion.dart new file mode 100644 index 000000000000..c2a539a63dab --- /dev/null +++ b/packages/camera/camera_avfoundation/lib/src/type_conversion.dart @@ -0,0 +1,50 @@ +// 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. + +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import +import 'dart:typed_data'; + +import 'package:camera_platform_interface/camera_platform_interface.dart'; + +/// Converts method channel call [data] for `receivedImageStreamData` to a +/// [CameraImageData]. +CameraImageData cameraImageFromPlatformData(Map data) { + return CameraImageData( + format: _cameraImageFormatFromPlatformData(data['format']), + height: data['height'] as int, + width: data['width'] as int, + lensAperture: data['lensAperture'] as double?, + sensorExposureTime: data['sensorExposureTime'] as int?, + sensorSensitivity: data['sensorSensitivity'] as double?, + planes: List.unmodifiable( + (data['planes'] as List).map( + (dynamic planeData) => _cameraImagePlaneFromPlatformData( + planeData as Map)))); +} + +CameraImageFormat _cameraImageFormatFromPlatformData(dynamic data) { + return CameraImageFormat(_imageFormatGroupFromPlatformData(data), raw: data); +} + +ImageFormatGroup _imageFormatGroupFromPlatformData(dynamic data) { + switch (data) { + case 875704438: // kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange + return ImageFormatGroup.yuv420; + + case 1111970369: // kCVPixelFormatType_32BGRA + return ImageFormatGroup.bgra8888; + } + + return ImageFormatGroup.unknown; +} + +CameraImagePlane _cameraImagePlaneFromPlatformData(Map data) { + return CameraImagePlane( + bytes: data['bytes'] as Uint8List, + bytesPerPixel: data['bytesPerPixel'] as int?, + bytesPerRow: data['bytesPerRow'] as int, + height: data['height'] as int?, + width: data['width'] as int?); +} diff --git a/packages/camera/camera_avfoundation/lib/src/utils.dart b/packages/camera/camera_avfoundation/lib/src/utils.dart new file mode 100644 index 000000000000..663ec6da7a97 --- /dev/null +++ b/packages/camera/camera_avfoundation/lib/src/utils.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:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/services.dart'; + +/// Parses a string into a corresponding CameraLensDirection. +CameraLensDirection parseCameraLensDirection(String string) { + switch (string) { + case 'front': + return CameraLensDirection.front; + case 'back': + return CameraLensDirection.back; + case 'external': + return CameraLensDirection.external; + } + throw ArgumentError('Unknown CameraLensDirection value'); +} + +/// Returns the device orientation as a String. +String serializeDeviceOrientation(DeviceOrientation orientation) { + switch (orientation) { + case DeviceOrientation.portraitUp: + return 'portraitUp'; + case DeviceOrientation.portraitDown: + return 'portraitDown'; + case DeviceOrientation.landscapeRight: + return 'landscapeRight'; + case DeviceOrientation.landscapeLeft: + return 'landscapeLeft'; + default: + throw ArgumentError('Unknown DeviceOrientation value'); + } +} + +/// Returns the device orientation for a given String. +DeviceOrientation deserializeDeviceOrientation(String str) { + switch (str) { + case 'portraitUp': + return DeviceOrientation.portraitUp; + case 'portraitDown': + return DeviceOrientation.portraitDown; + case 'landscapeRight': + return DeviceOrientation.landscapeRight; + case 'landscapeLeft': + return DeviceOrientation.landscapeLeft; + default: + throw ArgumentError('"$str" is not a valid DeviceOrientation value'); + } +} diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index 237f5ad329c5..231ce26227b8 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_avfoundation description: iOS implementation of the camera plugin. repository: https://github.com/flutter/plugins/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.7+1 +version: 0.9.8 environment: sdk: ">=2.14.0 <3.0.0" @@ -14,13 +14,16 @@ flutter: platforms: ios: pluginClass: CameraPlugin + dartPluginClass: AVFoundationCamera dependencies: camera_platform_interface: ^2.2.0 flutter: sdk: flutter + stream_transform: ^2.0.0 dev_dependencies: + async: ^2.5.0 flutter_driver: sdk: flutter flutter_test: diff --git a/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart new file mode 100644 index 000000000000..4b32d2e50b4a --- /dev/null +++ b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart @@ -0,0 +1,1075 @@ +// 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:async'; +import 'dart:math'; + +import 'package:async/async.dart'; +import 'package:camera_avfoundation/src/avfoundation_camera.dart'; +import 'package:camera_avfoundation/src/utils.dart'; +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'method_channel_mock.dart'; + +const String _channelName = 'plugins.flutter.io/camera_avfoundation'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + test('registers instance', () async { + AVFoundationCamera.registerWith(); + expect(CameraPlatform.instance, isA()); + }); + + group('Creation, Initialization & Disposal Tests', () { + test('Should send creation data and receive back a camera id', () async { + // Arrange + final MethodChannelMock cameraMockChannel = MethodChannelMock( + channelName: _channelName, + methods: { + 'create': { + 'cameraId': 1, + 'imageFormatGroup': 'unknown', + } + }); + final AVFoundationCamera camera = AVFoundationCamera(); + + // Act + final int cameraId = await camera.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0), + ResolutionPreset.high, + ); + + // Assert + expect(cameraMockChannel.log, [ + isMethodCall( + 'create', + arguments: { + 'cameraName': 'Test', + 'resolutionPreset': 'high', + 'enableAudio': false + }, + ), + ]); + expect(cameraId, 1); + }); + + test('Should throw CameraException when create throws a PlatformException', + () { + // Arrange + MethodChannelMock(channelName: _channelName, methods: { + 'create': PlatformException( + code: 'TESTING_ERROR_CODE', + message: 'Mock error message used during testing.', + ) + }); + final AVFoundationCamera camera = AVFoundationCamera(); + + // Act + expect( + () => camera.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0, + ), + ResolutionPreset.high, + ), + throwsA( + isA() + .having( + (CameraException e) => e.code, 'code', 'TESTING_ERROR_CODE') + .having((CameraException e) => e.description, 'description', + 'Mock error message used during testing.'), + ), + ); + }); + + test('Should throw CameraException when create throws a PlatformException', + () { + // Arrange + MethodChannelMock(channelName: _channelName, methods: { + 'create': PlatformException( + code: 'TESTING_ERROR_CODE', + message: 'Mock error message used during testing.', + ) + }); + final AVFoundationCamera camera = AVFoundationCamera(); + + // Act + expect( + () => camera.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0, + ), + ResolutionPreset.high, + ), + throwsA( + isA() + .having( + (CameraException e) => e.code, 'code', 'TESTING_ERROR_CODE') + .having((CameraException e) => e.description, 'description', + 'Mock error message used during testing.'), + ), + ); + }); + + test( + 'Should throw CameraException when initialize throws a PlatformException', + () { + // Arrange + MethodChannelMock( + channelName: _channelName, + methods: { + 'initialize': PlatformException( + code: 'TESTING_ERROR_CODE', + message: 'Mock error message used during testing.', + ) + }, + ); + final AVFoundationCamera camera = AVFoundationCamera(); + + // Act + expect( + () => camera.initializeCamera(0), + throwsA( + isA() + .having( + (CameraException e) => e.code, 'code', 'TESTING_ERROR_CODE') + .having( + (CameraException e) => e.description, + 'description', + 'Mock error message used during testing.', + ), + ), + ); + }, + ); + + test('Should send initialization data', () async { + // Arrange + final MethodChannelMock cameraMockChannel = MethodChannelMock( + channelName: _channelName, + methods: { + 'create': { + 'cameraId': 1, + 'imageFormatGroup': 'unknown', + }, + 'initialize': null + }); + final AVFoundationCamera camera = AVFoundationCamera(); + final int cameraId = await camera.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0, + ), + ResolutionPreset.high, + ); + + // Act + final Future initializeFuture = camera.initializeCamera(cameraId); + camera.cameraEventStreamController.add(CameraInitializedEvent( + cameraId, + 1920, + 1080, + ExposureMode.auto, + true, + FocusMode.auto, + true, + )); + await initializeFuture; + + // Assert + expect(cameraId, 1); + expect(cameraMockChannel.log, [ + anything, + isMethodCall( + 'initialize', + arguments: { + 'cameraId': 1, + 'imageFormatGroup': 'unknown', + }, + ), + ]); + }); + + test('Should send a disposal call on dispose', () async { + // Arrange + final MethodChannelMock cameraMockChannel = MethodChannelMock( + channelName: _channelName, + methods: { + 'create': {'cameraId': 1}, + 'initialize': null, + 'dispose': {'cameraId': 1} + }); + + final AVFoundationCamera camera = AVFoundationCamera(); + final int cameraId = await camera.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0, + ), + ResolutionPreset.high, + ); + final Future initializeFuture = camera.initializeCamera(cameraId); + camera.cameraEventStreamController.add(CameraInitializedEvent( + cameraId, + 1920, + 1080, + ExposureMode.auto, + true, + FocusMode.auto, + true, + )); + await initializeFuture; + + // Act + await camera.dispose(cameraId); + + // Assert + expect(cameraId, 1); + expect(cameraMockChannel.log, [ + anything, + anything, + isMethodCall( + 'dispose', + arguments: {'cameraId': 1}, + ), + ]); + }); + }); + + group('Event Tests', () { + late AVFoundationCamera camera; + late int cameraId; + setUp(() async { + MethodChannelMock( + channelName: _channelName, + methods: { + 'create': {'cameraId': 1}, + 'initialize': null + }, + ); + camera = AVFoundationCamera(); + cameraId = await camera.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0, + ), + ResolutionPreset.high, + ); + final Future initializeFuture = camera.initializeCamera(cameraId); + camera.cameraEventStreamController.add(CameraInitializedEvent( + cameraId, + 1920, + 1080, + ExposureMode.auto, + true, + FocusMode.auto, + true, + )); + await initializeFuture; + }); + + test('Should receive initialized event', () async { + // Act + final Stream eventStream = + camera.onCameraInitialized(cameraId); + final StreamQueue streamQueue = + StreamQueue(eventStream); + + // Emit test events + final CameraInitializedEvent event = CameraInitializedEvent( + cameraId, + 3840, + 2160, + ExposureMode.auto, + true, + FocusMode.auto, + true, + ); + await camera.handleCameraMethodCall( + MethodCall('initialized', event.toJson()), cameraId); + + // Assert + expect(await streamQueue.next, event); + + // Clean up + await streamQueue.cancel(); + }); + + test('Should receive resolution changes', () async { + // Act + final Stream resolutionStream = + camera.onCameraResolutionChanged(cameraId); + final StreamQueue streamQueue = + StreamQueue(resolutionStream); + + // Emit test events + final CameraResolutionChangedEvent fhdEvent = + CameraResolutionChangedEvent(cameraId, 1920, 1080); + final CameraResolutionChangedEvent uhdEvent = + CameraResolutionChangedEvent(cameraId, 3840, 2160); + await camera.handleCameraMethodCall( + MethodCall('resolution_changed', fhdEvent.toJson()), cameraId); + await camera.handleCameraMethodCall( + MethodCall('resolution_changed', uhdEvent.toJson()), cameraId); + await camera.handleCameraMethodCall( + MethodCall('resolution_changed', fhdEvent.toJson()), cameraId); + await camera.handleCameraMethodCall( + MethodCall('resolution_changed', uhdEvent.toJson()), cameraId); + + // Assert + expect(await streamQueue.next, fhdEvent); + expect(await streamQueue.next, uhdEvent); + expect(await streamQueue.next, fhdEvent); + expect(await streamQueue.next, uhdEvent); + + // Clean up + await streamQueue.cancel(); + }); + + test('Should receive camera closing events', () async { + // Act + final Stream eventStream = + camera.onCameraClosing(cameraId); + final StreamQueue streamQueue = + StreamQueue(eventStream); + + // Emit test events + final CameraClosingEvent event = CameraClosingEvent(cameraId); + await camera.handleCameraMethodCall( + MethodCall('camera_closing', event.toJson()), cameraId); + await camera.handleCameraMethodCall( + MethodCall('camera_closing', event.toJson()), cameraId); + await camera.handleCameraMethodCall( + MethodCall('camera_closing', event.toJson()), cameraId); + + // Assert + expect(await streamQueue.next, event); + expect(await streamQueue.next, event); + expect(await streamQueue.next, event); + + // Clean up + await streamQueue.cancel(); + }); + + test('Should receive camera error events', () async { + // Act + final Stream errorStream = + camera.onCameraError(cameraId); + final StreamQueue streamQueue = + StreamQueue(errorStream); + + // Emit test events + final CameraErrorEvent event = + CameraErrorEvent(cameraId, 'Error Description'); + await camera.handleCameraMethodCall( + MethodCall('error', event.toJson()), cameraId); + await camera.handleCameraMethodCall( + MethodCall('error', event.toJson()), cameraId); + await camera.handleCameraMethodCall( + MethodCall('error', event.toJson()), cameraId); + + // Assert + expect(await streamQueue.next, event); + expect(await streamQueue.next, event); + expect(await streamQueue.next, event); + + // Clean up + await streamQueue.cancel(); + }); + + test('Should receive device orientation change events', () async { + // Act + final Stream eventStream = + camera.onDeviceOrientationChanged(); + final StreamQueue streamQueue = + StreamQueue(eventStream); + + // Emit test events + const DeviceOrientationChangedEvent event = + DeviceOrientationChangedEvent(DeviceOrientation.portraitUp); + await camera.handleDeviceMethodCall( + MethodCall('orientation_changed', event.toJson())); + await camera.handleDeviceMethodCall( + MethodCall('orientation_changed', event.toJson())); + await camera.handleDeviceMethodCall( + MethodCall('orientation_changed', event.toJson())); + + // Assert + expect(await streamQueue.next, event); + expect(await streamQueue.next, event); + expect(await streamQueue.next, event); + + // Clean up + await streamQueue.cancel(); + }); + }); + + group('Function Tests', () { + late AVFoundationCamera camera; + late int cameraId; + + setUp(() async { + MethodChannelMock( + channelName: _channelName, + methods: { + 'create': {'cameraId': 1}, + 'initialize': null + }, + ); + camera = AVFoundationCamera(); + cameraId = await camera.createCamera( + const CameraDescription( + name: 'Test', + lensDirection: CameraLensDirection.back, + sensorOrientation: 0, + ), + ResolutionPreset.high, + ); + final Future initializeFuture = camera.initializeCamera(cameraId); + camera.cameraEventStreamController.add( + CameraInitializedEvent( + cameraId, + 1920, + 1080, + ExposureMode.auto, + true, + FocusMode.auto, + true, + ), + ); + await initializeFuture; + }); + + test('Should fetch CameraDescription instances for available cameras', + () async { + // Arrange + final List returnData = [ + { + 'name': 'Test 1', + 'lensFacing': 'front', + 'sensorOrientation': 1 + }, + { + 'name': 'Test 2', + 'lensFacing': 'back', + 'sensorOrientation': 2 + } + ]; + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'availableCameras': returnData}, + ); + + // Act + final List cameras = await camera.availableCameras(); + + // Assert + expect(channel.log, [ + isMethodCall('availableCameras', arguments: null), + ]); + expect(cameras.length, returnData.length); + for (int i = 0; i < returnData.length; i++) { + final CameraDescription cameraDescription = CameraDescription( + name: returnData[i]['name']! as String, + lensDirection: + parseCameraLensDirection(returnData[i]['lensFacing']! as String), + sensorOrientation: returnData[i]['sensorOrientation']! as int, + ); + expect(cameras[i], cameraDescription); + } + }); + + test( + 'Should throw CameraException when availableCameras throws a PlatformException', + () { + // Arrange + MethodChannelMock(channelName: _channelName, methods: { + 'availableCameras': PlatformException( + code: 'TESTING_ERROR_CODE', + message: 'Mock error message used during testing.', + ) + }); + + // Act + expect( + camera.availableCameras, + throwsA( + isA() + .having( + (CameraException e) => e.code, 'code', 'TESTING_ERROR_CODE') + .having((CameraException e) => e.description, 'description', + 'Mock error message used during testing.'), + ), + ); + }); + + test('Should take a picture and return an XFile instance', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'takePicture': '/test/path.jpg'}); + + // Act + final XFile file = await camera.takePicture(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('takePicture', arguments: { + 'cameraId': cameraId, + }), + ]); + expect(file.path, '/test/path.jpg'); + }); + + test('Should prepare for video recording', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'prepareForVideoRecording': null}, + ); + + // Act + await camera.prepareForVideoRecording(); + + // Assert + expect(channel.log, [ + isMethodCall('prepareForVideoRecording', arguments: null), + ]); + }); + + test('Should start recording a video', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'startVideoRecording': null}, + ); + + // Act + await camera.startVideoRecording(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('startVideoRecording', arguments: { + 'cameraId': cameraId, + 'maxVideoDuration': null, + }), + ]); + }); + + test('Should pass maxVideoDuration when starting recording a video', + () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'startVideoRecording': null}, + ); + + // Act + await camera.startVideoRecording( + cameraId, + maxVideoDuration: const Duration(seconds: 10), + ); + + // Assert + expect(channel.log, [ + isMethodCall('startVideoRecording', arguments: { + 'cameraId': cameraId, + 'maxVideoDuration': 10000 + }), + ]); + }); + + test('Should stop a video recording and return the file', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'stopVideoRecording': '/test/path.mp4'}, + ); + + // Act + final XFile file = await camera.stopVideoRecording(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('stopVideoRecording', arguments: { + 'cameraId': cameraId, + }), + ]); + expect(file.path, '/test/path.mp4'); + }); + + test('Should pause a video recording', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'pauseVideoRecording': null}, + ); + + // Act + await camera.pauseVideoRecording(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('pauseVideoRecording', arguments: { + 'cameraId': cameraId, + }), + ]); + }); + + test('Should resume a video recording', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'resumeVideoRecording': null}, + ); + + // Act + await camera.resumeVideoRecording(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('resumeVideoRecording', arguments: { + 'cameraId': cameraId, + }), + ]); + }); + + test('Should set the flash mode', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'setFlashMode': null}, + ); + + // Act + await camera.setFlashMode(cameraId, FlashMode.torch); + await camera.setFlashMode(cameraId, FlashMode.always); + await camera.setFlashMode(cameraId, FlashMode.auto); + await camera.setFlashMode(cameraId, FlashMode.off); + + // Assert + expect(channel.log, [ + isMethodCall('setFlashMode', arguments: { + 'cameraId': cameraId, + 'mode': 'torch' + }), + isMethodCall('setFlashMode', arguments: { + 'cameraId': cameraId, + 'mode': 'always' + }), + isMethodCall('setFlashMode', + arguments: {'cameraId': cameraId, 'mode': 'auto'}), + isMethodCall('setFlashMode', + arguments: {'cameraId': cameraId, 'mode': 'off'}), + ]); + }); + + test('Should set the exposure mode', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'setExposureMode': null}, + ); + + // Act + await camera.setExposureMode(cameraId, ExposureMode.auto); + await camera.setExposureMode(cameraId, ExposureMode.locked); + + // Assert + expect(channel.log, [ + isMethodCall('setExposureMode', + arguments: {'cameraId': cameraId, 'mode': 'auto'}), + isMethodCall('setExposureMode', arguments: { + 'cameraId': cameraId, + 'mode': 'locked' + }), + ]); + }); + + test('Should set the exposure point', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'setExposurePoint': null}, + ); + + // Act + await camera.setExposurePoint(cameraId, const Point(0.5, 0.5)); + await camera.setExposurePoint(cameraId, null); + + // Assert + expect(channel.log, [ + isMethodCall('setExposurePoint', arguments: { + 'cameraId': cameraId, + 'x': 0.5, + 'y': 0.5, + 'reset': false + }), + isMethodCall('setExposurePoint', arguments: { + 'cameraId': cameraId, + 'x': null, + 'y': null, + 'reset': true + }), + ]); + }); + + test('Should get the min exposure offset', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'getMinExposureOffset': 2.0}, + ); + + // Act + final double minExposureOffset = + await camera.getMinExposureOffset(cameraId); + + // Assert + expect(minExposureOffset, 2.0); + expect(channel.log, [ + isMethodCall('getMinExposureOffset', arguments: { + 'cameraId': cameraId, + }), + ]); + }); + + test('Should get the max exposure offset', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'getMaxExposureOffset': 2.0}, + ); + + // Act + final double maxExposureOffset = + await camera.getMaxExposureOffset(cameraId); + + // Assert + expect(maxExposureOffset, 2.0); + expect(channel.log, [ + isMethodCall('getMaxExposureOffset', arguments: { + 'cameraId': cameraId, + }), + ]); + }); + + test('Should get the exposure offset step size', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'getExposureOffsetStepSize': 0.25}, + ); + + // Act + final double stepSize = await camera.getExposureOffsetStepSize(cameraId); + + // Assert + expect(stepSize, 0.25); + expect(channel.log, [ + isMethodCall('getExposureOffsetStepSize', arguments: { + 'cameraId': cameraId, + }), + ]); + }); + + test('Should set the exposure offset', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'setExposureOffset': 0.6}, + ); + + // Act + final double actualOffset = await camera.setExposureOffset(cameraId, 0.5); + + // Assert + expect(actualOffset, 0.6); + expect(channel.log, [ + isMethodCall('setExposureOffset', arguments: { + 'cameraId': cameraId, + 'offset': 0.5, + }), + ]); + }); + + test('Should set the focus mode', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'setFocusMode': null}, + ); + + // Act + await camera.setFocusMode(cameraId, FocusMode.auto); + await camera.setFocusMode(cameraId, FocusMode.locked); + + // Assert + expect(channel.log, [ + isMethodCall('setFocusMode', + arguments: {'cameraId': cameraId, 'mode': 'auto'}), + isMethodCall('setFocusMode', arguments: { + 'cameraId': cameraId, + 'mode': 'locked' + }), + ]); + }); + + test('Should set the exposure point', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'setFocusPoint': null}, + ); + + // Act + await camera.setFocusPoint(cameraId, const Point(0.5, 0.5)); + await camera.setFocusPoint(cameraId, null); + + // Assert + expect(channel.log, [ + isMethodCall('setFocusPoint', arguments: { + 'cameraId': cameraId, + 'x': 0.5, + 'y': 0.5, + 'reset': false + }), + isMethodCall('setFocusPoint', arguments: { + 'cameraId': cameraId, + 'x': null, + 'y': null, + 'reset': true + }), + ]); + }); + + test('Should build a texture widget as preview widget', () async { + // Act + final Widget widget = camera.buildPreview(cameraId); + + // Act + expect(widget is Texture, isTrue); + expect((widget as Texture).textureId, cameraId); + }); + + test('Should throw MissingPluginException when handling unknown method', + () { + final AVFoundationCamera camera = AVFoundationCamera(); + + expect( + () => camera.handleCameraMethodCall( + const MethodCall('unknown_method'), 1), + throwsA(isA())); + }); + + test('Should get the max zoom level', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'getMaxZoomLevel': 10.0}, + ); + + // Act + final double maxZoomLevel = await camera.getMaxZoomLevel(cameraId); + + // Assert + expect(maxZoomLevel, 10.0); + expect(channel.log, [ + isMethodCall('getMaxZoomLevel', arguments: { + 'cameraId': cameraId, + }), + ]); + }); + + test('Should get the min zoom level', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'getMinZoomLevel': 1.0}, + ); + + // Act + final double maxZoomLevel = await camera.getMinZoomLevel(cameraId); + + // Assert + expect(maxZoomLevel, 1.0); + expect(channel.log, [ + isMethodCall('getMinZoomLevel', arguments: { + 'cameraId': cameraId, + }), + ]); + }); + + test('Should set the zoom level', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'setZoomLevel': null}, + ); + + // Act + await camera.setZoomLevel(cameraId, 2.0); + + // Assert + expect(channel.log, [ + isMethodCall('setZoomLevel', + arguments: {'cameraId': cameraId, 'zoom': 2.0}), + ]); + }); + + test('Should throw CameraException when illegal zoom level is supplied', + () async { + // Arrange + MethodChannelMock( + channelName: _channelName, + methods: { + 'setZoomLevel': PlatformException( + code: 'ZOOM_ERROR', + message: 'Illegal zoom error', + details: null, + ) + }, + ); + + // Act & assert + expect( + () => camera.setZoomLevel(cameraId, -1.0), + throwsA(isA() + .having((CameraException e) => e.code, 'code', 'ZOOM_ERROR') + .having((CameraException e) => e.description, 'description', + 'Illegal zoom error'))); + }); + + test('Should lock the capture orientation', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'lockCaptureOrientation': null}, + ); + + // Act + await camera.lockCaptureOrientation( + cameraId, DeviceOrientation.portraitUp); + + // Assert + expect(channel.log, [ + isMethodCall('lockCaptureOrientation', arguments: { + 'cameraId': cameraId, + 'orientation': 'portraitUp' + }), + ]); + }); + + test('Should unlock the capture orientation', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'unlockCaptureOrientation': null}, + ); + + // Act + await camera.unlockCaptureOrientation(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('unlockCaptureOrientation', + arguments: {'cameraId': cameraId}), + ]); + }); + + test('Should pause the camera preview', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'pausePreview': null}, + ); + + // Act + await camera.pausePreview(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('pausePreview', + arguments: {'cameraId': cameraId}), + ]); + }); + + test('Should resume the camera preview', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: {'resumePreview': null}, + ); + + // Act + await camera.resumePreview(cameraId); + + // Assert + expect(channel.log, [ + isMethodCall('resumePreview', + arguments: {'cameraId': cameraId}), + ]); + }); + + test('Should start streaming', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: { + 'startImageStream': null, + 'stopImageStream': null, + }, + ); + + // Act + final StreamSubscription subscription = camera + .onStreamedFrameAvailable(cameraId) + .listen((CameraImageData imageData) {}); + + // Assert + expect(channel.log, [ + isMethodCall('startImageStream', arguments: null), + ]); + + subscription.cancel(); + }); + + test('Should stop streaming', () async { + // Arrange + final MethodChannelMock channel = MethodChannelMock( + channelName: _channelName, + methods: { + 'startImageStream': null, + 'stopImageStream': null, + }, + ); + + // Act + final StreamSubscription subscription = camera + .onStreamedFrameAvailable(cameraId) + .listen((CameraImageData imageData) {}); + subscription.cancel(); + + // Assert + expect(channel.log, [ + isMethodCall('startImageStream', arguments: null), + isMethodCall('stopImageStream', arguments: null), + ]); + }); + }); +} diff --git a/packages/camera/camera_avfoundation/test/method_channel_mock.dart b/packages/camera/camera_avfoundation/test/method_channel_mock.dart new file mode 100644 index 000000000000..413c10633cc1 --- /dev/null +++ b/packages/camera/camera_avfoundation/test/method_channel_mock.dart @@ -0,0 +1,39 @@ +// 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:flutter_test/flutter_test.dart'; + +class MethodChannelMock { + MethodChannelMock({ + required String channelName, + this.delay, + required this.methods, + }) : methodChannel = MethodChannel(channelName) { + methodChannel.setMockMethodCallHandler(_handler); + } + + final Duration? delay; + final MethodChannel methodChannel; + final Map methods; + final List log = []; + + Future _handler(MethodCall methodCall) async { + log.add(methodCall); + + if (!methods.containsKey(methodCall.method)) { + throw MissingPluginException('No implementation found for method ' + '${methodCall.method} on channel ${methodChannel.name}'); + } + + return Future.delayed(delay ?? Duration.zero, () { + final dynamic result = methods[methodCall.method]; + if (result is Exception) { + throw result; + } + + return Future.value(result); + }); + } +} diff --git a/packages/camera/camera_avfoundation/test/type_conversion_test.dart b/packages/camera/camera_avfoundation/test/type_conversion_test.dart new file mode 100644 index 000000000000..282f4aedb21d --- /dev/null +++ b/packages/camera/camera_avfoundation/test/type_conversion_test.dart @@ -0,0 +1,60 @@ +// 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. + +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import +import 'dart:typed_data'; + +import 'package:camera_avfoundation/src/type_conversion.dart'; +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('CameraImageData can be created', () { + final CameraImageData cameraImage = + cameraImageFromPlatformData({ + 'format': 1, + 'height': 1, + 'width': 4, + 'lensAperture': 1.8, + 'sensorExposureTime': 9991324, + 'sensorSensitivity': 92.0, + 'planes': [ + { + 'bytes': Uint8List.fromList([1, 2, 3, 4]), + 'bytesPerPixel': 1, + 'bytesPerRow': 4, + 'height': 1, + 'width': 4 + } + ] + }); + expect(cameraImage.height, 1); + expect(cameraImage.width, 4); + expect(cameraImage.format.group, ImageFormatGroup.unknown); + expect(cameraImage.planes.length, 1); + }); + + test('CameraImageData has ImageFormatGroup.yuv420', () { + final CameraImageData cameraImage = + cameraImageFromPlatformData({ + 'format': 875704438, + 'height': 1, + 'width': 4, + 'lensAperture': 1.8, + 'sensorExposureTime': 9991324, + 'sensorSensitivity': 92.0, + 'planes': [ + { + 'bytes': Uint8List.fromList([1, 2, 3, 4]), + 'bytesPerPixel': 1, + 'bytesPerRow': 4, + 'height': 1, + 'width': 4 + } + ] + }); + expect(cameraImage.format.group, ImageFormatGroup.yuv420); + }); +} diff --git a/packages/camera/camera_avfoundation/test/utils_test.dart b/packages/camera/camera_avfoundation/test/utils_test.dart new file mode 100644 index 000000000000..bd28abb0dc63 --- /dev/null +++ b/packages/camera/camera_avfoundation/test/utils_test.dart @@ -0,0 +1,60 @@ +// 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:camera_avfoundation/src/utils.dart'; +import 'package:camera_platform_interface/camera_platform_interface.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('Utility methods', () { + test( + 'Should return CameraLensDirection when valid value is supplied when parsing camera lens direction', + () { + expect( + parseCameraLensDirection('back'), + CameraLensDirection.back, + ); + expect( + parseCameraLensDirection('front'), + CameraLensDirection.front, + ); + expect( + parseCameraLensDirection('external'), + CameraLensDirection.external, + ); + }); + + test( + 'Should throw ArgumentException when invalid value is supplied when parsing camera lens direction', + () { + expect( + () => parseCameraLensDirection('test'), + throwsA(isArgumentError), + ); + }); + + test('serializeDeviceOrientation() should serialize correctly', () { + expect(serializeDeviceOrientation(DeviceOrientation.portraitUp), + 'portraitUp'); + expect(serializeDeviceOrientation(DeviceOrientation.portraitDown), + 'portraitDown'); + expect(serializeDeviceOrientation(DeviceOrientation.landscapeRight), + 'landscapeRight'); + expect(serializeDeviceOrientation(DeviceOrientation.landscapeLeft), + 'landscapeLeft'); + }); + + test('deserializeDeviceOrientation() should deserialize correctly', () { + expect(deserializeDeviceOrientation('portraitUp'), + DeviceOrientation.portraitUp); + expect(deserializeDeviceOrientation('portraitDown'), + DeviceOrientation.portraitDown); + expect(deserializeDeviceOrientation('landscapeRight'), + DeviceOrientation.landscapeRight); + expect(deserializeDeviceOrientation('landscapeLeft'), + DeviceOrientation.landscapeLeft); + }); + }); +} From b5b70279ba889b28349f96c6016089dd6cf0b64c Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Mon, 13 Jun 2022 14:14:08 -0700 Subject: [PATCH 421/844] [path_provider] Migrated path_provider_ios to pigeon. (#5944) * Migrated path_provider_ios to pigeon. * increased flutter version to 3.0.0 --- .../path_provider_ios/CHANGELOG.md | 4 + .../ios/Runner.xcodeproj/project.pbxproj | 4 +- .../ios/Classes/FLTPathProviderPlugin.m | 42 +++--- .../ios/Classes/messages.g.h | 28 ++++ .../ios/Classes/messages.g.m | 138 ++++++++++++++++++ .../path_provider_ios/lib/messages.g.dart | 124 ++++++++++++++++ .../lib/path_provider_ios.dart | 17 +-- .../path_provider_ios/pigeons/copyright.txt | 3 + .../path_provider_ios/pigeons/messages.dart | 21 +++ .../path_provider_ios/pubspec.yaml | 5 +- .../test/messages_test.g.dart | 88 +++++++++++ .../test/path_provider_ios_test.dart | 67 ++++----- 12 files changed, 463 insertions(+), 78 deletions(-) create mode 100644 packages/path_provider/path_provider_ios/ios/Classes/messages.g.h create mode 100644 packages/path_provider/path_provider_ios/ios/Classes/messages.g.m create mode 100644 packages/path_provider/path_provider_ios/lib/messages.g.dart create mode 100644 packages/path_provider/path_provider_ios/pigeons/copyright.txt create mode 100644 packages/path_provider/path_provider_ios/pigeons/messages.dart create mode 100644 packages/path_provider/path_provider_ios/test/messages_test.g.dart diff --git a/packages/path_provider/path_provider_ios/CHANGELOG.md b/packages/path_provider/path_provider_ios/CHANGELOG.md index 1940f5c7888e..8569c1b35027 100644 --- a/packages/path_provider/path_provider_ios/CHANGELOG.md +++ b/packages/path_provider/path_provider_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.10 + +* Switches backend to pigeon. + ## 2.0.9 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/path_provider/path_provider_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/path_provider/path_provider_ios/example/ios/Runner.xcodeproj/project.pbxproj index fec246b9e08c..601985b46ae6 100644 --- a/packages/path_provider/path_provider_ios/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/path_provider/path_provider_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 46; objects = { /* Begin PBXBuildFile section */ @@ -512,6 +512,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", @@ -533,6 +534,7 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", diff --git a/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m b/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m index ac6a1be9414f..82b8df5382fb 100644 --- a/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m +++ b/packages/path_provider/path_provider_ios/ios/Classes/FLTPathProviderPlugin.m @@ -3,47 +3,41 @@ // found in the LICENSE file. #import "FLTPathProviderPlugin.h" +#import "messages.g.h" -NSString *GetDirectoryOfType(NSSearchPathDirectory dir) { +static NSString *GetDirectoryOfType(NSSearchPathDirectory dir) { NSArray *paths = NSSearchPathForDirectoriesInDomains(dir, NSUserDomainMask, YES); return paths.firstObject; } +@interface FLTPathProviderPlugin () +@end + @implementation FLTPathProviderPlugin + (void)registerWithRegistrar:(NSObject *)registrar { - FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/path_provider_ios" - binaryMessenger:registrar.messenger]; - [channel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) { - if ([@"getTemporaryDirectory" isEqualToString:call.method]) { - result([self getTemporaryDirectory]); - } else if ([@"getApplicationDocumentsDirectory" isEqualToString:call.method]) { - result([self getApplicationDocumentsDirectory]); - } else if ([@"getApplicationSupportDirectory" isEqualToString:call.method]) { - result([self getApplicationSupportDirectory]); - } else if ([@"getLibraryDirectory" isEqualToString:call.method]) { - result([self getLibraryDirectory]); - } else { - result(FlutterMethodNotImplemented); - } - }]; -} - -+ (NSString *)getTemporaryDirectory { - return GetDirectoryOfType(NSCachesDirectory); + FLTPathProviderPlugin *plugin = [[FLTPathProviderPlugin alloc] init]; + FLTPathProviderApiSetup(registrar.messenger, plugin); } -+ (NSString *)getApplicationDocumentsDirectory { +- (nullable NSString *)getApplicationDocumentsPathWithError: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error { return GetDirectoryOfType(NSDocumentDirectory); } -+ (NSString *)getApplicationSupportDirectory { +- (nullable NSString *)getApplicationSupportPathWithError: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error { return GetDirectoryOfType(NSApplicationSupportDirectory); } -+ (NSString *)getLibraryDirectory { +- (nullable NSString *)getLibraryPathWithError: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error { return GetDirectoryOfType(NSLibraryDirectory); } +- (nullable NSString *)getTemporaryPathWithError: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error { + return GetDirectoryOfType(NSCachesDirectory); +} + @end diff --git a/packages/path_provider/path_provider_ios/ios/Classes/messages.g.h b/packages/path_provider/path_provider_ios/ios/Classes/messages.g.h new file mode 100644 index 000000000000..b6c1d4d92dd4 --- /dev/null +++ b/packages/path_provider/path_provider_ios/ios/Classes/messages.g.h @@ -0,0 +1,28 @@ +// 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 (v3.1.5), do not edit directly. +// See also: https://pub.dev/packages/pigeon +#import +@protocol FlutterBinaryMessenger; +@protocol FlutterMessageCodec; +@class FlutterError; +@class FlutterStandardTypedData; + +NS_ASSUME_NONNULL_BEGIN + +/// The codec used by FLTPathProviderApi. +NSObject *FLTPathProviderApiGetCodec(void); + +@protocol FLTPathProviderApi +- (nullable NSString *)getTemporaryPathWithError:(FlutterError *_Nullable *_Nonnull)error; +- (nullable NSString *)getApplicationSupportPathWithError:(FlutterError *_Nullable *_Nonnull)error; +- (nullable NSString *)getLibraryPathWithError:(FlutterError *_Nullable *_Nonnull)error; +- (nullable NSString *)getApplicationDocumentsPathWithError: + (FlutterError *_Nullable *_Nonnull)error; +@end + +extern void FLTPathProviderApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +NS_ASSUME_NONNULL_END diff --git a/packages/path_provider/path_provider_ios/ios/Classes/messages.g.m b/packages/path_provider/path_provider_ios/ios/Classes/messages.g.m new file mode 100644 index 000000000000..2589df1837e7 --- /dev/null +++ b/packages/path_provider/path_provider_ios/ios/Classes/messages.g.m @@ -0,0 +1,138 @@ +// 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 (v3.1.5), do not edit directly. +// See also: https://pub.dev/packages/pigeon +#import "messages.g.h" +#import + +#if !__has_feature(objc_arc) +#error File requires ARC to be enabled. +#endif + +static NSDictionary *wrapResult(id result, FlutterError *error) { + NSDictionary *errorDict = (NSDictionary *)[NSNull null]; + if (error) { + errorDict = @{ + @"code" : (error.code ?: [NSNull null]), + @"message" : (error.message ?: [NSNull null]), + @"details" : (error.details ?: [NSNull null]), + }; + } + return @{ + @"result" : (result ?: [NSNull null]), + @"error" : errorDict, + }; +} + +@interface FLTPathProviderApiCodecReader : FlutterStandardReader +@end +@implementation FLTPathProviderApiCodecReader +@end + +@interface FLTPathProviderApiCodecWriter : FlutterStandardWriter +@end +@implementation FLTPathProviderApiCodecWriter +@end + +@interface FLTPathProviderApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FLTPathProviderApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FLTPathProviderApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FLTPathProviderApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FLTPathProviderApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FLTPathProviderApiCodecReaderWriter *readerWriter = + [[FLTPathProviderApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FLTPathProviderApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.PathProviderApi.getTemporaryPath" + binaryMessenger:binaryMessenger + codec:FLTPathProviderApiGetCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(getTemporaryPathWithError:)], + @"FLTPathProviderApi api (%@) doesn't respond to @selector(getTemporaryPathWithError:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + NSString *output = [api getTemporaryPathWithError:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.PathProviderApi.getApplicationSupportPath" + binaryMessenger:binaryMessenger + codec:FLTPathProviderApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(getApplicationSupportPathWithError:)], + @"FLTPathProviderApi api (%@) doesn't respond to " + @"@selector(getApplicationSupportPathWithError:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + NSString *output = [api getApplicationSupportPathWithError:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.PathProviderApi.getLibraryPath" + binaryMessenger:binaryMessenger + codec:FLTPathProviderApiGetCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(getLibraryPathWithError:)], + @"FLTPathProviderApi api (%@) doesn't respond to @selector(getLibraryPathWithError:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + NSString *output = [api getLibraryPathWithError:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.PathProviderApi.getApplicationDocumentsPath" + binaryMessenger:binaryMessenger + codec:FLTPathProviderApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(getApplicationDocumentsPathWithError:)], + @"FLTPathProviderApi api (%@) doesn't respond to " + @"@selector(getApplicationDocumentsPathWithError:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + NSString *output = [api getApplicationDocumentsPathWithError:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} diff --git a/packages/path_provider/path_provider_ios/lib/messages.g.dart b/packages/path_provider/path_provider_ios/lib/messages.g.dart new file mode 100644 index 000000000000..1914119b8bd8 --- /dev/null +++ b/packages/path_provider/path_provider_ios/lib/messages.g.dart @@ -0,0 +1,124 @@ +// 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 (v3.1.5), 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 +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; + +class _PathProviderApiCodec extends StandardMessageCodec { + const _PathProviderApiCodec(); +} + +class PathProviderApi { + /// Constructor for [PathProviderApi]. 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. + PathProviderApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _PathProviderApiCodec(); + + Future getTemporaryPath() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getTemporaryPath', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send(null) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as String?); + } + } + + Future getApplicationSupportPath() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getApplicationSupportPath', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send(null) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as String?); + } + } + + Future getLibraryPath() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getLibraryPath', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send(null) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as String?); + } + } + + Future getApplicationDocumentsPath() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getApplicationDocumentsPath', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send(null) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as String?); + } + } +} diff --git a/packages/path_provider/path_provider_ios/lib/path_provider_ios.dart b/packages/path_provider/path_provider_ios/lib/path_provider_ios.dart index 88becf20850e..05be0534764a 100644 --- a/packages/path_provider/path_provider_ios/lib/path_provider_ios.dart +++ b/packages/path_provider/path_provider_ios/lib/path_provider_ios.dart @@ -4,16 +4,13 @@ import 'dart:io'; -import 'package:flutter/foundation.dart' show visibleForTesting; -import 'package:flutter/services.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; +import 'messages.g.dart'; /// The iOS implementation of [PathProviderPlatform]. class PathProviderIOS extends PathProviderPlatform { /// The method channel used to interact with the native platform. - @visibleForTesting - MethodChannel methodChannel = - const MethodChannel('plugins.flutter.io/path_provider_ios'); + final PathProviderApi _pathProvider = PathProviderApi(); /// Registers this class as the default instance of [PathProviderPlatform] static void registerWith() { @@ -22,13 +19,12 @@ class PathProviderIOS extends PathProviderPlatform { @override Future getTemporaryPath() async { - return methodChannel.invokeMethod('getTemporaryDirectory'); + return _pathProvider.getTemporaryPath(); } @override Future getApplicationSupportPath() async { - final String? path = await methodChannel - .invokeMethod('getApplicationSupportDirectory'); + final String? path = await _pathProvider.getApplicationSupportPath(); if (path != null) { // Ensure the directory exists before returning it, for consistency with // other platforms. @@ -39,13 +35,12 @@ class PathProviderIOS extends PathProviderPlatform { @override Future getLibraryPath() async { - return methodChannel.invokeMethod('getLibraryDirectory'); + return _pathProvider.getLibraryPath(); } @override Future getApplicationDocumentsPath() async { - return methodChannel - .invokeMethod('getApplicationDocumentsDirectory'); + return _pathProvider.getApplicationDocumentsPath(); } @override diff --git a/packages/path_provider/path_provider_ios/pigeons/copyright.txt b/packages/path_provider/path_provider_ios/pigeons/copyright.txt new file mode 100644 index 000000000000..1236b63caf3a --- /dev/null +++ b/packages/path_provider/path_provider_ios/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. diff --git a/packages/path_provider/path_provider_ios/pigeons/messages.dart b/packages/path_provider/path_provider_ios/pigeons/messages.dart new file mode 100644 index 000000000000..2ed79914e821 --- /dev/null +++ b/packages/path_provider/path_provider_ios/pigeons/messages.dart @@ -0,0 +1,21 @@ +// 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'; + +@ConfigurePigeon(PigeonOptions( + input: 'pigeons/messages.dart', + objcOptions: ObjcOptions(prefix: 'FLT'), + objcHeaderOut: 'ios/Classes/messages.g.h', + objcSourceOut: 'ios/Classes/messages.g.m', + dartOut: 'lib/messages.g.dart', + dartTestOut: 'test/messages_test.g.dart', + copyrightHeader: 'pigeons/copyright.txt', +)) +@HostApi(dartHostTestHandler: 'TestPathProviderApi') +abstract class PathProviderApi { + String? getTemporaryPath(); + String? getApplicationSupportPath(); + String? getLibraryPath(); + String? getApplicationDocumentsPath(); +} diff --git a/packages/path_provider/path_provider_ios/pubspec.yaml b/packages/path_provider/path_provider_ios/pubspec.yaml index d6c7de108c97..16d2f2e9021e 100644 --- a/packages/path_provider/path_provider_ios/pubspec.yaml +++ b/packages/path_provider/path_provider_ios/pubspec.yaml @@ -2,11 +2,11 @@ name: path_provider_ios description: iOS implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.9 +version: 2.0.10 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=3.0.0" flutter: plugin: @@ -29,5 +29,6 @@ dev_dependencies: integration_test: sdk: flutter path: ^1.8.0 + pigeon: ^3.1.5 plugin_platform_interface: ^2.0.0 test: ^1.16.0 diff --git a/packages/path_provider/path_provider_ios/test/messages_test.g.dart b/packages/path_provider/path_provider_ios/test/messages_test.g.dart new file mode 100644 index 000000000000..d1c9ff88dca1 --- /dev/null +++ b/packages/path_provider/path_provider_ios/test/messages_test.g.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. +// Autogenerated from Pigeon (v3.1.5), 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 +// ignore_for_file: avoid_relative_lib_imports +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../lib/messages.g.dart'; + +class _TestPathProviderApiCodec extends StandardMessageCodec { + const _TestPathProviderApiCodec(); +} + +abstract class TestPathProviderApi { + static const MessageCodec codec = _TestPathProviderApiCodec(); + + String? getTemporaryPath(); + String? getApplicationSupportPath(); + String? getLibraryPath(); + String? getApplicationDocumentsPath(); + static void setup(TestPathProviderApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getTemporaryPath', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + // ignore message + final String? output = api.getTemporaryPath(); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getApplicationSupportPath', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + // ignore message + final String? output = api.getApplicationSupportPath(); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getLibraryPath', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + // ignore message + final String? output = api.getLibraryPath(); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getApplicationDocumentsPath', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + // ignore message + final String? output = api.getApplicationDocumentsPath(); + return {'result': output}; + }); + } + } + } +} diff --git a/packages/path_provider/path_provider_ios/test/path_provider_ios_test.dart b/packages/path_provider/path_provider_ios/test/path_provider_ios_test.dart index 40f81c5f445a..16a7cd8d71a2 100644 --- a/packages/path_provider/path_provider_ios/test/path_provider_ios_test.dart +++ b/packages/path_provider/path_provider_ios/test/path_provider_ios_test.dart @@ -4,17 +4,35 @@ import 'dart:io'; -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:path/path.dart' as p; import 'package:path_provider_ios/path_provider_ios.dart'; +import 'messages_test.g.dart'; + +class _Api implements TestPathProviderApi { + String? applicationDocumentsPath; + String? applicationSupportPath; + String? libraryPath; + String? temporaryPath; + + @override + String? getApplicationDocumentsPath() => applicationDocumentsPath; + + @override + String? getApplicationSupportPath() => applicationSupportPath; + + @override + String? getLibraryPath() => libraryPath; + + @override + String? getTemporaryPath() => temporaryPath; +} void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('PathProviderIOS', () { late PathProviderIOS pathProvider; - late List log; // These unit tests use the actual filesystem, since an injectable // filesystem would add a runtime dependency to the package, so everything // is contained to a temporary directory. @@ -24,6 +42,7 @@ void main() { late String applicationSupportPath; late String libraryPath; late String applicationDocumentsPath; + late _Api api; setUp(() async { pathProvider = PathProviderIOS(); @@ -37,24 +56,12 @@ void main() { applicationDocumentsPath = p.join(basePath, 'application', 'documents', 'path'); - log = []; - TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger - .setMockMethodCallHandler(pathProvider.methodChannel, - (MethodCall methodCall) async { - log.add(methodCall); - switch (methodCall.method) { - case 'getTemporaryDirectory': - return temporaryPath; - case 'getApplicationSupportDirectory': - return applicationSupportPath; - case 'getLibraryDirectory': - return libraryPath; - case 'getApplicationDocumentsDirectory': - return applicationDocumentsPath; - default: - return null; - } - }); + api = _Api(); + api.applicationDocumentsPath = applicationDocumentsPath; + api.applicationSupportPath = applicationSupportPath; + api.libraryPath = libraryPath; + api.temporaryPath = temporaryPath; + TestPathProviderApi.setup(api); }); tearDown(() { @@ -63,21 +70,11 @@ void main() { test('getTemporaryPath', () async { final String? path = await pathProvider.getTemporaryPath(); - expect( - log, - [isMethodCall('getTemporaryDirectory', arguments: null)], - ); expect(path, temporaryPath); }); test('getApplicationSupportPath', () async { final String? path = await pathProvider.getApplicationSupportPath(); - expect( - log, - [ - isMethodCall('getApplicationSupportDirectory', arguments: null) - ], - ); expect(path, applicationSupportPath); }); @@ -89,21 +86,11 @@ void main() { test('getLibraryPath', () async { final String? path = await pathProvider.getLibraryPath(); - expect( - log, - [isMethodCall('getLibraryDirectory', arguments: null)], - ); expect(path, libraryPath); }); test('getApplicationDocumentsPath', () async { final String? path = await pathProvider.getApplicationDocumentsPath(); - expect( - log, - [ - isMethodCall('getApplicationDocumentsDirectory', arguments: null) - ], - ); expect(path, applicationDocumentsPath); }); From 6255da36c61438b96b23aabab09a163f8152c80f Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 13 Jun 2022 18:18:13 -0400 Subject: [PATCH 422/844] [google_maps_flutter] Add structure options to platform interface (#5960) --- .../CHANGELOG.md | 7 +- .../method_channel_google_maps_flutter.dart | 83 +++- .../google_maps_flutter_platform.dart | 49 ++- .../lib/src/types/bitmap.dart | 6 +- .../lib/src/types/map_configuration.dart | 248 +++++++++++ .../lib/src/types/map_objects.dart | 31 ++ .../src/types/map_widget_configuration.dart | 32 ++ .../lib/src/types/types.dart | 5 +- .../map_configuration_serialization.dart | 62 +++ .../pubspec.yaml | 2 +- .../google_maps_flutter_platform_test.dart | 19 + .../test/types/map_configuration_test.dart | 412 ++++++++++++++++++ .../map_configuration_serialization_test.dart | 75 ++++ 13 files changed, 999 insertions(+), 32 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_configuration.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_objects.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_widget_configuration.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/utils/map_configuration_serialization.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/map_configuration_test.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_platform_interface/test/utils/map_configuration_serialization_test.dart 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 016ec9e968e3..c1a7c95abfdb 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,10 @@ -## NEXT +## 2.2.0 +* Adds new versions of `buildView` and `updateOptions` that take a new option + class instead of a dictionary, to remove the cross-package dependency on + magic string keys. +* Adopts several parameter objects in the new `buildView` variant to + future-proof it against future changes. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). ## 2.1.7 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 60df6f52499f..a34ee48ac79a 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 @@ -16,6 +16,7 @@ import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platf import 'package:stream_transform/stream_transform.dart'; import '../types/tile_overlay_updates.dart'; +import '../types/utils/map_configuration_serialization.dart'; /// Error thrown when an unknown map ID is provided to a method channel API. class UnknownMapIDError extends Error { @@ -484,28 +485,22 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { /// Defaults to false. bool useAndroidViewSurface = false; - @override - Widget buildViewWithTextDirection( + Widget _buildView( int creationId, PlatformViewCreatedCallback onPlatformViewCreated, { - required CameraPosition initialCameraPosition, - required TextDirection textDirection, - Set markers = const {}, - Set polygons = const {}, - Set polylines = const {}, - Set circles = const {}, - Set tileOverlays = const {}, - Set>? gestureRecognizers, + required MapWidgetConfiguration widgetConfiguration, + MapObjects mapObjects = const MapObjects(), Map mapOptions = const {}, }) { final Map creationParams = { - 'initialCameraPosition': initialCameraPosition.toMap(), + 'initialCameraPosition': + widgetConfiguration.initialCameraPosition.toMap(), 'options': mapOptions, - 'markersToAdd': serializeMarkerSet(markers), - 'polygonsToAdd': serializePolygonSet(polygons), - 'polylinesToAdd': serializePolylineSet(polylines), - 'circlesToAdd': serializeCircleSet(circles), - 'tileOverlaysToAdd': serializeTileOverlaySet(tileOverlays), + 'markersToAdd': serializeMarkerSet(mapObjects.markers), + 'polygonsToAdd': serializePolygonSet(mapObjects.polygons), + 'polylinesToAdd': serializePolylineSet(mapObjects.polylines), + 'circlesToAdd': serializeCircleSet(mapObjects.circles), + 'tileOverlaysToAdd': serializeTileOverlaySet(mapObjects.tileOverlays), }; if (defaultTargetPlatform == TargetPlatform.android) { @@ -518,8 +513,7 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { ) { return AndroidViewSurface( controller: controller as AndroidViewController, - gestureRecognizers: gestureRecognizers ?? - const >{}, + gestureRecognizers: widgetConfiguration.gestureRecognizers, hitTestBehavior: PlatformViewHitTestBehavior.opaque, ); }, @@ -528,7 +522,7 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { PlatformViewsService.initSurfaceAndroidView( id: params.id, viewType: 'plugins.flutter.io/google_maps', - layoutDirection: textDirection, + layoutDirection: widgetConfiguration.textDirection, creationParams: creationParams, creationParamsCodec: const StandardMessageCodec(), onFocus: () => params.onFocusChanged(true), @@ -548,7 +542,7 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { return AndroidView( viewType: 'plugins.flutter.io/google_maps', onPlatformViewCreated: onPlatformViewCreated, - gestureRecognizers: gestureRecognizers, + gestureRecognizers: widgetConfiguration.gestureRecognizers, creationParams: creationParams, creationParamsCodec: const StandardMessageCodec(), ); @@ -557,7 +551,7 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { return UiKitView( viewType: 'plugins.flutter.io/google_maps', onPlatformViewCreated: onPlatformViewCreated, - gestureRecognizers: gestureRecognizers, + gestureRecognizers: widgetConfiguration.gestureRecognizers, creationParams: creationParams, creationParamsCodec: const StandardMessageCodec(), ); @@ -567,6 +561,53 @@ class MethodChannelGoogleMapsFlutter extends GoogleMapsFlutterPlatform { '$defaultTargetPlatform is not yet supported by the maps plugin'); } + @override + Widget buildViewWithConfiguration( + int creationId, + PlatformViewCreatedCallback onPlatformViewCreated, { + required MapWidgetConfiguration widgetConfiguration, + MapConfiguration mapConfiguration = const MapConfiguration(), + MapObjects mapObjects = const MapObjects(), + }) { + return _buildView( + creationId, + onPlatformViewCreated, + widgetConfiguration: widgetConfiguration, + mapObjects: mapObjects, + mapOptions: jsonForMapConfiguration(mapConfiguration), + ); + } + + @override + Widget buildViewWithTextDirection( + int creationId, + PlatformViewCreatedCallback onPlatformViewCreated, { + required CameraPosition initialCameraPosition, + required TextDirection textDirection, + Set markers = const {}, + Set polygons = const {}, + Set polylines = const {}, + Set circles = const {}, + Set tileOverlays = const {}, + Set>? gestureRecognizers, + Map mapOptions = const {}, + }) { + return _buildView( + creationId, + onPlatformViewCreated, + widgetConfiguration: MapWidgetConfiguration( + initialCameraPosition: initialCameraPosition, + textDirection: textDirection), + mapObjects: MapObjects( + markers: markers, + polygons: polygons, + polylines: polylines, + circles: circles, + tileOverlays: tileOverlays), + mapOptions: mapOptions, + ); + } + @override Widget buildView( int creationId, 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 8c36ebe4f6ef..b6b95018d0c4 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 @@ -13,6 +13,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'package:google_maps_flutter_platform_interface/src/types/utils/map_configuration_serialization.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; /// The interface that platform-specific implementations of `google_maps_flutter` must extend. @@ -50,7 +51,8 @@ abstract class GoogleMapsFlutterPlatform extends PlatformInterface { throw UnimplementedError('init() has not been implemented.'); } - /// Updates configuration options of the map user interface. + /// Updates configuration options of the map user interface - deprecated, use + /// updateMapConfiguration instead. /// /// Change listeners are notified once the update has been made on the /// platform side. @@ -63,6 +65,20 @@ abstract class GoogleMapsFlutterPlatform extends PlatformInterface { throw UnimplementedError('updateMapOptions() has not been implemented.'); } + /// Updates configuration options of the map user interface. + /// + /// Change listeners are notified once the update has been made on the + /// platform side. + /// + /// The returned [Future] completes after listeners have been notified. + Future updateMapConfiguration( + MapConfiguration configuration, { + required int mapId, + }) { + return updateMapOptions(jsonForMapConfiguration(configuration), + mapId: mapId); + } + /// Updates marker configuration. /// /// Change listeners are notified once the update has been made on the @@ -348,7 +364,8 @@ abstract class GoogleMapsFlutterPlatform extends PlatformInterface { throw UnimplementedError('dispose() has not been implemented.'); } - /// Returns a widget displaying the map view. + /// Returns a widget displaying the map view - deprecated, use + /// [buildViewWithConfiguration] instead. Widget buildView( int creationId, PlatformViewCreatedCallback onPlatformViewCreated, { @@ -367,7 +384,8 @@ abstract class GoogleMapsFlutterPlatform extends PlatformInterface { throw UnimplementedError('buildView() has not been implemented.'); } - /// Returns a widget displaying the map view. + /// Returns a widget displaying the map view - deprecated, use + /// [buildViewWithConfiguration] instead. /// /// This method is similar to [buildView], but contains a parameter for /// platforms that require a text direction. @@ -381,12 +399,12 @@ abstract class GoogleMapsFlutterPlatform extends PlatformInterface { PlatformViewCreatedCallback onPlatformViewCreated, { required CameraPosition initialCameraPosition, required TextDirection textDirection, + Set>? gestureRecognizers, Set markers = const {}, Set polygons = const {}, Set polylines = const {}, Set circles = const {}, Set tileOverlays = const {}, - Set>? gestureRecognizers, Map mapOptions = const {}, }) { return buildView( @@ -402,4 +420,27 @@ abstract class GoogleMapsFlutterPlatform extends PlatformInterface { mapOptions: mapOptions, ); } + + /// Returns a widget displaying the map view. + Widget buildViewWithConfiguration( + int creationId, + PlatformViewCreatedCallback onPlatformViewCreated, { + required MapWidgetConfiguration widgetConfiguration, + MapConfiguration mapConfiguration = const MapConfiguration(), + MapObjects mapObjects = const MapObjects(), + }) { + return buildViewWithTextDirection( + creationId, + onPlatformViewCreated, + initialCameraPosition: widgetConfiguration.initialCameraPosition, + textDirection: widgetConfiguration.textDirection, + markers: mapObjects.markers, + polygons: mapObjects.polygons, + polylines: mapObjects.polylines, + circles: mapObjects.circles, + tileOverlays: mapObjects.tileOverlays, + gestureRecognizers: widgetConfiguration.gestureRecognizers, + mapOptions: jsonForMapConfiguration(mapConfiguration), + ); + } } diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/bitmap.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/bitmap.dart index c43baf42db45..0ccc3e624abe 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/bitmap.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/bitmap.dart @@ -18,10 +18,8 @@ class BitmapDescriptor { const BitmapDescriptor._(this._json); /// The inverse of .toJson. - // This is needed in Web to re-hydrate BitmapDescriptors that have been - // transformed to JSON for transport. - // TODO(stuartmorgan): Clean this up. See - // https://github.com/flutter/flutter/issues/70330 + // TODO(stuartmorgan): Remove this in the next breaking change. + @Deprecated('No longer supported') BitmapDescriptor.fromJson(Object json) : _json = json { assert(_json is List); final List jsonList = json as List; 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 new file mode 100644 index 000000000000..4b43caffe5b6 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_configuration.dart @@ -0,0 +1,248 @@ +// 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 'ui.dart'; + +/// Configuration options for the GoogleMaps user interface. +@immutable +class MapConfiguration { + /// Creates a new configuration instance with the given options. + /// + /// Any options that aren't passed will be null, which allows this to serve + /// as either a full configuration selection, or an update to an existing + /// configuration where only non-null values are updated. + const MapConfiguration({ + this.compassEnabled, + this.mapToolbarEnabled, + this.cameraTargetBounds, + this.mapType, + this.minMaxZoomPreference, + this.rotateGesturesEnabled, + this.scrollGesturesEnabled, + this.tiltGesturesEnabled, + this.trackCameraPosition, + this.zoomControlsEnabled, + this.zoomGesturesEnabled, + this.liteModeEnabled, + this.myLocationEnabled, + this.myLocationButtonEnabled, + this.padding, + this.indoorViewEnabled, + this.trafficEnabled, + this.buildingsEnabled, + }); + + /// True if the compass UI should be shown. + final bool? compassEnabled; + + /// True if the map toolbar should be shown. + final bool? mapToolbarEnabled; + + /// The bounds to display. + final CameraTargetBounds? cameraTargetBounds; + + /// The type of the map. + final MapType? mapType; + + /// The prefered zoom range. + final MinMaxZoomPreference? minMaxZoomPreference; + + /// True if rotate gestures should be enabled. + final bool? rotateGesturesEnabled; + + /// True if scroll gestures should be enabled. + final bool? scrollGesturesEnabled; + + /// True if tilt gestures should be enabled. + final bool? tiltGesturesEnabled; + + /// True if camera position changes should trigger notifications. + final bool? trackCameraPosition; + + /// True if zoom controls should be displayed. + final bool? zoomControlsEnabled; + + /// True if zoom gestures should be enabled. + final bool? zoomGesturesEnabled; + + /// True if the map should use Lite Mode, showing a limited-interactivity + /// bitmap, on supported platforms. + final bool? liteModeEnabled; + + /// True if the current location should be tracked and displayed. + final bool? myLocationEnabled; + + /// True if the control to jump to the current location should be displayed. + final bool? myLocationButtonEnabled; + + /// The padding for the map display. + final EdgeInsets? padding; + + /// True if indoor map views should be enabled. + final bool? indoorViewEnabled; + + /// True if the traffic overlay should be enabled. + final bool? trafficEnabled; + + /// True if 3D building display should be enabled. + final bool? buildingsEnabled; + + /// Returns a new options object containing only the values of this instance + /// that are different from [other]. + MapConfiguration diffFrom(MapConfiguration other) { + return MapConfiguration( + compassEnabled: + compassEnabled != other.compassEnabled ? compassEnabled : null, + mapToolbarEnabled: mapToolbarEnabled != other.mapToolbarEnabled + ? mapToolbarEnabled + : null, + cameraTargetBounds: cameraTargetBounds != other.cameraTargetBounds + ? cameraTargetBounds + : null, + mapType: mapType != other.mapType ? mapType : null, + minMaxZoomPreference: minMaxZoomPreference != other.minMaxZoomPreference + ? minMaxZoomPreference + : null, + rotateGesturesEnabled: + rotateGesturesEnabled != other.rotateGesturesEnabled + ? rotateGesturesEnabled + : null, + scrollGesturesEnabled: + scrollGesturesEnabled != other.scrollGesturesEnabled + ? scrollGesturesEnabled + : null, + tiltGesturesEnabled: tiltGesturesEnabled != other.tiltGesturesEnabled + ? tiltGesturesEnabled + : null, + trackCameraPosition: trackCameraPosition != other.trackCameraPosition + ? trackCameraPosition + : null, + zoomControlsEnabled: zoomControlsEnabled != other.zoomControlsEnabled + ? zoomControlsEnabled + : null, + zoomGesturesEnabled: zoomGesturesEnabled != other.zoomGesturesEnabled + ? zoomGesturesEnabled + : null, + liteModeEnabled: + liteModeEnabled != other.liteModeEnabled ? liteModeEnabled : null, + myLocationEnabled: myLocationEnabled != other.myLocationEnabled + ? myLocationEnabled + : null, + myLocationButtonEnabled: + myLocationButtonEnabled != other.myLocationButtonEnabled + ? myLocationButtonEnabled + : null, + padding: padding != other.padding ? padding : null, + indoorViewEnabled: indoorViewEnabled != other.indoorViewEnabled + ? indoorViewEnabled + : null, + trafficEnabled: + trafficEnabled != other.trafficEnabled ? trafficEnabled : null, + buildingsEnabled: + buildingsEnabled != other.buildingsEnabled ? buildingsEnabled : null, + ); + } + + /// Returns a copy of this instance with any non-null settings form [diff] + /// replacing the previous values. + MapConfiguration applyDiff(MapConfiguration diff) { + return MapConfiguration( + compassEnabled: diff.compassEnabled ?? compassEnabled, + mapToolbarEnabled: diff.mapToolbarEnabled ?? mapToolbarEnabled, + cameraTargetBounds: diff.cameraTargetBounds ?? cameraTargetBounds, + mapType: diff.mapType ?? mapType, + minMaxZoomPreference: diff.minMaxZoomPreference ?? minMaxZoomPreference, + rotateGesturesEnabled: + diff.rotateGesturesEnabled ?? rotateGesturesEnabled, + scrollGesturesEnabled: + diff.scrollGesturesEnabled ?? scrollGesturesEnabled, + tiltGesturesEnabled: diff.tiltGesturesEnabled ?? tiltGesturesEnabled, + trackCameraPosition: diff.trackCameraPosition ?? trackCameraPosition, + zoomControlsEnabled: diff.zoomControlsEnabled ?? zoomControlsEnabled, + zoomGesturesEnabled: diff.zoomGesturesEnabled ?? zoomGesturesEnabled, + liteModeEnabled: diff.liteModeEnabled ?? liteModeEnabled, + myLocationEnabled: diff.myLocationEnabled ?? myLocationEnabled, + myLocationButtonEnabled: + diff.myLocationButtonEnabled ?? myLocationButtonEnabled, + padding: diff.padding ?? padding, + indoorViewEnabled: diff.indoorViewEnabled ?? indoorViewEnabled, + trafficEnabled: diff.trafficEnabled ?? trafficEnabled, + buildingsEnabled: diff.buildingsEnabled ?? buildingsEnabled, + ); + } + + /// True if no options are set. + bool get isEmpty => + compassEnabled == null && + mapToolbarEnabled == null && + cameraTargetBounds == null && + mapType == null && + minMaxZoomPreference == null && + rotateGesturesEnabled == null && + scrollGesturesEnabled == null && + tiltGesturesEnabled == null && + trackCameraPosition == null && + zoomControlsEnabled == null && + zoomGesturesEnabled == null && + liteModeEnabled == null && + myLocationEnabled == null && + myLocationButtonEnabled == null && + padding == null && + indoorViewEnabled == null && + trafficEnabled == null && + buildingsEnabled == null; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return other is MapConfiguration && + compassEnabled == other.compassEnabled && + mapToolbarEnabled == other.mapToolbarEnabled && + cameraTargetBounds == other.cameraTargetBounds && + mapType == other.mapType && + minMaxZoomPreference == other.minMaxZoomPreference && + rotateGesturesEnabled == other.rotateGesturesEnabled && + scrollGesturesEnabled == other.scrollGesturesEnabled && + tiltGesturesEnabled == other.tiltGesturesEnabled && + trackCameraPosition == other.trackCameraPosition && + zoomControlsEnabled == other.zoomControlsEnabled && + zoomGesturesEnabled == other.zoomGesturesEnabled && + liteModeEnabled == other.liteModeEnabled && + myLocationEnabled == other.myLocationEnabled && + myLocationButtonEnabled == other.myLocationButtonEnabled && + padding == other.padding && + indoorViewEnabled == other.indoorViewEnabled && + trafficEnabled == other.trafficEnabled && + buildingsEnabled == other.buildingsEnabled; + } + + @override + int get hashCode => Object.hash( + compassEnabled, + mapToolbarEnabled, + cameraTargetBounds, + mapType, + minMaxZoomPreference, + rotateGesturesEnabled, + scrollGesturesEnabled, + tiltGesturesEnabled, + trackCameraPosition, + zoomControlsEnabled, + zoomGesturesEnabled, + liteModeEnabled, + myLocationEnabled, + myLocationButtonEnabled, + padding, + indoorViewEnabled, + trafficEnabled, + buildingsEnabled, + ); +} 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 new file mode 100644 index 000000000000..56f80e8312dd --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_objects.dart @@ -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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/foundation.dart'; + +import 'types.dart'; + +/// A container object for all the types of maps objects. +/// +/// This is intended for use as a parameter in platform interface methods, to +/// allow adding new object types to existing methods. +@immutable +class MapObjects { + /// Creates a new set of map objects with all the given object types. + const MapObjects({ + this.markers = const {}, + this.polygons = const {}, + this.polylines = const {}, + this.circles = const {}, + this.tileOverlays = const {}, + }); + + final Set markers; + final Set polygons; + final Set polylines; + final Set circles; + final Set tileOverlays; +} diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_widget_configuration.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_widget_configuration.dart new file mode 100644 index 000000000000..029af9901661 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_widget_configuration.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 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; + +import 'types.dart'; + +/// A container object for configuration options when building a widget. +/// +/// This is intended for use as a parameter in platform interface methods, to +/// allow adding new configuration options to existing methods. +@immutable +class MapWidgetConfiguration { + /// Creates a new configuration with all the given settings. + const MapWidgetConfiguration({ + required this.initialCameraPosition, + required this.textDirection, + this.gestureRecognizers = const >{}, + }); + + /// The initial camera position to display. + final CameraPosition initialCameraPosition; + + /// The text direction for the widget. + final TextDirection textDirection; + + /// Gesture recognizers to add to the widget. + final Set> gestureRecognizers; +} 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 1e1bef8ee6c0..0beb7d747ec8 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 @@ -11,6 +11,9 @@ export 'circle.dart'; export 'circle_updates.dart'; export 'joint_type.dart'; export 'location.dart'; +export 'map_configuration.dart'; +export 'map_objects.dart'; +export 'map_widget_configuration.dart'; export 'maps_object.dart'; export 'maps_object_updates.dart'; export 'marker.dart'; @@ -25,7 +28,7 @@ export 'tile.dart'; export 'tile_overlay.dart'; export 'tile_provider.dart'; export 'ui.dart'; -// Export the utils, they're used by the Widget +// Export the utils used by the Widget export 'utils/circle.dart'; export 'utils/marker.dart'; export 'utils/polygon.dart'; 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 new file mode 100644 index 000000000000..01f4fa054570 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/utils/map_configuration_serialization.dart @@ -0,0 +1,62 @@ +// 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 '../map_configuration.dart'; + +/// Returns a JSON representation of [config]. +/// +/// This is intended for two purposes: +/// - Conversion of [MapConfiguration] to the map options dictionary used by +/// legacy platform interface methods. +/// - Conversion of [MapConfiguration] to the default method channel +/// implementation's representation. +/// +/// Both of these are parts of the public interface, so any change to the +/// representation other than adding a new field requires a breaking change to +/// the package. +Map jsonForMapConfiguration(MapConfiguration config) { + final EdgeInsets? padding = config.padding; + return { + if (config.compassEnabled != null) 'compassEnabled': config.compassEnabled!, + if (config.mapToolbarEnabled != null) + 'mapToolbarEnabled': config.mapToolbarEnabled!, + if (config.cameraTargetBounds != null) + 'cameraTargetBounds': config.cameraTargetBounds!.toJson(), + if (config.mapType != null) 'mapType': config.mapType!.index, + if (config.minMaxZoomPreference != null) + 'minMaxZoomPreference': config.minMaxZoomPreference!.toJson(), + if (config.rotateGesturesEnabled != null) + 'rotateGesturesEnabled': config.rotateGesturesEnabled!, + if (config.scrollGesturesEnabled != null) + 'scrollGesturesEnabled': config.scrollGesturesEnabled!, + if (config.tiltGesturesEnabled != null) + 'tiltGesturesEnabled': config.tiltGesturesEnabled!, + if (config.zoomControlsEnabled != null) + 'zoomControlsEnabled': config.zoomControlsEnabled!, + if (config.zoomGesturesEnabled != null) + 'zoomGesturesEnabled': config.zoomGesturesEnabled!, + if (config.liteModeEnabled != null) + 'liteModeEnabled': config.liteModeEnabled!, + if (config.trackCameraPosition != null) + 'trackCameraPosition': config.trackCameraPosition!, + if (config.myLocationEnabled != null) + 'myLocationEnabled': config.myLocationEnabled!, + if (config.myLocationButtonEnabled != null) + 'myLocationButtonEnabled': config.myLocationButtonEnabled!, + if (padding != null) + 'padding': [ + padding.top, + padding.left, + padding.bottom, + padding.right, + ], + if (config.indoorViewEnabled != null) + 'indoorEnabled': config.indoorViewEnabled!, + if (config.trafficEnabled != null) 'trafficEnabled': config.trafficEnabled!, + if (config.buildingsEnabled != null) + 'buildingsEnabled': config.buildingsEnabled!, + }; +} 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 759daf2bb1cb..2b01e6244210 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/plugins/tree/main/packages/google_maps_fl 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.1.7 +version: 2.2.0 environment: sdk: '>=2.12.0 <3.0.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 0899bb6a8fb2..d185aabe1a5c 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 @@ -57,6 +57,25 @@ void main() { ); }, ); + + test( + 'default implementation of `buildViewWithConfiguration` delegates to `buildViewWithTextDirection`', + () { + final GoogleMapsFlutterPlatform platform = + BuildViewGoogleMapsFlutterPlatform(); + expect( + platform.buildViewWithConfiguration( + 0, + (_) {}, + widgetConfiguration: const MapWidgetConfiguration( + initialCameraPosition: CameraPosition(target: LatLng(0.0, 0.0)), + textDirection: TextDirection.ltr, + ), + ), + isA(), + ); + }, + ); }); } 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 new file mode 100644 index 000000000000..edd1fd091073 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/map_configuration_test.dart @@ -0,0 +1,412 @@ +// 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:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +void main() { + group('diffs', () { + // A options instance with every field set, to test diffs against. + final MapConfiguration diffBase = MapConfiguration( + compassEnabled: false, + mapToolbarEnabled: false, + cameraTargetBounds: CameraTargetBounds(LatLngBounds( + northeast: const LatLng(30, 20), southwest: const LatLng(10, 40))), + mapType: MapType.normal, + minMaxZoomPreference: const MinMaxZoomPreference(1.0, 10.0), + rotateGesturesEnabled: false, + scrollGesturesEnabled: false, + tiltGesturesEnabled: false, + trackCameraPosition: false, + zoomControlsEnabled: false, + zoomGesturesEnabled: false, + liteModeEnabled: false, + myLocationEnabled: false, + myLocationButtonEnabled: false, + padding: const EdgeInsets.all(5.0), + indoorViewEnabled: false, + trafficEnabled: false, + buildingsEnabled: false, + ); + + test('only include changed fields', () async { + const MapConfiguration nullOptions = MapConfiguration(); + + // Everything should be null since nothing changed. + expect(diffBase.diffFrom(diffBase), nullOptions); + }); + + test('only apply non-null fields', () async { + const MapConfiguration smallDiff = MapConfiguration(compassEnabled: true); + + final MapConfiguration updated = diffBase.applyDiff(smallDiff); + + // The diff should be updated. + expect(updated.compassEnabled, true); + // Spot check that other fields weren't stomped. + expect(updated.mapToolbarEnabled, isNot(null)); + expect(updated.cameraTargetBounds, isNot(null)); + expect(updated.mapType, isNot(null)); + expect(updated.zoomControlsEnabled, isNot(null)); + expect(updated.liteModeEnabled, isNot(null)); + expect(updated.padding, isNot(null)); + expect(updated.trafficEnabled, isNot(null)); + }); + + test('handle compassEnabled', () async { + const MapConfiguration diff = MapConfiguration(compassEnabled: true); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // A diff applied to non-empty options should update that field. + expect(updated.compassEnabled, true); + }); + + test('handle mapToolbarEnabled', () async { + const MapConfiguration diff = MapConfiguration(mapToolbarEnabled: true); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // A diff applied to non-empty options should update that field. + expect(updated.mapToolbarEnabled, true); + }); + + test('handle cameraTargetBounds', () async { + final CameraTargetBounds newBounds = CameraTargetBounds(LatLngBounds( + northeast: const LatLng(55, 15), southwest: const LatLng(5, 15))); + final MapConfiguration diff = + MapConfiguration(cameraTargetBounds: newBounds); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // A diff applied to non-empty options should update that field. + expect(updated.cameraTargetBounds, newBounds); + }); + + test('handle mapType', () async { + const MapConfiguration diff = + MapConfiguration(mapType: MapType.satellite); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // A diff applied to non-empty options should update that field. + expect(updated.mapType, MapType.satellite); + }); + + test('handle minMaxZoomPreference', () async { + const MinMaxZoomPreference newZoomPref = MinMaxZoomPreference(3.3, 4.5); + const MapConfiguration diff = + MapConfiguration(minMaxZoomPreference: newZoomPref); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // A diff applied to non-empty options should update that field. + expect(updated.minMaxZoomPreference, newZoomPref); + }); + + test('handle rotateGesturesEnabled', () async { + const MapConfiguration diff = + MapConfiguration(rotateGesturesEnabled: true); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // A diff applied to non-empty options should update that field. + expect(updated.rotateGesturesEnabled, true); + }); + + test('handle scrollGesturesEnabled', () async { + const MapConfiguration diff = + MapConfiguration(scrollGesturesEnabled: true); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // A diff applied to non-empty options should update that field. + expect(updated.scrollGesturesEnabled, true); + }); + + test('handle tiltGesturesEnabled', () async { + const MapConfiguration diff = MapConfiguration(tiltGesturesEnabled: true); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // A diff applied to non-empty options should update that field. + expect(updated.tiltGesturesEnabled, true); + }); + + test('handle trackCameraPosition', () async { + const MapConfiguration diff = MapConfiguration(trackCameraPosition: true); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // A diff applied to non-empty options should update that field. + expect(updated.trackCameraPosition, true); + }); + + test('handle zoomControlsEnabled', () async { + const MapConfiguration diff = MapConfiguration(zoomControlsEnabled: true); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // A diff applied to non-empty options should update that field. + expect(updated.zoomControlsEnabled, true); + }); + + test('handle zoomGesturesEnabled', () async { + const MapConfiguration diff = MapConfiguration(zoomGesturesEnabled: true); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // A diff applied to non-empty options should update that field. + expect(updated.zoomGesturesEnabled, true); + }); + + test('handle liteModeEnabled', () async { + const MapConfiguration diff = MapConfiguration(liteModeEnabled: true); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // A diff applied to non-empty options should update that field. + expect(updated.liteModeEnabled, true); + }); + + test('handle myLocationEnabled', () async { + const MapConfiguration diff = MapConfiguration(myLocationEnabled: true); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // A diff applied to non-empty options should update that field. + expect(updated.myLocationEnabled, true); + }); + + test('handle myLocationButtonEnabled', () async { + const MapConfiguration diff = + MapConfiguration(myLocationButtonEnabled: true); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // A diff applied to non-empty options should update that field. + expect(updated.myLocationButtonEnabled, true); + }); + + test('handle padding', () async { + const EdgeInsets newPadding = + EdgeInsets.symmetric(vertical: 1.0, horizontal: 3.0); + const MapConfiguration diff = MapConfiguration(padding: newPadding); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // A diff applied to non-empty options should update that field. + expect(updated.padding, newPadding); + }); + + test('handle indoorViewEnabled', () async { + const MapConfiguration diff = MapConfiguration(indoorViewEnabled: true); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // A diff applied to non-empty options should update that field. + expect(updated.indoorViewEnabled, true); + }); + + test('handle trafficEnabled', () async { + const MapConfiguration diff = MapConfiguration(trafficEnabled: true); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // A diff applied to non-empty options should update that field. + expect(updated.trafficEnabled, true); + }); + + test('handle buildingsEnabled', () async { + const MapConfiguration diff = MapConfiguration(buildingsEnabled: true); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // A diff applied to non-empty options should update that field. + expect(updated.buildingsEnabled, true); + }); + }); + + group('isEmpty', () { + test('is true for empty', () async { + const MapConfiguration nullOptions = MapConfiguration(); + + expect(nullOptions.isEmpty, true); + }); + + test('is false with compassEnabled', () async { + const MapConfiguration diff = MapConfiguration(compassEnabled: true); + + expect(diff.isEmpty, false); + }); + + test('is false with mapToolbarEnabled', () async { + const MapConfiguration diff = MapConfiguration(mapToolbarEnabled: true); + + expect(diff.isEmpty, false); + }); + + test('is false with cameraTargetBounds', () async { + final CameraTargetBounds newBounds = CameraTargetBounds(LatLngBounds( + northeast: const LatLng(55, 15), southwest: const LatLng(5, 15))); + final MapConfiguration diff = + MapConfiguration(cameraTargetBounds: newBounds); + + expect(diff.isEmpty, false); + }); + + test('is false with mapType', () async { + const MapConfiguration diff = + MapConfiguration(mapType: MapType.satellite); + + expect(diff.isEmpty, false); + }); + + test('is false with minMaxZoomPreference', () async { + const MinMaxZoomPreference newZoomPref = MinMaxZoomPreference(3.3, 4.5); + const MapConfiguration diff = + MapConfiguration(minMaxZoomPreference: newZoomPref); + + expect(diff.isEmpty, false); + }); + + test('is false with rotateGesturesEnabled', () async { + const MapConfiguration diff = + MapConfiguration(rotateGesturesEnabled: true); + + expect(diff.isEmpty, false); + }); + + test('is false with scrollGesturesEnabled', () async { + const MapConfiguration diff = + MapConfiguration(scrollGesturesEnabled: true); + + expect(diff.isEmpty, false); + }); + + test('is false with tiltGesturesEnabled', () async { + const MapConfiguration diff = MapConfiguration(tiltGesturesEnabled: true); + + expect(diff.isEmpty, false); + }); + + test('is false with trackCameraPosition', () async { + const MapConfiguration diff = MapConfiguration(trackCameraPosition: true); + + expect(diff.isEmpty, false); + }); + + test('is false with zoomControlsEnabled', () async { + const MapConfiguration diff = MapConfiguration(zoomControlsEnabled: true); + + expect(diff.isEmpty, false); + }); + + test('is false with zoomGesturesEnabled', () async { + const MapConfiguration diff = MapConfiguration(zoomGesturesEnabled: true); + + expect(diff.isEmpty, false); + }); + + test('is false with liteModeEnabled', () async { + const MapConfiguration diff = MapConfiguration(liteModeEnabled: true); + + expect(diff.isEmpty, false); + }); + + test('is false with myLocationEnabled', () async { + const MapConfiguration diff = MapConfiguration(myLocationEnabled: true); + + expect(diff.isEmpty, false); + }); + + test('is false with myLocationButtonEnabled', () async { + const MapConfiguration diff = + MapConfiguration(myLocationButtonEnabled: true); + + expect(diff.isEmpty, false); + }); + + test('is false with padding', () async { + const EdgeInsets newPadding = + EdgeInsets.symmetric(vertical: 1.0, horizontal: 3.0); + const MapConfiguration diff = MapConfiguration(padding: newPadding); + + expect(diff.isEmpty, false); + }); + + test('is false with indoorViewEnabled', () async { + const MapConfiguration diff = MapConfiguration(indoorViewEnabled: true); + + expect(diff.isEmpty, false); + }); + + test('is false with trafficEnabled', () async { + const MapConfiguration diff = MapConfiguration(trafficEnabled: true); + + expect(diff.isEmpty, false); + }); + + test('is false with buildingsEnabled', () async { + const MapConfiguration diff = MapConfiguration(buildingsEnabled: true); + + expect(diff.isEmpty, false); + }); + }); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/utils/map_configuration_serialization_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/utils/map_configuration_serialization_test.dart new file mode 100644 index 000000000000..71a0f8c4b1b1 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/utils/map_configuration_serialization_test.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. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'package:google_maps_flutter_platform_interface/src/types/utils/map_configuration_serialization.dart'; + +void main() { + test('empty serialization', () async { + const MapConfiguration config = MapConfiguration(); + + final Map json = jsonForMapConfiguration(config); + + expect(json.isEmpty, true); + }); + + test('complete serialization', () async { + final MapConfiguration config = MapConfiguration( + compassEnabled: false, + mapToolbarEnabled: false, + cameraTargetBounds: CameraTargetBounds(LatLngBounds( + northeast: const LatLng(30, 20), southwest: const LatLng(10, 40))), + mapType: MapType.normal, + minMaxZoomPreference: const MinMaxZoomPreference(1.0, 10.0), + rotateGesturesEnabled: false, + scrollGesturesEnabled: false, + tiltGesturesEnabled: false, + trackCameraPosition: false, + zoomControlsEnabled: false, + zoomGesturesEnabled: false, + liteModeEnabled: false, + myLocationEnabled: false, + myLocationButtonEnabled: false, + padding: const EdgeInsets.all(5.0), + indoorViewEnabled: false, + trafficEnabled: false, + buildingsEnabled: false, + ); + + final Map json = jsonForMapConfiguration(config); + + // This uses literals instead of toJson() for the expectations on + // sub-objects, because if the serialization of any of those objects were + // ever to change MapConfiguration would need to update to serialize those + // objects manually to preserve the format, in order to avoid breaking + // implementations. + expect(json, { + 'compassEnabled': false, + 'mapToolbarEnabled': false, + 'cameraTargetBounds': [ + [ + [10.0, 40.0], + [30.0, 20.0] + ] + ], + 'mapType': 1, + 'minMaxZoomPreference': [1.0, 10.0], + 'rotateGesturesEnabled': false, + 'scrollGesturesEnabled': false, + 'tiltGesturesEnabled': false, + 'zoomControlsEnabled': false, + 'zoomGesturesEnabled': false, + 'liteModeEnabled': false, + 'trackCameraPosition': false, + 'myLocationEnabled': false, + 'myLocationButtonEnabled': false, + 'padding': [5.0, 5.0, 5.0, 5.0], + 'indoorEnabled': false, + 'trafficEnabled': false, + 'buildingsEnabled': false + }); + }); +} From 602e4ace3b5c12d51285a2dbb636205b1e7bfe67 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 14 Jun 2022 05:58:10 -0400 Subject: [PATCH 423/844] [ci] Skip expensive native tests on stable in presubmit (#5962) --- .cirrus.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.cirrus.yml b/.cirrus.yml index 699ec264db77..70f9ddbfb251 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -187,6 +187,8 @@ task: - flutter config --enable-linux-desktop << : *BUILD_ALL_PLUGINS_APP_TEMPLATE - name: linux-platform_tests + # Don't run full platform tests on both channels in pre-submit. + skip: $CIRRUS_PR != '' && $CHANNEL == 'stable' env: matrix: CHANNEL: "master" @@ -216,6 +218,8 @@ task: matrix: ### Android tasks ### - name: android-platform_tests + # Don't run full platform tests on both channels in pre-submit. + skip: $CIRRUS_PR != '' && $CHANNEL == 'stable' env: matrix: PLUGIN_SHARDING: "--shardIndex 0 --shardCount 5" @@ -316,6 +320,8 @@ task: << : *BUILD_ALL_PLUGINS_APP_TEMPLATE ### macOS desktop tasks ### - name: macos-platform_tests + # Don't run full platform tests on both channels in pre-submit. + skip: $CIRRUS_PR != '' && $CHANNEL == 'stable' env: matrix: CHANNEL: "master" @@ -351,6 +357,8 @@ task: # tests are reliable on the ARM infrastructure. See discussion at # https://github.com/flutter/plugins/pull/5693#issuecomment-1126011089 - name: ios-platform_tests + # Don't run full platform tests on both channels in pre-submit. + skip: $CIRRUS_PR != '' && $CHANNEL == 'stable' env: PATH: $PATH:/usr/local/bin matrix: From 729e62c19803271cf2100bad9fba24486a8617fa Mon Sep 17 00:00:00 2001 From: Darren Austin Date: Tue, 14 Jun 2022 06:58:10 -0700 Subject: [PATCH 424/844] Ignore deprecation for `styleFrom` button APIs (#5945) --- packages/camera/camera/CHANGELOG.md | 4 ++++ packages/camera/camera/example/lib/main.dart | 8 ++++++++ packages/camera/camera/pubspec.yaml | 2 +- packages/file_selector/file_selector/CHANGELOG.md | 4 ++++ .../file_selector/example/lib/get_directory_page.dart | 3 +++ .../file_selector/example/lib/home_page.dart | 3 +++ .../file_selector/example/lib/open_image_page.dart | 3 +++ .../example/lib/open_multiple_images_page.dart | 3 +++ .../file_selector/example/lib/open_text_page.dart | 3 +++ .../file_selector/example/lib/save_text_page.dart | 3 +++ packages/file_selector/file_selector_macos/CHANGELOG.md | 4 ++++ .../example/lib/get_directory_page.dart | 3 +++ .../file_selector_macos/example/lib/home_page.dart | 3 +++ .../file_selector_macos/example/lib/open_image_page.dart | 3 +++ .../example/lib/open_multiple_images_page.dart | 3 +++ .../file_selector_macos/example/lib/open_text_page.dart | 3 +++ .../file_selector_macos/example/lib/save_text_page.dart | 3 +++ packages/file_selector/file_selector_windows/CHANGELOG.md | 4 ++++ .../example/lib/get_directory_page.dart | 3 +++ .../file_selector_windows/example/lib/home_page.dart | 3 +++ .../example/lib/open_image_page.dart | 3 +++ .../example/lib/open_multiple_images_page.dart | 3 +++ .../file_selector_windows/example/lib/open_text_page.dart | 3 +++ .../file_selector_windows/example/lib/save_text_page.dart | 3 +++ packages/in_app_purchase/in_app_purchase/CHANGELOG.md | 4 ++++ .../in_app_purchase/in_app_purchase/example/lib/main.dart | 4 ++++ packages/in_app_purchase/in_app_purchase/pubspec.yaml | 2 +- .../in_app_purchase/in_app_purchase_android/CHANGELOG.md | 4 ++++ .../in_app_purchase_android/example/lib/main.dart | 2 ++ .../in_app_purchase/in_app_purchase_android/pubspec.yaml | 2 +- .../in_app_purchase/in_app_purchase_storekit/CHANGELOG.md | 4 ++++ .../in_app_purchase_storekit/example/lib/main.dart | 4 ++++ .../in_app_purchase/in_app_purchase_storekit/pubspec.yaml | 2 +- 33 files changed, 104 insertions(+), 4 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 28032c9658fa..7f4de92e0d97 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.8+1 + +* Ignores deprecation warnings for upcoming styleFrom button API changes. + ## 0.9.8 * Moves Android and iOS implementations to federated packages. diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index c0181a5d36a1..f8f28dd5d23f 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -371,11 +371,15 @@ class _CameraExampleHomeState extends State Widget _exposureModeControlRowWidget() { final ButtonStyle styleAuto = TextButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: controller?.value.exposureMode == ExposureMode.auto ? Colors.orange : Colors.blue, ); final ButtonStyle styleLocked = TextButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: controller?.value.exposureMode == ExposureMode.locked ? Colors.orange : Colors.blue, @@ -456,11 +460,15 @@ class _CameraExampleHomeState extends State Widget _focusModeControlRowWidget() { final ButtonStyle styleAuto = TextButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: controller?.value.focusMode == FocusMode.auto ? Colors.orange : Colors.blue, ); final ButtonStyle styleLocked = TextButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: controller?.value.focusMode == FocusMode.locked ? Colors.orange : Colors.blue, diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 3270889c08e3..a23405d083e3 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.8 +version: 0.9.8+1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index ed7b213b5686..db29f829771d 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores deprecation warnings for upcoming styleFrom button API changes. + ## 0.8.4+3 * Improves API docs and examples. diff --git a/packages/file_selector/file_selector/example/lib/get_directory_page.dart b/packages/file_selector/file_selector/example/lib/get_directory_page.dart index 506134d66bd8..1a36be3fe26b 100644 --- a/packages/file_selector/file_selector/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector/example/lib/get_directory_page.dart @@ -37,7 +37,10 @@ class GetDirectoryPage extends StatelessWidget { children: [ ElevatedButton( style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.blue, + // ignore: deprecated_member_use onPrimary: Colors.white, ), child: const Text('Press to ask user to choose a directory'), diff --git a/packages/file_selector/file_selector/example/lib/home_page.dart b/packages/file_selector/file_selector/example/lib/home_page.dart index 9a0733b56b93..7b4582c5f5e3 100644 --- a/packages/file_selector/file_selector/example/lib/home_page.dart +++ b/packages/file_selector/file_selector/example/lib/home_page.dart @@ -12,7 +12,10 @@ class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.blue, + // ignore: deprecated_member_use onPrimary: Colors.white, ); return Scaffold( diff --git a/packages/file_selector/file_selector/example/lib/open_image_page.dart b/packages/file_selector/file_selector/example/lib/open_image_page.dart index 4fb06864c570..28afca065121 100644 --- a/packages/file_selector/file_selector/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_image_page.dart @@ -47,7 +47,10 @@ class OpenImagePage extends StatelessWidget { children: [ ElevatedButton( style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.blue, + // ignore: deprecated_member_use onPrimary: Colors.white, ), child: const Text('Press to open an image file(png, jpg)'), diff --git a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart index ac3d66fc955c..22703425b47b 100644 --- a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart @@ -50,7 +50,10 @@ class OpenMultipleImagesPage extends StatelessWidget { children: [ ElevatedButton( style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.blue, + // ignore: deprecated_member_use onPrimary: Colors.white, ), child: const Text('Press to open multiple images (png, jpg)'), diff --git a/packages/file_selector/file_selector/example/lib/open_text_page.dart b/packages/file_selector/file_selector/example/lib/open_text_page.dart index 057925ed43c2..27fb0b749c3b 100644 --- a/packages/file_selector/file_selector/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_text_page.dart @@ -50,7 +50,10 @@ class OpenTextPage extends StatelessWidget { children: [ ElevatedButton( style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.blue, + // ignore: deprecated_member_use onPrimary: Colors.white, ), child: const Text('Press to open a text file (json, txt)'), diff --git a/packages/file_selector/file_selector/example/lib/save_text_page.dart b/packages/file_selector/file_selector/example/lib/save_text_page.dart index 257add5b6de8..d2a8f30db06b 100644 --- a/packages/file_selector/file_selector/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/save_text_page.dart @@ -75,7 +75,10 @@ class SaveTextPage extends StatelessWidget { const SizedBox(height: 10), ElevatedButton( style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.blue, + // ignore: deprecated_member_use onPrimary: Colors.white, ), child: const Text('Press to save a text file'), diff --git a/packages/file_selector/file_selector_macos/CHANGELOG.md b/packages/file_selector/file_selector_macos/CHANGELOG.md index e909fe5a4377..8a4f217f8b9a 100644 --- a/packages/file_selector/file_selector_macos/CHANGELOG.md +++ b/packages/file_selector/file_selector_macos/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores deprecation warnings for upcoming styleFrom button API changes. + ## 0.8.2+2 * Updates references to the obsolete master branch. diff --git a/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart b/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart index a27ab2b40aba..a2a209dc9529 100644 --- a/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/get_directory_page.dart @@ -39,7 +39,10 @@ class GetDirectoryPage extends StatelessWidget { children: [ ElevatedButton( style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.blue, + // ignore: deprecated_member_use onPrimary: Colors.white, ), child: const Text('Press to ask user to choose a directory'), diff --git a/packages/file_selector/file_selector_macos/example/lib/home_page.dart b/packages/file_selector/file_selector_macos/example/lib/home_page.dart index 4d6ca7e6e6e3..a4b2ae1f63ea 100644 --- a/packages/file_selector/file_selector_macos/example/lib/home_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/home_page.dart @@ -12,7 +12,10 @@ class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.blue, + // ignore: deprecated_member_use onPrimary: Colors.white, ); return Scaffold( diff --git a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart index 1a05343b27ab..9e1d2074d5f2 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart @@ -46,7 +46,10 @@ class OpenImagePage extends StatelessWidget { children: [ ElevatedButton( style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.blue, + // ignore: deprecated_member_use onPrimary: Colors.white, ), child: const Text('Press to open an image file(png, jpg)'), diff --git a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart index 9c3c8e369f5b..21da8c22fa4d 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart @@ -50,7 +50,10 @@ class OpenMultipleImagesPage extends StatelessWidget { children: [ ElevatedButton( style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.blue, + // ignore: deprecated_member_use onPrimary: Colors.white, ), child: const Text('Press to open multiple images (png, jpg)'), diff --git a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart index 9adde400b5e1..05c6d166fb6f 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart @@ -43,7 +43,10 @@ class OpenTextPage extends StatelessWidget { children: [ ElevatedButton( style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.blue, + // ignore: deprecated_member_use onPrimary: Colors.white, ), child: const Text('Press to open a text file (json, txt)'), diff --git a/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart index a44a387c019b..3f215fea0a23 100644 --- a/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/save_text_page.dart @@ -67,7 +67,10 @@ class SaveTextPage extends StatelessWidget { const SizedBox(height: 10), ElevatedButton( style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.blue, + // ignore: deprecated_member_use onPrimary: Colors.white, ), onPressed: _saveFile, diff --git a/packages/file_selector/file_selector_windows/CHANGELOG.md b/packages/file_selector/file_selector_windows/CHANGELOG.md index d3278a534154..f5354b3286bc 100644 --- a/packages/file_selector/file_selector_windows/CHANGELOG.md +++ b/packages/file_selector/file_selector_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores deprecation warnings for upcoming styleFrom button API changes. + ## 0.8.2+2 * Updates references to the obsolete master branch. diff --git a/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart b/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart index 8fc1a9001465..0699dd121541 100644 --- a/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart @@ -39,7 +39,10 @@ class GetDirectoryPage extends StatelessWidget { children: [ ElevatedButton( style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.blue, + // ignore: deprecated_member_use onPrimary: Colors.white, ), child: const Text('Press to ask user to choose a directory'), diff --git a/packages/file_selector/file_selector_windows/example/lib/home_page.dart b/packages/file_selector/file_selector_windows/example/lib/home_page.dart index 4d6ca7e6e6e3..a4b2ae1f63ea 100644 --- a/packages/file_selector/file_selector_windows/example/lib/home_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/home_page.dart @@ -12,7 +12,10 @@ class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { final ButtonStyle style = ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.blue, + // ignore: deprecated_member_use onPrimary: Colors.white, ); return Scaffold( diff --git a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart index 1a05343b27ab..9e1d2074d5f2 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart @@ -46,7 +46,10 @@ class OpenImagePage extends StatelessWidget { children: [ ElevatedButton( style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.blue, + // ignore: deprecated_member_use onPrimary: Colors.white, ), child: const Text('Press to open an image file(png, jpg)'), diff --git a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart index 9c3c8e369f5b..21da8c22fa4d 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart @@ -50,7 +50,10 @@ class OpenMultipleImagesPage extends StatelessWidget { children: [ ElevatedButton( style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.blue, + // ignore: deprecated_member_use onPrimary: Colors.white, ), child: const Text('Press to open multiple images (png, jpg)'), diff --git a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart index 9adde400b5e1..05c6d166fb6f 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart @@ -43,7 +43,10 @@ class OpenTextPage extends StatelessWidget { children: [ ElevatedButton( style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.blue, + // ignore: deprecated_member_use onPrimary: Colors.white, ), child: const Text('Press to open a text file (json, txt)'), diff --git a/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart index 961e0fb2fbc3..9803f285a536 100644 --- a/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/save_text_page.dart @@ -67,7 +67,10 @@ class SaveTextPage extends StatelessWidget { const SizedBox(height: 10), ElevatedButton( style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.blue, + // ignore: deprecated_member_use onPrimary: Colors.white, ), onPressed: _saveFile, diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 61fd745f7871..08ba6dd206c4 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.0.6 + +* Ignores deprecation warnings for upcoming styleFrom button API changes. + ## 3.0.5 * Updates references to the obsolete master branch. diff --git a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart index 5dbdd8c14b29..017b72e976d5 100644 --- a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart @@ -260,6 +260,8 @@ class _MyAppState extends State<_MyApp> { : TextButton( style: TextButton.styleFrom( backgroundColor: Colors.green[800], + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.white, ), onPressed: () { @@ -362,6 +364,8 @@ class _MyAppState extends State<_MyApp> { TextButton( style: TextButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.white, ), onPressed: () => _inAppPurchase.restorePurchases(), diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index 8503b22aa299..8fb69206a95d 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 3.0.5 +version: 3.0.6 environment: sdk: ">=2.12.0 <3.0.0" 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 b7069cf40b04..966df5115e67 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.2+8 + +* Ignores deprecation warnings for upcoming styleFrom button API changes. + ## 0.2.2+7 * Updates references to the obsolete master branch. 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 b1d90d40b97c..337811a9acfd 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 @@ -266,6 +266,8 @@ class _MyAppState extends State<_MyApp> { : TextButton( style: TextButton.styleFrom( backgroundColor: Colors.green[800], + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.white, ), onPressed: () { 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 b9a2b86b96c2..b1e77666a0b5 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/plugins/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.2.2+7 +version: 0.2.2+8 environment: sdk: ">=2.14.0 <3.0.0" 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 30448aef0c84..a6897eaf1412 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.0+10 + +* Ignores deprecation warnings for upcoming styleFrom button API changes. + ## 0.3.0+9 * Updates references to the obsolete master branch. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart index 5ebf1b051942..5849b17d59d6 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart @@ -261,6 +261,8 @@ class _MyAppState extends State<_MyApp> { : TextButton( style: TextButton.styleFrom( backgroundColor: Colors.green[800], + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.white, ), onPressed: () { @@ -339,6 +341,8 @@ class _MyAppState extends State<_MyApp> { TextButton( style: TextButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: Colors.white, ), onPressed: () => _iapStoreKitPlatform.restorePurchases(), 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 fab5218c34de..ada883ce8f57 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 platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/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.0+9 +version: 0.3.0+10 environment: sdk: ">=2.14.0 <3.0.0" From 511850636df69a6dbc7331a2d715e1a43da73c71 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 14 Jun 2022 12:43:12 -0400 Subject: [PATCH 425/844] Roll Flutter from 712860d33b00 to 0cd8f3d02b8a (6 revisions) (#5967) --- .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 aa3ae00b60df..5fa4028e9929 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -712860d33b00bf3acf02118512901a6261ad5219 +0cd8f3d02b8a0c8a0a09ac1c075f52e4b7b1dafe From 4ed55ed2eb63eaaa0cfde2cddba11ed09c0aec17 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 14 Jun 2022 15:48:08 -0400 Subject: [PATCH 426/844] [google_maps_flutter] Switch to using new structured options interface methods (#5825) --- .../google_maps_flutter/CHANGELOG.md | 4 +- .../lib/src/controller.dart | 5 +- .../lib/src/google_map.dart | 156 +++++------------- .../google_maps_flutter/pubspec.yaml | 4 +- .../test/map_creation_test.dart | 20 +-- 5 files changed, 57 insertions(+), 132 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index d09aa4d8bd07..2bf5b17e1f80 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,5 +1,7 @@ -## NEXT +## 2.1.8 +* Switches to new platform interface versions of `buildView` and + `updateOptions`. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). ## 2.1.7 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 dfc6e579d560..71b1434eb293 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 @@ -102,10 +102,9 @@ class GoogleMapController { /// platform side. /// /// The returned [Future] completes after listeners have been notified. - Future _updateMapOptions(Map optionsUpdate) { - assert(optionsUpdate != null); + Future _updateMapConfiguration(MapConfiguration update) { return GoogleMapsFlutterPlatform.instance - .updateMapOptions(optionsUpdate, mapId: mapId); + .updateMapConfiguration(update, mapId: mapId); } /// Updates marker configuration. 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 8ecbfbbad499..b76d103a99f4 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 @@ -294,30 +294,34 @@ class _GoogleMapState extends State { Map _polygons = {}; Map _polylines = {}; Map _circles = {}; - late _GoogleMapOptions _googleMapOptions; + late MapConfiguration _mapConfiguration; @override Widget build(BuildContext context) { - return GoogleMapsFlutterPlatform.instance.buildViewWithTextDirection( + return GoogleMapsFlutterPlatform.instance.buildViewWithConfiguration( _mapId, onPlatformViewCreated, - textDirection: widget.layoutDirection ?? - Directionality.maybeOf(context) ?? - TextDirection.ltr, - initialCameraPosition: widget.initialCameraPosition, - markers: widget.markers, - polygons: widget.polygons, - polylines: widget.polylines, - circles: widget.circles, - gestureRecognizers: widget.gestureRecognizers, - mapOptions: _googleMapOptions.toMap(), + widgetConfiguration: MapWidgetConfiguration( + textDirection: widget.layoutDirection ?? + Directionality.maybeOf(context) ?? + TextDirection.ltr, + initialCameraPosition: widget.initialCameraPosition, + gestureRecognizers: widget.gestureRecognizers, + ), + mapObjects: MapObjects( + markers: widget.markers, + polygons: widget.polygons, + polylines: widget.polylines, + circles: widget.circles, + ), + mapConfiguration: _mapConfiguration, ); } @override void initState() { super.initState(); - _googleMapOptions = _GoogleMapOptions.fromWidget(widget); + _mapConfiguration = _configurationFromMapWidget(widget); _markers = keyByMarkerId(widget.markers); _polygons = keyByPolygonId(widget.polygons); _polylines = keyByPolylineId(widget.polylines); @@ -347,16 +351,15 @@ class _GoogleMapState extends State { } Future _updateOptions() async { - final _GoogleMapOptions newOptions = _GoogleMapOptions.fromWidget(widget); - final Map updates = - _googleMapOptions.updatesMap(newOptions); + final MapConfiguration newConfig = _configurationFromMapWidget(widget); + final MapConfiguration updates = newConfig.diffFrom(_mapConfiguration); if (updates.isEmpty) { return; } final GoogleMapController controller = await _controller.future; // ignore: unawaited_futures - controller._updateMapOptions(updates); - _googleMapOptions = newOptions; + controller._updateMapConfiguration(updates); + _mapConfiguration = newConfig; } Future _updateMarkers() async { @@ -524,98 +527,27 @@ class _GoogleMapState extends State { } } -/// Configuration options for the GoogleMaps user interface. -class _GoogleMapOptions { - _GoogleMapOptions.fromWidget(GoogleMap map) - : compassEnabled = map.compassEnabled, - mapToolbarEnabled = map.mapToolbarEnabled, - cameraTargetBounds = map.cameraTargetBounds, - mapType = map.mapType, - minMaxZoomPreference = map.minMaxZoomPreference, - rotateGesturesEnabled = map.rotateGesturesEnabled, - scrollGesturesEnabled = map.scrollGesturesEnabled, - tiltGesturesEnabled = map.tiltGesturesEnabled, - trackCameraPosition = map.onCameraMove != null, - zoomControlsEnabled = map.zoomControlsEnabled, - zoomGesturesEnabled = map.zoomGesturesEnabled, - liteModeEnabled = map.liteModeEnabled, - myLocationEnabled = map.myLocationEnabled, - myLocationButtonEnabled = map.myLocationButtonEnabled, - padding = map.padding, - indoorViewEnabled = map.indoorViewEnabled, - trafficEnabled = map.trafficEnabled, - buildingsEnabled = map.buildingsEnabled, - assert(!map.liteModeEnabled || Platform.isAndroid); - - final bool compassEnabled; - - final bool mapToolbarEnabled; - - final CameraTargetBounds cameraTargetBounds; - - final MapType mapType; - - final MinMaxZoomPreference minMaxZoomPreference; - - final bool rotateGesturesEnabled; - - final bool scrollGesturesEnabled; - - final bool tiltGesturesEnabled; - - final bool trackCameraPosition; - - final bool zoomControlsEnabled; - - final bool zoomGesturesEnabled; - - final bool liteModeEnabled; - - final bool myLocationEnabled; - - final bool myLocationButtonEnabled; - - final EdgeInsets padding; - - final bool indoorViewEnabled; - - final bool trafficEnabled; - - final bool buildingsEnabled; - - Map toMap() { - return { - 'compassEnabled': compassEnabled, - 'mapToolbarEnabled': mapToolbarEnabled, - 'cameraTargetBounds': cameraTargetBounds.toJson(), - 'mapType': mapType.index, - 'minMaxZoomPreference': minMaxZoomPreference.toJson(), - 'rotateGesturesEnabled': rotateGesturesEnabled, - 'scrollGesturesEnabled': scrollGesturesEnabled, - 'tiltGesturesEnabled': tiltGesturesEnabled, - 'zoomControlsEnabled': zoomControlsEnabled, - 'zoomGesturesEnabled': zoomGesturesEnabled, - 'liteModeEnabled': liteModeEnabled, - 'trackCameraPosition': trackCameraPosition, - 'myLocationEnabled': myLocationEnabled, - 'myLocationButtonEnabled': myLocationButtonEnabled, - 'padding': [ - padding.top, - padding.left, - padding.bottom, - padding.right, - ], - 'indoorEnabled': indoorViewEnabled, - 'trafficEnabled': trafficEnabled, - 'buildingsEnabled': buildingsEnabled, - }; - } - - Map updatesMap(_GoogleMapOptions newOptions) { - final Map prevOptionsMap = toMap(); - - return newOptions.toMap() - ..removeWhere( - (String key, dynamic value) => prevOptionsMap[key] == value); - } +/// Builds a [MapConfiguration] from the given [map]. +MapConfiguration _configurationFromMapWidget(GoogleMap map) { + assert(!map.liteModeEnabled || Platform.isAndroid); + return MapConfiguration( + compassEnabled: map.compassEnabled, + mapToolbarEnabled: map.mapToolbarEnabled, + cameraTargetBounds: map.cameraTargetBounds, + mapType: map.mapType, + minMaxZoomPreference: map.minMaxZoomPreference, + rotateGesturesEnabled: map.rotateGesturesEnabled, + scrollGesturesEnabled: map.scrollGesturesEnabled, + tiltGesturesEnabled: map.tiltGesturesEnabled, + trackCameraPosition: map.onCameraMove != null, + zoomControlsEnabled: map.zoomControlsEnabled, + zoomGesturesEnabled: map.zoomGesturesEnabled, + liteModeEnabled: map.liteModeEnabled, + myLocationEnabled: map.myLocationEnabled, + myLocationButtonEnabled: map.myLocationButtonEnabled, + padding: map.padding, + indoorViewEnabled: map.indoorViewEnabled, + trafficEnabled: map.trafficEnabled, + buildingsEnabled: map.buildingsEnabled, + ); } diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index 215a930c91b9..1a0dd4ebfc6f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/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.1.7 +version: 2.1.8 environment: sdk: ">=2.14.0 <3.0.0" @@ -21,7 +21,7 @@ dependencies: flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 - google_maps_flutter_platform_interface: ^2.1.2 + google_maps_flutter_platform_interface: ^2.2.0 dev_dependencies: flutter_test: diff --git a/packages/google_maps_flutter/google_maps_flutter/test/map_creation_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/map_creation_test.dart index 2b03d785953b..b34fccbfa422 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/map_creation_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/map_creation_test.dart @@ -7,8 +7,6 @@ import 'dart:async'; // ignore: unnecessary_import import 'dart:typed_data'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/gestures.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -90,8 +88,8 @@ class TestGoogleMapsFlutterPlatform extends GoogleMapsFlutterPlatform { Future init(int mapId) async {} @override - Future updateMapOptions( - Map optionsUpdate, { + Future updateMapConfiguration( + MapConfiguration update, { required int mapId, }) async {} @@ -278,18 +276,12 @@ class TestGoogleMapsFlutterPlatform extends GoogleMapsFlutterPlatform { } @override - Widget buildView( + Widget buildViewWithConfiguration( int creationId, PlatformViewCreatedCallback onPlatformViewCreated, { - required CameraPosition initialCameraPosition, - Set markers = const {}, - Set polygons = const {}, - Set polylines = const {}, - Set circles = const {}, - Set tileOverlays = const {}, - Set>? gestureRecognizers = - const >{}, - Map mapOptions = const {}, + required MapWidgetConfiguration widgetConfiguration, + MapObjects mapObjects = const MapObjects(), + MapConfiguration mapConfiguration = const MapConfiguration(), }) { onPlatformViewCreated(0); createdIds.add(creationId); From 4274bba637426d15589139f9c2ad1cf785cbf8da Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Tue, 14 Jun 2022 16:43:10 -0700 Subject: [PATCH 427/844] [url_launcher] Fixes call to setState after dispose. (#5963) --- .../url_launcher_web/CHANGELOG.md | 6 ++++ .../integration_test/link_widget_test.dart | 30 +++++++++++++++++++ .../url_launcher_web/lib/src/link.dart | 20 +++++++------ .../url_launcher_web/pubspec.yaml | 2 +- 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md index 068650be6d53..75c0819cefdc 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,3 +1,9 @@ +## 2.0.12 + +* Fixes call to `setState` after dispose on the `Link` widget. +[Issue](https://github.com/flutter/flutter/issues/102741). +* Removes unused `BuildContext` from the `LinkViewController`. + ## 2.0.11 * Minor fixes for new analysis options. 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 3b75e0556686..6b19861c5ce5 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 @@ -123,6 +123,36 @@ void main() { final html.Element anchor = _findSingleAnchor(); expect(anchor.hasAttribute('href'), false); }); + + testWidgets('can be created and disposed', (WidgetTester tester) async { + final Uri uri = Uri.parse('http://foobar'); + const int itemCount = 500; + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: MediaQuery( + data: const MediaQueryData(), + child: ListView.builder( + itemCount: itemCount, + itemBuilder: (_, int index) => WebLinkDelegate(TestLinkInfo( + uri: uri, + target: LinkTarget.defaultTarget, + builder: (BuildContext context, FollowLink? followLink) => + Text('#$index', textAlign: TextAlign.center), + )), + ), + ), + ), + ); + + await tester.pumpAndSettle(); + + await tester.scrollUntilVisible( + find.text('#${itemCount - 1}'), + 2500, + maxScrolls: 1000, + ); + }); }); } 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 eccd9aef80e3..112d07ea7571 100644 --- a/packages/url_launcher/url_launcher_web/lib/src/link.dart +++ b/packages/url_launcher/url_launcher_web/lib/src/link.dart @@ -76,7 +76,7 @@ class WebLinkDelegateState extends State { child: PlatformViewLink( viewType: linkViewType, onCreatePlatformView: (PlatformViewCreationParams params) { - _controller = LinkViewController.fromParams(params, context); + _controller = LinkViewController.fromParams(params); return _controller ..setUri(widget.link.uri) ..setTarget(widget.link.target); @@ -100,7 +100,7 @@ class WebLinkDelegateState extends State { /// Controls link views. class LinkViewController extends PlatformViewController { /// Creates a [LinkViewController] instance with the unique [viewId]. - LinkViewController(this.viewId, this.context) { + LinkViewController(this.viewId) { if (_instances.isEmpty) { // This is the first controller being created, attach the global click // listener. @@ -113,12 +113,17 @@ class LinkViewController extends PlatformViewController { /// platform view [params]. factory LinkViewController.fromParams( PlatformViewCreationParams params, - BuildContext context, ) { final int viewId = params.id; - final LinkViewController controller = LinkViewController(viewId, context); + final LinkViewController controller = LinkViewController(viewId); controller._initialize().then((_) { - params.onPlatformViewCreated(viewId); + /// Because _initialize is async, it can happen that [LinkViewController.dispose] + /// may get called before this `then` callback. + /// Check that the `controller` that was created by this factory is not + /// disposed before calling `onPlatformViewCreated`. + if (_instances[viewId] == controller) { + params.onPlatformViewCreated(viewId); + } }); return controller; } @@ -159,9 +164,6 @@ class LinkViewController extends PlatformViewController { @override final int viewId; - /// The context of the [Link] widget that created this controller. - final BuildContext context; - late html.Element _element; bool get _isInitialized => _element != null; @@ -208,7 +210,7 @@ class LinkViewController extends PlatformViewController { // browser handle it. event.preventDefault(); final String routeName = _uri.toString(); - pushRouteNameToFramework(context, routeName); + pushRouteNameToFramework(null, routeName); } Uri? _uri; diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index cef323035379..d0e0fa905d57 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_web description: Web platform implementation of url_launcher repository: https://github.com/flutter/plugins/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.0.11 +version: 2.0.12 environment: sdk: ">=2.12.0 <3.0.0" From 1c57e27d81059db4498b9e28764401cb26bad1ba Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 14 Jun 2022 20:38:09 -0400 Subject: [PATCH 428/844] [google_maps_flutter] Switch web to structured options (#5965) --- .../google_maps_flutter_web/CHANGELOG.md | 7 +- .../google_maps_controller_test.dart | 76 ++++++++++--------- .../google_maps_plugin_test.dart | 37 +++++---- .../google_maps_plugin_test.mocks.dart | 9 ++- .../lib/google_maps_flutter_web.dart | 1 - .../lib/src/convert.dart | 66 ++++++++-------- .../lib/src/google_maps_controller.dart | 65 ++++++++-------- .../lib/src/google_maps_flutter_web.dart | 33 +++----- .../google_maps_flutter_web/pubspec.yaml | 4 +- 9 files changed, 155 insertions(+), 143 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 95dfaede8d92..0f040ec5696c 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,10 @@ -## NEXT +## 0.4.0 +* Implements the new platform interface versions of `buildView` and + `updateOptions` with structured option types. +* **BREAKING CHANGE**: No longer implements the unstructured option dictionary + versions of those methods, so this version can only be used with + `google_maps_flutter` 2.1.8 or later. * Adds `const` constructor parameters in example tests. ## 0.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 ef9136afa961..dd2520d418f6 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 @@ -40,21 +40,17 @@ void main() { GoogleMapController _createController({ CameraPosition initialCameraPosition = const CameraPosition(target: LatLng(0, 0)), - Set markers = const {}, - Set polygons = const {}, - Set polylines = const {}, - Set circles = const {}, - Map options = const {}, + MapObjects mapObjects = const MapObjects(), + MapConfiguration mapConfiguration = const MapConfiguration(), }) { return GoogleMapController( mapId: mapId, streamController: stream, - initialCameraPosition: initialCameraPosition, - markers: markers, - polygons: polygons, - polylines: polylines, - circles: circles, - mapOptions: options, + widgetConfiguration: MapWidgetConfiguration( + initialCameraPosition: initialCameraPosition, + textDirection: TextDirection.ltr), + mapObjects: mapObjects, + mapConfiguration: mapConfiguration, ); } @@ -284,7 +280,8 @@ void main() { }); testWidgets('renders initial geometry', (WidgetTester tester) async { - controller = _createController(circles: { + controller = _createController( + mapObjects: MapObjects(circles: { const Circle( circleId: CircleId('circle-1'), zIndex: 1234, @@ -327,7 +324,7 @@ void main() { LatLng(43.354469, -5.851318), LatLng(43.354762, -5.850824), ]) - }); + })); controller.debugSetOverrides( circles: circles, @@ -363,9 +360,10 @@ void main() { testWidgets('empty infoWindow does not create InfoWindow instance.', (WidgetTester tester) async { - controller = _createController(markers: { + controller = _createController( + mapObjects: MapObjects(markers: { const Marker(markerId: MarkerId('marker-1')), - }); + })); controller.debugSetOverrides( markers: markers, @@ -385,10 +383,11 @@ void main() { capturedOptions = null; }); testWidgets('translates initial options', (WidgetTester tester) async { - controller = _createController(options: { - 'mapType': 2, - 'zoomControlsEnabled': true, - }); + controller = _createController( + mapConfiguration: const MapConfiguration( + mapType: MapType.satellite, + zoomControlsEnabled: true, + )); controller.debugSetOverrides( createMap: (_, gmaps.MapOptions options) { capturedOptions = options; @@ -407,9 +406,10 @@ void main() { testWidgets('disables gestureHandling with scrollGesturesEnabled false', (WidgetTester tester) async { - controller = _createController(options: { - 'scrollGesturesEnabled': false, - }); + controller = _createController( + mapConfiguration: const MapConfiguration( + scrollGesturesEnabled: false, + )); controller.debugSetOverrides( createMap: (_, gmaps.MapOptions options) { capturedOptions = options; @@ -426,9 +426,10 @@ void main() { testWidgets('disables gestureHandling with zoomGesturesEnabled false', (WidgetTester tester) async { - controller = _createController(options: { - 'zoomGesturesEnabled': false, - }); + controller = _createController( + mapConfiguration: const MapConfiguration( + zoomGesturesEnabled: false, + )); controller.debugSetOverrides( createMap: (_, gmaps.MapOptions options) { capturedOptions = options; @@ -477,9 +478,10 @@ void main() { testWidgets('initializes with traffic layer', (WidgetTester tester) async { - controller = _createController(options: { - 'trafficEnabled': true, - }); + controller = _createController( + mapConfiguration: const MapConfiguration( + trafficEnabled: true, + )); controller.debugSetOverrides(createMap: (_, __) => map); controller.init(); expect(controller.trafficLayer, isNotNull); @@ -505,9 +507,9 @@ void main() { group('updateRawOptions', () { testWidgets('can update `options`', (WidgetTester tester) async { - controller.updateRawOptions({ - 'mapType': 2, - }); + controller.updateMapConfiguration(const MapConfiguration( + mapType: MapType.satellite, + )); expect(map.mapTypeId, gmaps.MapTypeId.SATELLITE); }); @@ -515,15 +517,15 @@ void main() { testWidgets('can turn on/off traffic', (WidgetTester tester) async { expect(controller.trafficLayer, isNull); - controller.updateRawOptions({ - 'trafficEnabled': true, - }); + controller.updateMapConfiguration(const MapConfiguration( + trafficEnabled: true, + )); expect(controller.trafficLayer, isNotNull); - controller.updateRawOptions({ - 'trafficEnabled': false, - }); + controller.updateMapConfiguration(const MapConfiguration( + trafficEnabled: false, + )); expect(controller.trafficLayer, isNull); }); diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart index f28a6041da2a..e66a3f47c78c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart @@ -79,10 +79,13 @@ void main() { {}; plugin.debugSetMapById(cache); - final Widget widget = plugin.buildView( + final Widget widget = plugin.buildViewWithConfiguration( testMapId, onPlatformViewCreated, - initialCameraPosition: initialCameraPosition, + widgetConfiguration: const MapWidgetConfiguration( + initialCameraPosition: initialCameraPosition, + textDirection: TextDirection.ltr, + ), ); expect(widget, isA()); @@ -114,10 +117,13 @@ void main() { testMapId: controller, }); - final Widget widget = plugin.buildView( + final Widget widget = plugin.buildViewWithConfiguration( testMapId, onPlatformViewCreated, - initialCameraPosition: initialCameraPosition, + widgetConfiguration: const MapWidgetConfiguration( + initialCameraPosition: initialCameraPosition, + textDirection: TextDirection.ltr, + ), ); expect(widget, equals(expected)); @@ -130,10 +136,13 @@ void main() { {}; plugin.debugSetMapById(cache); - plugin.buildView( + plugin.buildViewWithConfiguration( testMapId, onPlatformViewCreated, - initialCameraPosition: initialCameraPosition, + widgetConfiguration: const MapWidgetConfiguration( + initialCameraPosition: initialCameraPosition, + textDirection: TextDirection.ltr, + ), ); // Simulate Google Maps JS SDK being "ready" @@ -176,11 +185,10 @@ void main() { await plugin.setMapStyle(mapStyle, mapId: 0); final dynamic captured = - verify(controller.updateRawOptions(captureThat(isMap))).captured[0]; + verify(controller.updateStyles(captureThat(isList))).captured[0]; - expect(captured, contains('styles')); final List styles = - captured['styles'] as List; + captured as List; expect(styles.length, 1); // Let's peek inside the styles... final gmaps.MapTypeStyle style = styles[0]; @@ -221,14 +229,13 @@ void main() { plugin.debugSetMapById({mapId: controller}); }); // Options - testWidgets('updateMapOptions', (WidgetTester tester) async { - final Map expectedMapOptions = { - 'someOption': 12345 - }; + testWidgets('updateMapConfiguration', (WidgetTester tester) async { + const MapConfiguration configuration = + MapConfiguration(mapType: MapType.satellite); - await plugin.updateMapOptions(expectedMapOptions, mapId: mapId); + await plugin.updateMapConfiguration(configuration, mapId: mapId); - verify(controller.updateRawOptions(expectedMapOptions)); + verify(controller.updateMapConfiguration(configuration)); }); // Geometry testWidgets('updateMarkers', (WidgetTester tester) async { 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 bbc92ffc6096..744552f45d4d 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 @@ -4,6 +4,7 @@ import 'dart:async' as _i2; +import 'package:google_maps/google_maps.dart' as _i5; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart' as _i3; import 'package:google_maps_flutter_web/google_maps_flutter_web.dart' as _i4; @@ -68,8 +69,12 @@ class MockGoogleMapController extends _i1.Mock void init() => super.noSuchMethod(Invocation.method(#init, []), returnValueForMissingStub: null); @override - void updateRawOptions(Map? optionsUpdate) => - super.noSuchMethod(Invocation.method(#updateRawOptions, [optionsUpdate]), + void updateMapConfiguration(_i3.MapConfiguration? update) => + super.noSuchMethod(Invocation.method(#updateMapConfiguration, [update]), + returnValueForMissingStub: null); + @override + void updateStyles(List<_i5.MapTypeStyle>? styles) => + super.noSuchMethod(Invocation.method(#updateStyles, [styles]), returnValueForMissingStub: null); @override _i2.Future<_i3.LatLngBounds> getVisibleRegion() => (super.noSuchMethod( 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 7ae646687f19..0650184a14d0 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 @@ -10,7 +10,6 @@ import 'dart:html'; import 'dart:js_util'; import 'package:flutter/foundation.dart'; -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.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 c6f3164ff207..250bb5468fa7 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 @@ -13,16 +13,6 @@ final gmaps.LatLngBounds _nullGmapsLatLngBounds = const String _defaultCssColor = '#000000'; const double _defaultCssOpacity = 0.0; -// Indices in the plugin side don't match with the ones -// in the gmaps lib. This translates from plugin -> gmaps. -final Map _mapTypeToMapTypeId = { - 0: gmaps.MapTypeId.ROADMAP, // "none" in the plugin - 1: gmaps.MapTypeId.ROADMAP, - 2: gmaps.MapTypeId.SATELLITE, - 3: gmaps.MapTypeId.TERRAIN, - 4: gmaps.MapTypeId.HYBRID, -}; - // Converts a [Color] into a valid CSS value #RRGGBB. String _getCssColor(Color color) { if (color == null) { @@ -55,49 +45,64 @@ double _getCssOpacity(Color color) { // indoorViewEnabled seems to not have an equivalent in web // buildingsEnabled seems to not have an equivalent in web // padding seems to behave differently in web than mobile. You can't move UI elements in web. -gmaps.MapOptions _rawOptionsToGmapsOptions(Map rawOptions) { +gmaps.MapOptions _configurationAndStyleToGmapsOptions( + MapConfiguration configuration, List styles) { final gmaps.MapOptions options = gmaps.MapOptions(); - if (_mapTypeToMapTypeId.containsKey(rawOptions['mapType'])) { - options.mapTypeId = _mapTypeToMapTypeId[rawOptions['mapType']]; + if (configuration.mapType != null) { + options.mapTypeId = _gmapTypeIDForPluginType(configuration.mapType!); } - if (rawOptions['minMaxZoomPreference'] != null) { - final List minMaxPreference = - rawOptions['minMaxZoomPreference']! as List; + final MinMaxZoomPreference? zoomPreference = + configuration.minMaxZoomPreference; + if (zoomPreference != null) { options - ..minZoom = minMaxPreference[0] as num? - ..maxZoom = minMaxPreference[1] as num?; + ..minZoom = zoomPreference.minZoom + ..maxZoom = zoomPreference.maxZoom; } - if (rawOptions['cameraTargetBounds'] != null) { + if (configuration.cameraTargetBounds != null) { // Needs gmaps.MapOptions.restriction and gmaps.MapRestriction // see: https://developers.google.com/maps/documentation/javascript/reference/map#MapOptions.restriction } - if (rawOptions['zoomControlsEnabled'] != null) { - options.zoomControl = rawOptions['zoomControlsEnabled'] as bool?; - } - - if (rawOptions['styles'] != null) { - options.styles = rawOptions['styles'] as List?; + if (configuration.zoomControlsEnabled != null) { + options.zoomControl = configuration.zoomControlsEnabled; } - if (rawOptions['scrollGesturesEnabled'] == false || - rawOptions['zoomGesturesEnabled'] == false) { + if (configuration.scrollGesturesEnabled == false || + configuration.zoomGesturesEnabled == false) { options.gestureHandling = 'none'; } else { options.gestureHandling = 'auto'; } - // These don't have any rawOptions entry, but they seem to be off in the native maps. + // These don't have any configuration entries, but they seem to be off in the + // native maps. options.mapTypeControl = false; options.fullscreenControl = false; options.streetViewControl = false; + options.styles = styles; + return options; } +gmaps.MapTypeId _gmapTypeIDForPluginType(MapType type) { + switch (type) { + case MapType.satellite: + return gmaps.MapTypeId.SATELLITE; + case MapType.terrain: + return gmaps.MapTypeId.TERRAIN; + case MapType.hybrid: + return gmaps.MapTypeId.HYBRID; + case MapType.normal: + case MapType.none: + default: + return gmaps.MapTypeId.ROADMAP; + } +} + gmaps.MapOptions _applyInitialPosition( CameraPosition initialPosition, gmaps.MapOptions options, @@ -111,11 +116,6 @@ gmaps.MapOptions _applyInitialPosition( return options; } -// Extracts the status of the traffic layer from the rawOptions map. -bool _isTrafficLayerEnabled(Map rawOptions) { - return rawOptions['trafficEnabled'] as bool? ?? false; -} - // The keys we'd expect to see in a serialized MapTypeStyle JSON object. final Set _mapStyleKeys = { 'elementType', 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 b7e902014281..a659fb218803 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 @@ -15,20 +15,17 @@ class GoogleMapController { GoogleMapController({ required int mapId, required StreamController> streamController, - required CameraPosition initialCameraPosition, - Set markers = const {}, - Set polygons = const {}, - Set polylines = const {}, - Set circles = const {}, - Map mapOptions = const {}, + required MapWidgetConfiguration widgetConfiguration, + MapObjects mapObjects = const MapObjects(), + MapConfiguration mapConfiguration = const MapConfiguration(), }) : _mapId = mapId, _streamController = streamController, - _initialCameraPosition = initialCameraPosition, - _markers = markers, - _polygons = polygons, - _polylines = polylines, - _circles = circles, - _rawMapOptions = mapOptions { + _initialCameraPosition = widgetConfiguration.initialCameraPosition, + _markers = mapObjects.markers, + _polygons = mapObjects.polygons, + _polylines = mapObjects.polylines, + _circles = mapObjects.circles, + _lastMapConfiguration = mapConfiguration { _circlesController = CirclesController(stream: _streamController); _polygonsController = PolygonsController(stream: _streamController); _polylinesController = PolylinesController(stream: _streamController); @@ -56,9 +53,10 @@ class GoogleMapController { final Set _polygons; final Set _polylines; final Set _circles; - // The raw options passed by the user, before converting to gmaps. + // The configuraiton passed by the user, before converting to gmaps. // Caching this allows us to re-create the map faithfully when needed. - Map _rawMapOptions = {}; + MapConfiguration _lastMapConfiguration = const MapConfiguration(); + List _lastStyles = const []; // Creates the 'viewType' for the _widget String _getViewType(int mapId) => 'plugins.flutter.io/google_maps_$mapId'; @@ -158,7 +156,8 @@ class GoogleMapController { /// Failure to call this method would result in the GMap not rendering at all, /// and most of the public methods on this class no-op'ing. void init() { - gmaps.MapOptions options = _rawOptionsToGmapsOptions(_rawMapOptions); + gmaps.MapOptions options = _configurationAndStyleToGmapsOptions( + _lastMapConfiguration, _lastStyles); // Initial position can only to be set here! options = _applyInitialPosition(_initialCameraPosition, options); @@ -177,7 +176,7 @@ class GoogleMapController { polylines: _polylines, ); - _setTrafficLayer(map, _isTrafficLayerEnabled(_rawMapOptions)); + _setTrafficLayer(map, _lastMapConfiguration.trafficEnabled ?? false); } // Funnels map gmap events into the plugin's stream controller. @@ -260,27 +259,33 @@ class GoogleMapController { _polylinesController!.addPolylines(polylines); } - // Merges new options coming from the plugin into the _rawMapOptions map. + // Merges new options coming from the plugin into _lastConfiguration. // - // Returns the updated _rawMapOptions object. - Map _mergeRawOptions(Map newOptions) { - _rawMapOptions = { - ..._rawMapOptions, - ...newOptions, - }; - return _rawMapOptions; + // Returns the updated _lastConfiguration object. + MapConfiguration _mergeConfigurations(MapConfiguration update) { + _lastMapConfiguration = _lastMapConfiguration.applyDiff(update); + return _lastMapConfiguration; } - /// Updates the map options from a `Map`. + /// Updates the map options from a [MapConfiguration]. /// - /// This method converts the map into the proper [gmaps.MapOptions] - void updateRawOptions(Map optionsUpdate) { + /// 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 Map newOptions = _mergeRawOptions(optionsUpdate); + final MapConfiguration newConfiguration = _mergeConfigurations(update); + final gmaps.MapOptions newOptions = + _configurationAndStyleToGmapsOptions(newConfiguration, _lastStyles); + + _setOptions(newOptions); + _setTrafficLayer(_googleMap!, newConfiguration.trafficEnabled ?? false); + } - _setOptions(_rawOptionsToGmapsOptions(newOptions)); - _setTrafficLayer(_googleMap!, _isTrafficLayerEnabled(newOptions)); + /// Updates the map options with a new list of [styles]. + void updateStyles(List styles) { + _lastStyles = styles; + _setOptions( + _configurationAndStyleToGmapsOptions(_lastMapConfiguration, styles)); } // Sets new [gmaps.MapOptions] on the wrapped map. 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 043952d176a0..c2085a2bddfc 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 @@ -47,11 +47,11 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { /// This attempts to merge the new `optionsUpdate` passed in, with the previous /// options passed to the map (in other updates, or when creating it). @override - Future updateMapOptions( - Map optionsUpdate, { + Future updateMapConfiguration( + MapConfiguration update, { required int mapId, }) async { - _map(mapId).updateRawOptions(optionsUpdate); + _map(mapId).updateMapConfiguration(update); } /// Applies the passed in `markerUpdates` to the `mapId`. @@ -135,9 +135,7 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { String? mapStyle, { required int mapId, }) async { - _map(mapId).updateRawOptions({ - 'styles': _mapStyles(mapStyle), - }); + _map(mapId).updateStyles(_mapStyles(mapStyle)); } /// Returns the bounds of the current viewport. @@ -289,18 +287,12 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { } @override - Widget buildView( + Widget buildViewWithConfiguration( int creationId, PlatformViewCreatedCallback onPlatformViewCreated, { - required CameraPosition initialCameraPosition, - Set markers = const {}, - Set polygons = const {}, - Set polylines = const {}, - Set circles = const {}, - Set tileOverlays = const {}, - Set>? gestureRecognizers = - const >{}, - Map mapOptions = const {}, + required MapWidgetConfiguration widgetConfiguration, + MapObjects mapObjects = const MapObjects(), + MapConfiguration mapConfiguration = const MapConfiguration(), }) { // Bail fast if we've already rendered this map ID... if (_mapById[creationId]?.widget != null) { @@ -311,14 +303,11 @@ class GoogleMapsPlugin extends GoogleMapsFlutterPlatform { StreamController>.broadcast(); final GoogleMapController mapController = GoogleMapController( - initialCameraPosition: initialCameraPosition, mapId: creationId, streamController: controller, - markers: markers, - polygons: polygons, - polylines: polylines, - circles: circles, - mapOptions: mapOptions, + widgetConfiguration: widgetConfiguration, + mapObjects: mapObjects, + mapConfiguration: mapConfiguration, )..init(); // Initialize the controller _mapById[creationId] = mapController; 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 b46f7561e78d..a05be66b83ba 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/plugins/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.3.3 +version: 0.4.0 environment: sdk: ">=2.12.0 <3.0.0" @@ -22,7 +22,7 @@ dependencies: flutter_web_plugins: sdk: flutter google_maps: ^6.1.0 - google_maps_flutter_platform_interface: ^2.1.2 + google_maps_flutter_platform_interface: ^2.2.0 sanitize_html: ^2.0.0 stream_transform: ^2.0.0 From a52dec7c87508eb7c4d98d4a6364b910b79fb542 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 14 Jun 2022 18:28:11 -0700 Subject: [PATCH 429/844] [webview_flutter_wkwebview] Fix bug of overriding default values of `NSURLRequest` (#5969) --- .../example/ios/RunnerTests/FWFDataConvertersTests.m | 10 ++++++++++ .../ios/Classes/FWFDataConverters.m | 8 ++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) 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 1fc5a95398bd..ca7d6f938599 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 @@ -25,6 +25,16 @@ - (void)testFWFNSURLRequestFromRequestData { XCTAssertEqualObjects(request.allHTTPHeaderFields, @{@"a" : @"header"}); } +- (void)testFWFNSURLRequestFromRequestDataDoesNotOverrideDefaultValuesWithNull { + NSURLRequest *request = + FWFNSURLRequestFromRequestData([FWFNSUrlRequestData makeWithUrl:@"https://flutter.dev" + httpMethod:nil + httpBody:nil + allHttpHeaderFields:@{}]); + + XCTAssertEqualObjects(request.HTTPMethod, @"GET"); +} + - (void)testFWFNSHTTPCookieFromCookieData { NSHTTPCookie *cookie = FWFNSHTTPCookieFromCookieData([FWFNSHttpCookieData makeWithPropertyKeys:@[ [FWFNSHttpCookiePropertyKeyEnumData 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 759bfedc1621..8ecc9d303000 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m @@ -17,8 +17,12 @@ return nil; } - [request setHTTPMethod:data.httpMethod]; - [request setHTTPBody:data.httpBody.data]; + if (data.httpMethod) { + [request setHTTPMethod:data.httpMethod]; + } + if (data.httpBody) { + [request setHTTPBody:data.httpBody.data]; + } [request setAllHTTPHeaderFields:data.allHttpHeaderFields]; return request; From 3a7a163caaac8c2aea7b5c960a03e332b92fa4a3 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 14 Jun 2022 20:13:09 -0700 Subject: [PATCH 430/844] [webview_flutter_wkwebview] Fixes bug where an `NSError` not an `NSErrorData` was returned (#5973) --- .../ios/RunnerTests/FWFWebViewHostApiTests.m | 37 +++++++++++++++++++ .../ios/Classes/FWFWebViewHostApi.m | 4 +- .../lib/src/web_kit/web_kit_api_impls.dart | 24 +++++++++--- .../lib/src/web_kit_webview_widget.dart | 4 ++ .../test/src/web_kit/web_kit_test.dart | 25 +++++++++++++ .../test/src/web_kit_webview_widget_test.dart | 36 ++++++++++++++++++ 6 files changed, 123 insertions(+), 7 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m index 0c71e3391dbb..ca17cc212d31 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m @@ -351,4 +351,41 @@ - (void)testEvaluateJavaScript { XCTAssertEqualObjects(returnValue, @"result"); XCTAssertNil(returnError); } + +- (void)testEvaluateJavaScriptReturnsNSErrorData { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + OCMStub([mockWebView + evaluateJavaScript:@"runJavaScript" + completionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], + [NSError errorWithDomain:@"errorDomain" + code:0 + userInfo:@{ + NSLocalizedDescriptionKey : + @"description" + }], + nil])]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostAPI = + [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + + NSString __block *returnValue; + FlutterError __block *returnError; + [hostAPI evaluateJavaScriptForWebViewWithIdentifier:@0 + javaScriptString:@"runJavaScript" + completion:^(id result, FlutterError *error) { + returnValue = result; + returnError = error; + }]; + + XCTAssertNil(returnValue); + FWFNSErrorData *errorData = returnError.details; + XCTAssertTrue([errorData isKindOfClass:[FWFNSErrorData class]]); + XCTAssertEqualObjects(errorData.code, @0); + XCTAssertEqualObjects(errorData.domain, @"errorDomain"); + XCTAssertEqualObjects(errorData.localizedDescription, @"description"); +} @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m index 66149dd9ed37..2962aadd1647 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m @@ -141,7 +141,7 @@ - (void)evaluateJavaScriptForWebViewWithIdentifier:(nonnull NSNumber *)identifie if (!result || [result isKindOfClass:[NSString class]] || [result isKindOfClass:[NSNumber class]]) { returnValue = result; - } else { + } else if (![result isKindOfClass:[NSNull class]]) { NSString *className = NSStringFromClass([result class]); NSLog(@"Return type of evaluateJavaScript is not directly supported: %@. Returned " @"description of value.", @@ -151,7 +151,7 @@ - (void)evaluateJavaScriptForWebViewWithIdentifier:(nonnull NSNumber *)identifie } else { flutterError = [FlutterError errorWithCode:@"FWFEvaluateJavaScriptError" message:@"Failed evaluating JavaScript." - details:error]; + details:FWFNSErrorDataFromNSError(error)]; } completion(returnValue, flutterError); 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 c27fc3e73aee..4eb173238e71 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 @@ -908,11 +908,25 @@ class WKWebViewHostApiImpl extends WKWebViewHostApi { Future evaluateJavaScriptForInstances( WKWebView instance, String javaScriptString, - ) { - return evaluateJavaScript( - instanceManager.getIdentifier(instance)!, - javaScriptString, - ); + ) async { + try { + final Object? result = await evaluateJavaScript( + instanceManager.getIdentifier(instance)!, + javaScriptString, + ); + return result; + } on PlatformException catch (exception) { + if (exception.details is! NSErrorData) { + rethrow; + } + + throw PlatformException( + code: exception.code, + message: exception.message, + stacktrace: exception.stacktrace, + details: (exception.details as NSErrorData).toNSError(), + ); + } } /// Calls [setNavigationDelegate] with the ids of the provided object instances. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 05a77f56f851..b193fa8c1fac 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -573,6 +573,10 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { return '""'; } return '(null)'; + } else if (value is bool) { + return value ? '1' : '0'; + } else if (value is double && value.truncate() == value) { + return value.truncate().toString(); } else if (value is List) { final List stringValues = []; for (final Object? listValue in value) { 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 d71a6c5e0838..1d6c1f0e67a1 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 @@ -4,6 +4,7 @@ import 'dart:async'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; @@ -815,6 +816,30 @@ void main() { .thenAnswer((_) => Future.value('stopstop')); expect(webView.evaluateJavaScript('gogo'), completion('stopstop')); }); + + test('evaluateJavaScript returns NSError', () { + when(mockPlatformHostApi.evaluateJavaScript(webViewInstanceId, 'gogo')) + .thenThrow( + PlatformException( + code: '', + details: NSErrorData( + code: 0, + domain: 'domain', + localizedDescription: 'desc', + ), + ), + ); + expect( + webView.evaluateJavaScript('gogo'), + throwsA( + isA().having( + (PlatformException exception) => exception.details, + 'details', + isA(), + ), + ), + ); + }); }); group('WKUIDelegate', () { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 24155d2a05e1..905fdb542063 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -634,6 +634,42 @@ void main() { ); }); + testWidgets('evaluateJavascript with bool return value', + (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value(true), + ); + // The legacy implementation of webview_flutter_wkwebview would convert + // objects to strings before returning them to Dart. This verifies bool + // is represented the way it is in Objective-C. + // `NSNumber.description` converts bool values to a 1 or 0. + expect( + testController.evaluateJavascript('runJavaScript'), + completion('1'), + ); + }); + + testWidgets('evaluateJavascript with double return value', + (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value(1.0), + ); + // The legacy implementation of webview_flutter_wkwebview would convert + // objects to strings before returning them to Dart. This verifies + // double is represented the way it is in Objective-C. If a double + // doesn't contain any decimal values, it gets truncated to an int. + // This should be happenning because NSNumber convertes float values + // with no decimals to an int when using `NSNumber.description`. + expect( + testController.evaluateJavascript('runJavaScript'), + completion('1'), + ); + }); + testWidgets('evaluateJavascript with list return value', (WidgetTester tester) async { await buildWidget(tester); From 1410600d0c175db1c96a1e13b6e1029a77513094 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 14 Jun 2022 20:13:12 -0700 Subject: [PATCH 431/844] [webview_flutter_wkwebview] Implements `currentUrl` (#5974) --- .../lib/src/web_kit_webview_widget.dart | 3 +++ .../test/src/web_kit_webview_widget_test.dart | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index b193fa8c1fac..a5c7c8f86ab6 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -354,6 +354,9 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { @override Future getTitle() => webView.getTitle(); + @override + Future currentUrl() => webView.getUrl(); + @override Future scrollTo(int x, int y) async { webView.scrollView.setContentOffset(Point( diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 905fdb542063..a1656badad01 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -783,6 +783,14 @@ void main() { expect(testController.getTitle(), completion('Web Title')); }); + testWidgets('currentUrl', (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.getUrl()) + .thenAnswer((_) => Future.value('myUrl.com')); + expect(testController.currentUrl(), completion('myUrl.com')); + }); + testWidgets('scrollTo', (WidgetTester tester) async { await buildWidget(tester); From 3b183f790dc397d9f89d5eeeb7e952bc7629ed53 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 15 Jun 2022 06:58:10 -0700 Subject: [PATCH 432/844] [webview_flutter_wkwebview] Return an NSNumber that represents a bool (#5968) --- .../example/ios/RunnerTests/FWFWebsiteDataStoreHostApiTests.m | 2 ++ .../ios/Classes/FWFWebsiteDataStoreHostApi.m | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebsiteDataStoreHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebsiteDataStoreHostApiTests.m index 18bc21facd21..c518f55194c4 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebsiteDataStoreHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebsiteDataStoreHostApiTests.m @@ -70,6 +70,8 @@ - (void)testRemoveDataOfTypes { blockError = error; }]; XCTAssertEqualObjects(returnValue, @YES); + // Asserts whether the NSNumber will be deserialized by the standard codec as a boolean. + XCTAssertEqual(CFGetTypeID((__bridge CFTypeRef)(returnValue)), CFBooleanGetTypeID()); XCTAssertNil(blockError); } @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m index 4587917ba640..0fd85e010a0a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m @@ -60,7 +60,7 @@ - (void)createDefaultDataStoreWithIdentifier:(nonnull NSNumber *)identifier modifiedSince:[NSDate dateWithTimeIntervalSince1970: modificationTimeInSecondsSinceEpoch.doubleValue] completionHandler:^{ - completion(@(records.count > 0), nil); + completion([NSNumber numberWithBool:(records.count > 0)], nil); }]; }]; } From 6b4c8fc5df9f3a1613bb8e1f22bd0c573c7686b8 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 15 Jun 2022 07:43:11 -0700 Subject: [PATCH 433/844] [webview_flutter_wkwebview] Instantiate a `WKWebViewConfiguration` in `WKUIDelegate.onCreateWebView` (#5971) --- .../RunnerTests/FWFUIDelegateHostApiTests.m | 10 +- .../ios/Classes/FWFGeneratedWebKitApis.h | 10 +- .../ios/Classes/FWFGeneratedWebKitApis.m | 124 +++++++++++++++++- .../ios/Classes/FWFUIDelegateHostApi.h | 3 + .../ios/Classes/FWFUIDelegateHostApi.m | 17 ++- .../Classes/FWFWebViewConfigurationHostApi.h | 12 ++ .../Classes/FWFWebViewConfigurationHostApi.m | 23 ++++ .../lib/src/common/web_kit.pigeon.dart | 35 ++++- .../lib/src/web_kit/web_kit_api_impls.dart | 42 +++++- .../pigeons/web_kit.dart | 9 ++ .../test/src/common/test_web_kit.pigeon.dart | 2 +- .../src/foundation/foundation_test.mocks.dart | 2 +- .../test/src/ui_kit/ui_kit_test.mocks.dart | 2 +- .../test/src/web_kit/web_kit_test.dart | 14 ++ .../test/src/web_kit/web_kit_test.mocks.dart | 2 +- .../web_kit_cookie_manager_test.mocks.dart | 2 +- .../web_kit_webview_widget_test.mocks.dart | 2 +- 17 files changed, 293 insertions(+), 18 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m index 17cb4367b3aa..eec7a2b5a6dc 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m @@ -68,7 +68,13 @@ - (void)testOnCreateWebViewForDelegateWithIdentifier { [instanceManager addDartCreatedInstance:mockWebView withIdentifier:1]; WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init]; - [instanceManager addDartCreatedInstance:configuration withIdentifier:2]; + id mockConfigurationFlutterApi = OCMPartialMock(mockFlutterAPI.webViewConfigurationFlutterApi); + NSNumber *__block configurationIdentifier; + OCMStub([mockConfigurationFlutterApi createWithIdentifier:[OCMArg checkWithBlock:^BOOL(id value) { + configurationIdentifier = value; + return YES; + }] + completion:OCMOCK_ANY]); WKNavigationAction *mockNavigationAction = OCMClassMock([WKNavigationAction class]); OCMStub([mockNavigationAction request]) @@ -85,7 +91,7 @@ - (void)testOnCreateWebViewForDelegateWithIdentifier { OCMVerify([mockFlutterAPI onCreateWebViewForDelegateWithIdentifier:@0 webViewIdentifier:@1 - configurationIdentifier:@2 + configurationIdentifier:configurationIdentifier navigationAction:[OCMArg isKindOfClass:[FWFWKNavigationActionData class]] completion:OCMOCK_ANY]); 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 ebd29f5cacad..b0856ae11038 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 (v3.1.4), do not edit directly. +// Autogenerated from Pigeon (v3.1.5), do not edit directly. // See also: https://pub.dev/packages/pigeon #import @protocol FlutterBinaryMessenger; @@ -297,6 +297,14 @@ extern void FWFWKWebViewConfigurationHostApiSetup( id binaryMessenger, NSObject *_Nullable api); +/// The codec used by FWFWKWebViewConfigurationFlutterApi. +NSObject *FWFWKWebViewConfigurationFlutterApiGetCodec(void); + +@interface FWFWKWebViewConfigurationFlutterApi : NSObject +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger; +- (void)createWithIdentifier:(NSNumber *)identifier + completion:(void (^)(NSError *_Nullable))completion; +@end /// The codec used by FWFWKUserContentControllerHostApi. NSObject *FWFWKUserContentControllerHostApiGetCodec(void); 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 592fa87ac9d1..96c59987e841 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 (v3.1.4), do not edit directly. +// Autogenerated from Pigeon (v3.1.5), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "FWFGeneratedWebKitApis.h" #import @@ -35,58 +35,72 @@ static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { @interface FWFNSKeyValueObservingOptionsEnumData () + (FWFNSKeyValueObservingOptionsEnumData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFNSKeyValueObservingOptionsEnumData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFNSKeyValueChangeKeyEnumData () + (FWFNSKeyValueChangeKeyEnumData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFNSKeyValueChangeKeyEnumData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFWKUserScriptInjectionTimeEnumData () + (FWFWKUserScriptInjectionTimeEnumData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFWKUserScriptInjectionTimeEnumData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFWKAudiovisualMediaTypeEnumData () + (FWFWKAudiovisualMediaTypeEnumData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFWKAudiovisualMediaTypeEnumData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFWKWebsiteDataTypeEnumData () + (FWFWKWebsiteDataTypeEnumData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFWKWebsiteDataTypeEnumData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFWKNavigationActionPolicyEnumData () + (FWFWKNavigationActionPolicyEnumData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFWKNavigationActionPolicyEnumData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFNSHttpCookiePropertyKeyEnumData () + (FWFNSHttpCookiePropertyKeyEnumData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFNSHttpCookiePropertyKeyEnumData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFNSUrlRequestData () + (FWFNSUrlRequestData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFNSUrlRequestData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFWKUserScriptData () + (FWFWKUserScriptData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFWKUserScriptData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFWKNavigationActionData () + (FWFWKNavigationActionData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFWKNavigationActionData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFWKFrameInfoData () + (FWFWKFrameInfoData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFWKFrameInfoData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFNSErrorData () + (FWFNSErrorData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFNSErrorData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFWKScriptMessageData () + (FWFWKScriptMessageData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFWKScriptMessageData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @interface FWFNSHttpCookieData () + (FWFNSHttpCookieData *)fromMap:(NSDictionary *)dict; ++ (nullable FWFNSHttpCookieData *)nullableFromMap:(NSDictionary *)dict; - (NSDictionary *)toMap; @end @@ -103,6 +117,9 @@ + (FWFNSKeyValueObservingOptionsEnumData *)fromMap:(NSDictionary *)dict { pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; return pigeonResult; } ++ (nullable FWFNSKeyValueObservingOptionsEnumData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFNSKeyValueObservingOptionsEnumData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"value" : @(self.value), @@ -121,6 +138,9 @@ + (FWFNSKeyValueChangeKeyEnumData *)fromMap:(NSDictionary *)dict { pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; return pigeonResult; } ++ (nullable FWFNSKeyValueChangeKeyEnumData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFNSKeyValueChangeKeyEnumData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"value" : @(self.value), @@ -141,6 +161,9 @@ + (FWFWKUserScriptInjectionTimeEnumData *)fromMap:(NSDictionary *)dict { pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; return pigeonResult; } ++ (nullable FWFWKUserScriptInjectionTimeEnumData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFWKUserScriptInjectionTimeEnumData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"value" : @(self.value), @@ -161,6 +184,9 @@ + (FWFWKAudiovisualMediaTypeEnumData *)fromMap:(NSDictionary *)dict { pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; return pigeonResult; } ++ (nullable FWFWKAudiovisualMediaTypeEnumData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFWKAudiovisualMediaTypeEnumData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"value" : @(self.value), @@ -179,6 +205,9 @@ + (FWFWKWebsiteDataTypeEnumData *)fromMap:(NSDictionary *)dict { pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; return pigeonResult; } ++ (nullable FWFWKWebsiteDataTypeEnumData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFWKWebsiteDataTypeEnumData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"value" : @(self.value), @@ -199,6 +228,9 @@ + (FWFWKNavigationActionPolicyEnumData *)fromMap:(NSDictionary *)dict { pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; return pigeonResult; } ++ (nullable FWFWKNavigationActionPolicyEnumData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFWKNavigationActionPolicyEnumData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"value" : @(self.value), @@ -219,6 +251,9 @@ + (FWFNSHttpCookiePropertyKeyEnumData *)fromMap:(NSDictionary *)dict { pigeonResult.value = [GetNullableObject(dict, @"value") integerValue]; return pigeonResult; } ++ (nullable FWFNSHttpCookiePropertyKeyEnumData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFNSHttpCookiePropertyKeyEnumData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"value" : @(self.value), @@ -248,6 +283,9 @@ + (FWFNSUrlRequestData *)fromMap:(NSDictionary *)dict { NSAssert(pigeonResult.allHttpHeaderFields != nil, @""); return pigeonResult; } ++ (nullable FWFNSUrlRequestData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFNSUrlRequestData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"url" : (self.url ?: [NSNull null]), @@ -272,12 +310,15 @@ + (FWFWKUserScriptData *)fromMap:(NSDictionary *)dict { FWFWKUserScriptData *pigeonResult = [[FWFWKUserScriptData alloc] init]; pigeonResult.source = GetNullableObject(dict, @"source"); NSAssert(pigeonResult.source != nil, @""); - pigeonResult.injectionTime = - [FWFWKUserScriptInjectionTimeEnumData fromMap:GetNullableObject(dict, @"injectionTime")]; + pigeonResult.injectionTime = [FWFWKUserScriptInjectionTimeEnumData + nullableFromMap:GetNullableObject(dict, @"injectionTime")]; pigeonResult.isMainFrameOnly = GetNullableObject(dict, @"isMainFrameOnly"); NSAssert(pigeonResult.isMainFrameOnly != nil, @""); return pigeonResult; } ++ (nullable FWFWKUserScriptData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFWKUserScriptData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"source" : (self.source ?: [NSNull null]), @@ -297,12 +338,16 @@ + (instancetype)makeWithRequest:(FWFNSUrlRequestData *)request } + (FWFWKNavigationActionData *)fromMap:(NSDictionary *)dict { FWFWKNavigationActionData *pigeonResult = [[FWFWKNavigationActionData alloc] init]; - pigeonResult.request = [FWFNSUrlRequestData fromMap:GetNullableObject(dict, @"request")]; + pigeonResult.request = [FWFNSUrlRequestData nullableFromMap:GetNullableObject(dict, @"request")]; NSAssert(pigeonResult.request != nil, @""); - pigeonResult.targetFrame = [FWFWKFrameInfoData fromMap:GetNullableObject(dict, @"targetFrame")]; + pigeonResult.targetFrame = + [FWFWKFrameInfoData nullableFromMap:GetNullableObject(dict, @"targetFrame")]; NSAssert(pigeonResult.targetFrame != nil, @""); return pigeonResult; } ++ (nullable FWFWKNavigationActionData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFWKNavigationActionData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"request" : (self.request ? [self.request toMap] : [NSNull null]), @@ -323,6 +368,9 @@ + (FWFWKFrameInfoData *)fromMap:(NSDictionary *)dict { NSAssert(pigeonResult.isMainFrame != nil, @""); return pigeonResult; } ++ (nullable FWFWKFrameInfoData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFWKFrameInfoData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"isMainFrame" : (self.isMainFrame ?: [NSNull null]), @@ -350,6 +398,9 @@ + (FWFNSErrorData *)fromMap:(NSDictionary *)dict { NSAssert(pigeonResult.localizedDescription != nil, @""); return pigeonResult; } ++ (nullable FWFNSErrorData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFNSErrorData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"code" : (self.code ?: [NSNull null]), @@ -373,6 +424,9 @@ + (FWFWKScriptMessageData *)fromMap:(NSDictionary *)dict { pigeonResult.body = GetNullableObject(dict, @"body"); return pigeonResult; } ++ (nullable FWFWKScriptMessageData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFWKScriptMessageData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"name" : (self.name ?: [NSNull null]), @@ -397,6 +451,9 @@ + (FWFNSHttpCookieData *)fromMap:(NSDictionary *)dict { NSAssert(pigeonResult.propertyValues != nil, @""); return pigeonResult; } ++ (nullable FWFNSHttpCookieData *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FWFNSHttpCookieData fromMap:dict] : nil; +} - (NSDictionary *)toMap { return @{ @"propertyKeys" : (self.propertyKeys ?: [NSNull null]), @@ -899,6 +956,63 @@ void FWFWKWebViewConfigurationHostApiSetup(id binaryMess } } } +@interface FWFWKWebViewConfigurationFlutterApiCodecReader : FlutterStandardReader +@end +@implementation FWFWKWebViewConfigurationFlutterApiCodecReader +@end + +@interface FWFWKWebViewConfigurationFlutterApiCodecWriter : FlutterStandardWriter +@end +@implementation FWFWKWebViewConfigurationFlutterApiCodecWriter +@end + +@interface FWFWKWebViewConfigurationFlutterApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FWFWKWebViewConfigurationFlutterApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FWFWKWebViewConfigurationFlutterApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FWFWKWebViewConfigurationFlutterApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FWFWKWebViewConfigurationFlutterApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FWFWKWebViewConfigurationFlutterApiCodecReaderWriter *readerWriter = + [[FWFWKWebViewConfigurationFlutterApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +@interface FWFWKWebViewConfigurationFlutterApi () +@property(nonatomic, strong) NSObject *binaryMessenger; +@end + +@implementation FWFWKWebViewConfigurationFlutterApi + +- (instancetype)initWithBinaryMessenger:(NSObject *)binaryMessenger { + self = [super init]; + if (self) { + _binaryMessenger = binaryMessenger; + } + return self; +} +- (void)createWithIdentifier:(NSNumber *)arg_identifier + completion:(void (^)(NSError *_Nullable))completion { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.WKWebViewConfigurationFlutterApi.create" + binaryMessenger:self.binaryMessenger + codec:FWFWKWebViewConfigurationFlutterApiGetCodec()]; + [channel sendMessage:@[ arg_identifier ?: [NSNull null] ] + reply:^(id reply) { + completion(nil); + }]; +} +@end @interface FWFWKUserContentControllerHostApiCodecReader : FlutterStandardReader @end @implementation FWFWKUserContentControllerHostApiCodecReader 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 65c148e6b0b1..1bb65914e097 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h @@ -8,6 +8,7 @@ #import "FWFGeneratedWebKitApis.h" #import "FWFInstanceManager.h" #import "FWFObjectHostApi.h" +#import "FWFWebViewConfigurationHostApi.h" NS_ASSUME_NONNULL_BEGIN @@ -17,6 +18,8 @@ NS_ASSUME_NONNULL_BEGIN * Handles making callbacks to Dart for a WKUIDelegate. */ @interface FWFUIDelegateFlutterApiImpl : FWFWKUIDelegateFlutterApi +@property(readonly, nonatomic) + FWFWebViewConfigurationFlutterApiImpl *webViewConfigurationFlutterApi; - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m index 4ec8b583b1f8..36c4b2feefe3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m @@ -4,10 +4,11 @@ #import "FWFUIDelegateHostApi.h" #import "FWFDataConverters.h" -#import "FWFWebViewConfigurationHostApi.h" @interface FWFUIDelegateFlutterApiImpl () -// This reference must be weak to prevent a circular reference with the objects it stores. +// BinaryMessenger and InstanceManager must be weak to prevent a circular reference +// with the objects it stores. +@property(nonatomic, weak) id binaryMessenger; @property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @@ -16,7 +17,11 @@ - (instancetype)initWithBinaryMessenger:(id)binaryMessen instanceManager:(FWFInstanceManager *)instanceManager { self = [self initWithBinaryMessenger:binaryMessenger]; if (self) { + _binaryMessenger = binaryMessenger; _instanceManager = instanceManager; + _webViewConfigurationFlutterApi = + [[FWFWebViewConfigurationFlutterApiImpl alloc] initWithBinaryMessenger:binaryMessenger + instanceManager:instanceManager]; } return self; } @@ -30,10 +35,18 @@ - (void)onCreateWebViewForDelegate:(FWFUIDelegate *)instance configuration:(WKWebViewConfiguration *)configuration navigationAction:(WKNavigationAction *)navigationAction completion:(void (^)(NSError *_Nullable))completion { + if (![self.instanceManager containsInstance:configuration]) { + [self.webViewConfigurationFlutterApi createWithConfiguration:configuration + completion:^(NSError *error) { + NSAssert(!error, @"%@", error); + }]; + } + NSNumber *configurationIdentifier = @([self.instanceManager identifierWithStrongReferenceForInstance:configuration]); FWFWKNavigationActionData *navigationActionData = FWFWKNavigationActionDataFromNavigationAction(navigationAction); + [self onCreateWebViewForDelegateWithIdentifier:@([self identifierForDelegate:instance]) webViewIdentifier: @([self.instanceManager 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 4c01d9354107..e2279b48c409 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.h @@ -10,6 +10,18 @@ NS_ASSUME_NONNULL_BEGIN +/** + * Flutter api implementation for WKWebViewConfiguration. + * + * Handles making callbacks to Dart for a WKWebViewConfiguration. + */ +@interface FWFWebViewConfigurationFlutterApiImpl : FWFWKWebViewConfigurationFlutterApi +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager; +- (void)createWithConfiguration:(WKWebViewConfiguration *)configuration + completion:(void (^)(NSError *_Nullable))completion; +@end + /** * Host api implementation for WKWebViewConfiguration. * diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m index 05ca38e2a477..2b7d1be86d37 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m @@ -6,6 +6,29 @@ #import "FWFDataConverters.h" #import "FWFWebViewConfigurationHostApi.h" +@interface FWFWebViewConfigurationFlutterApiImpl () +// BinaryMessenger and InstanceManager must be weak to prevent a circular reference +// with the objects it stores. +@property(nonatomic, weak) FWFInstanceManager *instanceManager; +@end + +@implementation FWFWebViewConfigurationFlutterApiImpl +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager { + self = [self initWithBinaryMessenger:binaryMessenger]; + if (self) { + _instanceManager = instanceManager; + } + return self; +} + +- (void)createWithConfiguration:(WKWebViewConfiguration *)configuration + completion:(void (^)(NSError *_Nullable))completion { + long identifier = [self.instanceManager addHostCreatedInstance:configuration]; + [self createWithIdentifier:@(identifier) completion:completion]; +} +@end + @interface FWFWebViewConfigurationHostApiImpl () @property(nonatomic) FWFInstanceManager *instanceManager; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart index c1971f09d068..1640c61211cf 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.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 (v3.1.4), do not edit directly. +// Autogenerated from Pigeon (v3.1.5), 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 // @dart = 2.12 @@ -868,6 +868,39 @@ class WKWebViewConfigurationHostApi { } } +class _WKWebViewConfigurationFlutterApiCodec extends StandardMessageCodec { + const _WKWebViewConfigurationFlutterApiCodec(); +} + +abstract class WKWebViewConfigurationFlutterApi { + static const MessageCodec codec = + _WKWebViewConfigurationFlutterApiCodec(); + + void create(int identifier); + static void setup(WKWebViewConfigurationFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewConfigurationFlutterApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WKWebViewConfigurationFlutterApi.create 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.WKWebViewConfigurationFlutterApi.create was null, expected non-null int.'); + api.create(arg_identifier!); + return; + }); + } + } + } +} + class _WKUserContentControllerHostApiCodec extends StandardMessageCodec { const _WKUserContentControllerHostApiCodec(); @override 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 4eb173238e71..d0423b44df84 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 @@ -239,9 +239,14 @@ class WebKitFlutterApis { instanceManager: instanceManager, ), scriptMessageHandler = WKScriptMessageHandlerFlutterApiImpl( - instanceManager: instanceManager), + instanceManager: instanceManager, + ), uiDelegate = WKUIDelegateFlutterApiImpl( instanceManager: instanceManager, + ), + webViewConfiguration = WKWebViewConfigurationFlutterApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, ); static WebKitFlutterApis _instance = WebKitFlutterApis(); @@ -272,6 +277,10 @@ class WebKitFlutterApis { @visibleForTesting final WKUIDelegateFlutterApiImpl uiDelegate; + /// Flutter Api for [WKWebViewConfiguration]. + @visibleForTesting + final WKWebViewConfigurationFlutterApiImpl webViewConfiguration; + /// Ensures all the Flutter APIs have been set up to receive calls from native code. void ensureSetUp() { if (!_hasBeenSetUp) { @@ -576,6 +585,37 @@ class WKWebViewConfigurationHostApiImpl extends WKWebViewConfigurationHostApi { } } +/// Flutter api implementation for [WKWebViewConfiguration]. +@immutable +class WKWebViewConfigurationFlutterApiImpl + extends WKWebViewConfigurationFlutterApi { + /// Constructs a [WKWebViewConfigurationFlutterApiImpl]. + WKWebViewConfigurationFlutterApiImpl({ + this.binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; + + /// Receives binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; + + @override + void create(int identifier) { + instanceManager.addHostCreatedInstance( + WKWebViewConfiguration.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ), + identifier, + ); + } +} + /// Host api implementation for [WKUIDelegate]. class WKUIDelegateHostApiImpl extends WKUIDelegateHostApi { /// Constructs a [WKUIDelegateHostApiImpl]. 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 e46dafa01a62..dfd271633573 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -312,6 +312,15 @@ abstract class WKWebViewConfigurationHostApi { ); } +/// Handles callbacks from an WKWebViewConfiguration instance. +/// +/// See https://developer.apple.com/documentation/webkit/wkwebviewconfiguration?language=objc. +@FlutterApi() +abstract class WKWebViewConfigurationFlutterApi { + @ObjCSelector('createWithIdentifier:') + void create(int identifier); +} + /// Mirror of WKUserContentController. /// /// See https://developer.apple.com/documentation/webkit/wkusercontentcontroller?language=objc. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart index 5bf50e2b9850..a9e5c8bb1db4 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.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 (v3.1.4), do not edit directly. +// Autogenerated from Pigeon (v3.1.5), 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 // ignore_for_file: avoid_relative_lib_imports 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 e328a292fcbe..62a51e17bc75 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 @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.2.0 from annotations -// in webview_flutter_wkwebview/test/src/foundation/foundation_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart. // Do not manually edit this file. import 'package:mockito/mockito.dart' as _i1; 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 58939f5b8829..a382ecff677c 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 @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.2.0 from annotations -// in webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart. // Do not manually edit this file. import 'dart:async' as _i4; 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 1d6c1f0e67a1..4000e0d718da 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 @@ -70,6 +70,20 @@ void main() { TestWKWebViewConfigurationHostApi.setup(null); }); + test('WKWebViewConfigurationFlutterApi.create', () { + final WebKitFlutterApis flutterApis = WebKitFlutterApis( + instanceManager: instanceManager, + ); + + flutterApis.webViewConfiguration.create(2); + + expect(instanceManager.containsIdentifier(2), isTrue); + expect( + instanceManager.getInstanceWithWeakReference(2), + isA(), + ); + }); + test('createFromWebViewConfiguration', () { verify(mockPlatformHostApi.createFromWebViewConfiguration( instanceManager.getIdentifier(websiteDataStore), 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 39ba08f3aa93..18f30d434952 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 @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.2.0 from annotations -// in webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart. // Do not manually edit this file. import 'dart:async' as _i3; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart index 8289831fc4e4..b0c63b663066 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.2.0 from annotations -// in webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart. // Do not manually edit this file. import 'dart:async' as _i4; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index c8ed4c856dcb..6161bd044435 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.2.0 from annotations -// in webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. // Do not manually edit this file. import 'dart:async' as _i6; From ae3f9d6d104ff0abbd63bb68b43a8d564caa2ef7 Mon Sep 17 00:00:00 2001 From: godofredoc Date: Wed, 15 Jun 2022 09:08:10 -0700 Subject: [PATCH 434/844] Reduce dependabot freq from daily to weekly. (#5972) --- .github/dependabot.yml | 74 +++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 02bfe34d9608..d5ca4f193ecc 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,7 +5,7 @@ updates: commit-message: prefix: "[camera]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -13,7 +13,7 @@ updates: commit-message: prefix: "[camera]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -21,7 +21,7 @@ updates: commit-message: prefix: "[espresso]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -29,7 +29,7 @@ updates: commit-message: prefix: "[espresso]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -37,7 +37,7 @@ updates: commit-message: prefix: "[lifecycle]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -45,7 +45,7 @@ updates: commit-message: prefix: "[lifecycle]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -53,7 +53,7 @@ updates: commit-message: prefix: "[google_maps]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -61,7 +61,7 @@ updates: commit-message: prefix: "[google_maps]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -69,7 +69,7 @@ updates: commit-message: prefix: "[sign_in]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -77,7 +77,7 @@ updates: commit-message: prefix: "[sign_in]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -85,7 +85,7 @@ updates: commit-message: prefix: "[sign_in]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -93,7 +93,7 @@ updates: commit-message: prefix: "[in_app_pur]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -101,7 +101,7 @@ updates: commit-message: prefix: "[in_app_pur]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -109,7 +109,7 @@ updates: commit-message: prefix: "[in_app_pur]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -117,7 +117,7 @@ updates: commit-message: prefix: "[image_picker]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -125,7 +125,7 @@ updates: commit-message: prefix: "[image_picker]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -133,7 +133,7 @@ updates: commit-message: prefix: "[image_picker]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -141,7 +141,7 @@ updates: commit-message: prefix: "[local_auth]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -149,7 +149,7 @@ updates: commit-message: prefix: "[local_auth]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -157,7 +157,7 @@ updates: commit-message: prefix: "[local_auth]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -165,7 +165,7 @@ updates: commit-message: prefix: "[path_provider]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -173,7 +173,7 @@ updates: commit-message: prefix: "[path_provider]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -181,7 +181,7 @@ updates: commit-message: prefix: "[path_provider]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -189,7 +189,7 @@ updates: commit-message: prefix: "[quick_actions]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -197,7 +197,7 @@ updates: commit-message: prefix: "[quick_actions]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -205,7 +205,7 @@ updates: commit-message: prefix: "[quick_actions]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -213,7 +213,7 @@ updates: commit-message: prefix: "[shared_pref]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -221,7 +221,7 @@ updates: commit-message: prefix: "[shared_pref]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -229,7 +229,7 @@ updates: commit-message: prefix: "[url_launcher]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -237,7 +237,7 @@ updates: commit-message: prefix: "[url_launcher]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -245,7 +245,7 @@ updates: commit-message: prefix: "[url_launcher]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -253,7 +253,7 @@ updates: commit-message: prefix: "[video_player]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -261,7 +261,7 @@ updates: commit-message: prefix: "[video_player]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -269,7 +269,7 @@ updates: commit-message: prefix: "[webview]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -277,7 +277,7 @@ updates: commit-message: prefix: "[webview]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "gradle" @@ -285,7 +285,7 @@ updates: commit-message: prefix: "[webview]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 - package-ecosystem: "github-actions" @@ -293,5 +293,5 @@ updates: commit-message: prefix: "[gh_actions]" schedule: - interval: "daily" + interval: "weekly" open-pull-requests-limit: 10 From 0b299249a45106df51d1a37efcdaeb8541f1b15e Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 15 Jun 2022 09:58:11 -0700 Subject: [PATCH 435/844] [webview_flutter_wkwebview] Adds the dispose method to NSObjectFlutterApi (#5970) --- .../ios/Classes/FWFGeneratedWebKitApis.h | 2 ++ .../ios/Classes/FWFGeneratedWebKitApis.m | 11 ++++++++++ .../lib/src/common/web_kit.pigeon.dart | 20 +++++++++++++++++++ .../src/foundation/foundation_api_impls.dart | 5 +++++ .../pigeons/web_kit.dart | 3 +++ .../test/src/foundation/foundation_test.dart | 16 ++++++++++++++- 6 files changed, 56 insertions(+), 1 deletion(-) 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 b0856ae11038..8cbd2c7c194c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h @@ -451,6 +451,8 @@ NSObject *FWFNSObjectFlutterApiGetCodec(void); changeKeys:(NSArray *)changeKeys changeValues:(NSArray *)changeValues completion:(void (^)(NSError *_Nullable))completion; +- (void)disposeObjectWithIdentifier:(NSNumber *)identifier + completion:(void (^)(NSError *_Nullable))completion; @end /// The codec used by FWFWKWebViewHostApi. NSObject *FWFWKWebViewHostApiGetCodec(void); 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 96c59987e841..10680227ee43 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m @@ -1989,6 +1989,17 @@ - (void)observeValueForObjectWithIdentifier:(NSNumber *)arg_identifier completion(nil); }]; } +- (void)disposeObjectWithIdentifier:(NSNumber *)arg_identifier + completion:(void (^)(NSError *_Nullable))completion { + FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel + messageChannelWithName:@"dev.flutter.pigeon.NSObjectFlutterApi.dispose" + binaryMessenger:self.binaryMessenger + codec:FWFNSObjectFlutterApiGetCodec()]; + [channel sendMessage:@[ arg_identifier ?: [NSNull null] ] + reply:^(id reply) { + completion(nil); + }]; +} @end @interface FWFWKWebViewHostApiCodecReader : FlutterStandardReader @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart index 1640c61211cf..54bb3015af64 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart @@ -1770,6 +1770,7 @@ abstract class NSObjectFlutterApi { int objectIdentifier, List changeKeys, List changeValues); + void dispose(int identifier); static void setup(NSObjectFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { @@ -1806,6 +1807,25 @@ abstract class NSObjectFlutterApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NSObjectFlutterApi.dispose', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NSObjectFlutterApi.dispose 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.NSObjectFlutterApi.dispose was null, expected non-null int.'); + api.dispose(arg_identifier!); + return; + }); + } + } } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart index f9efe3616ca7..de760ec5b36e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart @@ -183,4 +183,9 @@ class NSObjectFlutterApiImpl extends NSObjectFlutterApi { ), changeValues), ); } + + @override + void dispose(int identifier) { + instanceManager.remove(identifier); + } } 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 dfd271633573..c20a10ebfadd 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -510,6 +510,9 @@ abstract class NSObjectFlutterApi { List changeKeys, List changeValues, ); + + @ObjCSelector('disposeObjectWithIdentifier:') + void dispose(int identifier); } /// Mirror of WKWebView. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart index 8d15d65f3598..d97d152739c4 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart @@ -90,7 +90,7 @@ void main() { )); }); - test('dispose', () async { + test('NSObjectHostApi.dispose', () async { int? callbackIdentifier; final InstanceManager instanceManager = InstanceManager(onWeakReferenceRemoved: (int identifier) { @@ -145,6 +145,20 @@ void main() { ]), ); }); + + test('NSObjectFlutterApi.dispose', () { + FoundationFlutterApis.instance = FoundationFlutterApis( + instanceManager: instanceManager, + ); + + object = NSObject(instanceManager: instanceManager); + instanceManager.addHostCreatedInstance(object, 1); + + instanceManager.removeWeakReference(object); + FoundationFlutterApis.instance.object.dispose(1); + + expect(instanceManager.containsIdentifier(1), isFalse); + }); }); }); } From 95a1f2cb00a316440d0bd8ba7e9a81c37a538917 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 15 Jun 2022 13:46:05 -0400 Subject: [PATCH 436/844] [image_picker] Switch Android to internal method channel (#5958) --- .../image_picker_android/CHANGELOG.md | 4 + .../imagepicker/ImagePickerPlugin.java | 2 +- .../lib/image_picker_android.dart | 282 ++++ .../image_picker_android/pubspec.yaml | 3 +- .../test/image_picker_android_test.dart | 1256 +++++++++++++++++ 5 files changed, 1545 insertions(+), 2 deletions(-) create mode 100644 packages/image_picker/image_picker_android/lib/image_picker_android.dart create mode 100644 packages/image_picker/image_picker_android/test/image_picker_android_test.dart diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index a5cc6139bf0f..664be36f880f 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.5+1 + +* Switches to an internal method channel implementation. + ## 0.8.5 * Updates gradle to 7.1.2. diff --git a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java index 311ef19103ac..8336a145e93a 100644 --- a/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java +++ b/packages/image_picker/image_picker_android/android/src/main/java/io/flutter/plugins/imagepicker/ImagePickerPlugin.java @@ -183,7 +183,7 @@ ImagePickerDelegate getDelegate() { private static final String METHOD_CALL_RETRIEVE = "retrieve"; private static final int CAMERA_DEVICE_FRONT = 1; private static final int CAMERA_DEVICE_REAR = 0; - private static final String CHANNEL = "plugins.flutter.io/image_picker"; + private static final String CHANNEL = "plugins.flutter.io/image_picker_android"; private static final int SOURCE_CAMERA = 0; private static final int SOURCE_GALLERY = 1; diff --git a/packages/image_picker/image_picker_android/lib/image_picker_android.dart b/packages/image_picker/image_picker_android/lib/image_picker_android.dart new file mode 100644 index 000000000000..b6073c7a436a --- /dev/null +++ b/packages/image_picker/image_picker_android/lib/image_picker_android.dart @@ -0,0 +1,282 @@ +// 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:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.io/image_picker_android'); + +/// An Android implementation of [ImagePickerPlatform]. +class ImagePickerAndroid extends ImagePickerPlatform { + /// The MethodChannel that is being used by this implementation of the plugin. + @visibleForTesting + MethodChannel get channel => _channel; + + /// Registers this class as the default platform implementation. + static void registerWith() { + ImagePickerPlatform.instance = ImagePickerAndroid(); + } + + @override + Future pickImage({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) async { + final String? path = await _getImagePath( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? PickedFile(path) : null; + } + + @override + Future?> pickMultiImage({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + }) async { + final List? paths = await _getMultiImagePath( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + ); + if (paths == null) { + return null; + } + + return paths.map((dynamic path) => PickedFile(path as String)).toList(); + } + + Future?> _getMultiImagePath({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + }) { + if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { + throw ArgumentError.value( + imageQuality, 'imageQuality', 'must be between 0 and 100'); + } + + if (maxWidth != null && maxWidth < 0) { + throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative'); + } + + if (maxHeight != null && maxHeight < 0) { + throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative'); + } + + return _channel.invokeMethod?>( + 'pickMultiImage', + { + 'maxWidth': maxWidth, + 'maxHeight': maxHeight, + 'imageQuality': imageQuality, + }, + ); + } + + Future _getImagePath({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + bool requestFullMetadata = true, + }) { + if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { + throw ArgumentError.value( + imageQuality, 'imageQuality', 'must be between 0 and 100'); + } + + if (maxWidth != null && maxWidth < 0) { + throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative'); + } + + if (maxHeight != null && maxHeight < 0) { + throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative'); + } + + return _channel.invokeMethod( + 'pickImage', + { + 'source': source.index, + 'maxWidth': maxWidth, + 'maxHeight': maxHeight, + 'imageQuality': imageQuality, + 'cameraDevice': preferredCameraDevice.index, + 'requestFullMetadata': requestFullMetadata, + }, + ); + } + + @override + Future pickVideo({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) async { + final String? path = await _getVideoPath( + source: source, + maxDuration: maxDuration, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? PickedFile(path) : null; + } + + Future _getVideoPath({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) { + return _channel.invokeMethod( + 'pickVideo', + { + 'source': source.index, + 'maxDuration': maxDuration?.inSeconds, + 'cameraDevice': preferredCameraDevice.index + }, + ); + } + + @override + Future getImage({ + required ImageSource source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + CameraDevice preferredCameraDevice = CameraDevice.rear, + }) async { + final String? path = await _getImagePath( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? XFile(path) : null; + } + + @override + Future getImageFromSource({ + required ImageSource source, + ImagePickerOptions options = const ImagePickerOptions(), + }) async { + final String? path = await _getImagePath( + source: source, + maxHeight: options.maxHeight, + maxWidth: options.maxWidth, + imageQuality: options.imageQuality, + preferredCameraDevice: options.preferredCameraDevice, + requestFullMetadata: options.requestFullMetadata, + ); + return path != null ? XFile(path) : null; + } + + @override + Future?> getMultiImage({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + }) async { + final List? paths = await _getMultiImagePath( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + ); + if (paths == null) { + return null; + } + + return paths.map((dynamic path) => XFile(path as String)).toList(); + } + + @override + Future getVideo({ + required ImageSource source, + CameraDevice preferredCameraDevice = CameraDevice.rear, + Duration? maxDuration, + }) async { + final String? path = await _getVideoPath( + source: source, + maxDuration: maxDuration, + preferredCameraDevice: preferredCameraDevice, + ); + return path != null ? XFile(path) : null; + } + + @override + Future retrieveLostData() async { + final LostDataResponse result = await getLostData(); + + if (result.isEmpty) { + return LostData.empty(); + } + + return LostData( + file: result.file != null ? PickedFile(result.file!.path) : null, + exception: result.exception, + type: result.type, + ); + } + + @override + Future getLostData() async { + List? pickedFileList; + + final Map? result = + await _channel.invokeMapMethod('retrieve'); + + if (result == null) { + return LostDataResponse.empty(); + } + + assert(result.containsKey('path') != result.containsKey('errorCode')); + + final String? type = result['type'] as String?; + assert(type == kTypeImage || type == kTypeVideo); + + RetrieveType? retrieveType; + if (type == kTypeImage) { + retrieveType = RetrieveType.image; + } else if (type == kTypeVideo) { + retrieveType = RetrieveType.video; + } + + PlatformException? exception; + if (result.containsKey('errorCode')) { + exception = PlatformException( + code: result['errorCode']! as String, + message: result['errorMessage'] as String?); + } + + final String? path = result['path'] as String?; + + final List? pathList = + (result['pathList'] as List?)?.cast(); + if (pathList != null) { + pickedFileList = []; + for (final String path in pathList) { + pickedFileList.add(XFile(path)); + } + } + + return LostDataResponse( + file: path != null ? XFile(path) : null, + exception: exception, + type: retrieveType, + files: pickedFileList, + ); + } +} diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index 3d17e8ae7745..8cbaaac71daf 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/plugins/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.5 +version: 0.8.5+1 environment: sdk: ">=2.14.0 <3.0.0" @@ -15,6 +15,7 @@ flutter: android: package: io.flutter.plugins.imagepicker pluginClass: ImagePickerPlugin + dartPluginClass: ImagePickerAndroid dependencies: flutter: diff --git a/packages/image_picker/image_picker_android/test/image_picker_android_test.dart b/packages/image_picker/image_picker_android/test/image_picker_android_test.dart new file mode 100644 index 000000000000..ee1eb79f1045 --- /dev/null +++ b/packages/image_picker/image_picker_android/test/image_picker_android_test.dart @@ -0,0 +1,1256 @@ +// 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:flutter_test/flutter_test.dart'; + +import 'package:image_picker_android/image_picker_android.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + final ImagePickerAndroid picker = ImagePickerAndroid(); + + final List log = []; + dynamic returnValue = ''; + + setUp(() { + returnValue = ''; + picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return returnValue; + }); + + log.clear(); + }); + + test('registers instance', () async { + ImagePickerAndroid.registerWith(); + expect(ImagePickerPlatform.instance, isA()); + }); + + group('#pickImage', () { + test('passes the image source argument correctly', () async { + await picker.pickImage(source: ImageSource.camera); + await picker.pickImage(source: ImageSource.gallery); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 1, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + await picker.pickImage(source: ImageSource.camera); + await picker.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + ); + await picker.pickImage( + source: ImageSource.camera, + maxHeight: 10.0, + ); + await picker.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + ); + await picker.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + imageQuality: 70, + ); + await picker.pickImage( + source: ImageSource.camera, + maxHeight: 10.0, + imageQuality: 70, + ); + await picker.pickImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('does not accept an invalid imageQuality argument', () { + expect( + () => picker.pickImage(imageQuality: -1, source: ImageSource.gallery), + throwsArgumentError, + ); + + expect( + () => picker.pickImage(imageQuality: 101, source: ImageSource.gallery), + throwsArgumentError, + ); + + expect( + () => picker.pickImage(imageQuality: -1, source: ImageSource.camera), + throwsArgumentError, + ); + + expect( + () => picker.pickImage(imageQuality: 101, source: ImageSource.camera), + throwsArgumentError, + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.pickImage(source: ImageSource.camera, maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + () => picker.pickImage(source: ImageSource.camera, maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.pickImage(source: ImageSource.gallery), isNull); + expect(await picker.pickImage(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.pickImage(source: ImageSource.camera); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.pickImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 1, + 'requestFullMetadata': true, + }), + ], + ); + }); + }); + + group('#pickMultiImage', () { + test('calls the method correctly', () async { + returnValue = ['0', '1']; + await picker.pickMultiImage(); + + expect( + log, + [ + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + returnValue = ['0', '1']; + await picker.pickMultiImage(); + await picker.pickMultiImage( + maxWidth: 10.0, + ); + await picker.pickMultiImage( + maxHeight: 10.0, + ); + await picker.pickMultiImage( + maxWidth: 10.0, + maxHeight: 20.0, + ); + await picker.pickMultiImage( + maxWidth: 10.0, + imageQuality: 70, + ); + await picker.pickMultiImage( + maxHeight: 10.0, + imageQuality: 70, + ); + await picker.pickMultiImage( + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ); + + expect( + log, + [ + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + }), + ], + ); + }); + + test('does not accept a negative width or height argument', () { + returnValue = ['0', '1']; + expect( + () => picker.pickMultiImage(maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + () => picker.pickMultiImage(maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('does not accept an invalid imageQuality argument', () { + returnValue = ['0', '1']; + expect( + () => picker.pickMultiImage(imageQuality: -1), + throwsArgumentError, + ); + + expect( + () => picker.pickMultiImage(imageQuality: 101), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.pickMultiImage(), isNull); + expect(await picker.pickMultiImage(), isNull); + }); + }); + + group('#pickVideo', () { + test('passes the image source argument correctly', () async { + await picker.pickVideo(source: ImageSource.camera); + await picker.pickVideo(source: ImageSource.gallery); + + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'cameraDevice': 0, + 'maxDuration': null, + }), + isMethodCall('pickVideo', arguments: { + 'source': 1, + 'cameraDevice': 0, + 'maxDuration': null, + }), + ], + ); + }); + + test('passes the duration argument correctly', () async { + await picker.pickVideo(source: ImageSource.camera); + await picker.pickVideo( + source: ImageSource.camera, + maxDuration: const Duration(seconds: 10), + ); + await picker.pickVideo( + source: ImageSource.camera, + maxDuration: const Duration(minutes: 1), + ); + await picker.pickVideo( + source: ImageSource.camera, + maxDuration: const Duration(hours: 1), + ); + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': null, + 'cameraDevice': 0, + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': 10, + 'cameraDevice': 0, + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': 60, + 'cameraDevice': 0, + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': 3600, + 'cameraDevice': 0, + }), + ], + ); + }); + + test('handles a null video path response gracefully', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.pickVideo(source: ImageSource.gallery), isNull); + expect(await picker.pickVideo(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.pickVideo(source: ImageSource.camera); + + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'cameraDevice': 0, + 'maxDuration': null, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.pickVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front, + ); + + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': null, + 'cameraDevice': 1, + }), + ], + ); + }); + }); + + group('#retrieveLostData', () { + test('retrieveLostData get success response', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { + return { + 'type': 'image', + 'path': '/example/path', + }; + }); + final LostData response = await picker.retrieveLostData(); + expect(response.type, RetrieveType.image); + expect(response.file, isNotNull); + expect(response.file!.path, '/example/path'); + }); + + test('retrieveLostData get error response', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { + return { + 'type': 'video', + 'errorCode': 'test_error_code', + 'errorMessage': 'test_error_message', + }; + }); + final LostData response = await picker.retrieveLostData(); + expect(response.type, RetrieveType.video); + expect(response.exception, isNotNull); + expect(response.exception!.code, 'test_error_code'); + expect(response.exception!.message, 'test_error_message'); + }); + + test('retrieveLostData get null response', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { + return null; + }); + expect((await picker.retrieveLostData()).isEmpty, true); + }); + + test('retrieveLostData get both path and error should throw', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { + return { + 'type': 'video', + 'errorCode': 'test_error_code', + 'errorMessage': 'test_error_message', + 'path': '/example/path', + }; + }); + expect(picker.retrieveLostData(), throwsAssertionError); + }); + }); + + group('#getImage', () { + test('passes the image source argument correctly', () async { + await picker.getImage(source: ImageSource.camera); + await picker.getImage(source: ImageSource.gallery); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 1, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + await picker.getImage(source: ImageSource.camera); + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + ); + await picker.getImage( + source: ImageSource.camera, + maxHeight: 10.0, + ); + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + ); + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + imageQuality: 70, + ); + await picker.getImage( + source: ImageSource.camera, + maxHeight: 10.0, + imageQuality: 70, + ); + await picker.getImage( + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('does not accept an invalid imageQuality argument', () { + expect( + () => picker.getImage(imageQuality: -1, source: ImageSource.gallery), + throwsArgumentError, + ); + + expect( + () => picker.getImage(imageQuality: 101, source: ImageSource.gallery), + throwsArgumentError, + ); + + expect( + () => picker.getImage(imageQuality: -1, source: ImageSource.camera), + throwsArgumentError, + ); + + expect( + () => picker.getImage(imageQuality: 101, source: ImageSource.camera), + throwsArgumentError, + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.getImage(source: ImageSource.camera, maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + () => picker.getImage(source: ImageSource.camera, maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.getImage(source: ImageSource.gallery), isNull); + expect(await picker.getImage(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.getImage(source: ImageSource.camera); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.getImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 1, + 'requestFullMetadata': true, + }), + ], + ); + }); + }); + + group('#getMultiImage', () { + test('calls the method correctly', () async { + returnValue = ['0', '1']; + await picker.getMultiImage(); + + expect( + log, + [ + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + returnValue = ['0', '1']; + await picker.getMultiImage(); + await picker.getMultiImage( + maxWidth: 10.0, + ); + await picker.getMultiImage( + maxHeight: 10.0, + ); + await picker.getMultiImage( + maxWidth: 10.0, + maxHeight: 20.0, + ); + await picker.getMultiImage( + maxWidth: 10.0, + imageQuality: 70, + ); + await picker.getMultiImage( + maxHeight: 10.0, + imageQuality: 70, + ); + await picker.getMultiImage( + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ); + + expect( + log, + [ + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + }), + ], + ); + }); + + test('does not accept a negative width or height argument', () { + returnValue = ['0', '1']; + expect( + () => picker.getMultiImage(maxWidth: -1.0), + throwsArgumentError, + ); + + expect( + () => picker.getMultiImage(maxHeight: -1.0), + throwsArgumentError, + ); + }); + + test('does not accept an invalid imageQuality argument', () { + returnValue = ['0', '1']; + expect( + () => picker.getMultiImage(imageQuality: -1), + throwsArgumentError, + ); + + expect( + () => picker.getMultiImage(imageQuality: 101), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.getMultiImage(), isNull); + expect(await picker.getMultiImage(), isNull); + }); + }); + + group('#getVideo', () { + test('passes the image source argument correctly', () async { + await picker.getVideo(source: ImageSource.camera); + await picker.getVideo(source: ImageSource.gallery); + + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'cameraDevice': 0, + 'maxDuration': null, + }), + isMethodCall('pickVideo', arguments: { + 'source': 1, + 'cameraDevice': 0, + 'maxDuration': null, + }), + ], + ); + }); + + test('passes the duration argument correctly', () async { + await picker.getVideo(source: ImageSource.camera); + await picker.getVideo( + source: ImageSource.camera, + maxDuration: const Duration(seconds: 10), + ); + await picker.getVideo( + source: ImageSource.camera, + maxDuration: const Duration(minutes: 1), + ); + await picker.getVideo( + source: ImageSource.camera, + maxDuration: const Duration(hours: 1), + ); + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': null, + 'cameraDevice': 0, + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': 10, + 'cameraDevice': 0, + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': 60, + 'cameraDevice': 0, + }), + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': 3600, + 'cameraDevice': 0, + }), + ], + ); + }); + + test('handles a null video path response gracefully', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.getVideo(source: ImageSource.gallery), isNull); + expect(await picker.getVideo(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.getVideo(source: ImageSource.camera); + + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'cameraDevice': 0, + 'maxDuration': null, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.getVideo( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front, + ); + + expect( + log, + [ + isMethodCall('pickVideo', arguments: { + 'source': 0, + 'maxDuration': null, + 'cameraDevice': 1, + }), + ], + ); + }); + }); + + group('#getLostData', () { + test('getLostData get success response', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { + return { + 'type': 'image', + 'path': '/example/path', + }; + }); + final LostDataResponse response = await picker.getLostData(); + expect(response.type, RetrieveType.image); + expect(response.file, isNotNull); + expect(response.file!.path, '/example/path'); + }); + + test('getLostData should successfully retrieve multiple files', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { + return { + 'type': 'image', + 'path': '/example/path1', + 'pathList': ['/example/path0', '/example/path1'], + }; + }); + final LostDataResponse response = await picker.getLostData(); + expect(response.type, RetrieveType.image); + expect(response.file, isNotNull); + expect(response.file!.path, '/example/path1'); + expect(response.files!.first.path, '/example/path0'); + expect(response.files!.length, 2); + }); + + test('getLostData get error response', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { + return { + 'type': 'video', + 'errorCode': 'test_error_code', + 'errorMessage': 'test_error_message', + }; + }); + final LostDataResponse response = await picker.getLostData(); + expect(response.type, RetrieveType.video); + expect(response.exception, isNotNull); + expect(response.exception!.code, 'test_error_code'); + expect(response.exception!.message, 'test_error_message'); + }); + + test('getLostData get null response', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { + return null; + }); + expect((await picker.getLostData()).isEmpty, true); + }); + + test('getLostData get both path and error should throw', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) async { + return { + 'type': 'video', + 'errorCode': 'test_error_code', + 'errorMessage': 'test_error_message', + 'path': '/example/path', + }; + }); + expect(picker.getLostData(), throwsAssertionError); + }); + }); + + group('#getImageFromSource', () { + test('passes the image source argument correctly', () async { + await picker.getImageFromSource(source: ImageSource.camera); + await picker.getImageFromSource(source: ImageSource.gallery); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 1, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + await picker.getImageFromSource(source: ImageSource.camera); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxWidth: 10.0), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxHeight: 10.0), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxWidth: 10.0, + maxHeight: 20.0, + ), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxWidth: 10.0, + imageQuality: 70, + ), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxHeight: 10.0, + imageQuality: 70, + ), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ), + ); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('does not accept an invalid imageQuality argument', () { + expect( + () => picker.getImageFromSource( + source: ImageSource.gallery, + options: const ImagePickerOptions(imageQuality: -1), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.gallery, + options: const ImagePickerOptions(imageQuality: 101), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(imageQuality: -1), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(imageQuality: 101), + ), + throwsArgumentError, + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxWidth: -1.0), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxHeight: -1.0), + ), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + picker.channel.setMockMethodCallHandler((MethodCall methodCall) => null); + + expect( + await picker.getImageFromSource(source: ImageSource.gallery), isNull); + expect( + await picker.getImageFromSource(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.getImageFromSource(source: ImageSource.camera); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + preferredCameraDevice: CameraDevice.front, + ), + ); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 1, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('passes the full metadata argument correctly', () async { + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(requestFullMetadata: false), + ); + + expect( + log, + [ + isMethodCall('pickImage', arguments: { + 'source': 0, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': 0, + 'requestFullMetadata': false, + }), + ], + ); + }); + }); +} From ec8a69bd9ca42aa80ea9aaef98b10045c38bca5f Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 15 Jun 2022 11:33:14 -0700 Subject: [PATCH 437/844] [webview_flutter_wkwebview] Prevents `NSObject.removeObserver` from being called without calling `addObserver` first (#5975) --- .../lib/src/web_kit_webview_widget.dart | 12 ++++++++---- .../test/src/web_kit_webview_widget_test.dart | 10 ++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index a5c7c8f86ab6..99195fba4c33 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -93,6 +93,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { bool _zoomEnabled = true; bool _hasNavigationDelegate = false; + bool _progressObserverSet = false; final Map _scriptMessageHandlers = {}; @@ -455,17 +456,20 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { await _resetUserScripts(removedJavaScriptChannels: javascriptChannelNames); } - Future _setHasProgressTracking(bool hasProgressTracking) { + Future _setHasProgressTracking(bool hasProgressTracking) async { if (hasProgressTracking) { - return webView.addObserver( + _progressObserverSet = true; + await webView.addObserver( webView, keyPath: 'estimatedProgress', options: { NSKeyValueObservingOptions.newValue, }, ); - } else { - return webView.removeObserver(webView, keyPath: 'estimatedProgress'); + } else if (_progressObserverSet) { + // Calls to removeObserver before addObserver causes a crash. + _progressObserverSet = false; + await webView.removeObserver(webView, keyPath: 'estimatedProgress'); } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index a1656badad01..3580afe1c3a2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -1195,6 +1195,16 @@ void main() { verify(mockCallbacksHandler.onProgress(32)); }); + + testWidgets('progress observer is not removed without being set first', + (WidgetTester tester) async { + await buildWidget(tester, hasProgressTracking: false); + + verifyNever(mockWebView.removeObserver( + mockWebView, + keyPath: 'estimatedProgress', + )); + }); }); group('JavascriptChannelRegistry', () { From bc6884621248f2994b36600924166ef013096c1a Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Wed, 15 Jun 2022 12:34:05 -0700 Subject: [PATCH 438/844] [camera] Ignore body_might_complete_normally_catch_error violation (#5957) --- packages/camera/camera_platform_interface/CHANGELOG.md | 1 + .../lib/src/method_channel/method_channel_camera.dart | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index a46b758d8053..9cc0b14dafbd 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,6 +1,7 @@ ## NEXT * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). +* Ignores missing return warnings in preparation for [upcoming analysis changes](https://github.com/flutter/flutter/issues/105750). ## 2.2.0 diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index babef144b086..fd9ad530bd27 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -130,7 +130,12 @@ class MethodChannelCamera extends CameraPlatform { 'cameraId': cameraId, 'imageFormatGroup': imageFormatGroup.name(), }, - ).catchError( + ) + // TODO(srawlins): This should return a value of the future's type. This + // will fail upcoming analysis checks with + // https://github.com/flutter/flutter/issues/105750. + // ignore: body_might_complete_normally_catch_error + .catchError( (Object error, StackTrace stackTrace) { if (error is! PlatformException) { throw error; From 584765302d39731d758105a87e79501ee98c8cac Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 16 Jun 2022 07:13:09 -0400 Subject: [PATCH 439/844] Roll Flutter from 0cd8f3d02b8a to 873d343e091c (11 revisions) (#5982) --- .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 5fa4028e9929..9681ede70611 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -0cd8f3d02b8a0c8a0a09ac1c075f52e4b7b1dafe +873d343e091c16a2ca9dd51ab609593038e49528 From 8bee94ca98221246cba76c233a5e218532e6c351 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Thu, 16 Jun 2022 04:14:05 -0700 Subject: [PATCH 440/844] [path_provider]: Migrated path_provider for android to pigeon. (#5959) --- .../path_provider_android/CHANGELOG.md | 4 + .../plugins/pathprovider/Messages.java | 241 ++++++++++++++++++ .../pathprovider/PathProviderPlugin.java | 237 +++++------------ .../path_provider_android/lib/messages.g.dart | 196 ++++++++++++++ .../lib/path_provider_android.dart | 55 ++-- .../pigeons/copyright.txt | 3 + .../pigeons/messages.dart | 43 ++++ .../path_provider_android/pubspec.yaml | 5 +- .../test/messages_test.g.dart | 131 ++++++++++ .../test/path_provider_android_test.dart | 105 +++----- 10 files changed, 760 insertions(+), 260 deletions(-) create mode 100644 packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/Messages.java create mode 100644 packages/path_provider/path_provider_android/lib/messages.g.dart create mode 100644 packages/path_provider/path_provider_android/pigeons/copyright.txt create mode 100644 packages/path_provider/path_provider_android/pigeons/messages.dart create mode 100644 packages/path_provider/path_provider_android/test/messages_test.g.dart diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index 4b15e2605038..6b1b6611004e 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.15 + +* Switches the medium from MethodChannels to Pigeon. + ## 2.0.14 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/Messages.java b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/Messages.java new file mode 100644 index 000000000000..1e78916ebbc6 --- /dev/null +++ b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/Messages.java @@ -0,0 +1,241 @@ +// 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 (v3.1.5), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +package io.flutter.plugins.pathprovider; + +import android.util.Log; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.flutter.plugin.common.BasicMessageChannel; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.MessageCodec; +import io.flutter.plugin.common.StandardMessageCodec; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** Generated class from Pigeon. */ +@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"}) +public class Messages { + + public enum StorageDirectory { + music(0), + podcasts(1), + ringtones(2), + alarms(3), + notifications(4), + pictures(5), + movies(6), + downloads(7), + dcim(8), + documents(9); + + private int index; + + private StorageDirectory(final int index) { + this.index = index; + } + } + + private static class PathProviderApiCodec extends StandardMessageCodec { + public static final PathProviderApiCodec INSTANCE = new PathProviderApiCodec(); + + private PathProviderApiCodec() {} + } + + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface PathProviderApi { + @Nullable + String getTemporaryPath(); + + @Nullable + String getApplicationSupportPath(); + + @Nullable + String getApplicationDocumentsPath(); + + @Nullable + String getExternalStoragePath(); + + @NonNull + List getExternalCachePaths(); + + @NonNull + List getExternalStoragePaths(@NonNull StorageDirectory directory); + + /** The codec used by PathProviderApi. */ + static MessageCodec getCodec() { + return PathProviderApiCodec.INSTANCE; + } + + /** + * Sets up an instance of `PathProviderApi` to handle messages through the `binaryMessenger`. + */ + static void setup(BinaryMessenger binaryMessenger, PathProviderApi api) { + { + BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.PathProviderApi.getTemporaryPath", + getCodec(), + taskQueue); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + String output = api.getTemporaryPath(); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.PathProviderApi.getApplicationSupportPath", + getCodec(), + taskQueue); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + String output = api.getApplicationSupportPath(); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.PathProviderApi.getApplicationDocumentsPath", + getCodec(), + taskQueue); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + String output = api.getApplicationDocumentsPath(); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.PathProviderApi.getExternalStoragePath", + getCodec(), + taskQueue); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + String output = api.getExternalStoragePath(); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.PathProviderApi.getExternalCachePaths", + getCodec(), + taskQueue); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + List output = api.getExternalCachePaths(); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.PathProviderApi.getExternalStoragePaths", + getCodec(), + taskQueue); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + StorageDirectory directoryArg = + args.get(0) == null ? null : StorageDirectory.values()[(int) args.get(0)]; + if (directoryArg == null) { + throw new NullPointerException("directoryArg unexpectedly null."); + } + List output = api.getExternalStoragePaths(directoryArg); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } + + private static Map wrapError(Throwable exception) { + Map errorMap = new HashMap<>(); + errorMap.put("message", exception.toString()); + errorMap.put("code", exception.getClass().getSimpleName()); + errorMap.put( + "details", + "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); + return errorMap; + } +} diff --git a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java index 6dcf9595ac86..8a5ff48b6477 100644 --- a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java +++ b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java @@ -7,164 +7,34 @@ import android.content.Context; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; -import android.os.Handler; -import android.os.Looper; import android.util.Log; import androidx.annotation.NonNull; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.SettableFuture; -import com.google.common.util.concurrent.ThreadFactoryBuilder; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.BinaryMessenger.TaskQueue; -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; -import io.flutter.plugin.common.StandardMethodCodec; +import io.flutter.plugins.pathprovider.Messages.PathProviderApi; import io.flutter.util.PathUtils; import java.io.File; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; +import javax.annotation.Nullable; -public class PathProviderPlugin implements FlutterPlugin, MethodCallHandler { +public class PathProviderPlugin implements FlutterPlugin, PathProviderApi { static final String TAG = "PathProviderPlugin"; private Context context; - private MethodChannel channel; - private PathProviderImpl impl; - - /** - * An abstraction over how to access the paths in a thread-safe manner. - * - *

We need this so on versions of Flutter that support Background Platform Channels this plugin - * can take advantage of it. - * - *

This can be removed after https://github.com/flutter/engine/pull/29147 becomes available on - * the stable branch. - */ - private interface PathProviderImpl { - void getTemporaryDirectory(@NonNull Result result); - - void getApplicationDocumentsDirectory(@NonNull Result result); - - void getStorageDirectory(@NonNull Result result); - - void getExternalCacheDirectories(@NonNull Result result); - - void getExternalStorageDirectories(@NonNull String directoryName, @NonNull Result result); - - void getApplicationSupportDirectory(@NonNull Result result); - } - - /** The implementation for getting system paths that executes from the platform */ - private class PathProviderPlatformThread implements PathProviderImpl { - private final Executor uiThreadExecutor = new UiThreadExecutor(); - private final Executor executor = - Executors.newSingleThreadExecutor( - new ThreadFactoryBuilder() - .setNameFormat("path-provider-background-%d") - .setPriority(Thread.NORM_PRIORITY) - .build()); - - public void getTemporaryDirectory(@NonNull Result result) { - executeInBackground(() -> getPathProviderTemporaryDirectory(), result); - } - - public void getApplicationDocumentsDirectory(@NonNull Result result) { - executeInBackground(() -> getPathProviderApplicationDocumentsDirectory(), result); - } - - public void getStorageDirectory(@NonNull Result result) { - executeInBackground(() -> getPathProviderStorageDirectory(), result); - } - - public void getExternalCacheDirectories(@NonNull Result result) { - executeInBackground(() -> getPathProviderExternalCacheDirectories(), result); - } - - public void getExternalStorageDirectories( - @NonNull String directoryName, @NonNull Result result) { - executeInBackground(() -> getPathProviderExternalStorageDirectories(directoryName), result); - } - - public void getApplicationSupportDirectory(@NonNull Result result) { - executeInBackground(() -> PathProviderPlugin.this.getApplicationSupportDirectory(), result); - } - - private void executeInBackground(Callable task, Result result) { - final SettableFuture future = SettableFuture.create(); - Futures.addCallback( - future, - new FutureCallback() { - public void onSuccess(T answer) { - result.success(answer); - } - - public void onFailure(Throwable t) { - result.error(t.getClass().getName(), t.getMessage(), null); - } - }, - uiThreadExecutor); - executor.execute( - () -> { - try { - future.set(task.call()); - } catch (Throwable t) { - future.setException(t); - } - }); - } - } - - /** The implementation for getting system paths that executes from a background thread. */ - private class PathProviderBackgroundThread implements PathProviderImpl { - public void getTemporaryDirectory(@NonNull Result result) { - result.success(getPathProviderTemporaryDirectory()); - } - - public void getApplicationDocumentsDirectory(@NonNull Result result) { - result.success(getPathProviderApplicationDocumentsDirectory()); - } - - public void getStorageDirectory(@NonNull Result result) { - result.success(getPathProviderStorageDirectory()); - } - - public void getExternalCacheDirectories(@NonNull Result result) { - result.success(getPathProviderExternalCacheDirectories()); - } - - public void getExternalStorageDirectories( - @NonNull String directoryName, @NonNull Result result) { - result.success(getPathProviderExternalStorageDirectories(directoryName)); - } - - public void getApplicationSupportDirectory(@NonNull Result result) { - result.success(PathProviderPlugin.this.getApplicationSupportDirectory()); - } - } public PathProviderPlugin() {} private void setup(BinaryMessenger messenger, Context context) { - String channelName = "plugins.flutter.io/path_provider_android"; TaskQueue taskQueue = messenger.makeBackgroundTaskQueue(); try { - channel = - (MethodChannel) - new MethodChannel(messenger, channelName, StandardMethodCodec.INSTANCE, taskQueue); - impl = new PathProviderBackgroundThread(); + PathProviderApi.setup(messenger, this); } catch (Exception ex) { Log.e(TAG, "Received exception while setting up PathProviderPlugin", ex); } this.context = context; - channel.setMethodCallHandler(this); } @SuppressWarnings("deprecation") @@ -180,36 +50,38 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { @Override public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - channel.setMethodCallHandler(null); - channel = null; + PathProviderApi.setup(binding.getBinaryMessenger(), null); } @Override - public void onMethodCall(MethodCall call, @NonNull Result result) { - switch (call.method) { - case "getTemporaryDirectory": - impl.getTemporaryDirectory(result); - break; - case "getApplicationDocumentsDirectory": - impl.getApplicationDocumentsDirectory(result); - break; - case "getStorageDirectory": - impl.getStorageDirectory(result); - break; - case "getExternalCacheDirectories": - impl.getExternalCacheDirectories(result); - break; - case "getExternalStorageDirectories": - final Integer type = call.argument("type"); - final String directoryName = StorageDirectoryMapper.androidType(type); - impl.getExternalStorageDirectories(directoryName, result); - break; - case "getApplicationSupportDirectory": - impl.getApplicationSupportDirectory(result); - break; - default: - result.notImplemented(); - } + public @Nullable String getTemporaryPath() { + return getPathProviderTemporaryDirectory(); + } + + @Override + public @Nullable String getApplicationSupportPath() { + return getApplicationSupportDirectory(); + } + + @Override + public @Nullable String getApplicationDocumentsPath() { + return getPathProviderApplicationDocumentsDirectory(); + } + + @Override + public @Nullable String getExternalStoragePath() { + return getPathProviderStorageDirectory(); + } + + @Override + public @NonNull List getExternalCachePaths() { + return getPathProviderExternalCacheDirectories(); + } + + @Override + public @NonNull List getExternalStoragePaths( + @NonNull Messages.StorageDirectory directory) { + return getPathProviderExternalStorageDirectories(directory); } private String getPathProviderTemporaryDirectory() { @@ -251,17 +123,45 @@ private List getPathProviderExternalCacheDirectories() { return paths; } - private List getPathProviderExternalStorageDirectories(String type) { + private String getStorageDirectoryString(@NonNull Messages.StorageDirectory directory) { + switch (directory) { + case music: + return "music"; + case podcasts: + return "podcasts"; + case ringtones: + return "ringtones"; + case alarms: + return "alarms"; + case notifications: + return "notifications"; + case pictures: + return "pictures"; + case movies: + return "movies"; + case downloads: + return "downloads"; + case dcim: + return "dcim"; + case documents: + return "documents"; + default: + throw new RuntimeException("Unrecognized directory: " + directory); + } + } + + private List getPathProviderExternalStorageDirectories( + @NonNull Messages.StorageDirectory directory) { final List paths = new ArrayList(); if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) { - for (File dir : context.getExternalFilesDirs(type)) { + for (File dir : context.getExternalFilesDirs(getStorageDirectoryString(directory))) { if (dir != null) { paths.add(dir.getAbsolutePath()); } } } else { - File dir = context.getExternalFilesDir(type); + File dir = context.getExternalFilesDir(getStorageDirectoryString(directory)); if (dir != null) { paths.add(dir.getAbsolutePath()); } @@ -269,13 +169,4 @@ private List getPathProviderExternalStorageDirectories(String type) { return paths; } - - private static class UiThreadExecutor implements Executor { - private final Handler handler = new Handler(Looper.getMainLooper()); - - @Override - public void execute(Runnable command) { - handler.post(command); - } - } } diff --git a/packages/path_provider/path_provider_android/lib/messages.g.dart b/packages/path_provider/path_provider_android/lib/messages.g.dart new file mode 100644 index 000000000000..50009f454ad7 --- /dev/null +++ b/packages/path_provider/path_provider_android/lib/messages.g.dart @@ -0,0 +1,196 @@ +// 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 (v3.1.5), 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 +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; + +enum StorageDirectory { + music, + podcasts, + ringtones, + alarms, + notifications, + pictures, + movies, + downloads, + dcim, + documents, +} + +class _PathProviderApiCodec extends StandardMessageCodec { + const _PathProviderApiCodec(); +} + +class PathProviderApi { + /// Constructor for [PathProviderApi]. 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. + PathProviderApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _PathProviderApiCodec(); + + Future getTemporaryPath() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getTemporaryPath', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send(null) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as String?); + } + } + + Future getApplicationSupportPath() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getApplicationSupportPath', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send(null) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as String?); + } + } + + Future getApplicationDocumentsPath() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getApplicationDocumentsPath', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send(null) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as String?); + } + } + + Future getExternalStoragePath() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getExternalStoragePath', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send(null) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return (replyMap['result'] as String?); + } + } + + Future> getExternalCachePaths() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getExternalCachePaths', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send(null) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as List?)!.cast(); + } + } + + Future> getExternalStoragePaths( + StorageDirectory arg_directory) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getExternalStoragePaths', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_directory.index]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as List?)!.cast(); + } + } +} diff --git a/packages/path_provider/path_provider_android/lib/path_provider_android.dart b/packages/path_provider/path_provider_android/lib/path_provider_android.dart index b0f3808d2859..4f08d7af08d4 100644 --- a/packages/path_provider/path_provider_android/lib/path_provider_android.dart +++ b/packages/path_provider/path_provider_android/lib/path_provider_android.dart @@ -2,16 +2,37 @@ // 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/services.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; +import 'messages.g.dart' as messages; + +messages.StorageDirectory _convertStorageDirectory(StorageDirectory directory) { + switch (directory) { + case StorageDirectory.music: + return messages.StorageDirectory.music; + case StorageDirectory.podcasts: + return messages.StorageDirectory.podcasts; + case StorageDirectory.ringtones: + return messages.StorageDirectory.ringtones; + case StorageDirectory.alarms: + return messages.StorageDirectory.alarms; + case StorageDirectory.notifications: + return messages.StorageDirectory.notifications; + case StorageDirectory.pictures: + return messages.StorageDirectory.pictures; + case StorageDirectory.movies: + return messages.StorageDirectory.movies; + case StorageDirectory.downloads: + return messages.StorageDirectory.downloads; + case StorageDirectory.dcim: + return messages.StorageDirectory.dcim; + case StorageDirectory.documents: + return messages.StorageDirectory.documents; + } +} /// The Android implementation of [PathProviderPlatform]. class PathProviderAndroid extends PathProviderPlatform { - /// The method channel used to interact with the native platform. - @visibleForTesting - MethodChannel methodChannel = - const MethodChannel('plugins.flutter.io/path_provider_android'); + final messages.PathProviderApi _api = messages.PathProviderApi(); /// Registers this class as the default instance of [PathProviderPlatform]. static void registerWith() { @@ -20,12 +41,12 @@ class PathProviderAndroid extends PathProviderPlatform { @override Future getTemporaryPath() { - return methodChannel.invokeMethod('getTemporaryDirectory'); + return _api.getTemporaryPath(); } @override Future getApplicationSupportPath() { - return methodChannel.invokeMethod('getApplicationSupportDirectory'); + return _api.getApplicationSupportPath(); } @override @@ -35,29 +56,27 @@ class PathProviderAndroid extends PathProviderPlatform { @override Future getApplicationDocumentsPath() { - return methodChannel - .invokeMethod('getApplicationDocumentsDirectory'); + return _api.getApplicationDocumentsPath(); } @override Future getExternalStoragePath() { - return methodChannel.invokeMethod('getStorageDirectory'); + return _api.getExternalStoragePath(); } @override - Future?> getExternalCachePaths() { - return methodChannel - .invokeListMethod('getExternalCacheDirectories'); + Future?> getExternalCachePaths() async { + return (await _api.getExternalCachePaths()).cast(); } @override Future?> getExternalStoragePaths({ StorageDirectory? type, }) async { - return methodChannel.invokeListMethod( - 'getExternalStorageDirectories', - {'type': type?.index}, - ); + return type == null + ? [] + : (await _api.getExternalStoragePaths(_convertStorageDirectory(type))) + .cast(); } @override diff --git a/packages/path_provider/path_provider_android/pigeons/copyright.txt b/packages/path_provider/path_provider_android/pigeons/copyright.txt new file mode 100644 index 000000000000..1236b63caf3a --- /dev/null +++ b/packages/path_provider/path_provider_android/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. diff --git a/packages/path_provider/path_provider_android/pigeons/messages.dart b/packages/path_provider/path_provider_android/pigeons/messages.dart new file mode 100644 index 000000000000..464166771b0a --- /dev/null +++ b/packages/path_provider/path_provider_android/pigeons/messages.dart @@ -0,0 +1,43 @@ +// 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'; + +@ConfigurePigeon(PigeonOptions( + input: 'pigeons/messages.dart', + javaOut: + 'android/src/main/java/io/flutter/plugins/pathprovider/Messages.java', + javaOptions: JavaOptions( + className: 'Messages', package: 'io.flutter.plugins.pathprovider'), + dartOut: 'lib/messages.g.dart', + dartTestOut: 'test/messages_test.g.dart', + copyrightHeader: 'pigeons/copyright.txt', +)) +enum StorageDirectory { + music, + podcasts, + ringtones, + alarms, + notifications, + pictures, + movies, + downloads, + dcim, + documents, +} + +@HostApi(dartHostTestHandler: 'TestPathProviderApi') +abstract class PathProviderApi { + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + String? getTemporaryPath(); + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + String? getApplicationSupportPath(); + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + String? getApplicationDocumentsPath(); + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + String? getExternalStoragePath(); + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + List getExternalCachePaths(); + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + List getExternalStoragePaths(StorageDirectory directory); +} diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index f1dc92abdefc..64b953b2ca07 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,11 +2,11 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.14 +version: 2.0.15 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.1" + flutter: ">=3.0.0" flutter: plugin: @@ -29,4 +29,5 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter + pigeon: ^3.1.5 test: ^1.16.0 diff --git a/packages/path_provider/path_provider_android/test/messages_test.g.dart b/packages/path_provider/path_provider_android/test/messages_test.g.dart new file mode 100644 index 000000000000..dc8ee55acc3b --- /dev/null +++ b/packages/path_provider/path_provider_android/test/messages_test.g.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. +// Autogenerated from Pigeon (v3.1.5), 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 +// ignore_for_file: avoid_relative_lib_imports +// @dart = 2.12 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +// The following line is edited by hand to avoid confusing dart with overloaded types. +import 'package:path_provider_android/messages.g.dart'; + +class _TestPathProviderApiCodec extends StandardMessageCodec { + const _TestPathProviderApiCodec(); +} + +abstract class TestPathProviderApi { + static const MessageCodec codec = _TestPathProviderApiCodec(); + + String? getTemporaryPath(); + String? getApplicationSupportPath(); + String? getApplicationDocumentsPath(); + String? getExternalStoragePath(); + List getExternalCachePaths(); + List getExternalStoragePaths(StorageDirectory directory); + static void setup(TestPathProviderApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getTemporaryPath', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + // ignore message + final String? output = api.getTemporaryPath(); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getApplicationSupportPath', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + // ignore message + final String? output = api.getApplicationSupportPath(); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getApplicationDocumentsPath', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + // ignore message + final String? output = api.getApplicationDocumentsPath(); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getExternalStoragePath', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + // ignore message + final String? output = api.getExternalStoragePath(); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getExternalCachePaths', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + // ignore message + final List output = api.getExternalCachePaths(); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.PathProviderApi.getExternalStoragePaths', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.PathProviderApi.getExternalStoragePaths was null.'); + final List args = (message as List?)!; + + /// TODO(gaaclarke): The following line was tweaked by hand to address + /// https://github.com/flutter/flutter/issues/105742. Alternatively + /// the tests could be written with a mock BinaryMessenger but this is + /// how we want to address it eventually. + final StorageDirectory? arg_directory = + StorageDirectory.values[args[0] as int]; + assert(arg_directory != null, + 'Argument for dev.flutter.pigeon.PathProviderApi.getExternalStoragePaths was null, expected non-null StorageDirectory.'); + final List output = + api.getExternalStoragePaths(arg_directory!); + return {'result': output}; + }); + } + } + } +} diff --git a/packages/path_provider/path_provider_android/test/path_provider_android_test.dart b/packages/path_provider/path_provider_android/test/path_provider_android_test.dart index d2f9682bf6d7..2fe4a51fe5eb 100644 --- a/packages/path_provider/path_provider_android/test/path_provider_android_test.dart +++ b/packages/path_provider/path_provider_android/test/path_provider_android_test.dart @@ -2,73 +2,59 @@ // 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:path_provider_android/messages.g.dart' as messages; import 'package:path_provider_android/path_provider_android.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; +import 'messages_test.g.dart'; + +const String kTemporaryPath = 'temporaryPath'; +const String kApplicationSupportPath = 'applicationSupportPath'; +const String kLibraryPath = 'libraryPath'; +const String kApplicationDocumentsPath = 'applicationDocumentsPath'; +const String kExternalCachePaths = 'externalCachePaths'; +const String kExternalStoragePaths = 'externalStoragePaths'; +const String kDownloadsPath = 'downloadsPath'; + +class _Api implements TestPathProviderApi { + @override + String? getApplicationDocumentsPath() => kApplicationDocumentsPath; + + @override + String? getApplicationSupportPath() => kApplicationSupportPath; + + @override + List getExternalCachePaths() => [kExternalCachePaths]; + + @override + String? getExternalStoragePath() => kExternalStoragePaths; + + @override + List getExternalStoragePaths(messages.StorageDirectory directory) => + [kExternalStoragePaths]; + + @override + String? getTemporaryPath() => kTemporaryPath; +} void main() { TestWidgetsFlutterBinding.ensureInitialized(); - const String kTemporaryPath = 'temporaryPath'; - const String kApplicationSupportPath = 'applicationSupportPath'; - const String kLibraryPath = 'libraryPath'; - const String kApplicationDocumentsPath = 'applicationDocumentsPath'; - const String kExternalCachePaths = 'externalCachePaths'; - const String kExternalStoragePaths = 'externalStoragePaths'; - const String kDownloadsPath = 'downloadsPath'; group('PathProviderAndroid', () { late PathProviderAndroid pathProvider; - final List log = []; setUp(() async { pathProvider = PathProviderAndroid(); - TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger - .setMockMethodCallHandler(pathProvider.methodChannel, - (MethodCall methodCall) async { - log.add(methodCall); - switch (methodCall.method) { - case 'getTemporaryDirectory': - return kTemporaryPath; - case 'getApplicationSupportDirectory': - return kApplicationSupportPath; - case 'getLibraryDirectory': - return kLibraryPath; - case 'getApplicationDocumentsDirectory': - return kApplicationDocumentsPath; - case 'getExternalStorageDirectories': - return [kExternalStoragePaths]; - case 'getExternalCacheDirectories': - return [kExternalCachePaths]; - case 'getDownloadsDirectory': - return kDownloadsPath; - default: - return null; - } - }); - }); - - tearDown(() { - log.clear(); + TestPathProviderApi.setup(_Api()); }); test('getTemporaryPath', () async { final String? path = await pathProvider.getTemporaryPath(); - expect( - log, - [isMethodCall('getTemporaryDirectory', arguments: null)], - ); expect(path, kTemporaryPath); }); test('getApplicationSupportPath', () async { final String? path = await pathProvider.getApplicationSupportPath(); - expect( - log, - [ - isMethodCall('getApplicationSupportDirectory', arguments: null) - ], - ); expect(path, kApplicationSupportPath); }); @@ -83,47 +69,32 @@ void main() { test('getApplicationDocumentsPath', () async { final String? path = await pathProvider.getApplicationDocumentsPath(); - expect( - log, - [ - isMethodCall('getApplicationDocumentsDirectory', arguments: null) - ], - ); expect(path, kApplicationDocumentsPath); }); test('getExternalCachePaths succeeds', () async { final List? result = await pathProvider.getExternalCachePaths(); - expect( - log, - [isMethodCall('getExternalCacheDirectories', arguments: null)], - ); expect(result!.length, 1); expect(result.first, kExternalCachePaths); }); for (final StorageDirectory? type in [ - null, ...StorageDirectory.values ]) { test('getExternalStoragePaths (type: $type) android succeeds', () async { final List? result = await pathProvider.getExternalStoragePaths(type: type); - expect( - log, - [ - isMethodCall( - 'getExternalStorageDirectories', - arguments: {'type': type?.index}, - ) - ], - ); - expect(result!.length, 1); expect(result.first, kExternalStoragePaths); }); } // end of for-loop + test('getExternalStoragePaths with null android succeeds', () async { + final List? result = + await pathProvider.getExternalStoragePaths(type: null); + expect(result!.length, 0); + }); + test('getDownloadsPath fails', () async { try { await pathProvider.getDownloadsPath(); From a78274f88f628a52f217b6786618c22328dffc6f Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 16 Jun 2022 14:18:08 -0400 Subject: [PATCH 441/844] Roll Flutter from 873d343e091c to 7bad4eb9d871 (13 revisions) (#5984) --- .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 9681ede70611..893dba0db9dc 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -873d343e091c16a2ca9dd51ab609593038e49528 +7bad4eb9d871ee13ea2fb904eec8b9c645ba90d2 From c7aa9942a625192b4bed62be4f564a9a3b4f2a62 Mon Sep 17 00:00:00 2001 From: Navaron Bracke Date: Fri, 17 Jun 2022 01:42:04 +0200 Subject: [PATCH 442/844] [video_player] Fix disposed VideoPlayerController throwing an error when calling dispose() again (#5952) --- packages/video_player/video_player/CHANGELOG.md | 3 ++- .../video_player/video_player/lib/video_player.dart | 4 ++++ packages/video_player/video_player/pubspec.yaml | 2 +- .../video_player/test/video_player_test.dart | 11 +++++++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 379909b86e42..2b0ffadaecdf 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.4.5 * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). +* Fixes an exception when a disposed VideoPlayerController is disposed again. ## 2.4.4 diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 95bdc07dc86c..96aa881aba39 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -426,6 +426,10 @@ class VideoPlayerController extends ValueNotifier { @override Future dispose() async { + if (_isDisposed) { + return; + } + if (_creatingCompleter != null) { await _creatingCompleter!.future; if (!_isDisposed) { diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 26723992628f..bb0e8a8ec581 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/plugins/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.4.4 +version: 2.4.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index f4eda111fb92..728e3a29ad99 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -373,6 +373,17 @@ void main() { expect(await controller.position, isNull); }); + test('calling dispose() on disposed controller does not throw', () async { + final VideoPlayerController controller = VideoPlayerController.network( + 'https://127.0.0.1', + ); + + await controller.initialize(); + await controller.dispose(); + + expect(() async => await controller.dispose(), returnsNormally); + }); + test('play', () async { final VideoPlayerController controller = VideoPlayerController.network( 'https://127.0.0.1', From c3955d221178056351f05f33d042b3b96b08b9a3 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 16 Jun 2022 23:07:36 -0400 Subject: [PATCH 443/844] [url_launcher] Add a new launchUrl to platform interface (#5966) This creates a new platform interface method for launching that closely parallels the new public-facing API, so that implementations can switch to implementing a more platform-neutral implementation. This will pave the way for things like cleanly implementing `externalNonBrowserApplication` support on non-iOS platforms. A follow-up will switch the app-facing package to call this new methods instead of the legacy method. Implementation packages can adopt the new method as is useful for them; eventually we can do a cleanup pass if we want to fully deprecate the old method. --- .../CHANGELOG.md | 4 + .../lib/src/types.dart | 69 ++++++++++ .../lib/src/url_launcher_platform.dart | 94 ++++++++++++++ .../lib/url_launcher_platform_interface.dart | 68 +--------- .../pubspec.yaml | 2 +- .../test/url_launcher_platform_test.dart | 121 ++++++++++++++++++ 6 files changed, 291 insertions(+), 67 deletions(-) create mode 100644 packages/url_launcher/url_launcher_platform_interface/lib/src/types.dart create mode 100644 packages/url_launcher/url_launcher_platform_interface/lib/src/url_launcher_platform.dart create mode 100644 packages/url_launcher/url_launcher_platform_interface/test/url_launcher_platform_test.dart diff --git a/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md b/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md index 1a9c575c27cb..78818eff7bbf 100644 --- a/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.0 + +* Adds a new `launchUrl` method corresponding to the new app-facing interface. + ## 2.0.5 * Updates code for new analysis options. diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/src/types.dart b/packages/url_launcher/url_launcher_platform_interface/lib/src/types.dart new file mode 100644 index 000000000000..08d87e03a128 --- /dev/null +++ b/packages/url_launcher/url_launcher_platform_interface/lib/src/types.dart @@ -0,0 +1,69 @@ +// 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 desired mode to launch a URL. +/// +/// Support for these modes varies by platform. Platforms that do not support +/// the requested mode may substitute another mode. +enum PreferredLaunchMode { + /// Leaves the decision of how to launch the URL to the platform + /// implementation. + platformDefault, + + /// Loads the URL in an in-app web view (e.g., Safari View Controller). + inAppWebView, + + /// Passes the URL to the OS to be handled by another application. + externalApplication, + + /// Passes the URL to the OS to be handled by another non-browser application. + externalNonBrowserApplication, +} + +/// Additional configuration options for [PreferredLaunchMode.inAppWebView]. +/// +/// Not all options are supported on all platforms. This is a superset of +/// available options exposed across all implementations. +@immutable +class InAppWebViewConfiguration { + /// Creates a new WebViewConfiguration with the given settings. + const InAppWebViewConfiguration({ + this.enableJavaScript = true, + this.enableDomStorage = true, + this.headers = const {}, + }); + + /// Whether or not JavaScript is enabled for the web content. + final bool enableJavaScript; + + /// Whether or not DOM storage is enabled for the web content. + final bool enableDomStorage; + + /// Additional headers to pass in the load request. + final Map headers; +} + +/// Options for [launchUrl]. +@immutable +class LaunchOptions { + /// Creates a new parameter object with the given options. + const LaunchOptions({ + this.mode = PreferredLaunchMode.platformDefault, + this.webViewConfiguration = const InAppWebViewConfiguration(), + this.webOnlyWindowName, + }); + + /// The requested launch mode. + final PreferredLaunchMode mode; + + /// Configuration for the web view in [PreferredLaunchMode.inAppWebView] mode. + final InAppWebViewConfiguration webViewConfiguration; + + /// A web-platform-specific option to set the link target. + /// + /// Default behaviour when unset should be to open the url in a new tab. + final String? webOnlyWindowName; +} diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/src/url_launcher_platform.dart b/packages/url_launcher/url_launcher_platform_interface/lib/src/url_launcher_platform.dart new file mode 100644 index 000000000000..aa499db4ce6f --- /dev/null +++ b/packages/url_launcher/url_launcher_platform_interface/lib/src/url_launcher_platform.dart @@ -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. + +import 'dart:async'; + +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:url_launcher_platform_interface/link.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +import '../method_channel_url_launcher.dart'; + +/// The interface that implementations of url_launcher must implement. +/// +/// Platform implementations should extend this class rather than implement it as `url_launcher` +/// does not consider newly added methods to be breaking changes. Extending this class +/// (using `extends`) ensures that the subclass will get the default implementation, while +/// platform implementations that `implements` this interface will be broken by newly added +/// [UrlLauncherPlatform] methods. +abstract class UrlLauncherPlatform extends PlatformInterface { + /// Constructs a UrlLauncherPlatform. + UrlLauncherPlatform() : super(token: _token); + + static final Object _token = Object(); + + static UrlLauncherPlatform _instance = MethodChannelUrlLauncher(); + + /// The default instance of [UrlLauncherPlatform] to use. + /// + /// Defaults to [MethodChannelUrlLauncher]. + static UrlLauncherPlatform get instance => _instance; + + /// Platform-specific plugins should set this with their own platform-specific + /// class that extends [UrlLauncherPlatform] when they register themselves. + // TODO(amirh): Extract common platform interface logic. + // https://github.com/flutter/flutter/issues/43368 + static set instance(UrlLauncherPlatform instance) { + PlatformInterface.verify(instance, _token); + _instance = instance; + } + + /// The delegate used by the Link widget to build itself. + LinkDelegate? get linkDelegate; + + /// Returns `true` if this platform is able to launch [url]. + Future canLaunch(String url) { + throw UnimplementedError('canLaunch() has not been implemented.'); + } + + /// Passes [url] to the underlying platform for handling. + /// + /// Returns `true` if the given [url] was successfully launched. + /// + /// For documentation on the other arguments, see the `launch` documentation + /// in `package:url_launcher/url_launcher.dart`. + Future launch( + String url, { + required bool useSafariVC, + required bool useWebView, + required bool enableJavaScript, + required bool enableDomStorage, + required bool universalLinksOnly, + required Map headers, + String? webOnlyWindowName, + }) { + throw UnimplementedError('launch() has not been implemented.'); + } + + /// Passes [url] to the underlying platform for handling. + /// + /// Returns `true` if the given [url] was successfully launched. + Future launchUrl(String url, LaunchOptions options) { + final bool isWebURL = url.startsWith('http:') || url.startsWith('https:'); + final bool useWebView = options.mode == PreferredLaunchMode.inAppWebView || + (isWebURL && options.mode == PreferredLaunchMode.platformDefault); + + return launch( + url, + useSafariVC: useWebView, + useWebView: useWebView, + enableJavaScript: options.webViewConfiguration.enableJavaScript, + enableDomStorage: options.webViewConfiguration.enableDomStorage, + universalLinksOnly: + options.mode == PreferredLaunchMode.externalNonBrowserApplication, + headers: options.webViewConfiguration.headers, + webOnlyWindowName: options.webOnlyWindowName, + ); + } + + /// Closes the WebView, if one was opened earlier by [launch]. + Future closeWebView() { + throw UnimplementedError('closeWebView() has not been implemented.'); + } +} diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/url_launcher_platform_interface.dart b/packages/url_launcher/url_launcher_platform_interface/lib/url_launcher_platform_interface.dart index 18d64eff8dcb..3312c2f5cd28 100644 --- a/packages/url_launcher/url_launcher_platform_interface/lib/url_launcher_platform_interface.dart +++ b/packages/url_launcher/url_launcher_platform_interface/lib/url_launcher_platform_interface.dart @@ -2,69 +2,5 @@ // 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:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:url_launcher_platform_interface/link.dart'; - -import 'method_channel_url_launcher.dart'; - -/// The interface that implementations of url_launcher must implement. -/// -/// Platform implementations should extend this class rather than implement it as `url_launcher` -/// does not consider newly added methods to be breaking changes. Extending this class -/// (using `extends`) ensures that the subclass will get the default implementation, while -/// platform implementations that `implements` this interface will be broken by newly added -/// [UrlLauncherPlatform] methods. -abstract class UrlLauncherPlatform extends PlatformInterface { - /// Constructs a UrlLauncherPlatform. - UrlLauncherPlatform() : super(token: _token); - - static final Object _token = Object(); - - static UrlLauncherPlatform _instance = MethodChannelUrlLauncher(); - - /// The default instance of [UrlLauncherPlatform] to use. - /// - /// Defaults to [MethodChannelUrlLauncher]. - static UrlLauncherPlatform get instance => _instance; - - /// Platform-specific plugins should set this with their own platform-specific - /// class that extends [UrlLauncherPlatform] when they register themselves. - // TODO(amirh): Extract common platform interface logic. - // https://github.com/flutter/flutter/issues/43368 - static set instance(UrlLauncherPlatform instance) { - PlatformInterface.verify(instance, _token); - _instance = instance; - } - - /// The delegate used by the Link widget to build itself. - LinkDelegate? get linkDelegate; - - /// Returns `true` if this platform is able to launch [url]. - Future canLaunch(String url) { - throw UnimplementedError('canLaunch() has not been implemented.'); - } - - /// Returns `true` if the given [url] was successfully launched. - /// - /// For documentation on the other arguments, see the `launch` documentation - /// in `package:url_launcher/url_launcher.dart`. - Future launch( - String url, { - required bool useSafariVC, - required bool useWebView, - required bool enableJavaScript, - required bool enableDomStorage, - required bool universalLinksOnly, - required Map headers, - String? webOnlyWindowName, - }) { - throw UnimplementedError('launch() has not been implemented.'); - } - - /// Closes the WebView, if one was opened earlier by [launch]. - Future closeWebView() { - throw UnimplementedError('closeWebView() has not been implemented.'); - } -} +export 'src/types.dart'; +export 'src/url_launcher_platform.dart'; diff --git a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml index 140a1aee9938..76461ff7b979 100644 --- a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml +++ b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/u issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%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.0.5 +version: 2.1.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_platform_interface/test/url_launcher_platform_test.dart b/packages/url_launcher/url_launcher_platform_interface/test/url_launcher_platform_test.dart new file mode 100644 index 000000000000..f764f679f96d --- /dev/null +++ b/packages/url_launcher/url_launcher_platform_interface/test/url_launcher_platform_test.dart @@ -0,0 +1,121 @@ +// 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:url_launcher_platform_interface/link.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +class CapturingUrlLauncher extends UrlLauncherPlatform { + String? url; + bool? useSafariVC; + bool? useWebView; + bool? enableJavaScript; + bool? enableDomStorage; + bool? universalLinksOnly; + Map headers = {}; + String? webOnlyWindowName; + + @override + final LinkDelegate? linkDelegate = null; + + @override + Future launch( + String url, { + required bool useSafariVC, + required bool useWebView, + required bool enableJavaScript, + required bool enableDomStorage, + required bool universalLinksOnly, + required Map headers, + String? webOnlyWindowName, + }) async { + this.url = url; + this.useSafariVC = useSafariVC; + this.useWebView = useWebView; + this.enableJavaScript = enableJavaScript; + this.enableDomStorage = enableDomStorage; + this.universalLinksOnly = universalLinksOnly; + this.headers = headers; + this.webOnlyWindowName = webOnlyWindowName; + + return true; + } +} + +void main() { + test('launchUrl calls through to launch with default options for web URL', + () async { + final CapturingUrlLauncher launcher = CapturingUrlLauncher(); + + await launcher.launchUrl('https://flutter.dev', const LaunchOptions()); + + expect(launcher.url, 'https://flutter.dev'); + expect(launcher.useSafariVC, true); + expect(launcher.useWebView, true); + expect(launcher.enableJavaScript, true); + expect(launcher.enableDomStorage, true); + expect(launcher.universalLinksOnly, false); + expect(launcher.headers, isEmpty); + expect(launcher.webOnlyWindowName, null); + }); + + test('launchUrl calls through to launch with default options for non-web URL', + () async { + final CapturingUrlLauncher launcher = CapturingUrlLauncher(); + + await launcher.launchUrl('tel:123456789', const LaunchOptions()); + + expect(launcher.url, 'tel:123456789'); + expect(launcher.useSafariVC, false); + expect(launcher.useWebView, false); + expect(launcher.enableJavaScript, true); + expect(launcher.enableDomStorage, true); + expect(launcher.universalLinksOnly, false); + expect(launcher.headers, isEmpty); + expect(launcher.webOnlyWindowName, null); + }); + + test('launchUrl calls through to launch with universal links', () async { + final CapturingUrlLauncher launcher = CapturingUrlLauncher(); + + await launcher.launchUrl( + 'https://flutter.dev', + const LaunchOptions( + mode: PreferredLaunchMode.externalNonBrowserApplication)); + + expect(launcher.url, 'https://flutter.dev'); + expect(launcher.useSafariVC, false); + expect(launcher.useWebView, false); + expect(launcher.enableJavaScript, true); + expect(launcher.enableDomStorage, true); + expect(launcher.universalLinksOnly, true); + expect(launcher.headers, isEmpty); + expect(launcher.webOnlyWindowName, null); + }); + + test('launchUrl calls through to launch with all non-default options', + () async { + final CapturingUrlLauncher launcher = CapturingUrlLauncher(); + + await launcher.launchUrl( + 'https://flutter.dev', + const LaunchOptions( + mode: PreferredLaunchMode.externalApplication, + webViewConfiguration: InAppWebViewConfiguration( + enableJavaScript: false, + enableDomStorage: false, + headers: {'foo': 'bar'}), + webOnlyWindowName: 'a_name', + )); + + expect(launcher.url, 'https://flutter.dev'); + expect(launcher.useSafariVC, false); + expect(launcher.useWebView, false); + expect(launcher.enableJavaScript, false); + expect(launcher.enableDomStorage, false); + expect(launcher.universalLinksOnly, false); + expect(launcher.headers['foo'], 'bar'); + expect(launcher.webOnlyWindowName, 'a_name'); + }); +} From 5da706dbb85524a3fd8efa00c804c03eda5b5542 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 17 Jun 2022 14:37:31 -0400 Subject: [PATCH 444/844] Roll Flutter from 7bad4eb9d871 to 02558d69d923 (26 revisions) (#5988) * d88212c89 added microbenchmark for loading assets (flutter/flutter#105982) * d3bc2bbcd [framework] fix RangeSlider regression due to touch slop changes (flutter/flutter#106094) * ede7fc67f Add more CMake unit tests (flutter/flutter#106076) * 995b3324c Revert "Make RenderSliverGrid more accurately report overflow" (flutter/flutter#106123) * 9e67070f8 [Conductor] Update post submit location (flutter/flutter#106120) * f67d9b75f Revert "[Conductor] Update post submit location (#106120)" (flutter/flutter#106127) * a783e422d Fix SliverPadding geometry (flutter/flutter#106071) * 3f401a193 Ignore uses of soon-to-be deprecated `NullThrownError`. (flutter/flutter#105693) * 8e8a1c8c2 Fix `StretchingOverscrollIndicator` clipping and add `clipBehavior` parameter (flutter/flutter#105303) * 96813e936 [gen_keycodes] Clarify the README that the code scheme also applies to physical keys (flutter/flutter#106078) * ddeb0b99c [gen_keycodes] Remove invalid Web code maps (flutter/flutter#106074) * 32b22b85d parse build version on xcodeproj (flutter/flutter#105908) * f104be7ae Ignore body_might_complete_normally_catch_error violations (flutter/flutter#105795) * 2c15e3cae [flutter_tools] update test/src to null safety (flutter/flutter#106064) * b1b1ee9ca [web] Fix JS crash when FF blocks service workers. (flutter/flutter#106072) * fae31eecc [flutter_tools] temporary directory (flutter/flutter#105815) * f6f0d608b Update the platform properties for android bots (flutter/flutter#106146) * 157277372 Update package:archive and pin test_api (flutter/flutter#106157) * d08a1b02a Roll Flutter Engine from f8c0dc87bc53 to ee71e31c36ce (41 revisions) (flutter/flutter#106162) * 58007fc64 Fix debugPaintSize throws 'Null Check error' (flutter/flutter#106108) * c462cfa28 Roll Flutter Engine from ee71e31c36ce to 3c4ca2762e20 (2 revisions) (flutter/flutter#106167) * cb2569f63 Roll Flutter Engine from 3c4ca2762e20 to df5144dd451d (1 revision) (flutter/flutter#106171) * 526c33fe3 Roll Flutter Engine from df5144dd451d to 746b33282f74 (1 revision) (flutter/flutter#106175) * b29c64b3f Roll Flutter Engine from 746b33282f74 to 6cb83ab0f155 (1 revision) (flutter/flutter#106178) * 12f2a35aa Roll Plugins from 8bee94ca9822 to c3955d221178 (3 revisions) (flutter/flutter#106200) * 02558d69d Revert "Fix `StretchingOverscrollIndicator` clipping and add `clipBehavior` parameter" (flutter/flutter#106207) --- .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 893dba0db9dc..e82972e36e2c 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -7bad4eb9d871ee13ea2fb904eec8b9c645ba90d2 +02558d69d92384e7ed1b66f50006796342c8945a From 715593b72d1c882111f164a04edecd3a9c7d6cc6 Mon Sep 17 00:00:00 2001 From: Darren Austin Date: Fri, 17 Jun 2022 12:23:06 -0700 Subject: [PATCH 445/844] Added ignore deprecation comments to `styleFrom` button APIs (cont) (#5983) --- packages/camera/camera_android/CHANGELOG.md | 4 ++++ packages/camera/camera_android/example/lib/main.dart | 8 ++++++++ packages/camera/camera_android/pubspec.yaml | 2 +- packages/camera/camera_avfoundation/CHANGELOG.md | 4 ++++ packages/camera/camera_avfoundation/example/lib/main.dart | 8 ++++++++ packages/camera/camera_avfoundation/pubspec.yaml | 2 +- 6 files changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index 5bc7c8ae8d19..eab03e81bef5 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.8+1 + +* Ignores deprecation warnings for upcoming styleFrom button API changes. + ## 0.9.8 * Switches to internal method channel implementation. diff --git a/packages/camera/camera_android/example/lib/main.dart b/packages/camera/camera_android/example/lib/main.dart index 1f3e06d33b9b..1dbdabb7d11c 100644 --- a/packages/camera/camera_android/example/lib/main.dart +++ b/packages/camera/camera_android/example/lib/main.dart @@ -374,11 +374,15 @@ class _CameraExampleHomeState extends State Widget _exposureModeControlRowWidget() { final ButtonStyle styleAuto = TextButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: controller?.value.exposureMode == ExposureMode.auto ? Colors.orange : Colors.blue, ); final ButtonStyle styleLocked = TextButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: controller?.value.exposureMode == ExposureMode.locked ? Colors.orange : Colors.blue, @@ -460,11 +464,15 @@ class _CameraExampleHomeState extends State Widget _focusModeControlRowWidget() { final ButtonStyle styleAuto = TextButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: controller?.value.focusMode == FocusMode.auto ? Colors.orange : Colors.blue, ); final ButtonStyle styleLocked = TextButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: controller?.value.focusMode == FocusMode.locked ? Colors.orange : Colors.blue, diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 73eeaa720dbc..eca2b53949e9 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android description: Android implementation of the camera plugin. repository: https://github.com/flutter/plugins/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.9.8 +version: 0.9.8+1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index 5bc7c8ae8d19..eab03e81bef5 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.8+1 + +* Ignores deprecation warnings for upcoming styleFrom button API changes. + ## 0.9.8 * Switches to internal method channel implementation. diff --git a/packages/camera/camera_avfoundation/example/lib/main.dart b/packages/camera/camera_avfoundation/example/lib/main.dart index 1f3e06d33b9b..1dbdabb7d11c 100644 --- a/packages/camera/camera_avfoundation/example/lib/main.dart +++ b/packages/camera/camera_avfoundation/example/lib/main.dart @@ -374,11 +374,15 @@ class _CameraExampleHomeState extends State Widget _exposureModeControlRowWidget() { final ButtonStyle styleAuto = TextButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: controller?.value.exposureMode == ExposureMode.auto ? Colors.orange : Colors.blue, ); final ButtonStyle styleLocked = TextButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: controller?.value.exposureMode == ExposureMode.locked ? Colors.orange : Colors.blue, @@ -460,11 +464,15 @@ class _CameraExampleHomeState extends State Widget _focusModeControlRowWidget() { final ButtonStyle styleAuto = TextButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: controller?.value.focusMode == FocusMode.auto ? Colors.orange : Colors.blue, ); final ButtonStyle styleLocked = TextButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use primary: controller?.value.focusMode == FocusMode.locked ? Colors.orange : Colors.blue, diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index 231ce26227b8..f1a5edfb6c07 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_avfoundation description: iOS implementation of the camera plugin. repository: https://github.com/flutter/plugins/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.8 +version: 0.9.8+1 environment: sdk: ">=2.14.0 <3.0.0" From 42aebbe98eb04a23e78ad5c900d227cf8e3096ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jun 2022 20:39:10 -0700 Subject: [PATCH 446/844] [webview]: Bump gradle from 3.3.0 to 7.1.2 in /packages/webview_flutter/webview_flutter_android/example/android (#5844) --- .../webview_flutter_android/example/android/app/build.gradle | 2 +- .../webview_flutter_android/example/android/build.gradle | 2 +- .../webview_flutter_android/example/android/gradle.properties | 1 - .../example/android/gradle/wrapper/gradle-wrapper.properties | 2 +- .../webview_flutter_android/example/pubspec.yaml | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle b/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle index fdd8193eb2b6..19c77efa342a 100644 --- a/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle @@ -58,5 +58,5 @@ dependencies { testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - api 'androidx.test:core:1.2.0' + api 'androidx.test:core:1.4.0' } diff --git a/packages/webview_flutter/webview_flutter_android/example/android/build.gradle b/packages/webview_flutter/webview_flutter_android/example/android/build.gradle index e101ac08df55..e29a4431f2ae 100644 --- a/packages/webview_flutter/webview_flutter_android/example/android/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:7.1.2' } } diff --git a/packages/webview_flutter/webview_flutter_android/example/android/gradle.properties b/packages/webview_flutter/webview_flutter_android/example/android/gradle.properties index a6738207fd15..94adc3a3f97a 100644 --- a/packages/webview_flutter/webview_flutter_android/example/android/gradle.properties +++ b/packages/webview_flutter/webview_flutter_android/example/android/gradle.properties @@ -1,4 +1,3 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -android.enableR8=true diff --git a/packages/webview_flutter/webview_flutter_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/webview_flutter/webview_flutter_android/example/android/gradle/wrapper/gradle-wrapper.properties index 2819f022f1fd..cc5527d781a7 100644 --- a/packages/webview_flutter/webview_flutter_android/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/webview_flutter/webview_flutter_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip diff --git a/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml index 85990bd02139..b457ca6b860a 100644 --- a/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml @@ -20,7 +20,7 @@ dependencies: path: ../ dev_dependencies: - espresso: ^0.1.0+2 + espresso: ^0.2.0 flutter_driver: sdk: flutter flutter_test: From c3ba21345b16666b0fe11aef2077adc2a3b50962 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Tue, 21 Jun 2022 15:54:11 +0200 Subject: [PATCH 447/844] [webview_flutter] Fixes bug when onNavigationRequestCallback returns false (#5981) --- .../webview_flutter_android/CHANGELOG.md | 4 + .../lib/webview_android_widget.dart | 8 +- .../webview_flutter_android/pubspec.yaml | 2 +- .../test/webview_android_widget_test.dart | 190 ++++++++++++++++++ .../webview_android_widget_test.mocks.dart | 30 +++ 5 files changed, 229 insertions(+), 5 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 66393c88cbb3..67c633fda18a 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.13 + +* Fixes a bug which causes an exception when the `onNavigationRequestCallback` return `false`. + ## 2.8.12 * Bumps mockito-inline from 3.11.1 to 4.6.1. diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart index f1b130c7e365..ff6265dbad00 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart @@ -652,8 +652,8 @@ class WebViewAndroidWebViewClient extends android_webview.WebViewClient { if (returnValue is bool && returnValue) { loadUrl!(url, {}); - } else { - (returnValue as Future).then((bool shouldLoadUrl) { + } else if (returnValue is Future) { + returnValue.then((bool shouldLoadUrl) { if (shouldLoadUrl) { loadUrl!(url, {}); } @@ -677,8 +677,8 @@ class WebViewAndroidWebViewClient extends android_webview.WebViewClient { if (returnValue is bool && returnValue) { loadUrl!(request.url, {}); - } else { - (returnValue as Future).then((bool shouldLoadUrl) { + } else if (returnValue is Future) { + returnValue.then((bool shouldLoadUrl) { if (shouldLoadUrl) { loadUrl!(request.url, {}); } diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 53f25c723fda..759e9d73b050 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/plugins/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: 2.8.12 +version: 2.8.13 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart index a987f1cf548d..2432b35a4814 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_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:typed_data'; import 'package:flutter/widgets.dart'; @@ -24,6 +25,7 @@ import 'webview_android_widget_test.mocks.dart'; android_webview.WebSettings, android_webview.WebStorage, android_webview.WebView, + android_webview.WebResourceRequest, WebViewAndroidDownloadListener, WebViewAndroidJavaScriptChannel, WebViewAndroidWebChromeClient, @@ -843,4 +845,192 @@ void main() { verify(mockPlatformHostApi.setWebContentsDebuggingEnabled(false)); }); }); + + group('WebViewAndroidWebViewClient', () { + test( + 'urlLoading should call loadUrl when onNavigationRequestCallback returns true', + () { + final Completer completer = Completer(); + final WebViewAndroidWebViewClient webViewClient = + WebViewAndroidWebViewClient.handlesNavigation( + onPageStartedCallback: (_) {}, + onPageFinishedCallback: (_) {}, + onWebResourceErrorCallback: (_) {}, + onNavigationRequestCallback: ({ + required bool isForMainFrame, + required String url, + }) => + true, + loadUrl: (String url, Map? headers) async { + completer.complete(); + }); + + webViewClient.urlLoading(MockWebView(), 'https://flutter.dev'); + expect(completer.isCompleted, isTrue); + }); + + test( + 'urlLoading should call loadUrl when onNavigationRequestCallback returns a Future true', + () async { + final Completer completer = Completer(); + final WebViewAndroidWebViewClient webViewClient = + WebViewAndroidWebViewClient.handlesNavigation( + onPageStartedCallback: (_) {}, + onPageFinishedCallback: (_) {}, + onWebResourceErrorCallback: (_) {}, + onNavigationRequestCallback: ({ + required bool isForMainFrame, + required String url, + }) => + Future.value(true), + loadUrl: (String url, Map? headers) async { + completer.complete(); + }); + + webViewClient.urlLoading(MockWebView(), 'https://flutter.dev'); + expect(completer.future, completes); + }); + + test( + 'urlLoading should not call laodUrl when onNavigationRequestCallback returns false', + () async { + final WebViewAndroidWebViewClient webViewClient = + WebViewAndroidWebViewClient.handlesNavigation( + onPageStartedCallback: (_) {}, + onPageFinishedCallback: (_) {}, + onWebResourceErrorCallback: (_) {}, + onNavigationRequestCallback: ({ + required bool isForMainFrame, + required String url, + }) => + false, + loadUrl: (String url, Map? headers) async { + fail( + 'loadUrl should not be called if onNavigationRequestCallback returns false.'); + }); + + webViewClient.urlLoading(MockWebView(), 'https://flutter.dev'); + }); + + test( + 'urlLoading should not call loadUrl when onNavigationRequestCallback returns a Future false', + () { + final WebViewAndroidWebViewClient webViewClient = + WebViewAndroidWebViewClient.handlesNavigation( + onPageStartedCallback: (_) {}, + onPageFinishedCallback: (_) {}, + onWebResourceErrorCallback: (_) {}, + onNavigationRequestCallback: ({ + required bool isForMainFrame, + required String url, + }) => + Future.value(false), + loadUrl: (String url, Map? headers) async { + fail( + 'loadUrl should not be called if onNavigationRequestCallback returns false.'); + }); + + webViewClient.urlLoading(MockWebView(), 'https://flutter.dev'); + }); + + test( + 'requestLoading should call loadUrl when onNavigationRequestCallback returns true', + () { + final Completer completer = Completer(); + final MockWebResourceRequest mockRequest = MockWebResourceRequest(); + when(mockRequest.isForMainFrame).thenReturn(true); + when(mockRequest.url).thenReturn('https://flutter.dev'); + final WebViewAndroidWebViewClient webViewClient = + WebViewAndroidWebViewClient.handlesNavigation( + onPageStartedCallback: (_) {}, + onPageFinishedCallback: (_) {}, + onWebResourceErrorCallback: (_) {}, + onNavigationRequestCallback: ({ + required bool isForMainFrame, + required String url, + }) => + true, + loadUrl: (String url, Map? headers) async { + expect(url, 'https://flutter.dev'); + completer.complete(); + }); + + webViewClient.requestLoading(MockWebView(), mockRequest); + expect(completer.isCompleted, isTrue); + }); + + test( + 'requestLoading should call loadUrl when onNavigationRequestCallback returns a Future true', + () async { + final Completer completer = Completer(); + final MockWebResourceRequest mockRequest = MockWebResourceRequest(); + when(mockRequest.isForMainFrame).thenReturn(true); + when(mockRequest.url).thenReturn('https://flutter.dev'); + final WebViewAndroidWebViewClient webViewClient = + WebViewAndroidWebViewClient.handlesNavigation( + onPageStartedCallback: (_) {}, + onPageFinishedCallback: (_) {}, + onWebResourceErrorCallback: (_) {}, + onNavigationRequestCallback: ({ + required bool isForMainFrame, + required String url, + }) => + Future.value(true), + loadUrl: (String url, Map? headers) async { + expect(url, 'https://flutter.dev'); + completer.complete(); + }); + + webViewClient.requestLoading(MockWebView(), mockRequest); + expect(completer.future, completes); + }); + + test( + 'requestLoading should not call loadUrl when onNavigationRequestCallback returns false', + () { + final MockWebResourceRequest mockRequest = MockWebResourceRequest(); + when(mockRequest.isForMainFrame).thenReturn(true); + when(mockRequest.url).thenReturn('https://flutter.dev'); + final WebViewAndroidWebViewClient webViewClient = + WebViewAndroidWebViewClient.handlesNavigation( + onPageStartedCallback: (_) {}, + onPageFinishedCallback: (_) {}, + onWebResourceErrorCallback: (_) {}, + onNavigationRequestCallback: ({ + required bool isForMainFrame, + required String url, + }) => + false, + loadUrl: (String url, Map? headers) { + fail( + 'loadUrl should not be called if onNavigationRequestCallback returns false.'); + }); + + webViewClient.requestLoading(MockWebView(), mockRequest); + }); + + test( + 'requestLoading should not call loadUrl when onNavigationRequestCallback returns a Future false', + () { + final MockWebResourceRequest mockRequest = MockWebResourceRequest(); + when(mockRequest.isForMainFrame).thenReturn(true); + when(mockRequest.url).thenReturn('https://flutter.dev'); + final WebViewAndroidWebViewClient webViewClient = + WebViewAndroidWebViewClient.handlesNavigation( + onPageStartedCallback: (_) {}, + onPageFinishedCallback: (_) {}, + onWebResourceErrorCallback: (_) {}, + onNavigationRequestCallback: ({ + required bool isForMainFrame, + required String url, + }) => + Future.value(false), + loadUrl: (String url, Map? headers) { + fail( + 'loadUrl should not be called if onNavigationRequestCallback returns false.'); + }); + + webViewClient.requestLoading(MockWebView(), mockRequest); + }); + }); } diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart index 3385e7998ba9..78e60cac1b8e 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart @@ -286,6 +286,36 @@ class MockWebView extends _i1.Mock implements _i2.WebView { returnValueForMissingStub: Future.value()) as _i4.Future); } +/// A class which mocks [WebResourceRequest]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockWebResourceRequest extends _i1.Mock + implements _i2.WebResourceRequest { + MockWebResourceRequest() { + _i1.throwOnMissingStub(this); + } + + @override + String get url => + (super.noSuchMethod(Invocation.getter(#url), returnValue: '') as String); + @override + bool get isForMainFrame => (super + .noSuchMethod(Invocation.getter(#isForMainFrame), returnValue: false) + as bool); + @override + bool get hasGesture => + (super.noSuchMethod(Invocation.getter(#hasGesture), returnValue: false) + as bool); + @override + String get method => + (super.noSuchMethod(Invocation.getter(#method), returnValue: '') + as String); + @override + Map get requestHeaders => + (super.noSuchMethod(Invocation.getter(#requestHeaders), + returnValue: {}) as Map); +} + /// A class which mocks [WebViewAndroidDownloadListener]. /// /// See the documentation for Mockito's code generation for more information. From e868ca2374505a0279a56414eed138d045e63aee Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 21 Jun 2022 13:33:24 -0400 Subject: [PATCH 448/844] [camera] Fix exception in registerWith (#6009) Fixes a regression from an unintented change in behavior during the conversion to an in-app method channel for Android and iOS. Although the Dart code for their implementations is almost identical to the shared method channel version, the differences in initialization paths caused the platform versions to try to use the widget bindings before they had been set up: The constructor for a `dartPluginClass` is called during `registerWith`, which is before `main`, but the constructor for the default implementation isn't called until `CameraPlatform.instance` is called, since Dart automatically does lazy static class initializtion. To avoid the issue without forcing bindings to be initialized early, this makes setting up the platform channel listener lazily. Fixes https://github.com/flutter/flutter/issues/106236 --- packages/camera/camera_android/CHANGELOG.md | 4 ++ .../lib/src/android_camera.dart | 38 +++++++++---------- packages/camera/camera_android/pubspec.yaml | 2 +- .../test/android_camera_test.dart | 32 +++++++++++++--- .../camera/camera_avfoundation/CHANGELOG.md | 4 ++ .../lib/src/avfoundation_camera.dart | 38 +++++++++---------- .../camera/camera_avfoundation/pubspec.yaml | 2 +- .../test/avfoundation_camera_test.dart | 32 +++++++++++++--- 8 files changed, 98 insertions(+), 54 deletions(-) diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index eab03e81bef5..e9972aede28a 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.8+2 + +* Fixes exception in registerWith caused by the switch to an in-package method channel. + ## 0.9.8+1 * Ignores deprecation warnings for upcoming styleFrom button API changes. diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index 5fb3443ff91e..db6264cae011 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -19,14 +19,6 @@ const MethodChannel _channel = /// The Android implementation of [CameraPlatform] that uses method channels. class AndroidCamera extends CameraPlatform { - /// Construct a new method channel camera instance. - AndroidCamera() { - const MethodChannel channel = - MethodChannel('plugins.flutter.io/camera_android/fromPlatform'); - channel.setMethodCallHandler( - (MethodCall call) => handleDeviceMethodCall(call)); - } - /// Registers this class as the default instance of [CameraPlatform]. static void registerWith() { CameraPlatform.instance = AndroidCamera(); @@ -34,6 +26,12 @@ class AndroidCamera extends CameraPlatform { final Map _channels = {}; + /// The name of the channel that device events from the platform side are + /// sent on. + @visibleForTesting + static const String deviceEventChannelName = + 'plugins.flutter.io/camera_android/fromPlatform'; + /// The controller we need to broadcast the different events coming /// from handleMethodCall, specific to camera events. /// @@ -50,11 +48,15 @@ class AndroidCamera extends CameraPlatform { /// /// It is a `broadcast` because multiple controllers will connect to /// different stream views of this Controller. - /// This is only exposed for test purposes. It shouldn't be used by clients of - /// the plugin as it may break or change at any time. - @visibleForTesting - final StreamController deviceEventStreamController = - StreamController.broadcast(); + late final StreamController _deviceEventStreamController = + _createDeviceEventStreamController(); + + StreamController _createDeviceEventStreamController() { + // Set up the method handler lazily. + const MethodChannel channel = MethodChannel(deviceEventChannelName); + channel.setMethodCallHandler(_handleDeviceMethodCall); + return StreamController.broadcast(); + } // The stream to receive frames from the native code. StreamSubscription? _platformImageStreamSubscription; @@ -192,7 +194,7 @@ class AndroidCamera extends CameraPlatform { @override Stream onDeviceOrientationChanged() { - return deviceEventStreamController.stream + return _deviceEventStreamController.stream .whereType(); } @@ -518,14 +520,10 @@ class AndroidCamera extends CameraPlatform { } /// Converts messages received from the native platform into device events. - /// - /// This is only exposed for test purposes. It shouldn't be used by clients of - /// the plugin as it may break or change at any time. - @visibleForTesting - Future handleDeviceMethodCall(MethodCall call) async { + Future _handleDeviceMethodCall(MethodCall call) async { switch (call.method) { case 'orientation_changed': - deviceEventStreamController.add(DeviceOrientationChangedEvent( + _deviceEventStreamController.add(DeviceOrientationChangedEvent( deserializeDeviceOrientation( call.arguments['orientation']! as String))); break; diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index eca2b53949e9..7d93ecb22e47 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android description: Android implementation of the camera plugin. repository: https://github.com/flutter/plugins/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.9.8+1 +version: 0.9.8+2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_android/test/android_camera_test.dart b/packages/camera/camera_android/test/android_camera_test.dart index 9674b0c5a420..ca1f245019a8 100644 --- a/packages/camera/camera_android/test/android_camera_test.dart +++ b/packages/camera/camera_android/test/android_camera_test.dart @@ -25,6 +25,24 @@ void main() { expect(CameraPlatform.instance, isA()); }); + test('registration does not set message handlers', () async { + AndroidCamera.registerWith(); + + // Setting up a handler requires bindings to be initialized, and since + // registerWith is called very early in initialization the bindings won't + // have been initialized. While registerWith could intialize them, that + // could slow down startup, so instead the handler should be set up lazily. + final ByteData? response = await TestDefaultBinaryMessengerBinding + .instance!.defaultBinaryMessenger + .handlePlatformMessage( + AndroidCamera.deviceEventChannelName, + const StandardMethodCodec().encodeMethodCall(const MethodCall( + 'orientation_changed', + {'orientation': 'portraitDown'})), + (ByteData? data) {}); + expect(response, null); + }); + group('Creation, Initialization & Disposal Tests', () { test('Should send creation data and receive back a camera id', () async { // Arrange @@ -402,12 +420,14 @@ void main() { // Emit test events const DeviceOrientationChangedEvent event = DeviceOrientationChangedEvent(DeviceOrientation.portraitUp); - await camera.handleDeviceMethodCall( - MethodCall('orientation_changed', event.toJson())); - await camera.handleDeviceMethodCall( - MethodCall('orientation_changed', event.toJson())); - await camera.handleDeviceMethodCall( - MethodCall('orientation_changed', event.toJson())); + for (int i = 0; i < 3; i++) { + await TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger + .handlePlatformMessage( + AndroidCamera.deviceEventChannelName, + const StandardMethodCodec().encodeMethodCall( + MethodCall('orientation_changed', event.toJson())), + null); + } // Assert expect(await streamQueue.next, event); diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index eab03e81bef5..e9972aede28a 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.8+2 + +* Fixes exception in registerWith caused by the switch to an in-package method channel. + ## 0.9.8+1 * Ignores deprecation warnings for upcoming styleFrom button API changes. diff --git a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart index 1bff9011586b..c75d41f47c81 100644 --- a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart +++ b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart @@ -19,14 +19,6 @@ const MethodChannel _channel = /// An iOS implementation of [CameraPlatform] based on AVFoundation. class AVFoundationCamera extends CameraPlatform { - /// Construct a new method channel camera instance. - AVFoundationCamera() { - const MethodChannel channel = - MethodChannel('plugins.flutter.io/camera_avfoundation/fromPlatform'); - channel.setMethodCallHandler( - (MethodCall call) => handleDeviceMethodCall(call)); - } - /// Registers this class as the default instance of [CameraPlatform]. static void registerWith() { CameraPlatform.instance = AVFoundationCamera(); @@ -34,6 +26,12 @@ class AVFoundationCamera extends CameraPlatform { final Map _channels = {}; + /// The name of the channel that device events from the platform side are + /// sent on. + @visibleForTesting + static const String deviceEventChannelName = + 'plugins.flutter.io/camera_avfoundation/fromPlatform'; + /// The controller we need to broadcast the different events coming /// from handleMethodCall, specific to camera events. /// @@ -50,11 +48,15 @@ class AVFoundationCamera extends CameraPlatform { /// /// It is a `broadcast` because multiple controllers will connect to /// different stream views of this Controller. - /// This is only exposed for test purposes. It shouldn't be used by clients of - /// the plugin as it may break or change at any time. - @visibleForTesting - final StreamController deviceEventStreamController = - StreamController.broadcast(); + late final StreamController _deviceEventStreamController = + _createDeviceEventStreamController(); + + StreamController _createDeviceEventStreamController() { + // Set up the method handler lazily. + const MethodChannel channel = MethodChannel(deviceEventChannelName); + channel.setMethodCallHandler(_handleDeviceMethodCall); + return StreamController.broadcast(); + } // The stream to receive frames from the native code. StreamSubscription? _platformImageStreamSubscription; @@ -192,7 +194,7 @@ class AVFoundationCamera extends CameraPlatform { @override Stream onDeviceOrientationChanged() { - return deviceEventStreamController.stream + return _deviceEventStreamController.stream .whereType(); } @@ -523,14 +525,10 @@ class AVFoundationCamera extends CameraPlatform { } /// Converts messages received from the native platform into device events. - /// - /// This is only exposed for test purposes. It shouldn't be used by clients of - /// the plugin as it may break or change at any time. - @visibleForTesting - Future handleDeviceMethodCall(MethodCall call) async { + Future _handleDeviceMethodCall(MethodCall call) async { switch (call.method) { case 'orientation_changed': - deviceEventStreamController.add(DeviceOrientationChangedEvent( + _deviceEventStreamController.add(DeviceOrientationChangedEvent( deserializeDeviceOrientation( call.arguments['orientation']! as String))); break; diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index f1a5edfb6c07..adb0bf8dbafb 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_avfoundation description: iOS implementation of the camera plugin. repository: https://github.com/flutter/plugins/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.8+1 +version: 0.9.8+2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart index 4b32d2e50b4a..67adcfab81f2 100644 --- a/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart +++ b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart @@ -25,6 +25,24 @@ void main() { expect(CameraPlatform.instance, isA()); }); + test('registration does not set message handlers', () async { + AVFoundationCamera.registerWith(); + + // Setting up a handler requires bindings to be initialized, and since + // registerWith is called very early in initialization the bindings won't + // have been initialized. While registerWith could intialize them, that + // could slow down startup, so instead the handler should be set up lazily. + final ByteData? response = await TestDefaultBinaryMessengerBinding + .instance!.defaultBinaryMessenger + .handlePlatformMessage( + AVFoundationCamera.deviceEventChannelName, + const StandardMethodCodec().encodeMethodCall(const MethodCall( + 'orientation_changed', + {'orientation': 'portraitDown'})), + (ByteData? data) {}); + expect(response, null); + }); + group('Creation, Initialization & Disposal Tests', () { test('Should send creation data and receive back a camera id', () async { // Arrange @@ -402,12 +420,14 @@ void main() { // Emit test events const DeviceOrientationChangedEvent event = DeviceOrientationChangedEvent(DeviceOrientation.portraitUp); - await camera.handleDeviceMethodCall( - MethodCall('orientation_changed', event.toJson())); - await camera.handleDeviceMethodCall( - MethodCall('orientation_changed', event.toJson())); - await camera.handleDeviceMethodCall( - MethodCall('orientation_changed', event.toJson())); + for (int i = 0; i < 3; i++) { + await TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger + .handlePlatformMessage( + AVFoundationCamera.deviceEventChannelName, + const StandardMethodCodec().encodeMethodCall( + MethodCall('orientation_changed', event.toJson())), + null); + } // Assert expect(await streamQueue.next, event); From f33899d90a4e0b92c6814b3977afda521e46e5d7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 21 Jun 2022 15:59:06 -0400 Subject: [PATCH 449/844] Roll Flutter from 02558d69d923 to dfaec11ba519 (29 revisions) (#6011) --- .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 e82972e36e2c..0c70247eb92b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -02558d69d92384e7ed1b66f50006796342c8945a +dfaec11ba519421ccc6869226dd8930bb5689fb6 From 41722ee5594aac1b4048036f840a98e0369bb7c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jun 2022 18:04:43 -0700 Subject: [PATCH 450/844] [webview]: Bump annotation from 1.0.0 to 1.4.0 in /packages/webview_flutter/webview_flutter_android/android (#5992) * [webview]: Bump annotation Bumps annotation from 1.0.0 to 1.4.0. --- updated-dependencies: - dependency-name: androidx.annotation:annotation dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * version bump Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> --- packages/webview_flutter/webview_flutter_android/CHANGELOG.md | 4 ++++ .../webview_flutter_android/android/build.gradle | 2 +- packages/webview_flutter/webview_flutter_android/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 67c633fda18a..12a34ecd00dd 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.14 + +* Bumps androidx.annotation from 1.0.0 to 1.4.0. + ## 2.8.13 * Fixes a bug which causes an exception when the `onNavigationRequestCallback` return `false`. diff --git a/packages/webview_flutter/webview_flutter_android/android/build.gradle b/packages/webview_flutter/webview_flutter_android/android/build.gradle index c7c6d992c823..20d7660ece50 100644 --- a/packages/webview_flutter/webview_flutter_android/android/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/android/build.gradle @@ -35,7 +35,7 @@ android { } dependencies { - implementation 'androidx.annotation:annotation:1.0.0' + implementation 'androidx.annotation:annotation:1.4.0' implementation 'androidx.webkit:webkit:1.0.0' testImplementation 'junit:junit:4.12' testImplementation 'org.mockito:mockito-inline:4.6.1' diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 759e9d73b050..a386cad9028c 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/plugins/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: 2.8.13 +version: 2.8.14 environment: sdk: ">=2.14.0 <3.0.0" From dfd1b6e7d83e439c6533ef62f601473ca3c59c5b Mon Sep 17 00:00:00 2001 From: Alexandre Ardhuin Date: Wed, 22 Jun 2022 16:59:10 +0200 Subject: [PATCH 451/844] Ignore upcoming warnings (#6007) --- packages/camera/camera/CHANGELOG.md | 4 ++++ packages/camera/camera/test/camera_value_test.dart | 4 ++++ packages/camera/camera_web/CHANGELOG.md | 4 ++++ .../example/integration_test/camera_service_test.dart | 2 ++ .../camera_web/example/integration_test/camera_web_test.dart | 2 ++ packages/camera/camera_web/lib/src/camera_service.dart | 2 ++ packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md | 4 ++++ .../example/integration_test/google_map_inspector.dart | 2 ++ packages/image_picker/image_picker_ios/CHANGELOG.md | 4 ++++ packages/image_picker/image_picker_ios/test/test_api.dart | 2 ++ packages/ios_platform_images/CHANGELOG.md | 4 ++++ packages/ios_platform_images/lib/ios_platform_images.dart | 2 ++ packages/video_player/video_player_android/CHANGELOG.md | 4 ++++ .../video_player_android/test/android_video_player_test.dart | 2 ++ packages/video_player/video_player_android/test/test_api.dart | 2 ++ packages/video_player/video_player_avfoundation/CHANGELOG.md | 4 ++++ .../test/avfoundation_video_player_test.dart | 2 ++ .../video_player/video_player_avfoundation/test/test_api.dart | 2 ++ .../video_player/video_player_platform_interface/CHANGELOG.md | 4 ++++ .../test/method_channel_video_player_test.dart | 2 ++ packages/webview_flutter/webview_flutter_android/CHANGELOG.md | 4 ++++ .../lib/src/android_webview_api_impls.dart | 2 ++ .../webview_flutter_platform_interface/CHANGELOG.md | 4 ++++ .../test/src/method_channel/webview_method_channel_test.dart | 4 ++++ .../webview_flutter/webview_flutter_wkwebview/CHANGELOG.md | 4 ++++ .../webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart | 2 ++ .../test/src/web_kit_webview_widget_test.dart | 2 ++ 27 files changed, 80 insertions(+) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 7f4de92e0d97..b9af4eb81e17 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). + ## 0.9.8+1 * Ignores deprecation warnings for upcoming styleFrom button API changes. diff --git a/packages/camera/camera/test/camera_value_test.dart b/packages/camera/camera/test/camera_value_test.dart index d718d5e48f02..e70f2ce69868 100644 --- a/packages/camera/camera/test/camera_value_test.dart +++ b/packages/camera/camera/test/camera_value_test.dart @@ -2,9 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'dart:ui'; import 'package:camera/camera.dart'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 7d8c2ab6c03b..a3522cfee52d 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). + ## 0.3.0 * **BREAKING CHANGE**: Renames error code `cameraPermission` to `CameraAccessDenied` to be consistent with other platforms. diff --git a/packages/camera/camera_web/example/integration_test/camera_service_test.dart b/packages/camera/camera_web/example/integration_test/camera_service_test.dart index 6bc03429940c..96debbf0f491 100644 --- a/packages/camera/camera_web/example/integration_test/camera_service_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_service_test.dart @@ -4,6 +4,8 @@ import 'dart:html'; import 'dart:js_util' as js_util; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'dart:ui'; import 'package:camera_platform_interface/camera_platform_interface.dart'; 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 143783f5225b..7fe6266587ae 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 @@ -4,6 +4,8 @@ import 'dart:async'; import 'dart:html'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'dart:ui'; import 'package:async/async.dart'; diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index b118169f0618..5f4a5fdde9a4 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -3,6 +3,8 @@ // found in the LICENSE file. import 'dart:html' as html; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'dart:ui'; import 'package:camera_platform_interface/camera_platform_interface.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 2bf5b17e1f80..84f7867905ff 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). + ## 2.1.8 * Switches to new platform interface versions of `buildView` and diff --git a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart index 34baa902ab1b..fe3461b9142f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.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. +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'dart:typed_data'; import 'package:flutter/services.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index 86ee7d0a2d00..978046075a54 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). + ## 0.8.5+5 * Adds non-deprecated codepaths for iOS 13+. diff --git a/packages/image_picker/image_picker_ios/test/test_api.dart b/packages/image_picker/image_picker_ios/test/test_api.dart index d22a26b2489b..8b20f2129d89 100644 --- a/packages/image_picker/image_picker_ios/test/test_api.dart +++ b/packages/image_picker/image_picker_ios/test/test_api.dart @@ -7,6 +7,8 @@ // ignore_for_file: avoid_relative_lib_imports // @dart = 2.12 import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index f7616dc9aa5d..eadf63de9b92 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). + ## 0.2.0+9 * Ignores the warning for the upcoming deprecation of `DecoderCallback`. diff --git a/packages/ios_platform_images/lib/ios_platform_images.dart b/packages/ios_platform_images/lib/ios_platform_images.dart index 29733a3f8c2d..fa40eb08fafd 100644 --- a/packages/ios_platform_images/lib/ios_platform_images.dart +++ b/packages/ios_platform_images/lib/ios_platform_images.dart @@ -3,6 +3,8 @@ // found in the LICENSE file. import 'dart:async'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'dart:typed_data'; import 'dart:ui' as ui; diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index beabb2344524..920c9102e6b2 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). + ## 2.3.6 * Updates references to the obsolete master branch. diff --git a/packages/video_player/video_player_android/test/android_video_player_test.dart b/packages/video_player/video_player_android/test/android_video_player_test.dart index aad31e4d83cc..a0512a1d1d88 100644 --- a/packages/video_player/video_player_android/test/android_video_player_test.dart +++ b/packages/video_player/video_player_android/test/android_video_player_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. +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'dart:ui'; import 'package:flutter/services.dart'; diff --git a/packages/video_player/video_player_android/test/test_api.dart b/packages/video_player/video_player_android/test/test_api.dart index c0ff23de9c22..6361522e247c 100644 --- a/packages/video_player/video_player_android/test/test_api.dart +++ b/packages/video_player/video_player_android/test/test_api.dart @@ -8,6 +8,8 @@ // @dart = 2.12 import 'dart:async'; import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 90d8ee61819e..bb5c940d3fad 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). + ## 2.3.5 * Updates references to the obsolete master branch. diff --git a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart index 9b6d1dfa195c..1e6b024d9a5c 100644 --- a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart +++ b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'dart:ui'; import 'package:flutter/services.dart'; diff --git a/packages/video_player/video_player_avfoundation/test/test_api.dart b/packages/video_player/video_player_avfoundation/test/test_api.dart index af6d072107c8..c8f7bbd026a5 100644 --- a/packages/video_player/video_player_avfoundation/test/test_api.dart +++ b/packages/video_player/video_player_avfoundation/test/test_api.dart @@ -8,6 +8,8 @@ // @dart = 2.12 import 'dart:async'; import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index 2229bf3bbfd4..cf5d0168efb3 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). + ## 5.1.3 * Updates references to the obsolete master branch. diff --git a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart b/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart index ca17196cf523..924dd08e464d 100644 --- a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart +++ b/packages/video_player/video_player_platform_interface/test/method_channel_video_player_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. +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'dart:ui'; import 'package:flutter/services.dart'; diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 12a34ecd00dd..7261cc1c2cb5 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). + ## 2.8.14 * Bumps androidx.annotation from 1.0.0 to 1.4.0. 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 b40a0518dca0..b1268f4f9cb0 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 @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'dart:typed_data'; import 'package:flutter/services.dart'; diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index 86b79335d7ba..565bfee21186 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). + ## 1.9.1 * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart index a34262fe460a..8b9a4ceebc91 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart @@ -2,8 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'dart:typed_data'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index f1cfcbc25375..979b0f797968 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). + ## 2.8.1 * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart index 2ed1602b9ac9..88aefc18e454 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart @@ -4,6 +4,8 @@ import 'dart:math'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'package:flutter/painting.dart' show Color; import 'package:flutter/services.dart'; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index 3580afe1c3a2..b5f7b1a486dd 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -3,6 +3,8 @@ // found in the LICENSE file. import 'dart:math'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'dart:typed_data'; import 'package:flutter/material.dart'; From 3a1ae795c4a0aec80bae140db6234dc16dc7cd6c Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 22 Jun 2022 14:29:12 -0400 Subject: [PATCH 452/844] [ci] Ensure complete dependabot coverage (#5976) --- .cirrus.yml | 1 + .github/dependabot.yml | 30 +++- script/tool/CHANGELOG.md | 1 + .../lib/src/dependabot_check_command.dart | 114 ++++++++++++++ script/tool/lib/src/main.dart | 2 + .../test/dependabot_check_command_test.dart | 141 ++++++++++++++++++ 6 files changed, 286 insertions(+), 3 deletions(-) create mode 100644 script/tool/lib/src/dependabot_check_command.dart create mode 100644 script/tool/test/dependabot_check_command_test.dart diff --git a/.cirrus.yml b/.cirrus.yml index 70f9ddbfb251..6ea4680f6dec 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -113,6 +113,7 @@ task: # run with --require-excerpts and no exclusions. - ./script/tool_runner.sh readme-check --require-excerpts --exclude=script/configs/temp_exclude_excerpt.yaml license_script: dart $PLUGIN_TOOL license-check + dependabot_script: dart $PLUGIN_TOOL dependabot-check - name: federated_safety # This check is only meaningful for PRs, as it validates changes # rather than state. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d5ca4f193ecc..33094a27e9a4 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,7 +1,15 @@ version: 2 updates: - package-ecosystem: "gradle" - directory: "/packages/camera/camera/android" + directory: "/packages/camera/camera_android/android" + commit-message: + prefix: "[camera]" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/camera/camera_android/example/android/app" commit-message: prefix: "[camera]" schedule: @@ -216,6 +224,14 @@ updates: interval: "weekly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/packages/shared_preferences/shared_preferences_android/android" + commit-message: + prefix: "[shared_pref]" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" directory: "/packages/shared_preferences/shared_preferences_android/example/android/app" commit-message: @@ -248,6 +264,14 @@ updates: interval: "weekly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/packages/video_player/video_player/example/android/app" + commit-message: + prefix: "[video_player]" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" directory: "/packages/video_player/video_player_android/android" commit-message: @@ -281,13 +305,13 @@ updates: open-pull-requests-limit: 10 - package-ecosystem: "gradle" - directory: "/packages/webview_flutter/webview_flutter_android/example/android" + directory: "/packages/webview_flutter/webview_flutter_android/example/android/app" commit-message: prefix: "[webview]" schedule: interval: "weekly" open-pull-requests-limit: 10 - + - package-ecosystem: "github-actions" directory: "/" commit-message: diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 36d8d23eb753..07cafee7f8b6 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -3,6 +3,7 @@ - Supports empty custom analysis allow list files. - `drive-examples` now validates files to ensure that they don't accidentally use `test(...)`. +- Adds a new `dependabot-check` command to ensure complete Dependabot coverage. ## 0.8.6 diff --git a/script/tool/lib/src/dependabot_check_command.dart b/script/tool/lib/src/dependabot_check_command.dart new file mode 100644 index 000000000000..5aa762e916e5 --- /dev/null +++ b/script/tool/lib/src/dependabot_check_command.dart @@ -0,0 +1,114 @@ +// 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:async'; + +import 'package:file/file.dart'; +import 'package:git/git.dart'; +import 'package:yaml/yaml.dart'; + +import 'common/core.dart'; +import 'common/package_looping_command.dart'; +import 'common/repository_package.dart'; + +/// A command to verify Dependabot configuration coverage of packages. +class DependabotCheckCommand extends PackageLoopingCommand { + /// Creates Dependabot check command instance. + DependabotCheckCommand(Directory packagesDir, {GitDir? gitDir}) + : super(packagesDir, gitDir: gitDir) { + argParser.addOption(_configPathFlag, + help: 'Path to the Dependabot configuration file', + defaultsTo: '.github/dependabot.yml'); + } + + static const String _configPathFlag = 'config'; + + late Directory _repoRoot; + + // The set of directories covered by "gradle" entries in the config. + Set _gradleDirs = const {}; + + @override + final String name = 'dependabot-check'; + + @override + final String description = + 'Checks that all packages have Dependabot coverage.'; + + @override + final PackageLoopingType packageLoopingType = + PackageLoopingType.includeAllSubpackages; + + @override + final bool hasLongOutput = false; + + @override + Future initializeRun() async { + _repoRoot = packagesDir.fileSystem.directory((await gitDir).path); + + final YamlMap config = loadYaml(_repoRoot + .childFile(getStringArg(_configPathFlag)) + .readAsStringSync()) as YamlMap; + final dynamic entries = config['updates']; + if (entries is! YamlList) { + return; + } + + const String typeKey = 'package-ecosystem'; + const String dirKey = 'directory'; + _gradleDirs = entries + .where((dynamic entry) => entry[typeKey] == 'gradle') + .map((dynamic entry) => (entry as YamlMap)[dirKey] as String) + .toSet(); + } + + @override + Future runForPackage(RepositoryPackage package) async { + bool skipped = true; + final List errors = []; + + final RunState gradleState = _validateDependabotGradleCoverage(package); + skipped = skipped && gradleState == RunState.skipped; + if (gradleState == RunState.failed) { + printError('${indentation}Missing Gradle coverage.'); + errors.add('Missing Gradle coverage'); + } + + // TODO(stuartmorgan): Add other ecosystem checks here as more are enabled. + + if (skipped) { + return PackageResult.skip('No supported package ecosystems'); + } + return errors.isEmpty + ? PackageResult.success() + : PackageResult.fail(errors); + } + + /// Returns the state for the Dependabot coverage of the Gradle ecosystem for + /// [package]: + /// - succeeded if it includes gradle and is covered. + /// - failed if it includes gradle and is not covered. + /// - skipped if it doesn't include gradle. + RunState _validateDependabotGradleCoverage(RepositoryPackage package) { + final Directory androidDir = + package.platformDirectory(FlutterPlatform.android); + final Directory appDir = androidDir.childDirectory('app'); + if (appDir.existsSync()) { + // It's an app, so only check for the app directory to be covered. + final String dependabotPath = + '/${getRelativePosixPath(appDir, from: _repoRoot)}'; + return _gradleDirs.contains(dependabotPath) + ? RunState.succeeded + : RunState.failed; + } else if (androidDir.existsSync()) { + // It's a library, so only check for the android directory to be covered. + final String dependabotPath = + '/${getRelativePosixPath(androidDir, from: _repoRoot)}'; + return _gradleDirs.contains(dependabotPath) + ? RunState.succeeded + : RunState.failed; + } + return RunState.skipped; + } +} diff --git a/script/tool/lib/src/main.dart b/script/tool/lib/src/main.dart index 739aef56878d..966e7b6be56a 100644 --- a/script/tool/lib/src/main.dart +++ b/script/tool/lib/src/main.dart @@ -7,6 +7,7 @@ import 'dart:io' as io; import 'package:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:file/local.dart'; +import 'package:flutter_plugin_tools/src/dependabot_check_command.dart'; import 'analyze_command.dart'; import 'build_examples_command.dart'; @@ -55,6 +56,7 @@ void main(List args) { ..addCommand(BuildExamplesCommand(packagesDir)) ..addCommand(CreateAllPluginsAppCommand(packagesDir)) ..addCommand(CustomTestCommand(packagesDir)) + ..addCommand(DependabotCheckCommand(packagesDir)) ..addCommand(DriveExamplesCommand(packagesDir)) ..addCommand(FederationSafetyCheckCommand(packagesDir)) ..addCommand(FirebaseTestLabCommand(packagesDir)) diff --git a/script/tool/test/dependabot_check_command_test.dart b/script/tool/test/dependabot_check_command_test.dart new file mode 100644 index 000000000000..a4c8693b2c21 --- /dev/null +++ b/script/tool/test/dependabot_check_command_test.dart @@ -0,0 +1,141 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_plugin_tools/src/common/core.dart'; +import 'package:flutter_plugin_tools/src/dependabot_check_command.dart'; +import 'package:mockito/mockito.dart'; +import 'package:test/test.dart'; + +import 'common/plugin_command_test.mocks.dart'; +import 'util.dart'; + +void main() { + late CommandRunner runner; + late FileSystem fileSystem; + late Directory root; + late Directory packagesDir; + + setUp(() { + fileSystem = MemoryFileSystem(); + root = fileSystem.currentDirectory; + packagesDir = root.childDirectory('packages'); + + final MockGitDir gitDir = MockGitDir(); + when(gitDir.path).thenReturn(root.path); + + final DependabotCheckCommand command = DependabotCheckCommand( + packagesDir, + gitDir: gitDir, + ); + runner = CommandRunner( + 'dependabot_test', 'Test for $DependabotCheckCommand'); + runner.addCommand(command); + }); + + void _setDependabotCoverage({ + Iterable gradleDirs = const [], + }) { + final Iterable gradleEntries = + gradleDirs.map((String directory) => ''' + - package-ecosystem: "gradle" + directory: "/$directory" + schedule: + interval: "daily" +'''); + final File configFile = + root.childDirectory('.github').childFile('dependabot.yml'); + configFile.createSync(recursive: true); + configFile.writeAsStringSync(''' +version: 2 +updates: +${gradleEntries.join('\n')} +'''); + } + + test('skips with no supported ecosystems', () async { + _setDependabotCoverage(); + createFakePackage('a_package', packagesDir); + + final List output = + await runCapturingPrint(runner, ['dependabot-check']); + + expect( + output, + containsAllInOrder([ + contains('SKIPPING: No supported package ecosystems'), + ])); + }); + + test('fails for app missing Gradle coverage', () async { + _setDependabotCoverage(); + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + package.directory + .childDirectory('example') + .childDirectory('android') + .childDirectory('app') + .createSync(recursive: true); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['dependabot-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Missing Gradle coverage.'), + contains('a_package/example:\n' + ' Missing Gradle coverage') + ])); + }); + + test('fails for plugin missing Gradle coverage', () async { + _setDependabotCoverage(); + final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir); + plugin.directory.childDirectory('android').createSync(recursive: true); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['dependabot-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Missing Gradle coverage.'), + contains('a_plugin:\n' + ' Missing Gradle coverage') + ])); + }); + + test('passes for correct Gradle coverage', () async { + _setDependabotCoverage(gradleDirs: [ + 'packages/a_plugin/android', + 'packages/a_plugin/example/android/app', + ]); + final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir); + // Test the plugin. + plugin.directory.childDirectory('android').createSync(recursive: true); + // And its example app. + plugin.directory + .childDirectory('example') + .childDirectory('android') + .childDirectory('app') + .createSync(recursive: true); + + final List output = + await runCapturingPrint(runner, ['dependabot-check']); + + expect(output, + containsAllInOrder([contains('Ran for 2 package(s)')])); + }); +} From 7fafe737b27b8c88a64cc973023d0d9c8d73db4e Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 22 Jun 2022 14:34:11 -0400 Subject: [PATCH 453/844] [url_launcher] Switch to new launchUrl interface (#5985) --- .../url_launcher/url_launcher/CHANGELOG.md | 3 +- .../url_launcher/lib/src/type_conversion.dart | 32 +++++++++++++++++ .../lib/src/url_launcher_string.dart | 26 +++++--------- .../lib/src/url_launcher_uri.dart | 20 +++++------ .../url_launcher/url_launcher/pubspec.yaml | 4 +-- .../url_launcher/test/link_test.dart | 8 ++--- .../mocks/mock_url_launcher_platform.dart | 19 ++++++++-- .../test/src/url_launcher_string_test.dart | 36 +++++++------------ .../test/src/url_launcher_uri_test.dart | 33 ++++++----------- 9 files changed, 98 insertions(+), 83 deletions(-) create mode 100644 packages/url_launcher/url_launcher/lib/src/type_conversion.dart diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 182119345ccc..e5e98f658617 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 6.1.4 +* Adopts new platform interface method for launching URLs. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/105648). ## 6.1.3 diff --git a/packages/url_launcher/url_launcher/lib/src/type_conversion.dart b/packages/url_launcher/url_launcher/lib/src/type_conversion.dart new file mode 100644 index 000000000000..970f04dced57 --- /dev/null +++ b/packages/url_launcher/url_launcher/lib/src/type_conversion.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 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +import 'types.dart'; + +/// Converts an (app-facing) [WebViewConfiguration] to a (platform interface) +/// [InAppWebViewConfiguration]. +InAppWebViewConfiguration convertConfiguration(WebViewConfiguration config) { + return InAppWebViewConfiguration( + enableJavaScript: config.enableJavaScript, + enableDomStorage: config.enableDomStorage, + headers: config.headers, + ); +} + +/// Converts an (app-facing) [LaunchMode] to a (platform interface) +/// [PreferredLaunchMode]. +PreferredLaunchMode convertLaunchMode(LaunchMode mode) { + switch (mode) { + case LaunchMode.platformDefault: + return PreferredLaunchMode.platformDefault; + case LaunchMode.inAppWebView: + return PreferredLaunchMode.inAppWebView; + case LaunchMode.externalApplication: + return PreferredLaunchMode.externalApplication; + case LaunchMode.externalNonBrowserApplication: + return PreferredLaunchMode.externalNonBrowserApplication; + } +} diff --git a/packages/url_launcher/url_launcher/lib/src/url_launcher_string.dart b/packages/url_launcher/url_launcher/lib/src/url_launcher_string.dart index bee2a80a59c0..cf96ebc095da 100644 --- a/packages/url_launcher/url_launcher/lib/src/url_launcher_string.dart +++ b/packages/url_launcher/url_launcher/lib/src/url_launcher_string.dart @@ -4,6 +4,7 @@ import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; +import 'type_conversion.dart'; import 'types.dart'; /// String version of [launchUrl]. @@ -24,27 +25,18 @@ Future launchUrlString( WebViewConfiguration webViewConfiguration = const WebViewConfiguration(), String? webOnlyWindowName, }) async { - final bool isWebURL = - urlString.startsWith('http:') || urlString.startsWith('https:'); - if (mode == LaunchMode.inAppWebView && !isWebURL) { + if (mode == LaunchMode.inAppWebView && + !(urlString.startsWith('https:') || urlString.startsWith('http:'))) { throw ArgumentError.value(urlString, 'urlString', 'To use an in-app web view, you must provide an http(s) URL.'); } - final bool useWebView = mode == LaunchMode.inAppWebView || - (isWebURL && mode == LaunchMode.platformDefault); - - // TODO(stuartmorgan): Create a replacement platform interface method that - // uses something more like the new argument structure, and switch to using - // that, to support launch mode on more platforms. - return await UrlLauncherPlatform.instance.launch( + return await UrlLauncherPlatform.instance.launchUrl( urlString, - useSafariVC: useWebView, - useWebView: useWebView, - enableJavaScript: webViewConfiguration.enableJavaScript, - enableDomStorage: webViewConfiguration.enableDomStorage, - universalLinksOnly: mode == LaunchMode.externalNonBrowserApplication, - headers: webViewConfiguration.headers, - webOnlyWindowName: webOnlyWindowName, + LaunchOptions( + mode: convertLaunchMode(mode), + webViewConfiguration: convertConfiguration(webViewConfiguration), + webOnlyWindowName: webOnlyWindowName, + ), ); } diff --git a/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart b/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart index fc33f05e5afb..9061b517e0d5 100644 --- a/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart +++ b/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart @@ -7,6 +7,8 @@ import 'dart:async'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; +import 'type_conversion.dart'; + /// Passes [url] to the underlying platform for handling. /// /// [mode] support varies significantly by platform: @@ -43,20 +45,18 @@ Future launchUrl( WebViewConfiguration webViewConfiguration = const WebViewConfiguration(), String? webOnlyWindowName, }) async { - final bool isWebURL = url.scheme == 'http' || url.scheme == 'https'; - if (mode == LaunchMode.inAppWebView && !isWebURL) { + if (mode == LaunchMode.inAppWebView && + !(url.scheme == 'https' || url.scheme == 'http')) { throw ArgumentError.value(url, 'url', 'To use an in-app web view, you must provide an http(s) URL.'); } - // TODO(stuartmorgan): Use UrlLauncherPlatform directly once a new API - // that better matches these parameters has been added. For now, delegate to - // launchUrlString so that there's only one copy of the parameter translation - // logic. - return await launchUrlString( + return await UrlLauncherPlatform.instance.launchUrl( url.toString(), - mode: mode, - webViewConfiguration: webViewConfiguration, - webOnlyWindowName: webOnlyWindowName, + LaunchOptions( + mode: convertLaunchMode(mode), + webViewConfiguration: convertConfiguration(webViewConfiguration), + webOnlyWindowName: webOnlyWindowName, + ), ); } diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 2a7ddcdb1dbe..e01cd9b7a4f7 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/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.1.3 +version: 6.1.4 environment: sdk: ">=2.14.0 <3.0.0" @@ -34,7 +34,7 @@ dependencies: # implementations, as both are compatible. url_launcher_linux: ">=2.0.0 <4.0.0" url_launcher_macos: ">=2.0.0 <4.0.0" - url_launcher_platform_interface: ^2.0.3 + url_launcher_platform_interface: ^2.1.0 url_launcher_web: ^2.0.0 url_launcher_windows: ">=2.0.0 <4.0.0" diff --git a/packages/url_launcher/url_launcher/test/link_test.dart b/packages/url_launcher/url_launcher/test/link_test.dart index 6242397c5ed7..1c3d3e1e2d5b 100644 --- a/packages/url_launcher/url_launcher/test/link_test.dart +++ b/packages/url_launcher/url_launcher/test/link_test.dart @@ -19,7 +19,7 @@ void main() { UrlLauncherPlatform.instance = mock; }); - group('$Link', () { + group('Link', () { testWidgets('handles null uri correctly', (WidgetTester tester) async { bool isBuilt = false; FollowLink? followLink; @@ -55,8 +55,7 @@ void main() { mock ..setLaunchExpectations( url: 'http://example.com/foobar', - useSafariVC: false, - useWebView: false, + launchMode: PreferredLaunchMode.externalApplication, universalLinksOnly: false, enableJavaScript: true, enableDomStorage: true, @@ -85,8 +84,7 @@ void main() { mock ..setLaunchExpectations( url: 'http://example.com/foobar', - useSafariVC: true, - useWebView: true, + launchMode: PreferredLaunchMode.inAppWebView, universalLinksOnly: false, enableJavaScript: true, enableDomStorage: true, diff --git a/packages/url_launcher/url_launcher/test/mocks/mock_url_launcher_platform.dart b/packages/url_launcher/url_launcher/test/mocks/mock_url_launcher_platform.dart index 5c53257f7630..05c8b5e4b375 100644 --- a/packages/url_launcher/url_launcher/test/mocks/mock_url_launcher_platform.dart +++ b/packages/url_launcher/url_launcher/test/mocks/mock_url_launcher_platform.dart @@ -11,6 +11,7 @@ class MockUrlLauncher extends Fake with MockPlatformInterfaceMixin implements UrlLauncherPlatform { String? url; + PreferredLaunchMode? launchMode; bool? useSafariVC; bool? useWebView; bool? enableJavaScript; @@ -32,8 +33,9 @@ class MockUrlLauncher extends Fake void setLaunchExpectations({ required String url, - required bool? useSafariVC, - required bool useWebView, + PreferredLaunchMode? launchMode, + bool? useSafariVC, + bool? useWebView, required bool enableJavaScript, required bool enableDomStorage, required bool universalLinksOnly, @@ -41,6 +43,7 @@ class MockUrlLauncher extends Fake required String? webOnlyWindowName, }) { this.url = url; + this.launchMode = launchMode; this.useSafariVC = useSafariVC; this.useWebView = useWebView; this.enableJavaScript = enableJavaScript; @@ -88,6 +91,18 @@ class MockUrlLauncher extends Fake return response!; } + @override + Future launchUrl(String url, LaunchOptions options) async { + expect(url, this.url); + expect(options.mode, launchMode); + expect(options.webViewConfiguration.enableJavaScript, enableJavaScript); + expect(options.webViewConfiguration.enableDomStorage, enableDomStorage); + expect(options.webViewConfiguration.headers, headers); + expect(options.webOnlyWindowName, webOnlyWindowName); + launchCalled = true; + return response!; + } + @override Future closeWebView() async { closeWebViewCalled = true; diff --git a/packages/url_launcher/url_launcher/test/src/url_launcher_string_test.dart b/packages/url_launcher/url_launcher/test/src/url_launcher_string_test.dart index 95b2f5c27bf3..02c0b22903e0 100644 --- a/packages/url_launcher/url_launcher/test/src/url_launcher_string_test.dart +++ b/packages/url_launcher/url_launcher/test/src/url_launcher_string_test.dart @@ -43,8 +43,7 @@ void main() { mock ..setLaunchExpectations( url: urlString, - useSafariVC: true, - useWebView: true, + launchMode: PreferredLaunchMode.platformDefault, enableJavaScript: true, enableDomStorage: true, universalLinksOnly: false, @@ -60,8 +59,7 @@ void main() { mock ..setLaunchExpectations( url: urlString, - useSafariVC: false, - useWebView: false, + launchMode: PreferredLaunchMode.platformDefault, enableJavaScript: true, enableDomStorage: true, universalLinksOnly: false, @@ -77,8 +75,7 @@ void main() { mock ..setLaunchExpectations( url: urlString, - useSafariVC: true, - useWebView: true, + launchMode: PreferredLaunchMode.platformDefault, enableJavaScript: true, enableDomStorage: true, universalLinksOnly: false, @@ -95,8 +92,7 @@ void main() { mock ..setLaunchExpectations( url: urlString, - useSafariVC: false, - useWebView: false, + launchMode: PreferredLaunchMode.platformDefault, enableJavaScript: true, enableDomStorage: true, universalLinksOnly: false, @@ -113,8 +109,7 @@ void main() { mock ..setLaunchExpectations( url: urlString, - useSafariVC: true, - useWebView: true, + launchMode: PreferredLaunchMode.inAppWebView, enableJavaScript: true, enableDomStorage: true, universalLinksOnly: false, @@ -131,8 +126,7 @@ void main() { mock ..setLaunchExpectations( url: urlString, - useSafariVC: false, - useWebView: false, + launchMode: PreferredLaunchMode.externalApplication, enableJavaScript: true, enableDomStorage: true, universalLinksOnly: false, @@ -151,8 +145,7 @@ void main() { mock ..setLaunchExpectations( url: urlString, - useSafariVC: false, - useWebView: false, + launchMode: PreferredLaunchMode.externalNonBrowserApplication, enableJavaScript: true, enableDomStorage: true, universalLinksOnly: true, @@ -171,8 +164,7 @@ void main() { mock ..setLaunchExpectations( url: urlString, - useSafariVC: true, - useWebView: true, + launchMode: PreferredLaunchMode.inAppWebView, enableJavaScript: false, enableDomStorage: true, universalLinksOnly: false, @@ -193,8 +185,7 @@ void main() { mock ..setLaunchExpectations( url: urlString, - useSafariVC: true, - useWebView: true, + launchMode: PreferredLaunchMode.inAppWebView, enableJavaScript: true, enableDomStorage: false, universalLinksOnly: false, @@ -215,8 +206,7 @@ void main() { mock ..setLaunchExpectations( url: urlString, - useSafariVC: true, - useWebView: true, + launchMode: PreferredLaunchMode.inAppWebView, enableJavaScript: true, enableDomStorage: true, universalLinksOnly: false, @@ -245,8 +235,7 @@ void main() { mock ..setLaunchExpectations( url: emailLaunchUrlString, - useSafariVC: false, - useWebView: false, + launchMode: PreferredLaunchMode.platformDefault, enableJavaScript: true, enableDomStorage: true, universalLinksOnly: false, @@ -264,8 +253,7 @@ void main() { mock ..setLaunchExpectations( url: urlString, - useSafariVC: false, - useWebView: false, + launchMode: PreferredLaunchMode.platformDefault, enableJavaScript: true, enableDomStorage: true, universalLinksOnly: false, diff --git a/packages/url_launcher/url_launcher/test/src/url_launcher_uri_test.dart b/packages/url_launcher/url_launcher/test/src/url_launcher_uri_test.dart index 8286e0c43d20..e226e591a4ae 100644 --- a/packages/url_launcher/url_launcher/test/src/url_launcher_uri_test.dart +++ b/packages/url_launcher/url_launcher/test/src/url_launcher_uri_test.dart @@ -48,8 +48,7 @@ void main() { mock ..setLaunchExpectations( url: url.toString(), - useSafariVC: true, - useWebView: true, + launchMode: PreferredLaunchMode.platformDefault, enableJavaScript: true, enableDomStorage: true, universalLinksOnly: false, @@ -65,8 +64,7 @@ void main() { mock ..setLaunchExpectations( url: url.toString(), - useSafariVC: false, - useWebView: false, + launchMode: PreferredLaunchMode.platformDefault, enableJavaScript: true, enableDomStorage: true, universalLinksOnly: false, @@ -82,8 +80,7 @@ void main() { mock ..setLaunchExpectations( url: url.toString(), - useSafariVC: true, - useWebView: true, + launchMode: PreferredLaunchMode.platformDefault, enableJavaScript: true, enableDomStorage: true, universalLinksOnly: false, @@ -99,8 +96,7 @@ void main() { mock ..setLaunchExpectations( url: url.toString(), - useSafariVC: false, - useWebView: false, + launchMode: PreferredLaunchMode.platformDefault, enableJavaScript: true, enableDomStorage: true, universalLinksOnly: false, @@ -116,8 +112,7 @@ void main() { mock ..setLaunchExpectations( url: url.toString(), - useSafariVC: true, - useWebView: true, + launchMode: PreferredLaunchMode.inAppWebView, enableJavaScript: true, enableDomStorage: true, universalLinksOnly: false, @@ -133,8 +128,7 @@ void main() { mock ..setLaunchExpectations( url: url.toString(), - useSafariVC: false, - useWebView: false, + launchMode: PreferredLaunchMode.externalApplication, enableJavaScript: true, enableDomStorage: true, universalLinksOnly: false, @@ -151,8 +145,7 @@ void main() { mock ..setLaunchExpectations( url: url.toString(), - useSafariVC: false, - useWebView: false, + launchMode: PreferredLaunchMode.externalNonBrowserApplication, enableJavaScript: true, enableDomStorage: true, universalLinksOnly: true, @@ -170,8 +163,7 @@ void main() { mock ..setLaunchExpectations( url: url.toString(), - useSafariVC: true, - useWebView: true, + launchMode: PreferredLaunchMode.inAppWebView, enableJavaScript: false, enableDomStorage: true, universalLinksOnly: false, @@ -192,8 +184,7 @@ void main() { mock ..setLaunchExpectations( url: url.toString(), - useSafariVC: true, - useWebView: true, + launchMode: PreferredLaunchMode.inAppWebView, enableJavaScript: true, enableDomStorage: false, universalLinksOnly: false, @@ -214,8 +205,7 @@ void main() { mock ..setLaunchExpectations( url: url.toString(), - useSafariVC: true, - useWebView: true, + launchMode: PreferredLaunchMode.inAppWebView, enableJavaScript: true, enableDomStorage: true, universalLinksOnly: false, @@ -247,8 +237,7 @@ void main() { mock ..setLaunchExpectations( url: emailLaunchUrl.toString(), - useSafariVC: false, - useWebView: false, + launchMode: PreferredLaunchMode.platformDefault, enableJavaScript: true, enableDomStorage: true, universalLinksOnly: false, From df6260e001c04b9c7fe9a3ec825ffdb1c389d8d7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 22 Jun 2022 15:24:08 -0400 Subject: [PATCH 454/844] Roll Flutter from dfaec11ba519 to 60f30e5d3ed4 (23 revisions) (#6015) --- .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 0c70247eb92b..e2f727e78c19 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -dfaec11ba519421ccc6869226dd8930bb5689fb6 +60f30e5d3ed41a2a615545171d85139b8abf664e From f62b23747a1e2c5ea512042d48e99e7b155ff93f Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 22 Jun 2022 16:09:11 -0400 Subject: [PATCH 455/844] [webview_flutter_wkwebview] Update copy method for Dart classes and support the `NSObject.observeValue` for subclasses (#5961) --- .../FWFScriptMessageHandlerHostApiTests.m | 5 +- .../RunnerTests/FWFUIDelegateHostApiTests.m | 5 +- .../FWFWebViewConfigurationHostApiTests.m | 20 +- .../ios/RunnerTests/FWFWebViewHostApiTests.m | 104 +++-- .../ios/Classes/FWFHTTPCookieStoreHostApi.m | 3 +- .../Classes/FWFNavigationDelegateHostApi.h | 1 + .../Classes/FWFNavigationDelegateHostApi.m | 8 +- .../ios/Classes/FWFObjectHostApi.h | 7 + .../ios/Classes/FWFObjectHostApi.m | 4 +- .../ios/Classes/FWFPreferencesHostApi.m | 3 +- .../Classes/FWFScriptMessageHandlerHostApi.h | 4 +- .../Classes/FWFScriptMessageHandlerHostApi.m | 14 +- .../ios/Classes/FWFScrollViewHostApi.m | 3 +- .../ios/Classes/FWFUIDelegateHostApi.h | 5 +- .../ios/Classes/FWFUIDelegateHostApi.m | 16 +- .../ios/Classes/FWFUIViewHostApi.m | 3 +- .../Classes/FWFUserContentControllerHostApi.m | 3 +- .../Classes/FWFWebViewConfigurationHostApi.h | 15 +- .../Classes/FWFWebViewConfigurationHostApi.m | 42 +- .../ios/Classes/FWFWebViewHostApi.h | 14 +- .../ios/Classes/FWFWebViewHostApi.m | 45 +- .../ios/Classes/FWFWebsiteDataStoreHostApi.m | 3 +- .../lib/src/common/instance_manager.dart | 2 - .../lib/src/foundation/foundation.dart | 30 +- .../src/foundation/foundation_api_impls.dart | 13 - .../lib/src/ui_kit/ui_kit.dart | 72 +++- .../lib/src/ui_kit/ui_kit_api_impls.dart | 23 +- .../lib/src/web_kit/web_kit.dart | 357 +++++++++++----- .../lib/src/web_kit/web_kit_api_impls.dart | 105 +++-- .../lib/src/web_kit_webview_widget.dart | 2 - .../test/src/foundation/foundation_test.dart | 18 +- .../test/src/ui_kit/ui_kit_test.dart | 2 +- .../web_kit_cookie_manager_test.mocks.dart | 69 ++-- .../web_kit_webview_widget_test.mocks.dart | 388 +++++++++--------- 34 files changed, 895 insertions(+), 513 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m index 5a51e7b980f8..84d31d1c543e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFScriptMessageHandlerHostApiTests.m @@ -45,8 +45,9 @@ - (id)mockFlutterApiWithManager:(FWFInstanceManager *)instanceManager { - (void)testCreateWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - FWFScriptMessageHandlerHostApiImpl *hostAPI = - [[FWFScriptMessageHandlerHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFScriptMessageHandlerHostApiImpl *hostAPI = [[FWFScriptMessageHandlerHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; [hostAPI createWithIdentifier:@0 error:&error]; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m index eec7a2b5a6dc..939c14873fa4 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFUIDelegateHostApiTests.m @@ -45,8 +45,9 @@ - (id)mockFlutterApiWithManager:(FWFInstanceManager *)instanceManager { - (void)testCreateWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - FWFUIDelegateHostApiImpl *hostAPI = - [[FWFUIDelegateHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFUIDelegateHostApiImpl *hostAPI = [[FWFUIDelegateHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; [hostAPI createWithIdentifier:@0 error:&error]; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewConfigurationHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewConfigurationHostApiTests.m index e09e16b62e45..2ec74d0522dd 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewConfigurationHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewConfigurationHostApiTests.m @@ -14,8 +14,9 @@ @interface FWFWebViewConfigurationHostApiTests : XCTestCase @implementation FWFWebViewConfigurationHostApiTests - (void)testCreateWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - FWFWebViewConfigurationHostApiImpl *hostAPI = - [[FWFWebViewConfigurationHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewConfigurationHostApiImpl *hostAPI = [[FWFWebViewConfigurationHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; [hostAPI createWithIdentifier:@0 error:&error]; @@ -27,8 +28,9 @@ - (void)testCreateWithIdentifier { - (void)testCreateFromWebViewWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - FWFWebViewConfigurationHostApiImpl *hostAPI = - [[FWFWebViewConfigurationHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewConfigurationHostApiImpl *hostAPI = [[FWFWebViewConfigurationHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; WKWebView *mockWebView = OCMClassMock([WKWebView class]); OCMStub([mockWebView configuration]).andReturn(OCMClassMock([WKWebViewConfiguration class])); @@ -48,8 +50,9 @@ - (void)testSetAllowsInlineMediaPlayback { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebViewConfiguration withIdentifier:0]; - FWFWebViewConfigurationHostApiImpl *hostAPI = - [[FWFWebViewConfigurationHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewConfigurationHostApiImpl *hostAPI = [[FWFWebViewConfigurationHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; [hostAPI setAllowsInlineMediaPlaybackForConfigurationWithIdentifier:@0 @@ -65,8 +68,9 @@ - (void)testSetMediaTypesRequiringUserActionForPlayback { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebViewConfiguration withIdentifier:0]; - FWFWebViewConfigurationHostApiImpl *hostAPI = - [[FWFWebViewConfigurationHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewConfigurationHostApiImpl *hostAPI = [[FWFWebViewConfigurationHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; [hostAPI diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m index ca17cc212d31..1061abb78f45 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m @@ -14,8 +14,9 @@ @interface FWFWebViewHostApiTests : XCTestCase @implementation FWFWebViewHostApiTests - (void)testCreateWithIdentifier { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; [instanceManager addDartCreatedInstance:[[WKWebViewConfiguration alloc] init] withIdentifier:0]; @@ -32,8 +33,9 @@ - (void)testLoadRequest { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; FWFNSUrlRequestData *requestData = [FWFNSUrlRequestData makeWithUrl:@"https://www.flutter.dev" @@ -57,8 +59,9 @@ - (void)testLoadRequestWithInvalidUrl { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; FWFNSUrlRequestData *requestData = [FWFNSUrlRequestData makeWithUrl:@"%invalidUrl%" @@ -78,8 +81,9 @@ - (void)testSetCustomUserAgent { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; [hostAPI setUserAgentForWebViewWithIdentifier:@0 userAgent:@"userA" error:&error]; @@ -94,8 +98,9 @@ - (void)testURL { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; XCTAssertEqualObjects([hostAPI URLForWebViewWithIdentifier:@0 error:&error], @@ -110,8 +115,9 @@ - (void)testCanGoBack { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; XCTAssertEqualObjects([hostAPI canGoBackForWebViewWithIdentifier:@0 error:&error], @YES); @@ -124,8 +130,9 @@ - (void)testSetUIDelegate { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; id mockDelegate = OCMProtocolMock(@protocol(WKUIDelegate)); [instanceManager addDartCreatedInstance:mockDelegate withIdentifier:1]; @@ -142,8 +149,9 @@ - (void)testSetNavigationDelegate { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; id mockDelegate = OCMProtocolMock(@protocol(WKNavigationDelegate)); [instanceManager addDartCreatedInstance:mockDelegate withIdentifier:1]; @@ -161,8 +169,9 @@ - (void)testEstimatedProgress { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; XCTAssertEqualObjects([hostAPI estimatedProgressForWebViewWithIdentifier:@0 error:&error], @34.0); @@ -175,8 +184,9 @@ - (void)testloadHTMLString { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; [hostAPI loadHTMLForWebViewWithIdentifier:@0 @@ -193,8 +203,9 @@ - (void)testLoadFileURL { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; [hostAPI loadFileForWebViewWithIdentifier:@0 @@ -222,10 +233,11 @@ - (void)testLoadFlutterAsset { OCMStub([mockBundle URLForResource:@"myFolder/assets/index" withExtension:@"html"]) .andReturn([NSURL URLWithString:@"webview_flutter/myFolder/assets/index.html"]); - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager - bundle:mockBundle - assetManager:mockAssetManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager + bundle:mockBundle + assetManager:mockAssetManager]; FlutterError *error; [hostAPI loadAssetForWebViewWithIdentifier:@0 assetKey:@"assets/index.html" error:&error]; @@ -243,8 +255,9 @@ - (void)testCanGoForward { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; XCTAssertEqualObjects([hostAPI canGoForwardForWebViewWithIdentifier:@0 error:&error], @NO); @@ -257,8 +270,9 @@ - (void)testGoBack { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; [hostAPI goBackForWebViewWithIdentifier:@0 error:&error]; @@ -272,8 +286,9 @@ - (void)testGoForward { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; [hostAPI goForwardForWebViewWithIdentifier:@0 error:&error]; @@ -287,8 +302,9 @@ - (void)testReload { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; [hostAPI reloadWebViewWithIdentifier:@0 error:&error]; @@ -303,8 +319,9 @@ - (void)testTitle { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; XCTAssertEqualObjects([hostAPI titleForWebViewWithIdentifier:@0 error:&error], @"myTitle"); @@ -317,8 +334,9 @@ - (void)testSetAllowsBackForwardNavigationGestures { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; FlutterError *error; [hostAPI setAllowsBackForwardForWebViewWithIdentifier:@0 isAllowed:@YES error:&error]; @@ -336,8 +354,9 @@ - (void)testEvaluateJavaScript { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; NSString __block *returnValue; FlutterError __block *returnError; @@ -369,8 +388,9 @@ - (void)testEvaluateJavaScriptReturnsNSErrorData { FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; - FWFWebViewHostApiImpl *hostAPI = - [[FWFWebViewHostApiImpl alloc] initWithInstanceManager:instanceManager]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; NSString __block *returnValue; FlutterError __block *returnError; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.m index 3ad0a169b81f..79a3a684b805 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFHTTPCookieStoreHostApi.m @@ -7,7 +7,8 @@ #import "FWFWebsiteDataStoreHostApi.h" @interface FWFHTTPCookieStoreHostApiImpl () -@property(nonatomic) FWFInstanceManager *instanceManager; +// InstanceManager must be weak to prevent a circular reference with the object it stores. +@property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @implementation FWFHTTPCookieStoreHostApiImpl 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 da5939a3abed..90e55417cd1b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.h @@ -27,6 +27,7 @@ NS_ASSUME_NONNULL_BEGIN */ @interface FWFNavigationDelegate : FWFObject @property(readonly, nonnull, nonatomic) FWFNavigationDelegateFlutterApiImpl *navigationDelegateAPI; + - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; @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 6946783088d5..1132e02880b2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m @@ -7,7 +7,7 @@ #import "FWFWebViewConfigurationHostApi.h" @interface FWFNavigationDelegateFlutterApiImpl () -// This reference must be weak to prevent a circular reference with the objects it stores. +// InstanceManager must be weak to prevent a circular reference with the object it stores. @property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @@ -183,8 +183,10 @@ - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView { @end @interface FWFNavigationDelegateHostApiImpl () -@property(weak) id binaryMessenger; -// This reference must be weak to prevent a circular reference with the objects it stores. +// BinaryMessenger must be weak to prevent a circular reference with the host API it +// references. +@property(nonatomic, weak) id binaryMessenger; +// InstanceManager must be weak to prevent a circular reference with the object it stores. @property(nonatomic, weak) FWFInstanceManager *instanceManager; @end 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 1f7b2943974e..0b740a524cef 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.h @@ -17,6 +17,12 @@ NS_ASSUME_NONNULL_BEGIN @interface FWFObjectFlutterApiImpl : FWFNSObjectFlutterApi - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; + +- (void)observeValueForObject:(NSObject *)instance + keyPath:(NSString *)keyPath + object:(NSObject *)object + change:(NSDictionary *)change + completion:(void (^)(NSError *_Nullable))completion; @end /** @@ -24,6 +30,7 @@ NS_ASSUME_NONNULL_BEGIN */ @interface FWFObject : NSObject @property(readonly, nonnull, nonatomic) FWFObjectFlutterApiImpl *objectApi; + - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m index f88b91750493..c88b2f4e56cb 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFObjectHostApi.m @@ -6,7 +6,7 @@ #import "FWFDataConverters.h" @interface FWFObjectFlutterApiImpl () -// This reference must be weak to prevent a circular reference with the objects it stores. +// InstanceManager must be weak to prevent a circular reference with the object it stores. @property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @@ -74,7 +74,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath @end @interface FWFObjectHostApiImpl () -// This reference must be weak to prevent a circular reference with the objects it stores. +// InstanceManager must be weak to prevent a circular reference with the object it stores. @property(nonatomic, weak) FWFInstanceManager *instanceManager; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.m index dbb04fccccd2..1a10c08eec4a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFPreferencesHostApi.m @@ -6,7 +6,8 @@ #import "FWFWebViewConfigurationHostApi.h" @interface FWFPreferencesHostApiImpl () -@property(nonatomic) FWFInstanceManager *instanceManager; +// InstanceManager must be weak to prevent a circular reference with the object it stores. +@property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @implementation FWFPreferencesHostApiImpl 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 36f12feac3fe..9c5769e4658b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.h @@ -27,6 +27,7 @@ NS_ASSUME_NONNULL_BEGIN @interface FWFScriptMessageHandler : FWFObject @property(readonly, nonnull, nonatomic) FWFScriptMessageHandlerFlutterApiImpl *scriptMessageHandlerAPI; + - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; @end @@ -37,7 +38,8 @@ NS_ASSUME_NONNULL_BEGIN * Handles creating WKScriptMessageHandler that intercommunicate with a paired Dart object. */ @interface FWFScriptMessageHandlerHostApiImpl : NSObject -- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager; @end NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m index 69ace0202824..d9e8b934a79a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScriptMessageHandlerHostApi.m @@ -6,7 +6,7 @@ #import "FWFDataConverters.h" @interface FWFScriptMessageHandlerFlutterApiImpl () -// This reference must be weak to prevent a circular reference with the objects it stores. +// InstanceManager must be weak to prevent a circular reference with the object it stores. @property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @@ -62,13 +62,19 @@ - (void)userContentController:(nonnull WKUserContentController *)userContentCont @end @interface FWFScriptMessageHandlerHostApiImpl () +// BinaryMessenger must be weak to prevent a circular reference with the host API it +// references. +@property(nonatomic, weak) id binaryMessenger; +// InstanceManager must be weak to prevent a circular reference with the object it stores. @property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @implementation FWFScriptMessageHandlerHostApiImpl -- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager { self = [self init]; if (self) { + _binaryMessenger = binaryMessenger; _instanceManager = instanceManager; } return self; @@ -81,7 +87,9 @@ - (FWFScriptMessageHandler *)scriptMessageHandlerForIdentifier:(NSNumber *)ident - (void)createWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error { - FWFScriptMessageHandler *scriptMessageHandler = [[FWFScriptMessageHandler alloc] init]; + FWFScriptMessageHandler *scriptMessageHandler = + [[FWFScriptMessageHandler alloc] initWithBinaryMessenger:self.binaryMessenger + instanceManager:self.instanceManager]; [self.instanceManager addDartCreatedInstance:scriptMessageHandler withIdentifier:identifier.longValue]; } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.m index fb77b717aa64..a32e9565b514 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFScrollViewHostApi.m @@ -6,7 +6,8 @@ #import "FWFWebViewHostApi.h" @interface FWFScrollViewHostApiImpl () -@property(nonatomic) FWFInstanceManager *instanceManager; +// InstanceManager must be weak to prevent a circular reference with the object it stores. +@property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @implementation FWFScrollViewHostApiImpl 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 1bb65914e097..7b6b4eec9b8e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.h @@ -20,6 +20,7 @@ NS_ASSUME_NONNULL_BEGIN @interface FWFUIDelegateFlutterApiImpl : FWFWKUIDelegateFlutterApi @property(readonly, nonatomic) FWFWebViewConfigurationFlutterApiImpl *webViewConfigurationFlutterApi; + - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; @end @@ -29,6 +30,7 @@ NS_ASSUME_NONNULL_BEGIN */ @interface FWFUIDelegate : FWFObject @property(readonly, nonnull, nonatomic) FWFUIDelegateFlutterApiImpl *UIDelegateAPI; + - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; @end @@ -39,7 +41,8 @@ NS_ASSUME_NONNULL_BEGIN * Handles creating WKUIDelegate that intercommunicate with a paired Dart object. */ @interface FWFUIDelegateHostApiImpl : NSObject -- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager; @end NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m index 36c4b2feefe3..60e7ad11965c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m @@ -6,9 +6,10 @@ #import "FWFDataConverters.h" @interface FWFUIDelegateFlutterApiImpl () -// BinaryMessenger and InstanceManager must be weak to prevent a circular reference -// with the objects it stores. +// BinaryMessenger must be weak to prevent a circular reference with the host API it +// references. @property(nonatomic, weak) id binaryMessenger; +// InstanceManager must be weak to prevent a circular reference with the object it stores. @property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @@ -84,13 +85,19 @@ - (WKWebView *)webView:(WKWebView *)webView @end @interface FWFUIDelegateHostApiImpl () +// BinaryMessenger must be weak to prevent a circular reference with the host API it +// references. +@property(nonatomic, weak) id binaryMessenger; +// InstanceManager must be weak to prevent a circular reference with the object it stores. @property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @implementation FWFUIDelegateHostApiImpl -- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager { self = [self init]; if (self) { + _binaryMessenger = binaryMessenger; _instanceManager = instanceManager; } return self; @@ -102,7 +109,8 @@ - (FWFUIDelegate *)delegateForIdentifier:(NSNumber *)identifier { - (void)createWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error { - FWFUIDelegate *uIDelegate = [[FWFUIDelegate alloc] init]; + FWFUIDelegate *uIDelegate = [[FWFUIDelegate alloc] initWithBinaryMessenger:self.binaryMessenger + instanceManager:self.instanceManager]; [self.instanceManager addDartCreatedInstance:uIDelegate withIdentifier:identifier.longValue]; } @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.m index 465738b570cd..a990561c4fba 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIViewHostApi.m @@ -5,7 +5,8 @@ #import "FWFUIViewHostApi.h" @interface FWFUIViewHostApiImpl () -@property(nonatomic) FWFInstanceManager *instanceManager; +// InstanceManager must be weak to prevent a circular reference with the object it stores. +@property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @implementation FWFUIViewHostApiImpl diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.m index d15341a7883d..08bbaa68c99c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUserContentControllerHostApi.m @@ -7,7 +7,8 @@ #import "FWFWebViewConfigurationHostApi.h" @interface FWFUserContentControllerHostApiImpl () -@property(nonatomic) FWFInstanceManager *instanceManager; +// InstanceManager must be weak to prevent a circular reference with the object it stores. +@property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @implementation FWFUserContentControllerHostApiImpl 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 e2279b48c409..f1e62cc0cba3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.h @@ -7,6 +7,7 @@ #import "FWFGeneratedWebKitApis.h" #import "FWFInstanceManager.h" +#import "FWFObjectHostApi.h" NS_ASSUME_NONNULL_BEGIN @@ -18,17 +19,29 @@ NS_ASSUME_NONNULL_BEGIN @interface FWFWebViewConfigurationFlutterApiImpl : FWFWKWebViewConfigurationFlutterApi - (instancetype)initWithBinaryMessenger:(id)binaryMessenger instanceManager:(FWFInstanceManager *)instanceManager; + - (void)createWithConfiguration:(WKWebViewConfiguration *)configuration completion:(void (^)(NSError *_Nullable))completion; @end +/** + * Implementation of WKWebViewConfiguration for FWFWebViewConfigurationHostApiImpl. + */ +@interface FWFWebViewConfiguration : WKWebViewConfiguration +@property(readonly, nonnull, nonatomic) FWFObjectFlutterApiImpl *objectApi; + +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager; +@end + /** * Host api implementation for WKWebViewConfiguration. * * Handles creating WKWebViewConfiguration that intercommunicate with a paired Dart object. */ @interface FWFWebViewConfigurationHostApiImpl : NSObject -- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager; @end NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m index 2b7d1be86d37..a083a2a031ef 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewConfigurationHostApi.m @@ -7,8 +7,7 @@ #import "FWFWebViewConfigurationHostApi.h" @interface FWFWebViewConfigurationFlutterApiImpl () -// BinaryMessenger and InstanceManager must be weak to prevent a circular reference -// with the objects it stores. +// InstanceManager must be weak to prevent a circular reference with the object it stores. @property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @@ -29,14 +28,45 @@ - (void)createWithConfiguration:(WKWebViewConfiguration *)configuration } @end +@implementation FWFWebViewConfiguration +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager { + self = [self init]; + if (self) { + _objectApi = [[FWFObjectFlutterApiImpl alloc] initWithBinaryMessenger:binaryMessenger + instanceManager:instanceManager]; + } + return self; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context { + [self.objectApi observeValueForObject:self + keyPath:keyPath + object:object + change:change + completion:^(NSError *error) { + NSAssert(!error, @"%@", error); + }]; +} +@end + @interface FWFWebViewConfigurationHostApiImpl () -@property(nonatomic) FWFInstanceManager *instanceManager; +// BinaryMessenger must be weak to prevent a circular reference with the host API it +// references. +@property(nonatomic, weak) id binaryMessenger; +// InstanceManager must be weak to prevent a circular reference with the object it stores. +@property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @implementation FWFWebViewConfigurationHostApiImpl -- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager { self = [self init]; if (self) { + _binaryMessenger = binaryMessenger; _instanceManager = instanceManager; } return self; @@ -49,7 +79,9 @@ - (WKWebViewConfiguration *)webViewConfigurationForIdentifier:(NSNumber *)identi - (void)createWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable *_Nonnull)error { - WKWebViewConfiguration *webViewConfiguration = [[WKWebViewConfiguration alloc] init]; + FWFWebViewConfiguration *webViewConfiguration = + [[FWFWebViewConfiguration alloc] initWithBinaryMessenger:self.binaryMessenger + instanceManager:self.instanceManager]; [self.instanceManager addDartCreatedInstance:webViewConfiguration withIdentifier:identifier.longValue]; } 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 968a9a85b2e7..f1bb59bcb9ae 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.h @@ -7,6 +7,7 @@ #import "FWFGeneratedWebKitApis.h" #import "FWFInstanceManager.h" +#import "FWFObjectHostApi.h" NS_ASSUME_NONNULL_BEGIN @@ -23,6 +24,12 @@ NS_ASSUME_NONNULL_BEGIN * Implementation of WKWebView that can be used as a FlutterPlatformView. */ @interface FWFWebView : WKWebView +@property(readonly, nonnull, nonatomic) FWFObjectFlutterApiImpl *objectApi; + +- (instancetype)initWithFrame:(CGRect)frame + configuration:(nonnull WKWebViewConfiguration *)configuration + binaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager; @end /** @@ -31,8 +38,11 @@ NS_ASSUME_NONNULL_BEGIN * Handles creating WKWebViews that intercommunicate with a paired Dart object. */ @interface FWFWebViewHostApiImpl : NSObject -- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager; -- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager; + +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager bundle:(NSBundle *)bundle assetManager:(FWFAssetManager *)assetManager; @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m index 2962aadd1647..9a8aedd1e646 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m @@ -12,6 +12,18 @@ - (NSString *)lookupKeyForAsset:(NSString *)asset { @end @implementation FWFWebView +- (instancetype)initWithFrame:(CGRect)frame + configuration:(nonnull WKWebViewConfiguration *)configuration + binaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager { + self = [self initWithFrame:frame configuration:configuration]; + if (self) { + _objectApi = [[FWFObjectFlutterApiImpl alloc] initWithBinaryMessenger:binaryMessenger + instanceManager:instanceManager]; + } + return self; +} + - (void)setFrame:(CGRect)frame { [super setFrame:frame]; // Prevents the contentInsets from being adjusted by iOS and gives control to Flutter. @@ -28,29 +40,50 @@ - (void)setFrame:(CGRect)frame { } } +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context { + [self.objectApi observeValueForObject:self + keyPath:keyPath + object:object + change:change + completion:^(NSError *error) { + NSAssert(!error, @"%@", error); + }]; +} + - (nonnull UIView *)view { return self; } @end @interface FWFWebViewHostApiImpl () -@property(nonatomic) FWFInstanceManager *instanceManager; +// BinaryMessenger must be weak to prevent a circular reference with the host API it +// references. +@property(nonatomic, weak) id binaryMessenger; +// InstanceManager must be weak to prevent a circular reference with the object it stores. +@property(nonatomic, weak) FWFInstanceManager *instanceManager; @property NSBundle *bundle; @property FWFAssetManager *assetManager; @end @implementation FWFWebViewHostApiImpl -- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager { - return [self initWithInstanceManager:instanceManager +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager { + return [self initWithBinaryMessenger:binaryMessenger + instanceManager:instanceManager bundle:[NSBundle mainBundle] assetManager:[[FWFAssetManager alloc] init]]; } -- (instancetype)initWithInstanceManager:(FWFInstanceManager *)instanceManager +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + instanceManager:(FWFInstanceManager *)instanceManager bundle:(NSBundle *)bundle assetManager:(FWFAssetManager *)assetManager { self = [self init]; if (self) { + _binaryMessenger = binaryMessenger; _instanceManager = instanceManager; _bundle = bundle; _assetManager = assetManager; @@ -77,7 +110,9 @@ - (void)createWithIdentifier:(nonnull NSNumber *)identifier WKWebViewConfiguration *configuration = (WKWebViewConfiguration *)[self.instanceManager instanceForIdentifier:configurationIdentifier.longValue]; FWFWebView *webView = [[FWFWebView alloc] initWithFrame:CGRectMake(0, 0, 0, 0) - configuration:configuration]; + configuration:configuration + binaryMessenger:self.binaryMessenger + instanceManager:self.instanceManager]; [self.instanceManager addDartCreatedInstance:webView withIdentifier:identifier.longValue]; } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m index 0fd85e010a0a..5398d14d4e8b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebsiteDataStoreHostApi.m @@ -7,7 +7,8 @@ #import "FWFWebViewConfigurationHostApi.h" @interface FWFWebsiteDataStoreHostApiImpl () -@property(nonatomic) FWFInstanceManager *instanceManager; +// InstanceManager must be weak to prevent a circular reference with the object it stores. +@property(nonatomic, weak) FWFInstanceManager *instanceManager; @end @implementation FWFWebsiteDataStoreHostApiImpl diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.dart index c9c8c2324cc6..3cc100aebd46 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/instance_manager.dart @@ -179,8 +179,6 @@ class InstanceManager { final Copyable copy = instance.copy(); _identifiers[copy] = identifier; _strongInstances[identifier] = copy; - - assert(instance == copy); } /// Whether this manager contains the given [identifier]. 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 0a1dd347f8a2..f537a4454c4f 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 @@ -237,13 +237,12 @@ class NSHttpCookie { /// The root class of most Objective-C class hierarchies. @immutable class NSObject with Copyable { - // TODO(bparrishMines): Change constructor name to `detached`. /// Constructs a [NSObject] without creating the associated /// Objective-C object. /// /// This should only be used by subclasses created by this library or to /// create copies. - NSObject({ + NSObject.detached({ this.observeValue, BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, @@ -255,6 +254,11 @@ class NSObject with Copyable { FoundationFlutterApis.instance.ensureSetUp(); } + /// Release the reference to the Objective-C object. + static void dispose(NSObject instance) { + instance._api.instanceManager.removeWeakReference(instance); + } + /// Global instance of [InstanceManager]. static final InstanceManager globalInstanceManager = InstanceManager(onWeakReferenceRemoved: (int instanceId) { @@ -290,30 +294,12 @@ class NSObject with Copyable { return _api.removeObserverForInstances(this, observer, keyPath); } - /// Release the reference to the Objective-C object. - static void dispose(NSObject instance) { - instance._api.instanceManager.removeWeakReference(instance); - } - @override - Copyable copy() { - return NSObject( + NSObject copy() { + return NSObject.detached( observeValue: observeValue, binaryMessenger: _api.binaryMessenger, instanceManager: _api.instanceManager, ); } - - @override - int get hashCode { - return Object.hash(_api, _api.instanceManager.getIdentifier(this)); - } - - @override - bool operator ==(Object other) { - return other is NSObject && - _api == other._api && - _api.instanceManager.getIdentifier(this) == - other._api.instanceManager.getIdentifier(other); - } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart index de760ec5b36e..d2310e0a5df8 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart @@ -89,7 +89,6 @@ class FoundationFlutterApis { } /// Host api implementation for [NSObject]. -@immutable class NSObjectHostApiImpl extends NSObjectHostApi { /// Constructs an [NSObjectHostApiImpl]. NSObjectHostApiImpl({ @@ -134,18 +133,6 @@ class NSObjectHostApiImpl extends NSObjectHostApi { keyPath, ); } - - @override - int get hashCode { - return Object.hash(binaryMessenger, instanceManager); - } - - @override - bool operator ==(Object other) { - return other is NSObjectHostApiImpl && - binaryMessenger == other.binaryMessenger && - instanceManager == other.instanceManager; - } } /// Flutter api implementation for [NSObject]. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart index 88aefc18e454..33447091e5f9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit.dart @@ -4,6 +4,7 @@ import 'dart:math'; +import 'package:flutter/foundation.dart'; // TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) // ignore: unnecessary_import import 'package:flutter/painting.dart' show Color; @@ -14,25 +15,42 @@ import '../foundation/foundation.dart'; import '../web_kit/web_kit.dart'; import 'ui_kit_api_impls.dart'; -// TODO(bparrishMines): All subclasses of NSObject need to pass their -// InstanceManager and BinaryMessenger to its parent. They also need to -// override copy(); - /// A view that allows the scrolling and zooming of its contained views. /// /// Wraps [UIScrollView](https://developer.apple.com/documentation/uikit/uiscrollview?language=objc). +@immutable class UIScrollView extends UIView { /// Constructs a [UIScrollView] that is owned by [webView]. - UIScrollView.fromWebView( + factory UIScrollView.fromWebView( WKWebView webView, { BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : _scrollViewApi = UIScrollViewHostApiImpl( + }) { + final UIScrollView scrollView = UIScrollView.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + scrollView._scrollViewApi.createFromWebViewForInstances( + scrollView, + webView, + ); + return scrollView; + } + + /// Constructs a [UIScrollView] without creating the associated + /// Objective-C object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies. + UIScrollView.detached({ + super.observeValue, + super.binaryMessenger, + super.instanceManager, + }) : _scrollViewApi = UIScrollViewHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, - ) { - _scrollViewApi.createFromWebViewForInstances(this, webView); - } + ), + super.detached(); final UIScrollViewHostApiImpl _scrollViewApi; @@ -59,21 +77,36 @@ class UIScrollView extends UIView { Future setContentOffset(Point offset) { return _scrollViewApi.setContentOffsetForInstances(this, offset); } + + @override + UIScrollView copy() { + return UIScrollView.detached( + observeValue: observeValue, + binaryMessenger: _viewApi.binaryMessenger, + instanceManager: _viewApi.instanceManager, + ); + } } /// Manages the content for a rectangular area on the screen. /// /// Wraps [UIView](https://developer.apple.com/documentation/uikit/uiview?language=objc). +@immutable class UIView extends NSObject { - /// Constructs an [NSObject]. - UIView({ + /// Constructs a [UIView] without creating the associated + /// Objective-C object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies. + UIView.detached({ super.observeValue, - BinaryMessenger? binaryMessenger, - InstanceManager? instanceManager, - }) : _viewApi = UIViewHostApiImpl( + super.binaryMessenger, + super.instanceManager, + }) : _viewApi = UIViewHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, - ); + ), + super.detached(); final UIViewHostApiImpl _viewApi; @@ -92,4 +125,13 @@ class UIView extends NSObject { Future setOpaque(bool opaque) { return _viewApi.setOpaqueForInstances(this, opaque); } + + @override + UIView copy() { + return UIView.detached( + observeValue: observeValue, + binaryMessenger: _viewApi.binaryMessenger, + instanceManager: _viewApi.instanceManager, + ); + } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart index e921c4bad15a..6a54166fbad8 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:math'; import 'package:flutter/painting.dart' show Color; +import 'package:flutter/services.dart'; import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; @@ -18,9 +19,16 @@ import 'ui_kit.dart'; class UIScrollViewHostApiImpl extends UIScrollViewHostApi { /// Constructs a [UIScrollViewHostApiImpl]. UIScrollViewHostApiImpl({ - super.binaryMessenger, + this.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); + + /// Sends binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -75,9 +83,16 @@ class UIScrollViewHostApiImpl extends UIScrollViewHostApi { class UIViewHostApiImpl extends UIViewHostApi { /// Constructs a [UIViewHostApiImpl]. UIViewHostApiImpl({ - super.binaryMessenger, + this.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); + + /// Sends binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; 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 b2945293e624..ffc6eb8c23bf 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 @@ -10,10 +10,6 @@ import '../foundation/foundation.dart'; import '../ui_kit/ui_kit.dart'; import 'web_kit_api_impls.dart'; -// TODO(bparrishMines): All subclasses of NSObject need to pass their -// InstanceManager and BinaryMessenger to its parent. They also need to -// override copy(): https://github.com/flutter/flutter/issues/105245 - /// Times at which to inject script content into a webpage. /// /// Wraps [WKUserScriptInjectionTime](https://developer.apple.com/documentation/webkit/wkuserscriptinjectiontime?language=objc). @@ -214,23 +210,40 @@ class WKScriptMessage { /// Encapsulates the standard behaviors to apply to websites. /// /// Wraps [WKPreferences](https://developer.apple.com/documentation/webkit/wkpreferences?language=objc). +@immutable class WKPreferences extends NSObject { /// Constructs a [WKPreferences] that is owned by [configuration]. - @visibleForTesting - WKPreferences.fromWebViewConfiguration( + factory WKPreferences.fromWebViewConfiguration( WKWebViewConfiguration configuration, { BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : _preferencesApi = WKPreferencesHostApiImpl( - binaryMessenger: binaryMessenger, - instanceManager: instanceManager, - ) { - _preferencesApi.createFromWebViewConfigurationForInstances( - this, + }) { + final WKPreferences preferences = WKPreferences.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + preferences._preferencesApi.createFromWebViewConfigurationForInstances( + preferences, configuration, ); + return preferences; } + /// Constructs a [WKPreferences] without creating the associated + /// Objective-C object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies. + WKPreferences.detached({ + super.observeValue, + super.binaryMessenger, + super.instanceManager, + }) : _preferencesApi = WKPreferencesHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ), + super.detached(); + final WKPreferencesHostApiImpl _preferencesApi; // TODO(bparrishMines): Deprecated for iOS 14.0+. Add support for alternative. @@ -240,22 +253,39 @@ class WKPreferences extends NSObject { Future setJavaScriptEnabled(bool enabled) { return _preferencesApi.setJavaScriptEnabledForInstances(this, enabled); } + + @override + WKPreferences copy() { + return WKPreferences.detached( + observeValue: observeValue, + binaryMessenger: _preferencesApi.binaryMessenger, + instanceManager: _preferencesApi.instanceManager, + ); + } } /// Manages cookies, disk and memory caches, and other types of data for a web view. /// /// Wraps [WKWebsiteDataStore](https://developer.apple.com/documentation/webkit/wkwebsitedatastore?language=objc). +@immutable class WKWebsiteDataStore extends NSObject { - WKWebsiteDataStore._({ - BinaryMessenger? binaryMessenger, - InstanceManager? instanceManager, - }) : _websiteDataStoreApi = WKWebsiteDataStoreHostApiImpl( + /// Constructs a [WKWebsiteDataStore] without creating the associated + /// Objective-C object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies. + WKWebsiteDataStore.detached({ + super.observeValue, + super.binaryMessenger, + super.instanceManager, + }) : _websiteDataStoreApi = WKWebsiteDataStoreHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, - ); + ), + super.detached(); factory WKWebsiteDataStore._defaultDataStore() { - final WKWebsiteDataStore websiteDataStore = WKWebsiteDataStore._(); + final WKWebsiteDataStore websiteDataStore = WKWebsiteDataStore.detached(); websiteDataStore._websiteDataStoreApi.createDefaultDataStoreForInstances( websiteDataStore, ); @@ -263,13 +293,12 @@ class WKWebsiteDataStore extends NSObject { } /// Constructs a [WKWebsiteDataStore] that is owned by [configuration]. - @visibleForTesting factory WKWebsiteDataStore.fromWebViewConfiguration( WKWebViewConfiguration configuration, { BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, }) { - final WKWebsiteDataStore websiteDataStore = WKWebsiteDataStore._( + final WKWebsiteDataStore websiteDataStore = WKWebsiteDataStore.detached( binaryMessenger: binaryMessenger, instanceManager: instanceManager, ); @@ -304,28 +333,44 @@ class WKWebsiteDataStore extends NSObject { secondsModifiedSinceEpoch: since.millisecondsSinceEpoch / 1000, ); } + + @override + WKWebsiteDataStore copy() { + return WKWebsiteDataStore.detached( + observeValue: observeValue, + binaryMessenger: _websiteDataStoreApi.binaryMessenger, + instanceManager: _websiteDataStoreApi.instanceManager, + ); + } } /// An object that manages the HTTP cookies associated with a particular web view. /// /// Wraps [WKHTTPCookieStore](https://developer.apple.com/documentation/webkit/wkhttpcookiestore?language=objc). +@immutable class WKHttpCookieStore extends NSObject { - WKHttpCookieStore._({ - BinaryMessenger? binaryMessenger, - InstanceManager? instanceManager, - }) : _httpCookieStoreApi = WKHttpCookieStoreHostApiImpl( + /// Constructs a [WKHttpCookieStore] without creating the associated + /// Objective-C object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies. + WKHttpCookieStore.detached({ + super.observeValue, + super.binaryMessenger, + super.instanceManager, + }) : _httpCookieStoreApi = WKHttpCookieStoreHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, - ); + ), + super.detached(); /// Constructs a [WKHttpCookieStore] that is owned by [dataStore]. - @visibleForTesting factory WKHttpCookieStore.fromWebsiteDataStore( WKWebsiteDataStore dataStore, { BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, }) { - final WKHttpCookieStore cookieStore = WKHttpCookieStore._( + final WKHttpCookieStore cookieStore = WKHttpCookieStore.detached( binaryMessenger: binaryMessenger, instanceManager: instanceManager, ); @@ -342,25 +387,54 @@ class WKHttpCookieStore extends NSObject { Future setCookie(NSHttpCookie cookie) { return _httpCookieStoreApi.setCookieForInsances(this, cookie); } + + @override + WKHttpCookieStore copy() { + return WKHttpCookieStore.detached( + observeValue: observeValue, + binaryMessenger: _httpCookieStoreApi.binaryMessenger, + instanceManager: _httpCookieStoreApi.instanceManager, + ); + } } /// An interface for receiving messages from JavaScript code running in a webpage. /// -/// Wraps [WKScriptMessageHandler](https://developer.apple.com/documentation/webkit/wkscriptmessagehandler?language=objc) +/// Wraps [WKScriptMessageHandler](https://developer.apple.com/documentation/webkit/wkscriptmessagehandler?language=objc). +@immutable class WKScriptMessageHandler extends NSObject { /// Constructs a [WKScriptMessageHandler]. WKScriptMessageHandler({ required this.didReceiveScriptMessage, super.observeValue, - BinaryMessenger? binaryMessenger, - InstanceManager? instanceManager, - }) : _scriptMessageHandlerApi = WKScriptMessageHandlerHostApiImpl( + super.binaryMessenger, + super.instanceManager, + }) : _scriptMessageHandlerApi = WKScriptMessageHandlerHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, - ) { + ), + super.detached() { + // Ensures FlutterApis for the WebKit library are set up. + WebKitFlutterApis.instance.ensureSetUp(); _scriptMessageHandlerApi.createForInstances(this); } + /// Constructs a [WKScriptMessageHandler] without creating the associated + /// Objective-C object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies. + WKScriptMessageHandler.detached({ + required this.didReceiveScriptMessage, + super.observeValue, + super.binaryMessenger, + super.instanceManager, + }) : _scriptMessageHandlerApi = WKScriptMessageHandlerHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ), + super.detached(); + final WKScriptMessageHandlerHostApiImpl _scriptMessageHandlerApi; /// Tells the handler that a webpage sent a script message. @@ -372,6 +446,16 @@ class WKScriptMessageHandler extends NSObject { WKUserContentController userContentController, WKScriptMessage message, ) didReceiveScriptMessage; + + @override + WKScriptMessageHandler copy() { + return WKScriptMessageHandler.detached( + didReceiveScriptMessage: didReceiveScriptMessage, + observeValue: observeValue, + binaryMessenger: _scriptMessageHandlerApi.binaryMessenger, + instanceManager: _scriptMessageHandlerApi.instanceManager, + ); + } } /// Manages interactions between JavaScript code and your web view. @@ -383,21 +467,25 @@ class WKScriptMessageHandler extends NSObject { /// code. /// /// Wraps [WKUserContentController](https://developer.apple.com/documentation/webkit/wkusercontentcontroller?language=objc). +@immutable class WKUserContentController extends NSObject { /// Constructs a [WKUserContentController] that is owned by [configuration]. - @visibleForTesting - WKUserContentController.fromWebViewConfiguration( + factory WKUserContentController.fromWebViewConfiguration( WKWebViewConfiguration configuration, { BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : _userContentControllerApi = WKUserContentControllerHostApiImpl( - binaryMessenger: binaryMessenger, - instanceManager: instanceManager, - ) { - _userContentControllerApi.createFromWebViewConfigurationForInstances( - this, + }) { + final WKUserContentController userContentController = + WKUserContentController.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + userContentController._userContentControllerApi + .createFromWebViewConfigurationForInstances( + userContentController, configuration, ); + return userContentController; } /// Constructs a [WKUserContentController] without creating the associated @@ -406,12 +494,14 @@ class WKUserContentController extends NSObject { /// This should only be used outside of tests by subclasses created by this /// library or to create a copy for an InstanceManager. WKUserContentController.detached({ - BinaryMessenger? binaryMessenger, - InstanceManager? instanceManager, - }) : _userContentControllerApi = WKUserContentControllerHostApiImpl( + super.observeValue, + super.binaryMessenger, + super.instanceManager, + }) : _userContentControllerApi = WKUserContentControllerHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, - ); + ), + super.detached(); final WKUserContentControllerHostApiImpl _userContentControllerApi; @@ -474,24 +564,35 @@ class WKUserContentController extends NSObject { Future removeAllUserScripts() { return _userContentControllerApi.removeAllUserScriptsForInstances(this); } + + @override + WKUserContentController copy() { + return WKUserContentController.detached( + observeValue: observeValue, + binaryMessenger: _userContentControllerApi.binaryMessenger, + instanceManager: _userContentControllerApi.instanceManager, + ); + } } /// A collection of properties that you use to initialize a web view. /// /// Wraps [WKWebViewConfiguration](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration?language=objc). +@immutable class WKWebViewConfiguration extends NSObject { /// Constructs a [WKWebViewConfiguration]. - factory WKWebViewConfiguration({ - BinaryMessenger? binaryMessenger, - InstanceManager? instanceManager, - }) { - final WKWebViewConfiguration configuration = - WKWebViewConfiguration.detached( - binaryMessenger: binaryMessenger, - instanceManager: instanceManager, - ); - configuration._webViewConfigurationApi.createForInstances(configuration); - return configuration; + WKWebViewConfiguration({ + super.observeValue, + super.binaryMessenger, + super.instanceManager, + }) : _webViewConfigurationApi = WKWebViewConfigurationHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ), + super.detached() { + // Ensures FlutterApis for the WebKit library are set up. + WebKitFlutterApis.instance.ensureSetUp(); + _webViewConfigurationApi.createForInstances(this); } /// A WKWebViewConfiguration that is owned by webView. @@ -519,17 +620,14 @@ class WKWebViewConfiguration extends NSObject { /// This should only be used outside of tests by subclasses created by this /// library or to create a copy for an InstanceManager. WKWebViewConfiguration.detached({ - BinaryMessenger? binaryMessenger, - InstanceManager? instanceManager, - }) : _binaryMessenger = binaryMessenger, - _instanceManager = instanceManager, - _webViewConfigurationApi = WKWebViewConfigurationHostApiImpl( + super.observeValue, + super.binaryMessenger, + super.instanceManager, + }) : _webViewConfigurationApi = WKWebViewConfigurationHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, - ); - - final BinaryMessenger? _binaryMessenger; - final InstanceManager? _instanceManager; + ), + super.detached(); late final WKWebViewConfigurationHostApiImpl _webViewConfigurationApi; @@ -537,13 +635,16 @@ class WKWebViewConfiguration extends NSObject { late final WKUserContentController userContentController = WKUserContentController.fromWebViewConfiguration( this, - binaryMessenger: _binaryMessenger, - instanceManager: _instanceManager, + binaryMessenger: _webViewConfigurationApi.binaryMessenger, + instanceManager: _webViewConfigurationApi.instanceManager, ); /// Manages the preference-related settings for the web view. - late final WKPreferences preferences = - WKPreferences.fromWebViewConfiguration(this); + late final WKPreferences preferences = WKPreferences.fromWebViewConfiguration( + this, + binaryMessenger: _webViewConfigurationApi.binaryMessenger, + instanceManager: _webViewConfigurationApi.instanceManager, + ); /// Used to get and set the site’s cookies and to track the cached data objects. /// @@ -551,8 +652,8 @@ class WKWebViewConfiguration extends NSObject { late final WKWebsiteDataStore websiteDataStore = WKWebsiteDataStore.fromWebViewConfiguration( this, - binaryMessenger: _binaryMessenger, - instanceManager: _instanceManager, + binaryMessenger: _webViewConfigurationApi.binaryMessenger, + instanceManager: _webViewConfigurationApi.instanceManager, ); /// Indicates whether HTML5 videos play inline or use the native full-screen controller. @@ -581,25 +682,54 @@ class WKWebViewConfiguration extends NSObject { types, ); } + + @override + WKWebViewConfiguration copy() { + return WKWebViewConfiguration.detached( + observeValue: observeValue, + binaryMessenger: _webViewConfigurationApi.binaryMessenger, + instanceManager: _webViewConfigurationApi.instanceManager, + ); + } } /// The methods for presenting native user interface elements on behalf of a webpage. /// /// Wraps [WKUIDelegate](https://developer.apple.com/documentation/webkit/wkuidelegate?language=objc). +@immutable class WKUIDelegate extends NSObject { /// Constructs a [WKUIDelegate]. WKUIDelegate({ this.onCreateWebView, super.observeValue, - BinaryMessenger? binaryMessenger, - InstanceManager? instanceManager, - }) : _uiDelegateApi = WKUIDelegateHostApiImpl( + super.binaryMessenger, + super.instanceManager, + }) : _uiDelegateApi = WKUIDelegateHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, - ) { + ), + super.detached() { + // Ensures FlutterApis for the WebKit library are set up. + WebKitFlutterApis.instance.ensureSetUp(); _uiDelegateApi.createForInstances(this); } + /// Constructs a [WKUIDelegate] without creating the associated Objective-C + /// object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies. + WKUIDelegate.detached({ + this.onCreateWebView, + super.observeValue, + super.binaryMessenger, + super.instanceManager, + }) : _uiDelegateApi = WKUIDelegateHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ), + super.detached(); + final WKUIDelegateHostApiImpl _uiDelegateApi; /// Indicates a new [WKWebView] was requested to be created with [configuration]. @@ -608,6 +738,16 @@ class WKUIDelegate extends NSObject { WKWebViewConfiguration configuration, WKNavigationAction navigationAction, )? onCreateWebView; + + @override + WKUIDelegate copy() { + return WKUIDelegate.detached( + onCreateWebView: onCreateWebView, + observeValue: observeValue, + binaryMessenger: _uiDelegateApi.binaryMessenger, + instanceManager: _uiDelegateApi.instanceManager, + ); + } } /// Methods for handling navigation changes and tracking navigation requests. @@ -629,10 +769,12 @@ class WKNavigationDelegate extends NSObject { super.observeValue, super.binaryMessenger, super.instanceManager, - }) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl( + }) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, - ) { + ), + super.detached() { + // Ensures FlutterApis for the WebKit library are set up. WebKitFlutterApis.instance.ensureSetUp(); _navigationDelegateApi.createForInstances(this); } @@ -652,10 +794,11 @@ class WKNavigationDelegate extends NSObject { super.observeValue, super.binaryMessenger, super.instanceManager, - }) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl( + }) : _navigationDelegateApi = WKNavigationDelegateHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, - ); + ), + super.detached(); final WKNavigationDelegateHostApiImpl _navigationDelegateApi; @@ -683,7 +826,7 @@ class WKNavigationDelegate extends NSObject { final void Function(WKWebView webView)? webViewWebContentProcessDidTerminate; @override - Copyable copy() { + WKNavigationDelegate copy() { return WKNavigationDelegate.detached( didFinishNavigation: didFinishNavigation, didStartProvisionalNavigation: didStartProvisionalNavigation, @@ -697,26 +840,12 @@ class WKNavigationDelegate extends NSObject { instanceManager: _navigationDelegateApi.instanceManager, ); } - - @override - int get hashCode { - return Object.hash(didFinishNavigation, _navigationDelegateApi, - _navigationDelegateApi.instanceManager.getIdentifier(this)); - } - - @override - bool operator ==(Object other) { - return other is WKNavigationDelegate && - didFinishNavigation == other.didFinishNavigation && - _navigationDelegateApi == other._navigationDelegateApi && - _navigationDelegateApi.instanceManager.getIdentifier(this) == - other._navigationDelegateApi.instanceManager.getIdentifier(other); - } } /// Object that displays interactive web content, such as for an in-app browser. /// /// Wraps [WKWebView](https://developer.apple.com/documentation/webkit/wkwebview?language=objc). +@immutable class WKWebView extends UIView { /// Constructs a [WKWebView]. /// @@ -732,17 +861,18 @@ class WKWebView extends UIView { super.observeValue, super.binaryMessenger, super.instanceManager, - }) : _binaryMessenger = binaryMessenger, - _instanceManager = instanceManager, - _webViewApi = WKWebViewHostApiImpl( + }) : _webViewApi = WKWebViewHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, - ) { + ), + super.detached() { + // Ensures FlutterApis for the WebKit library are set up. + WebKitFlutterApis.instance.ensureSetUp(); _webViewApi.createForInstances(this, configuration); } - /// Constructs a [WKWebView] without creating the associated - /// Objective-C object. + /// Constructs a [WKWebView] without creating the associated Objective-C + /// object. /// /// This should only be used outside of tests by subclasses created by this /// library or to create a copy for an InstanceManager. @@ -750,15 +880,11 @@ class WKWebView extends UIView { super.observeValue, super.binaryMessenger, super.instanceManager, - }) : _binaryMessenger = binaryMessenger, - _instanceManager = instanceManager, - _webViewApi = WKWebViewHostApiImpl( + }) : _webViewApi = WKWebViewHostApiImpl( binaryMessenger: binaryMessenger, instanceManager: instanceManager, - ); - - final BinaryMessenger? _binaryMessenger; - final InstanceManager? _instanceManager; + ), + super.detached(); final WKWebViewHostApiImpl _webViewApi; @@ -774,15 +900,15 @@ class WKWebView extends UIView { late final WKWebViewConfiguration configuration = WKWebViewConfiguration.fromWebView( this, - binaryMessenger: _binaryMessenger, - instanceManager: _instanceManager, + binaryMessenger: _webViewApi.binaryMessenger, + instanceManager: _webViewApi.instanceManager, ); /// The scrollable view associated with the web view. late final UIScrollView scrollView = UIScrollView.fromWebView( this, - binaryMessenger: _binaryMessenger, - instanceManager: _instanceManager, + binaryMessenger: _webViewApi.binaryMessenger, + instanceManager: _webViewApi.instanceManager, ); /// Used to integrate custom user interface elements into web view interactions. @@ -904,4 +1030,13 @@ class WKWebView extends UIView { javaScriptString, ); } + + @override + WKWebView copy() { + return WKWebView.detached( + observeValue: observeValue, + binaryMessenger: _webViewApi.binaryMessenger, + instanceManager: _webViewApi.instanceManager, + ); + } } 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 d0423b44df84..6a7fb6254889 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 @@ -296,6 +296,10 @@ class WebKitFlutterApis { uiDelegate, binaryMessenger: _binaryMessenger, ); + WKWebViewConfigurationFlutterApi.setup( + webViewConfiguration, + binaryMessenger: _binaryMessenger, + ); _hasBeenSetUp = true; } } @@ -305,9 +309,16 @@ class WebKitFlutterApis { class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi { /// Constructs a [WebsiteDataStoreHostApiImpl]. WKWebsiteDataStoreHostApiImpl({ - super.binaryMessenger, + this.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); + + /// Sends binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -350,9 +361,16 @@ class WKWebsiteDataStoreHostApiImpl extends WKWebsiteDataStoreHostApi { class WKScriptMessageHandlerHostApiImpl extends WKScriptMessageHandlerHostApi { /// Constructs a [WKScriptMessageHandlerHostApiImpl]. WKScriptMessageHandlerHostApiImpl({ - super.binaryMessenger, + this.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); + + /// Sends binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -396,9 +414,16 @@ class WKScriptMessageHandlerFlutterApiImpl class WKPreferencesHostApiImpl extends WKPreferencesHostApi { /// Constructs a [WKPreferencesHostApiImpl]. WKPreferencesHostApiImpl({ - super.binaryMessenger, + this.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); + + /// Sends binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -430,9 +455,16 @@ class WKPreferencesHostApiImpl extends WKPreferencesHostApi { class WKHttpCookieStoreHostApiImpl extends WKHttpCookieStoreHostApi { /// Constructs a [WKHttpCookieStoreHostApiImpl]. WKHttpCookieStoreHostApiImpl({ - super.binaryMessenger, + this.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); + + /// Sends binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -465,9 +497,16 @@ class WKUserContentControllerHostApiImpl extends WKUserContentControllerHostApi { /// Constructs a [WKUserContentControllerHostApiImpl]. WKUserContentControllerHostApiImpl({ - super.binaryMessenger, + this.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); + + /// Sends binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -539,9 +578,16 @@ class WKUserContentControllerHostApiImpl class WKWebViewConfigurationHostApiImpl extends WKWebViewConfigurationHostApi { /// Constructs a [WKWebViewConfigurationHostApiImpl]. WKWebViewConfigurationHostApiImpl({ - super.binaryMessenger, + this.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); + + /// Sends binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -620,9 +666,16 @@ class WKWebViewConfigurationFlutterApiImpl class WKUIDelegateHostApiImpl extends WKUIDelegateHostApi { /// Constructs a [WKUIDelegateHostApiImpl]. WKUIDelegateHostApiImpl({ - super.binaryMessenger, + this.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); + + /// Sends binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; @@ -666,7 +719,6 @@ class WKUIDelegateFlutterApiImpl extends WKUIDelegateFlutterApi { } /// Host api implementation for [WKNavigationDelegate]. -@immutable class WKNavigationDelegateHostApiImpl extends WKNavigationDelegateHostApi { /// Constructs a [WKNavigationDelegateHostApiImpl]. WKNavigationDelegateHostApiImpl({ @@ -688,18 +740,6 @@ class WKNavigationDelegateHostApiImpl extends WKNavigationDelegateHostApi { Future createForInstances(WKNavigationDelegate instance) async { return create(instanceManager.addDartCreatedInstance(instance)); } - - @override - int get hashCode { - return Object.hash(binaryMessenger, instanceManager); - } - - @override - bool operator ==(Object other) { - return other is WKNavigationDelegateHostApiImpl && - binaryMessenger == other.binaryMessenger && - instanceManager == other.instanceManager; - } } /// Flutter api implementation for [WKNavigationDelegate]. @@ -819,9 +859,16 @@ class WKNavigationDelegateFlutterApiImpl class WKWebViewHostApiImpl extends WKWebViewHostApi { /// Constructs a [WKWebViewHostApiImpl]. WKWebViewHostApiImpl({ - super.binaryMessenger, + this.binaryMessenger, InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager; + }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); + + /// Sends binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; /// Maintains instances stored to communicate with Objective-C objects. final InstanceManager instanceManager; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 99195fba4c33..ed1912ee7898 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -330,8 +330,6 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { // unsupported. This also goes for `null` and `undefined` on iOS 14+. For // example, when running a void function. For ease of use, this specific // error is ignored when no return value is expected. - // TODO(bparrishMines): Ensure the platform code includes the NSError in - // the FlutterError.details. if (exception.details is! NSError || exception.details.code != WKErrorCode.javaScriptResultTypeIsUnsupported) { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart index d97d152739c4..87b659885b52 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart @@ -37,7 +37,7 @@ void main() { mockPlatformHostApi = MockTestNSObjectHostApi(); TestNSObjectHostApi.setup(mockPlatformHostApi); - object = NSObject(instanceManager: instanceManager); + object = NSObject.detached(instanceManager: instanceManager); instanceManager.addDartCreatedInstance(object); }); @@ -46,7 +46,9 @@ void main() { }); test('addObserver', () async { - final NSObject observer = NSObject(instanceManager: instanceManager); + final NSObject observer = NSObject.detached( + instanceManager: instanceManager, + ); instanceManager.addDartCreatedInstance(observer); await object.addObserver( @@ -78,7 +80,9 @@ void main() { }); test('removeObserver', () async { - final NSObject observer = NSObject(instanceManager: instanceManager); + final NSObject observer = NSObject.detached( + instanceManager: instanceManager, + ); instanceManager.addDartCreatedInstance(observer); await object.removeObserver(observer, keyPath: 'aKeyPath'); @@ -97,7 +101,9 @@ void main() { callbackIdentifier = identifier; }); - final NSObject object = NSObject(instanceManager: instanceManager); + final NSObject object = NSObject.detached( + instanceManager: instanceManager, + ); final int identifier = instanceManager.addDartCreatedInstance(object); NSObject.dispose(object); @@ -112,7 +118,7 @@ void main() { instanceManager: instanceManager, ); - object = NSObject( + object = NSObject.detached( instanceManager: instanceManager, observeValue: ( String keyPath, @@ -151,7 +157,7 @@ void main() { instanceManager: instanceManager, ); - object = NSObject(instanceManager: instanceManager); + object = NSObject.detached(instanceManager: instanceManager); instanceManager.addHostCreatedInstance(object, 1); instanceManager.removeWeakReference(object); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart index aeb4e1cc54ce..f2250e1ac423 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.dart @@ -97,7 +97,7 @@ void main() { mockPlatformHostApi = MockTestUIViewHostApi(); TestUIViewHostApi.setup(mockPlatformHostApi); - view = UIView(instanceManager: instanceManager); + view = UIView.detached(instanceManager: instanceManager); viewInstanceId = instanceManager.addDartCreatedInstance(view); }); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart index b0c63b663066..e44e7b13a205 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.mocks.dart @@ -2,14 +2,12 @@ // in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart. // Do not manually edit this file. -import 'dart:async' as _i4; +import 'dart:async' as _i3; import 'package:mockito/mockito.dart' as _i1; -import 'package:webview_flutter_wkwebview/src/common/instance_manager.dart' - as _i2; import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart' - as _i5; -import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i3; + as _i4; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i2; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -21,77 +19,82 @@ import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i3; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types -class _FakeCopyable_0 extends _i1.Fake implements _i2.Copyable {} +class _FakeWKHttpCookieStore_0 extends _i1.Fake + implements _i2.WKHttpCookieStore {} -class _FakeWKHttpCookieStore_1 extends _i1.Fake - implements _i3.WKHttpCookieStore {} +class _FakeWKWebsiteDataStore_1 extends _i1.Fake + implements _i2.WKWebsiteDataStore {} /// A class which mocks [WKHttpCookieStore]. /// /// See the documentation for Mockito's code generation for more information. -class MockWKHttpCookieStore extends _i1.Mock implements _i3.WKHttpCookieStore { +// ignore: must_be_immutable +class MockWKHttpCookieStore extends _i1.Mock implements _i2.WKHttpCookieStore { MockWKHttpCookieStore() { _i1.throwOnMissingStub(this); } @override - _i4.Future setCookie(_i5.NSHttpCookie? cookie) => + _i3.Future setCookie(_i4.NSHttpCookie? cookie) => (super.noSuchMethod(Invocation.method(#setCookie, [cookie]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i3.Future); @override - _i4.Future addObserver(_i5.NSObject? observer, - {String? keyPath, Set<_i5.NSKeyValueObservingOptions>? options}) => + _i2.WKHttpCookieStore copy() => + (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWKHttpCookieStore_0()) as _i2.WKHttpCookieStore); + @override + _i3.Future addObserver(_i4.NSObject? observer, + {String? keyPath, Set<_i4.NSKeyValueObservingOptions>? options}) => (super.noSuchMethod( Invocation.method( #addObserver, [observer], {#keyPath: keyPath, #options: options}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i3.Future); @override - _i4.Future removeObserver(_i5.NSObject? observer, {String? keyPath}) => + _i3.Future removeObserver(_i4.NSObject? observer, {String? keyPath}) => (super.noSuchMethod( Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); - @override - _i2.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeCopyable_0()) as _i2.Copyable); + returnValueForMissingStub: Future.value()) as _i3.Future); } /// A class which mocks [WKWebsiteDataStore]. /// /// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable class MockWKWebsiteDataStore extends _i1.Mock - implements _i3.WKWebsiteDataStore { + implements _i2.WKWebsiteDataStore { MockWKWebsiteDataStore() { _i1.throwOnMissingStub(this); } @override - _i3.WKHttpCookieStore get httpCookieStore => + _i2.WKHttpCookieStore get httpCookieStore => (super.noSuchMethod(Invocation.getter(#httpCookieStore), - returnValue: _FakeWKHttpCookieStore_1()) as _i3.WKHttpCookieStore); + returnValue: _FakeWKHttpCookieStore_0()) as _i2.WKHttpCookieStore); @override - _i4.Future removeDataOfTypes( - Set<_i3.WKWebsiteDataType>? dataTypes, DateTime? since) => + _i3.Future removeDataOfTypes( + Set<_i2.WKWebsiteDataType>? dataTypes, DateTime? since) => (super.noSuchMethod( Invocation.method(#removeDataOfTypes, [dataTypes, since]), - returnValue: Future.value(false)) as _i4.Future); + returnValue: Future.value(false)) as _i3.Future); @override - _i4.Future addObserver(_i5.NSObject? observer, - {String? keyPath, Set<_i5.NSKeyValueObservingOptions>? options}) => + _i2.WKWebsiteDataStore copy() => + (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWKWebsiteDataStore_1()) as _i2.WKWebsiteDataStore); + @override + _i3.Future addObserver(_i4.NSObject? observer, + {String? keyPath, Set<_i4.NSKeyValueObservingOptions>? options}) => (super.noSuchMethod( Invocation.method( #addObserver, [observer], {#keyPath: keyPath, #options: options}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i3.Future); @override - _i4.Future removeObserver(_i5.NSObject? observer, {String? keyPath}) => + _i3.Future removeObserver(_i4.NSObject? observer, {String? keyPath}) => (super.noSuchMethod( Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); - @override - _i2.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeCopyable_0()) as _i2.Copyable); + returnValueForMissingStub: Future.value()) as _i3.Future); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index 6161bd044435..f216711ca0b2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -2,25 +2,23 @@ // in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart. // Do not manually edit this file. -import 'dart:async' as _i6; +import 'dart:async' as _i5; import 'dart:math' as _i2; -import 'dart:ui' as _i7; +import 'dart:ui' as _i6; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_platform_interface/src/types/javascript_channel.dart' - as _i10; + as _i9; import 'package:webview_flutter_platform_interface/src/types/types.dart' - as _i11; + as _i10; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart' - as _i9; -import 'package:webview_flutter_wkwebview/src/common/instance_manager.dart' - as _i3; -import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart' as _i8; -import 'package:webview_flutter_wkwebview/src/ui_kit/ui_kit.dart' as _i5; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart' + as _i7; +import 'package:webview_flutter_wkwebview/src/ui_kit/ui_kit.dart' as _i3; import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i4; import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart' - as _i12; + as _i11; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -34,84 +32,83 @@ import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart' class _FakePoint_0 extends _i1.Fake implements _i2.Point {} -class _FakeCopyable_1 extends _i1.Fake implements _i3.Copyable {} +class _FakeUIScrollView_1 extends _i1.Fake implements _i3.UIScrollView {} + +class _FakeWKNavigationDelegate_2 extends _i1.Fake + implements _i4.WKNavigationDelegate {} + +class _FakeWKPreferences_3 extends _i1.Fake implements _i4.WKPreferences {} -class _FakeWKWebViewConfiguration_2 extends _i1.Fake +class _FakeWKScriptMessageHandler_4 extends _i1.Fake + implements _i4.WKScriptMessageHandler {} + +class _FakeWKWebViewConfiguration_5 extends _i1.Fake implements _i4.WKWebViewConfiguration {} -class _FakeUIScrollView_3 extends _i1.Fake implements _i5.UIScrollView {} +class _FakeWKWebView_6 extends _i1.Fake implements _i4.WKWebView {} -class _FakeWKUserContentController_4 extends _i1.Fake +class _FakeWKUserContentController_7 extends _i1.Fake implements _i4.WKUserContentController {} -class _FakeWKPreferences_5 extends _i1.Fake implements _i4.WKPreferences {} - -class _FakeWKWebsiteDataStore_6 extends _i1.Fake +class _FakeWKWebsiteDataStore_8 extends _i1.Fake implements _i4.WKWebsiteDataStore {} -class _FakeWKHttpCookieStore_7 extends _i1.Fake +class _FakeWKHttpCookieStore_9 extends _i1.Fake implements _i4.WKHttpCookieStore {} -class _FakeWKWebView_8 extends _i1.Fake implements _i4.WKWebView {} - -class _FakeWKScriptMessageHandler_9 extends _i1.Fake - implements _i4.WKScriptMessageHandler {} - class _FakeWKUIDelegate_10 extends _i1.Fake implements _i4.WKUIDelegate {} -class _FakeWKNavigationDelegate_11 extends _i1.Fake - implements _i4.WKNavigationDelegate {} - /// A class which mocks [UIScrollView]. /// /// See the documentation for Mockito's code generation for more information. -class MockUIScrollView extends _i1.Mock implements _i5.UIScrollView { +// ignore: must_be_immutable +class MockUIScrollView extends _i1.Mock implements _i3.UIScrollView { MockUIScrollView() { _i1.throwOnMissingStub(this); } @override - _i6.Future<_i2.Point> getContentOffset() => (super.noSuchMethod( + _i5.Future<_i2.Point> getContentOffset() => (super.noSuchMethod( Invocation.method(#getContentOffset, []), returnValue: Future<_i2.Point>.value(_FakePoint_0())) - as _i6.Future<_i2.Point>); + as _i5.Future<_i2.Point>); @override - _i6.Future scrollBy(_i2.Point? offset) => + _i5.Future scrollBy(_i2.Point? offset) => (super.noSuchMethod(Invocation.method(#scrollBy, [offset]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future setContentOffset(_i2.Point? offset) => + _i5.Future setContentOffset(_i2.Point? offset) => (super.noSuchMethod(Invocation.method(#setContentOffset, [offset]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i3.UIScrollView copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeUIScrollView_1()) as _i3.UIScrollView); @override - _i6.Future setBackgroundColor(_i7.Color? color) => + _i5.Future setBackgroundColor(_i6.Color? color) => (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future setOpaque(bool? opaque) => + _i5.Future setOpaque(bool? opaque) => (super.noSuchMethod(Invocation.method(#setOpaque, [opaque]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future addObserver(_i8.NSObject? observer, - {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => (super.noSuchMethod( Invocation.method( #addObserver, [observer], {#keyPath: keyPath, #options: options}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future removeObserver(_i8.NSObject? observer, {String? keyPath}) => + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => (super.noSuchMethod( Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override - _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeCopyable_1()) as _i3.Copyable); + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKNavigationDelegate]. @@ -125,59 +122,62 @@ class MockWKNavigationDelegate extends _i1.Mock } @override - _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeCopyable_1()) as _i3.Copyable); + _i4.WKNavigationDelegate copy() => (super.noSuchMethod( + Invocation.method(#copy, []), + returnValue: _FakeWKNavigationDelegate_2()) as _i4.WKNavigationDelegate); @override - _i6.Future addObserver(_i8.NSObject? observer, - {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => (super.noSuchMethod( Invocation.method( #addObserver, [observer], {#keyPath: keyPath, #options: options}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future removeObserver(_i8.NSObject? observer, {String? keyPath}) => + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => (super.noSuchMethod( Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKPreferences]. /// /// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable class MockWKPreferences extends _i1.Mock implements _i4.WKPreferences { MockWKPreferences() { _i1.throwOnMissingStub(this); } @override - _i6.Future setJavaScriptEnabled(bool? enabled) => + _i5.Future setJavaScriptEnabled(bool? enabled) => (super.noSuchMethod(Invocation.method(#setJavaScriptEnabled, [enabled]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future addObserver(_i8.NSObject? observer, - {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => + _i4.WKPreferences copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWKPreferences_3()) as _i4.WKPreferences); + @override + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => (super.noSuchMethod( Invocation.method( #addObserver, [observer], {#keyPath: keyPath, #options: options}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future removeObserver(_i8.NSObject? observer, {String? keyPath}) => + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => (super.noSuchMethod( Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override - _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeCopyable_1()) as _i3.Copyable); + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKScriptMessageHandler]. /// /// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable class MockWKScriptMessageHandler extends _i1.Mock implements _i4.WKScriptMessageHandler { MockWKScriptMessageHandler() { @@ -192,27 +192,30 @@ class MockWKScriptMessageHandler extends _i1.Mock _i4.WKScriptMessage message) {}) as void Function( _i4.WKUserContentController, _i4.WKScriptMessage)); @override - _i6.Future addObserver(_i8.NSObject? observer, - {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => + _i4.WKScriptMessageHandler copy() => + (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWKScriptMessageHandler_4()) + as _i4.WKScriptMessageHandler); + @override + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => (super.noSuchMethod( Invocation.method( #addObserver, [observer], {#keyPath: keyPath, #options: options}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future removeObserver(_i8.NSObject? observer, {String? keyPath}) => + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => (super.noSuchMethod( Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override - _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeCopyable_1()) as _i3.Copyable); + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKWebView]. /// /// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable class MockWKWebView extends _i1.Mock implements _i4.WKWebView { MockWKWebView() { _i1.throwOnMissingStub(this); @@ -221,127 +224,128 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { @override _i4.WKWebViewConfiguration get configuration => (super.noSuchMethod(Invocation.getter(#configuration), - returnValue: _FakeWKWebViewConfiguration_2()) + returnValue: _FakeWKWebViewConfiguration_5()) as _i4.WKWebViewConfiguration); @override - _i5.UIScrollView get scrollView => + _i3.UIScrollView get scrollView => (super.noSuchMethod(Invocation.getter(#scrollView), - returnValue: _FakeUIScrollView_3()) as _i5.UIScrollView); + returnValue: _FakeUIScrollView_1()) as _i3.UIScrollView); @override - _i6.Future setUIDelegate(_i4.WKUIDelegate? delegate) => + _i5.Future setUIDelegate(_i4.WKUIDelegate? delegate) => (super.noSuchMethod(Invocation.method(#setUIDelegate, [delegate]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future setNavigationDelegate(_i4.WKNavigationDelegate? delegate) => + _i5.Future setNavigationDelegate(_i4.WKNavigationDelegate? delegate) => (super.noSuchMethod(Invocation.method(#setNavigationDelegate, [delegate]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future getUrl() => + _i5.Future getUrl() => (super.noSuchMethod(Invocation.method(#getUrl, []), - returnValue: Future.value()) as _i6.Future); + returnValue: Future.value()) as _i5.Future); @override - _i6.Future getEstimatedProgress() => + _i5.Future getEstimatedProgress() => (super.noSuchMethod(Invocation.method(#getEstimatedProgress, []), - returnValue: Future.value(0.0)) as _i6.Future); + returnValue: Future.value(0.0)) as _i5.Future); @override - _i6.Future loadRequest(_i8.NSUrlRequest? request) => + _i5.Future loadRequest(_i7.NSUrlRequest? request) => (super.noSuchMethod(Invocation.method(#loadRequest, [request]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future loadHtmlString(String? string, {String? baseUrl}) => + _i5.Future loadHtmlString(String? string, {String? baseUrl}) => (super.noSuchMethod( Invocation.method(#loadHtmlString, [string], {#baseUrl: baseUrl}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future loadFileUrl(String? url, {String? readAccessUrl}) => + _i5.Future loadFileUrl(String? url, {String? readAccessUrl}) => (super.noSuchMethod( Invocation.method( #loadFileUrl, [url], {#readAccessUrl: readAccessUrl}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future loadFlutterAsset(String? key) => + _i5.Future loadFlutterAsset(String? key) => (super.noSuchMethod(Invocation.method(#loadFlutterAsset, [key]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future canGoBack() => + _i5.Future canGoBack() => (super.noSuchMethod(Invocation.method(#canGoBack, []), - returnValue: Future.value(false)) as _i6.Future); + returnValue: Future.value(false)) as _i5.Future); @override - _i6.Future canGoForward() => + _i5.Future canGoForward() => (super.noSuchMethod(Invocation.method(#canGoForward, []), - returnValue: Future.value(false)) as _i6.Future); + returnValue: Future.value(false)) as _i5.Future); @override - _i6.Future goBack() => + _i5.Future goBack() => (super.noSuchMethod(Invocation.method(#goBack, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future goForward() => + _i5.Future goForward() => (super.noSuchMethod(Invocation.method(#goForward, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future reload() => + _i5.Future reload() => (super.noSuchMethod(Invocation.method(#reload, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future getTitle() => + _i5.Future getTitle() => (super.noSuchMethod(Invocation.method(#getTitle, []), - returnValue: Future.value()) as _i6.Future); + returnValue: Future.value()) as _i5.Future); @override - _i6.Future setAllowsBackForwardNavigationGestures(bool? allow) => + _i5.Future setAllowsBackForwardNavigationGestures(bool? allow) => (super.noSuchMethod( Invocation.method(#setAllowsBackForwardNavigationGestures, [allow]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future setCustomUserAgent(String? userAgent) => + _i5.Future setCustomUserAgent(String? userAgent) => (super.noSuchMethod(Invocation.method(#setCustomUserAgent, [userAgent]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future evaluateJavaScript(String? javaScriptString) => (super + _i5.Future evaluateJavaScript(String? javaScriptString) => (super .noSuchMethod(Invocation.method(#evaluateJavaScript, [javaScriptString]), - returnValue: Future.value()) as _i6.Future); + returnValue: Future.value()) as _i5.Future); + @override + _i4.WKWebView copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWKWebView_6()) as _i4.WKWebView); @override - _i6.Future setBackgroundColor(_i7.Color? color) => + _i5.Future setBackgroundColor(_i6.Color? color) => (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future setOpaque(bool? opaque) => + _i5.Future setOpaque(bool? opaque) => (super.noSuchMethod(Invocation.method(#setOpaque, [opaque]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future addObserver(_i8.NSObject? observer, - {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => (super.noSuchMethod( Invocation.method( #addObserver, [observer], {#keyPath: keyPath, #options: options}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future removeObserver(_i8.NSObject? observer, {String? keyPath}) => + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => (super.noSuchMethod( Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override - _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeCopyable_1()) as _i3.Copyable); + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKWebViewConfiguration]. /// /// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable class MockWKWebViewConfiguration extends _i1.Mock implements _i4.WKWebViewConfiguration { MockWKWebViewConfiguration() { @@ -351,51 +355,54 @@ class MockWKWebViewConfiguration extends _i1.Mock @override _i4.WKUserContentController get userContentController => (super.noSuchMethod(Invocation.getter(#userContentController), - returnValue: _FakeWKUserContentController_4()) + returnValue: _FakeWKUserContentController_7()) as _i4.WKUserContentController); @override _i4.WKPreferences get preferences => (super.noSuchMethod(Invocation.getter(#preferences), - returnValue: _FakeWKPreferences_5()) as _i4.WKPreferences); + returnValue: _FakeWKPreferences_3()) as _i4.WKPreferences); @override _i4.WKWebsiteDataStore get websiteDataStore => (super.noSuchMethod(Invocation.getter(#websiteDataStore), - returnValue: _FakeWKWebsiteDataStore_6()) as _i4.WKWebsiteDataStore); + returnValue: _FakeWKWebsiteDataStore_8()) as _i4.WKWebsiteDataStore); @override - _i6.Future setAllowsInlineMediaPlayback(bool? allow) => (super + _i5.Future setAllowsInlineMediaPlayback(bool? allow) => (super .noSuchMethod(Invocation.method(#setAllowsInlineMediaPlayback, [allow]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future setMediaTypesRequiringUserActionForPlayback( + _i5.Future setMediaTypesRequiringUserActionForPlayback( Set<_i4.WKAudiovisualMediaType>? types) => (super.noSuchMethod( Invocation.method( #setMediaTypesRequiringUserActionForPlayback, [types]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i4.WKWebViewConfiguration copy() => + (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWKWebViewConfiguration_5()) + as _i4.WKWebViewConfiguration); @override - _i6.Future addObserver(_i8.NSObject? observer, - {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => (super.noSuchMethod( Invocation.method( #addObserver, [observer], {#keyPath: keyPath, #options: options}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future removeObserver(_i8.NSObject? observer, {String? keyPath}) => + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => (super.noSuchMethod( Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override - _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeCopyable_1()) as _i3.Copyable); + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKWebsiteDataStore]. /// /// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable class MockWKWebsiteDataStore extends _i1.Mock implements _i4.WKWebsiteDataStore { MockWKWebsiteDataStore() { @@ -405,62 +412,65 @@ class MockWKWebsiteDataStore extends _i1.Mock @override _i4.WKHttpCookieStore get httpCookieStore => (super.noSuchMethod(Invocation.getter(#httpCookieStore), - returnValue: _FakeWKHttpCookieStore_7()) as _i4.WKHttpCookieStore); + returnValue: _FakeWKHttpCookieStore_9()) as _i4.WKHttpCookieStore); @override - _i6.Future removeDataOfTypes( + _i5.Future removeDataOfTypes( Set<_i4.WKWebsiteDataType>? dataTypes, DateTime? since) => (super.noSuchMethod( Invocation.method(#removeDataOfTypes, [dataTypes, since]), - returnValue: Future.value(false)) as _i6.Future); + returnValue: Future.value(false)) as _i5.Future); + @override + _i4.WKWebsiteDataStore copy() => + (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWKWebsiteDataStore_8()) as _i4.WKWebsiteDataStore); @override - _i6.Future addObserver(_i8.NSObject? observer, - {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => (super.noSuchMethod( Invocation.method( #addObserver, [observer], {#keyPath: keyPath, #options: options}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future removeObserver(_i8.NSObject? observer, {String? keyPath}) => + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => (super.noSuchMethod( Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override - _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeCopyable_1()) as _i3.Copyable); + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKUIDelegate]. /// /// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable class MockWKUIDelegate extends _i1.Mock implements _i4.WKUIDelegate { MockWKUIDelegate() { _i1.throwOnMissingStub(this); } @override - _i6.Future addObserver(_i8.NSObject? observer, - {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => + _i4.WKUIDelegate copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWKUIDelegate_10()) as _i4.WKUIDelegate); + @override + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => (super.noSuchMethod( Invocation.method( #addObserver, [observer], {#keyPath: keyPath, #options: options}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future removeObserver(_i8.NSObject? observer, {String? keyPath}) => + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => (super.noSuchMethod( Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override - _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeCopyable_1()) as _i3.Copyable); + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [WKUserContentController]. /// /// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable class MockWKUserContentController extends _i1.Mock implements _i4.WKUserContentController { MockWKUserContentController() { @@ -468,72 +478,74 @@ class MockWKUserContentController extends _i1.Mock } @override - _i6.Future addScriptMessageHandler( + _i5.Future addScriptMessageHandler( _i4.WKScriptMessageHandler? handler, String? name) => (super.noSuchMethod( Invocation.method(#addScriptMessageHandler, [handler, name]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future removeScriptMessageHandler(String? name) => (super + _i5.Future removeScriptMessageHandler(String? name) => (super .noSuchMethod(Invocation.method(#removeScriptMessageHandler, [name]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future removeAllScriptMessageHandlers() => (super.noSuchMethod( + _i5.Future removeAllScriptMessageHandlers() => (super.noSuchMethod( Invocation.method(#removeAllScriptMessageHandlers, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future addUserScript(_i4.WKUserScript? userScript) => + _i5.Future addUserScript(_i4.WKUserScript? userScript) => (super.noSuchMethod(Invocation.method(#addUserScript, [userScript]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future removeAllUserScripts() => + _i5.Future removeAllUserScripts() => (super.noSuchMethod(Invocation.method(#removeAllUserScripts, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future addObserver(_i8.NSObject? observer, - {String? keyPath, Set<_i8.NSKeyValueObservingOptions>? options}) => + _i4.WKUserContentController copy() => + (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWKUserContentController_7()) + as _i4.WKUserContentController); + @override + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => (super.noSuchMethod( Invocation.method( #addObserver, [observer], {#keyPath: keyPath, #options: options}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i6.Future removeObserver(_i8.NSObject? observer, {String? keyPath}) => + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => (super.noSuchMethod( Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i6.Future); - @override - _i3.Copyable copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeCopyable_1()) as _i3.Copyable); + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [JavascriptChannelRegistry]. /// /// See the documentation for Mockito's code generation for more information. class MockJavascriptChannelRegistry extends _i1.Mock - implements _i9.JavascriptChannelRegistry { + implements _i8.JavascriptChannelRegistry { MockJavascriptChannelRegistry() { _i1.throwOnMissingStub(this); } @override - Map get channels => + Map get channels => (super.noSuchMethod(Invocation.getter(#channels), - returnValue: {}) - as Map); + returnValue: {}) + as Map); @override void onJavascriptChannelMessage(String? channel, String? message) => super.noSuchMethod( Invocation.method(#onJavascriptChannelMessage, [channel, message]), returnValueForMissingStub: null); @override - void updateJavascriptChannelsFromSet(Set<_i10.JavascriptChannel>? channels) => + void updateJavascriptChannelsFromSet(Set<_i9.JavascriptChannel>? channels) => super.noSuchMethod( Invocation.method(#updateJavascriptChannelsFromSet, [channels]), returnValueForMissingStub: null); @@ -543,17 +555,17 @@ class MockJavascriptChannelRegistry extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewPlatformCallbacksHandler extends _i1.Mock - implements _i9.WebViewPlatformCallbacksHandler { + implements _i8.WebViewPlatformCallbacksHandler { MockWebViewPlatformCallbacksHandler() { _i1.throwOnMissingStub(this); } @override - _i6.FutureOr onNavigationRequest({String? url, bool? isForMainFrame}) => + _i5.FutureOr onNavigationRequest({String? url, bool? isForMainFrame}) => (super.noSuchMethod( Invocation.method(#onNavigationRequest, [], {#url: url, #isForMainFrame: isForMainFrame}), - returnValue: Future.value(false)) as _i6.FutureOr); + returnValue: Future.value(false)) as _i5.FutureOr); @override void onPageStarted(String? url) => super.noSuchMethod(Invocation.method(#onPageStarted, [url]), @@ -567,7 +579,7 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock super.noSuchMethod(Invocation.method(#onProgress, [progress]), returnValueForMissingStub: null); @override - void onWebResourceError(_i11.WebResourceError? error) => + void onWebResourceError(_i10.WebResourceError? error) => super.noSuchMethod(Invocation.method(#onWebResourceError, [error]), returnValueForMissingStub: null); } @@ -576,7 +588,7 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewWidgetProxy extends _i1.Mock - implements _i12.WebViewWidgetProxy { + implements _i11.WebViewWidgetProxy { MockWebViewWidgetProxy() { _i1.throwOnMissingStub(this); } @@ -584,12 +596,12 @@ class MockWebViewWidgetProxy extends _i1.Mock @override _i4.WKWebView createWebView(_i4.WKWebViewConfiguration? configuration, {void Function( - String, _i8.NSObject, Map<_i8.NSKeyValueChangeKey, Object?>)? + String, _i7.NSObject, Map<_i7.NSKeyValueChangeKey, Object?>)? observeValue}) => (super.noSuchMethod( Invocation.method( #createWebView, [configuration], {#observeValue: observeValue}), - returnValue: _FakeWKWebView_8()) as _i4.WKWebView); + returnValue: _FakeWKWebView_6()) as _i4.WKWebView); @override _i4.WKScriptMessageHandler createScriptMessageHandler( {void Function(_i4.WKUserContentController, _i4.WKScriptMessage)? @@ -597,7 +609,7 @@ class MockWebViewWidgetProxy extends _i1.Mock (super.noSuchMethod( Invocation.method(#createScriptMessageHandler, [], {#didReceiveScriptMessage: didReceiveScriptMessage}), - returnValue: _FakeWKScriptMessageHandler_9()) + returnValue: _FakeWKScriptMessageHandler_4()) as _i4.WKScriptMessageHandler); @override _i4.WKUIDelegate createUIDelgate( @@ -612,11 +624,11 @@ class MockWebViewWidgetProxy extends _i1.Mock _i4.WKNavigationDelegate createNavigationDelegate( {void Function(_i4.WKWebView, String?)? didFinishNavigation, void Function(_i4.WKWebView, String?)? didStartProvisionalNavigation, - _i6.Future<_i4.WKNavigationActionPolicy> Function( + _i5.Future<_i4.WKNavigationActionPolicy> Function( _i4.WKWebView, _i4.WKNavigationAction)? decidePolicyForNavigationAction, - void Function(_i4.WKWebView, _i8.NSError)? didFailNavigation, - void Function(_i4.WKWebView, _i8.NSError)? + void Function(_i4.WKWebView, _i7.NSError)? didFailNavigation, + void Function(_i4.WKWebView, _i7.NSError)? didFailProvisionalNavigation, void Function(_i4.WKWebView)? webViewWebContentProcessDidTerminate}) => @@ -631,6 +643,6 @@ class MockWebViewWidgetProxy extends _i1.Mock #webViewWebContentProcessDidTerminate: webViewWebContentProcessDidTerminate }), - returnValue: _FakeWKNavigationDelegate_11()) + returnValue: _FakeWKNavigationDelegate_2()) as _i4.WKNavigationDelegate); } From 0bdbddeb1f0e438cf15413f4a52b1eccc3d9a03b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jun 2022 22:11:04 +0000 Subject: [PATCH 456/844] [webview]: Bump junit from 4.12 to 4.13.2 in /packages/webview_flutter/webview_flutter_android/example/android/app (#6030) --- .../webview_flutter_android/example/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle b/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle index 19c77efa342a..b75bc9df5ebb 100644 --- a/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle @@ -55,7 +55,7 @@ flutter { } dependencies { - testImplementation 'junit:junit:4.12' + 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' From 1f45e9c03445ec3db63164c25a51b3d9af368da0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jun 2022 19:07:56 -0400 Subject: [PATCH 457/844] Bump junit to 4.13.2 (#5585) * [path_provider]: Bump junit Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.2. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.2) --- updated-dependencies: - dependency-name: junit:junit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Bump junit versions Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: camsim99 Co-authored-by: Stuart Morgan --- packages/camera/camera/example/android/app/build.gradle | 2 +- packages/camera/camera_android/android/build.gradle | 2 +- packages/espresso/README.md | 2 +- packages/espresso/android/build.gradle | 2 +- packages/espresso/example/android/app/build.gradle | 2 +- packages/flutter_plugin_android_lifecycle/android/build.gradle | 2 +- .../example/android/app/build.gradle | 2 +- .../google_maps_flutter/android/build.gradle | 2 +- .../google_maps_flutter/example/android/app/build.gradle | 2 +- .../google_sign_in/google_sign_in_android/android/build.gradle | 2 +- .../image_picker/image_picker/example/android/app/build.gradle | 2 +- packages/image_picker/image_picker_android/android/build.gradle | 2 +- .../image_picker_android/example/android/app/build.gradle | 2 +- .../in_app_purchase/example/android/app/build.gradle | 2 +- .../in_app_purchase_android/android/build.gradle | 2 +- .../in_app_purchase_android/example/android/app/build.gradle | 2 +- packages/local_auth/local_auth/example/android/app/build.gradle | 2 +- packages/local_auth/local_auth_android/android/build.gradle | 2 +- .../local_auth_android/example/android/app/build.gradle | 2 +- .../path_provider/example/android/app/build.gradle | 2 +- .../path_provider/path_provider_android/android/build.gradle | 2 +- .../path_provider_android/example/android/app/build.gradle | 2 +- .../quick_actions/example/android/app/build.gradle | 2 +- .../quick_actions/quick_actions_android/android/build.gradle | 2 +- .../quick_actions_android/example/android/app/build.gradle | 2 +- .../shared_preferences_android/android/build.gradle | 2 +- .../url_launcher/url_launcher/example/android/app/build.gradle | 2 +- packages/url_launcher/url_launcher_android/android/build.gradle | 2 +- .../url_launcher_android/example/android/app/build.gradle | 2 +- packages/video_player/video_player_android/android/build.gradle | 2 +- .../webview_flutter/example/android/app/build.gradle | 2 +- .../webview_flutter_android/android/build.gradle | 2 +- 32 files changed, 32 insertions(+), 32 deletions(-) diff --git a/packages/camera/camera/example/android/app/build.gradle b/packages/camera/camera/example/android/app/build.gradle index 476d65373723..5d6af5887012 100644 --- a/packages/camera/camera/example/android/app/build.gradle +++ b/packages/camera/camera/example/android/app/build.gradle @@ -57,7 +57,7 @@ flutter { } dependencies { - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:rules:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' diff --git a/packages/camera/camera_android/android/build.gradle b/packages/camera/camera_android/android/build.gradle index 264f7f8edc7d..202767632117 100644 --- a/packages/camera/camera_android/android/build.gradle +++ b/packages/camera/camera_android/android/build.gradle @@ -60,7 +60,7 @@ android { dependencies { compileOnly 'androidx.annotation:annotation:1.1.0' - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:4.0.0' testImplementation 'androidx.test:core:1.3.0' testImplementation 'org.robolectric:robolectric:4.5' diff --git a/packages/espresso/README.md b/packages/espresso/README.md index 6d66bfbe85b5..95c72e334423 100644 --- a/packages/espresso/README.md +++ b/packages/espresso/README.md @@ -18,7 +18,7 @@ Add the following dependencies in android/app/build.gradle: ```groovy dependencies { - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' testImplementation "com.google.truth:truth:1.0" androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' diff --git a/packages/espresso/android/build.gradle b/packages/espresso/android/build.gradle index 62baba0ac31b..6b003f9befde 100644 --- a/packages/espresso/android/build.gradle +++ b/packages/espresso/android/build.gradle @@ -54,7 +54,7 @@ dependencies { implementation 'com.google.code.gson:gson:2.8.6' androidTestImplementation 'org.hamcrest:hamcrest:2.2' - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' testImplementation "com.google.truth:truth:1.0" api 'androidx.test:runner:1.1.1' api 'androidx.test.espresso:espresso-core:3.1.1' diff --git a/packages/espresso/example/android/app/build.gradle b/packages/espresso/example/android/app/build.gradle index 5ed5eedb0175..21a59edcba40 100644 --- a/packages/espresso/example/android/app/build.gradle +++ b/packages/espresso/example/android/app/build.gradle @@ -55,7 +55,7 @@ flutter { } dependencies { - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' testImplementation "com.google.truth:truth:1.0" androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' diff --git a/packages/flutter_plugin_android_lifecycle/android/build.gradle b/packages/flutter_plugin_android_lifecycle/android/build.gradle index 7f44c6584456..36695ffef5be 100644 --- a/packages/flutter_plugin_android_lifecycle/android/build.gradle +++ b/packages/flutter_plugin_android_lifecycle/android/build.gradle @@ -53,7 +53,7 @@ android { } dependencies { - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:1.10.19' } diff --git a/packages/flutter_plugin_android_lifecycle/example/android/app/build.gradle b/packages/flutter_plugin_android_lifecycle/example/android/app/build.gradle index bc87d03f1aeb..e96ede6844ff 100644 --- a/packages/flutter_plugin_android_lifecycle/example/android/app/build.gradle +++ b/packages/flutter_plugin_android_lifecycle/example/android/app/build.gradle @@ -55,7 +55,7 @@ flutter { } dependencies { - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } diff --git a/packages/google_maps_flutter/google_maps_flutter/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter/android/build.gradle index 356e3c5e22f7..24502e5193c8 100644 --- a/packages/google_maps_flutter/google_maps_flutter/android/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter/android/build.gradle @@ -39,7 +39,7 @@ android { androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:rules:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:3.2.4' testImplementation 'androidx.test:core:1.2.0' testImplementation "org.robolectric:robolectric:4.3.1" diff --git a/packages/google_maps_flutter/google_maps_flutter/example/android/app/build.gradle b/packages/google_maps_flutter/google_maps_flutter/example/android/app/build.gradle index e46f18b41c22..f6d29f63fadc 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/android/app/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter/example/android/app/build.gradle @@ -60,7 +60,7 @@ android { } dependencies { - testImplementation 'junit:junit:4.12' + 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.2.0' 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 8a2b69cb1789..c977e4daaf5e 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 @@ -50,6 +50,6 @@ android { dependencies { implementation 'com.google.android.gms:play-services-auth:20.0.1' implementation 'com.google.guava:guava:28.1-android' - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:3.9.0' } diff --git a/packages/image_picker/image_picker/example/android/app/build.gradle b/packages/image_picker/image_picker/example/android/app/build.gradle index e73e3fe01003..e83cb5a13c06 100755 --- a/packages/image_picker/image_picker/example/android/app/build.gradle +++ b/packages/image_picker/image_picker/example/android/app/build.gradle @@ -60,7 +60,7 @@ flutter { } dependencies { - testImplementation 'junit:junit:4.12' + 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.2.0' diff --git a/packages/image_picker/image_picker_android/android/build.gradle b/packages/image_picker/image_picker_android/android/build.gradle index 516c7ae9770c..393c4bc4b559 100755 --- a/packages/image_picker/image_picker_android/android/build.gradle +++ b/packages/image_picker/image_picker_android/android/build.gradle @@ -37,7 +37,7 @@ android { implementation 'androidx.annotation:annotation:1.3.0' implementation 'androidx.exifinterface:exifinterface:1.3.3' - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:3.10.0' testImplementation 'androidx.test:core:1.4.0' testImplementation "org.robolectric:robolectric:4.8.1" diff --git a/packages/image_picker/image_picker_android/example/android/app/build.gradle b/packages/image_picker/image_picker_android/example/android/app/build.gradle index c8e8f3f5b90d..31d8c82a0a9d 100755 --- a/packages/image_picker/image_picker_android/example/android/app/build.gradle +++ b/packages/image_picker/image_picker_android/example/android/app/build.gradle @@ -60,7 +60,7 @@ flutter { } dependencies { - testImplementation 'junit:junit:4.12' + 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/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 772b8839be59..dc8718957291 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 @@ -107,7 +107,7 @@ flutter { dependencies { implementation 'com.android.billingclient:billing:3.0.2' - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:3.6.0' testImplementation 'org.json:json:20180813' androidTestImplementation 'androidx.test:runner:1.1.1' diff --git a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle index cbb0855509a5..9a5a74d0032b 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle +++ b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle @@ -54,7 +54,7 @@ android { dependencies { implementation 'androidx.annotation:annotation:1.0.0' implementation 'com.android.billingclient:billing:3.0.2' - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' testImplementation 'org.json:json:20180813' testImplementation 'org.mockito:mockito-core:3.6.0' androidTestImplementation 'androidx.test:runner:1.1.1' 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 8ddcaf74ef47..b7dc1856204e 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 @@ -107,7 +107,7 @@ flutter { dependencies { implementation 'com.android.billingclient:billing:3.0.2' - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:3.6.0' testImplementation 'org.json:json:20180813' androidTestImplementation 'androidx.test:runner:1.1.1' diff --git a/packages/local_auth/local_auth/example/android/app/build.gradle b/packages/local_auth/local_auth/example/android/app/build.gradle index d1cef4bf53a9..3c6eca7ce8a7 100644 --- a/packages/local_auth/local_auth/example/android/app/build.gradle +++ b/packages/local_auth/local_auth/example/android/app/build.gradle @@ -52,7 +52,7 @@ flutter { } dependencies { - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } diff --git a/packages/local_auth/local_auth_android/android/build.gradle b/packages/local_auth/local_auth_android/android/build.gradle index de8ae50641b2..309d2fb1e424 100644 --- a/packages/local_auth/local_auth_android/android/build.gradle +++ b/packages/local_auth/local_auth_android/android/build.gradle @@ -52,7 +52,7 @@ dependencies { api "androidx.core:core:1.3.2" api "androidx.biometric:biometric:1.1.0" api "androidx.fragment:fragment:1.3.2" - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:3.9.0' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:rules:1.2.0' diff --git a/packages/local_auth/local_auth_android/example/android/app/build.gradle b/packages/local_auth/local_auth_android/example/android/app/build.gradle index d1cef4bf53a9..3c6eca7ce8a7 100644 --- a/packages/local_auth/local_auth_android/example/android/app/build.gradle +++ b/packages/local_auth/local_auth_android/example/android/app/build.gradle @@ -52,7 +52,7 @@ flutter { } dependencies { - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } diff --git a/packages/path_provider/path_provider/example/android/app/build.gradle b/packages/path_provider/path_provider/example/android/app/build.gradle index 6ef1396ef1ac..6d2bd6dadc36 100644 --- a/packages/path_provider/path_provider/example/android/app/build.gradle +++ b/packages/path_provider/path_provider/example/android/app/build.gradle @@ -58,7 +58,7 @@ dependencies { androidTestImplementation 'androidx.test:rules:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } diff --git a/packages/path_provider/path_provider_android/android/build.gradle b/packages/path_provider/path_provider_android/android/build.gradle index c7ceec2584c6..4bfa738ac44c 100644 --- a/packages/path_provider/path_provider_android/android/build.gradle +++ b/packages/path_provider/path_provider_android/android/build.gradle @@ -54,5 +54,5 @@ android { dependencies { implementation 'androidx.annotation:annotation:1.1.0' implementation 'com.google.guava:guava:28.1-android' - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' } diff --git a/packages/path_provider/path_provider_android/example/android/app/build.gradle b/packages/path_provider/path_provider_android/example/android/app/build.gradle index 6ef1396ef1ac..6d2bd6dadc36 100644 --- a/packages/path_provider/path_provider_android/example/android/app/build.gradle +++ b/packages/path_provider/path_provider_android/example/android/app/build.gradle @@ -58,7 +58,7 @@ dependencies { androidTestImplementation 'androidx.test:rules:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } diff --git a/packages/quick_actions/quick_actions/example/android/app/build.gradle b/packages/quick_actions/quick_actions/example/android/app/build.gradle index 54f2e59bacf7..75fe3543e987 100644 --- a/packages/quick_actions/quick_actions/example/android/app/build.gradle +++ b/packages/quick_actions/quick_actions/example/android/app/build.gradle @@ -52,7 +52,7 @@ flutter { } dependencies { - testImplementation 'junit:junit:4.12' + 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.2.0' diff --git a/packages/quick_actions/quick_actions_android/android/build.gradle b/packages/quick_actions/quick_actions_android/android/build.gradle index 590bc6b4a826..252a35246aa9 100644 --- a/packages/quick_actions/quick_actions_android/android/build.gradle +++ b/packages/quick_actions/quick_actions_android/android/build.gradle @@ -34,7 +34,7 @@ android { } dependencies { - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:3.2.4' } diff --git a/packages/quick_actions/quick_actions_android/example/android/app/build.gradle b/packages/quick_actions/quick_actions_android/example/android/app/build.gradle index 54f2e59bacf7..75fe3543e987 100644 --- a/packages/quick_actions/quick_actions_android/example/android/app/build.gradle +++ b/packages/quick_actions/quick_actions_android/example/android/app/build.gradle @@ -52,7 +52,7 @@ flutter { } dependencies { - testImplementation 'junit:junit:4.12' + 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.2.0' diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index 6eeab718059f..76106179a8a9 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -42,7 +42,7 @@ android { baseline file("lint-baseline.xml") } dependencies { - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:3.9.0' } diff --git a/packages/url_launcher/url_launcher/example/android/app/build.gradle b/packages/url_launcher/url_launcher/example/android/app/build.gradle index ca41cb14b6f4..8c7e84563ee6 100644 --- a/packages/url_launcher/url_launcher/example/android/app/build.gradle +++ b/packages/url_launcher/url_launcher/example/android/app/build.gradle @@ -54,7 +54,7 @@ flutter { } dependencies { - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } diff --git a/packages/url_launcher/url_launcher_android/android/build.gradle b/packages/url_launcher/url_launcher_android/android/build.gradle index 180d7b2bdd9c..9c95688eca6d 100644 --- a/packages/url_launcher/url_launcher_android/android/build.gradle +++ b/packages/url_launcher/url_launcher_android/android/build.gradle @@ -49,7 +49,7 @@ android { dependencies { compileOnly 'androidx.annotation:annotation:1.0.0' - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:1.10.19' testImplementation 'androidx.test:core:1.0.0' testImplementation 'org.robolectric:robolectric:4.3' diff --git a/packages/url_launcher/url_launcher_android/example/android/app/build.gradle b/packages/url_launcher/url_launcher_android/example/android/app/build.gradle index ca41cb14b6f4..8c7e84563ee6 100644 --- a/packages/url_launcher/url_launcher_android/example/android/app/build.gradle +++ b/packages/url_launcher/url_launcher_android/example/android/app/build.gradle @@ -54,7 +54,7 @@ flutter { } dependencies { - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } diff --git a/packages/video_player/video_player_android/android/build.gradle b/packages/video_player/video_player_android/android/build.gradle index f9ad4e682630..f6b559408a7d 100644 --- a/packages/video_player/video_player_android/android/build.gradle +++ b/packages/video_player/video_player_android/android/build.gradle @@ -47,7 +47,7 @@ android { implementation 'com.google.android.exoplayer:exoplayer-hls:2.17.1' implementation 'com.google.android.exoplayer:exoplayer-dash:2.17.1' implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.17.1' - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' testImplementation 'androidx.test:core:1.3.0' testImplementation 'org.mockito:mockito-inline:3.9.0' testImplementation 'org.robolectric:robolectric:4.5' diff --git a/packages/webview_flutter/webview_flutter/example/android/app/build.gradle b/packages/webview_flutter/webview_flutter/example/android/app/build.gradle index 532b2adde9d6..8548d5b30ddd 100644 --- a/packages/webview_flutter/webview_flutter/example/android/app/build.gradle +++ b/packages/webview_flutter/webview_flutter/example/android/app/build.gradle @@ -55,7 +55,7 @@ flutter { } dependencies { - testImplementation 'junit:junit:4.12' + 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.2.0' diff --git a/packages/webview_flutter/webview_flutter_android/android/build.gradle b/packages/webview_flutter/webview_flutter_android/android/build.gradle index 20d7660ece50..6ba3f65dbedc 100644 --- a/packages/webview_flutter/webview_flutter_android/android/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/android/build.gradle @@ -37,7 +37,7 @@ android { dependencies { implementation 'androidx.annotation:annotation:1.4.0' implementation 'androidx.webkit:webkit:1.0.0' - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:4.6.1' testImplementation 'androidx.test:core:1.3.0' } From b6c1d110f27d166588637edf5d47ea3930d71049 Mon Sep 17 00:00:00 2001 From: Gabriel Terwesten Date: Thu, 23 Jun 2022 16:00:54 +0200 Subject: [PATCH 458/844] [google_sign_in_platform_interface] Add support for `serverClientId` (#5256) This PR is a prerequisite for implementing #5250. It adds support for passing a server client ID to platform implementations when initializing them. --- .../CHANGELOG.md | 4 ++++ .../src/method_channel_google_sign_in.dart | 1 + .../lib/src/types.dart | 24 ++++++++++++++++++- .../pubspec.yaml | 2 +- .../method_channel_google_sign_in_test.dart | 3 +++ 5 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md index e78060d84744..591f36eb1ae8 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.0 + +* Adds support for the `serverClientId` parameter. + ## 2.1.3 * Enables mocking models by changing overridden operator == parameter type from `dynamic` to `Object`. diff --git a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart index 8b755fbf1cdd..c3b158dd8450 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/method_channel_google_sign_in.dart @@ -39,6 +39,7 @@ class MethodChannelGoogleSignIn extends GoogleSignInPlatform { 'scopes': params.scopes, 'hostedDomain': params.hostedDomain, 'clientId': params.clientId, + 'serverClientId': params.serverClientId, 'forceCodeForRefreshToken': params.forceCodeForRefreshToken, }); } diff --git a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart index e1f455d6ea44..422fe807253d 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/lib/src/types.dart @@ -35,6 +35,7 @@ class SignInInitParameters { this.signInOption = SignInOption.standard, this.hostedDomain, this.clientId, + this.serverClientId, this.forceCodeForRefreshToken = false, }); @@ -49,9 +50,30 @@ class SignInInitParameters { /// By default, the list of accounts will not be restricted. final String? hostedDomain; - /// The client ID to use when signing in. + /// The OAuth client ID of the app. + /// + /// The default is null, which means that the client ID will be sourced from a + /// configuration file, if required on the current platform. A value specified + /// here takes precedence over a value specified in a configuration file. + /// See also: + /// + /// * [Platform Integration](https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in#platform-integration), + /// where you can find the details about the configuration files. final String? clientId; + /// The OAuth client ID of the backend server. + /// + /// The default is null, which means that the server client ID will be sourced + /// from a configuration file, if available and supported on the current + /// platform. A value specified here takes precedence over a value specified + /// in a configuration file. + /// + /// See also: + /// + /// * [Platform Integration](https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in#platform-integration), + /// where you can find the details about the configuration files. + final String? serverClientId; + /// If true, ensures the authorization code can be exchanged for an access /// token. /// diff --git a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml index 9f6ab508d249..9ad3e1cf005b 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%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.1.3 +version: 2.2.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart b/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart index 1ffdc5a4f95e..944ad3419b8e 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/test/method_channel_google_sign_in_test.dart @@ -107,6 +107,7 @@ void main() { 'scopes': ['two', 'scopes'], 'signInOption': 'SignInOption.games', 'clientId': 'fakeClientId', + 'serverClientId': null, 'forceCodeForRefreshToken': false, }), () { @@ -144,6 +145,7 @@ void main() { scopes: ['two', 'scopes'], signInOption: SignInOption.games, clientId: 'fakeClientId', + serverClientId: 'fakeServerClientId', forceCodeForRefreshToken: true)); expect(log, [ isMethodCall('init', arguments: { @@ -151,6 +153,7 @@ void main() { 'scopes': ['two', 'scopes'], 'signInOption': 'SignInOption.games', 'clientId': 'fakeClientId', + 'serverClientId': 'fakeServerClientId', 'forceCodeForRefreshToken': true, }), ]); From f8f2bd1537f2dc57787cdf402b7d9e8b17b18218 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 23 Jun 2022 13:39:09 -0400 Subject: [PATCH 459/844] Roll Flutter from 60f30e5d3ed4 to a30012b27505 (8 revisions) (#6033) --- .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 e2f727e78c19..003cd3e29d7d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -60f30e5d3ed41a2a615545171d85139b8abf664e +a30012b27505622633429b4fa001c8631da5e048 From 8bf46f15bfe88cb801f3b746965eda3542e85ce8 Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Thu, 23 Jun 2022 16:04:09 -0700 Subject: [PATCH 460/844] [camera] Partially Address CameraAccessException: CAMERA_ERROR (#5723) --- packages/camera/camera_android/CHANGELOG.md | 4 ++ .../io/flutter/plugins/camera/Camera.java | 22 ++++-- .../io/flutter/plugins/camera/CameraTest.java | 72 +++++++++++++++++++ packages/camera/camera_android/pubspec.yaml | 2 +- 4 files changed, 95 insertions(+), 5 deletions(-) diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index e9972aede28a..1bc9be5af9c3 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.8+3 + +* Skips duplicate calls to stop background thread and removes unnecessary closings of camera capture sessions on Android. + ## 0.9.8+2 * Fixes exception in registerWith caused by the switch to an in-package method channel. diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 0521c422d794..401963c91374 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -131,6 +131,8 @@ class Camera /** An additional thread for running tasks that shouldn't block the UI. */ private HandlerThread backgroundHandlerThread; + /** True when backgroundHandlerThread is in the process of being stopped. */ + private boolean stoppingBackgroundHandlerThread = false; private CameraDeviceWrapper cameraDevice; private CameraCaptureSession captureSession; @@ -382,8 +384,8 @@ public void onError(@NonNull CameraDevice cameraDevice, int errorCode) { backgroundHandler); } - private void createCaptureSession(int templateType, Surface... surfaces) - throws CameraAccessException { + @VisibleForTesting + void createCaptureSession(int templateType, Surface... surfaces) throws CameraAccessException { createCaptureSession(templateType, null, surfaces); } @@ -391,7 +393,7 @@ private void createCaptureSession( int templateType, Runnable onSuccessCallback, Surface... surfaces) throws CameraAccessException { // Close any existing capture session. - closeCaptureSession(); + captureSession = null; // Create a new capture builder. previewRequestBuilder = cameraDevice.createCaptureRequest(templateType); @@ -669,7 +671,11 @@ public void startBackgroundThread() { /** Stops the background thread and its {@link Handler}. */ public void stopBackgroundThread() { + if (stoppingBackgroundHandlerThread) { + return; + } if (backgroundHandlerThread != null) { + stoppingBackgroundHandlerThread = true; backgroundHandlerThread.quitSafely(); try { backgroundHandlerThread.join(); @@ -679,6 +685,7 @@ public void stopBackgroundThread() { } backgroundHandlerThread = null; backgroundHandler = null; + stoppingBackgroundHandlerThread = false; } /** Start capturing a picture, doing autofocus first. */ @@ -1173,12 +1180,19 @@ private void closeCaptureSession() { public void close() { Log.i(TAG, "close"); - closeCaptureSession(); if (cameraDevice != null) { cameraDevice.close(); cameraDevice = null; + + // Closing the CameraDevice without closing the CameraCaptureSession is recommended + // for quickly closing the camera: + // https://developer.android.com/reference/android/hardware/camera2/CameraCaptureSession#close() + captureSession = null; + } else { + closeCaptureSession(); } + if (pictureImageReader != null) { pictureImageReader.close(); pictureImageReader = null; diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java index 167733b9dcca..b85b685ca90b 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java @@ -18,8 +18,10 @@ import static org.mockito.Mockito.when; import android.app.Activity; +import android.graphics.SurfaceTexture; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.params.SessionConfiguration; @@ -28,6 +30,7 @@ import android.os.Build; import android.os.Handler; import android.os.HandlerThread; +import android.util.Size; import android.view.Surface; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -35,6 +38,7 @@ import io.flutter.embedding.engine.systemchannels.PlatformChannel; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugins.camera.features.CameraFeatureFactory; +import io.flutter.plugins.camera.features.CameraFeatures; import io.flutter.plugins.camera.features.Point; import io.flutter.plugins.camera.features.autofocus.AutoFocusFeature; import io.flutter.plugins.camera.features.autofocus.FocusMode; @@ -833,6 +837,28 @@ public void startBackgroundThread_shouldNotStartNewThreadWhenAlreadyCreated() { verify(mockHandlerThread, times(1)).start(); } + @Test + public void stopBackgroundThread_cancelsDuplicateCalls() throws InterruptedException { + TestUtils.setPrivateField(camera, "stoppingBackgroundHandlerThread", true); + + camera.startBackgroundThread(); + camera.stopBackgroundThread(); + + verify(mockHandlerThread, never()).quitSafely(); + verify(mockHandlerThread, never()).join(); + } + + @Test + public void stopBackgroundThread_proceedsWithoutDuplicateCall() throws InterruptedException { + TestUtils.setPrivateField(camera, "stoppingBackgroundHandlerThread", false); + + camera.startBackgroundThread(); + camera.stopBackgroundThread(); + + verify(mockHandlerThread).quitSafely(); + verify(mockHandlerThread).join(); + } + @Test public void onConverge_shouldTakePictureWithoutAbortingSession() throws CameraAccessException { ArrayList mockRequestBuilders = new ArrayList<>(); @@ -856,6 +882,52 @@ public void onConverge_shouldTakePictureWithoutAbortingSession() throws CameraAc verify(mockCaptureSession, never()).abortCaptures(); } + @Test + public void createCaptureSession_doesNotCloseCaptureSession() throws CameraAccessException { + Surface mockSurface = mock(Surface.class); + SurfaceTexture mockSurfaceTexture = mock(SurfaceTexture.class); + ResolutionFeature mockResolutionFeature = mock(ResolutionFeature.class); + Size mockSize = mock(Size.class); + ArrayList mockRequestBuilders = new ArrayList<>(); + mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); + CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); + TestUtils.setPrivateField(camera, "cameraDevice", fakeCamera); + + TextureRegistry.SurfaceTextureEntry cameraFlutterTexture = + (TextureRegistry.SurfaceTextureEntry) TestUtils.getPrivateField(camera, "flutterTexture"); + CameraFeatures cameraFeatures = + (CameraFeatures) TestUtils.getPrivateField(camera, "cameraFeatures"); + ResolutionFeature resolutionFeature = + (ResolutionFeature) + TestUtils.getPrivateField(mockCameraFeatureFactory, "mockResolutionFeature"); + + when(cameraFlutterTexture.surfaceTexture()).thenReturn(mockSurfaceTexture); + when(resolutionFeature.getPreviewSize()).thenReturn(mockSize); + + camera.createCaptureSession(CameraDevice.TEMPLATE_PREVIEW, mockSurface); + + verify(mockCaptureSession, never()).close(); + } + + @Test + public void close_doesCloseCaptureSessionWhenCameraDeviceNull() { + camera.close(); + + verify(mockCaptureSession).close(); + } + + @Test + public void close_doesNotCloseCaptureSessionWhenCameraDeviceNonNull() { + ArrayList mockRequestBuilders = new ArrayList<>(); + mockRequestBuilders.add(mock(CaptureRequest.Builder.class)); + CameraDeviceWrapper fakeCamera = new FakeCameraDeviceWrapper(mockRequestBuilders); + TestUtils.setPrivateField(camera, "cameraDevice", fakeCamera); + + camera.close(); + + verify(mockCaptureSession, never()).close(); + } + private static class TestCameraFeatureFactory implements CameraFeatureFactory { private final AutoFocusFeature mockAutoFocusFeature; private final ExposureLockFeature mockExposureLockFeature; diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 7d93ecb22e47..a1b7a930e228 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android description: Android implementation of the camera plugin. repository: https://github.com/flutter/plugins/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.9.8+2 +version: 0.9.8+3 environment: sdk: ">=2.14.0 <3.0.0" From 5d72e9ae59ab5fcd0eb074017efd2b98acd8d30b Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 24 Jun 2022 12:04:12 -0400 Subject: [PATCH 461/844] Roll Flutter from a30012b27505 to 6c3a0e4d21da (6 revisions) (#6037) --- .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 003cd3e29d7d..6ed90983369e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a30012b27505622633429b4fa001c8631da5e048 +6c3a0e4d21da2478a58d8b00fbabfa29ef0df045 From abeb29f102c9bc5412f7b99f123c5d9771c0ddee Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Fri, 24 Jun 2022 14:10:19 -0700 Subject: [PATCH 462/844] [path_provider] Fixed support for querying the root external storage directory (#6036) --- .../path_provider_android/CHANGELOG.md | 4 ++++ .../plugins/pathprovider/Messages.java | 23 ++++++++++--------- .../pathprovider/PathProviderPlugin.java | 2 ++ .../integration_test/path_provider_test.dart | 1 + .../path_provider_android/lib/messages.g.dart | 3 ++- .../lib/path_provider_android.dart | 11 +++++---- .../pigeons/messages.dart | 1 + .../path_provider_android/pubspec.yaml | 2 +- .../test/path_provider_android_test.dart | 6 ----- 9 files changed, 29 insertions(+), 24 deletions(-) diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index 6b1b6611004e..c40c103d5848 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.16 + +* Fixes bug with `getExternalStoragePaths(null)`. + ## 2.0.15 * Switches the medium from MethodChannels to Pigeon. diff --git a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/Messages.java b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/Messages.java index 1e78916ebbc6..47144d4a8fcd 100644 --- a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/Messages.java +++ b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/Messages.java @@ -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 (v3.1.5), do not edit directly. +// Autogenerated from Pigeon (v3.2.0), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.pathprovider; @@ -23,16 +23,17 @@ public class Messages { public enum StorageDirectory { - music(0), - podcasts(1), - ringtones(2), - alarms(3), - notifications(4), - pictures(5), - movies(6), - downloads(7), - dcim(8), - documents(9); + root(0), + music(1), + podcasts(2), + ringtones(3), + alarms(4), + notifications(5), + pictures(6), + movies(7), + downloads(8), + dcim(9), + documents(10); private int index; diff --git a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java index 8a5ff48b6477..7ef82198b22c 100644 --- a/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java +++ b/packages/path_provider/path_provider_android/android/src/main/java/io/flutter/plugins/pathprovider/PathProviderPlugin.java @@ -125,6 +125,8 @@ private List getPathProviderExternalCacheDirectories() { private String getStorageDirectoryString(@NonNull Messages.StorageDirectory directory) { switch (directory) { + case root: + return null; case music: return "music"; case podcasts: diff --git a/packages/path_provider/path_provider_android/example/integration_test/path_provider_test.dart b/packages/path_provider/path_provider_android/example/integration_test/path_provider_test.dart index 0538738ade7e..2be88130b4e7 100644 --- a/packages/path_provider/path_provider_android/example/integration_test/path_provider_test.dart +++ b/packages/path_provider/path_provider_android/example/integration_test/path_provider_test.dart @@ -68,6 +68,7 @@ void main() { final List? directories = await provider.getExternalStoragePaths(type: type); expect(directories, isNotNull); + expect(directories, isNotEmpty); for (final String result in directories!) { _verifySampleFile(result, '$type'); } diff --git a/packages/path_provider/path_provider_android/lib/messages.g.dart b/packages/path_provider/path_provider_android/lib/messages.g.dart index 50009f454ad7..cf095c244b8d 100644 --- a/packages/path_provider/path_provider_android/lib/messages.g.dart +++ b/packages/path_provider/path_provider_android/lib/messages.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v3.1.5), do not edit directly. +// Autogenerated from Pigeon (v3.2.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 // @dart = 2.12 @@ -12,6 +12,7 @@ import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; import 'package:flutter/services.dart'; enum StorageDirectory { + root, music, podcasts, ringtones, diff --git a/packages/path_provider/path_provider_android/lib/path_provider_android.dart b/packages/path_provider/path_provider_android/lib/path_provider_android.dart index 4f08d7af08d4..f5c74f540253 100644 --- a/packages/path_provider/path_provider_android/lib/path_provider_android.dart +++ b/packages/path_provider/path_provider_android/lib/path_provider_android.dart @@ -5,8 +5,11 @@ import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; import 'messages.g.dart' as messages; -messages.StorageDirectory _convertStorageDirectory(StorageDirectory directory) { +messages.StorageDirectory _convertStorageDirectory( + StorageDirectory? directory) { switch (directory) { + case null: + return messages.StorageDirectory.root; case StorageDirectory.music: return messages.StorageDirectory.music; case StorageDirectory.podcasts: @@ -73,10 +76,8 @@ class PathProviderAndroid extends PathProviderPlatform { Future?> getExternalStoragePaths({ StorageDirectory? type, }) async { - return type == null - ? [] - : (await _api.getExternalStoragePaths(_convertStorageDirectory(type))) - .cast(); + return (await _api.getExternalStoragePaths(_convertStorageDirectory(type))) + .cast(); } @override diff --git a/packages/path_provider/path_provider_android/pigeons/messages.dart b/packages/path_provider/path_provider_android/pigeons/messages.dart index 464166771b0a..96ad6343d3b0 100644 --- a/packages/path_provider/path_provider_android/pigeons/messages.dart +++ b/packages/path_provider/path_provider_android/pigeons/messages.dart @@ -14,6 +14,7 @@ import 'package:pigeon/pigeon.dart'; copyrightHeader: 'pigeons/copyright.txt', )) enum StorageDirectory { + root, music, podcasts, ringtones, diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index 64b953b2ca07..4c228bbb43bc 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.15 +version: 2.0.16 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_android/test/path_provider_android_test.dart b/packages/path_provider/path_provider_android/test/path_provider_android_test.dart index 2fe4a51fe5eb..e3011474a2a3 100644 --- a/packages/path_provider/path_provider_android/test/path_provider_android_test.dart +++ b/packages/path_provider/path_provider_android/test/path_provider_android_test.dart @@ -89,12 +89,6 @@ void main() { }); } // end of for-loop - test('getExternalStoragePaths with null android succeeds', () async { - final List? result = - await pathProvider.getExternalStoragePaths(type: null); - expect(result!.length, 0); - }); - test('getDownloadsPath fails', () async { try { await pathProvider.getDownloadsPath(); From 1d327c4a5684f25d1f54bd49748f20e7615795c3 Mon Sep 17 00:00:00 2001 From: Vladislav Khomenko Date: Mon, 27 Jun 2022 21:09:10 +0300 Subject: [PATCH 463/844] Added ability to purchase multiple quantity of one product (#5711) --- .../in_app_purchase_storekit/CHANGELOG.md | 4 ++ .../in_app_purchase_storekit_platform.dart | 3 +- .../src/types/app_store_purchase_param.dart | 4 ++ .../in_app_purchase_storekit/pubspec.yaml | 2 +- .../test/fakes/fake_storekit_platform.dart | 55 ++++++++++------ ...n_app_purchase_storekit_platform_test.dart | 62 +++++++++++++++++++ 6 files changed, 108 insertions(+), 22 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 a6897eaf1412..445fdb6aa491 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.1 + +* Adds ability to purchase more than one of a product. + ## 0.3.0+10 * Ignores deprecation warnings for upcoming styleFrom button API changes. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart index 629355d12d4c..f81b36699834 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart @@ -71,7 +71,8 @@ class InAppPurchaseStoreKitPlatform extends InAppPurchasePlatform { Future buyNonConsumable({required PurchaseParam purchaseParam}) async { await _skPaymentQueueWrapper.addPayment(SKPaymentWrapper( productIdentifier: purchaseParam.productDetails.id, - quantity: 1, + quantity: + purchaseParam is AppStorePurchaseParam ? purchaseParam.quantity : 1, applicationUsername: purchaseParam.applicationUserName, simulatesAskToBuyInSandbox: purchaseParam is AppStorePurchaseParam && purchaseParam.simulatesAskToBuyInSandbox, diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_param.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_param.dart index b2d8eea9d791..168ef5cea5f4 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_param.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_param.dart @@ -12,6 +12,7 @@ class AppStorePurchaseParam extends PurchaseParam { AppStorePurchaseParam({ required ProductDetails productDetails, String? applicationUserName, + this.quantity = 1, this.simulatesAskToBuyInSandbox = false, }) : super( productDetails: productDetails, @@ -28,4 +29,7 @@ class AppStorePurchaseParam extends PurchaseParam { /// /// See also [SKPaymentWrapper.simulatesAskToBuyInSandbox]. final bool simulatesAskToBuyInSandbox; + + /// Quantity of the product user requested to buy. + final int quantity; } 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 ada883ce8f57..dd65b259a283 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 platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/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.0+10 +version: 0.3.1 environment: sdk: ">=2.14.0 <3.0.0" 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 7c543750c25e..e987a5ceac8c 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 @@ -56,20 +56,24 @@ class FakeStoreKitPlatform { queueIsActive = false; } - SKPaymentTransactionWrapper createPendingTransaction(String id) { + SKPaymentTransactionWrapper createPendingTransaction(String id, + {int quantity = 1}) { return SKPaymentTransactionWrapper( - transactionIdentifier: '', - payment: SKPaymentWrapper(productIdentifier: id), - transactionState: SKPaymentTransactionStateWrapper.purchasing, - transactionTimeStamp: 123123.121, - error: null, - originalTransaction: null); + transactionIdentifier: '', + payment: SKPaymentWrapper(productIdentifier: id, quantity: quantity), + transactionState: SKPaymentTransactionStateWrapper.purchasing, + transactionTimeStamp: 123123.121, + error: null, + originalTransaction: null, + ); } SKPaymentTransactionWrapper createPurchasedTransaction( - String productId, String transactionId) { + String productId, String transactionId, + {int quantity = 1}) { return SKPaymentTransactionWrapper( - payment: SKPaymentWrapper(productIdentifier: productId), + payment: + SKPaymentWrapper(productIdentifier: productId, quantity: quantity), transactionState: SKPaymentTransactionStateWrapper.purchased, transactionTimeStamp: 123123.121, transactionIdentifier: transactionId, @@ -77,10 +81,12 @@ class FakeStoreKitPlatform { originalTransaction: null); } - SKPaymentTransactionWrapper createFailedTransaction(String productId) { + SKPaymentTransactionWrapper createFailedTransaction(String productId, + {int quantity = 1}) { return SKPaymentTransactionWrapper( transactionIdentifier: '', - payment: SKPaymentWrapper(productIdentifier: productId), + payment: + SKPaymentWrapper(productIdentifier: productId, quantity: quantity), transactionState: SKPaymentTransactionStateWrapper.failed, transactionTimeStamp: 123123.121, error: const SKError( @@ -91,10 +97,12 @@ class FakeStoreKitPlatform { } SKPaymentTransactionWrapper createCanceledTransaction( - String productId, int errorCode) { + String productId, int errorCode, + {int quantity = 1}) { return SKPaymentTransactionWrapper( transactionIdentifier: '', - payment: SKPaymentWrapper(productIdentifier: productId), + payment: + SKPaymentWrapper(productIdentifier: productId, quantity: quantity), transactionState: SKPaymentTransactionStateWrapper.failed, transactionTimeStamp: 123123.121, error: SKError( @@ -105,9 +113,11 @@ class FakeStoreKitPlatform { } SKPaymentTransactionWrapper createRestoredTransaction( - String productId, String transactionId) { + String productId, String transactionId, + {int quantity = 1}) { return SKPaymentTransactionWrapper( - payment: SKPaymentWrapper(productIdentifier: productId), + payment: + SKPaymentWrapper(productIdentifier: productId, quantity: quantity), transactionState: SKPaymentTransactionStateWrapper.restored, transactionTimeStamp: 123123.121, transactionIdentifier: transactionId, @@ -166,25 +176,29 @@ class FakeStoreKitPlatform { return Future.sync(() {}); case '-[InAppPurchasePlugin addPayment:result:]': final String id = call.arguments['productIdentifier'] as String; + final int quantity = call.arguments['quantity'] as int; final SKPaymentTransactionWrapper transaction = - createPendingTransaction(id); + createPendingTransaction(id, quantity: quantity); + transactions.add(transaction); InAppPurchaseStoreKitPlatform.observer.updatedTransactions( transactions: [transaction]); sleep(const Duration(milliseconds: 30)); if (testTransactionFail) { final SKPaymentTransactionWrapper transactionFailed = - createFailedTransaction(id); + createFailedTransaction(id, quantity: quantity); InAppPurchaseStoreKitPlatform.observer.updatedTransactions( transactions: [transactionFailed]); } else if (testTransactionCancel > 0) { final SKPaymentTransactionWrapper transactionCanceled = - createCanceledTransaction(id, testTransactionCancel); + createCanceledTransaction(id, testTransactionCancel, + quantity: quantity); InAppPurchaseStoreKitPlatform.observer.updatedTransactions( transactions: [transactionCanceled]); } else { final SKPaymentTransactionWrapper transactionFinished = createPurchasedTransaction( - id, transaction.transactionIdentifier ?? ''); + id, transaction.transactionIdentifier ?? '', + quantity: quantity); InAppPurchaseStoreKitPlatform.observer.updatedTransactions( transactions: [transactionFinished]); } @@ -192,7 +206,8 @@ class FakeStoreKitPlatform { case '-[InAppPurchasePlugin finishTransaction:result:]': finishedTransactions.add(createPurchasedTransaction( call.arguments['productIdentifier'] as String, - call.arguments['transactionIdentifier'] as String)); + call.arguments['transactionIdentifier'] as String, + quantity: transactions.first.payment.quantity)); break; case '-[SKPaymentQueue startObservingTransactionQueue]': queueIsActive = true; 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 e92d8487ed93..852599ac3670 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 @@ -427,6 +427,68 @@ void main() { final PurchaseStatus purchaseStatus = await completer.future; expect(purchaseStatus, PurchaseStatus.canceled); }); + + test( + 'buying non consumable, should be able to purchase multiple quantity of one product', + () async { + final List details = []; + final Completer> completer = + Completer>(); + final Stream> stream = + iapStoreKitPlatform.purchaseStream; + late StreamSubscription> subscription; + subscription = stream.listen((List purchaseDetailsList) { + details.addAll(purchaseDetailsList); + for (final PurchaseDetails purchaseDetails in purchaseDetailsList) { + if (purchaseDetails.pendingCompletePurchase) { + iapStoreKitPlatform.completePurchase(purchaseDetails); + completer.complete(details); + subscription.cancel(); + } + } + }); + final AppStoreProductDetails productDetails = + AppStoreProductDetails.fromSKProduct(dummyProductWrapper); + final AppStorePurchaseParam purchaseParam = AppStorePurchaseParam( + productDetails: productDetails, + quantity: 5, + applicationUserName: 'appName'); + await iapStoreKitPlatform.buyNonConsumable(purchaseParam: purchaseParam); + await completer.future; + expect( + fakeStoreKitPlatform.finishedTransactions.first.payment.quantity, 5); + }); + + test( + 'buying consumable, should be able to purchase multiple quantity of one product', + () async { + final List details = []; + final Completer> completer = + Completer>(); + final Stream> stream = + iapStoreKitPlatform.purchaseStream; + late StreamSubscription> subscription; + subscription = stream.listen((List purchaseDetailsList) { + details.addAll(purchaseDetailsList); + for (final PurchaseDetails purchaseDetails in purchaseDetailsList) { + if (purchaseDetails.pendingCompletePurchase) { + iapStoreKitPlatform.completePurchase(purchaseDetails); + completer.complete(details); + subscription.cancel(); + } + } + }); + final AppStoreProductDetails productDetails = + AppStoreProductDetails.fromSKProduct(dummyProductWrapper); + final AppStorePurchaseParam purchaseParam = AppStorePurchaseParam( + productDetails: productDetails, + quantity: 5, + applicationUserName: 'appName'); + await iapStoreKitPlatform.buyConsumable(purchaseParam: purchaseParam); + await completer.future; + expect( + fakeStoreKitPlatform.finishedTransactions.first.payment.quantity, 5); + }); }); group('complete purchase', () { From 4e29c26dedcf3de97836572821d20adbb0e8178a Mon Sep 17 00:00:00 2001 From: Alexandre Ardhuin Date: Tue, 28 Jun 2022 16:54:08 +0200 Subject: [PATCH 464/844] ignore upcoming warnings (#6044) --- .../lib/src/ui_kit/ui_kit_api_impls.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart index 6a54166fbad8..b6e4cadec879 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart @@ -5,6 +5,8 @@ import 'dart:async'; import 'dart:math'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) +// ignore: unnecessary_import import 'package:flutter/painting.dart' show Color; import 'package:flutter/services.dart'; From e121b784f0037bdc2808a2c2873be61dd9aca729 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 28 Jun 2022 17:13:11 -0400 Subject: [PATCH 465/844] [google_maps_flutter] Temporary fix for initial coordinate when the surface is changed (#6054) --- .../example/integration_test/google_maps_test.dart | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart index 35351505e495..9246466d00cf 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart @@ -426,7 +426,12 @@ void main() { }); testWidgets('testInitialCenterLocationAtCenter', (WidgetTester tester) async { - await tester.binding.setSurfaceSize(const Size(800.0, 600.0)); + // TODO(bparrishMines): Remove this line when resizing virtual displays doesn't + // clamp displays that are smaller than the screen. + // See https://github.com/flutter/flutter/issues/106750 + AndroidGoogleMapsFlutter.useAndroidViewSurface = true; + await tester.binding.setSurfaceSize(const Size(800, 600)); + final Completer mapControllerCompleter = Completer(); final Key key = GlobalKey(); @@ -473,6 +478,7 @@ void main() { .round()); } await tester.binding.setSurfaceSize(null); + AndroidGoogleMapsFlutter.useAndroidViewSurface = false; }); testWidgets('testGetVisibleRegion', (WidgetTester tester) async { From dd829af02c22cece7fc6f6abb6794c8683832b84 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 28 Jun 2022 18:49:11 -0400 Subject: [PATCH 466/844] Roll Flutter from 6c3a0e4d21da to baf86869b14a (51 revisions) (#6055) --- .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 6ed90983369e..228385e182d9 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -6c3a0e4d21da2478a58d8b00fbabfa29ef0df045 +baf86869b14a2a944f015ec20763719b5f911955 From 670ade44963f9ba307a258331419dc42ff5ca91f Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 29 Jun 2022 12:04:14 -0400 Subject: [PATCH 467/844] Roll Flutter from baf86869b14a to 3b11ad8cfe78 (13 revisions) (#6058) --- .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 228385e182d9..0bd894f72058 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -baf86869b14a2a944f015ec20763719b5f911955 +3b11ad8cfe78af71f4cea83cc7f894a3928b0d06 From 7233295fd2631050b9b1b8e27d08ae63b61d7026 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 30 Jun 2022 14:44:04 -0400 Subject: [PATCH 468/844] Roll Flutter from 3b11ad8cfe78 to 6c6ae063814c (28 revisions) (#6060) --- .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 0bd894f72058..2010efc2ac57 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -3b11ad8cfe78af71f4cea83cc7f894a3928b0d06 +6c6ae063814c8bb3b655eba21411053b71d13b42 From b5e4ad208a0651cd19e5162619a3f958ddf18cff Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 30 Jun 2022 19:34:08 -0400 Subject: [PATCH 469/844] [tools] Allow skipping packages by Dart version (#6038) --- script/tool/CHANGELOG.md | 5 ++- .../src/common/package_looping_command.dart | 23 ++++++++++- script/tool/pubspec.yaml | 2 +- .../common/package_looping_command_test.dart | 33 ++++++++++++++- script/tool/test/util.dart | 41 ++++++++++++------- 5 files changed, 86 insertions(+), 18 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 07cafee7f8b6..9606eed20b27 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,9 +1,12 @@ -## NEXT +## 0.8.7 - Supports empty custom analysis allow list files. - `drive-examples` now validates files to ensure that they don't accidentally use `test(...)`. - Adds a new `dependabot-check` command to ensure complete Dependabot coverage. +- Adds `skip-if-not-supporting-dart-version` to allow for the same use cases + as `skip-if-not-supporting-flutter-version` but for packages without Flutter + constraints. ## 0.8.6 diff --git a/script/tool/lib/src/common/package_looping_command.dart b/script/tool/lib/src/common/package_looping_command.dart index a295215f3628..1a194bd45b9e 100644 --- a/script/tool/lib/src/common/package_looping_command.dart +++ b/script/tool/lib/src/common/package_looping_command.dart @@ -96,10 +96,17 @@ abstract class PackageLoopingCommand extends PluginCommand { help: 'Skip any packages that require a Flutter version newer than ' 'the provided version.', ); + argParser.addOption( + _skipByDartVersionArg, + help: 'Skip any packages that require a Dart version newer than ' + 'the provided version.', + ); } static const String _skipByFlutterVersionArg = 'skip-if-not-supporting-flutter-version'; + static const String _skipByDartVersionArg = + 'skip-if-not-supporting-dart-version'; /// Packages that had at least one [logWarning] call. final Set _packagesWithWarnings = @@ -264,6 +271,9 @@ abstract class PackageLoopingCommand extends PluginCommand { final Version? minFlutterVersion = minFlutterVersionArg.isEmpty ? null : Version.parse(minFlutterVersionArg); + final String minDartVersionArg = getStringArg(_skipByDartVersionArg); + final Version? minDartVersion = + minDartVersionArg.isEmpty ? null : Version.parse(minDartVersionArg); final DateTime runStart = DateTime.now(); @@ -289,7 +299,8 @@ abstract class PackageLoopingCommand extends PluginCommand { PackageResult result; try { result = await _runForPackageIfSupported(entry.package, - minFlutterVersion: minFlutterVersion); + minFlutterVersion: minFlutterVersion, + minDartVersion: minDartVersion); } catch (e, stack) { printError(e.toString()); printError(stack.toString()); @@ -337,6 +348,7 @@ abstract class PackageLoopingCommand extends PluginCommand { Future _runForPackageIfSupported( RepositoryPackage package, { Version? minFlutterVersion, + Version? minDartVersion, }) async { if (minFlutterVersion != null) { final Pubspec pubspec = package.parsePubspec(); @@ -349,6 +361,15 @@ abstract class PackageLoopingCommand extends PluginCommand { } } + if (minDartVersion != null) { + final Pubspec pubspec = package.parsePubspec(); + final VersionConstraint? dartConstraint = pubspec.environment?['sdk']; + if (dartConstraint != null && !dartConstraint.allows(minDartVersion)) { + return PackageResult.skip( + 'Does not support Dart ${minDartVersion.toString()}'); + } + } + return await runForPackage(package); } diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 138c1183fa1c..1e631b1c0a17 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.8.6 +version: 0.8.7 dependencies: args: ^2.1.0 diff --git a/script/tool/test/common/package_looping_command_test.dart b/script/tool/test/common/package_looping_command_test.dart index a7e7dfdf6ebb..ec2b9b9be232 100644 --- a/script/tool/test/common/package_looping_command_test.dart +++ b/script/tool/test/common/package_looping_command_test.dart @@ -340,7 +340,7 @@ void main() { } }); - test('skips unsupported versions when requested', () async { + test('skips unsupported Flutter versions when requested', () async { final RepositoryPackage excluded = createFakePlugin( 'a_plugin', packagesDir, flutterConstraint: '>=2.10.0'); @@ -370,6 +370,37 @@ void main() { '$_startSkipColor SKIPPING: Does not support Flutter 2.5.0$_endColor', ])); }); + + test('skips unsupported Dart versions when requested', () async { + final RepositoryPackage excluded = createFakePackage( + 'excluded_package', packagesDir, + isFlutter: false, dartConstraint: '>=2.17.0 <3.0.0'); + final RepositoryPackage included = createFakePackage( + 'a_package', packagesDir, + isFlutter: false, dartConstraint: '>=2.14.0 <3.0.0'); + + final TestPackageLoopingCommand command = createTestCommand( + packageLoopingType: PackageLoopingType.includeAllSubpackages, + hasLongOutput: false); + final List output = await runCommand(command, + arguments: ['--skip-if-not-supporting-dart-version=2.14.0']); + + expect( + command.checkedPackages, + unorderedEquals([ + included.path, + getExampleDir(included).path, + ])); + expect(command.checkedPackages, isNot(contains(excluded.path))); + + expect( + output, + containsAllInOrder([ + '${_startHeadingColor}Running for a_package...$_endColor', + '${_startHeadingColor}Running for excluded_package...$_endColor', + '$_startSkipColor SKIPPING: Does not support Dart 2.14.0$_endColor', + ])); + }); }); group('output', () { diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index 4c99873e937d..041d93367f9f 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -23,6 +23,9 @@ import 'mocks.dart'; export 'package:flutter_plugin_tools/src/common/repository_package.dart'; +const String _defaultDartConstraint = '>=2.14.0 <3.0.0'; +const String _defaultFlutterConstraint = '>=2.5.0'; + /// Returns the exe name that command will use when running Flutter on /// [platform]. String getFlutterCommand(Platform platform) => @@ -97,14 +100,19 @@ RepositoryPackage createFakePlugin( Map platformSupport = const {}, String? version = '0.0.1', - String flutterConstraint = '>=2.5.0', + String flutterConstraint = _defaultFlutterConstraint, + String dartConstraint = _defaultDartConstraint, }) { - final RepositoryPackage package = createFakePackage(name, parentDirectory, - isFlutter: true, - examples: examples, - extraFiles: extraFiles, - version: version, - flutterConstraint: flutterConstraint); + final RepositoryPackage package = createFakePackage( + name, + parentDirectory, + isFlutter: true, + examples: examples, + extraFiles: extraFiles, + version: version, + flutterConstraint: flutterConstraint, + dartConstraint: dartConstraint, + ); createFakePubspec( package, @@ -114,6 +122,7 @@ RepositoryPackage createFakePlugin( platformSupport: platformSupport, version: version, flutterConstraint: flutterConstraint, + dartConstraint: dartConstraint, ); return package; @@ -136,7 +145,8 @@ RepositoryPackage createFakePackage( List extraFiles = const [], bool isFlutter = false, String? version = '0.0.1', - String flutterConstraint = '>=2.5.0', + String flutterConstraint = _defaultFlutterConstraint, + String dartConstraint = _defaultDartConstraint, bool includeCommonFiles = true, String? directoryName, String? publishTo, @@ -150,7 +160,8 @@ RepositoryPackage createFakePackage( name: name, isFlutter: isFlutter, version: version, - flutterConstraint: flutterConstraint); + flutterConstraint: flutterConstraint, + dartConstraint: dartConstraint); if (includeCommonFiles) { package.changelogFile.writeAsStringSync(''' ## $version @@ -167,7 +178,8 @@ RepositoryPackage createFakePackage( includeCommonFiles: false, isFlutter: isFlutter, publishTo: 'none', - flutterConstraint: flutterConstraint); + flutterConstraint: flutterConstraint, + dartConstraint: dartConstraint); } else if (examples.isNotEmpty) { final Directory examplesDirectory = getExampleDir(package)..createSync(); for (final String exampleName in examples) { @@ -176,7 +188,8 @@ RepositoryPackage createFakePackage( includeCommonFiles: false, isFlutter: isFlutter, publishTo: 'none', - flutterConstraint: flutterConstraint); + flutterConstraint: flutterConstraint, + dartConstraint: dartConstraint); } } @@ -189,7 +202,7 @@ RepositoryPackage createFakePackage( return package; } -/// Creates a `pubspec.yaml` file with a flutter dependency. +/// Creates a `pubspec.yaml` file for [package]. /// /// [platformSupport] is a map of platform string to the support details for /// that platform. If empty, no `plugin` entry will be created unless `isPlugin` @@ -203,8 +216,8 @@ void createFakePubspec( const {}, String? publishTo, String? version, - String dartConstraint = '>=2.0.0 <3.0.0', - String flutterConstraint = '>=2.5.0', + String dartConstraint = _defaultDartConstraint, + String flutterConstraint = _defaultFlutterConstraint, }) { isPlugin |= platformSupport.isNotEmpty; From df2dff6e0ae9687394d28fc89608965c57acd5ac Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 1 Jul 2022 12:04:09 -0400 Subject: [PATCH 470/844] Roll Flutter from 6c6ae063814c to 1add0d790d78 (16 revisions) (#6064) --- .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 2010efc2ac57..e370b1335677 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -6c6ae063814c8bb3b655eba21411053b71d13b42 +1add0d790d78918a560387c23802afd2979d15d7 From eb33778bada2d2f29169c69f488b31baa55088c7 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 1 Jul 2022 14:54:04 -0400 Subject: [PATCH 471/844] [tools] Allow pre-release versions (#6061) --- script/tool/CHANGELOG.md | 4 + .../tool/lib/src/version_check_command.dart | 51 +++-- script/tool/pubspec.yaml | 2 +- .../tool/test/version_check_command_test.dart | 180 ++++++++++++++++++ 4 files changed, 224 insertions(+), 13 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 9606eed20b27..da14eae285e3 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.8 + +- Allows pre-release versions in `version-check`. + ## 0.8.7 - Supports empty custom analysis allow list files. diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart index 62abdb2a432b..246382dfae00 100644 --- a/script/tool/lib/src/version_check_command.dart +++ b/script/tool/lib/src/version_check_command.dart @@ -31,8 +31,8 @@ enum NextVersionType { /// A bugfix change. PATCH, - /// The release of an existing prerelease version. - RELEASE, + /// The release of an existing pre-1.0 version. + V1_RELEASE, } /// The state of a package's version relative to the comparison base. @@ -53,8 +53,8 @@ enum _CurrentVersionState { unknown, } -/// Returns the set of allowed next versions, with their change type, for -/// [version]. +/// Returns the set of allowed next non-prerelease versions, with their change +/// type, for [version]. /// /// [newVersion] is used to check whether this is a pre-1.0 version bump, as /// those have different semver rules. @@ -78,17 +78,17 @@ Map getAllowedNextVersions( final int currentBuildNumber = version.build.first as int; nextBuildNumber = currentBuildNumber + 1; } - final Version preReleaseVersion = Version( + final Version nextBuildVersion = Version( version.major, version.minor, version.patch, build: nextBuildNumber.toString(), ); allowedNextVersions.clear(); - allowedNextVersions[version.nextMajor] = NextVersionType.RELEASE; + allowedNextVersions[version.nextMajor] = NextVersionType.V1_RELEASE; allowedNextVersions[version.nextMinor] = NextVersionType.BREAKING_MAJOR; allowedNextVersions[version.nextPatch] = NextVersionType.MINOR; - allowedNextVersions[preReleaseVersion] = NextVersionType.PATCH; + allowedNextVersions[nextBuildVersion] = NextVersionType.PATCH; } return allowedNextVersions; } @@ -337,12 +337,11 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} // Check for reverts when doing local validation. if (!getBoolArg(_againstPubFlag) && currentVersion < previousVersion) { - final Map possibleVersionsFromNewVersion = - getAllowedNextVersions(currentVersion, newVersion: previousVersion); // Since this skips validation, try to ensure that it really is likely // to be a revert rather than a typo by checking that the transition // from the lower version to the new version would have been valid. - if (possibleVersionsFromNewVersion.containsKey(previousVersion)) { + if (_shouldAllowVersionChange( + oldVersion: currentVersion, newVersion: previousVersion)) { logWarning('${indentation}New version is lower than previous version. ' 'This is assumed to be a revert.'); return _CurrentVersionState.validRevert; @@ -352,7 +351,8 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} final Map allowedNextVersions = getAllowedNextVersions(previousVersion, newVersion: currentVersion); - if (allowedNextVersions.containsKey(currentVersion)) { + if (_shouldAllowVersionChange( + oldVersion: previousVersion, newVersion: currentVersion)) { print('$indentation$previousVersion -> $currentVersion'); } else { printError('${indentation}Incorrectly updated version.\n' @@ -361,7 +361,13 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} return _CurrentVersionState.invalidChange; } - if (allowedNextVersions[currentVersion] == NextVersionType.BREAKING_MAJOR && + // Check whether the version (or for a pre-release, the version that + // pre-release would eventually be released as) is a breaking change, and + // if so, validate it. + final Version targetReleaseVersion = + currentVersion.isPreRelease ? currentVersion.nextPatch : currentVersion; + if (allowedNextVersions[targetReleaseVersion] == + NextVersionType.BREAKING_MAJOR && !_validateBreakingChange(package)) { printError('${indentation}Breaking change detected.\n' '${indentation}Breaking changes to platform interfaces are not ' @@ -520,6 +526,27 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. return file.readAsStringSync(); } + /// Returns true if the given version transition should be allowed. + bool _shouldAllowVersionChange( + {required Version oldVersion, required Version newVersion}) { + // Get the non-pre-release next version mapping. + final Map allowedNextVersions = + getAllowedNextVersions(oldVersion, newVersion: newVersion); + + if (allowedNextVersions.containsKey(newVersion)) { + return true; + } + // Allow a pre-release version of a version that would be a valid + // transition. + if (newVersion.isPreRelease) { + final Version targetReleaseVersion = newVersion.nextPatch; + if (allowedNextVersions.containsKey(targetReleaseVersion)) { + return true; + } + } + return false; + } + /// Returns an error string if the changes to this package should have /// resulted in a version change, or shoud have resulted in a CHANGELOG change /// but didn't. diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 1e631b1c0a17..b8233de11b41 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.8.7 +version: 0.8.8 dependencies: args: ^2.1.0 diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart index a310f0f09fcb..8f8d510fd106 100644 --- a/script/tool/test/version_check_command_test.dart +++ b/script/tool/test/version_check_command_test.dart @@ -1178,6 +1178,186 @@ ${indentation}HTTP response: null ]), ); }); + + group('prelease versions', () { + test( + 'allow an otherwise-valid transition that also adds a pre-release component', + () async { + createFakePlugin('plugin', packagesDir, version: '2.0.0-dev'); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + final List output = await runCapturingPrint( + runner, ['version-check', '--base-sha=main']); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin'), + contains('1.0.0 -> 2.0.0-dev'), + ]), + ); + expect( + processRunner.recordedCalls, + containsAllInOrder(const [ + ProcessCall('git-show', + ['main:packages/plugin/pubspec.yaml'], null) + ])); + }); + + test('allow releasing a pre-release', () async { + createFakePlugin('plugin', packagesDir, version: '1.2.0'); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.2.0-dev'), + ]; + final List output = await runCapturingPrint( + runner, ['version-check', '--base-sha=main']); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin'), + contains('1.2.0-dev -> 1.2.0'), + ]), + ); + expect( + processRunner.recordedCalls, + containsAllInOrder(const [ + ProcessCall('git-show', + ['main:packages/plugin/pubspec.yaml'], null) + ])); + }); + + // Allow abandoning a pre-release version in favor of a different version + // change type. + test( + 'allow an otherwise-valid transition that also removes a pre-release component', + () async { + createFakePlugin('plugin', packagesDir, version: '2.0.0'); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.2.0-dev'), + ]; + final List output = await runCapturingPrint( + runner, ['version-check', '--base-sha=main']); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin'), + contains('1.2.0-dev -> 2.0.0'), + ]), + ); + expect( + processRunner.recordedCalls, + containsAllInOrder(const [ + ProcessCall('git-show', + ['main:packages/plugin/pubspec.yaml'], null) + ])); + }); + + test('allow changing only the pre-release version', () async { + createFakePlugin('plugin', packagesDir, version: '1.2.0-dev.2'); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.2.0-dev.1'), + ]; + final List output = await runCapturingPrint( + runner, ['version-check', '--base-sha=main']); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin'), + contains('1.2.0-dev.1 -> 1.2.0-dev.2'), + ]), + ); + expect( + processRunner.recordedCalls, + containsAllInOrder(const [ + ProcessCall('git-show', + ['main:packages/plugin/pubspec.yaml'], null) + ])); + }); + + test('denies invalid version change that also adds a pre-release', + () async { + createFakePlugin('plugin', packagesDir, version: '0.2.0-dev'); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 0.0.1'), + ]; + Error? commandError; + final List output = await runCapturingPrint( + runner, ['version-check', '--base-sha=main'], + errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Incorrectly updated version.'), + ])); + expect( + processRunner.recordedCalls, + containsAllInOrder(const [ + ProcessCall('git-show', + ['main:packages/plugin/pubspec.yaml'], null) + ])); + }); + + test('denies invalid version change that also removes a pre-release', + () async { + createFakePlugin('plugin', packagesDir, version: '0.2.0'); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 0.0.1-dev'), + ]; + Error? commandError; + final List output = await runCapturingPrint( + runner, ['version-check', '--base-sha=main'], + errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Incorrectly updated version.'), + ])); + expect( + processRunner.recordedCalls, + containsAllInOrder(const [ + ProcessCall('git-show', + ['main:packages/plugin/pubspec.yaml'], null) + ])); + }); + + test('denies invalid version change between pre-releases', () async { + createFakePlugin('plugin', packagesDir, version: '0.2.0-dev'); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 0.0.1-dev'), + ]; + Error? commandError; + final List output = await runCapturingPrint( + runner, ['version-check', '--base-sha=main'], + errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Incorrectly updated version.'), + ])); + expect( + processRunner.recordedCalls, + containsAllInOrder(const [ + ProcessCall('git-show', + ['main:packages/plugin/pubspec.yaml'], null) + ])); + }); + }); }); group('Pre 1.0', () { From b66987935b83eca62832621f7acafe4ff9154556 Mon Sep 17 00:00:00 2001 From: Ricardo Amador <32242716+ricardoamador@users.noreply.github.com> Date: Fri, 1 Jul 2022 14:57:05 -0700 Subject: [PATCH 472/844] testing autosubmit (#6062) From eae84280f397b6033241e89b891be065062e089e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 2 Jul 2022 12:04:07 -0400 Subject: [PATCH 473/844] Roll Flutter from 1add0d790d78 to efd006e1b925 (15 revisions) (#6065) --- .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 e370b1335677..354f51e290c9 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1add0d790d78918a560387c23802afd2979d15d7 +efd006e1b925f50a66d5941b4f811966694baab0 From 509a6c920461694c899cd6e39939ae8d9ddbb1d0 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 3 Jul 2022 12:04:06 -0400 Subject: [PATCH 474/844] Roll Flutter from efd006e1b925 to 629f731b99c0 (6 revisions) (#6068) --- .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 354f51e290c9..504a5eed8092 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -efd006e1b925f50a66d5941b4f811966694baab0 +629f731b99c0c1d013d6ff21d4002968ef7bb355 From a0c773b3c9e8eb995b46d0359e8063bacfdc84e1 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 5 Jul 2022 21:04:07 -0400 Subject: [PATCH 475/844] Roll Flutter from 629f731b99c0 to 39a38b7882fe (16 revisions) (#6076) --- .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 504a5eed8092..d7869e03c983 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -629f731b99c0c1d013d6ff21d4002968ef7bb355 +39a38b7882fe40436176ebc97e554be11141cc7d From 3a2cd24a4a89c8468834f2bdc9a367e494a86b80 Mon Sep 17 00:00:00 2001 From: Flafy Date: Wed, 6 Jul 2022 17:55:06 +0300 Subject: [PATCH 476/844] [webview_flutter] fix: unreliable encoding for web (#5737) --- .../webview_flutter_web/CHANGELOG.md | 4 + .../lib/webview_flutter_web.dart | 14 +++- .../webview_flutter_web/pubspec.yaml | 2 +- .../test/webview_flutter_web_test.dart | 74 +++++++++++++++---- 4 files changed, 77 insertions(+), 17 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index 8ab70f9a78d3..631608689a7a 100644 --- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.0+4 + +* Fixes incorrect escaping of some characters when setting the HTML to the iframe element. + ## 0.1.0+3 * Minor fixes for new analysis options. diff --git a/packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart b/packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart index 637c24926275..adf6495b8f2a 100644 --- a/packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart +++ b/packages/webview_flutter/webview_flutter_web/lib/webview_flutter_web.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:convert'; import 'dart:html'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; @@ -183,7 +184,11 @@ class WebWebViewPlatformController implements WebViewPlatformController { String? baseUrl, }) async { // ignore: unsafe_html - _element.src = 'data:text/html,${Uri.encodeFull(html)}'; + _element.src = Uri.dataFromString( + html, + mimeType: 'text/html', + encoding: utf8, + ).toString(); } @override @@ -199,8 +204,11 @@ class WebWebViewPlatformController implements WebViewPlatformController { final String contentType = httpReq.getResponseHeader('content-type') ?? 'text/html'; // ignore: unsafe_html - _element.src = - 'data:$contentType,${Uri.encodeFull(httpReq.responseText ?? '')}'; + _element.src = Uri.dataFromString( + httpReq.responseText ?? '', + mimeType: contentType, + encoding: utf8, + ).toString(); } @override diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml index 6464e20fe37c..12bec7242519 100644 --- a/packages/webview_flutter/webview_flutter_web/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_web description: A Flutter plugin that provides a WebView widget on web. repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 0.1.0+3 +version: 0.1.0+4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.dart b/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.dart index 6058dcf07272..08337e42e661 100644 --- a/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.dart +++ b/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.dart @@ -54,17 +54,33 @@ void main() { verify(mockElement.src = 'test url'); }); - test('loadHtmlString loads html into iframe', () { - // Setup - final MockIFrameElement mockElement = MockIFrameElement(); - final WebWebViewPlatformController controller = - WebWebViewPlatformController( - mockElement, - ); - // Run - controller.loadHtmlString('test html'); - // Verify - verify(mockElement.src = 'data:text/html,${Uri.encodeFull('test html')}'); + group('loadHtmlString', () { + test('loadHtmlString loads html into iframe', () { + // Setup + final MockIFrameElement mockElement = MockIFrameElement(); + final WebWebViewPlatformController controller = + WebWebViewPlatformController( + mockElement, + ); + // Run + controller.loadHtmlString('test html'); + // Verify + verify(mockElement.src = + 'data:text/html;charset=utf-8,${Uri.encodeFull('test html')}'); + }); + + test('loadHtmlString escapes "#" correctly', () { + // Setup + final MockIFrameElement mockElement = MockIFrameElement(); + final WebWebViewPlatformController controller = + WebWebViewPlatformController( + mockElement, + ); + // Run + controller.loadHtmlString('#'); + // Verify + verify(mockElement.src = argThat(contains('%23'))); + }); }); group('loadRequest', () { @@ -122,8 +138,40 @@ void main() { requestHeaders: {'Foo': 'Bar'}, sendData: Uint8List.fromList('test body'.codeUnits), )); - verify( - mockElement.src = 'data:text/plain,${Uri.encodeFull('test data')}'); + verify(mockElement.src = + 'data:;charset=utf-8,${Uri.encodeFull('test data')}'); + }); + + test('loadRequest escapes "#" correctly', () async { + // Setup + final MockIFrameElement mockElement = MockIFrameElement(); + final WebWebViewPlatformController controller = + WebWebViewPlatformController( + mockElement, + ); + final MockHttpRequest mockHttpRequest = MockHttpRequest(); + when(mockHttpRequest.getResponseHeader('content-type')) + .thenReturn('text/html'); + when(mockHttpRequest.responseText).thenReturn('#'); + final MockHttpRequestFactory mockHttpRequestFactory = + MockHttpRequestFactory(); + when(mockHttpRequestFactory.request( + any, + method: anyNamed('method'), + requestHeaders: anyNamed('requestHeaders'), + sendData: anyNamed('sendData'), + )).thenAnswer((_) => Future.value(mockHttpRequest)); + controller.httpRequestFactory = mockHttpRequestFactory; + // Run + await controller.loadRequest( + WebViewRequest( + uri: Uri.parse('https://flutter.dev'), + method: WebViewRequestMethod.post, + body: Uint8List.fromList('test body'.codeUnits), + headers: {'Foo': 'Bar'}), + ); + // Verify + verify(mockElement.src = argThat(contains('%23'))); }); }); }); From 1787fa36c0c3a98704cc9a9878131fb68c641583 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 6 Jul 2022 13:09:10 -0400 Subject: [PATCH 477/844] Roll Flutter from 39a38b7882fe to 587cf5fa715f (5 revisions) (#6078) --- .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 d7869e03c983..566ce0197354 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -39a38b7882fe40436176ebc97e554be11141cc7d +587cf5fa715fffd95df9a224453f265482bb26f5 From c8c6d9d375b8d0564795dd5c89be781795247688 Mon Sep 17 00:00:00 2001 From: Alex Sandri Date: Thu, 7 Jul 2022 17:56:04 +0200 Subject: [PATCH 478/844] [image_picker_ios] fix wrong plugin name (#6072) --- packages/image_picker/image_picker_ios/CHANGELOG.md | 3 ++- packages/image_picker/image_picker_ios/pubspec.yaml | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index 978046075a54..ecaaf773ece5 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.8.5+6 +* Updates description. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). ## 0.8.5+5 diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index e1bccad60ea4..314a52e94510 100755 --- a/packages/image_picker/image_picker_ios/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -1,8 +1,8 @@ name: image_picker_ios -description: iOS implementation of the video_picker plugin. +description: iOS implementation of the image_picker plugin. repository: https://github.com/flutter/plugins/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.5+5 +version: 0.8.5+6 environment: sdk: ">=2.14.0 <3.0.0" From 1be7a6a9379cb81f67fbaae627e6ce3e2e7cdb63 Mon Sep 17 00:00:00 2001 From: Gabriel Terwesten Date: Thu, 7 Jul 2022 17:58:05 +0200 Subject: [PATCH 479/844] [google_sign_in] Implement Dart-based configuration and `serverClientId` (#6034) --- .../google_sign_in_android/CHANGELOG.md | 11 +- .../googlesignin/GoogleSignInPlugin.java | 57 +++-- .../googlesignin/GoogleSignInWrapper.java | 6 + .../googlesignin/GoogleSignInTest.java | 72 ++++++ .../example/lib/main.dart | 10 +- .../example/pubspec.yaml | 2 +- .../google_sign_in_android/pubspec.yaml | 4 +- .../google_sign_in_ios/CHANGELOG.md | 7 +- .../ios/RunnerTests/GoogleSignInTests.m | 57 +++++ .../google_sign_in_ios/example/lib/main.dart | 13 +- .../google_sign_in_ios/example/pubspec.yaml | 2 +- .../ios/Classes/FLTGoogleSignInPlugin.m | 46 +++- .../ios/Classes/FLTGoogleSignInPlugin_Test.h | 11 +- .../google_sign_in_ios/pubspec.yaml | 4 +- .../google_sign_in_web/CHANGELOG.md | 5 + .../google_sign_in_web/README.md | 3 + .../auth2_legacy_init_test.dart | 223 ++++++++++++++++++ .../example/integration_test/auth2_test.dart | 60 +++-- .../gapi_load_legacy_init_test.dart | 51 ++++ .../integration_test/gapi_load_test.dart | 9 +- .../lib/google_sign_in_web.dart | 35 ++- .../google_sign_in_web/pubspec.yaml | 4 +- 22 files changed, 609 insertions(+), 83 deletions(-) create mode 100644 packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_legacy_init_test.dart create mode 100644 packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_load_legacy_init_test.dart 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 67fb08f66e17..a91f0c0351c4 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,3 +1,12 @@ +## 6.0.0 + +* Deprecates `clientId` and adds support for `serverClientId` instead. + Historically `clientId` was interpreted as `serverClientId`, but only on Android. On + other platforms it was interpreted as the OAuth `clientId` of the app. For backwards-compatibility + `clientId` will still be used as a server client ID if `serverClientId` is not provided. +* **BREAKING CHANGES**: + * Adds `serverClientId` parameter to `IDelegate.init` (Java). + ## 5.2.8 * Suppresses `deprecation` warnings (for using Android V1 embedding). @@ -13,4 +22,4 @@ ## 5.2.5 -* Splits from `video_player` as a federated implementation. +* Splits from `google_sign_in` as a federated implementation. diff --git a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java index 9bee8fad38d3..21640233f210 100644 --- a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java +++ b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java @@ -8,6 +8,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.google.android.gms.auth.GoogleAuthUtil; @@ -138,7 +139,9 @@ public void onMethodCall(MethodCall call, Result result) { List requestedScopes = call.argument("scopes"); String hostedDomain = call.argument("hostedDomain"); String clientId = call.argument("clientId"); - delegate.init(result, signInOption, requestedScopes, hostedDomain, clientId); + String serverClientId = call.argument("serverClientId"); + delegate.init( + result, signInOption, requestedScopes, hostedDomain, clientId, serverClientId); break; case METHOD_SIGN_IN_SILENTLY: @@ -194,7 +197,8 @@ public void init( String signInOption, List requestedScopes, String hostedDomain, - String clientId); + String clientId, + String serverClientId); /** * Returns the account information for the user who is signed in to this app. If no user is @@ -321,7 +325,8 @@ public void init( String signInOption, List requestedScopes, String hostedDomain, - String clientId) { + String clientId, + String serverClientId) { try { GoogleSignInOptions.Builder optionsBuilder; @@ -338,20 +343,38 @@ public void init( throw new IllegalStateException("Unknown signInOption"); } - // Only requests a clientId if google-services.json was present and parsed - // by the google-services Gradle script. - // TODO(jackson): Perhaps we should provide a mechanism to override this - // behavior. - int clientIdIdentifier = - context - .getResources() - .getIdentifier("default_web_client_id", "string", context.getPackageName()); + // The clientId parameter is not supported on Android. + // Android apps are identified by their package name and the SHA-1 of their signing key. + // https://developers.google.com/android/guides/client-auth + // https://developers.google.com/identity/sign-in/android/start#configure-a-google-api-project if (!Strings.isNullOrEmpty(clientId)) { - optionsBuilder.requestIdToken(clientId); - optionsBuilder.requestServerAuthCode(clientId); - } else if (clientIdIdentifier != 0) { - optionsBuilder.requestIdToken(context.getString(clientIdIdentifier)); - optionsBuilder.requestServerAuthCode(context.getString(clientIdIdentifier)); + if (Strings.isNullOrEmpty(serverClientId)) { + Log.w( + "google_sing_in", + "clientId is not supported on Android and is interpreted as serverClientId." + + "Use serverClientId instead to suppress this warning."); + serverClientId = clientId; + } else { + Log.w("google_sing_in", "clientId is not supported on Android and is ignored."); + } + } + + if (Strings.isNullOrEmpty(serverClientId)) { + // Only requests a clientId if google-services.json was present and parsed + // by the google-services Gradle script. + // TODO(jackson): Perhaps we should provide a mechanism to override this + // behavior. + int webClientIdIdentifier = + context + .getResources() + .getIdentifier("default_web_client_id", "string", context.getPackageName()); + if (webClientIdIdentifier != 0) { + serverClientId = context.getString(webClientIdIdentifier); + } + } + if (!Strings.isNullOrEmpty(serverClientId)) { + optionsBuilder.requestIdToken(serverClientId); + optionsBuilder.requestServerAuthCode(serverClientId); } for (String scope : requestedScopes) { optionsBuilder.requestScopes(new Scope(scope)); @@ -361,7 +384,7 @@ public void init( } this.requestedScopes = requestedScopes; - signInClient = GoogleSignIn.getClient(context, optionsBuilder.build()); + signInClient = googleSignInWrapper.getClient(context, optionsBuilder.build()); result.success(null); } catch (Exception e) { result.error(ERROR_REASON_EXCEPTION, e.getMessage(), null); diff --git a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInWrapper.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInWrapper.java index 5af0b50136ce..c035329f8e96 100644 --- a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInWrapper.java +++ b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInWrapper.java @@ -8,6 +8,8 @@ import android.content.Context; import com.google.android.gms.auth.api.signin.GoogleSignIn; import com.google.android.gms.auth.api.signin.GoogleSignInAccount; +import com.google.android.gms.auth.api.signin.GoogleSignInClient; +import com.google.android.gms.auth.api.signin.GoogleSignInOptions; import com.google.android.gms.common.api.Scope; /** @@ -21,6 +23,10 @@ */ public class GoogleSignInWrapper { + GoogleSignInClient getClient(Context context, GoogleSignInOptions options) { + return GoogleSignIn.getClient(context, options); + } + GoogleSignInAccount getLastSignedInAccount(Context context) { return GoogleSignIn.getLastSignedInAccount(context); } diff --git a/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java b/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java index 3b6ad960f548..11f8cda2e9b1 100644 --- a/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java +++ b/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java @@ -4,6 +4,7 @@ package io.flutter.plugins.googlesignin; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -12,7 +13,10 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.content.res.Resources; import com.google.android.gms.auth.api.signin.GoogleSignInAccount; +import com.google.android.gms.auth.api.signin.GoogleSignInClient; +import com.google.android.gms.auth.api.signin.GoogleSignInOptions; import com.google.android.gms.common.api.Scope; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MethodCall; @@ -21,6 +25,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -30,12 +35,14 @@ public class GoogleSignInTest { @Mock Context mockContext; + @Mock Resources mockResources; @Mock Activity mockActivity; @Mock PluginRegistry.Registrar mockRegistrar; @Mock BinaryMessenger mockMessenger; @Spy MethodChannel.Result result; @Mock GoogleSignInWrapper mockGoogleSignIn; @Mock GoogleSignInAccount account; + @Mock GoogleSignInClient mockClient; private GoogleSignInPlugin plugin; @Before @@ -44,6 +51,7 @@ public void setUp() { when(mockRegistrar.messenger()).thenReturn(mockMessenger); when(mockRegistrar.context()).thenReturn(mockContext); when(mockRegistrar.activity()).thenReturn(mockActivity); + when(mockContext.getResources()).thenReturn(mockResources); plugin = new GoogleSignInPlugin(); plugin.initInstance(mockRegistrar.messenger(), mockRegistrar.context(), mockGoogleSignIn); plugin.setUpRegistrar(mockRegistrar); @@ -195,4 +203,68 @@ public void signInThrowsWithoutActivity() { plugin.onMethodCall(new MethodCall("signIn", null), null); } + + @Test + public void init_LoadsServerClientIdFromResources() { + final String packageName = "fakePackageName"; + final String serverClientId = "fakeServerClientId"; + final int resourceId = 1; + MethodCall methodCall = buildInitMethodCall(null, null); + when(mockContext.getPackageName()).thenReturn(packageName); + when(mockResources.getIdentifier("default_web_client_id", "string", packageName)) + .thenReturn(resourceId); + when(mockContext.getString(resourceId)).thenReturn(serverClientId); + initAndAssertServerClientId(methodCall, serverClientId); + } + + @Test + public void init_InterpretsClientIdAsServerClientId() { + final String clientId = "fakeClientId"; + MethodCall methodCall = buildInitMethodCall(clientId, null); + initAndAssertServerClientId(methodCall, clientId); + } + + @Test + public void init_ForwardsServerClientId() { + final String serverClientId = "fakeServerClientId"; + MethodCall methodCall = buildInitMethodCall(null, serverClientId); + initAndAssertServerClientId(methodCall, serverClientId); + } + + @Test + public void init_IgnoresClientIdIfServerClientIdIsProvided() { + final String clientId = "fakeClientId"; + final String serverClientId = "fakeServerClientId"; + MethodCall methodCall = buildInitMethodCall(clientId, serverClientId); + initAndAssertServerClientId(methodCall, serverClientId); + } + + public void initAndAssertServerClientId(MethodCall methodCall, String serverClientId) { + ArgumentCaptor optionsCaptor = + ArgumentCaptor.forClass(GoogleSignInOptions.class); + when(mockGoogleSignIn.getClient(any(Context.class), optionsCaptor.capture())) + .thenReturn(mockClient); + plugin.onMethodCall(methodCall, result); + verify(result).success(null); + Assert.assertEquals(serverClientId, optionsCaptor.getValue().getServerClientId()); + } + + private static MethodCall buildInitMethodCall(String clientId, String serverClientId) { + return buildInitMethodCall( + "SignInOption.standard", Collections.emptyList(), clientId, serverClientId); + } + + private static MethodCall buildInitMethodCall( + String signInOption, List scopes, String clientId, String serverClientId) { + HashMap arguments = new HashMap<>(); + arguments.put("signInOption", signInOption); + arguments.put("scopes", scopes); + if (clientId != null) { + arguments.put("clientId", clientId); + } + if (serverClientId != null) { + arguments.put("serverClientId", serverClientId); + } + return new MethodCall("init", arguments); + } } diff --git a/packages/google_sign_in/google_sign_in_android/example/lib/main.dart b/packages/google_sign_in/google_sign_in_android/example/lib/main.dart index 526cf8b69ccf..5818b6040fcc 100644 --- a/packages/google_sign_in/google_sign_in_android/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_android/example/lib/main.dart @@ -41,14 +41,16 @@ class SignInDemoState extends State { } Future _ensureInitialized() { - return _initialization ??= GoogleSignInPlatform.instance.init( + return _initialization ??= + GoogleSignInPlatform.instance.initWithParams(const SignInInitParameters( scopes: [ 'email', 'https://www.googleapis.com/auth/contacts.readonly', ], - )..catchError((dynamic _) { - _initialization = null; - }); + )) + ..catchError((dynamic _) { + _initialization = null; + }); } void _setUser(GoogleSignInUserData? user) { diff --git a/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml index 3aa8a80ee585..4d12bbad7987 100644 --- a/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml @@ -16,7 +16,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_sign_in_platform_interface: ^2.1.0 + google_sign_in_platform_interface: ^2.2.0 http: ^0.13.0 dev_dependencies: 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 c4c30cd3e545..4424ce3ff2d9 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/plugins/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: 5.2.8 +version: 6.0.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -20,7 +20,7 @@ flutter: dependencies: flutter: sdk: flutter - google_sign_in_platform_interface: ^2.1.0 + google_sign_in_platform_interface: ^2.2.0 dev_dependencies: flutter_driver: 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 c17f1415b724..5ed38de5cb74 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,8 @@ +## 5.4.0 + +* Adds support for `serverClientId` configuration option. +* Makes `Google-Services.info` file optional. + ## 5.3.1 * Suppresses warnings for pre-iOS-13 codepaths. @@ -17,4 +22,4 @@ ## 5.2.5 -* Splits from `video_player` as a federated implementation. +* Splits from `google_sign_in` as a federated implementation. diff --git a/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m index 7efd490f30fe..5738b7f1c1fc 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m +++ b/packages/google_sign_in/google_sign_in_ios/example/ios/RunnerTests/GoogleSignInTests.m @@ -96,6 +96,25 @@ - (void)testDisconnectIgnoresError { #pragma mark - Init +- (void)testInitNoClientIdError { + // Init plugin without GoogleService-Info.plist. + self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.mockSignIn + withGoogleServiceProperties:nil]; + + // init call does not provide a clientId. + FlutterMethodCall *initMethodCall = [FlutterMethodCall methodCallWithMethodName:@"init" + arguments:@{}]; + + XCTestExpectation *initExpectation = + [self expectationWithDescription:@"expect result returns true"]; + [self.plugin handleMethodCall:initMethodCall + result:^(FlutterError *result) { + XCTAssertEqualObjects(result.code, @"missing-config"); + [initExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:nil]; +} + - (void)testInitGoogleServiceInfoPlist { FlutterMethodCall *initMethodCall = [FlutterMethodCall methodCallWithMethodName:@"init" @@ -133,6 +152,10 @@ - (void)testInitGoogleServiceInfoPlist { } - (void)testInitDynamicClientIdNullDomain { + // Init plugin without GoogleService-Info.plist. + self.plugin = [[FLTGoogleSignInPlugin alloc] initWithSignIn:self.mockSignIn + withGoogleServiceProperties:nil]; + FlutterMethodCall *initMethodCall = [FlutterMethodCall methodCallWithMethodName:@"init" arguments:@{@"hostedDomain" : [NSNull null], @"clientId" : @"mockClientId"}]; @@ -163,6 +186,40 @@ - (void)testInitDynamicClientIdNullDomain { callback:OCMOCK_ANY]); } +- (void)testInitDynamicServerClientIdNullDomain { + FlutterMethodCall *initMethodCall = + [FlutterMethodCall methodCallWithMethodName:@"init" + arguments:@{ + @"hostedDomain" : [NSNull null], + @"serverClientId" : @"mockServerClientId" + }]; + + XCTestExpectation *initExpectation = + [self expectationWithDescription:@"expect result returns true"]; + [self.plugin handleMethodCall:initMethodCall + result:^(id result) { + XCTAssertNil(result); + [initExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5.0 handler:nil]; + + // Initialization values used in the next sign in request. + FlutterMethodCall *signInMethodCall = [FlutterMethodCall methodCallWithMethodName:@"signIn" + arguments:nil]; + [self.plugin handleMethodCall:signInMethodCall + result:^(id r){ + }]; + OCMVerify([self.mockSignIn + signInWithConfiguration:[OCMArg checkWithBlock:^BOOL(GIDConfiguration *configuration) { + return configuration.hostedDomain == nil && + [configuration.serverClientID isEqualToString:@"mockServerClientId"]; + }] + presentingViewController:[OCMArg isKindOfClass:[FlutterViewController class]] + hint:nil + additionalScopes:OCMOCK_ANY + callback:OCMOCK_ANY]); +} + #pragma mark - Is signed in - (void)testIsNotSignedIn { diff --git a/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart b/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart index 526cf8b69ccf..e23935ded1da 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart +++ b/packages/google_sign_in/google_sign_in_ios/example/lib/main.dart @@ -31,7 +31,8 @@ class SignInDemo extends StatefulWidget { class SignInDemoState extends State { GoogleSignInUserData? _currentUser; String _contactText = ''; - // Future that completes when `init` has completed on the sign in instance. + // Future that completes when `initWithParams` has completed on the sign in + // instance. Future? _initialization; @override @@ -41,14 +42,16 @@ class SignInDemoState extends State { } Future _ensureInitialized() { - return _initialization ??= GoogleSignInPlatform.instance.init( + return _initialization ??= + GoogleSignInPlatform.instance.initWithParams(const SignInInitParameters( scopes: [ 'email', 'https://www.googleapis.com/auth/contacts.readonly', ], - )..catchError((dynamic _) { - _initialization = null; - }); + )) + ..catchError((dynamic _) { + _initialization = null; + }); } void _setUser(GoogleSignInUserData? user) { diff --git a/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml index ed51e3b63a58..d17c929a989f 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml @@ -16,7 +16,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_sign_in_platform_interface: ^2.1.0 + google_sign_in_platform_interface: ^2.2.0 http: ^0.13.0 dev_dependencies: diff --git a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m index 608cdc2bec6d..7beb604aaee3 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin.m @@ -14,6 +14,15 @@ static NSString *const kServerClientIdKey = @"SERVER_CLIENT_ID"; +static NSDictionary *loadGoogleServiceInfo() { + NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" + ofType:@"plist"]; + if (plistPath) { + return [[NSDictionary alloc] initWithContentsOfFile:plistPath]; + } + return nil; +} + // These error codes must match with ones declared on Android and Dart sides. static NSString *const kErrorReasonSignInRequired = @"sign_in_required"; static NSString *const kErrorReasonSignInCanceled = @"sign_in_canceled"; @@ -52,6 +61,9 @@ @interface FLTGoogleSignInPlugin () // sign in, sign out, and requesting additional scopes. @property(strong, readonly) GIDSignIn *signIn; +// The contents of GoogleService-Info.plist, if it exists. +@property(strong, nullable) NSDictionary *googleServiceProperties; + // Redeclared as not a designated initializer. - (instancetype)init; @@ -73,9 +85,15 @@ - (instancetype)init { } - (instancetype)initWithSignIn:(GIDSignIn *)signIn { + return [self initWithSignIn:signIn withGoogleServiceProperties:loadGoogleServiceInfo()]; +} + +- (instancetype)initWithSignIn:(GIDSignIn *)signIn + withGoogleServiceProperties:(nullable NSDictionary *)googleServiceProperties { self = [super init]; if (self) { _signIn = signIn; + _googleServiceProperties = googleServiceProperties; // On the iOS simulator, we get "Broken pipe" errors after sign-in for some // unknown reason. We can avoid crashing the app by ignoring them. @@ -91,6 +109,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result if ([call.method isEqualToString:@"init"]) { GIDConfiguration *configuration = [self configurationWithClientIdArgument:call.arguments[@"clientId"] + serverClientIdArgument:call.arguments[@"serverClientId"] hostedDomainArgument:call.arguments[@"hostedDomain"]]; if (configuration != nil) { if ([call.arguments[@"scopes"] isKindOfClass:[NSArray class]]) { @@ -100,7 +119,8 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result result(nil); } else { result([FlutterError errorWithCode:@"missing-config" - message:@"GoogleService-Info.plist file not found" + message:@"GoogleService-Info.plist file not found and clientId " + @"was not provided programmatically." details:nil]); } } else if ([call.method isEqualToString:@"signInSilently"]) { @@ -113,6 +133,7 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result @try { GIDConfiguration *configuration = self.configuration ?: [self configurationWithClientIdArgument:nil + serverClientIdArgument:nil hostedDomainArgument:nil]; [self.signIn signInWithConfiguration:configuration presentingViewController:[self topViewController] @@ -198,25 +219,32 @@ - (void)signIn:(GIDSignIn *)signIn dismissViewController:(UIViewController *)vie #pragma mark - private methods -/// @return @c nil if GoogleService-Info.plist not found. +/// @return @c nil if GoogleService-Info.plist not found and clientId is not provided. - (GIDConfiguration *)configurationWithClientIdArgument:(id)clientIDArg + serverClientIdArgument:(id)serverClientIDArg hostedDomainArgument:(id)hostedDomainArg { - NSString *plistPath = [NSBundle.mainBundle pathForResource:@"GoogleService-Info" ofType:@"plist"]; - if (plistPath == nil) { + NSString *clientID; + BOOL hasDynamicClientId = [clientIDArg isKindOfClass:[NSString class]]; + if (hasDynamicClientId) { + clientID = clientIDArg; + } else if (self.googleServiceProperties) { + clientID = self.googleServiceProperties[kClientIdKey]; + } else { + // We couldn't resolve a clientId, without which we cannot create a GIDConfiguration. return nil; } - NSDictionary *plist = [[NSDictionary alloc] initWithContentsOfFile:plistPath]; - - BOOL hasDynamicClientId = [clientIDArg isKindOfClass:[NSString class]]; - NSString *clientID = hasDynamicClientId ? clientIDArg : plist[kClientIdKey]; + BOOL hasDynamicServerClientId = [serverClientIDArg isKindOfClass:[NSString class]]; + NSString *serverClientID = hasDynamicServerClientId + ? serverClientIDArg + : self.googleServiceProperties[kServerClientIdKey]; NSString *hostedDomain = nil; if (hostedDomainArg != [NSNull null]) { hostedDomain = hostedDomainArg; } return [[GIDConfiguration alloc] initWithClientID:clientID - serverClientID:plist[kServerClientIdKey] + serverClientID:serverClientID hostedDomain:hostedDomain openIDRealm:nil]; } diff --git a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin_Test.h b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin_Test.h index f8d5be6f8522..17ddb7f616bc 100644 --- a/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin_Test.h +++ b/packages/google_sign_in/google_sign_in_ios/ios/Classes/FLTGoogleSignInPlugin_Test.h @@ -6,12 +6,21 @@ #import +NS_ASSUME_NONNULL_BEGIN + @class GIDSignIn; /// Methods exposed for unit testing. @interface FLTGoogleSignInPlugin () /// Inject @c GIDSignIn for testing. -- (instancetype)initWithSignIn:(GIDSignIn *)signIn NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithSignIn:(GIDSignIn *)signIn; + +/// Inject @c GIDSignIn and @c googleServiceProperties for testing. +- (instancetype)initWithSignIn:(GIDSignIn *)signIn + withGoogleServiceProperties:(nullable NSDictionary *)googleServiceProperties + NS_DESIGNATED_INITIALIZER; @end + +NS_ASSUME_NONNULL_END 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 13f045b6006c..65c8928c1402 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/plugins/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.3.1 +version: 5.4.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - google_sign_in_platform_interface: ^2.1.0 + google_sign_in_platform_interface: ^2.2.0 dev_dependencies: flutter_driver: 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 672b1b2ca857..12e6d9630f9c 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.10.2 + +* Migrates to new platform-interface `initWithParams` method. +* Throws when unsupported `serverClientId` option is provided. + ## 0.10.1+3 * Updates references to the obsolete master branch. diff --git a/packages/google_sign_in/google_sign_in_web/README.md b/packages/google_sign_in/google_sign_in_web/README.md index 6f6dcf2dbf15..7c02379808da 100644 --- a/packages/google_sign_in/google_sign_in_web/README.md +++ b/packages/google_sign_in/google_sign_in_web/README.md @@ -63,8 +63,11 @@ GoogleSignIn _googleSignIn = GoogleSignIn( ], ); ``` + [Full list of available scopes](https://developers.google.com/identity/protocols/googlescopes). +Note that the `serverClientId` parameter of the `GoogleSignIn` constructor is not supported on Web. + You can now use the `GoogleSignIn` class to authenticate in your Dart code, e.g. ```dart diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_legacy_init_test.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_legacy_init_test.dart new file mode 100644 index 000000000000..12f8f2f3f167 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_legacy_init_test.dart @@ -0,0 +1,223 @@ +// 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 is a copy of `auth2_test.dart`, before it was migrated to the +// new `initWithParams` method, and is kept to ensure test coverage of the +// deprecated `init` method, until it is removed. + +import 'dart:html' as html; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; +import 'package:google_sign_in_web/google_sign_in_web.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:js/js_util.dart' as js_util; + +import 'gapi_mocks/gapi_mocks.dart' as gapi_mocks; +import 'src/test_utils.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + final GoogleSignInTokenData expectedTokenData = + GoogleSignInTokenData(idToken: '70k3n', accessToken: 'access_70k3n'); + + final GoogleSignInUserData expectedUserData = GoogleSignInUserData( + displayName: 'Foo Bar', + email: 'foo@example.com', + id: '123', + photoUrl: 'http://example.com/img.jpg', + idToken: expectedTokenData.idToken, + ); + + late GoogleSignInPlugin plugin; + + group('plugin.initialize() throws a catchable exception', () { + setUp(() { + // The pre-configured use case for the instances of the plugin in this test + gapiUrl = toBase64Url(gapi_mocks.auth2InitError()); + plugin = GoogleSignInPlugin(); + }); + + testWidgets('initialize throws PlatformException', + (WidgetTester tester) async { + await expectLater( + plugin.init( + hostedDomain: 'foo', + scopes: ['some', 'scope'], + clientId: '1234', + ), + throwsA(isA())); + }); + + testWidgets('initialize forwards error code from JS', + (WidgetTester tester) async { + try { + await plugin.init( + hostedDomain: 'foo', + scopes: ['some', 'scope'], + clientId: '1234', + ); + fail('plugin.initialize should have thrown an exception!'); + } catch (e) { + final String code = js_util.getProperty(e, 'code'); + expect(code, 'idpiframe_initialization_failed'); + } + }); + }); + + group('other methods also throw catchable exceptions on initialize fail', () { + // This function ensures that initialize gets called, but for some reason, + // we ignored that it has thrown stuff... + Future _discardInit() async { + try { + await plugin.init( + hostedDomain: 'foo', + scopes: ['some', 'scope'], + clientId: '1234', + ); + } catch (e) { + // Noop so we can call other stuff + } + } + + setUp(() { + gapiUrl = toBase64Url(gapi_mocks.auth2InitError()); + plugin = GoogleSignInPlugin(); + }); + + testWidgets('signInSilently throws', (WidgetTester tester) async { + await _discardInit(); + await expectLater( + plugin.signInSilently(), throwsA(isA())); + }); + + testWidgets('signIn throws', (WidgetTester tester) async { + await _discardInit(); + await expectLater(plugin.signIn(), throwsA(isA())); + }); + + testWidgets('getTokens throws', (WidgetTester tester) async { + await _discardInit(); + await expectLater(plugin.getTokens(email: 'test@example.com'), + throwsA(isA())); + }); + testWidgets('requestScopes', (WidgetTester tester) async { + await _discardInit(); + await expectLater(plugin.requestScopes(['newScope']), + throwsA(isA())); + }); + }); + + group('auth2 Init Successful', () { + setUp(() { + // The pre-configured use case for the instances of the plugin in this test + gapiUrl = toBase64Url(gapi_mocks.auth2InitSuccess(expectedUserData)); + plugin = GoogleSignInPlugin(); + }); + + testWidgets('Init requires clientId', (WidgetTester tester) async { + expect(plugin.init(hostedDomain: ''), throwsAssertionError); + }); + + testWidgets("Init doesn't accept spaces in scopes", + (WidgetTester tester) async { + expect( + plugin.init( + hostedDomain: '', + clientId: '', + scopes: ['scope with spaces'], + ), + throwsAssertionError); + }); + + // See: https://github.com/flutter/flutter/issues/88084 + testWidgets('Init passes plugin_name parameter with the expected value', + (WidgetTester tester) async { + await plugin.init( + hostedDomain: 'foo', + scopes: ['some', 'scope'], + clientId: '1234', + ); + + final Object? initParameters = + js_util.getProperty(html.window, 'gapi2.init.parameters'); + expect(initParameters, isNotNull); + + final Object? pluginNameParameter = + js_util.getProperty(initParameters!, 'plugin_name'); + expect(pluginNameParameter, isA()); + expect(pluginNameParameter, 'dart-google_sign_in_web'); + }); + + group('Successful .initialize, then', () { + setUp(() async { + await plugin.init( + hostedDomain: 'foo', + scopes: ['some', 'scope'], + clientId: '1234', + ); + await plugin.initialized; + }); + + testWidgets('signInSilently', (WidgetTester tester) async { + final GoogleSignInUserData actualUser = + (await plugin.signInSilently())!; + + expect(actualUser, expectedUserData); + }); + + testWidgets('signIn', (WidgetTester tester) async { + final GoogleSignInUserData actualUser = (await plugin.signIn())!; + + expect(actualUser, expectedUserData); + }); + + testWidgets('getTokens', (WidgetTester tester) async { + final GoogleSignInTokenData actualToken = + await plugin.getTokens(email: expectedUserData.email); + + expect(actualToken, expectedTokenData); + }); + + testWidgets('requestScopes', (WidgetTester tester) async { + final bool scopeGranted = + await plugin.requestScopes(['newScope']); + + expect(scopeGranted, isTrue); + }); + }); + }); + + group('auth2 Init successful, but exception on signIn() method', () { + setUp(() async { + // The pre-configured use case for the instances of the plugin in this test + gapiUrl = toBase64Url(gapi_mocks.auth2SignInError()); + plugin = GoogleSignInPlugin(); + await plugin.init( + hostedDomain: 'foo', + scopes: ['some', 'scope'], + clientId: '1234', + ); + await plugin.initialized; + }); + + testWidgets('User aborts sign in flow, throws PlatformException', + (WidgetTester tester) async { + await expectLater(plugin.signIn(), throwsA(isA())); + }); + + testWidgets('User aborts sign in flow, error code is forwarded from JS', + (WidgetTester tester) async { + try { + await plugin.signIn(); + fail('plugin.signIn() should have thrown an exception!'); + } catch (e) { + final String code = js_util.getProperty(e, 'code'); + expect(code, 'popup_closed_by_user'); + } + }); + }); +} diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart index d8c7655a11c4..81d9f1489a23 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart @@ -30,32 +30,31 @@ void main() { late GoogleSignInPlugin plugin; - group('plugin.init() throws a catchable exception', () { + group('plugin.initWithParams() throws a catchable exception', () { setUp(() { // The pre-configured use case for the instances of the plugin in this test gapiUrl = toBase64Url(gapi_mocks.auth2InitError()); plugin = GoogleSignInPlugin(); }); - testWidgets('init throws PlatformException', (WidgetTester tester) async { + testWidgets('throws PlatformException', (WidgetTester tester) async { await expectLater( - plugin.init( + plugin.initWithParams(const SignInInitParameters( hostedDomain: 'foo', scopes: ['some', 'scope'], clientId: '1234', - ), + )), throwsA(isA())); }); - testWidgets('init forwards error code from JS', - (WidgetTester tester) async { + testWidgets('forwards error code from JS', (WidgetTester tester) async { try { - await plugin.init( + await plugin.initWithParams(const SignInInitParameters( hostedDomain: 'foo', scopes: ['some', 'scope'], clientId: '1234', - ); - fail('plugin.init should have thrown an exception!'); + )); + fail('plugin.initWithParams should have thrown an exception!'); } catch (e) { final String code = js_util.getProperty(e, 'code'); expect(code, 'idpiframe_initialization_failed'); @@ -63,16 +62,17 @@ void main() { }); }); - group('other methods also throw catchable exceptions on init fail', () { - // This function ensures that init gets called, but for some reason, we - // ignored that it has thrown stuff... + group('other methods also throw catchable exceptions on initWithParams fail', + () { + // This function ensures that initWithParams gets called, but for some + // reason, we ignored that it has thrown stuff... Future _discardInit() async { try { - await plugin.init( + await plugin.initWithParams(const SignInInitParameters( hostedDomain: 'foo', scopes: ['some', 'scope'], clientId: '1234', - ); + )); } catch (e) { // Noop so we can call other stuff } @@ -114,28 +114,40 @@ void main() { }); testWidgets('Init requires clientId', (WidgetTester tester) async { - expect(plugin.init(hostedDomain: ''), throwsAssertionError); + expect( + plugin.initWithParams(const SignInInitParameters(hostedDomain: '')), + throwsAssertionError); + }); + + testWidgets("Init doesn't accept serverClientId", + (WidgetTester tester) async { + expect( + plugin.initWithParams(const SignInInitParameters( + clientId: '', + serverClientId: '', + )), + throwsAssertionError); }); testWidgets("Init doesn't accept spaces in scopes", (WidgetTester tester) async { expect( - plugin.init( + plugin.initWithParams(const SignInInitParameters( hostedDomain: '', clientId: '', scopes: ['scope with spaces'], - ), + )), throwsAssertionError); }); // See: https://github.com/flutter/flutter/issues/88084 testWidgets('Init passes plugin_name parameter with the expected value', (WidgetTester tester) async { - await plugin.init( + await plugin.initWithParams(const SignInInitParameters( hostedDomain: 'foo', scopes: ['some', 'scope'], clientId: '1234', - ); + )); final Object? initParameters = js_util.getProperty(html.window, 'gapi2.init.parameters'); @@ -147,13 +159,13 @@ void main() { expect(pluginNameParameter, 'dart-google_sign_in_web'); }); - group('Successful .init, then', () { + group('Successful .initWithParams, then', () { setUp(() async { - await plugin.init( + await plugin.initWithParams(const SignInInitParameters( hostedDomain: 'foo', scopes: ['some', 'scope'], clientId: '1234', - ); + )); await plugin.initialized; }); @@ -191,11 +203,11 @@ void main() { // The pre-configured use case for the instances of the plugin in this test gapiUrl = toBase64Url(gapi_mocks.auth2SignInError()); plugin = GoogleSignInPlugin(); - await plugin.init( + await plugin.initWithParams(const SignInInitParameters( hostedDomain: 'foo', scopes: ['some', 'scope'], clientId: '1234', - ); + )); await plugin.initialized; }); diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_load_legacy_init_test.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_load_legacy_init_test.dart new file mode 100644 index 000000000000..7bfef53f7a23 --- /dev/null +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_load_legacy_init_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. + +// This file is a copy of `gapi_load_test.dart`, before it was migrated to the +// new `initWithParams` method, and is kept to ensure test coverage of the +// deprecated `init` method, until it is removed. + +import 'dart:html' as html; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; +import 'package:google_sign_in_web/google_sign_in_web.dart'; +import 'package:integration_test/integration_test.dart'; + +import 'gapi_mocks/gapi_mocks.dart' as gapi_mocks; +import 'src/test_utils.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + gapiUrl = toBase64Url(gapi_mocks.auth2InitSuccess( + GoogleSignInUserData(email: 'test@test.com', id: '1234'))); + + testWidgets('Plugin is initialized after GAPI fully loads and init is called', + (WidgetTester tester) async { + expect( + html.querySelector('script[src^="data:"]'), + isNull, + reason: 'Mock script not present before instantiating the plugin', + ); + final GoogleSignInPlugin plugin = GoogleSignInPlugin(); + expect( + html.querySelector('script[src^="data:"]'), + isNotNull, + reason: 'Mock script should be injected', + ); + expect(() { + plugin.initialized; + }, throwsStateError, + reason: + 'The plugin should throw if checking for `initialized` before calling .init'); + await plugin.init(hostedDomain: '', clientId: ''); + await plugin.initialized; + expect( + plugin.initialized, + completes, + reason: 'The plugin should complete the future once initialized.', + ); + }); +} diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_load_test.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_load_test.dart index 5da42283367f..fc753e20d92c 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_load_test.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/gapi_load_test.dart @@ -34,9 +34,12 @@ void main() { expect(() { plugin.initialized; }, throwsStateError, - reason: - 'The plugin should throw if checking for `initialized` before calling .init'); - await plugin.init(hostedDomain: '', clientId: ''); + reason: 'The plugin should throw if checking for `initialized` before ' + 'calling .initWithParams'); + await plugin.initWithParams(const SignInInitParameters( + hostedDomain: '', + clientId: '', + )); await plugin.initialized; expect( plugin.initialized, diff --git a/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart b/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart index ae6180d34acb..c305cae2a33d 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/google_sign_in_web.dart @@ -41,13 +41,15 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { late Future _isAuthInitialized; bool _isInitCalled = false; - // This method throws if init hasn't been called at some point in the past. - // It is used by the [initialized] getter to ensure that users can't await - // on a Future that will never resolve. + // This method throws if init or initWithParams hasn't been called at some + // point in the past. It is used by the [initialized] getter to ensure that + // users can't await on a Future that will never resolve. void _assertIsInitCalled() { if (!_isInitCalled) { throw StateError( - 'GoogleSignInPlugin::init() must be called before any other method in this plugin.'); + 'GoogleSignInPlugin::init() or GoogleSignInPlugin::initWithParams() ' + 'must be called before any other method in this plugin.', + ); } } @@ -71,16 +73,29 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { SignInOption signInOption = SignInOption.standard, String? hostedDomain, String? clientId, - }) async { - final String? appClientId = clientId ?? _autoDetectedClientId; + }) { + return initWithParams(SignInInitParameters( + scopes: scopes, + signInOption: signInOption, + hostedDomain: hostedDomain, + clientId: clientId, + )); + } + + @override + Future initWithParams(SignInInitParameters params) async { + final String? appClientId = params.clientId ?? _autoDetectedClientId; assert( appClientId != null, 'ClientID not set. Either set it on a ' ' tag,' - ' or pass clientId when calling init()'); + ' or pass clientId when initializing GoogleSignIn'); + + assert(params.serverClientId == null, + 'serverClientId is not supported on Web.'); assert( - !scopes.any((String scope) => scope.contains(' ')), + !params.scopes.any((String scope) => scope.contains(' ')), "OAuth 2.0 Scopes for Google APIs can't contain spaces. " 'Check https://developers.google.com/identity/protocols/googlescopes ' 'for a list of valid OAuth 2.0 scopes.'); @@ -88,9 +103,9 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { await _isGapiInitialized; final auth2.GoogleAuth auth = auth2.init(auth2.ClientConfig( - hosted_domain: hostedDomain, + hosted_domain: params.hostedDomain, // The js lib wants a space-separated list of values - scope: scopes.join(' '), + scope: params.scopes.join(' '), client_id: appClientId!, plugin_name: 'dart-google_sign_in_web', )); 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 907cc90c81eb..1dedd6de6666 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/plugins/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.10.1+3 +version: 0.10.2 environment: sdk: ">=2.12.0 <3.0.0" @@ -22,7 +22,7 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter - google_sign_in_platform_interface: ^2.0.0 + google_sign_in_platform_interface: ^2.2.0 js: ^0.6.3 dev_dependencies: From 5e5dee8d48da440842e61426b65928707379a929 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 7 Jul 2022 12:59:08 -0400 Subject: [PATCH 480/844] [webview_flutter_wkwebview] Prevent leaking when a callback method references an object that references itself (#6056) --- .../webview_flutter_test.dart | 53 ++++++ .../lib/src/common/weak_reference_utils.dart | 34 ++++ .../lib/src/foundation/foundation.dart | 15 +- .../lib/src/web_kit/web_kit.dart | 16 ++ .../lib/src/web_kit_webview_widget.dart | 167 ++++++++++-------- 5 files changed, 215 insertions(+), 70 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/weak_reference_utils.dart 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 9687c7d56cbe..1119f7457bc9 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 @@ -19,6 +19,8 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'package:webview_flutter_wkwebview/src/common/instance_manager.dart'; +import 'package:webview_flutter_wkwebview/src/common/weak_reference_utils.dart'; import 'package:webview_flutter_wkwebview_example/navigation_decision.dart'; import 'package:webview_flutter_wkwebview_example/navigation_request.dart'; import 'package:webview_flutter_wkwebview_example/web_view.dart'; @@ -66,6 +68,27 @@ Future main() async { expect(currentUrl, primaryUrl); }); + testWidgets( + 'withWeakRefenceTo allows encapsulating class to be garbage collected', + (WidgetTester tester) async { + final Completer gcCompleter = Completer(); + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: gcCompleter.complete, + ); + + ClassWithCallbackClass? instance = ClassWithCallbackClass(); + instanceManager.addHostCreatedInstance(instance.callbackClass, 0); + instance = null; + + // Force garbage collection. + await IntegrationTestWidgetsFlutterBinding.instance + .watchPerformance(() async { + await tester.pumpAndSettle(); + }); + + expect(gcCompleter.future, completion(0)); + }, timeout: const Timeout(Duration(seconds: 10))); + testWidgets('loadUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); @@ -1253,3 +1276,33 @@ class ResizableWebViewState extends State { ); } } + +class CopyableObjectWithCallback with Copyable { + CopyableObjectWithCallback(this.callback); + + final VoidCallback callback; + + @override + CopyableObjectWithCallback copy() { + return CopyableObjectWithCallback(callback); + } +} + +class ClassWithCallbackClass { + ClassWithCallbackClass() { + callbackClass = CopyableObjectWithCallback( + withWeakRefenceTo( + this, + (WeakReference weakReference) { + return () { + // Weak reference to `this` in callback. + // ignore: unnecessary_statements + weakReference; + }; + }, + ), + ); + } + + late final CopyableObjectWithCallback callbackClass; +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/weak_reference_utils.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/weak_reference_utils.dart new file mode 100644 index 000000000000..ad0c9ebf4f5c --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/weak_reference_utils.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. + +/// Helper method for creating callbacks methods with a weak reference. +/// +/// Example: +/// ``` +/// final JavascriptChannelRegistry javascriptChannelRegistry = ... +/// +/// final WKScriptMessageHandler handler = WKScriptMessageHandler( +/// didReceiveScriptMessage: withWeakRefenceTo( +/// javascriptChannelRegistry, +/// (WeakReference weakReference) { +/// return ( +/// WKUserContentController userContentController, +/// WKScriptMessage message, +/// ) { +/// weakReference.target?.onJavascriptChannelMessage( +/// message.name, +/// message.body!.toString(), +/// ); +/// }; +/// }, +/// ), +/// ); +/// ``` +S withWeakRefenceTo( + T reference, + S Function(WeakReference weakReference) onCreate, +) { + final WeakReference weakReference = WeakReference(reference); + return onCreate(weakReference); +} 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 f537a4454c4f..2059aa544207 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 @@ -8,6 +8,7 @@ import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; +import 'package:webview_flutter_wkwebview/src/common/weak_reference_utils.dart'; import '../common/instance_manager.dart'; import 'foundation_api_impls.dart'; @@ -267,7 +268,19 @@ class NSObject with Copyable { final NSObjectHostApiImpl _api; - /// Informs the observing object when the value at the specified key path has changed. + /// Informs the observing object when the value at the specified key path has + /// changed. + /// + /// {@template webview_flutter_wkwebview.foundation.callbacks} + /// For the associated Objective-C object to be automatically garbage + /// collected, it is required that this Function doesn't contain a strong + /// reference to the encapsulating class instance. Consider using + /// `WeakReference` when referencing an object not received as a parameter. + /// Otherwise, use [NSObject.dispose] to release the associated Objective-C + /// object manually. + /// + /// See [withWeakRefenceTo]. + /// {@endtemplate} final void Function( String keyPath, NSObject object, 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 ffc6eb8c23bf..a671064ddc0f 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 @@ -442,6 +442,8 @@ class WKScriptMessageHandler extends NSObject { /// Use this method to respond to a message sent from the webpage’s /// JavaScript code. Use the [message] parameter to get the message contents and /// to determine the originating web view. + /// + /// {@macro webview_flutter_wkwebview.foundation.callbacks} final void Function( WKUserContentController userContentController, WKScriptMessage message, @@ -733,6 +735,8 @@ class WKUIDelegate extends NSObject { final WKUIDelegateHostApiImpl _uiDelegateApi; /// Indicates a new [WKWebView] was requested to be created with [configuration]. + /// + /// {@macro webview_flutter_wkwebview.foundation.callbacks} final void Function( WKWebView webView, WKWebViewConfiguration configuration, @@ -803,26 +807,38 @@ class WKNavigationDelegate extends NSObject { final WKNavigationDelegateHostApiImpl _navigationDelegateApi; /// Called when navigation is complete. + /// + /// {@macro webview_flutter_wkwebview.foundation.callbacks} final void Function(WKWebView webView, String? url)? didFinishNavigation; /// Called when navigation from the main frame has started. + /// + /// {@macro webview_flutter_wkwebview.foundation.callbacks} final void Function(WKWebView webView, String? url)? didStartProvisionalNavigation; /// Called when permission is needed to navigate to new content. + /// + /// {@macro webview_flutter_wkwebview.foundation.callbacks} final Future Function( WKWebView webView, WKNavigationAction navigationAction, )? decidePolicyForNavigationAction; /// Called when an error occurred during navigation. + /// + /// {@macro webview_flutter_wkwebview.foundation.callbacks} final void Function(WKWebView webView, NSError error)? didFailNavigation; /// Called when an error occurred during the early navigation process. + /// + /// {@macro webview_flutter_wkwebview.foundation.callbacks} final void Function(WKWebView webView, NSError error)? didFailProvisionalNavigation; /// Called when the web view’s content process was terminated. + /// + /// {@macro webview_flutter_wkwebview.foundation.callbacks} final void Function(WKWebView webView)? webViewWebContentProcessDidTerminate; @override diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index ed1912ee7898..852f9caa0c49 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -11,6 +11,7 @@ import 'package:flutter/services.dart'; import 'package:path/path.dart' as path; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'common/weak_reference_utils.dart'; import 'foundation/foundation.dart'; import 'web_kit/web_kit.dart'; @@ -114,58 +115,74 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { /// Used to integrate custom user interface elements into web view interactions. @visibleForTesting - late final WKUIDelegate uiDelegate = - webViewProxy.createUIDelgate(onCreateWebView: ( - WKWebView webView, - WKWebViewConfiguration configuration, - WKNavigationAction navigationAction, - ) { - if (!navigationAction.targetFrame.isMainFrame) { - webView.loadRequest(navigationAction.request); - } - }); + late final WKUIDelegate uiDelegate = webViewProxy.createUIDelgate( + onCreateWebView: ( + WKWebView webView, + WKWebViewConfiguration configuration, + WKNavigationAction navigationAction, + ) { + if (!navigationAction.targetFrame.isMainFrame) { + webView.loadRequest(navigationAction.request); + } + }, + ); /// Methods for handling navigation changes and tracking navigation requests. @visibleForTesting - late final WKNavigationDelegate navigationDelegate = - webViewProxy.createNavigationDelegate( - didFinishNavigation: (WKWebView webView, String? url) { - callbacksHandler.onPageFinished(url ?? ''); - }, - didStartProvisionalNavigation: (WKWebView webView, String? url) { - callbacksHandler.onPageStarted(url ?? ''); - }, - decidePolicyForNavigationAction: ( - WKWebView webView, - WKNavigationAction action, - ) async { - if (!_hasNavigationDelegate) { - return WKNavigationActionPolicy.allow; - } + late final WKNavigationDelegate navigationDelegate = withWeakRefenceTo( + this, + (WeakReference weakReference) { + return webViewProxy.createNavigationDelegate( + didFinishNavigation: (WKWebView webView, String? url) { + weakReference.target?.callbacksHandler.onPageFinished(url ?? ''); + }, + didStartProvisionalNavigation: (WKWebView webView, String? url) { + weakReference.target?.callbacksHandler.onPageStarted(url ?? ''); + }, + decidePolicyForNavigationAction: ( + WKWebView webView, + WKNavigationAction action, + ) async { + if (weakReference.target == null) { + return WKNavigationActionPolicy.allow; + } + + if (!weakReference.target!._hasNavigationDelegate) { + return WKNavigationActionPolicy.allow; + } + + final bool allow = + await weakReference.target!.callbacksHandler.onNavigationRequest( + url: action.request.url, + isForMainFrame: action.targetFrame.isMainFrame, + ); - final bool allow = await callbacksHandler.onNavigationRequest( - url: action.request.url, - isForMainFrame: action.targetFrame.isMainFrame, + return allow + ? WKNavigationActionPolicy.allow + : WKNavigationActionPolicy.cancel; + }, + didFailNavigation: (WKWebView webView, NSError error) { + weakReference.target?.callbacksHandler.onWebResourceError( + _toWebResourceError(error), + ); + }, + didFailProvisionalNavigation: (WKWebView webView, NSError error) { + weakReference.target?.callbacksHandler.onWebResourceError( + _toWebResourceError(error), + ); + }, + webViewWebContentProcessDidTerminate: (WKWebView webView) { + weakReference.target?.callbacksHandler.onWebResourceError( + WebResourceError( + errorCode: WKErrorCode.webContentProcessTerminated, + // Value from https://developer.apple.com/documentation/webkit/wkerrordomain?language=objc. + domain: 'WKErrorDomain', + description: '', + errorType: WebResourceErrorType.webContentProcessTerminated, + ), + ); + }, ); - - return allow - ? WKNavigationActionPolicy.allow - : WKNavigationActionPolicy.cancel; - }, - didFailNavigation: (WKWebView webView, NSError error) { - callbacksHandler.onWebResourceError(_toWebResourceError(error)); - }, - didFailProvisionalNavigation: (WKWebView webView, NSError error) { - callbacksHandler.onWebResourceError(_toWebResourceError(error)); - }, - webViewWebContentProcessDidTerminate: (WKWebView webView) { - callbacksHandler.onWebResourceError(WebResourceError( - errorCode: WKErrorCode.webContentProcessTerminated, - // Value from https://developer.apple.com/documentation/webkit/wkerrordomain?language=objc. - domain: 'WKErrorDomain', - description: '', - errorType: WebResourceErrorType.webContentProcessTerminated, - )); }, ); @@ -179,14 +196,23 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { autoMediaPlaybackPolicy: params.autoMediaPlaybackPolicy, ); - webView = webViewProxy.createWebView(configuration, observeValue: ( - String keyPath, - NSObject object, - Map change, - ) { - final double progress = change[NSKeyValueChangeKey.newValue]! as double; - callbacksHandler.onProgress((progress * 100).round()); - }); + webView = webViewProxy.createWebView( + configuration, + observeValue: withWeakRefenceTo( + callbacksHandler, + (WeakReference weakReference) { + return ( + String keyPath, + NSObject object, + Map change, + ) { + final double progress = + change[NSKeyValueChangeKey.newValue]! as double; + weakReference.target?.onProgress((progress * 100).round()); + }; + }, + ), + ); webView.setUIDelegate(uiDelegate); @@ -413,15 +439,22 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { ).map>( (String channelName) { final WKScriptMessageHandler handler = - webViewProxy.createScriptMessageHandler(didReceiveScriptMessage: ( - WKUserContentController userContentController, - WKScriptMessage message, - ) { - javascriptChannelRegistry.onJavascriptChannelMessage( - message.name, - message.body!.toString(), - ); - }); + webViewProxy.createScriptMessageHandler( + didReceiveScriptMessage: withWeakRefenceTo( + javascriptChannelRegistry, + (WeakReference weakReference) { + return ( + WKUserContentController userContentController, + WKScriptMessage message, + ) { + weakReference.target?.onJavascriptChannelMessage( + message.name, + message.body!.toString(), + ); + }; + }, + ), + ); _scriptMessageHandlers[channelName] = handler; final String wrapperSource = @@ -652,11 +685,7 @@ class WebViewWidgetProxy { /// Constructs a [WKNavigationDelegate]. WKNavigationDelegate createNavigationDelegate({ - void Function( - WKWebView webView, - String? url, - )? - didFinishNavigation, + void Function(WKWebView webView, String? url)? didFinishNavigation, void Function(WKWebView webView, String? url)? didStartProvisionalNavigation, Future Function( From 569268fcf139a3b5e46c131198e0a3b4e768b2ee Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 7 Jul 2022 13:49:08 -0400 Subject: [PATCH 481/844] Roll Flutter from 587cf5fa715f to 78e3b93664c7 (23 revisions) (#6080) --- .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 566ce0197354..b0a8d034304b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -587cf5fa715fffd95df9a224453f265482bb26f5 +78e3b93664c7784d806fe00080bd800f30cec662 From d07de9300b2d7ad89f20355a8595149d33d10903 Mon Sep 17 00:00:00 2001 From: Gabriel Terwesten Date: Thu, 7 Jul 2022 20:40:05 +0200 Subject: [PATCH 482/844] [google_sign_in] Support Dart-based configuration and `serverClientId` (#5250) --- .../google_sign_in/CHANGELOG.md | 5 ++ .../google_sign_in/google_sign_in/README.md | 16 +++++ .../google_sign_in/lib/google_sign_in.dart | 37 +++++++++-- .../google_sign_in/pubspec.yaml | 8 +-- .../test/google_sign_in_test.dart | 63 ++++++++++++++++--- 5 files changed, 110 insertions(+), 19 deletions(-) diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index d3f0bdaea17e..7e433a71368f 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,3 +1,8 @@ +## 5.4.0 + +* Adds support for configuring `serverClientId` through `GoogleSignIn` constructor. +* Adds support for Dart-based configuration as alternative to `GoogleService-Info.plist` for iOS. + ## 5.3.3 * Updates references to the obsolete master branch. diff --git a/packages/google_sign_in/google_sign_in/README.md b/packages/google_sign_in/google_sign_in/README.md index 5ede3be051b2..e467ca8541b9 100644 --- a/packages/google_sign_in/google_sign_in/README.md +++ b/packages/google_sign_in/google_sign_in/README.md @@ -65,6 +65,22 @@ This plugin requires iOS 9.0 or higher. ``` +As an alternative to adding `GoogleService-Info.plist` to your Xcode project, you can instead +configure your app in Dart code. In this case, skip steps 3-6 and pass `clientId` and +`serverClientId` to the `GoogleSignIn` constructor: + +```dart +GoogleSignIn _googleSignIn = GoogleSignIn( + ... + // The OAuth client id of your app. This is required. + clientId: ..., + // If you need to authenticate to a backend server, specify its OAuth client. This is optional. + serverClientId: ..., +); +``` + +Note that step 7 is still required. + #### iOS additional requirement Note that according to diff --git a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart index 3c62e0e1a655..a1f8f7bc49ca 100644 --- a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart +++ b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart @@ -12,6 +12,7 @@ import 'src/common.dart'; export 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart' show SignInOption; + export 'src/common.dart'; export 'widgets.dart'; @@ -183,6 +184,7 @@ class GoogleSignIn { this.scopes = const [], this.hostedDomain, this.clientId, + this.serverClientId, }); /// Factory for creating default sign in user experience. @@ -228,9 +230,29 @@ class GoogleSignIn { /// Domain to restrict sign-in to. final String? hostedDomain; - /// Client ID being used to connect to google sign-in. Only supported on web. + /// Client ID being used to connect to google sign-in. + /// + /// This option is not supported on all platforms (e.g. Android). It is + /// optional if file-based configuration is used. + /// + /// The value specified here has precedence over a value from a configuration + /// file. final String? clientId; + /// Client ID of the backend server to which the app needs to authenticate + /// itself. + /// + /// Optional and not supported on all platforms (e.g. web). By default, it + /// is initialized from a configuration file if available. + /// + /// The value specified here has precedence over a value from a configuration + /// file. + /// + /// [GoogleSignInAuthentication.idToken] and + /// [GoogleSignInAccount.serverAuthCode] will be specific to the backend + /// server. + final String? serverClientId; + final StreamController _currentUserController = StreamController.broadcast(); @@ -260,15 +282,18 @@ class GoogleSignIn { } Future _ensureInitialized() { - return _initialization ??= GoogleSignInPlatform.instance.init( + return _initialization ??= + GoogleSignInPlatform.instance.initWithParams(SignInInitParameters( signInOption: signInOption, scopes: scopes, hostedDomain: hostedDomain, clientId: clientId, - )..catchError((dynamic _) { - // Invalidate initialization if it errors out. - _initialization = null; - }); + serverClientId: serverClientId, + )) + ..catchError((dynamic _) { + // Invalidate initialization if it errors out. + _initialization = null; + }); } /// The most recently scheduled method call. diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 9ea09dd1aeb4..c7724adcebad 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/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 and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.3.3 +version: 5.4.0 environment: @@ -23,9 +23,9 @@ flutter: dependencies: flutter: sdk: flutter - google_sign_in_android: ^5.2.5 - google_sign_in_ios: ^5.2.5 - google_sign_in_platform_interface: ^2.1.0 + google_sign_in_android: ^6.0.0 + google_sign_in_ios: ^5.4.0 + google_sign_in_platform_interface: ^2.2.0 google_sign_in_web: ^0.10.0 dev_dependencies: diff --git a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart index 2bc51b63d111..b8676bda298e 100644 --- a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart @@ -9,6 +9,7 @@ import 'package:google_sign_in/google_sign_in.dart'; import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; + import 'google_sign_in_test.mocks.dart'; /// Verify that [GoogleSignInAccount] can be mocked even though it's unused @@ -58,7 +59,7 @@ void main() { verify(mockPlatform.signIn()); }); - test('signIn prioritize clientId parameter when available', () async { + test('clientId parameter is forwarded to implementation', () async { const String fakeClientId = 'fakeClientId'; final GoogleSignIn googleSignIn = GoogleSignIn(clientId: fakeClientId); @@ -68,6 +69,17 @@ void main() { verify(mockPlatform.signIn()); }); + test('serverClientId parameter is forwarded to implementation', () async { + const String fakeServerClientId = 'fakeServerClientId'; + final GoogleSignIn googleSignIn = + GoogleSignIn(serverClientId: fakeServerClientId); + + await googleSignIn.signIn(); + + _verifyInit(mockPlatform, serverClientId: fakeServerClientId); + verify(mockPlatform.signIn()); + }); + test('signOut', () async { final GoogleSignIn googleSignIn = GoogleSignIn(); @@ -240,10 +252,12 @@ void main() { test('can sign in after init failed before', () async { final GoogleSignIn googleSignIn = GoogleSignIn(); - when(mockPlatform.init()).thenThrow(Exception('First init fails')); + when(mockPlatform.initWithParams(any)) + .thenThrow(Exception('First init fails')); expect(googleSignIn.signIn(), throwsA(isInstanceOf())); - when(mockPlatform.init()).thenAnswer((Invocation _) async {}); + when(mockPlatform.initWithParams(any)) + .thenAnswer((Invocation _) async {}); expect(await googleSignIn.signIn(), isNotNull); }); @@ -334,13 +348,44 @@ void main() { void _verifyInit( MockGoogleSignInPlatform mockSignIn, { + List scopes = const [], SignInOption signInOption = SignInOption.standard, + String? hostedDomain, String? clientId, + String? serverClientId, + bool forceCodeForRefreshToken = false, }) { - verify(mockSignIn.init( - signInOption: signInOption, - scopes: [], - hostedDomain: null, - clientId: clientId, - )); + verify(mockSignIn.initWithParams(argThat( + isA() + .having( + (SignInInitParameters p) => p.scopes, + 'scopes', + scopes, + ) + .having( + (SignInInitParameters p) => p.signInOption, + 'signInOption', + signInOption, + ) + .having( + (SignInInitParameters p) => p.hostedDomain, + 'hostedDomain', + hostedDomain, + ) + .having( + (SignInInitParameters p) => p.clientId, + 'clientId', + clientId, + ) + .having( + (SignInInitParameters p) => p.serverClientId, + 'serverClientId', + serverClientId, + ) + .having( + (SignInInitParameters p) => p.forceCodeForRefreshToken, + 'forceCodeForRefreshToken', + forceCodeForRefreshToken, + ), + ))); } From b4e2224cefaeb7132fa45189e1e271dd28cfdc3b Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Fri, 8 Jul 2022 10:25:05 -0700 Subject: [PATCH 483/844] [camera] Ignore body_might_complete_normally_catch_error violation (#6049) --- packages/camera/camera_android/CHANGELOG.md | 4 ++++ packages/camera/camera_android/lib/src/android_camera.dart | 7 ++++++- packages/camera/camera_avfoundation/CHANGELOG.md | 4 ++++ .../camera_avfoundation/lib/src/avfoundation_camera.dart | 7 ++++++- 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index 1bc9be5af9c3..e3899c340571 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores missing return warnings in preparation for [upcoming analysis changes](https://github.com/flutter/flutter/issues/105750). + ## 0.9.8+3 * Skips duplicate calls to stop background thread and removes unnecessary closings of camera capture sessions on Android. diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index db6264cae011..b02929ec8c8c 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -138,7 +138,12 @@ class AndroidCamera extends CameraPlatform { 'cameraId': cameraId, 'imageFormatGroup': imageFormatGroup.name(), }, - ).catchError( + ) + // TODO(srawlins): This should return a value of the future's type. This + // will fail upcoming analysis checks with + // https://github.com/flutter/flutter/issues/105750. + // ignore: body_might_complete_normally_catch_error + .catchError( (Object error, StackTrace stackTrace) { if (error is! PlatformException) { throw error; diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index e9972aede28a..9bab2ec375c1 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Ignores missing return warnings in preparation for [upcoming analysis changes](https://github.com/flutter/flutter/issues/105750). + ## 0.9.8+2 * Fixes exception in registerWith caused by the switch to an in-package method channel. diff --git a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart index c75d41f47c81..d4f986074671 100644 --- a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart +++ b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart @@ -138,7 +138,12 @@ class AVFoundationCamera extends CameraPlatform { 'cameraId': cameraId, 'imageFormatGroup': imageFormatGroup.name(), }, - ).catchError( + ) + // TODO(srawlins): This should return a value of the future's type. This + // will fail upcoming analysis checks with + // https://github.com/flutter/flutter/issues/105750. + // ignore: body_might_complete_normally_catch_error + .catchError( (Object error, StackTrace stackTrace) { if (error is! PlatformException) { throw error; From 2adf9f30fed2378297247de8d562f3346a955692 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Jul 2022 11:10:09 -0700 Subject: [PATCH 484/844] [sign_in]: Bump gradle from 3.3.0 to 7.2.1 in /packages/google_sign_in/google_sign_in_android/android (#5838) --- packages/google_sign_in/google_sign_in_android/CHANGELOG.md | 4 ++++ .../google_sign_in_android/android/build.gradle | 2 +- packages/google_sign_in/google_sign_in_android/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 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 a91f0c0351c4..9852a1f733ae 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.0.1 + +* Updates gradle version to 7.2.1 on Android. + ## 6.0.0 * Deprecates `clientId` and adds support for `serverClientId` instead. 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 c977e4daaf5e..aeef6d6e14e3 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 @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:7.2.1' } } 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 4424ce3ff2d9..0a863dce1714 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/plugins/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.0.0 +version: 6.0.1 environment: sdk: ">=2.14.0 <3.0.0" From bde0d0d78f66b57606097b1ffab36c91ac6cafdb Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 8 Jul 2022 12:11:08 -0700 Subject: [PATCH 485/844] [video_player] Use epislon to compare values in matrix in test (#6088) --- .../video_player/test/video_player_test.dart | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index 728e3a29ad99..7837a0bec328 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'dart:io'; import 'dart:math' as math; +import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -160,8 +161,16 @@ void main() { await tester.pumpWidget(VideoPlayer(controller)); final Transform actualRotationCorrection = find.byType(Transform).evaluate().single.widget as Transform; - expect( - actualRotationCorrection.transform, equals(Matrix4.rotationZ(math.pi))); + final Float64List actualRotationCorrectionStorage = + actualRotationCorrection.transform.storage; + final Float64List expectedMatrixStorage = + Matrix4.rotationZ(math.pi).storage; + expect(actualRotationCorrectionStorage.length, + equals(expectedMatrixStorage.length)); + for (int i = 0; i < actualRotationCorrectionStorage.length; i++) { + expect(actualRotationCorrectionStorage[i], + moreOrLessEquals(expectedMatrixStorage[i])); + } }); testWidgets('no transform when rotationCorrection is zero', From efd33e34d59cae5e2365814023b809223d169bac Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Fri, 8 Jul 2022 16:30:02 -0700 Subject: [PATCH 486/844] [in_app_purchase] Migrate android to Billing to 5.0.0 (#5405) --- .../in_app_purchase_android/CHANGELOG.md | 6 ++ .../android/build.gradle | 12 +-- .../inapppurchase/InAppPurchasePlugin.java | 2 + .../inapppurchase/MethodCallHandlerImpl.java | 63 +++++++++++--- .../plugins/inapppurchase/Translator.java | 17 ++-- .../inapppurchase/MethodCallHandlerTest.java | 82 +++++++++---------- .../plugins/inapppurchase/TranslatorTest.java | 38 +-------- .../example/android/app/build.gradle | 4 +- .../purchase_wrapper.dart | 29 +++++-- .../purchase_wrapper.g.dart | 10 ++- .../in_app_purchase_android/pubspec.yaml | 2 +- .../purchase_wrapper_test.dart | 12 +-- ...in_app_purchase_android_platform_test.dart | 8 +- 13 files changed, 156 insertions(+), 129 deletions(-) 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 966df5115e67..2f37f09478c8 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.2.3 + +* Upgrades Google Play Billing Library to 5.0 +* Migrates APIs to support breaking changes in new Google Play Billing API +* `PurchaseWrapper` and `PurchaseHistoryRecordWrapper` now handles `skus` a list of sku strings. `sku` is deprecated. + ## 0.2.2+8 * Ignores deprecation warnings for upcoming styleFrom button API changes. diff --git a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle index 9a5a74d0032b..663a4dfad46e 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle +++ b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle @@ -52,11 +52,11 @@ android { } dependencies { - implementation 'androidx.annotation:annotation:1.0.0' - implementation 'com.android.billingclient:billing:3.0.2' + implementation 'androidx.annotation:annotation:1.3.0' + implementation 'com.android.billingclient:billing:5.0.0' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.json:json:20180813' - testImplementation 'org.mockito:mockito-core:3.6.0' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' + testImplementation 'org.json:json:20220320' + testImplementation 'org.mockito:mockito-core:4.5.1' + androidTestImplementation 'androidx.test:runner:1.4.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' } diff --git a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/InAppPurchasePlugin.java b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/InAppPurchasePlugin.java index b21ab6992608..6f4e4bbfd8ee 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/InAppPurchasePlugin.java +++ b/packages/in_app_purchase/in_app_purchase_android/android/src/main/java/io/flutter/plugins/inapppurchase/InAppPurchasePlugin.java @@ -39,6 +39,7 @@ static final class MethodNames { static final String ON_PURCHASES_UPDATED = "PurchasesUpdatedListener#onPurchasesUpdated(int, List)"; static final String QUERY_PURCHASES = "BillingClient#queryPurchases(String)"; + static final String QUERY_PURCHASES_ASYNC = "BillingClient#queryPurchasesAsync(String)"; static final String QUERY_PURCHASE_HISTORY_ASYNC = "BillingClient#queryPurchaseHistoryAsync(String, PurchaseHistoryResponseListener)"; static final String CONSUME_PURCHASE_ASYNC = @@ -48,6 +49,7 @@ static final class MethodNames { static final String IS_FEATURE_SUPPORTED = "BillingClient#isFeatureSupported(String)"; static final String LAUNCH_PRICE_CHANGE_CONFIRMATION_FLOW = "BillingClient#launchPriceChangeConfirmationFlow (Activity, PriceChangeFlowParams, PriceChangeConfirmationListener)"; + static final String GET_CONNECTION_STATE = "BillingClient#getConnectionState()"; private MethodNames() {}; } 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 adad84b39e1d..ab12b2db8630 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 @@ -5,7 +5,7 @@ package io.flutter.plugins.inapppurchase; import static io.flutter.plugins.inapppurchase.Translator.fromPurchaseHistoryRecordList; -import static io.flutter.plugins.inapppurchase.Translator.fromPurchasesResult; +import static io.flutter.plugins.inapppurchase.Translator.fromPurchasesList; import static io.flutter.plugins.inapppurchase.Translator.fromSkuDetailsList; import android.app.Activity; @@ -25,8 +25,12 @@ import com.android.billingclient.api.ConsumeParams; import com.android.billingclient.api.ConsumeResponseListener; import com.android.billingclient.api.PriceChangeFlowParams; +import com.android.billingclient.api.Purchase; import com.android.billingclient.api.PurchaseHistoryRecord; import com.android.billingclient.api.PurchaseHistoryResponseListener; +import com.android.billingclient.api.PurchasesResponseListener; +import com.android.billingclient.api.QueryPurchaseHistoryParams; +import com.android.billingclient.api.QueryPurchasesParams; import com.android.billingclient.api.SkuDetails; import com.android.billingclient.api.SkuDetailsParams; import com.android.billingclient.api.SkuDetailsResponseListener; @@ -131,10 +135,14 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) { : ProrationMode.UNKNOWN_SUBSCRIPTION_UPGRADE_DOWNGRADE_POLICY, result); break; - case InAppPurchasePlugin.MethodNames.QUERY_PURCHASES: - queryPurchases((String) call.argument("skuType"), result); + case InAppPurchasePlugin.MethodNames.QUERY_PURCHASES: // Legacy method name. + queryPurchasesAsync((String) call.argument("skuType"), result); + break; + case InAppPurchasePlugin.MethodNames.QUERY_PURCHASES_ASYNC: + queryPurchasesAsync((String) call.argument("skuType"), result); break; case InAppPurchasePlugin.MethodNames.QUERY_PURCHASE_HISTORY_ASYNC: + Log.e("flutter", (String) call.argument("skuType")); queryPurchaseHistoryAsync((String) call.argument("skuType"), result); break; case InAppPurchasePlugin.MethodNames.CONSUME_PURCHASE_ASYNC: @@ -149,6 +157,9 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) { case InAppPurchasePlugin.MethodNames.LAUNCH_PRICE_CHANGE_CONFIRMATION_FLOW: launchPriceChangeConfirmationFlow((String) call.argument("sku"), result); break; + case InAppPurchasePlugin.MethodNames.GET_CONNECTION_STATE: + getConnectionState(result); + break; default: result.notImplemented(); } @@ -174,6 +185,7 @@ private void isReady(MethodChannel.Result result) { result.success(billingClient.isReady()); } + // TODO(garyq): Migrate to new subscriptions API: https://developer.android.com/google/play/billing/migrate-gpblv5 private void querySkuDetailsAsync( final String skuType, final List skusList, final MethodChannel.Result result) { if (billingClientError(result)) { @@ -208,7 +220,6 @@ private void launchBillingFlow( if (billingClientError(result)) { return; } - SkuDetails skuDetails = cachedSkus.get(sku); if (skuDetails == null) { result.error( @@ -255,12 +266,15 @@ private void launchBillingFlow( if (obfuscatedProfileId != null && !obfuscatedProfileId.isEmpty()) { paramsBuilder.setObfuscatedProfileId(obfuscatedProfileId); } - if (oldSku != null && !oldSku.isEmpty()) { - paramsBuilder.setOldSku(oldSku, purchaseToken); + BillingFlowParams.SubscriptionUpdateParams.Builder subscriptionUpdateParamsBuilder = + BillingFlowParams.SubscriptionUpdateParams.newBuilder(); + if (oldSku != null && !oldSku.isEmpty() && purchaseToken != null) { + subscriptionUpdateParamsBuilder.setOldPurchaseToken(purchaseToken); + // The proration mode value has to match one of the following declared in + // https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.ProrationMode + subscriptionUpdateParamsBuilder.setReplaceProrationMode(prorationMode); + paramsBuilder.setSubscriptionUpdateParams(subscriptionUpdateParamsBuilder.build()); } - // The proration mode value has to match one of the following declared in - // https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.ProrationMode - paramsBuilder.setReplaceSkusProrationMode(prorationMode); result.success( Translator.fromBillingResult( billingClient.launchBillingFlow(activity, paramsBuilder.build()))); @@ -286,14 +300,30 @@ public void onConsumeResponse(BillingResult billingResult, String outToken) { billingClient.consumeAsync(params, listener); } - private void queryPurchases(String skuType, MethodChannel.Result result) { + private void queryPurchasesAsync(String skuType, MethodChannel.Result result) { if (billingClientError(result)) { return; } // Like in our connect call, consider the billing client responding a "success" here regardless // of status code. - result.success(fromPurchasesResult(billingClient.queryPurchases(skuType))); + QueryPurchasesParams.Builder paramsBuilder = QueryPurchasesParams.newBuilder(); + paramsBuilder.setProductType(skuType); + billingClient.queryPurchasesAsync( + paramsBuilder.build(), + new PurchasesResponseListener() { + @Override + public void onQueryPurchasesResponse( + BillingResult billingResult, List purchasesList) { + final Map serialized = new HashMap<>(); + // The response code is no longer passed, as part of billing 4.0, so we pass OK here + // as success is implied by calling this callback. + serialized.put("responseCode", BillingClient.BillingResponseCode.OK); + serialized.put("billingResult", Translator.fromBillingResult(billingResult)); + serialized.put("purchaseList", fromPurchasesList(purchasesList)); + result.success(serialized); + } + }); } private void queryPurchaseHistoryAsync(String skuType, final MethodChannel.Result result) { @@ -302,7 +332,7 @@ private void queryPurchaseHistoryAsync(String skuType, final MethodChannel.Resul } billingClient.queryPurchaseHistoryAsync( - skuType, + QueryPurchaseHistoryParams.newBuilder().setProductType(skuType).build(), new PurchaseHistoryResponseListener() { @Override public void onPurchaseHistoryResponse( @@ -316,6 +346,15 @@ public void onPurchaseHistoryResponse( }); } + private void getConnectionState(final MethodChannel.Result result) { + if (billingClientError(result)) { + return; + } + final Map serialized = new HashMap<>(); + serialized.put("connectionState", billingClient.getConnectionState()); + result.success(serialized); + } + private void startConnection(final int handle, final MethodChannel.Result result) { if (billingClient == null) { billingClient = billingClientFactory.createBillingClient(applicationContext, methodChannel); 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 7546fe7db58d..5a0cf6ea3727 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 @@ -8,7 +8,6 @@ import com.android.billingclient.api.AccountIdentifiers; import com.android.billingclient.api.BillingResult; import com.android.billingclient.api.Purchase; -import com.android.billingclient.api.Purchase.PurchasesResult; import com.android.billingclient.api.PurchaseHistoryRecord; import com.android.billingclient.api.SkuDetails; import java.util.ArrayList; @@ -56,17 +55,19 @@ static List> fromSkuDetailsList( static HashMap fromPurchase(Purchase purchase) { HashMap info = new HashMap<>(); + List skus = purchase.getSkus(); info.put("orderId", purchase.getOrderId()); info.put("packageName", purchase.getPackageName()); info.put("purchaseTime", purchase.getPurchaseTime()); info.put("purchaseToken", purchase.getPurchaseToken()); info.put("signature", purchase.getSignature()); - info.put("sku", purchase.getSku()); + info.put("skus", skus); info.put("isAutoRenewing", purchase.isAutoRenewing()); info.put("originalJson", purchase.getOriginalJson()); info.put("developerPayload", purchase.getDeveloperPayload()); info.put("isAcknowledged", purchase.isAcknowledged()); info.put("purchaseState", purchase.getPurchaseState()); + info.put("quantity", purchase.getQuantity()); AccountIdentifiers accountIdentifiers = purchase.getAccountIdentifiers(); if (accountIdentifiers != null) { info.put("obfuscatedAccountId", accountIdentifiers.getObfuscatedAccountId()); @@ -78,12 +79,14 @@ static HashMap fromPurchase(Purchase purchase) { static HashMap fromPurchaseHistoryRecord( PurchaseHistoryRecord purchaseHistoryRecord) { HashMap info = new HashMap<>(); + List skus = purchaseHistoryRecord.getSkus(); info.put("purchaseTime", purchaseHistoryRecord.getPurchaseTime()); info.put("purchaseToken", purchaseHistoryRecord.getPurchaseToken()); info.put("signature", purchaseHistoryRecord.getSignature()); - info.put("sku", purchaseHistoryRecord.getSku()); + info.put("skus", skus); info.put("developerPayload", purchaseHistoryRecord.getDeveloperPayload()); info.put("originalJson", purchaseHistoryRecord.getOriginalJson()); + info.put("quantity", purchaseHistoryRecord.getQuantity()); return info; } @@ -112,14 +115,6 @@ static List> fromPurchaseHistoryRecordList( return serialized; } - static HashMap fromPurchasesResult(PurchasesResult purchasesResult) { - HashMap info = new HashMap<>(); - info.put("responseCode", purchasesResult.getResponseCode()); - info.put("billingResult", fromBillingResult(purchasesResult.getBillingResult())); - info.put("purchasesList", fromPurchasesList(purchasesResult.getPurchasesList())); - return info; - } - static HashMap fromBillingResult(BillingResult billingResult) { HashMap info = new HashMap<>(); info.put("responseCode", billingResult.getResponseCode()); 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 d676bf3436ee..e99ff46dd2cc 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 @@ -13,21 +13,19 @@ import static io.flutter.plugins.inapppurchase.InAppPurchasePlugin.MethodNames.LAUNCH_PRICE_CHANGE_CONFIRMATION_FLOW; import static io.flutter.plugins.inapppurchase.InAppPurchasePlugin.MethodNames.ON_DISCONNECT; import static io.flutter.plugins.inapppurchase.InAppPurchasePlugin.MethodNames.ON_PURCHASES_UPDATED; -import static io.flutter.plugins.inapppurchase.InAppPurchasePlugin.MethodNames.QUERY_PURCHASES; +import static io.flutter.plugins.inapppurchase.InAppPurchasePlugin.MethodNames.QUERY_PURCHASES_ASYNC; import static io.flutter.plugins.inapppurchase.InAppPurchasePlugin.MethodNames.QUERY_PURCHASE_HISTORY_ASYNC; import static io.flutter.plugins.inapppurchase.InAppPurchasePlugin.MethodNames.QUERY_SKU_DETAILS; import static io.flutter.plugins.inapppurchase.InAppPurchasePlugin.MethodNames.START_CONNECTION; import static io.flutter.plugins.inapppurchase.Translator.fromBillingResult; import static io.flutter.plugins.inapppurchase.Translator.fromPurchaseHistoryRecordList; import static io.flutter.plugins.inapppurchase.Translator.fromPurchasesList; -import static io.flutter.plugins.inapppurchase.Translator.fromPurchasesResult; import static io.flutter.plugins.inapppurchase.Translator.fromSkuDetailsList; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static java.util.Collections.unmodifiableList; import static java.util.stream.Collectors.toList; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.contains; @@ -56,9 +54,9 @@ import com.android.billingclient.api.PriceChangeConfirmationListener; import com.android.billingclient.api.PriceChangeFlowParams; import com.android.billingclient.api.Purchase; -import com.android.billingclient.api.Purchase.PurchasesResult; import com.android.billingclient.api.PurchaseHistoryRecord; import com.android.billingclient.api.PurchaseHistoryResponseListener; +import com.android.billingclient.api.QueryPurchaseHistoryParams; import com.android.billingclient.api.SkuDetails; import com.android.billingclient.api.SkuDetailsParams; import com.android.billingclient.api.SkuDetailsResponseListener; @@ -294,7 +292,6 @@ public void launchBillingFlow_null_AccountId_do_not_crash() { ArgumentCaptor.forClass(BillingFlowParams.class); verify(mockBillingClient).launchBillingFlow(any(), billingFlowParamsCaptor.capture()); BillingFlowParams params = billingFlowParamsCaptor.getValue(); - assertEquals(params.getSku(), skuId); // Verify we pass the response code to result verify(result, never()).error(any(), any(), any()); @@ -327,8 +324,6 @@ public void launchBillingFlow_ok_null_OldSku() { ArgumentCaptor.forClass(BillingFlowParams.class); verify(mockBillingClient).launchBillingFlow(any(), billingFlowParamsCaptor.capture()); BillingFlowParams params = billingFlowParamsCaptor.getValue(); - assertEquals(params.getSku(), skuId); - assertNull(params.getOldSku()); // Verify we pass the response code to result verify(result, never()).error(any(), any(), any()); verify(result, times(1)).success(fromBillingResult(billingResult)); @@ -380,8 +375,6 @@ public void launchBillingFlow_ok_oldSku() { ArgumentCaptor.forClass(BillingFlowParams.class); verify(mockBillingClient).launchBillingFlow(any(), billingFlowParamsCaptor.capture()); BillingFlowParams params = billingFlowParamsCaptor.getValue(); - assertEquals(params.getSku(), skuId); - assertEquals(params.getOldSku(), oldSkuId); // Verify we pass the response code to result verify(result, never()).error(any(), any(), any()); @@ -413,7 +406,6 @@ public void launchBillingFlow_ok_AccountId() { ArgumentCaptor.forClass(BillingFlowParams.class); verify(mockBillingClient).launchBillingFlow(any(), billingFlowParamsCaptor.capture()); BillingFlowParams params = billingFlowParamsCaptor.getValue(); - assertEquals(params.getSku(), skuId); // Verify we pass the response code to result verify(result, never()).error(any(), any(), any()); @@ -451,10 +443,6 @@ public void launchBillingFlow_ok_Proration() { ArgumentCaptor.forClass(BillingFlowParams.class); verify(mockBillingClient).launchBillingFlow(any(), billingFlowParamsCaptor.capture()); BillingFlowParams params = billingFlowParamsCaptor.getValue(); - assertEquals(params.getSku(), skuId); - assertEquals(params.getOldSku(), oldSkuId); - assertEquals(params.getOldSkuPurchaseToken(), purchaseToken); - assertEquals(params.getReplaceSkusProrationMode(), prorationMode); // Verify we pass the response code to result verify(result, never()).error(any(), any(), any()); @@ -495,6 +483,43 @@ public void launchBillingFlow_ok_Proration_with_null_OldSku() { verify(result, never()).success(any()); } + @Test + public void launchBillingFlow_ok_Full() { + // Fetch the sku details first and query the method call + String skuId = "foo"; + String oldSkuId = "oldFoo"; + String purchaseToken = "purchaseTokenFoo"; + String accountId = "account"; + int prorationMode = BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE; + queryForSkus(unmodifiableList(asList(skuId, oldSkuId))); + HashMap arguments = new HashMap<>(); + arguments.put("sku", skuId); + arguments.put("accountId", accountId); + arguments.put("oldSku", oldSkuId); + arguments.put("purchaseToken", purchaseToken); + arguments.put("prorationMode", prorationMode); + MethodCall launchCall = new MethodCall(LAUNCH_BILLING_FLOW, arguments); + + // Launch the billing flow + BillingResult billingResult = + BillingResult.newBuilder() + .setResponseCode(100) + .setDebugMessage("dummy debug message") + .build(); + when(mockBillingClient.launchBillingFlow(any(), any())).thenReturn(billingResult); + methodChannelHandler.onMethodCall(launchCall, result); + + // Verify we pass the arguments to the billing flow + ArgumentCaptor billingFlowParamsCaptor = + ArgumentCaptor.forClass(BillingFlowParams.class); + verify(mockBillingClient).launchBillingFlow(any(), billingFlowParamsCaptor.capture()); + BillingFlowParams params = billingFlowParamsCaptor.getValue(); + + // Verify we pass the response code to result + verify(result, never()).error(any(), any(), any()); + verify(result, times(1)).success(fromBillingResult(billingResult)); + } + @Test public void launchBillingFlow_clientDisconnected() { // Prepare the launch call after disconnecting the client @@ -553,31 +578,6 @@ public void launchBillingFlow_oldSkuNotFound() { verify(result, never()).success(any()); } - @Test - public void queryPurchases() { - establishConnectedBillingClient(null, null); - PurchasesResult purchasesResult = mock(PurchasesResult.class); - Purchase purchase = buildPurchase("foo"); - when(purchasesResult.getPurchasesList()).thenReturn(asList(purchase)); - BillingResult billingResult = - BillingResult.newBuilder() - .setResponseCode(100) - .setDebugMessage("dummy debug message") - .build(); - when(purchasesResult.getBillingResult()).thenReturn(billingResult); - when(mockBillingClient.queryPurchases(SkuType.INAPP)).thenReturn(purchasesResult); - - HashMap arguments = new HashMap<>(); - arguments.put("skuType", SkuType.INAPP); - methodChannelHandler.onMethodCall(new MethodCall(QUERY_PURCHASES, arguments), result); - - // Verify we pass the response to result - ArgumentCaptor> resultCaptor = ArgumentCaptor.forClass(HashMap.class); - verify(result, never()).error(any(), any(), any()); - verify(result, times(1)).success(resultCaptor.capture()); - assertEquals(fromPurchasesResult(purchasesResult), resultCaptor.getValue()); - } - @Test public void queryPurchases_clientDisconnected() { // Prepare the launch call after disconnecting the client @@ -585,7 +585,7 @@ public void queryPurchases_clientDisconnected() { HashMap arguments = new HashMap<>(); arguments.put("skuType", SkuType.INAPP); - methodChannelHandler.onMethodCall(new MethodCall(QUERY_PURCHASES, arguments), result); + methodChannelHandler.onMethodCall(new MethodCall(QUERY_PURCHASES_ASYNC, arguments), result); // Assert that we sent an error back. verify(result).error(contains("UNAVAILABLE"), contains("BillingClient"), any()); @@ -613,7 +613,7 @@ public void queryPurchaseHistoryAsync() { // Verify we pass the data to result verify(mockBillingClient) - .queryPurchaseHistoryAsync(eq(SkuType.INAPP), listenerCaptor.capture()); + .queryPurchaseHistoryAsync(any(QueryPurchaseHistoryParams.class), listenerCaptor.capture()); listenerCaptor.getValue().onPurchaseHistoryResponse(billingResult, purchasesList); verify(result).success(resultCaptor.capture()); HashMap resultData = resultCaptor.getValue(); diff --git a/packages/in_app_purchase/in_app_purchase_android/android/src/test/java/io/flutter/plugins/inapppurchase/TranslatorTest.java b/packages/in_app_purchase/in_app_purchase_android/android/src/test/java/io/flutter/plugins/inapppurchase/TranslatorTest.java index 2837dceea652..79852e7e8ca5 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/src/test/java/io/flutter/plugins/inapppurchase/TranslatorTest.java +++ b/packages/in_app_purchase/in_app_purchase_android/android/src/test/java/io/flutter/plugins/inapppurchase/TranslatorTest.java @@ -9,15 +9,12 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import androidx.annotation.NonNull; import com.android.billingclient.api.AccountIdentifiers; import com.android.billingclient.api.BillingClient; import com.android.billingclient.api.BillingResult; import com.android.billingclient.api.Purchase; -import com.android.billingclient.api.Purchase.PurchasesResult; import com.android.billingclient.api.PurchaseHistoryRecord; import com.android.billingclient.api.SkuDetails; import java.util.Arrays; @@ -138,37 +135,6 @@ public void fromPurchasesList_null() { assertEquals(Collections.emptyList(), Translator.fromPurchasesList(null)); } - @Test - public void fromPurchasesResult() throws JSONException { - PurchasesResult result = mock(PurchasesResult.class); - final String purchase2Json = - "{\"orderId\":\"foo2\",\"packageName\":\"bar\",\"productId\":\"consumable\",\"purchaseTime\":11111111,\"purchaseState\":0,\"purchaseToken\":\"baz\",\"developerPayload\":\"dummy payload\",\"isAcknowledged\":\"true\"}"; - final String signature = "signature"; - final List expectedPurchases = - Arrays.asList( - new Purchase(PURCHASE_EXAMPLE_JSON, signature), new Purchase(purchase2Json, signature)); - when(result.getPurchasesList()).thenReturn(expectedPurchases); - when(result.getResponseCode()).thenReturn(BillingClient.BillingResponseCode.OK); - BillingResult newBillingResult = - BillingResult.newBuilder() - .setDebugMessage("dummy debug message") - .setResponseCode(BillingClient.BillingResponseCode.OK) - .build(); - when(result.getBillingResult()).thenReturn(newBillingResult); - final HashMap serialized = Translator.fromPurchasesResult(result); - - assertEquals(BillingClient.BillingResponseCode.OK, serialized.get("responseCode")); - List> serializedPurchases = - (List>) serialized.get("purchasesList"); - assertEquals(expectedPurchases.size(), serializedPurchases.size()); - assertSerialized(expectedPurchases.get(0), serializedPurchases.get(0)); - assertSerialized(expectedPurchases.get(1), serializedPurchases.get(1)); - - Map billingResultMap = (Map) serialized.get("billingResult"); - assertEquals(billingResultMap.get("responseCode"), newBillingResult.getResponseCode()); - assertEquals(billingResultMap.get("debugMessage"), newBillingResult.getDebugMessage()); - } - @Test public void fromBillingResult() throws JSONException { BillingResult newBillingResult = @@ -232,7 +198,7 @@ private void assertSerialized(Purchase expected, Map serialized) assertEquals(expected.getPurchaseToken(), serialized.get("purchaseToken")); assertEquals(expected.getSignature(), serialized.get("signature")); assertEquals(expected.getOriginalJson(), serialized.get("originalJson")); - assertEquals(expected.getSku(), serialized.get("sku")); + assertEquals(expected.getSkus(), serialized.get("skus")); assertEquals(expected.getDeveloperPayload(), serialized.get("developerPayload")); assertEquals(expected.isAcknowledged(), serialized.get("isAcknowledged")); assertEquals(expected.getPurchaseState(), serialized.get("purchaseState")); @@ -251,7 +217,7 @@ private void assertSerialized(PurchaseHistoryRecord expected, Map map) => @@ -104,8 +105,13 @@ class PurchaseWrapper { final String signature; /// The product ID of this purchase. - @JsonKey(defaultValue: '') - final String sku; + @Deprecated('Use skus instead') + String get sku => _sku ?? (skus.isNotEmpty ? skus.first : ''); + final String? _sku; + + /// The product IDs of this purchase. + @JsonKey(defaultValue: []) + final List skus; /// True for subscriptions that renew automatically. Does not apply to /// [SkuType.inapp] products. @@ -178,10 +184,11 @@ class PurchaseHistoryRecordWrapper { required this.purchaseTime, required this.purchaseToken, required this.signature, - required this.sku, + @Deprecated('Use skus instead') String? sku, + required this.skus, required this.originalJson, required this.developerPayload, - }); + }) : _sku = sku; /// Factory for creating a [PurchaseHistoryRecordWrapper] from a [Map] with the record details. factory PurchaseHistoryRecordWrapper.fromJson(Map map) => @@ -201,8 +208,14 @@ class PurchaseHistoryRecordWrapper { final String signature; /// The product ID of this purchase. - @JsonKey(defaultValue: '') - final String sku; + @Deprecated('Use skus instead') + String get sku => _sku ?? (skus.isNotEmpty ? skus.first : ''); + + final String? _sku; + + /// The product ID of this purchase. + @JsonKey(defaultValue: []) + final List skus; /// Details about this purchase, in JSON. /// diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.g.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.g.dart index 5815a866c82d..7f6fd0f61d94 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.g.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.g.dart @@ -12,7 +12,10 @@ PurchaseWrapper _$PurchaseWrapperFromJson(Map json) => PurchaseWrapper( purchaseTime: json['purchaseTime'] as int? ?? 0, purchaseToken: json['purchaseToken'] as String? ?? '', signature: json['signature'] as String? ?? '', - sku: json['sku'] as String? ?? '', + skus: json['skus'] != null + ? (json['skus'] as List)?.map((item) => item as String)?.toList() ?? + [] + : [], isAutoRenewing: json['isAutoRenewing'] as bool, originalJson: json['originalJson'] as String? ?? '', developerPayload: json['developerPayload'] as String?, @@ -28,7 +31,10 @@ PurchaseHistoryRecordWrapper _$PurchaseHistoryRecordWrapperFromJson(Map json) => purchaseTime: json['purchaseTime'] as int? ?? 0, purchaseToken: json['purchaseToken'] as String? ?? '', signature: json['signature'] as String? ?? '', - sku: json['sku'] as String? ?? '', + skus: json['skus'] != null + ? (json['skus'] as List)?.map((item) => item as String)?.toList() ?? + [] + : [], originalJson: json['originalJson'] as String? ?? '', developerPayload: json['developerPayload'] as String?, ); 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 b1e77666a0b5..db419b8e1aa4 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/plugins/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.2.2+8 +version: 0.2.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/purchase_wrapper_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/purchase_wrapper_test.dart index 65c8bb213cc4..184d9331e6c1 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/purchase_wrapper_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/billing_client_wrappers/purchase_wrapper_test.dart @@ -11,7 +11,7 @@ const PurchaseWrapper dummyPurchase = PurchaseWrapper( packageName: 'packageName', purchaseTime: 0, signature: 'signature', - sku: 'sku', + skus: ['sku'], purchaseToken: 'purchaseToken', isAutoRenewing: false, originalJson: '', @@ -27,7 +27,7 @@ const PurchaseWrapper dummyUnacknowledgedPurchase = PurchaseWrapper( packageName: 'packageName', purchaseTime: 0, signature: 'signature', - sku: 'sku', + skus: ['sku'], purchaseToken: 'purchaseToken', isAutoRenewing: false, originalJson: '', @@ -40,7 +40,7 @@ const PurchaseHistoryRecordWrapper dummyPurchaseHistoryRecord = PurchaseHistoryRecordWrapper( purchaseTime: 0, signature: 'signature', - sku: 'sku', + skus: ['sku'], purchaseToken: 'purchaseToken', originalJson: '', developerPayload: 'dummy payload', @@ -51,7 +51,7 @@ const PurchaseWrapper dummyOldPurchase = PurchaseWrapper( packageName: 'oldPackageName', purchaseTime: 0, signature: 'oldSignature', - sku: 'oldSku', + skus: ['oldSku'], purchaseToken: 'oldPurchaseToken', isAutoRenewing: false, originalJson: '', @@ -205,7 +205,7 @@ Map buildPurchaseMap(PurchaseWrapper original) { 'packageName': original.packageName, 'purchaseTime': original.purchaseTime, 'signature': original.signature, - 'sku': original.sku, + 'skus': original.skus, 'purchaseToken': original.purchaseToken, 'isAutoRenewing': original.isAutoRenewing, 'originalJson': original.originalJson, @@ -223,7 +223,7 @@ Map buildPurchaseHistoryRecordMap( return { 'purchaseTime': original.purchaseTime, 'signature': original.signature, - 'sku': original.sku, + 'skus': original.skus, 'purchaseToken': original.purchaseToken, 'originalJson': original.originalJson, 'developerPayload': original.developerPayload, diff --git a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart index b19d631092e7..4f90dccf94f4 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart @@ -299,7 +299,7 @@ void main() { 'purchasesList': [ { 'orderId': 'orderID1', - 'sku': skuDetails.sku, + 'skus': [skuDetails.sku], 'isAutoRenewing': false, 'packageName': 'package', 'purchaseTime': 1231231231, @@ -401,7 +401,7 @@ void main() { 'purchasesList': [ { 'orderId': 'orderID1', - 'sku': skuDetails.sku, + 'skus': [skuDetails.sku], 'isAutoRenewing': false, 'packageName': 'package', 'purchaseTime': 1231231231, @@ -515,7 +515,7 @@ void main() { 'purchasesList': [ { 'orderId': 'orderID1', - 'sku': skuDetails.sku, + 'skus': [skuDetails.sku], 'isAutoRenewing': false, 'packageName': 'package', 'purchaseTime': 1231231231, @@ -592,7 +592,7 @@ void main() { 'purchasesList': [ { 'orderId': 'orderID1', - 'sku': skuDetails.sku, + 'skus': [skuDetails.sku], 'isAutoRenewing': false, 'packageName': 'package', 'purchaseTime': 1231231231, From 8ed5e3f3ce0c5099031f156693ce35638676652e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 9 Jul 2022 01:07:06 +0000 Subject: [PATCH 487/844] [espresso]: Bump okhttp from 3.12.1 to 4.10.0 in /packages/espresso/android (#5955) --- packages/espresso/CHANGELOG.md | 4 ++++ packages/espresso/android/build.gradle | 2 +- packages/espresso/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/espresso/CHANGELOG.md b/packages/espresso/CHANGELOG.md index dad0a912e174..8f41a8ddb2ae 100644 --- a/packages/espresso/CHANGELOG.md +++ b/packages/espresso/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.0+3 + +* Bumps okhttp to 4.10.0. + ## 0.2.0+2 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/espresso/android/build.gradle b/packages/espresso/android/build.gradle index 6b003f9befde..7fafa0b309ec 100644 --- a/packages/espresso/android/build.gradle +++ b/packages/espresso/android/build.gradle @@ -50,7 +50,7 @@ android { dependencies { implementation 'com.google.guava:guava:31.1-android' - implementation 'com.squareup.okhttp3:okhttp:3.12.1' + implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'com.google.code.gson:gson:2.8.6' androidTestImplementation 'org.hamcrest:hamcrest:2.2' diff --git a/packages/espresso/pubspec.yaml b/packages/espresso/pubspec.yaml index 36b557029f3a..644d0ab5980a 100644 --- a/packages/espresso/pubspec.yaml +++ b/packages/espresso/pubspec.yaml @@ -3,7 +3,7 @@ description: Java classes for testing Flutter apps using Espresso. Allows driving Flutter widgets from a native Espresso test. repository: https://github.com/flutter/plugins/tree/main/packages/espresso issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+espresso%22 -version: 0.2.0+2 +version: 0.2.0+3 environment: sdk: ">=2.12.0 <3.0.0" From db466d696e6ce2e48d7d244b23290baffd8b098b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Jul 2022 15:39:06 +0000 Subject: [PATCH 488/844] [video_player]: Bump gradle from 3.5.0 to 7.2.1 in /packages/video_player/video_player_android/android (#5841) --- packages/video_player/video_player_android/CHANGELOG.md | 3 ++- .../video_player/video_player_android/android/build.gradle | 2 +- packages/video_player/video_player_android/pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 920c9102e6b2..0c839961d6f7 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.3.7 +* Bumps gradle version to 7.2.1. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). ## 2.3.6 diff --git a/packages/video_player/video_player_android/android/build.gradle b/packages/video_player/video_player_android/android/build.gradle index f6b559408a7d..8c1aaaf5f923 100644 --- a/packages/video_player/video_player_android/android/build.gradle +++ b/packages/video_player/video_player_android/android/build.gradle @@ -9,7 +9,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:7.2.1' } } diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index 0b630467ac05..f11a74ec1ec0 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.6 +version: 2.3.7 environment: sdk: ">=2.14.0 <3.0.0" From 50a25336935b91a1ff1369dda83c607391f824b1 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 11 Jul 2022 11:40:10 -0400 Subject: [PATCH 489/844] Roll Flutter from 78e3b93664c7 to d59923b2e260 (49 revisions) (#6090) --- .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 b0a8d034304b..ab71b70942fc 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -78e3b93664c7784d806fe00080bd800f30cec662 +d59923b2e260ded719f6d9eda21c4db62258738d From 68cdc58aa67b82209c9eeb832d3e95c9734e3983 Mon Sep 17 00:00:00 2001 From: TabooSun Date: Tue, 12 Jul 2022 01:23:04 +0800 Subject: [PATCH 490/844] [quick_actions] Android handle quick action without restart (#5048) --- .../quick_actions_android/CHANGELOG.md | 4 + .../android/src/main/AndroidManifest.xml | 6 +- .../quickactions/MethodCallHandlerImpl.java | 5 +- .../quickactions/QuickActionsPlugin.java | 14 +- .../quickactions/QuickActionsTest.java | 16 +++ .../example/android/app/build.gradle | 10 +- .../quickactionsexample/QuickActionsTest.java | 133 +++++++++++++++++- .../integration_test/quick_actions_test.dart | 20 ++- .../example/lib/main.dart | 2 +- .../quick_actions_android/pubspec.yaml | 2 +- 10 files changed, 190 insertions(+), 22 deletions(-) diff --git a/packages/quick_actions/quick_actions_android/CHANGELOG.md b/packages/quick_actions/quick_actions_android/CHANGELOG.md index 35f9c9fd51d9..5b5f70946e91 100644 --- a/packages/quick_actions/quick_actions_android/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.1 + +* Allows Android to trigger quick actions without restarting the app. + ## 0.6.0+11 * Updates references to the obsolete master branch. diff --git a/packages/quick_actions/quick_actions_android/android/src/main/AndroidManifest.xml b/packages/quick_actions/quick_actions_android/android/src/main/AndroidManifest.xml index 5b02f6d8aef2..5ec81f08ec6a 100644 --- a/packages/quick_actions/quick_actions_android/android/src/main/AndroidManifest.xml +++ b/packages/quick_actions/quick_actions_android/android/src/main/AndroidManifest.xml @@ -1,4 +1,6 @@ + xmlns:tools="http://schemas.android.com/tools" + package="io.flutter.plugins.quickactions"> + + diff --git a/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/MethodCallHandlerImpl.java b/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/MethodCallHandlerImpl.java index 6316e8428288..96b141fb9c31 100644 --- a/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/MethodCallHandlerImpl.java +++ b/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/MethodCallHandlerImpl.java @@ -74,7 +74,8 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) { final boolean didSucceed = dynamicShortcutsSet; - // TODO(camsim99): Move re-dispatch below to background thread when Flutter 2.8+ is stable. + // TODO(camsim99): Move re-dispatch below to background thread when Flutter 2.8+ is + // stable. uiThreadExecutor.execute( () -> { if (didSucceed) { @@ -163,7 +164,7 @@ private Intent getIntentToOpenMainActivity(String type) { .setAction(Intent.ACTION_RUN) .putExtra(EXTRA_ACTION, type) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); } private static class UiThreadExecutor implements Executor { diff --git a/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActionsPlugin.java b/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActionsPlugin.java index 99ce0f8426a0..b41087816889 100644 --- a/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActionsPlugin.java +++ b/packages/quick_actions/quick_actions_android/android/src/main/java/io/flutter/plugins/quickactions/QuickActionsPlugin.java @@ -7,6 +7,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.content.pm.ShortcutManager; import android.os.Build; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.activity.ActivityAware; @@ -21,6 +22,7 @@ public class QuickActionsPlugin implements FlutterPlugin, ActivityAware, NewInte private MethodChannel channel; private MethodCallHandlerImpl handler; + private Activity activity; /** * Plugin registration. @@ -45,9 +47,10 @@ public void onDetachedFromEngine(FlutterPluginBinding binding) { @Override public void onAttachedToActivity(ActivityPluginBinding binding) { - handler.setActivity(binding.getActivity()); + activity = binding.getActivity(); + handler.setActivity(activity); binding.addOnNewIntentListener(this); - onNewIntent(binding.getActivity().getIntent()); + onNewIntent(activity.getIntent()); } @Override @@ -74,7 +77,12 @@ public boolean onNewIntent(Intent intent) { } // Notify the Dart side if the launch intent has the intent extra relevant to quick actions. if (intent.hasExtra(MethodCallHandlerImpl.EXTRA_ACTION) && channel != null) { - channel.invokeMethod("launch", intent.getStringExtra(MethodCallHandlerImpl.EXTRA_ACTION)); + Context context = activity.getApplicationContext(); + ShortcutManager shortcutManager = + (ShortcutManager) context.getSystemService(Context.SHORTCUT_SERVICE); + String shortcutId = intent.getStringExtra(MethodCallHandlerImpl.EXTRA_ACTION); + channel.invokeMethod("launch", shortcutId); + shortcutManager.reportShortcutUsed(shortcutId); } return false; } diff --git a/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java b/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java index d2e63b62f229..dc4b36e168db 100644 --- a/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java +++ b/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java @@ -13,7 +13,9 @@ import static org.mockito.Mockito.when; import android.app.Activity; +import android.content.Context; import android.content.Intent; +import android.content.pm.ShortcutManager; import android.os.Build; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -86,6 +88,11 @@ public void onAttachedToActivity_buildVersionSupported_invokesLaunchMethod() when(mockMainActivity.getIntent()).thenReturn(mockIntent); final ActivityPluginBinding mockActivityPluginBinding = mock(ActivityPluginBinding.class); when(mockActivityPluginBinding.getActivity()).thenReturn(mockMainActivity); + final Context mockContext = mock(Context.class); + when(mockMainActivity.getApplicationContext()).thenReturn(mockContext); + final ShortcutManager mockShortcutManager = mock(ShortcutManager.class); + when(mockContext.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(mockShortcutManager); + plugin.onAttachedToActivity(mockActivityPluginBinding); // Act plugin.onAttachedToActivity(mockActivityPluginBinding); @@ -123,6 +130,15 @@ public void onNewIntent_buildVersionSupported_invokesLaunchMethod() setUpMessengerAndFlutterPluginBinding(testBinaryMessenger, plugin); setBuildVersion(SUPPORTED_BUILD); final Intent mockIntent = createMockIntentWithQuickActionExtra(); + final Activity mockMainActivity = mock(Activity.class); + when(mockMainActivity.getIntent()).thenReturn(mockIntent); + final ActivityPluginBinding mockActivityPluginBinding = mock(ActivityPluginBinding.class); + when(mockActivityPluginBinding.getActivity()).thenReturn(mockMainActivity); + final Context mockContext = mock(Context.class); + when(mockMainActivity.getApplicationContext()).thenReturn(mockContext); + final ShortcutManager mockShortcutManager = mock(ShortcutManager.class); + when(mockContext.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(mockShortcutManager); + plugin.onAttachedToActivity(mockActivityPluginBinding); // Act final boolean onNewIntentReturn = plugin.onNewIntent(mockIntent); diff --git a/packages/quick_actions/quick_actions_android/example/android/app/build.gradle b/packages/quick_actions/quick_actions_android/example/android/app/build.gradle index 75fe3543e987..75920e00fcab 100644 --- a/packages/quick_actions/quick_actions_android/example/android/app/build.gradle +++ b/packages/quick_actions/quick_actions_android/example/android/app/build.gradle @@ -24,6 +24,8 @@ if (flutterVersionName == null) { apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" +def androidXTestVersion = '1.2.0' + android { compileSdkVersion 31 @@ -55,5 +57,11 @@ 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.2.0' + api "androidx.test:core:$androidXTestVersion" + + androidTestImplementation "androidx.test:runner:$androidXTestVersion" + androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' + androidTestImplementation 'androidx.test.ext:junit:1.0.0' + androidTestImplementation 'org.mockito:mockito-core:4.3.1' + androidTestImplementation 'org.mockito:mockito-android:4.3.1' } diff --git a/packages/quick_actions/quick_actions_android/example/android/app/src/androidTest/java/io/flutter/plugins/quickactionsexample/QuickActionsTest.java b/packages/quick_actions/quick_actions_android/example/android/app/src/androidTest/java/io/flutter/plugins/quickactionsexample/QuickActionsTest.java index 9d2fed13fc27..8b50fd7a90eb 100644 --- a/packages/quick_actions/quick_actions_android/example/android/app/src/androidTest/java/io/flutter/plugins/quickactionsexample/QuickActionsTest.java +++ b/packages/quick_actions/quick_actions_android/example/android/app/src/androidTest/java/io/flutter/plugins/quickactionsexample/QuickActionsTest.java @@ -4,15 +4,55 @@ package io.flutter.plugins.quickactionsexample; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; +import android.util.Log; +import androidx.lifecycle.Lifecycle; import androidx.test.core.app.ActivityScenario; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.Until; import io.flutter.plugins.quickactions.QuickActionsPlugin; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +@RunWith(AndroidJUnit4.class) public class QuickActionsTest { + private Context context; + private UiDevice device; + private ActivityScenario scenario; + + @Before + public void setUp() { + context = ApplicationProvider.getApplicationContext(); + device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + scenario = ensureAppRunToView(); + ensureAllAppShortcutsAreCreated(); + } + + @After + public void tearDown() { + scenario.close(); + Log.i(QuickActionsTest.class.getSimpleName(), "Run to completion"); + } + @Test - public void imagePickerPluginIsAdded() { + public void quickActionPluginIsAdded() { final ActivityScenario scenario = ActivityScenario.launch(QuickActionsTestActivity.class); scenario.onActivity( @@ -20,4 +60,95 @@ public void imagePickerPluginIsAdded() { assertTrue(activity.engine.getPlugins().has(QuickActionsPlugin.class)); }); } + + @Test + public void appShortcutsAreCreated() { + List expectedShortcuts = createMockShortcuts(); + + ShortcutManager shortcutManager = + (ShortcutManager) context.getSystemService(Context.SHORTCUT_SERVICE); + List dynamicShortcuts = shortcutManager.getDynamicShortcuts(); + + // Assert the app shortcuts defined in ../lib/main.dart. + assertFalse(dynamicShortcuts.isEmpty()); + assertEquals(expectedShortcuts.size(), dynamicShortcuts.size()); + for (ShortcutInfo expectedShortcut : expectedShortcuts) { + ShortcutInfo dynamicShortcut = + dynamicShortcuts + .stream() + .filter(s -> s.getId().equals(expectedShortcut.getId())) + .findFirst() + .get(); + + assertEquals(expectedShortcut.getShortLabel(), dynamicShortcut.getShortLabel()); + assertEquals(expectedShortcut.getLongLabel(), dynamicShortcut.getLongLabel()); + } + } + + @Test + public void appShortcutLaunchActivityAfterStarting() { + // Arrange + List shortcuts = createMockShortcuts(); + ShortcutInfo firstShortcut = shortcuts.get(0); + ShortcutManager shortcutManager = + (ShortcutManager) context.getSystemService(Context.SHORTCUT_SERVICE); + List dynamicShortcuts = shortcutManager.getDynamicShortcuts(); + ShortcutInfo dynamicShortcut = + dynamicShortcuts + .stream() + .filter(s -> s.getId().equals(firstShortcut.getId())) + .findFirst() + .get(); + Intent dynamicShortcutIntent = dynamicShortcut.getIntent(); + AtomicReference initialActivity = new AtomicReference<>(); + scenario.onActivity(initialActivity::set); + String appReadySentinel = " has launched"; + + // Act + context.startActivity(dynamicShortcutIntent); + device.wait(Until.hasObject(By.descContains(appReadySentinel)), 2000); + AtomicReference currentActivity = new AtomicReference<>(); + scenario.onActivity(currentActivity::set); + + // Assert + Assert.assertTrue( + "AppShortcut:" + firstShortcut.getId() + " does not launch the correct activity", + // We can only find the shortcut type in content description while inspecting it in Ui + // Automator Viewer. + device.hasObject(By.desc(firstShortcut.getId() + appReadySentinel))); + // This is Android SingleTop behavior in which Android does not destroy the initial activity and + // launch a new activity. + Assert.assertEquals(initialActivity.get(), currentActivity.get()); + } + + private void ensureAllAppShortcutsAreCreated() { + device.wait(Until.hasObject(By.text("actions ready")), 1000); + } + + private List createMockShortcuts() { + List expectedShortcuts = new ArrayList<>(); + + String actionOneLocalizedTitle = "Action one"; + expectedShortcuts.add( + new ShortcutInfo.Builder(context, "action_one") + .setShortLabel(actionOneLocalizedTitle) + .setLongLabel(actionOneLocalizedTitle) + .build()); + + String actionTwoLocalizedTitle = "Action two"; + expectedShortcuts.add( + new ShortcutInfo.Builder(context, "action_two") + .setShortLabel(actionTwoLocalizedTitle) + .setLongLabel(actionTwoLocalizedTitle) + .build()); + + return expectedShortcuts; + } + + private ActivityScenario ensureAppRunToView() { + final ActivityScenario scenario = + ActivityScenario.launch(QuickActionsTestActivity.class); + scenario.moveToState(Lifecycle.State.STARTED); + return scenario; + } } diff --git a/packages/quick_actions/quick_actions_android/example/integration_test/quick_actions_test.dart b/packages/quick_actions/quick_actions_android/example/integration_test/quick_actions_test.dart index f9c42ad109e7..e0abe90f75aa 100644 --- a/packages/quick_actions/quick_actions_android/example/integration_test/quick_actions_test.dart +++ b/packages/quick_actions/quick_actions_android/example/integration_test/quick_actions_test.dart @@ -2,23 +2,21 @@ // 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:integration_test/integration_test.dart'; -import 'package:quick_actions_platform_interface/quick_actions_platform_interface.dart'; +import 'package:quick_actions_example/main.dart' as app; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - testWidgets('Can set shortcuts', (WidgetTester tester) async { - final QuickActionsPlatform quickActions = QuickActionsPlatform.instance; - await quickActions.initialize((String value) {}); + testWidgets('Can run MyApp', (WidgetTester tester) async { + app.main(); - const ShortcutItem shortCutItem = ShortcutItem( - type: 'action_one', - localizedTitle: 'Action one', - icon: 'AppIcon', - ); - expect( - quickActions.setShortcutItems([shortCutItem]), completes); + await tester.pumpAndSettle(); + await tester.pump(const Duration(seconds: 1)); + + expect(find.byType(Text), findsWidgets); + expect(find.byType(app.MyHomePage), findsOneWidget); }); } diff --git a/packages/quick_actions/quick_actions_android/example/lib/main.dart b/packages/quick_actions/quick_actions_android/example/lib/main.dart index d8b7832bf9dc..8f66e69ffb4e 100644 --- a/packages/quick_actions/quick_actions_android/example/lib/main.dart +++ b/packages/quick_actions/quick_actions_android/example/lib/main.dart @@ -44,7 +44,7 @@ class _MyHomePageState extends State { quickActions.initialize((String shortcutType) { setState(() { if (shortcutType != null) { - shortcut = shortcutType; + shortcut = '$shortcutType has launched'; } }); }); diff --git a/packages/quick_actions/quick_actions_android/pubspec.yaml b/packages/quick_actions/quick_actions_android/pubspec.yaml index fa39e36b4fab..7cb274116536 100644 --- a/packages/quick_actions/quick_actions_android/pubspec.yaml +++ b/packages/quick_actions/quick_actions_android/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_android description: An implementation for the Android platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.6.0+11 +version: 0.6.1 environment: sdk: ">=2.15.0 <3.0.0" From bee370927a2f70af88c958c8539719216b877d2f Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 11 Jul 2022 16:43:04 -0400 Subject: [PATCH 491/844] [in_app_purchase] Update json_serializable (#6092) --- .../in_app_purchase_android/CHANGELOG.md | 4 ++++ .../billing_client_wrappers/purchase_wrapper.dart | 2 ++ .../purchase_wrapper.g.dart | 14 ++++++-------- .../in_app_purchase_android/pubspec.yaml | 6 +++--- 4 files changed, 15 insertions(+), 11 deletions(-) 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 2f37f09478c8..6f476436b3d6 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.3+1 + +* Updates `json_serializable` to fix warnings in generated code. + ## 0.2.3 * Upgrades Google Play Billing Library to 5.0 diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.dart index af1aaa5d556a..4e6b953096e2 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.dart @@ -106,6 +106,7 @@ class PurchaseWrapper { /// The product ID of this purchase. @Deprecated('Use skus instead') + @JsonKey(ignore: true) String get sku => _sku ?? (skus.isNotEmpty ? skus.first : ''); final String? _sku; @@ -209,6 +210,7 @@ class PurchaseHistoryRecordWrapper { /// The product ID of this purchase. @Deprecated('Use skus instead') + @JsonKey(ignore: true) String get sku => _sku ?? (skus.isNotEmpty ? skus.first : ''); final String? _sku; diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.g.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.g.dart index 7f6fd0f61d94..ad2a909fbfdc 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.g.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/billing_client_wrappers/purchase_wrapper.g.dart @@ -12,10 +12,9 @@ PurchaseWrapper _$PurchaseWrapperFromJson(Map json) => PurchaseWrapper( purchaseTime: json['purchaseTime'] as int? ?? 0, purchaseToken: json['purchaseToken'] as String? ?? '', signature: json['signature'] as String? ?? '', - skus: json['skus'] != null - ? (json['skus'] as List)?.map((item) => item as String)?.toList() ?? - [] - : [], + skus: + (json['skus'] as List?)?.map((e) => e as String).toList() ?? + [], isAutoRenewing: json['isAutoRenewing'] as bool, originalJson: json['originalJson'] as String? ?? '', developerPayload: json['developerPayload'] as String?, @@ -31,10 +30,9 @@ PurchaseHistoryRecordWrapper _$PurchaseHistoryRecordWrapperFromJson(Map json) => purchaseTime: json['purchaseTime'] as int? ?? 0, purchaseToken: json['purchaseToken'] as String? ?? '', signature: json['signature'] as String? ?? '', - skus: json['skus'] != null - ? (json['skus'] as List)?.map((item) => item as String)?.toList() ?? - [] - : [], + skus: + (json['skus'] as List?)?.map((e) => e as String).toList() ?? + [], originalJson: json['originalJson'] as String? ?? '', developerPayload: json['developerPayload'] as String?, ); 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 db419b8e1aa4..84bd36a8096b 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/plugins/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.2.3 +version: 0.2.3+1 environment: sdk: ">=2.14.0 <3.0.0" @@ -21,12 +21,12 @@ dependencies: flutter: sdk: flutter in_app_purchase_platform_interface: ^1.3.0 - json_annotation: ^4.3.0 + json_annotation: ^4.6.0 dev_dependencies: build_runner: ^2.0.0 flutter_test: sdk: flutter - json_serializable: ^6.0.0 + json_serializable: ^6.3.1 mockito: ^5.1.0 test: ^1.16.0 From b4896801235f09f853061799af28a515dcc2d094 Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Mon, 11 Jul 2022 14:35:09 -0700 Subject: [PATCH 492/844] [camera] Update Android Camera Access Permission Error Codes (#5640) --- packages/camera/camera_android/CHANGELOG.md | 3 +- .../plugins/camera/CameraPermissions.java | 24 ++++++++-- .../plugins/camera/CameraPermissionsTest.java | 48 +++++++++++++++++++ packages/camera/camera_android/pubspec.yaml | 2 +- 4 files changed, 71 insertions(+), 6 deletions(-) diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index e3899c340571..3a743340dc6e 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.10.0 +* **Breaking Change** Updates Android camera access permission error codes to be consistent with other platforms. If your app still handles the legacy `cameraPermission` exception, please update it to handle the new permission exception codes that are noted in the README. * Ignores missing return warnings in preparation for [upcoming analysis changes](https://github.com/flutter/flutter/issues/105750). ## 0.9.8+3 diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java index 7d60e0fffa5c..4441751e19cf 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java @@ -23,8 +23,22 @@ interface ResultCallback { void onResult(String errorCode, String errorDescription); } + /** + * Camera access permission errors handled when camera is created. See {@code MethodChannelCamera} + * in {@code camera/camera_platform_interface} for details. + */ + private static final String CAMERA_PERMISSIONS_REQUEST_ONGOING = + "CameraPermissionsRequestOngoing"; + + private static final String CAMERA_PERMISSIONS_REQUEST_ONGOING_MESSAGE = + "Another request is ongoing and multiple requests cannot be handled at once."; + private static final String CAMERA_ACCESS_DENIED = "CameraAccessDenied"; + private static final String CAMERA_ACCESS_DENIED_MESSAGE = "Camera access permission was denied."; + private static final String AUDIO_ACCESS_DENIED = "AudioAccessDenied"; + private static final String AUDIO_ACCESS_DENIED_MESSAGE = "Audio access permission was denied."; + private static final int CAMERA_REQUEST_ID = 9796; - private boolean ongoing = false; + @VisibleForTesting boolean ongoing = false; void requestPermissions( Activity activity, @@ -32,7 +46,9 @@ void requestPermissions( boolean enableAudio, ResultCallback callback) { if (ongoing) { - callback.onResult("cameraPermission", "Camera permission request ongoing"); + callback.onResult( + CAMERA_PERMISSIONS_REQUEST_ONGOING, CAMERA_PERMISSIONS_REQUEST_ONGOING_MESSAGE); + return; } if (!hasCameraPermission(activity) || (enableAudio && !hasAudioPermission(activity))) { permissionsRegistry.addListener( @@ -90,9 +106,9 @@ public boolean onRequestPermissionsResult(int id, String[] permissions, int[] gr alreadyCalled = true; if (grantResults[0] != PackageManager.PERMISSION_GRANTED) { - callback.onResult("cameraPermission", "MediaRecorderCamera permission not granted"); + callback.onResult(CAMERA_ACCESS_DENIED, CAMERA_ACCESS_DENIED_MESSAGE); } else if (grantResults.length > 1 && grantResults[1] != PackageManager.PERMISSION_GRANTED) { - callback.onResult("cameraPermission", "MediaRecorderAudio permission not granted"); + callback.onResult(AUDIO_ACCESS_DENIED, AUDIO_ACCESS_DENIED_MESSAGE); } else { callback.onResult(null, null); } diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraPermissionsTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraPermissionsTest.java index ecb96a88f31a..d734a63b15ca 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraPermissionsTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraPermissionsTest.java @@ -5,9 +5,13 @@ package io.flutter.plugins.camera; import static junit.framework.TestCase.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import android.content.pm.PackageManager; import io.flutter.plugins.camera.CameraPermissions.CameraRequestPermissionsListener; +import io.flutter.plugins.camera.CameraPermissions.ResultCallback; import org.junit.Test; public class CameraPermissionsTest { @@ -24,4 +28,48 @@ public void listener_respondsOnce() { assertEquals(1, calledCounter[0]); } + + @Test + public void callback_respondsWithCameraAccessDenied() { + ResultCallback fakeResultCallback = mock(ResultCallback.class); + CameraRequestPermissionsListener permissionsListener = + new CameraRequestPermissionsListener(fakeResultCallback); + + permissionsListener.onRequestPermissionsResult( + 9796, null, new int[] {PackageManager.PERMISSION_DENIED}); + + verify(fakeResultCallback) + .onResult("CameraAccessDenied", "Camera access permission was denied."); + } + + @Test + public void callback_respondsWithAudioAccessDenied() { + ResultCallback fakeResultCallback = mock(ResultCallback.class); + CameraRequestPermissionsListener permissionsListener = + new CameraRequestPermissionsListener(fakeResultCallback); + + permissionsListener.onRequestPermissionsResult( + 9796, + null, + new int[] {PackageManager.PERMISSION_GRANTED, PackageManager.PERMISSION_DENIED}); + + verify(fakeResultCallback).onResult("AudioAccessDenied", "Audio access permission was denied."); + } + + @Test + public void callback_doesNotRespond() { + ResultCallback fakeResultCallback = mock(ResultCallback.class); + CameraRequestPermissionsListener permissionsListener = + new CameraRequestPermissionsListener(fakeResultCallback); + + permissionsListener.onRequestPermissionsResult( + 9796, + null, + new int[] {PackageManager.PERMISSION_GRANTED, PackageManager.PERMISSION_GRANTED}); + + verify(fakeResultCallback, never()) + .onResult("CameraAccessDenied", "Camera access permission was denied."); + verify(fakeResultCallback, never()) + .onResult("AudioAccessDenied", "Audio access permission was denied."); + } } diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index a1b7a930e228..581780f0d87b 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android description: Android implementation of the camera plugin. repository: https://github.com/flutter/plugins/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.9.8+3 +version: 0.10.0 environment: sdk: ">=2.14.0 <3.0.0" From ea14939cebdbe724422268bdbd4fb30ba06a179b Mon Sep 17 00:00:00 2001 From: idkq <76702881+idkq@users.noreply.github.com> Date: Mon, 11 Jul 2022 18:47:04 -0400 Subject: [PATCH 493/844] [google_maps_flutter_web ] Update README.md to discuss mouse issue when stacked (#5875) --- .../google_maps_flutter/google_maps_flutter_web/CHANGELOG.md | 4 ++++ .../google_maps_flutter/google_maps_flutter_web/README.md | 2 ++ .../google_maps_flutter/google_maps_flutter_web/pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) 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 0f040ec5696c..d86642934b77 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.4.0+1 + +* Updates `README.md` to describe a hit-testing issue when Flutter widgets are overlaid on top of the Map widget. + ## 0.4.0 * Implements the new platform interface versions of `buildView` and diff --git a/packages/google_maps_flutter/google_maps_flutter_web/README.md b/packages/google_maps_flutter/google_maps_flutter_web/README.md index 9e7ce94e3e59..692814731bec 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/README.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/README.md @@ -46,3 +46,5 @@ There's no `defaultMarkerWithHue` in web. If you need colored pins/markers, you Indoor and building layers are still not available on the web. Traffic is. Only Android supports "[Lite Mode](https://developers.google.com/maps/documentation/android-sdk/lite)", so the `liteModeEnabled` constructor argument can't be set to `true` on web apps. + +Google Maps for web uses `HtmlElementView` to render maps. When a `GoogleMap` is stacked below other widgets, [`package:pointer_interceptor`](https://www.pub.dev/packages/pointer_interceptor) must be used to capture mouse events on the Flutter overlays. See issue [#73830](https://github.com/flutter/flutter/issues/73830). 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 a05be66b83ba..9670e0f28e2e 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/plugins/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.4.0 +version: 0.4.0+1 environment: sdk: ">=2.12.0 <3.0.0" From bdec9bad73da186c30f42eb105b84e6b17eeaf31 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 12 Jul 2022 12:05:12 -0400 Subject: [PATCH 494/844] Roll Flutter from d59923b2e260 to d092601debdd (35 revisions) (#6095) --- .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 ab71b70942fc..d26a933e7a6e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -d59923b2e260ded719f6d9eda21c4db62258738d +d092601debdda8209d761dab480c7cf98d4b3ed2 From f2d795bd3aa843561d9524d244b7072eb2a323ff Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 12 Jul 2022 17:15:06 -0400 Subject: [PATCH 495/844] [webview_flutter_wkwebview] Switches the platform implementation to use the native API Wrapper (#6031) --- .../webview_flutter_wkwebview/CHANGELOG.md | 3 +- .../ios/Runner.xcodeproj/project.pbxproj | 12 - .../ios/RunnerTests/FLTCookieManagerTests.m | 127 --- .../FLTWKNavigationDelegateTests.m | 73 -- .../example/ios/RunnerTests/FLTWebViewTests.m | 833 ------------------ .../ios/Classes/FLTCookieManager.h | 20 - .../ios/Classes/FLTCookieManager.m | 105 --- .../ios/Classes/FLTCookieManager_Test.h | 20 - .../ios/Classes/FLTWKNavigationDelegate.h | 26 - .../ios/Classes/FLTWKNavigationDelegate.m | 127 --- .../ios/Classes/FLTWKProgressionDelegate.h | 19 - .../ios/Classes/FLTWKProgressionDelegate.m | 41 - .../ios/Classes/FLTWebViewFlutterPlugin.m | 106 ++- .../ios/Classes/FlutterWebView.h | 40 - .../ios/Classes/FlutterWebView.m | 699 --------------- .../ios/Classes/FlutterWebView.modulemap | 2 - .../ios/Classes/FlutterWebView_Test.h | 21 - .../ios/Classes/JavaScriptChannelHandler.h | 17 - .../ios/Classes/JavaScriptChannelHandler.m | 36 - .../ios/Classes/webview-umbrella.h | 5 - .../lib/src/web_kit_cookie_manager.dart | 54 -- .../lib/src/web_kit_webview_widget.dart | 12 +- .../lib/src/webview_cupertino.dart | 35 +- .../lib/src/wkwebview_cookie_manager.dart | 42 +- .../webview_flutter_wkwebview/pubspec.yaml | 2 +- .../test/src/web_kit_cookie_manager_test.dart | 6 +- .../test/src/web_kit_webview_widget_test.dart | 17 + .../src/wkwebview_cookie_manager_test.dart | 65 -- 28 files changed, 183 insertions(+), 2382 deletions(-) delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTCookieManagerTests.m delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWKNavigationDelegateTests.m delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.h delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.m delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager_Test.h delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKNavigationDelegate.h delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKNavigationDelegate.m delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKProgressionDelegate.h delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKProgressionDelegate.m delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView_Test.h delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.h delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.m delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/src/wkwebview_cookie_manager_test.dart diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 979b0f797968..913b0a44b7ea 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.9.0 * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). +* Replaces platform implementation with WebKit API built with pigeon. ## 2.8.1 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj index 4ed8769ea518..1efee8f844ef 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,8 +8,6 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 334734012669319100DCC49E /* FLTWebViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68BDCAF523C3F97800D9C032 /* FLTWebViewTests.m */; }; - 334734022669319400DCC49E /* FLTWKNavigationDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 8FA6A87928062CD000A4B183 /* FWFInstanceManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */; }; 8FB79B5328134C3100C101D3 /* FWFWebViewHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */; }; @@ -32,7 +30,6 @@ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; D7587C3652F6906210B3AE88 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 17781D9462A1AEA7C99F8E45 /* libPods-RunnerTests.a */; }; DAF0E91266956134538CC667 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 572FFC2B2BA326B420B22679 /* libPods-Runner.a */; }; - E43693B527512C0F00382F85 /* FLTCookieManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E43693B427512C0F00382F85 /* FLTCookieManagerTests.m */; }; F7151F77266057800028CB91 /* FLTWebViewUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F76266057800028CB91 /* FLTWebViewUITests.m */; }; /* End PBXBuildFile section */ @@ -74,10 +71,8 @@ 39B2BDAA45DC06EAB8A6C4E7 /* 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 = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 572FFC2B2BA326B420B22679 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTWKNavigationDelegateTests.m; sourceTree = ""; }; 68BDCAE923C3F7CB00D9C032 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 68BDCAED23C3F7CB00D9C032 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 68BDCAF523C3F97800D9C032 /* FLTWebViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTWebViewTests.m; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -104,7 +99,6 @@ 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 = ""; }; B89AA31A64040E4A2F1E0CAF /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - E43693B427512C0F00382F85 /* FLTCookieManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTCookieManagerTests.m; sourceTree = ""; }; F7151F74266057800028CB91 /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F7151F76266057800028CB91 /* FLTWebViewUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTWebViewUITests.m; sourceTree = ""; }; F7151F78266057800028CB91 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -150,10 +144,7 @@ 68BDCAEA23C3F7CB00D9C032 /* RunnerTests */ = { isa = PBXGroup; children = ( - 686B4BF82548DBC7000AEA36 /* FLTWKNavigationDelegateTests.m */, - 68BDCAF523C3F97800D9C032 /* FLTWebViewTests.m */, 68BDCAED23C3F7CB00D9C032 /* Info.plist */, - E43693B427512C0F00382F85 /* FLTCookieManagerTests.m */, 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */, 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */, 8FB79B54281B24F600C101D3 /* FWFDataConvertersTests.m */, @@ -471,16 +462,13 @@ 8FB79B672820453400C101D3 /* FWFHTTPCookieStoreHostApiTests.m in Sources */, 8FB79B5328134C3100C101D3 /* FWFWebViewHostApiTests.m in Sources */, 8FB79B73282096B500C101D3 /* FWFScriptMessageHandlerHostApiTests.m in Sources */, - 334734012669319100DCC49E /* FLTWebViewTests.m in Sources */, 8FB79B7928209D1300C101D3 /* FWFUserContentControllerHostApiTests.m in Sources */, 8FB79B6B28204EE500C101D3 /* FWFWebsiteDataStoreHostApiTests.m in Sources */, 8FB79B8F2820BAB300C101D3 /* FWFScrollViewHostApiTests.m in Sources */, 8FB79B912820BAC700C101D3 /* FWFUIViewHostApiTests.m in Sources */, - 334734022669319400DCC49E /* FLTWKNavigationDelegateTests.m in Sources */, 8FB79B55281B24F600C101D3 /* FWFDataConvertersTests.m in Sources */, 8FB79B6D2820533B00C101D3 /* FWFWebViewConfigurationHostApiTests.m in Sources */, 8FB79B832820A39300C101D3 /* FWFNavigationDelegateHostApiTests.m in Sources */, - E43693B527512C0F00382F85 /* FLTCookieManagerTests.m in Sources */, 8FB79B6928204E8700C101D3 /* FWFPreferencesHostApiTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTCookieManagerTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTCookieManagerTests.m deleted file mode 100644 index 837c0d892b64..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTCookieManagerTests.m +++ /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 Flutter; -@import XCTest; -@import webview_flutter_wkwebview; -@import webview_flutter_wkwebview.Test; - -// OCMock library doesn't generate a valid modulemap. -#import - -@interface FLTCookieManagerTests : XCTestCase - -@end - -@implementation FLTCookieManagerTests - -- (void)setUp { - [super setUp]; -} - -- (void)testSetCookieForResultSetsCookieAndReturnsResultOnIOS11 { - if (@available(iOS 11.0, *)) { - // Setup - XCTestExpectation *resultExpectation = [self - expectationWithDescription:@"Should return success result when setting cookie completes."]; - [FLTCookieManager.instance setHttpCookieStore:OCMClassMock(WKHTTPCookieStore.class)]; - NSDictionary *arguments = @{ - @"name" : @"foo", - @"value" : @"bar", - @"domain" : @"flutter.dev", - @"path" : @"/", - }; - NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:@{ - NSHTTPCookieName : arguments[@"name"], - NSHTTPCookieValue : arguments[@"value"], - NSHTTPCookieDomain : arguments[@"domain"], - NSHTTPCookiePath : arguments[@"path"], - }]; - [OCMStub([FLTCookieManager.instance.httpCookieStore setCookie:[OCMArg isEqual:cookie] - completionHandler:[OCMArg any]]) - andDo:^(NSInvocation *invocation) { - void (^setCookieCompletionHandler)(void); - [invocation getArgument:&setCookieCompletionHandler atIndex:3]; - setCookieCompletionHandler(); - }]; - // Run - [[FLTCookieManager instance] - setCookieForResult:^(id _Nullable result) { - XCTAssertNil(result); - [resultExpectation fulfill]; - } - arguments:arguments]; - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; - } -} - -- (void)testSetCookieForDataSetsCookieOnIOS11 { - if (@available(iOS 11.0, *)) { - // Setup - WKHTTPCookieStore *mockHttpCookieStore = OCMClassMock(WKHTTPCookieStore.class); - [FLTCookieManager.instance setHttpCookieStore:mockHttpCookieStore]; - NSDictionary *cookieData = @{ - @"name" : @"foo", - @"value" : @"bar", - @"domain" : @"flutter.dev", - @"path" : @"/", - }; - // Run - [[FLTCookieManager instance] setCookieForData:cookieData]; - // Verify - NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:@{ - NSHTTPCookieName : cookieData[@"name"], - NSHTTPCookieValue : cookieData[@"value"], - NSHTTPCookieDomain : cookieData[@"domain"], - NSHTTPCookiePath : cookieData[@"path"], - }]; - OCMVerify([mockHttpCookieStore setCookie:[OCMArg isEqual:cookie] - completionHandler:[OCMArg any]]); - } -} - -- (void)testSetCookiesForDataSetsCookiesOnIOS11 { - if (@available(iOS 11.0, *)) { - // Setup - WKHTTPCookieStore *mockHttpCookieStore = OCMClassMock(WKHTTPCookieStore.class); - [FLTCookieManager.instance setHttpCookieStore:mockHttpCookieStore]; - NSArray *cookieDatas = @[ - @{ - @"name" : @"foo1", - @"value" : @"bar1", - @"domain" : @"flutter.dev", - @"path" : @"/", - }, - @{ - @"name" : @"foo2", - @"value" : @"bar2", - @"domain" : @"flutter2.dev", - @"path" : @"/2", - } - ]; - // Run - [[FLTCookieManager instance] setCookiesForData:cookieDatas]; - // Verify - NSHTTPCookie *cookie1 = [NSHTTPCookie cookieWithProperties:@{ - NSHTTPCookieName : cookieDatas[0][@"name"], - NSHTTPCookieValue : cookieDatas[0][@"value"], - NSHTTPCookieDomain : cookieDatas[0][@"domain"], - NSHTTPCookiePath : cookieDatas[0][@"path"], - }]; - - OCMVerify([mockHttpCookieStore setCookie:[OCMArg isEqual:cookie1] - completionHandler:[OCMArg any]]); - NSHTTPCookie *cookie2 = [NSHTTPCookie cookieWithProperties:@{ - NSHTTPCookieName : cookieDatas[1][@"name"], - NSHTTPCookieValue : cookieDatas[1][@"value"], - NSHTTPCookieDomain : cookieDatas[1][@"domain"], - NSHTTPCookiePath : cookieDatas[1][@"path"], - }]; - OCMVerify([mockHttpCookieStore setCookie:[OCMArg isEqual:cookie2] - completionHandler:[OCMArg any]]); - } -} - -@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWKNavigationDelegateTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWKNavigationDelegateTests.m deleted file mode 100644 index d39a9f203d70..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWKNavigationDelegateTests.m +++ /dev/null @@ -1,73 +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 XCTest; -@import webview_flutter_wkwebview; -@import webview_flutter_wkwebview.Test; - -// OCMock library doesn't generate a valid modulemap. -#import - -@interface FLTWKNavigationDelegateTests : XCTestCase - -@property(strong, nonatomic) FlutterMethodChannel *mockMethodChannel; -@property(strong, nonatomic) FLTWKNavigationDelegate *navigationDelegate; -@property(strong, nonatomic) WKNavigation *navigation; - -@end - -@implementation FLTWKNavigationDelegateTests - -NSString *const zoomDisablingJavascript = - @"var meta = document.createElement('meta');" - @"meta.name = 'viewport';" - @"meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0," - @"user-scalable=no';" - @"var head = document.getElementsByTagName('head')[0];head.appendChild(meta);"; - -- (void)setUp { - self.mockMethodChannel = OCMClassMock(FlutterMethodChannel.class); - self.navigationDelegate = - [[FLTWKNavigationDelegate alloc] initWithChannel:self.mockMethodChannel]; -} - -- (void)testWebViewWebContentProcessDidTerminateCallsRecourseErrorChannel { - if (@available(iOS 9.0, *)) { - // `webViewWebContentProcessDidTerminate` is only available on iOS 9.0 and above. - WKWebView *webview = OCMClassMock(WKWebView.class); - [self.navigationDelegate webViewWebContentProcessDidTerminate:webview]; - OCMVerify([self.mockMethodChannel - invokeMethod:@"onWebResourceError" - arguments:[OCMArg checkWithBlock:^BOOL(NSDictionary *args) { - XCTAssertEqualObjects(args[@"errorType"], @"webContentProcessTerminated"); - return true; - }]]); - } -} - -- (void)testWebViewWebEvaluateJavaScriptSourceIsCorrectWhenShouldEnableZoomIsFalse { - WKWebView *webview = OCMClassMock(WKWebView.class); - WKNavigation *navigation = OCMClassMock(WKNavigation.class); - NSURL *testUrl = [[NSURL alloc] initWithString:@"www.example.com"]; - OCMStub([webview URL]).andReturn(testUrl); - - self.navigationDelegate.shouldEnableZoom = false; - [self.navigationDelegate webView:webview didFinishNavigation:navigation]; - OCMVerify([webview evaluateJavaScript:zoomDisablingJavascript completionHandler:nil]); -} - -- (void)testWebViewWebEvaluateJavaScriptShouldNotBeCalledWhenShouldEnableZoomIsTrue { - WKWebView *webview = OCMClassMock(WKWebView.class); - WKNavigation *navigation = OCMClassMock(WKNavigation.class); - NSURL *testUrl = [[NSURL alloc] initWithString:@"www.example.com"]; - OCMStub([webview URL]).andReturn(testUrl); - - self.navigationDelegate.shouldEnableZoom = true; - - OCMReject([webview evaluateJavaScript:zoomDisablingJavascript completionHandler:nil]); - [self.navigationDelegate webView:webview didFinishNavigation:navigation]; -} - -@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m deleted file mode 100644 index 976b9c46579a..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m +++ /dev/null @@ -1,833 +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 XCTest; -@import webview_flutter_wkwebview; -@import webview_flutter_wkwebview.Test; - -// OCMock library doesn't generate a valid modulemap. -#import - -static bool feq(CGFloat a, CGFloat b) { return fabs(b - a) < FLT_EPSILON; } - -@interface FLTWebViewTests : XCTestCase - -@property(strong, nonatomic) NSObject *mockBinaryMessenger; - -@property(strong, nonatomic) FLTCookieManager *mockCookieManager; - -@end - -@implementation FLTWebViewTests - -- (void)setUp { - [super setUp]; - self.mockBinaryMessenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); - self.mockCookieManager = OCMClassMock(FLTCookieManager.class); -} - -- (void)testCanInitFLTWebViewController { - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTAssertNotNil(controller); -} - -- (void)testCanInitFLTWebViewFactory { - FLTWebViewFactory *factory = [[FLTWebViewFactory alloc] initWithMessenger:self.mockBinaryMessenger - cookieManager:self.mockCookieManager]; - XCTAssertNotNil(factory); -} - -- (void)webViewContentInsetBehaviorShouldBeNeverOnIOS11 { - if (@available(iOS 11, *)) { - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - UIView *view = controller.view; - XCTAssertTrue([view isKindOfClass:WKWebView.class]); - WKWebView *webView = (WKWebView *)view; - XCTAssertEqual(webView.scrollView.contentInsetAdjustmentBehavior, - UIScrollViewContentInsetAdjustmentNever); - } -} - -- (void)testWebViewScrollIndicatorAticautomaticallyAdjustsScrollIndicatorInsetsShouldbeNoOnIOS13 { - if (@available(iOS 13, *)) { - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - UIView *view = controller.view; - XCTAssertTrue([view isKindOfClass:WKWebView.class]); - WKWebView *webView = (WKWebView *)view; - XCTAssertFalse(webView.scrollView.automaticallyAdjustsScrollIndicatorInsets); - } -} - -- (void)testContentInsetsSumAlwaysZeroAfterSetFrame { - FLTWKWebView *webView = [[FLTWKWebView alloc] initWithFrame:CGRectMake(0, 0, 300, 400)]; - webView.scrollView.contentInset = UIEdgeInsetsMake(0, 0, 300, 0); - XCTAssertFalse(UIEdgeInsetsEqualToEdgeInsets(webView.scrollView.contentInset, UIEdgeInsetsZero)); - webView.frame = CGRectMake(0, 0, 300, 200); - XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(webView.scrollView.contentInset, UIEdgeInsetsZero)); - XCTAssertTrue(CGRectEqualToRect(webView.frame, CGRectMake(0, 0, 300, 200))); - - if (@available(iOS 11, *)) { - // After iOS 11, we need to make sure the contentInset compensates the adjustedContentInset. - UIScrollView *partialMockScrollView = OCMPartialMock(webView.scrollView); - UIEdgeInsets insetToAdjust = UIEdgeInsetsMake(0, 0, 300, 0); - OCMStub(partialMockScrollView.adjustedContentInset).andReturn(insetToAdjust); - XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(webView.scrollView.contentInset, UIEdgeInsetsZero)); - webView.frame = CGRectMake(0, 0, 300, 100); - XCTAssertTrue(feq(webView.scrollView.contentInset.bottom, -insetToAdjust.bottom)); - XCTAssertTrue(CGRectEqualToRect(webView.frame, CGRectMake(0, 0, 300, 100))); - } -} - -- (void)testLoadFileSucceeds { - NSString *testFilePath = @"/assets/file.html"; - NSURL *url = [NSURL fileURLWithPath:testFilePath isDirectory:NO]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return successful result over the method channel."]; - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockWebView; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFile" - arguments:testFilePath] - result:^(id _Nullable result) { - XCTAssertNil(result); - [resultExpectation fulfill]; - }]; - - [self waitForExpectations:@[ resultExpectation ] timeout:30.0]; - OCMVerify([mockWebView loadFileURL:url - allowingReadAccessToURL:[url URLByDeletingLastPathComponent]]); -} - -- (void)testLoadFileFailsWithInvalidPath { - NSArray *resultExpectations = @[ - [self expectationWithDescription:@"Should return failed result when argument is nil."], - [self expectationWithDescription: - @"Should return failed result when argument is not of type NSString*."], - [self expectationWithDescription: - @"Should return failed result when argument is an empty string."], - ]; - - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockWebView; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFile" arguments:nil] - result:^(id _Nullable result) { - FlutterError *expected = - [FlutterError errorWithCode:@"loadFile_failed" - message:@"Failed parsing file path." - details:@"Argument is nil."]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[0] fulfill]; - }]; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFile" arguments:@(10)] - result:^(id _Nullable result) { - FlutterError *expected = - [FlutterError errorWithCode:@"loadFile_failed" - message:@"Failed parsing file path." - details:@"Argument is not of type NSString."]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[1] fulfill]; - }]; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFile" arguments:@""] - result:^(id _Nullable result) { - FlutterError *expected = - [FlutterError errorWithCode:@"loadFile_failed" - message:@"Failed parsing file path." - details:@"Argument contains an empty string."]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[2] fulfill]; - }]; - - [self waitForExpectations:resultExpectations timeout:1.0]; - OCMReject([mockWebView loadFileURL:[OCMArg any] allowingReadAccessToURL:[OCMArg any]]); -} - -- (void)testLoadFlutterAssetSucceeds { - NSBundle *mockBundle = OCMPartialMock([NSBundle mainBundle]); - NSString *filePath = [FlutterDartProject lookupKeyForAsset:@"assets/file.html"]; - NSURL *url = [NSURL URLWithString:[@"file:///" stringByAppendingString:filePath]]; - [OCMStub([mockBundle URLForResource:[filePath stringByDeletingPathExtension] - withExtension:@"html"]) andReturn:(url)]; - - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return successful result over the method channel."]; - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockWebView; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" - arguments:@"assets/file.html"] - result:^(id _Nullable result) { - XCTAssertNil(result); - [resultExpectation fulfill]; - }]; - - [self waitForExpectations:@[ resultExpectation ] timeout:1.0]; - OCMVerify([mockWebView loadFileURL:url - allowingReadAccessToURL:[url URLByDeletingLastPathComponent]]); -} - -- (void)testLoadFlutterAssetFailsWithInvalidKey { - NSArray *resultExpectations = @[ - [self expectationWithDescription:@"Should return failed result when argument is nil."], - [self expectationWithDescription: - @"Should return failed result when argument is not of type NSString*."], - [self expectationWithDescription: - @"Should return failed result when argument is an empty string."], - ]; - - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockWebView; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" - arguments:nil] - result:^(id _Nullable result) { - FlutterError *expected = - [FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" - message:@"Supplied asset key is not valid." - details:@"Argument is nil."]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[0] fulfill]; - }]; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" - arguments:@(10)] - result:^(id _Nullable result) { - FlutterError *expected = - [FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" - message:@"Supplied asset key is not valid." - details:@"Argument is not of type NSString."]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[1] fulfill]; - }]; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" - arguments:@""] - result:^(id _Nullable result) { - FlutterError *expected = - [FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" - message:@"Supplied asset key is not valid." - details:@"Argument contains an empty string."]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[2] fulfill]; - }]; - - [self waitForExpectations:resultExpectations timeout:1.0]; - OCMReject([mockWebView loadFileURL:[OCMArg any] allowingReadAccessToURL:[OCMArg any]]); -} - -- (void)testLoadFlutterAssetFailsWithParsingError { - NSBundle *mockBundle = OCMPartialMock([NSBundle mainBundle]); - NSString *filePath = [FlutterDartProject lookupKeyForAsset:@"assets/file.html"]; - [OCMStub([mockBundle URLForResource:[filePath stringByDeletingPathExtension] - withExtension:@"html"]) andReturn:(nil)]; - - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return failed result over the method channel."]; - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockWebView; - [controller - onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" - arguments:@"assets/file.html"] - result:^(id _Nullable result) { - FlutterError *expected = [FlutterError - errorWithCode:@"loadFlutterAsset_invalidKey" - message:@"Failed parsing file path for supplied key." - details:[NSString - stringWithFormat: - @"Failed to convert path '%@' into NSURL for key '%@'.", - filePath, @"assets/file.html"]]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectation fulfill]; - }]; - - [self waitForExpectations:@[ resultExpectation ] timeout:1.0]; - OCMReject([mockWebView loadFileURL:[OCMArg any] allowingReadAccessToURL:[OCMArg any]]); -} - -- (void)testLoadHtmlStringSucceedsWithBaseUrl { - NSURL *baseUrl = [NSURL URLWithString:@"https://flutter.dev"]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return successful result over the method channel."]; - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockWebView; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString" - arguments:@{ - @"html" : @"some HTML string", - @"baseUrl" : @"https://flutter.dev" - }] - result:^(id _Nullable result) { - XCTAssertNil(result); - [resultExpectation fulfill]; - }]; - - [self waitForExpectations:@[ resultExpectation ] timeout:30.0]; - OCMVerify([mockWebView loadHTMLString:@"some HTML string" baseURL:baseUrl]); -} - -- (void)testLoadHtmlStringSucceedsWithoutBaseUrl { - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return successful result over the method channel."]; - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockWebView; - [controller - onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString" - arguments:@{@"html" : @"some HTML string"}] - result:^(id _Nullable result) { - XCTAssertNil(result); - [resultExpectation fulfill]; - }]; - - [self waitForExpectations:@[ resultExpectation ] timeout:30.0]; - OCMVerify([mockWebView loadHTMLString:@"some HTML string" baseURL:nil]); -} - -- (void)testLoadHtmlStringFailsWithInvalidArgument { - NSArray *resultExpectations = @[ - [self expectationWithDescription:@"Should return failed result when argument is nil."], - [self expectationWithDescription: - @"Should return failed result when argument is not of type NSDictionary*."], - [self expectationWithDescription:@"Should return failed result when HTML argument is nil."], - [self expectationWithDescription: - @"Should return failed result when HTML argument is not of type NSString*."], - [self expectationWithDescription: - @"Should return failed result when HTML argument is an empty string."], - ]; - - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockWebView; - FlutterError *expected = [FlutterError - errorWithCode:@"loadHtmlString_failed" - message:@"Failed parsing arguments." - details:@"Arguments should be a dictionary containing at least a 'html' element and " - @"optionally a 'baseUrl' argument. For example: `@{ @\"html\": @\"some html " - @"code\", @\"baseUrl\": @\"https://flutter.dev\" }`"]; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString" - arguments:nil] - result:^(id _Nullable result) { - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[0] fulfill]; - }]; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString" - arguments:@""] - result:^(id _Nullable result) { - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[1] fulfill]; - }]; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString" - arguments:@{}] - result:^(id _Nullable result) { - FlutterError *expected = - [FlutterError errorWithCode:@"loadHtmlString_failed" - message:@"Failed parsing HTML string argument." - details:@"Argument is nil."]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[2] fulfill]; - }]; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString" - arguments:@{ - @"html" : @(42), - }] - result:^(id _Nullable result) { - FlutterError *expected = - [FlutterError errorWithCode:@"loadHtmlString_failed" - message:@"Failed parsing HTML string argument." - details:@"Argument is not of type NSString."]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[3] fulfill]; - }]; - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadHtmlString" - arguments:@{ - @"html" : @"", - }] - result:^(id _Nullable result) { - FlutterError *expected = - [FlutterError errorWithCode:@"loadHtmlString_failed" - message:@"Failed parsing HTML string argument." - details:@"Argument contains an empty string."]; - [FLTWebViewTests assertFlutterError:result withExpected:expected]; - [resultExpectations[4] fulfill]; - }]; - - [self waitForExpectations:resultExpectations timeout:1.0]; - OCMReject([mockWebView loadHTMLString:[OCMArg any] baseURL:[OCMArg any]]); -} - -- (void)testRunJavascriptFailsForNullString { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return error result over the method channel."]; - - // Run - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascript" - arguments:nil] - result:^(id _Nullable result) { - XCTAssertTrue([result class] == [FlutterError class]); - [resultExpectation fulfill]; - }]; - - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testRunJavascriptRunsStringWithSuccessResult { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return successful result over the method channel."]; - FLTWKWebView *mockView = OCMClassMock(FLTWKWebView.class); - [OCMStub([mockView evaluateJavaScript:[OCMArg any] - completionHandler:[OCMArg any]]) andDo:^(NSInvocation *invocation) { - // __unsafe_unretained: https://github.com/erikdoe/ocmock/issues/384#issuecomment-589376668 - __unsafe_unretained void (^evalResultHandler)(id, NSError *); - [invocation getArgument:&evalResultHandler atIndex:3]; - evalResultHandler(@"RESULT", nil); - }]; - controller.webView = mockView; - - // Run - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascript" - arguments:@"Test JavaScript String"] - result:^(id _Nullable result) { - XCTAssertNil(result); - [resultExpectation fulfill]; - }]; - - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testRunJavascriptReturnsErrorResultForWKError { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return error result over the method channel."]; - NSError *testError = - [NSError errorWithDomain:@"" - // Any error code but WKErrorJavascriptResultTypeIsUnsupported - code:WKErrorJavaScriptResultTypeIsUnsupported + 1 - userInfo:@{NSLocalizedDescriptionKey : @"Test Error"}]; - FLTWKWebView *mockView = OCMClassMock(FLTWKWebView.class); - [OCMStub([mockView evaluateJavaScript:[OCMArg any] - completionHandler:[OCMArg any]]) andDo:^(NSInvocation *invocation) { - // __unsafe_unretained: https://github.com/erikdoe/ocmock/issues/384#issuecomment-589376668 - __unsafe_unretained void (^evalResultHandler)(id, NSError *); - [invocation getArgument:&evalResultHandler atIndex:3]; - evalResultHandler(nil, testError); - }]; - controller.webView = mockView; - - // Run - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascript" - arguments:@"Test JavaScript String"] - result:^(id _Nullable result) { - XCTAssertTrue([result class] == [FlutterError class]); - [resultExpectation fulfill]; - }]; - - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testRunJavascriptReturnsSuccessForWKErrorJavascriptResultTypeIsUnsupported { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return nil result over the method channel."]; - NSError *testError = [NSError errorWithDomain:@"" - code:WKErrorJavaScriptResultTypeIsUnsupported - userInfo:@{NSLocalizedDescriptionKey : @"Test Error"}]; - FLTWKWebView *mockView = OCMClassMock(FLTWKWebView.class); - [OCMStub([mockView evaluateJavaScript:[OCMArg any] - completionHandler:[OCMArg any]]) andDo:^(NSInvocation *invocation) { - // __unsafe_unretained: https://github.com/erikdoe/ocmock/issues/384#issuecomment-589376668 - __unsafe_unretained void (^evalResultHandler)(id, NSError *); - [invocation getArgument:&evalResultHandler atIndex:3]; - evalResultHandler(nil, testError); - }]; - controller.webView = mockView; - - // Run - [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascript" - arguments:@"Test JavaScript String"] - result:^(id _Nullable result) { - XCTAssertNil(result); - [resultExpectation fulfill]; - }]; - - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testRunJavascriptReturningResultFailsForNullString { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return error result over the method channel."]; - - // Run - [controller - onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascriptReturningResult" - arguments:nil] - result:^(id _Nullable result) { - XCTAssertTrue([result class] == [FlutterError class]); - [resultExpectation fulfill]; - }]; - - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testRunJavascriptReturningResultRunsStringWithSuccessResult { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return successful result over the method channel."]; - FLTWKWebView *mockView = OCMClassMock(FLTWKWebView.class); - [OCMStub([mockView evaluateJavaScript:[OCMArg any] - completionHandler:[OCMArg any]]) andDo:^(NSInvocation *invocation) { - // __unsafe_unretained: https://github.com/erikdoe/ocmock/issues/384#issuecomment-589376668 - __unsafe_unretained void (^evalResultHandler)(id, NSError *); - [invocation getArgument:&evalResultHandler atIndex:3]; - evalResultHandler(@"RESULT", nil); - }]; - controller.webView = mockView; - - // Run - [controller - onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascriptReturningResult" - arguments:@"Test JavaScript String"] - result:^(id _Nullable result) { - XCTAssertTrue([@"RESULT" isEqualToString:result]); - [resultExpectation fulfill]; - }]; - - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testRunJavascriptReturningResultReturnsErrorResultForWKError { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return error result over the method channel."]; - NSError *testError = [NSError errorWithDomain:@"" - code:5 - userInfo:@{NSLocalizedDescriptionKey : @"Test Error"}]; - FLTWKWebView *mockView = OCMClassMock(FLTWKWebView.class); - [OCMStub([mockView evaluateJavaScript:[OCMArg any] - completionHandler:[OCMArg any]]) andDo:^(NSInvocation *invocation) { - // __unsafe_unretained: https://github.com/erikdoe/ocmock/issues/384#issuecomment-589376668 - __unsafe_unretained void (^evalResultHandler)(id, NSError *); - [invocation getArgument:&evalResultHandler atIndex:3]; - evalResultHandler(nil, testError); - }]; - controller.webView = mockView; - - // Run - [controller - onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"runJavascriptReturningResult" - arguments:@"Test JavaScript String"] - result:^(id _Nullable result) { - XCTAssertTrue([result class] == [FlutterError class]); - [resultExpectation fulfill]; - }]; - - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -+ (void)assertFlutterError:(id)actual withExpected:(FlutterError *)expected { - XCTAssertTrue([actual class] == [FlutterError class]); - FlutterError *errorResult = actual; - XCTAssertEqualObjects(errorResult.code, expected.code); - XCTAssertEqualObjects(errorResult.message, expected.message); - XCTAssertEqualObjects(errorResult.details, expected.details); -} - -- (void)testBuildNSURLRequestReturnsNilForNonDictionaryValue { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - - // Run - NSURLRequest *request = [controller buildNSURLRequest:@{@"request" : @"Non Dictionary Value"}]; - - // Verify - XCTAssertNil(request); -} - -- (void)testBuildNSURLRequestReturnsNilForMissingURI { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - - // Run - NSURLRequest *request = [controller buildNSURLRequest:@{@"request" : @{}}]; - - // Verify - XCTAssertNil(request); -} - -- (void)testBuildNSURLRequestReturnsNilForInvalidURI { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - - // Run - NSDictionary *requestData = @{@"uri" : @"invalid uri"}; - NSURLRequest *request = [controller buildNSURLRequest:@{@"request" : requestData}]; - - // Verify - XCTAssertNil(request); -} - -- (void)testBuildNSURLRequestBuildsNSMutableURLRequestWithOptionalParameters { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - - // Run - NSDictionary *requestData = @{ - @"uri" : @"https://flutter.dev", - @"method" : @"POST", - @"headers" : @{@"Foo" : @"Bar"}, - @"body" : [FlutterStandardTypedData - typedDataWithBytes:[@"Test Data" dataUsingEncoding:NSUTF8StringEncoding]], - }; - NSURLRequest *request = [controller buildNSURLRequest:@{@"request" : requestData}]; - - // Verify - XCTAssertNotNil(request); - XCTAssertEqualObjects(request.URL.absoluteString, @"https://flutter.dev"); - XCTAssertEqualObjects(request.HTTPMethod, @"POST"); - XCTAssertEqualObjects(request.allHTTPHeaderFields, @{@"Foo" : @"Bar"}); - XCTAssertEqualObjects(request.HTTPBody, [@"Test Data" dataUsingEncoding:NSUTF8StringEncoding]); -} - -- (void)testBuildNSURLRequestBuildsNSMutableURLRequestWithoutOptionalParameters { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - - // Run - NSDictionary *requestData = @{ - @"uri" : @"https://flutter.dev", - }; - NSURLRequest *request = [controller buildNSURLRequest:@{@"request" : requestData}]; - - // Verify - XCTAssertNotNil(request); - XCTAssertEqualObjects(request.URL.absoluteString, @"https://flutter.dev"); - XCTAssertEqualObjects(request.HTTPMethod, @"GET"); - XCTAssertNil(request.allHTTPHeaderFields); - XCTAssertNil(request.HTTPBody); -} - -- (void)testOnLoadUrlReturnsErrorResultForInvalidRequest { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return error result when request cannot be built"]; - - // Run - FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"loadUrl" - arguments:@{}]; - [controller onLoadUrl:methodCall - result:^(id _Nullable result) { - XCTAssertTrue([result class] == [FlutterError class]); - [resultExpectation fulfill]; - }]; - - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testOnLoadUrlLoadsRequestWithSuccessResult { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = [self expectationWithDescription:@"Should return nil"]; - FLTWKWebView *mockView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockView; - - // Run - FlutterMethodCall *methodCall = - [FlutterMethodCall methodCallWithMethodName:@"loadUrl" - arguments:@{@"url" : @"https://flutter.dev/"}]; - [controller onLoadUrl:methodCall - result:^(id _Nullable result) { - XCTAssertNil(result); - [resultExpectation fulfill]; - }]; - - // Verify - OCMVerify([mockView loadRequest:[OCMArg any]]); - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testOnLoadRequestReturnsErrorResultForInvalidRequest { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = - [self expectationWithDescription:@"Should return error result when request cannot be built"]; - - // Run - FlutterMethodCall *methodCall = [FlutterMethodCall methodCallWithMethodName:@"loadRequest" - arguments:@{}]; - [controller onLoadRequest:methodCall - result:^(id _Nullable result) { - XCTAssertTrue([result class] == [FlutterError class]); - [resultExpectation fulfill]; - }]; - - // Verify - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testOnLoadRequestLoadsRequestWithSuccessResult { - // Setup - FLTWebViewController *controller = - [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:nil - binaryMessenger:self.mockBinaryMessenger]; - XCTestExpectation *resultExpectation = [self expectationWithDescription:@"Should return nil"]; - FLTWKWebView *mockView = OCMClassMock(FLTWKWebView.class); - controller.webView = mockView; - - // Run - FlutterMethodCall *methodCall = [FlutterMethodCall - methodCallWithMethodName:@"loadRequest" - arguments:@{@"request" : @{@"uri" : @"https://flutter.dev/"}}]; - [controller onLoadRequest:methodCall - result:^(id _Nullable result) { - XCTAssertNil(result); - [resultExpectation fulfill]; - }]; - - // Verify - OCMVerify([mockView loadRequest:[OCMArg any]]); - [self waitForExpectationsWithTimeout:30.0 handler:nil]; -} - -- (void)testCreateWithFrameShouldSetCookiesOnIOS11 { - if (@available(iOS 11, *)) { - // Setup - FLTWebViewFactory *factory = - [[FLTWebViewFactory alloc] initWithMessenger:self.mockBinaryMessenger - cookieManager:self.mockCookieManager]; - NSArray *cookies = - @[ @{@"name" : @"foo", @"value" : @"bar", @"domain" : @"flutter.dev", @"path" : @"/"} ]; - // Run - [factory createWithFrame:CGRectMake(0, 0, 300, 400) - viewIdentifier:1 - arguments:@{@"cookies" : cookies}]; - // Verify - OCMVerify([_mockCookieManager setCookiesForData:cookies]); - } -} - -@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.h deleted file mode 100644 index b18e58b57cb3..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.h +++ /dev/null @@ -1,20 +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 -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface FLTCookieManager : NSObject - -+ (FLTCookieManager *)instance; - -- (void)setCookiesForData:(NSArray *)cookies; - -- (void)setCookieForData:(NSDictionary *)cookie; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.m deleted file mode 100644 index 39976d11153e..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager.m +++ /dev/null @@ -1,105 +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 "FLTCookieManager.h" -#import "FLTCookieManager_Test.h" - -@implementation FLTCookieManager { - WKHTTPCookieStore *_httpCookieStore API_AVAILABLE(macos(10.13), ios(11.0)); -} - -+ (FLTCookieManager *)instance { - static FLTCookieManager *instance = nil; - if (instance == nil) { - instance = [[FLTCookieManager alloc] init]; - } - return instance; -} - -+ (void)registerWithRegistrar:(NSObject *)registrar { - FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/cookie_manager" - binaryMessenger:[registrar messenger]]; - [registrar addMethodCallDelegate:[self instance] channel:channel]; -} - -- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { - if ([[call method] isEqualToString:@"clearCookies"]) { - [self clearCookies:result]; - } else if ([[call method] isEqualToString:@"setCookie"]) { - [self setCookieForResult:result arguments:[call arguments]]; - } else { - result(FlutterMethodNotImplemented); - } -} - -- (void)clearCookies:(FlutterResult)result { - if (@available(iOS 9.0, *)) { - NSSet *websiteDataTypes = [NSSet setWithObject:WKWebsiteDataTypeCookies]; - WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore]; - - void (^deleteAndNotify)(NSArray *) = - ^(NSArray *cookies) { - BOOL hasCookies = cookies.count > 0; - [dataStore removeDataOfTypes:websiteDataTypes - forDataRecords:cookies - completionHandler:^{ - result(@(hasCookies)); - }]; - }; - - [dataStore fetchDataRecordsOfTypes:websiteDataTypes completionHandler:deleteAndNotify]; - } else { - // support for iOS8 tracked in https://github.com/flutter/flutter/issues/27624. - NSLog(@"Clearing cookies is not supported for Flutter WebViews prior to iOS 9."); - } -} - -- (void)setCookiesForData:(NSArray *)cookies { - for (id cookie in cookies) { - [self setCookieForData:cookie]; - } -} - -- (void)setCookieForData:(NSDictionary *)cookieData { - if (@available(iOS 11.0, *)) { - if (!_httpCookieStore) { - _httpCookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore]; - } - NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:@{ - NSHTTPCookieName : cookieData[@"name"], - NSHTTPCookieValue : cookieData[@"value"], - NSHTTPCookieDomain : cookieData[@"domain"], - NSHTTPCookiePath : cookieData[@"path"], - }]; - [_httpCookieStore setCookie:cookie - completionHandler:^{ - }]; - } else { - NSLog(@"Setting cookies is not supported for Flutter WebViews prior to iOS 11."); - } -} - -- (void)setCookieForResult:(FlutterResult)result arguments:(NSDictionary *)arguments { - if (@available(iOS 11.0, *)) { - if (!_httpCookieStore) { - _httpCookieStore = [[WKWebsiteDataStore defaultDataStore] httpCookieStore]; - } - NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:@{ - NSHTTPCookieName : arguments[@"name"], - NSHTTPCookieValue : arguments[@"value"], - NSHTTPCookieDomain : arguments[@"domain"], - NSHTTPCookiePath : arguments[@"path"], - }]; - [_httpCookieStore setCookie:cookie - completionHandler:^{ - result(nil); - }]; - } else { - NSLog(@"Setting cookies is not supported for Flutter WebViews prior to iOS 11."); - result(nil); - } -} - -@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager_Test.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager_Test.h deleted file mode 100644 index fecec4932570..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTCookieManager_Test.h +++ /dev/null @@ -1,20 +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. - -// This header is available in the Test module. Import via "@import webview_flutter_wkwebview.Test;" - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface FLTCookieManager () - -@property(nonatomic, strong) - WKHTTPCookieStore *httpCookieStore API_AVAILABLE(macos(10.13), ios(11.0)); - -- (void)setCookieForResult:(FlutterResult)result arguments:(NSDictionary *)arguments; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKNavigationDelegate.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKNavigationDelegate.h deleted file mode 100644 index 6531931c4cf4..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKNavigationDelegate.h +++ /dev/null @@ -1,26 +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 -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface FLTWKNavigationDelegate : NSObject - -- (instancetype)initWithChannel:(FlutterMethodChannel *)channel; - -/** - * Whether to delegate navigation decisions over the method channel. - */ -@property(nonatomic, assign) BOOL hasDartNavigationDelegate; - -/** - * Whether to allow zoom functionality on the WebView. - */ -@property(nonatomic, assign) BOOL shouldEnableZoom; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKNavigationDelegate.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKNavigationDelegate.m deleted file mode 100644 index 125d3cabdcf1..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKNavigationDelegate.m +++ /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 "FLTWKNavigationDelegate.h" - -@implementation FLTWKNavigationDelegate { - FlutterMethodChannel *_methodChannel; -} - -- (instancetype)initWithChannel:(FlutterMethodChannel *)channel { - self = [super init]; - if (self) { - _methodChannel = channel; - } - return self; -} - -#pragma mark - WKNavigationDelegate conformance - -- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation { - [_methodChannel invokeMethod:@"onPageStarted" arguments:@{@"url" : webView.URL.absoluteString}]; -} - -- (void)webView:(WKWebView *)webView - decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction - decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { - if (!self.hasDartNavigationDelegate) { - decisionHandler(WKNavigationActionPolicyAllow); - return; - } - NSDictionary *arguments = @{ - @"url" : navigationAction.request.URL.absoluteString, - @"isForMainFrame" : @(navigationAction.targetFrame.isMainFrame) - }; - [_methodChannel invokeMethod:@"navigationRequest" - arguments:arguments - result:^(id _Nullable result) { - if ([result isKindOfClass:[FlutterError class]]) { - NSLog(@"navigationRequest has unexpectedly completed with an error, " - @"allowing navigation."); - decisionHandler(WKNavigationActionPolicyAllow); - return; - } - if (result == FlutterMethodNotImplemented) { - NSLog(@"navigationRequest was unexepectedly not implemented: %@, " - @"allowing navigation.", - result); - decisionHandler(WKNavigationActionPolicyAllow); - return; - } - if (![result isKindOfClass:[NSNumber class]]) { - NSLog(@"navigationRequest unexpectedly returned a non boolean value: " - @"%@, allowing navigation.", - result); - decisionHandler(WKNavigationActionPolicyAllow); - return; - } - NSNumber *typedResult = result; - decisionHandler([typedResult boolValue] ? WKNavigationActionPolicyAllow - : WKNavigationActionPolicyCancel); - }]; -} - -- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { - if (!self.shouldEnableZoom) { - NSString *source = - @"var meta = document.createElement('meta');" - @"meta.name = 'viewport';" - @"meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0," - @"user-scalable=no';" - @"var head = document.getElementsByTagName('head')[0];head.appendChild(meta);"; - - [webView evaluateJavaScript:source completionHandler:nil]; - } - - [_methodChannel invokeMethod:@"onPageFinished" arguments:@{@"url" : webView.URL.absoluteString}]; -} - -+ (id)errorCodeToString:(NSUInteger)code { - switch (code) { - case WKErrorUnknown: - return @"unknown"; - case WKErrorWebContentProcessTerminated: - return @"webContentProcessTerminated"; - case WKErrorWebViewInvalidated: - return @"webViewInvalidated"; - case WKErrorJavaScriptExceptionOccurred: - return @"javaScriptExceptionOccurred"; - case WKErrorJavaScriptResultTypeIsUnsupported: - return @"javaScriptResultTypeIsUnsupported"; - } - - return [NSNull null]; -} - -- (void)onWebResourceError:(NSError *)error { - [_methodChannel invokeMethod:@"onWebResourceError" - arguments:@{ - @"errorCode" : @(error.code), - @"domain" : error.domain, - @"description" : error.description, - @"errorType" : [FLTWKNavigationDelegate errorCodeToString:error.code], - }]; -} - -- (void)webView:(WKWebView *)webView - didFailNavigation:(WKNavigation *)navigation - withError:(NSError *)error { - [self onWebResourceError:error]; -} - -- (void)webView:(WKWebView *)webView - didFailProvisionalNavigation:(WKNavigation *)navigation - withError:(NSError *)error { - [self onWebResourceError:error]; -} - -- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView { - NSError *contentProcessTerminatedError = - [[NSError alloc] initWithDomain:WKErrorDomain - code:WKErrorWebContentProcessTerminated - userInfo:nil]; - [self onWebResourceError:contentProcessTerminatedError]; -} - -@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKProgressionDelegate.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKProgressionDelegate.h deleted file mode 100644 index 96af4ef6c578..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKProgressionDelegate.h +++ /dev/null @@ -1,19 +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 -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface FLTWKProgressionDelegate : NSObject - -- (instancetype)initWithWebView:(WKWebView *)webView channel:(FlutterMethodChannel *)channel; - -- (void)stopObservingProgress:(WKWebView *)webView; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKProgressionDelegate.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKProgressionDelegate.m deleted file mode 100644 index 8e7af4649aa0..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKProgressionDelegate.m +++ /dev/null @@ -1,41 +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 "FLTWKProgressionDelegate.h" - -NSString *const FLTWKEstimatedProgressKeyPath = @"estimatedProgress"; - -@implementation FLTWKProgressionDelegate { - FlutterMethodChannel *_methodChannel; -} - -- (instancetype)initWithWebView:(WKWebView *)webView channel:(FlutterMethodChannel *)channel { - self = [super init]; - if (self) { - _methodChannel = channel; - [webView addObserver:self - forKeyPath:FLTWKEstimatedProgressKeyPath - options:NSKeyValueObservingOptionNew - context:nil]; - } - return self; -} - -- (void)stopObservingProgress:(WKWebView *)webView { - [webView removeObserver:self forKeyPath:FLTWKEstimatedProgressKeyPath]; -} - -- (void)observeValueForKeyPath:(NSString *)keyPath - ofObject:(id)object - change:(NSDictionary *)change - context:(void *)context { - if ([keyPath isEqualToString:FLTWKEstimatedProgressKeyPath]) { - NSNumber *newValue = - change[NSKeyValueChangeNewKey] ?: 0; // newValue is anywhere between 0.0 and 1.0 - int newValueAsInt = [newValue floatValue] * 100; // Anywhere between 0 and 100 - [_methodChannel invokeMethod:@"onProgress" arguments:@{@"progress" : @(newValueAsInt)}]; - } -} - -@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m index a63d6a60b114..5795018b2043 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m @@ -3,18 +3,112 @@ // found in the LICENSE file. #import "FLTWebViewFlutterPlugin.h" -#import "FLTCookieManager.h" +#import "FWFGeneratedWebKitApis.h" +#import "FWFHTTPCookieStoreHostApi.h" +#import "FWFInstanceManager.h" +#import "FWFNavigationDelegateHostApi.h" +#import "FWFObjectHostApi.h" +#import "FWFPreferencesHostApi.h" +#import "FWFScriptMessageHandlerHostApi.h" +#import "FWFScrollViewHostApi.h" +#import "FWFUIDelegateHostApi.h" +#import "FWFUIViewHostApi.h" +#import "FWFUserContentControllerHostApi.h" +#import "FWFWebViewConfigurationHostApi.h" #import "FWFWebViewHostApi.h" -#import "FlutterWebView.h" +#import "FWFWebsiteDataStoreHostApi.h" + +@interface FWFWebViewFactory : NSObject +@property(nonatomic, weak) FWFInstanceManager *instanceManager; + +- (instancetype)initWithManager:(FWFInstanceManager *)manager; +@end + +@implementation FWFWebViewFactory +- (instancetype)initWithManager:(FWFInstanceManager *)manager { + self = [self init]; + if (self) { + _instanceManager = manager; + } + return self; +} + +- (NSObject *)createArgsCodec { + return [FlutterStandardMessageCodec sharedInstance]; +} + +- (NSObject *)createWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args { + NSNumber *identifier = (NSNumber *)args; + FWFWebView *webView = + (FWFWebView *)[self.instanceManager instanceForIdentifier:identifier.longValue]; + webView.frame = frame; + return webView; +} + +@end @implementation FLTWebViewFlutterPlugin + (void)registerWithRegistrar:(NSObject *)registrar { - [FLTCookieManager registerWithRegistrar:registrar]; - FLTWebViewFactory *webviewFactory = - [[FLTWebViewFactory alloc] initWithMessenger:registrar.messenger - cookieManager:[FLTCookieManager instance]]; + FWFInstanceManager *instanceManager = + [[FWFInstanceManager alloc] initWithDeallocCallback:^(long identifier) { + FWFObjectFlutterApiImpl *objectApi = [[FWFObjectFlutterApiImpl alloc] + initWithBinaryMessenger:registrar.messenger + instanceManager:[[FWFInstanceManager alloc] init]]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [objectApi disposeObjectWithIdentifier:@(identifier) + completion:^(NSError *error) { + NSAssert(!error, @"%@", error); + }]; + }); + }]; + FWFWKHttpCookieStoreHostApiSetup( + registrar.messenger, + [[FWFHTTPCookieStoreHostApiImpl alloc] initWithInstanceManager:instanceManager]); + FWFWKNavigationDelegateHostApiSetup( + registrar.messenger, + [[FWFNavigationDelegateHostApiImpl alloc] initWithBinaryMessenger:registrar.messenger + instanceManager:instanceManager]); + FWFNSObjectHostApiSetup(registrar.messenger, + [[FWFObjectHostApiImpl alloc] initWithInstanceManager:instanceManager]); + FWFWKPreferencesHostApiSetup(registrar.messenger, [[FWFPreferencesHostApiImpl alloc] + initWithInstanceManager:instanceManager]); + FWFWKScriptMessageHandlerHostApiSetup( + registrar.messenger, + [[FWFScriptMessageHandlerHostApiImpl alloc] initWithBinaryMessenger:registrar.messenger + instanceManager:instanceManager]); + FWFUIScrollViewHostApiSetup(registrar.messenger, [[FWFScrollViewHostApiImpl alloc] + initWithInstanceManager:instanceManager]); + FWFWKUIDelegateHostApiSetup(registrar.messenger, [[FWFUIDelegateHostApiImpl alloc] + initWithBinaryMessenger:registrar.messenger + instanceManager:instanceManager]); + FWFUIViewHostApiSetup(registrar.messenger, + [[FWFUIViewHostApiImpl alloc] initWithInstanceManager:instanceManager]); + FWFWKUserContentControllerHostApiSetup( + registrar.messenger, + [[FWFUserContentControllerHostApiImpl alloc] initWithInstanceManager:instanceManager]); + FWFWKWebsiteDataStoreHostApiSetup( + registrar.messenger, + [[FWFWebsiteDataStoreHostApiImpl alloc] initWithInstanceManager:instanceManager]); + FWFWKWebViewConfigurationHostApiSetup( + registrar.messenger, + [[FWFWebViewConfigurationHostApiImpl alloc] initWithBinaryMessenger:registrar.messenger + instanceManager:instanceManager]); + FWFWKWebViewHostApiSetup(registrar.messenger, [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:registrar.messenger + instanceManager:instanceManager]); + + FWFWebViewFactory *webviewFactory = [[FWFWebViewFactory alloc] initWithManager:instanceManager]; [registrar registerViewFactory:webviewFactory withId:@"plugins.flutter.io/webview"]; + + // InstanceManager is published so that a strong reference is maintained. + [registrar publish:instanceManager]; } +- (void)detachFromEngineForRegistrar:(NSObject *)registrar { + [registrar publish:[NSNull null]]; +} @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h deleted file mode 100644 index 69a15fc063c8..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.h +++ /dev/null @@ -1,40 +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 -#import - -#import "FLTCookieManager.h" - -NS_ASSUME_NONNULL_BEGIN - -/** - * The WkWebView used for the plugin. - * - * This class overrides some methods in `WKWebView` to serve the needs for the plugin. - */ -@interface FLTWKWebView : WKWebView -@end - -@interface FLTWebViewController : NSObject - -@property(nonatomic) FLTWKWebView *webView; - -- (instancetype)initWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args - binaryMessenger:(NSObject *)messenger; - -- (UIView *)view; - -- (void)onMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result; - -@end - -@interface FLTWebViewFactory : NSObject -- (instancetype)initWithMessenger:(NSObject *)messenger - cookieManager:(FLTCookieManager *)cookieManager; -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m deleted file mode 100644 index 5bb81fce89db..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m +++ /dev/null @@ -1,699 +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 "FlutterWebView.h" -#import "FLTWKNavigationDelegate.h" -#import "FLTWKProgressionDelegate.h" -#import "FlutterWebView_Test.h" -#import "JavaScriptChannelHandler.h" - -@implementation FLTWebViewFactory { - NSObject *_messenger; - FLTCookieManager *_cookieManager; -} - -- (instancetype)initWithMessenger:(NSObject *)messenger - cookieManager:(FLTCookieManager *)cookieManager { - self = [super init]; - if (self) { - _messenger = messenger; - _cookieManager = cookieManager; - } - return self; -} - -- (NSObject *)createArgsCodec { - return [FlutterStandardMessageCodec sharedInstance]; -} - -- (NSObject *)createWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args { - if (@available(iOS 11.0, *)) { - [_cookieManager setCookiesForData:args[@"cookies"]]; - } - - FLTWebViewController *webviewController = [[FLTWebViewController alloc] initWithFrame:frame - viewIdentifier:viewId - arguments:args - binaryMessenger:_messenger]; - return webviewController; -} - -@end - -@implementation FLTWKWebView - -- (void)setFrame:(CGRect)frame { - [super setFrame:frame]; - self.scrollView.contentInset = UIEdgeInsetsZero; - // We don't want the contentInsets to be adjusted by iOS, flutter should always take control of - // webview's contentInsets. - // self.scrollView.contentInset = UIEdgeInsetsZero; - if (@available(iOS 11, *)) { - // Above iOS 11, adjust contentInset to compensate the adjustedContentInset so the sum will - // always be 0. - if (UIEdgeInsetsEqualToEdgeInsets(self.scrollView.adjustedContentInset, UIEdgeInsetsZero)) { - return; - } - UIEdgeInsets insetToAdjust = self.scrollView.adjustedContentInset; - self.scrollView.contentInset = UIEdgeInsetsMake(-insetToAdjust.top, -insetToAdjust.left, - -insetToAdjust.bottom, -insetToAdjust.right); - } -} - -@end - -@implementation FLTWebViewController { - FLTWKWebView *_webView; - int64_t _viewId; - FlutterMethodChannel *_channel; - NSString *_currentUrl; - // The set of registered JavaScript channel names. - NSMutableSet *_javaScriptChannelNames; - FLTWKNavigationDelegate *_navigationDelegate; - FLTWKProgressionDelegate *_progressionDelegate; -} - -- (instancetype)initWithFrame:(CGRect)frame - viewIdentifier:(int64_t)viewId - arguments:(id _Nullable)args - binaryMessenger:(NSObject *)messenger { - if (self = [super init]) { - _viewId = viewId; - - NSString *channelName = [NSString stringWithFormat:@"plugins.flutter.io/webview_%lld", viewId]; - _channel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:messenger]; - _javaScriptChannelNames = [[NSMutableSet alloc] init]; - - WKUserContentController *userContentController = [[WKUserContentController alloc] init]; - if ([args[@"javascriptChannelNames"] isKindOfClass:[NSArray class]]) { - NSArray *javaScriptChannelNames = args[@"javascriptChannelNames"]; - [_javaScriptChannelNames addObjectsFromArray:javaScriptChannelNames]; - [self registerJavaScriptChannels:_javaScriptChannelNames controller:userContentController]; - } - - NSDictionary *settings = args[@"settings"]; - - WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init]; - [self applyConfigurationSettings:settings toConfiguration:configuration]; - configuration.userContentController = userContentController; - [self updateAutoMediaPlaybackPolicy:args[@"autoMediaPlaybackPolicy"] - inConfiguration:configuration]; - - _webView = [[FLTWKWebView alloc] initWithFrame:frame configuration:configuration]; - - // Background color - NSNumber *backgroundColorNSNumber = args[@"backgroundColor"]; - if ([backgroundColorNSNumber isKindOfClass:[NSNumber class]]) { - int backgroundColorInt = [backgroundColorNSNumber intValue]; - UIColor *backgroundColor = [UIColor colorWithRed:(backgroundColorInt >> 16 & 0xff) / 255.0 - green:(backgroundColorInt >> 8 & 0xff) / 255.0 - blue:(backgroundColorInt & 0xff) / 255.0 - alpha:(backgroundColorInt >> 24 & 0xff) / 255.0]; - _webView.opaque = NO; - _webView.backgroundColor = UIColor.clearColor; - _webView.scrollView.backgroundColor = backgroundColor; - } - - _navigationDelegate = [[FLTWKNavigationDelegate alloc] initWithChannel:_channel]; - _webView.UIDelegate = self; - _webView.navigationDelegate = _navigationDelegate; - __weak __typeof__(self) weakSelf = self; - [_channel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) { - [weakSelf onMethodCall:call result:result]; - }]; - - if (@available(iOS 11.0, *)) { - _webView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; - if (@available(iOS 13.0, *)) { - _webView.scrollView.automaticallyAdjustsScrollIndicatorInsets = NO; - } - } - - [self applySettings:settings]; - // TODO(amirh): return an error if apply settings failed once it's possible to do so. - // https://github.com/flutter/flutter/issues/36228 - - NSString *initialUrl = args[@"initialUrl"]; - if ([initialUrl isKindOfClass:[NSString class]]) { - NSURL *url = [NSURL URLWithString:initialUrl]; - if (url) { - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; - [_webView loadRequest:request]; - } - } - } - return self; -} - -- (void)dealloc { - if (_progressionDelegate != nil) { - [_progressionDelegate stopObservingProgress:_webView]; - } -} - -- (UIView *)view { - return _webView; -} - -- (void)onMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { - if ([[call method] isEqualToString:@"updateSettings"]) { - [self onUpdateSettings:call result:result]; - } else if ([[call method] isEqualToString:@"loadFile"]) { - [self onLoadFile:call result:result]; - } else if ([[call method] isEqualToString:@"loadFlutterAsset"]) { - [self onLoadFlutterAsset:call result:result]; - } else if ([[call method] isEqualToString:@"loadHtmlString"]) { - [self onLoadHtmlString:call result:result]; - } else if ([[call method] isEqualToString:@"loadUrl"]) { - [self onLoadUrl:call result:result]; - } else if ([[call method] isEqualToString:@"loadRequest"]) { - [self onLoadRequest:call result:result]; - } else if ([[call method] isEqualToString:@"canGoBack"]) { - [self onCanGoBack:call result:result]; - } else if ([[call method] isEqualToString:@"canGoForward"]) { - [self onCanGoForward:call result:result]; - } else if ([[call method] isEqualToString:@"goBack"]) { - [self onGoBack:call result:result]; - } else if ([[call method] isEqualToString:@"goForward"]) { - [self onGoForward:call result:result]; - } else if ([[call method] isEqualToString:@"reload"]) { - [self onReload:call result:result]; - } else if ([[call method] isEqualToString:@"currentUrl"]) { - [self onCurrentUrl:call result:result]; - } else if ([[call method] isEqualToString:@"evaluateJavascript"]) { - [self onEvaluateJavaScript:call result:result]; - } else if ([[call method] isEqualToString:@"runJavascript"]) { - [self onRunJavaScript:call result:result sendReturnValue:NO]; - } else if ([[call method] isEqualToString:@"runJavascriptReturningResult"]) { - [self onRunJavaScript:call result:result sendReturnValue:YES]; - } else if ([[call method] isEqualToString:@"addJavascriptChannels"]) { - [self onAddJavaScriptChannels:call result:result]; - } else if ([[call method] isEqualToString:@"removeJavascriptChannels"]) { - [self onRemoveJavaScriptChannels:call result:result]; - } else if ([[call method] isEqualToString:@"clearCache"]) { - [self clearCache:result]; - } else if ([[call method] isEqualToString:@"getTitle"]) { - [self onGetTitle:result]; - } else if ([[call method] isEqualToString:@"scrollTo"]) { - [self onScrollTo:call result:result]; - } else if ([[call method] isEqualToString:@"scrollBy"]) { - [self onScrollBy:call result:result]; - } else if ([[call method] isEqualToString:@"getScrollX"]) { - [self getScrollX:call result:result]; - } else if ([[call method] isEqualToString:@"getScrollY"]) { - [self getScrollY:call result:result]; - } else { - result(FlutterMethodNotImplemented); - } -} - -- (void)onUpdateSettings:(FlutterMethodCall *)call result:(FlutterResult)result { - NSString *error = [self applySettings:[call arguments]]; - if (error == nil) { - result(nil); - return; - } - result([FlutterError errorWithCode:@"updateSettings_failed" message:error details:nil]); -} - -- (void)onLoadFile:(FlutterMethodCall *)call result:(FlutterResult)result { - NSString *error = nil; - if (![FLTWebViewController isValidStringArgument:[call arguments] withErrorMessage:&error]) { - result([FlutterError errorWithCode:@"loadFile_failed" - message:@"Failed parsing file path." - details:error]); - return; - } - - NSURL *url = [NSURL fileURLWithPath:[call arguments] isDirectory:NO]; - - if (!url) { - NSString *errorDetails = [NSString stringWithFormat:@"Initializing NSURL with the supplied " - @"'%@' path resulted in a nil value.", - [call arguments]]; - result([FlutterError errorWithCode:@"loadFile_failed" - message:@"Failed parsing file path." - details:errorDetails]); - return; - } - - NSURL *baseUrl = [url URLByDeletingLastPathComponent]; - - [_webView loadFileURL:url allowingReadAccessToURL:baseUrl]; - result(nil); -} - -- (void)onLoadFlutterAsset:(FlutterMethodCall *)call result:(FlutterResult)result { - NSString *error = nil; - if (![FLTWebViewController isValidStringArgument:[call arguments] withErrorMessage:&error]) { - result([FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" - message:@"Supplied asset key is not valid." - details:error]); - return; - } - - NSString *assetKey = [call arguments]; - NSString *assetFilePath = [FlutterDartProject lookupKeyForAsset:assetKey]; - NSURL *url = [[NSBundle mainBundle] URLForResource:[assetFilePath stringByDeletingPathExtension] - withExtension:assetFilePath.pathExtension]; - - if (!url) { - result([FlutterError - errorWithCode:@"loadFlutterAsset_invalidKey" - message:@"Failed parsing file path for supplied key." - details:[NSString - stringWithFormat:@"Failed to convert path '%@' into NSURL for key '%@'.", - assetFilePath, assetKey]]); - return; - } - - [_webView loadFileURL:url allowingReadAccessToURL:[url URLByDeletingLastPathComponent]]; - result(nil); -} - -- (void)onLoadHtmlString:(FlutterMethodCall *)call result:(FlutterResult)result { - NSDictionary *arguments = [call arguments]; - if (![arguments isKindOfClass:NSDictionary.class]) { - result([FlutterError - errorWithCode:@"loadHtmlString_failed" - message:@"Failed parsing arguments." - details:@"Arguments should be a dictionary containing at least a 'html' element and " - @"optionally a 'baseUrl' argument. For example: `@{ @\"html\": @\"some html " - @"code\", @\"baseUrl\": @\"https://flutter.dev\" }`"]); - return; - } - - NSString *htmlString = [call arguments][@"html"]; - NSString *baseUrl = - [call arguments][@"baseUrl"] == [NSNull null] ? nil : [call arguments][@"baseUrl"]; - NSString *error = nil; - if (![FLTWebViewController isValidStringArgument:htmlString withErrorMessage:&error]) { - result([FlutterError errorWithCode:@"loadHtmlString_failed" - message:@"Failed parsing HTML string argument." - details:error]); - return; - } - - [_webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:baseUrl]]; - result(nil); -} - -- (void)onLoadUrl:(FlutterMethodCall *)call result:(FlutterResult)result { - NSMutableDictionary *requestData = [[NSMutableDictionary alloc] init]; - if (call.arguments[@"url"]) { - requestData[@"uri"] = call.arguments[@"url"]; - } - if (call.arguments[@"headers"]) { - requestData[@"headers"] = call.arguments[@"headers"]; - } - NSURLRequest *request = [self buildNSURLRequest:@{@"request" : requestData}]; - if (!request) { - result([FlutterError - errorWithCode:@"loadUrl_failed" - message:@"Failed parsing the URL" - details:[NSString stringWithFormat:@"Request was: '%@'", [call arguments]]]); - } else { - [_webView loadRequest:request]; - result(nil); - } -} - -- (void)onLoadRequest:(FlutterMethodCall *)call result:(FlutterResult)result { - NSURLRequest *request = [self buildNSURLRequest:[call arguments]]; - if (!request) { - result([FlutterError - errorWithCode:@"loadRequest_failed" - message:@"Failed parsing the URL" - details:[NSString stringWithFormat:@"Request was: '%@'", [call arguments]]]); - } else { - [_webView loadRequest:request]; - result(nil); - } -} - -- (void)onCanGoBack:(FlutterMethodCall *)call result:(FlutterResult)result { - BOOL canGoBack = [_webView canGoBack]; - result(@(canGoBack)); -} - -- (void)onCanGoForward:(FlutterMethodCall *)call result:(FlutterResult)result { - BOOL canGoForward = [_webView canGoForward]; - result(@(canGoForward)); -} - -- (void)onGoBack:(FlutterMethodCall *)call result:(FlutterResult)result { - [_webView goBack]; - result(nil); -} - -- (void)onGoForward:(FlutterMethodCall *)call result:(FlutterResult)result { - [_webView goForward]; - result(nil); -} - -- (void)onReload:(FlutterMethodCall *)call result:(FlutterResult)result { - [_webView reload]; - result(nil); -} - -- (void)onCurrentUrl:(FlutterMethodCall *)call result:(FlutterResult)result { - _currentUrl = [[_webView URL] absoluteString]; - result(_currentUrl); -} - -- (void)onEvaluateJavaScript:(FlutterMethodCall *)call result:(FlutterResult)result { - NSString *jsString = [call arguments]; - if (!jsString) { - result([FlutterError errorWithCode:@"evaluateJavaScript_failed" - message:@"JavaScript String cannot be null" - details:nil]); - return; - } - [_webView evaluateJavaScript:jsString - completionHandler:^(_Nullable id evaluateResult, NSError *_Nullable error) { - if (error) { - result([FlutterError - errorWithCode:@"evaluateJavaScript_failed" - message:@"Failed evaluating JavaScript" - details:[NSString stringWithFormat:@"JavaScript string was: '%@'\n%@", - jsString, error]]); - } else { - result([NSString stringWithFormat:@"%@", evaluateResult]); - } - }]; -} - -- (void)onRunJavaScript:(FlutterMethodCall *)call - result:(FlutterResult)result - sendReturnValue:(BOOL)sendReturnValue { - NSString *jsString = [call arguments]; - if (!jsString) { - result([FlutterError errorWithCode:@"runJavascript_failed" - message:@"JavaScript String cannot be null" - details:nil]); - return; - } - [_webView - evaluateJavaScript:jsString - completionHandler:^(_Nullable id evaluateResult, NSError *_Nullable error) { - if (error) { - // WebKit will throw an error (WKErrorJavaScriptResultTypeIsUnsupported) when the - // type of the evaluated value is unsupported. This also goes for - // `null` and `undefined` on iOS 14+, for example when running a void function. - // For ease of use this specific error is ignored when no return value is expected. - BOOL sendError = - sendReturnValue || error.code != WKErrorJavaScriptResultTypeIsUnsupported; - result(sendError - ? [FlutterError - errorWithCode:(sendReturnValue ? @"runJavascriptReturningResult_failed" - : @"runJavascript_failed") - message:@"Failed running JavaScript" - details:[NSString - stringWithFormat:@"JavaScript string was: '%@'\n%@", - jsString, error]] - : nil); - return; - } - result(sendReturnValue ? [NSString stringWithFormat:@"%@", evaluateResult] : nil); - }]; -} - -- (void)onAddJavaScriptChannels:(FlutterMethodCall *)call result:(FlutterResult)result { - NSArray *channelNames = [call arguments]; - NSSet *channelNamesSet = [[NSSet alloc] initWithArray:channelNames]; - [_javaScriptChannelNames addObjectsFromArray:channelNames]; - [self registerJavaScriptChannels:channelNamesSet - controller:_webView.configuration.userContentController]; - result(nil); -} - -- (void)onRemoveJavaScriptChannels:(FlutterMethodCall *)call result:(FlutterResult)result { - // WkWebView does not support removing a single user script, so instead we remove all - // user scripts, all message handlers. And re-register channels that shouldn't be removed. - [_webView.configuration.userContentController removeAllUserScripts]; - for (NSString *channelName in _javaScriptChannelNames) { - [_webView.configuration.userContentController removeScriptMessageHandlerForName:channelName]; - } - - NSArray *channelNamesToRemove = [call arguments]; - for (NSString *channelName in channelNamesToRemove) { - [_javaScriptChannelNames removeObject:channelName]; - } - - [self registerJavaScriptChannels:_javaScriptChannelNames - controller:_webView.configuration.userContentController]; - result(nil); -} - -- (void)clearCache:(FlutterResult)result { - NSSet *cacheDataTypes = [WKWebsiteDataStore allWebsiteDataTypes]; - WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore]; - NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0]; - [dataStore removeDataOfTypes:cacheDataTypes - modifiedSince:dateFrom - completionHandler:^{ - result(nil); - }]; -} - -- (void)onGetTitle:(FlutterResult)result { - NSString *title = _webView.title; - result(title); -} - -- (void)onScrollTo:(FlutterMethodCall *)call result:(FlutterResult)result { - NSDictionary *arguments = [call arguments]; - int x = [arguments[@"x"] intValue]; - int y = [arguments[@"y"] intValue]; - - _webView.scrollView.contentOffset = CGPointMake(x, y); - result(nil); -} - -- (void)onScrollBy:(FlutterMethodCall *)call result:(FlutterResult)result { - CGPoint contentOffset = _webView.scrollView.contentOffset; - - NSDictionary *arguments = [call arguments]; - int x = [arguments[@"x"] intValue] + contentOffset.x; - int y = [arguments[@"y"] intValue] + contentOffset.y; - - _webView.scrollView.contentOffset = CGPointMake(x, y); - result(nil); -} - -- (void)getScrollX:(FlutterMethodCall *)call result:(FlutterResult)result { - int offsetX = _webView.scrollView.contentOffset.x; - result(@(offsetX)); -} - -- (void)getScrollY:(FlutterMethodCall *)call result:(FlutterResult)result { - int offsetY = _webView.scrollView.contentOffset.y; - result(@(offsetY)); -} - -// Returns nil when successful, or an error message when one or more keys are unknown. -- (NSString *)applySettings:(NSDictionary *)settings { - NSMutableArray *unknownKeys = [[NSMutableArray alloc] init]; - for (NSString *key in settings) { - if ([key isEqualToString:@"jsMode"]) { - NSNumber *mode = settings[key]; - [self updateJsMode:mode]; - } else if ([key isEqualToString:@"hasNavigationDelegate"]) { - NSNumber *hasDartNavigationDelegate = settings[key]; - _navigationDelegate.hasDartNavigationDelegate = [hasDartNavigationDelegate boolValue]; - } else if ([key isEqualToString:@"hasProgressTracking"]) { - NSNumber *hasProgressTrackingValue = settings[key]; - bool hasProgressTracking = [hasProgressTrackingValue boolValue]; - if (hasProgressTracking) { - _progressionDelegate = [[FLTWKProgressionDelegate alloc] initWithWebView:_webView - channel:_channel]; - } - } else if ([key isEqualToString:@"debuggingEnabled"]) { - // no-op debugging is always enabled on iOS. - } else if ([key isEqualToString:@"gestureNavigationEnabled"]) { - NSNumber *allowsBackForwardNavigationGestures = settings[key]; - _webView.allowsBackForwardNavigationGestures = - [allowsBackForwardNavigationGestures boolValue]; - } else if ([key isEqualToString:@"userAgent"]) { - NSString *userAgent = settings[key]; - [self updateUserAgent:[userAgent isEqual:[NSNull null]] ? nil : userAgent]; - } else if ([key isEqualToString:@"zoomEnabled"]) { - NSNumber *zoomEnabled = settings[key]; - _navigationDelegate.shouldEnableZoom = [zoomEnabled boolValue]; - } else { - [unknownKeys addObject:key]; - } - } - if ([unknownKeys count] == 0) { - return nil; - } - return [NSString stringWithFormat:@"webview_flutter: unknown setting keys: {%@}", - [unknownKeys componentsJoinedByString:@", "]]; -} - -- (void)applyConfigurationSettings:(NSDictionary *)settings - toConfiguration:(WKWebViewConfiguration *)configuration { - NSAssert(configuration != _webView.configuration, - @"configuration needs to be updated before webView.configuration."); - for (NSString *key in settings) { - if ([key isEqualToString:@"allowsInlineMediaPlayback"]) { - NSNumber *allowsInlineMediaPlayback = settings[key]; - configuration.allowsInlineMediaPlayback = [allowsInlineMediaPlayback boolValue]; - } - } -} - -- (void)updateJsMode:(NSNumber *)mode { - WKPreferences *preferences = [[_webView configuration] preferences]; - switch ([mode integerValue]) { - case 0: // disabled - [preferences setJavaScriptEnabled:NO]; - break; - case 1: // unrestricted - [preferences setJavaScriptEnabled:YES]; - break; - default: - NSLog(@"webview_flutter: unknown JavaScript mode: %@", mode); - } -} - -- (void)updateAutoMediaPlaybackPolicy:(NSNumber *)policy - inConfiguration:(WKWebViewConfiguration *)configuration { - switch ([policy integerValue]) { - case 0: // require_user_action_for_all_media_types - if (@available(iOS 10.0, *)) { - configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeAll; - } else { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - configuration.requiresUserActionForMediaPlayback = true; -#pragma clang diagnostic pop - } - break; - case 1: // always_allow - if (@available(iOS 10.0, *)) { - configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone; - } else { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - configuration.requiresUserActionForMediaPlayback = false; -#pragma clang diagnostic pop - } - break; - default: - NSLog(@"webview_flutter: unknown auto media playback policy: %@", policy); - } -} - -/** - * Parses the method call arguments and converts them to an NSURLRequest object. - * - * @param arguments the method call arguments. - * - * @return NSURLRequest object. - */ -- (NSURLRequest *)buildNSURLRequest:(NSDictionary *)arguments { - id requestParameters = arguments[@"request"]; - if (![requestParameters isKindOfClass:[NSDictionary class]]) { - return nil; - } - - NSString *urlString = requestParameters[@"uri"]; - if (!urlString) { - return nil; - } - - NSURL *url = [NSURL URLWithString:urlString]; - if (!url) { - return nil; - } - - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; - - NSString *httpMethod = requestParameters[@"method"]; - if (httpMethod) { - [request setHTTPMethod:httpMethod]; - } - - id httpBody = requestParameters[@"body"]; - if ([httpBody isKindOfClass:[FlutterStandardTypedData class]]) { - [request setHTTPBody:[httpBody data]]; - } - - id headers = requestParameters[@"headers"]; - if ([headers isKindOfClass:[NSDictionary class]]) { - [request setAllHTTPHeaderFields:headers]; - } - - return request; -} - -- (void)registerJavaScriptChannels:(NSSet *)channelNames - controller:(WKUserContentController *)userContentController { - for (NSString *channelName in channelNames) { - FLTJavaScriptChannel *channel = - [[FLTJavaScriptChannel alloc] initWithMethodChannel:_channel - javaScriptChannelName:channelName]; - [userContentController addScriptMessageHandler:channel name:channelName]; - NSString *wrapperSource = [NSString - stringWithFormat:@"window.%@ = webkit.messageHandlers.%@;", channelName, channelName]; - WKUserScript *wrapperScript = - [[WKUserScript alloc] initWithSource:wrapperSource - injectionTime:WKUserScriptInjectionTimeAtDocumentStart - forMainFrameOnly:NO]; - [userContentController addUserScript:wrapperScript]; - } -} - -- (void)updateUserAgent:(NSString *)userAgent { - [_webView setCustomUserAgent:userAgent]; -} - -/** - * Validates if the given `argument` is a non-null, non-empty string. - * - * @param argument The argument that should be validated. - * @param errorDetails An optional NSString variable which will contain a detailed error message in - * case the supplied argument is not valid. - * @return `YES` if the given `argument` is a valid non-null, non-empty string; otherwise `NO`. - */ -+ (BOOL)isValidStringArgument:(id)argument withErrorMessage:(NSString **)errorDetails { - if (!argument) { - if (errorDetails) { - *errorDetails = @"Argument is nil."; - } - return NO; - } - if (![argument isKindOfClass:NSString.class]) { - if (errorDetails) { - *errorDetails = @"Argument is not of type NSString."; - } - return NO; - } - if (![argument length]) { - if (errorDetails) { - *errorDetails = @"Argument contains an empty string."; - } - return NO; - } - - return YES; -} - -#pragma mark WKUIDelegate - -- (WKWebView *)webView:(WKWebView *)webView - createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration - forNavigationAction:(WKNavigationAction *)navigationAction - windowFeatures:(WKWindowFeatures *)windowFeatures { - if (!navigationAction.targetFrame.isMainFrame) { - [webView loadRequest:navigationAction.request]; - } - - return nil; -} - -@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.modulemap b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.modulemap index 639d89498d00..1b7eaf646ee9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.modulemap +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.modulemap @@ -5,8 +5,6 @@ framework module webview_flutter_wkwebview { module * { export * } explicit module Test { - header "FlutterWebView_Test.h" - header "FLTCookieManager_Test.h" header "FWFInstanceManager_Test.h" } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView_Test.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView_Test.h deleted file mode 100644 index a84954e17c97..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView_Test.h +++ /dev/null @@ -1,21 +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. - -// This header is available in the Test module. Import via "@import webview_flutter_wkwebview.Test;" - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface FLTWebViewController () - -- (NSURLRequest *)buildNSURLRequest:(NSDictionary *)arguments; - -- (void)onLoadUrl:(FlutterMethodCall *)call result:(FlutterResult)result; - -- (void)onLoadRequest:(FlutterMethodCall *)call result:(FlutterResult)result; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.h deleted file mode 100644 index f442d7af8d87..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.h +++ /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 -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface FLTJavaScriptChannel : NSObject - -- (instancetype)initWithMethodChannel:(FlutterMethodChannel *)methodChannel - javaScriptChannelName:(NSString *)javaScriptChannelName; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.m deleted file mode 100644 index 1aed25f1b7d9..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/JavaScriptChannelHandler.m +++ /dev/null @@ -1,36 +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 "JavaScriptChannelHandler.h" - -@implementation FLTJavaScriptChannel { - FlutterMethodChannel *_methodChannel; - NSString *_javaScriptChannelName; -} - -- (instancetype)initWithMethodChannel:(FlutterMethodChannel *)methodChannel - javaScriptChannelName:(NSString *)javaScriptChannelName { - self = [super init]; - NSAssert(methodChannel != nil, @"methodChannel must not be null."); - NSAssert(javaScriptChannelName != nil, @"javaScriptChannelName must not be null."); - if (self) { - _methodChannel = methodChannel; - _javaScriptChannelName = javaScriptChannelName; - } - return self; -} - -- (void)userContentController:(WKUserContentController *)userContentController - didReceiveScriptMessage:(WKScriptMessage *)message { - NSAssert(_methodChannel != nil, @"Can't send a message to an unitialized JavaScript channel."); - NSAssert(_javaScriptChannelName != nil, - @"Can't send a message to an unitialized JavaScript channel."); - NSDictionary *arguments = @{ - @"channel" : _javaScriptChannelName, - @"message" : [NSString stringWithFormat:@"%@", message.body] - }; - [_methodChannel invokeMethod:@"javascriptChannelMessage" arguments:arguments]; -} - -@end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h index 0aa409e4b31f..dbcd876d15c9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h @@ -3,9 +3,6 @@ // found in the LICENSE file. #import -#import -#import -#import #import #import #import @@ -22,5 +19,3 @@ #import #import #import -#import -#import diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart deleted file mode 100644 index 46322319ae89..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_cookie_manager.dart +++ /dev/null @@ -1,54 +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:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; -import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; -import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; - -/// Handles all cookie operations for the WebView platform. -class WebKitCookieManager extends WebViewCookieManagerPlatform { - /// Constructs a [WebKitCookieManager]. - WebKitCookieManager({WKWebsiteDataStore? websiteDataStore}) - : websiteDataStore = - websiteDataStore ?? WKWebsiteDataStore.defaultDataStore; - - /// Manages stored data for [WKWebView]s. - final WKWebsiteDataStore websiteDataStore; - - @override - Future clearCookies() async { - return websiteDataStore.removeDataOfTypes( - {WKWebsiteDataType.cookies}, - DateTime.fromMillisecondsSinceEpoch(0), - ); - } - - @override - Future setCookie(WebViewCookie cookie) { - if (!_isValidPath(cookie.path)) { - throw ArgumentError( - 'The path property for the provided cookie was not given a legal value.'); - } - - return websiteDataStore.httpCookieStore.setCookie( - NSHttpCookie.withProperties( - { - NSHttpCookiePropertyKey.name: cookie.name, - NSHttpCookiePropertyKey.value: cookie.value, - NSHttpCookiePropertyKey.domain: cookie.domain, - NSHttpCookiePropertyKey.path: cookie.path, - }, - ), - ); - } - - bool _isValidPath(String path) { - // Permitted ranges based on RFC6265bis: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1 - return !path.codeUnits.any( - (int char) { - return (char < 0x20 || char > 0x3A) && (char < 0x3C || char > 0x7E); - }, - ); - } -} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 852f9caa0c49..327210983ae2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart @@ -340,10 +340,6 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { @override Future evaluateJavascript(String javascript) async { final Object? result = await webView.evaluateJavaScript(javascript); - // The legacy implementation of webview_flutter_wkwebview would convert - // objects to strings before returning them to Dart. This method attempts - // to converts Dart objects to Strings the way it is done in Objective-C - // to avoid breaking users expecting the same String format. return _asObjectiveCString(result); } @@ -373,7 +369,7 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { 'Use `runJavascript` when expecting a null return value.', ); } - return result.toString(); + return _asObjectiveCString(result); } @override @@ -603,6 +599,12 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { ); } + // The legacy implementation of webview_flutter_wkwebview would convert + // objects to strings before returning them to Dart. This method attempts + // to converts Dart objects to Strings the way it is done in Objective-C + // to avoid breaking users expecting the same String format. + // TODO(bparrishMines): Remove this method with the next breaking change. + // See https://github.com/flutter/flutter/issues/107491 String _asObjectiveCString(Object? value, {bool inContainer = false}) { if (value == null) { // An NSNull inside an NSArray or NSDictionary is represented as a String diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webview_cupertino.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webview_cupertino.dart index cd00d9a7d14b..f046ea4378b8 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webview_cupertino.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webview_cupertino.dart @@ -9,6 +9,9 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart'; + +import 'foundation/foundation.dart'; /// Builds an iOS webview. /// @@ -25,22 +28,24 @@ class CupertinoWebView implements WebViewPlatform { WebViewPlatformCreatedCallback? onWebViewPlatformCreated, Set>? gestureRecognizers, }) { - return UiKitView( - viewType: 'plugins.flutter.io/webview', - onPlatformViewCreated: (int id) { - if (onWebViewPlatformCreated == null) { - return; - } - onWebViewPlatformCreated(MethodChannelWebViewPlatform( - id, - webViewPlatformCallbacksHandler, - javascriptChannelRegistry, - )); + return WebKitWebViewWidget( + creationParams: creationParams, + callbacksHandler: webViewPlatformCallbacksHandler, + javascriptChannelRegistry: javascriptChannelRegistry, + onBuildWidget: (WebKitWebViewPlatformController controller) { + return UiKitView( + viewType: 'plugins.flutter.io/webview', + onPlatformViewCreated: (int id) { + if (onWebViewPlatformCreated != null) { + onWebViewPlatformCreated(controller); + } + }, + gestureRecognizers: gestureRecognizers, + creationParams: + NSObject.globalInstanceManager.getIdentifier(controller.webView), + creationParamsCodec: const StandardMessageCodec(), + ); }, - gestureRecognizers: gestureRecognizers, - creationParams: - MethodChannelWebViewPlatform.creationParamsToMap(creationParams), - creationParamsCodec: const StandardMessageCodec(), ); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/wkwebview_cookie_manager.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/wkwebview_cookie_manager.dart index d460ce02ab68..59c9f580db74 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/wkwebview_cookie_manager.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/wkwebview_cookie_manager.dart @@ -3,11 +3,26 @@ // found in the LICENSE file. import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; -/// Handles all cookie operations for the current platform. +/// Handles all cookie operations for the WebView platform. class WKWebViewCookieManager extends WebViewCookieManagerPlatform { + /// Constructs a [WKWebViewCookieManager]. + WKWebViewCookieManager({WKWebsiteDataStore? websiteDataStore}) + : websiteDataStore = + websiteDataStore ?? WKWebsiteDataStore.defaultDataStore; + + /// Manages stored data for [WKWebView]s. + final WKWebsiteDataStore websiteDataStore; + @override - Future clearCookies() => MethodChannelWebViewPlatform.clearCookies(); + Future clearCookies() async { + return websiteDataStore.removeDataOfTypes( + {WKWebsiteDataType.cookies}, + DateTime.fromMillisecondsSinceEpoch(0), + ); + } @override Future setCookie(WebViewCookie cookie) { @@ -15,16 +30,25 @@ class WKWebViewCookieManager extends WebViewCookieManagerPlatform { throw ArgumentError( 'The path property for the provided cookie was not given a legal value.'); } - return MethodChannelWebViewPlatform.setCookie(cookie); + + return websiteDataStore.httpCookieStore.setCookie( + NSHttpCookie.withProperties( + { + NSHttpCookiePropertyKey.name: cookie.name, + NSHttpCookiePropertyKey.value: cookie.value, + NSHttpCookiePropertyKey.domain: cookie.domain, + NSHttpCookiePropertyKey.path: cookie.path, + }, + ), + ); } bool _isValidPath(String path) { // Permitted ranges based on RFC6265bis: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1 - for (final int char in path.codeUnits) { - if ((char < 0x20 || char > 0x3A) && (char < 0x3C || char > 0x7E)) { - return false; - } - } - return true; + return !path.codeUnits.any( + (int char) { + return (char < 0x20 || char > 0x3A) && (char < 0x3C || char > 0x7E); + }, + ); } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index c69d0d51b622..cd92b8625105 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/plugins/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: 2.8.1 +version: 2.9.0 environment: sdk: ">=2.17.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart index 6baff12eda17..73d8c8f33a11 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_cookie_manager_test.dart @@ -8,7 +8,7 @@ import 'package:mockito/mockito.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; -import 'package:webview_flutter_wkwebview/src/web_kit_cookie_manager.dart'; +import 'package:webview_flutter_wkwebview/src/wkwebview_cookie_manager.dart'; import 'web_kit_cookie_manager_test.mocks.dart'; @@ -23,7 +23,7 @@ void main() { late MockWKWebsiteDataStore mockWebsiteDataStore; late MockWKHttpCookieStore mockWKHttpCookieStore; - late WebKitCookieManager cookieManager; + late WKWebViewCookieManager cookieManager; setUp(() { mockWebsiteDataStore = MockWKWebsiteDataStore(); @@ -32,7 +32,7 @@ void main() { .thenReturn(mockWKHttpCookieStore); cookieManager = - WebKitCookieManager(websiteDataStore: mockWebsiteDataStore); + WKWebViewCookieManager(websiteDataStore: mockWebsiteDataStore); }); test('clearCookies', () async { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index b5f7b1a486dd..c6d90d04e35e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -745,6 +745,23 @@ void main() { ); }); + testWidgets('runJavascriptReturningResult with bool return value', + (WidgetTester tester) async { + await buildWidget(tester); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value(false), + ); + // The legacy implementation of webview_flutter_wkwebview would convert + // objects to strings before returning them to Dart. This verifies bool + // is represented the way it is in Objective-C. + // `NSNumber.description` converts bool values to a 1 or 0. + expect( + testController.runJavascriptReturningResult('runJavaScript'), + completion('0'), + ); + }); + testWidgets('runJavascript', (WidgetTester tester) async { await buildWidget(tester); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/wkwebview_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/wkwebview_cookie_manager_test.dart deleted file mode 100644 index 54b1921583b9..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/wkwebview_cookie_manager_test.dart +++ /dev/null @@ -1,65 +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/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; -import 'package:webview_flutter_wkwebview/src/wkwebview_cookie_manager.dart'; - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - const MethodChannel cookieChannel = - MethodChannel('plugins.flutter.io/cookie_manager'); - final List log = []; - - cookieChannel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - - if (methodCall.method == 'clearCookies') { - return true; - } - - // Return null explicitly instead of relying on the implicit null - // returned by the method channel if no return statement is specified. - return null; - }); - - tearDown(() { - log.clear(); - }); - - test('clearCookies should call `clearCookies` on the method channel', - () async { - await WKWebViewCookieManager().clearCookies(); - expect( - log, - [ - isMethodCall( - 'clearCookies', - arguments: null, - ), - ], - ); - }); - - test('setCookie should call `setCookie` on the method channel', () async { - await WKWebViewCookieManager().setCookie( - const WebViewCookie(name: 'foo', value: 'bar', domain: 'flutter.dev'), - ); - expect( - log, - [ - isMethodCall( - 'setCookie', - arguments: { - 'name': 'foo', - 'value': 'bar', - 'domain': 'flutter.dev', - 'path': '/', - }, - ), - ], - ); - }); -} From e6a9ae7b2d962158f279f2f2b23600be55f22629 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Jul 2022 16:00:08 -0700 Subject: [PATCH 496/844] [lifecycle]: Bump gradle from 3.5.0 to 7.2.1 in /packages/flutter_plugin_android_lifecycle/android (#5846) --- packages/flutter_plugin_android_lifecycle/CHANGELOG.md | 4 ++++ .../flutter_plugin_android_lifecycle/android/build.gradle | 2 +- packages/flutter_plugin_android_lifecycle/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md index a0a3f67bed46..b86228532d00 100644 --- a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md +++ b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.7 + +* Bumps gradle from 3.5.0 to 7.2.1. + ## 2.0.6 * Adds OS version support information to README. diff --git a/packages/flutter_plugin_android_lifecycle/android/build.gradle b/packages/flutter_plugin_android_lifecycle/android/build.gradle index 36695ffef5be..9702604009f3 100644 --- a/packages/flutter_plugin_android_lifecycle/android/build.gradle +++ b/packages/flutter_plugin_android_lifecycle/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:7.2.1' } } diff --git a/packages/flutter_plugin_android_lifecycle/pubspec.yaml b/packages/flutter_plugin_android_lifecycle/pubspec.yaml index 8073cdd9fd76..df8930dd9442 100644 --- a/packages/flutter_plugin_android_lifecycle/pubspec.yaml +++ b/packages/flutter_plugin_android_lifecycle/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_plugin_android_lifecycle description: Flutter plugin for accessing an Android Lifecycle within other plugins. repository: https://github.com/flutter/plugins/tree/main/packages/flutter_plugin_android_lifecycle issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_plugin_android_lifecycle%22 -version: 2.0.6 +version: 2.0.7 environment: sdk: ">=2.12.0 <3.0.0" From 595ff3f49006765fefe5cfe674f3fa00594edc4b Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 12 Jul 2022 20:01:05 -0400 Subject: [PATCH 497/844] [file_selector] Add `allowsAny` to `XTypeGroup` (#6094) --- .../CHANGELOG.md | 5 ++-- .../src/types/x_type_group/x_type_group.dart | 8 +++++ .../pubspec.yaml | 2 +- .../test/x_type_group_test.dart | 29 +++++++++++++++++++ 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md index 100b6ad136a7..b1fa45b708d5 100644 --- a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md +++ b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.1.0 -* Removes unnecessary imports. +* Adds `allowsAny` to `XTypeGroup` as a simple and future-proof way of identifying + wildcard groups. ## 2.0.4 diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart index 2146131023e1..506dc1ce2de9 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart @@ -42,6 +42,14 @@ class XTypeGroup { }; } + /// True if this type group should allow any file. + bool get allowsAny { + return (extensions?.isEmpty ?? true) && + (mimeTypes?.isEmpty ?? true) && + (macUTIs?.isEmpty ?? true) && + (webWildCards?.isEmpty ?? true); + } + static List? _removeLeadingDots(List? exts) => exts ?.map((String ext) => ext.startsWith('.') ? ext.substring(1) : ext) .toList(); diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml index 81ad53a8bab2..4ba84782d436 100644 --- a/packages/file_selector/file_selector_platform_interface/pubspec.yaml +++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%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.0.4 +version: 2.1.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart b/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart index 84f5ca1f0bd2..e09605c109c5 100644 --- a/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart @@ -40,6 +40,35 @@ void main() { expect(jsonMap['mimeTypes'], null); expect(jsonMap['macUTIs'], null); expect(jsonMap['webWildCards'], null); + expect(group.allowsAny, true); + }); + + test('allowsAny treats empty arrays the same as null', () { + final XTypeGroup group = XTypeGroup( + label: 'Any', + extensions: [], + mimeTypes: [], + macUTIs: [], + webWildCards: [], + ); + + expect(group.allowsAny, true); + }); + + test('allowsAny returns false if anything is set', () { + final XTypeGroup extensionOnly = + XTypeGroup(label: 'extensions', extensions: ['txt']); + final XTypeGroup mimeOnly = + XTypeGroup(label: 'mime', mimeTypes: ['text/plain']); + final XTypeGroup utiOnly = + XTypeGroup(label: 'utis', macUTIs: ['public.text']); + final XTypeGroup webOnly = + XTypeGroup(label: 'web', webWildCards: ['.txt']); + + expect(extensionOnly.allowsAny, false); + expect(mimeOnly.allowsAny, false); + expect(utiOnly.allowsAny, false); + expect(webOnly.allowsAny, false); }); test('Leading dots are removed from extensions', () { From 70a6b9af328ab05537e255a6d0037b3c49cdb2ed Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Tue, 12 Jul 2022 17:05:06 -0700 Subject: [PATCH 498/844] [camera] Bump camera_web and camera_android versions to update permission exception codes (#6081) --- packages/camera/camera/CHANGELOG.md | 5 ++++- packages/camera/camera/README.md | 2 -- packages/camera/camera/example/lib/main.dart | 4 ---- packages/camera/camera/pubspec.yaml | 6 +++--- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index b9af4eb81e17..1dc9270824d8 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,5 +1,8 @@ -## NEXT +## 0.10.0 +* **Breaking Change** Bumps default camera_web package version, which updates permission exception code from `cameraPermission` to `CameraAccessDenied`. +* **Breaking Change** Bumps default camera_android package version, which updates permission exception code from `cameraPermission` to + `CameraAccessDenied` and `AudioAccessDenied`. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). ## 0.9.8+1 diff --git a/packages/camera/camera/README.md b/packages/camera/camera/README.md index 610b7175b533..4d7c3d90791a 100644 --- a/packages/camera/camera/README.md +++ b/packages/camera/camera/README.md @@ -98,8 +98,6 @@ Here is a list of all permission error codes that can be thrown: - `AudioAccessRestricted`: iOS only for now. Thrown when audio access is restricted and users cannot grant permission (parental control). -- `cameraPermission`: Android and Web only. A legacy error code for all kinds of camera permission errors. - ### Example Here is a small example flutter app displaying a full screen camera preview. diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index f8f28dd5d23f..b90b0033563a 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -716,10 +716,6 @@ class _CameraExampleHomeState extends State // iOS only showInSnackBar('Audio access is restricted.'); break; - case 'cameraPermission': - // Android & web only - showInSnackBar('Unknown permission error.'); - break; default: _showCameraException(e); break; diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index a23405d083e3..e9a07e6b806c 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.9.8+1 +version: 0.10.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -21,10 +21,10 @@ flutter: default_package: camera_web dependencies: - camera_android: ^0.9.7+1 + camera_android: ^0.10.0 camera_avfoundation: ^0.9.7+1 camera_platform_interface: ^2.2.0 - camera_web: ^0.2.1 + camera_web: ^0.3.0 flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.2 From 25cd6e11407f3c28809f2aa325b3aa7075851f4f Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 13 Jul 2022 07:00:05 -0400 Subject: [PATCH 499/844] [google_maps_flutter] Removes hotfix for test that changes the map size (#6097) --- .../example/integration_test/google_maps_test.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart index 9246466d00cf..8742ff31af42 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart @@ -426,10 +426,6 @@ void main() { }); testWidgets('testInitialCenterLocationAtCenter', (WidgetTester tester) async { - // TODO(bparrishMines): Remove this line when resizing virtual displays doesn't - // clamp displays that are smaller than the screen. - // See https://github.com/flutter/flutter/issues/106750 - AndroidGoogleMapsFlutter.useAndroidViewSurface = true; await tester.binding.setSurfaceSize(const Size(800, 600)); final Completer mapControllerCompleter = From 19f566ba87aaffd06e8869c4cd481eab99f0a11d Mon Sep 17 00:00:00 2001 From: Devesh Pal Date: Wed, 13 Jul 2022 17:20:05 +0530 Subject: [PATCH 500/844] [url_launcher] Update README to use code excerpts. (#6042) --- .../url_launcher/url_launcher/CHANGELOG.md | 4 ++ packages/url_launcher/url_launcher/README.md | 60 +++++++++++----- .../android/app/src/main/AndroidManifest.xml | 12 +++- .../url_launcher/example/build.excerpt.yaml | 20 ++++++ .../url_launcher/example/lib/basic.dart | 34 +++++++++ .../url_launcher/example/lib/encoding.dart | 70 +++++++++++++++++++ .../url_launcher/example/lib/files.dart | 47 +++++++++++++ .../linux/flutter/generated_plugins.cmake | 8 +++ .../url_launcher/example/pubspec.yaml | 2 + .../windows/flutter/generated_plugins.cmake | 8 +++ .../url_launcher/url_launcher/pubspec.yaml | 2 +- script/configs/temp_exclude_excerpt.yaml | 1 - 12 files changed, 247 insertions(+), 21 deletions(-) create mode 100644 packages/url_launcher/url_launcher/example/build.excerpt.yaml create mode 100644 packages/url_launcher/url_launcher/example/lib/basic.dart create mode 100644 packages/url_launcher/url_launcher/example/lib/encoding.dart create mode 100644 packages/url_launcher/url_launcher/example/lib/files.dart diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index e5e98f658617..c6e4b9aed8b3 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.1.5 + +* Migrates `README.md` examples to the [`code-excerpt` system](https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#readme-code). + ## 6.1.4 * Adopts new platform interface method for launching URLs. diff --git a/packages/url_launcher/url_launcher/README.md b/packages/url_launcher/url_launcher/README.md index 9ef6b5aac5c7..e9e4dae476cc 100644 --- a/packages/url_launcher/url_launcher/README.md +++ b/packages/url_launcher/url_launcher/README.md @@ -1,3 +1,5 @@ + + # url_launcher [![pub package](https://img.shields.io/pub/v/url_launcher.svg)](https://pub.dev/packages/url_launcher) @@ -14,6 +16,7 @@ To use this plugin, add `url_launcher` as a [dependency in your pubspec.yaml fil ### Example + ``` dart import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -24,7 +27,7 @@ void main() => runApp( const MaterialApp( home: Material( child: Center( - child: RaisedButton( + child: ElevatedButton( onPressed: _launchUrl, child: Text('Show Flutter homepage'), ), @@ -33,8 +36,10 @@ void main() => runApp( ), ); -void _launchUrl() async { - if (!await launchUrl(_url)) throw 'Could not launch $_url'; +Future _launchUrl() async { + if (!await launchUrl(_url)) { + throw 'Could not launch $_url'; + } } ``` @@ -65,7 +70,10 @@ on Android 11 (API 30) or higher. A `` element must be added to your manifest as a child of the root element. Example: + + ``` xml + @@ -133,22 +141,37 @@ due to [a bug](https://github.com/dart-lang/sdk/issues/43838) in the way `Uri` encodes query parameters. Using `queryParameters` will result in spaces being converted to `+` in many cases. + ```dart String? encodeQueryParameters(Map params) { return params.entries - .map((e) => '${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}') + .map((MapEntry e) => + '${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}') .join('&'); } +// ··· + final Uri emailLaunchUri = Uri( + scheme: 'mailto', + path: 'smith@example.com', + query: encodeQueryParameters({ + 'subject': 'Example Subject & Symbols are allowed!', + }), + ); + + launchUrl(emailLaunchUri); +``` -final Uri emailLaunchUri = Uri( - scheme: 'mailto', - path: 'smith@example.com', - query: encodeQueryParameters({ - 'subject': 'Example Subject & Symbols are allowed!' - }), -); +Encoding for `sms` is slightly different: -launchUrl(emailLaunchUri); + +```dart +final Uri smsLaunchUri = Uri( + scheme: 'sms', + path: '0118 999 881 999 119 7253', + queryParameters: { + 'body': Uri.encodeComponent('Example Subject & Symbols are allowed!'), + }, +); ``` ### URLs not handled by `Uri` @@ -168,14 +191,17 @@ original APIs. We recommend checking first whether the directory or file exists before calling `launchUrl`. Example: + + ```dart -var filePath = '/path/to/file'; +final String filePath = testFile.absolute.path; final Uri uri = Uri.file(filePath); -if (await File(uri.toFilePath()).exists()) { - if (!await launchUrl(uri)) { - throw 'Could not launch $uri'; - } +if (!File(uri.toFilePath()).existsSync()) { + throw '$uri does not exist!'; +} +if (!await launchUrl(uri)) { + throw 'Could not launch $uri'; } ``` diff --git a/packages/url_launcher/url_launcher/example/android/app/src/main/AndroidManifest.xml b/packages/url_launcher/url_launcher/example/android/app/src/main/AndroidManifest.xml index 5c0d0afa0fe4..fe01f2fba9a8 100644 --- a/packages/url_launcher/url_launcher/example/android/app/src/main/AndroidManifest.xml +++ b/packages/url_launcher/url_launcher/example/android/app/src/main/AndroidManifest.xml @@ -7,21 +7,29 @@ --> + + - + + + + - + + + runApp( + const MaterialApp( + home: Material( + child: Center( + child: ElevatedButton( + onPressed: _launchUrl, + child: Text('Show Flutter homepage'), + ), + ), + ), + ), + ); + +Future _launchUrl() async { + if (!await launchUrl(_url)) { + throw 'Could not launch $_url'; + } +} +// #enddocregion basic-example diff --git a/packages/url_launcher/url_launcher/example/lib/encoding.dart b/packages/url_launcher/url_launcher/example/lib/encoding.dart new file mode 100644 index 000000000000..24c724466a77 --- /dev/null +++ b/packages/url_launcher/url_launcher/example/lib/encoding.dart @@ -0,0 +1,70 @@ +// 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. + +// Run this example with: flutter run -t lib/encoding.dart -d emulator + +// This file is used to extract code samples for the README.md file. +// Run update-excerpts if you modify this file. + +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; + +/// Encode [params] so it produces a correct query string. +/// Workaround for: https://github.com/dart-lang/sdk/issues/43838 +// #docregion encode-query-parameters +String? encodeQueryParameters(Map params) { + return params.entries + .map((MapEntry e) => + '${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}') + .join('&'); +} +// #enddocregion encode-query-parameters + +void main() => runApp( + MaterialApp( + home: Material( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: const [ + ElevatedButton( + onPressed: _composeMail, + child: Text('Compose an email'), + ), + ElevatedButton( + onPressed: _composeSms, + child: Text('Compose a SMS'), + ), + ], + ), + ), + ), + ); + +void _composeMail() { +// #docregion encode-query-parameters + final Uri emailLaunchUri = Uri( + scheme: 'mailto', + path: 'smith@example.com', + query: encodeQueryParameters({ + 'subject': 'Example Subject & Symbols are allowed!', + }), + ); + + launchUrl(emailLaunchUri); +// #enddocregion encode-query-parameters +} + +void _composeSms() { +// #docregion sms + final Uri smsLaunchUri = Uri( + scheme: 'sms', + path: '0118 999 881 999 119 7253', + queryParameters: { + 'body': Uri.encodeComponent('Example Subject & Symbols are allowed!'), + }, + ); +// #enddocregion sms + + launchUrl(smsLaunchUri); +} diff --git a/packages/url_launcher/url_launcher/example/lib/files.dart b/packages/url_launcher/url_launcher/example/lib/files.dart new file mode 100644 index 000000000000..d48440670406 --- /dev/null +++ b/packages/url_launcher/url_launcher/example/lib/files.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. + +// Run this example with: flutter run -t lib/files.dart -d linux + +// This file is used to extract code samples for the README.md file. +// Run update-excerpts if you modify this file. +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:path/path.dart' as p; +import 'package:url_launcher/url_launcher.dart'; + +void main() => runApp( + const MaterialApp( + home: Material( + child: Center( + child: ElevatedButton( + onPressed: _openFile, + child: Text('Open File'), + ), + ), + ), + ), + ); + +Future _openFile() async { + // Prepare a file within tmp + final String tempFilePath = p.joinAll([ + ...p.split(Directory.systemTemp.path), + 'flutter_url_launcher_example.txt' + ]); + final File testFile = File(tempFilePath); + await testFile.writeAsString('Hello, world!'); +// #docregion file + final String filePath = testFile.absolute.path; + final Uri uri = Uri.file(filePath); + + if (!File(uri.toFilePath()).existsSync()) { + throw '$uri does not exist!'; + } + if (!await launchUrl(uri)) { + throw 'Could not launch $uri'; + } +// #enddocregion file +} diff --git a/packages/url_launcher/url_launcher/example/linux/flutter/generated_plugins.cmake b/packages/url_launcher/url_launcher/example/linux/flutter/generated_plugins.cmake index 1fc8ed344297..f16b4c34213a 100644 --- a/packages/url_launcher/url_launcher/example/linux/flutter/generated_plugins.cmake +++ b/packages/url_launcher/url_launcher/example/linux/flutter/generated_plugins.cmake @@ -6,6 +6,9 @@ list(APPEND FLUTTER_PLUGIN_LIST url_launcher_linux ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -14,3 +17,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/url_launcher/url_launcher/example/pubspec.yaml b/packages/url_launcher/url_launcher/example/pubspec.yaml index 43b5265c45ec..673785e9e1f6 100644 --- a/packages/url_launcher/url_launcher/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher/example/pubspec.yaml @@ -9,6 +9,7 @@ environment: dependencies: flutter: sdk: flutter + path: ^1.8.0 url_launcher: # When depending on this package from a real application you should use: # url_launcher: ^x.y.z @@ -18,6 +19,7 @@ dependencies: path: ../ dev_dependencies: + build_runner: ^2.1.10 flutter_driver: sdk: flutter integration_test: diff --git a/packages/url_launcher/url_launcher/example/windows/flutter/generated_plugins.cmake b/packages/url_launcher/url_launcher/example/windows/flutter/generated_plugins.cmake index 411af46dd721..88b22e5c775e 100644 --- a/packages/url_launcher/url_launcher/example/windows/flutter/generated_plugins.cmake +++ b/packages/url_launcher/url_launcher/example/windows/flutter/generated_plugins.cmake @@ -6,6 +6,9 @@ list(APPEND FLUTTER_PLUGIN_LIST url_launcher_windows ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -14,3 +17,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index e01cd9b7a4f7..03d0846bf173 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/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.1.4 +version: 6.1.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/script/configs/temp_exclude_excerpt.yaml b/script/configs/temp_exclude_excerpt.yaml index dec99ee6edd9..9f14bf2315aa 100644 --- a/script/configs/temp_exclude_excerpt.yaml +++ b/script/configs/temp_exclude_excerpt.yaml @@ -18,7 +18,6 @@ - plugin_platform_interface - quick_actions/quick_actions - shared_preferences/shared_preferences -- url_launcher/url_launcher - video_player/video_player - webview_flutter/webview_flutter - webview_flutter_android From 0cd07eaeaa06f7d5322e34ce1f55523cd732cb11 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 13 Jul 2022 11:50:05 -0400 Subject: [PATCH 501/844] Roll Flutter from d092601debdd to 803ef6a456c3 (13 revisions) (#6100) --- .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 d26a933e7a6e..a0f6aaf06c4c 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -d092601debdda8209d761dab480c7cf98d4b3ed2 +803ef6a456c3127a4c5509eb726f9319569edb3d From 1d2cc3aa286ce6b8fb8abded23e54b0dae2628ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Jul 2022 15:28:06 -0700 Subject: [PATCH 502/844] [local_auth]: Bump core from 1.3.2 to 1.8.0 in /packages/local_auth/local_auth_android/android (#5885) --- packages/local_auth/local_auth_android/CHANGELOG.md | 4 ++++ packages/local_auth/local_auth_android/android/build.gradle | 2 +- packages/local_auth/local_auth_android/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index df9c8b87f3e6..99b337e667bc 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.6 + +* Updates androidx.core version to 1.8.0. + ## 1.0.5 * Updates references to the obsolete master branch. diff --git a/packages/local_auth/local_auth_android/android/build.gradle b/packages/local_auth/local_auth_android/android/build.gradle index 309d2fb1e424..0486b8cea21a 100644 --- a/packages/local_auth/local_auth_android/android/build.gradle +++ b/packages/local_auth/local_auth_android/android/build.gradle @@ -49,7 +49,7 @@ android { } dependencies { - api "androidx.core:core:1.3.2" + api "androidx.core:core:1.8.0" api "androidx.biometric:biometric:1.1.0" api "androidx.fragment:fragment:1.3.2" testImplementation 'junit:junit:4.13.2' diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index ec4eaab829f2..eb2638131ffb 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.5 +version: 1.0.6 environment: sdk: ">=2.14.0 <3.0.0" From 6f30f96e13316e28ab6b6f62e293e7f7bfb350c3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 14 Jul 2022 13:16:05 -0400 Subject: [PATCH 503/844] Roll Flutter from 803ef6a456c3 to 5a5d021e51ca (22 revisions) (#6103) --- .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 a0f6aaf06c4c..d8dd5baefc46 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -803ef6a456c3127a4c5509eb726f9319569edb3d +5a5d021e51ca9a16fdfc64b34a8d64c75be1b500 From bd8138874fcf4c595f68d27feec1e0ac3e748775 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 14 Jul 2022 14:08:05 -0400 Subject: [PATCH 504/844] [webview_flutter_wkwebvew] Fix UIScrollView regression for inset behavior (#6104) --- .../webview_flutter_wkwebview/CHANGELOG.md | 4 ++ .../ios/RunnerTests/FWFWebViewHostApiTests.m | 58 +++++++++++++++++++ .../ios/Classes/FWFWebViewHostApi.m | 6 ++ .../webview_flutter_wkwebview/pubspec.yaml | 2 +- 4 files changed, 69 insertions(+), 1 deletion(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 913b0a44b7ea..72fab859ea7d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.9.1 + +* Fixes regression where the behavior for the `UIScrollView` insets were removed. + ## 2.9.0 * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m index 1061abb78f45..a0026ca01f41 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m @@ -8,6 +8,8 @@ #import +static bool feq(CGFloat a, CGFloat b) { return fabs(b - a) < FLT_EPSILON; } + @interface FWFWebViewHostApiTests : XCTestCase @end @@ -408,4 +410,60 @@ - (void)testEvaluateJavaScriptReturnsNSErrorData { XCTAssertEqualObjects(errorData.domain, @"errorDomain"); XCTAssertEqualObjects(errorData.localizedDescription, @"description"); } + +- (void)testWebViewContentInsetBehaviorShouldBeNeverOnIOS11 API_AVAILABLE(ios(11.0)) { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; + + [instanceManager addDartCreatedInstance:[[WKWebViewConfiguration alloc] init] withIdentifier:0]; + + FlutterError *error; + [hostAPI createWithIdentifier:@1 configurationIdentifier:@0 error:&error]; + FWFWebView *webView = (FWFWebView *)[instanceManager instanceForIdentifier:1]; + + XCTAssertEqual(webView.scrollView.contentInsetAdjustmentBehavior, + UIScrollViewContentInsetAdjustmentNever); +} + +- (void)testScrollViewsAutomaticallyAdjustsScrollIndicatorInsetsShouldbeNoOnIOS13 API_AVAILABLE( + ios(13.0)) { + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; + + [instanceManager addDartCreatedInstance:[[WKWebViewConfiguration alloc] init] withIdentifier:0]; + + FlutterError *error; + [hostAPI createWithIdentifier:@1 configurationIdentifier:@0 error:&error]; + FWFWebView *webView = (FWFWebView *)[instanceManager instanceForIdentifier:1]; + + XCTAssertFalse(webView.scrollView.automaticallyAdjustsScrollIndicatorInsets); +} + +- (void)testContentInsetsSumAlwaysZeroAfterSetFrame { + FWFWebView *webView = [[FWFWebView alloc] initWithFrame:CGRectMake(0, 0, 300, 400) + configuration:[[WKWebViewConfiguration alloc] init]]; + + webView.scrollView.contentInset = UIEdgeInsetsMake(0, 0, 300, 0); + XCTAssertFalse(UIEdgeInsetsEqualToEdgeInsets(webView.scrollView.contentInset, UIEdgeInsetsZero)); + + webView.frame = CGRectMake(0, 0, 300, 200); + XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(webView.scrollView.contentInset, UIEdgeInsetsZero)); + XCTAssertTrue(CGRectEqualToRect(webView.frame, CGRectMake(0, 0, 300, 200))); + + if (@available(iOS 11, *)) { + // After iOS 11, we need to make sure the contentInset compensates the adjustedContentInset. + UIScrollView *partialMockScrollView = OCMPartialMock(webView.scrollView); + UIEdgeInsets insetToAdjust = UIEdgeInsetsMake(0, 0, 300, 0); + OCMStub(partialMockScrollView.adjustedContentInset).andReturn(insetToAdjust); + XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(webView.scrollView.contentInset, UIEdgeInsetsZero)); + + webView.frame = CGRectMake(0, 0, 300, 100); + XCTAssertTrue(feq(webView.scrollView.contentInset.bottom, -insetToAdjust.bottom)); + XCTAssertTrue(CGRectEqualToRect(webView.frame, CGRectMake(0, 0, 300, 100))); + } +} @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m index 9a8aedd1e646..ceaa346c8747 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m @@ -20,6 +20,12 @@ - (instancetype)initWithFrame:(CGRect)frame if (self) { _objectApi = [[FWFObjectFlutterApiImpl alloc] initWithBinaryMessenger:binaryMessenger instanceManager:instanceManager]; + if (@available(iOS 11.0, *)) { + self.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + if (@available(iOS 13.0, *)) { + self.scrollView.automaticallyAdjustsScrollIndicatorInsets = NO; + } + } } return self; } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index cd92b8625105..446f90973cb3 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/plugins/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: 2.9.0 +version: 2.9.1 environment: sdk: ">=2.17.0 <3.0.0" From f4c1bfe2a7ab56d513af5ea63d40e34abc4de6e8 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 15 Jul 2022 11:46:05 -0400 Subject: [PATCH 505/844] Roll Flutter from 5a5d021e51ca to bbec650b64bc (22 revisions) (#6107) --- .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 d8dd5baefc46..a85d40bd6a49 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -5a5d021e51ca9a16fdfc64b34a8d64c75be1b500 +bbec650b64bc50e6141c293cbe8415a4abf7fa40 From 5712aedbc5d1ad588056fd8ad7d947d5e39be85c Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 15 Jul 2022 12:38:05 -0400 Subject: [PATCH 506/844] [various] Remove duplicate native Android tests (#6098) --- .../imagepickerexample/ImagePickerTest.java | 23 ------------------- .../webviewflutterexample/WebViewTest.java | 23 ------------------- 2 files changed, 46 deletions(-) delete mode 100644 packages/image_picker/image_picker/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/ImagePickerTest.java delete mode 100644 packages/webview_flutter/webview_flutter/example/android/app/src/androidTest/java/io/flutter/plugins/webviewflutterexample/WebViewTest.java diff --git a/packages/image_picker/image_picker/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/ImagePickerTest.java b/packages/image_picker/image_picker/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/ImagePickerTest.java deleted file mode 100644 index c4a1532d940c..000000000000 --- a/packages/image_picker/image_picker/example/android/app/src/androidTest/java/io/flutter/plugins/imagepickerexample/ImagePickerTest.java +++ /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. - -package io.flutter.plugins.imagepickerexample; - -import static org.junit.Assert.assertTrue; - -import androidx.test.core.app.ActivityScenario; -import io.flutter.plugins.imagepicker.ImagePickerPlugin; -import org.junit.Test; - -public class ImagePickerTest { - @Test - public void imagePickerPluginIsAdded() { - final ActivityScenario scenario = - ActivityScenario.launch(ImagePickerTestActivity.class); - scenario.onActivity( - activity -> { - assertTrue(activity.engine.getPlugins().has(ImagePickerPlugin.class)); - }); - } -} diff --git a/packages/webview_flutter/webview_flutter/example/android/app/src/androidTest/java/io/flutter/plugins/webviewflutterexample/WebViewTest.java b/packages/webview_flutter/webview_flutter/example/android/app/src/androidTest/java/io/flutter/plugins/webviewflutterexample/WebViewTest.java deleted file mode 100644 index 0b3eeef9b6b7..000000000000 --- a/packages/webview_flutter/webview_flutter/example/android/app/src/androidTest/java/io/flutter/plugins/webviewflutterexample/WebViewTest.java +++ /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. - -package io.flutter.plugins.webviewflutterexample; - -import static org.junit.Assert.assertTrue; - -import androidx.test.core.app.ActivityScenario; -import io.flutter.plugins.webviewflutter.WebViewFlutterPlugin; -import org.junit.Test; - -public class WebViewTest { - @Test - public void webViewPluginIsAdded() { - final ActivityScenario scenario = - ActivityScenario.launch(WebViewTestActivity.class); - scenario.onActivity( - activity -> { - assertTrue(activity.engine.getPlugins().has(WebViewFlutterPlugin.class)); - }); - } -} From dd28529e8b7444833cfaf8e9d5d607735f394583 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Sat, 16 Jul 2022 07:43:15 -0400 Subject: [PATCH 507/844] Manual roll of Flutter to c4975d9be0e2f3ca6279ea4ba77b23cc549e2926 --- .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 a85d40bd6a49..387c518b8757 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -bbec650b64bc50e6141c293cbe8415a4abf7fa40 +c4975d9be0e2f3ca6279ea4ba77b23cc549e2926 From 4882ea83c1ac5c5fc6cbc177d318fcf3065cbd8c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 18 Jul 2022 14:32:05 -0400 Subject: [PATCH 508/844] Roll Flutter from c4975d9be0e2 to 4f9528293f54 (16 revisions) (#6111) --- .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 387c518b8757..c3b01bfdecbf 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c4975d9be0e2f3ca6279ea4ba77b23cc549e2926 +4f9528293f54037993a6b2eb8f1df5604a3da954 From 28eff0a8c167d146d73d62aa21a7d93d95bc2578 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 18 Jul 2022 16:18:06 -0400 Subject: [PATCH 509/844] [ci] Pin stable version (#6117) --- .ci.yaml | 2 ++ .ci/flutter_stable.version | 1 + .cirrus.yml | 8 ++++---- 3 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 .ci/flutter_stable.version diff --git a/.ci.yaml b/.ci.yaml index 7504373ac61a..bc1feca37966 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -47,6 +47,7 @@ targets: add_recipes_cq: "true" target_file: windows_build_and_platform_tests.yaml channel: stable + version_file: flutter_stable.version dependencies: > [ {"dependency": "vs_build", "version": "version:vs2019"} @@ -72,6 +73,7 @@ targets: add_recipes_cq: "true" target_file: build_all_plugins.yaml channel: stable + version_file: flutter_stable.version dependencies: > [ {"dependency": "vs_build", "version": "version:vs2019"} diff --git a/.ci/flutter_stable.version b/.ci/flutter_stable.version new file mode 100644 index 000000000000..8c2db2fa8a81 --- /dev/null +++ b/.ci/flutter_stable.version @@ -0,0 +1 @@ +f1875d570e39de09040c8f79aa13cc56baab8db1 diff --git a/.cirrus.yml b/.cirrus.yml index 6ea4680f6dec..479d49384c9e 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -14,11 +14,11 @@ tool_setup_template: &TOOL_SETUP_TEMPLATE flutter_upgrade_template: &FLUTTER_UPGRADE_TEMPLATE upgrade_flutter_script: - # Master uses a pinned, auto-rolled version to prevent out-of-band CI - # failures due to changes in Flutter. - # TODO(stuartmorgan): Investigate an autoroller for stable as well. + # Channels that are part of our normal test matrix use a pinned, + # auto-rolled version to prevent out-of-band CI failures due to changes in + # Flutter. - TARGET_TREEISH=$CHANNEL - - if [[ "$CHANNEL" == "master" ]]; then + - if [[ "$CHANNEL" == "master" || "$CHANNEL" == "stable" ]]; then - TARGET_TREEISH=$(< .ci/flutter_$CHANNEL.version) - fi # Ensure that the repository has all the branches. From 64ade14d53c41db36d62849a9b2160a50ccad0fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Jul 2022 17:02:06 +0000 Subject: [PATCH 510/844] [video_player]: Bump robolectric from 4.4 to 4.8.1 in /packages/video_player/video_player/example/android/app (#6025) --- .../video_player/video_player/example/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player/example/android/app/build.gradle b/packages/video_player/video_player/example/android/app/build.gradle index 7b3c7db80c7e..18e77a5e555c 100644 --- a/packages/video_player/video_player/example/android/app/build.gradle +++ b/packages/video_player/video_player/example/android/app/build.gradle @@ -59,7 +59,7 @@ flutter { dependencies { testImplementation 'junit:junit:4.13' - testImplementation 'org.robolectric:robolectric:4.4' + testImplementation 'org.robolectric:robolectric:4.8.1' testImplementation 'org.mockito:mockito-core:3.5.13' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' From f42aca34dcbd9e3171c3b77b66a4acd08260163f Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 19 Jul 2022 18:49:04 -0400 Subject: [PATCH 511/844] [video_player] Update ExoPlayer to 2.18.0 (#6123) --- .../video_player_android/CHANGELOG.md | 4 ++++ .../video_player_android/android/build.gradle | 10 ++++----- .../plugins/videoplayer/VideoPlayer.java | 21 ++++++++++--------- .../video_player_android/pubspec.yaml | 2 +- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 0c839961d6f7..fb34655e50dc 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.8 + +* Updates ExoPlayer to 2.18.0. + ## 2.3.7 * Bumps gradle version to 7.2.1. diff --git a/packages/video_player/video_player_android/android/build.gradle b/packages/video_player/video_player_android/android/build.gradle index 8c1aaaf5f923..e8032c583802 100644 --- a/packages/video_player/video_player_android/android/build.gradle +++ b/packages/video_player/video_player_android/android/build.gradle @@ -43,10 +43,10 @@ android { } dependencies { - implementation 'com.google.android.exoplayer:exoplayer-core:2.17.1' - implementation 'com.google.android.exoplayer:exoplayer-hls:2.17.1' - implementation 'com.google.android.exoplayer:exoplayer-dash:2.17.1' - implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.17.1' + implementation 'com.google.android.exoplayer:exoplayer-core:2.18.0' + implementation 'com.google.android.exoplayer:exoplayer-hls:2.18.0' + implementation 'com.google.android.exoplayer:exoplayer-dash:2.18.0' + implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.18.0' testImplementation 'junit:junit:4.13.2' testImplementation 'androidx.test:core:1.3.0' testImplementation 'org.mockito:mockito-inline:3.9.0' @@ -65,4 +65,4 @@ android { } } } -} \ No newline at end of file +} diff --git a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java index f215354cb929..e130c995aa2a 100644 --- a/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java +++ b/packages/video_player/video_player_android/android/src/main/java/io/flutter/plugins/videoplayer/VideoPlayer.java @@ -126,20 +126,20 @@ private MediaSource buildMediaSource( Uri uri, DataSource.Factory mediaDataSourceFactory, String formatHint, Context context) { int type; if (formatHint == null) { - type = Util.inferContentType(uri.getLastPathSegment()); + type = Util.inferContentType(uri); } else { switch (formatHint) { case FORMAT_SS: - type = C.TYPE_SS; + type = C.CONTENT_TYPE_SS; break; case FORMAT_DASH: - type = C.TYPE_DASH; + type = C.CONTENT_TYPE_DASH; break; case FORMAT_HLS: - type = C.TYPE_HLS; + type = C.CONTENT_TYPE_HLS; break; case FORMAT_OTHER: - type = C.TYPE_OTHER; + type = C.CONTENT_TYPE_OTHER; break; default: type = -1; @@ -147,20 +147,20 @@ private MediaSource buildMediaSource( } } switch (type) { - case C.TYPE_SS: + case C.CONTENT_TYPE_SS: return new SsMediaSource.Factory( new DefaultSsChunkSource.Factory(mediaDataSourceFactory), new DefaultDataSource.Factory(context, mediaDataSourceFactory)) .createMediaSource(MediaItem.fromUri(uri)); - case C.TYPE_DASH: + case C.CONTENT_TYPE_DASH: return new DashMediaSource.Factory( new DefaultDashChunkSource.Factory(mediaDataSourceFactory), new DefaultDataSource.Factory(context, mediaDataSourceFactory)) .createMediaSource(MediaItem.fromUri(uri)); - case C.TYPE_HLS: + case C.CONTENT_TYPE_HLS: return new HlsMediaSource.Factory(mediaDataSourceFactory) .createMediaSource(MediaItem.fromUri(uri)); - case C.TYPE_OTHER: + case C.CONTENT_TYPE_OTHER: return new ProgressiveMediaSource.Factory(mediaDataSourceFactory) .createMediaSource(MediaItem.fromUri(uri)); default: @@ -246,7 +246,8 @@ void sendBufferingUpdate() { private static void setAudioAttributes(ExoPlayer exoPlayer, boolean isMixMode) { exoPlayer.setAudioAttributes( - new AudioAttributes.Builder().setContentType(C.CONTENT_TYPE_MOVIE).build(), !isMixMode); + new AudioAttributes.Builder().setContentType(C.AUDIO_CONTENT_TYPE_MOVIE).build(), + !isMixMode); } void play() { diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index f11a74ec1ec0..056221a006d3 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.7 +version: 2.3.8 environment: sdk: ">=2.14.0 <3.0.0" From 06c241571e877e95b4e5becf2524e3de1be8ed0d Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 20 Jul 2022 10:39:14 -0400 Subject: [PATCH 512/844] Update CODEOWNERS (#6085) - Distributes blasten's ownership among other team members for now - Adds missing ecosystem-level owners --- CODEOWNERS | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 16bf98ba9e4b..9efe4a55e037 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -10,6 +10,7 @@ packages/file_selector/** @stuartmorgan packages/google_maps_flutter/** @stuartmorgan packages/google_sign_in/** @stuartmorgan packages/image_picker/** @stuartmorgan +packages/in_app_purchase/** @bparrishMines packages/local_auth/** @stuartmorgan packages/path_provider/** @gaaclarke packages/plugin_platform_interface/** @stuartmorgan @@ -27,17 +28,17 @@ packages/**/*_web/** @ditman # - Android packages/camera/camera_android/** @camsim99 -packages/espresso/** @blasten -packages/flutter_plugin_android_lifecycle/** @blasten +packages/espresso/** @GaryQian +packages/flutter_plugin_android_lifecycle/** @GaryQian packages/google_maps_flutter/google_maps_flutter/android/** @GaryQian packages/google_sign_in/google_sign_in_android/** @camsim99 packages/image_picker/image_picker_android/** @GaryQian -packages/in_app_purchase/in_app_purchase_android/** @blasten -packages/local_auth/local_auth_android/** @blasten +packages/in_app_purchase/in_app_purchase_android/** @GaryQian +packages/local_auth/local_auth_android/** @camsim99 packages/path_provider/path_provider_android/** @camsim99 packages/quick_actions/quick_actions_android/** @camsim99 packages/url_launcher/url_launcher_android/** @GaryQian -packages/video_player/video_player_android/** @blasten +packages/video_player/video_player_android/** @camsim99 # - iOS packages/camera/camera_avfoundation/** @hellohuanlin From 5d01548aefcbf7f2bbaee08be48855f8e2a14062 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 20 Jul 2022 11:25:09 -0400 Subject: [PATCH 513/844] [file_selector] Throw for unsupported type groups (#6120) --- .../file_selector_macos/CHANGELOG.md | 5 +- .../lib/file_selector_macos.dart | 11 +- .../file_selector_macos/pubspec.yaml | 4 +- .../test/file_selector_macos_test.dart | 124 +++++++++++++---- .../file_selector_web/CHANGELOG.md | 6 + .../file_selector_web/lib/src/utils.dart | 24 ++-- .../file_selector_web/pubspec.yaml | 4 +- .../file_selector_web/test/utils_test.dart | 7 + .../file_selector_windows/CHANGELOG.md | 5 +- .../lib/file_selector_windows.dart | 19 +++ .../file_selector_windows/pubspec.yaml | 4 +- .../test/file_selector_windows_test.dart | 128 ++++++++++++++---- 12 files changed, 269 insertions(+), 72 deletions(-) diff --git a/packages/file_selector/file_selector_macos/CHANGELOG.md b/packages/file_selector/file_selector_macos/CHANGELOG.md index 8a4f217f8b9a..0adf1ab891ad 100644 --- a/packages/file_selector/file_selector_macos/CHANGELOG.md +++ b/packages/file_selector/file_selector_macos/CHANGELOG.md @@ -1,5 +1,8 @@ -## NEXT +## 0.9.0 +* **BREAKING CHANGE**: Methods that take `XTypeGroup`s now throw an + `ArgumentError` if any group is not a wildcard (all filter types null or + empty), but doesn't include any of the filter types supported by macOS. * Ignores deprecation warnings for upcoming styleFrom button API changes. ## 0.8.2+2 diff --git a/packages/file_selector/file_selector_macos/lib/file_selector_macos.dart b/packages/file_selector/file_selector_macos/lib/file_selector_macos.dart index e50c296b005f..74ce2835d18c 100644 --- a/packages/file_selector/file_selector_macos/lib/file_selector_macos.dart +++ b/packages/file_selector/file_selector_macos/lib/file_selector_macos.dart @@ -105,10 +105,19 @@ class FileSelectorMacOS extends FileSelectorPlatform { }; for (final XTypeGroup typeGroup in typeGroups) { // If any group allows everything, no filtering should be done. + if (typeGroup.allowsAny) { + return null; + } + // Reject a filter that isn't an allow-any, but doesn't set any + // macOS-supported filter categories. if ((typeGroup.extensions?.isEmpty ?? true) && (typeGroup.macUTIs?.isEmpty ?? true) && (typeGroup.mimeTypes?.isEmpty ?? true)) { - return null; + throw ArgumentError('Provided type group $typeGroup does not allow ' + 'all files, but does not set any of the macOS-supported filter ' + 'categories. At least one of "extensions", "macUTIs", or ' + '"mimeTypes" must be non-empty for macOS if anything is ' + 'non-empty.'); } allowedTypes[extensionKey]!.addAll(typeGroup.extensions ?? []); allowedTypes[mimeTypeKey]!.addAll(typeGroup.mimeTypes ?? []); diff --git a/packages/file_selector/file_selector_macos/pubspec.yaml b/packages/file_selector/file_selector_macos/pubspec.yaml index 8c6d1f7c81ce..fc9ca4d62275 100644 --- a/packages/file_selector/file_selector_macos/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_macos description: macOS implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.2+2 +version: 0.9.0 environment: sdk: ">=2.12.0 <3.0.0" @@ -18,7 +18,7 @@ flutter: dependencies: cross_file: ^0.3.1 - file_selector_platform_interface: ^2.0.4 + file_selector_platform_interface: ^2.1.0 flutter: sdk: flutter flutter_test: diff --git a/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart b/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart index 1c1b9c11e069..40ab58534abd 100644 --- a/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart +++ b/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart @@ -62,6 +62,7 @@ void main() { ], ); }); + test('passes initialDirectory correctly', () async { await plugin.openFile(initialDirectory: '/example/directory'); @@ -77,6 +78,7 @@ void main() { ], ); }); + test('passes confirmButtonText correctly', () async { await plugin.openFile(confirmButtonText: 'Open File'); @@ -92,7 +94,28 @@ void main() { ], ); }); + + test('throws for a type group that does not support macOS', () async { + final XTypeGroup group = XTypeGroup( + label: 'images', + webWildCards: ['images/*'], + ); + + await expectLater( + plugin.openFile(acceptedTypeGroups: [group]), + throwsArgumentError); + }); + + test('allows a wildcard group', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + ); + + await expectLater( + plugin.openFile(acceptedTypeGroups: [group]), completes); + }); }); + group('openFiles', () { test('passes the accepted type groups correctly', () async { final XTypeGroup group = XTypeGroup( @@ -127,6 +150,7 @@ void main() { ], ); }); + test('passes initialDirectory correctly', () async { await plugin.openFiles(initialDirectory: '/example/directory'); @@ -142,6 +166,7 @@ void main() { ], ); }); + test('passes confirmButtonText correctly', () async { await plugin.openFiles(confirmButtonText: 'Open File'); @@ -157,6 +182,26 @@ void main() { ], ); }); + + test('throws for a type group that does not support macOS', () async { + final XTypeGroup group = XTypeGroup( + label: 'images', + webWildCards: ['images/*'], + ); + + await expectLater( + plugin.openFiles(acceptedTypeGroups: [group]), + throwsArgumentError); + }); + + test('allows a wildcard group', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + ); + + await expectLater( + plugin.openFiles(acceptedTypeGroups: [group]), completes); + }); }); group('getSavePath', () { @@ -194,6 +239,7 @@ void main() { ], ); }); + test('passes initialDirectory correctly', () async { await plugin.getSavePath(initialDirectory: '/example/directory'); @@ -209,6 +255,7 @@ void main() { ], ); }); + test('passes confirmButtonText correctly', () async { await plugin.getSavePath(confirmButtonText: 'Open File'); @@ -224,33 +271,56 @@ void main() { ], ); }); - group('getDirectoryPath', () { - test('passes initialDirectory correctly', () async { - await plugin.getDirectoryPath(initialDirectory: '/example/directory'); - - expect( - log, - [ - isMethodCall('getDirectoryPath', arguments: { - 'initialDirectory': '/example/directory', - 'confirmButtonText': null, - }), - ], - ); - }); - test('passes confirmButtonText correctly', () async { - await plugin.getDirectoryPath(confirmButtonText: 'Open File'); - - expect( - log, - [ - isMethodCall('getDirectoryPath', arguments: { - 'initialDirectory': null, - 'confirmButtonText': 'Open File', - }), - ], - ); - }); + + test('throws for a type group that does not support macOS', () async { + final XTypeGroup group = XTypeGroup( + label: 'images', + webWildCards: ['images/*'], + ); + + await expectLater( + plugin.getSavePath(acceptedTypeGroups: [group]), + throwsArgumentError); + }); + + test('allows a wildcard group', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + ); + + await expectLater( + plugin.getSavePath(acceptedTypeGroups: [group]), + completes); + }); + }); + + group('getDirectoryPath', () { + test('passes initialDirectory correctly', () async { + await plugin.getDirectoryPath(initialDirectory: '/example/directory'); + + expect( + log, + [ + isMethodCall('getDirectoryPath', arguments: { + 'initialDirectory': '/example/directory', + 'confirmButtonText': null, + }), + ], + ); + }); + + test('passes confirmButtonText correctly', () async { + await plugin.getDirectoryPath(confirmButtonText: 'Open File'); + + expect( + log, + [ + isMethodCall('getDirectoryPath', arguments: { + 'initialDirectory': null, + 'confirmButtonText': 'Open File', + }), + ], + ); }); }); diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md index 3963601e2ac5..8a8a53afe661 100644 --- a/packages/file_selector/file_selector_web/CHANGELOG.md +++ b/packages/file_selector/file_selector_web/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.9.0 + +* **BREAKING CHANGE**: Methods that take `XTypeGroup`s now throw an + `ArgumentError` if any group is not a wildcard (all filter types null or + empty), but doesn't include any of the filter types supported by web. + ## 0.8.1+5 * Minor fixes for new analysis options. diff --git a/packages/file_selector/file_selector_web/lib/src/utils.dart b/packages/file_selector/file_selector_web/lib/src/utils.dart index fe8d1f433647..7a7aa7a69509 100644 --- a/packages/file_selector/file_selector_web/lib/src/utils.dart +++ b/packages/file_selector/file_selector_web/lib/src/utils.dart @@ -11,7 +11,11 @@ String acceptedTypesToString(List? acceptedTypes) { } final List allTypes = []; for (final XTypeGroup group in acceptedTypes) { - _assertTypeGroupIsValid(group); + // If any group allows everything, no filtering should be done. + if (group.allowsAny) { + return ''; + } + _validateTypeGroup(group); if (group.extensions != null) { allTypes.addAll(group.extensions!.map(_normalizeExtension)); } @@ -25,13 +29,17 @@ String acceptedTypesToString(List? acceptedTypes) { return allTypes.join(','); } -/// Make sure that at least one of its fields is populated. -void _assertTypeGroupIsValid(XTypeGroup group) { - assert( - !((group.extensions == null || group.extensions!.isEmpty) && - (group.mimeTypes == null || group.mimeTypes!.isEmpty) && - (group.webWildCards == null || group.webWildCards!.isEmpty)), - 'At least one of extensions / mimeTypes / webWildCards is required for web.'); +/// Make sure that at least one of the supported fields is populated. +void _validateTypeGroup(XTypeGroup group) { + if ((group.extensions?.isEmpty ?? true) && + (group.mimeTypes?.isEmpty ?? true) && + (group.webWildCards?.isEmpty ?? true)) { + throw ArgumentError('Provided type group $group does not allow ' + 'all files, but does not set any of the web-supported filter ' + 'categories. At least one of "extensions", "mimeTypes", or ' + '"webWildCards" must be non-empty for web if anything is ' + 'non-empty.'); + } } /// Append a dot at the beggining if it is not there png -> .png diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index c685cca9e884..a764cae0245f 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/plugins/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.8.1+5 +version: 0.9.0 environment: sdk: ">=2.12.0 <3.0.0" @@ -17,7 +17,7 @@ flutter: fileName: file_selector_web.dart dependencies: - file_selector_platform_interface: ^2.0.0 + file_selector_platform_interface: ^2.1.0 flutter: sdk: flutter flutter_web_plugins: diff --git a/packages/file_selector/file_selector_web/test/utils_test.dart b/packages/file_selector/file_selector_web/test/utils_test.dart index 9bddfd2e6304..3281c4cdca8d 100644 --- a/packages/file_selector/file_selector_web/test/utils_test.dart +++ b/packages/file_selector/file_selector_web/test/utils_test.dart @@ -53,6 +53,13 @@ void main() { final String accepts = acceptedTypesToString(acceptedTypes); expect(accepts, 'image/*,audio/*,video/*'); }); + + test('throws for a type group that does not support web', () { + final List acceptedTypes = [ + XTypeGroup(label: 'text', macUTIs: ['public.text']), + ]; + expect(() => acceptedTypesToString(acceptedTypes), throwsArgumentError); + }); }); }); } diff --git a/packages/file_selector/file_selector_windows/CHANGELOG.md b/packages/file_selector/file_selector_windows/CHANGELOG.md index f5354b3286bc..3f15de73b421 100644 --- a/packages/file_selector/file_selector_windows/CHANGELOG.md +++ b/packages/file_selector/file_selector_windows/CHANGELOG.md @@ -1,5 +1,8 @@ -## NEXT +## 0.9.0 +* **BREAKING CHANGE**: Methods that take `XTypeGroup`s now throw an + `ArgumentError` if any group is not a wildcard (all filter types null or + empty), but doesn't include any of the filter types supported by Windows. * Ignores deprecation warnings for upcoming styleFrom button API changes. ## 0.8.2+2 diff --git a/packages/file_selector/file_selector_windows/lib/file_selector_windows.dart b/packages/file_selector/file_selector_windows/lib/file_selector_windows.dart index b91a22355572..6b1bc89c5078 100644 --- a/packages/file_selector/file_selector_windows/lib/file_selector_windows.dart +++ b/packages/file_selector/file_selector_windows/lib/file_selector_windows.dart @@ -26,6 +26,7 @@ class FileSelectorWindows extends FileSelectorPlatform { String? initialDirectory, String? confirmButtonText, }) async { + _validateTypeGroups(acceptedTypeGroups); final List? path = await _channel.invokeListMethod( 'openFile', { @@ -46,6 +47,7 @@ class FileSelectorWindows extends FileSelectorPlatform { String? initialDirectory, String? confirmButtonText, }) async { + _validateTypeGroups(acceptedTypeGroups); final List? pathList = await _channel.invokeListMethod( 'openFile', { @@ -67,6 +69,7 @@ class FileSelectorWindows extends FileSelectorPlatform { String? suggestedName, String? confirmButtonText, }) async { + _validateTypeGroups(acceptedTypeGroups); return _channel.invokeMethod( 'getSavePath', { @@ -93,4 +96,20 @@ class FileSelectorWindows extends FileSelectorPlatform { }, ); } + + /// Throws an [ArgumentError] if any of the provided type groups are not valid + /// for Windows. + void _validateTypeGroups(List? groups) { + if (groups == null) { + return; + } + for (final XTypeGroup group in groups) { + if (!group.allowsAny && (group.extensions?.isEmpty ?? true)) { + throw ArgumentError('Provided type group $group does not allow ' + 'all files, but does not set any of the Windows-supported filter ' + 'categories. "extensions" must be non-empty for Windows if ' + 'anything is non-empty.'); + } + } + } } diff --git a/packages/file_selector/file_selector_windows/pubspec.yaml b/packages/file_selector/file_selector_windows/pubspec.yaml index 7c933b2778d8..13487feb8873 100644 --- a/packages/file_selector/file_selector_windows/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_windows description: Windows implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.2+2 +version: 0.9.0 environment: sdk: ">=2.12.0 <3.0.0" @@ -18,7 +18,7 @@ flutter: dependencies: cross_file: ^0.3.1 - file_selector_platform_interface: ^2.0.4 + file_selector_platform_interface: ^2.1.0 flutter: sdk: flutter flutter_test: diff --git a/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart b/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart index 72604dd1668c..48e13c2a9892 100644 --- a/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart +++ b/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart @@ -10,7 +10,7 @@ import 'package:flutter_test/flutter_test.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - group('$FileSelectorWindows()', () { + group('FileSelectorWindows()', () { final FileSelectorWindows plugin = FileSelectorWindows(); final List log = []; @@ -63,6 +63,7 @@ void main() { ], ); }); + test('passes initialDirectory correctly', () async { await plugin.openFile(initialDirectory: '/example/directory'); @@ -78,6 +79,7 @@ void main() { ], ); }); + test('passes confirmButtonText correctly', () async { await plugin.openFile(confirmButtonText: 'Open File'); @@ -93,7 +95,29 @@ void main() { ], ); }); + + test('throws for a type group that does not support Windows', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + mimeTypes: ['text/plain'], + ); + + await expectLater( + plugin.openFile(acceptedTypeGroups: [group]), + throwsArgumentError); + }); + + test('allows a wildcard group', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + ); + + await expectLater( + plugin.openFile(acceptedTypeGroups: [group]), + completes); + }); }); + group('#openFiles', () { test('passes the accepted type groups correctly', () async { final XTypeGroup group = XTypeGroup( @@ -128,6 +152,7 @@ void main() { ], ); }); + test('passes initialDirectory correctly', () async { await plugin.openFiles(initialDirectory: '/example/directory'); @@ -143,6 +168,7 @@ void main() { ], ); }); + test('passes confirmButtonText correctly', () async { await plugin.openFiles(confirmButtonText: 'Open File'); @@ -158,6 +184,27 @@ void main() { ], ); }); + + test('throws for a type group that does not support Windows', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + mimeTypes: ['text/plain'], + ); + + await expectLater( + plugin.openFiles(acceptedTypeGroups: [group]), + throwsArgumentError); + }); + + test('allows a wildcard group', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + ); + + await expectLater( + plugin.openFiles(acceptedTypeGroups: [group]), + completes); + }); }); group('#getSavePath', () { @@ -194,6 +241,7 @@ void main() { ], ); }); + test('passes initialDirectory correctly', () async { await plugin.getSavePath(initialDirectory: '/example/directory'); @@ -209,6 +257,7 @@ void main() { ], ); }); + test('passes confirmButtonText correctly', () async { await plugin.getSavePath(confirmButtonText: 'Open File'); @@ -224,33 +273,56 @@ void main() { ], ); }); - group('#getDirectoryPath', () { - test('passes initialDirectory correctly', () async { - await plugin.getDirectoryPath(initialDirectory: '/example/directory'); - - expect( - log, - [ - isMethodCall('getDirectoryPath', arguments: { - 'initialDirectory': '/example/directory', - 'confirmButtonText': null, - }), - ], - ); - }); - test('passes confirmButtonText correctly', () async { - await plugin.getDirectoryPath(confirmButtonText: 'Open File'); - - expect( - log, - [ - isMethodCall('getDirectoryPath', arguments: { - 'initialDirectory': null, - 'confirmButtonText': 'Open File', - }), - ], - ); - }); + + test('throws for a type group that does not support Windows', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + mimeTypes: ['text/plain'], + ); + + await expectLater( + plugin.getSavePath(acceptedTypeGroups: [group]), + throwsArgumentError); + }); + + test('allows a wildcard group', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + ); + + await expectLater( + plugin.getSavePath(acceptedTypeGroups: [group]), + completes); + }); + }); + + group('#getDirectoryPath', () { + test('passes initialDirectory correctly', () async { + await plugin.getDirectoryPath(initialDirectory: '/example/directory'); + + expect( + log, + [ + isMethodCall('getDirectoryPath', arguments: { + 'initialDirectory': '/example/directory', + 'confirmButtonText': null, + }), + ], + ); + }); + + test('passes confirmButtonText correctly', () async { + await plugin.getDirectoryPath(confirmButtonText: 'Open File'); + + expect( + log, + [ + isMethodCall('getDirectoryPath', arguments: { + 'initialDirectory': null, + 'confirmButtonText': 'Open File', + }), + ], + ); }); }); }); From e657dcbbf622ac1e544b34dfc6f47cfd8194ee8e Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 20 Jul 2022 12:26:08 -0400 Subject: [PATCH 514/844] [webview_flutter_android] Adds support for use of old hybrid composition (#6063) --- .../webview_flutter_android/CHANGELOG.md | 9 +- .../lib/webview_surface_android.dart | 54 ++++++-- .../webview_flutter_android/pubspec.yaml | 4 +- .../test/surface_android_test.dart | 115 ++++++++++++++++++ 4 files changed, 171 insertions(+), 11 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_android/test/surface_android_test.dart diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 7261cc1c2cb5..a8e9a927d5a8 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,6 +1,13 @@ -## NEXT +## 2.9.0 * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). +* Fixes bug where `Directionality` from context didn't affect `SurfaceAndroidWebView`. +* Fixes bug where default text direction was different for `SurfaceAndroidWebView` and `AndroidWebView`. + Default is now `TextDirection.ltr` for both. +* Fixes bug where setting WebView to a transparent background could cause visual errors when using + `SurfaceAndroidWebView`. Hybrid composition is now used when the background color is not 100% + opaque. +* Raises minimum Flutter version to 3.0.0. ## 2.8.14 diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart index 00d7c8c53b7f..61ec11012881 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart @@ -14,14 +14,20 @@ import 'src/instance_manager.dart'; import 'webview_android.dart'; import 'webview_android_widget.dart'; -/// Android [WebViewPlatform] that uses [AndroidViewSurface] to build the [WebView] widget. +/// Android [WebViewPlatform] that uses [AndroidViewSurface] to build the +/// [WebView] widget. /// /// To use this, set [WebView.platform] to an instance of this class. /// -/// This implementation uses hybrid composition to render the [WebView] on +/// This implementation uses [AndroidViewSurface] to render the [WebView] on /// Android. It solves multiple issues related to accessibility and interaction /// with the [WebView] at the cost of some performance on Android versions below -/// 10. See https://github.com/flutter/flutter/wiki/Hybrid-Composition for more +/// 10. +/// +/// To support transparent backgrounds on all Android devices, this +/// implementation uses hybrid composition when the opacity of +/// `CreationParams.backgroundColor` is less than 1.0. See +/// https://github.com/flutter/flutter/wiki/Hybrid-Composition for more /// information. class SurfaceAndroidWebView extends AndroidWebView { @override @@ -53,16 +59,23 @@ class SurfaceAndroidWebView extends AndroidWebView { ); }, onCreatePlatformView: (PlatformViewCreationParams params) { - return PlatformViewsService.initSurfaceAndroidView( + final Color? backgroundColor = creationParams.backgroundColor; + return _createViewController( + // On some Android devices, transparent backgrounds can cause + // rendering issues on the non hybrid composition + // AndroidViewSurface. This switches the WebView to Hybrid + // Composition when the background color is not 100% opaque. + hybridComposition: + backgroundColor != null && backgroundColor.opacity < 1.0, id: params.id, viewType: 'plugins.flutter.io/webview', // WebView content is not affected by the Android view's layout direction, // we explicitly set it here so that the widget doesn't require an ambient // directionality. - layoutDirection: TextDirection.rtl, - creationParams: - InstanceManager.instance.getInstanceId(controller.webView), - creationParamsCodec: const StandardMessageCodec(), + layoutDirection: + Directionality.maybeOf(context) ?? TextDirection.ltr, + webViewIdentifier: + InstanceManager.instance.getInstanceId(controller.webView)!, ) ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated) ..addOnPlatformViewCreatedListener((int id) { @@ -76,4 +89,29 @@ class SurfaceAndroidWebView extends AndroidWebView { }, ); } + + AndroidViewController _createViewController({ + required bool hybridComposition, + required int id, + required String viewType, + required TextDirection layoutDirection, + required int webViewIdentifier, + }) { + if (hybridComposition) { + return PlatformViewsService.initExpensiveAndroidView( + id: id, + viewType: viewType, + layoutDirection: layoutDirection, + creationParams: webViewIdentifier, + creationParamsCodec: const StandardMessageCodec(), + ); + } + return PlatformViewsService.initSurfaceAndroidView( + id: id, + viewType: viewType, + layoutDirection: layoutDirection, + creationParams: webViewIdentifier, + creationParamsCodec: const StandardMessageCodec(), + ); + } } diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index a386cad9028c..aaa4984bc8a1 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,11 +2,11 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/plugins/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: 2.8.14 +version: 2.9.0 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=3.0.0" flutter: plugin: diff --git a/packages/webview_flutter/webview_flutter_android/test/surface_android_test.dart b/packages/webview_flutter/webview_flutter_android/test/surface_android_test.dart new file mode 100644 index 000000000000..63e752b419e6 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/test/surface_android_test.dart @@ -0,0 +1,115 @@ +// 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:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:webview_flutter_android/webview_surface_android.dart'; +import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('SurfaceAndroidWebView', () { + late List log; + + setUpAll(() { + SystemChannels.platform_views.setMockMethodCallHandler( + (MethodCall call) async { + log.add(call); + if (call.method == 'resize') { + return { + 'width': call.arguments['width'], + 'height': call.arguments['height'], + }; + } + }, + ); + }); + + tearDownAll(() { + SystemChannels.platform_views.setMockMethodCallHandler(null); + }); + + setUp(() { + log = []; + }); + + testWidgets( + 'uses hybrid composition when background color is not 100% opaque', + (WidgetTester tester) async { + await tester.pumpWidget(Builder(builder: (BuildContext context) { + return SurfaceAndroidWebView().build( + context: context, + creationParams: CreationParams( + backgroundColor: Colors.transparent, + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + hasNavigationDelegate: false, + )), + javascriptChannelRegistry: JavascriptChannelRegistry(null), + webViewPlatformCallbacksHandler: + TestWebViewPlatformCallbacksHandler(), + ); + })); + await tester.pumpAndSettle(); + + final MethodCall createMethodCall = log[0]; + expect(createMethodCall.method, 'create'); + expect(createMethodCall.arguments, containsPair('hybrid', true)); + }); + + testWidgets('default text direction is ltr', (WidgetTester tester) async { + await tester.pumpWidget(Builder(builder: (BuildContext context) { + return SurfaceAndroidWebView().build( + context: context, + creationParams: CreationParams( + webSettings: WebSettings( + userAgent: const WebSetting.absent(), + hasNavigationDelegate: false, + )), + javascriptChannelRegistry: JavascriptChannelRegistry(null), + webViewPlatformCallbacksHandler: + TestWebViewPlatformCallbacksHandler(), + ); + })); + await tester.pumpAndSettle(); + + final MethodCall createMethodCall = log[0]; + expect(createMethodCall.method, 'create'); + expect( + createMethodCall.arguments, + containsPair( + 'direction', + AndroidViewController.kAndroidLayoutDirectionLtr, + ), + ); + }); + }); +} + +class TestWebViewPlatformCallbacksHandler + implements WebViewPlatformCallbacksHandler { + @override + FutureOr onNavigationRequest({ + required String url, + required bool isForMainFrame, + }) { + throw UnimplementedError(); + } + + @override + void onPageFinished(String url) {} + + @override + void onPageStarted(String url) {} + + @override + void onProgress(int progress) {} + + @override + void onWebResourceError(WebResourceError error) {} +} From 84dfaa315ddc88cb75d82ee555c40c1d4f1c2606 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 20 Jul 2022 13:27:05 -0400 Subject: [PATCH 515/844] [camera] Improve Windows README (#6118) --- packages/camera/camera_windows/CHANGELOG.md | 4 ++++ packages/camera/camera_windows/README.md | 8 +++++--- packages/camera/camera_windows/pubspec.yaml | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index f84e442c68da..6883b5508318 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.0+3 + +* Updates the README to better explain how to use the unendorsed package. + ## 0.1.0+2 * Updates references to the obsolete master branch. diff --git a/packages/camera/camera_windows/README.md b/packages/camera/camera_windows/README.md index dc27bcc85e9d..4b66ad3dfe32 100644 --- a/packages/camera/camera_windows/README.md +++ b/packages/camera/camera_windows/README.md @@ -10,8 +10,10 @@ See [missing implementations and limitations](#missing-features-on-the-windows-p ### Depend on the package This package is not an [endorsed][endorsed-federated-plugin] -implementation of the [`camera`][camera] plugin, so you'll need to -[add it explicitly][install]. +implementation of the [`camera`][camera] plugin, so in addition to depending +on [`camera`][camera] you'll need to +[add `camera_windows` to your pubspec.yaml explicitly][install]. +Once you do, you can use the [`camera`][camera] APIs as you normally would. ## Missing features on the Windows platform @@ -63,4 +65,4 @@ disposing of the camera is the only way to reset the situation. [install]: https://pub.dev/packages/camera_windows/install [camera-control-issue]: https://github.com/flutter/flutter/issues/97537 [device-orientation-issue]: https://github.com/flutter/flutter/issues/97540 -[image-streams-issue]: https://github.com/flutter/flutter/issues/97542 \ No newline at end of file +[image-streams-issue]: https://github.com/flutter/flutter/issues/97542 diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index b519668c431a..055313bbcf29 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_windows description: A Flutter plugin for getting information about and controlling the camera on Windows. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.1.0+2 +version: 0.1.0+3 environment: sdk: ">=2.12.0 <3.0.0" From c16623d786d04e500c6f84809390c40cc305b548 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Wed, 20 Jul 2022 13:14:05 -0700 Subject: [PATCH 516/844] [video_player_web] Improve handling of "Infinite" videos. (#6101) --- .../video_player_web/CHANGELOG.md | 4 ++ .../integration_test/duration_utils_test.dart | 52 +++++++++++++++++++ .../example/integration_test/utils.dart | 40 ++++++++++++++ .../integration_test/video_player_test.dart | 22 ++++++++ .../video_player_web/example/pubspec.yaml | 1 + .../lib/src/duration_utils.dart | 33 ++++++++++++ .../lib/src/video_player.dart | 11 ++-- .../video_player_web/pubspec.yaml | 2 +- 8 files changed, 158 insertions(+), 7 deletions(-) create mode 100644 packages/video_player/video_player_web/example/integration_test/duration_utils_test.dart create mode 100644 packages/video_player/video_player_web/lib/src/duration_utils.dart diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index e36d044901a4..603f2967a605 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.11 + +* Improves handling of videos with `Infinity` duration. + ## 2.0.10 * Minor fixes for new analysis options. diff --git a/packages/video_player/video_player_web/example/integration_test/duration_utils_test.dart b/packages/video_player/video_player_web/example/integration_test/duration_utils_test.dart new file mode 100644 index 000000000000..c0d639843833 --- /dev/null +++ b/packages/video_player/video_player_web/example/integration_test/duration_utils_test.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_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:video_player_web/src/duration_utils.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('convertNumVideoDurationToPluginDuration', () { + testWidgets('Finite value converts to milliseconds', + (WidgetTester _) async { + final Duration? result = convertNumVideoDurationToPluginDuration(1.5); + final Duration? zero = convertNumVideoDurationToPluginDuration(0.0001); + + expect(result, isNotNull); + expect(result!.inMilliseconds, equals(1500)); + expect(zero, equals(Duration.zero)); + }); + + testWidgets('Finite value rounds 3rd decimal value', + (WidgetTester _) async { + final Duration? result = + convertNumVideoDurationToPluginDuration(1.567899089087); + final Duration? another = + convertNumVideoDurationToPluginDuration(1.567199089087); + + expect(result, isNotNull); + expect(result!.inMilliseconds, equals(1568)); + expect(another!.inMilliseconds, equals(1567)); + }); + + testWidgets('Infinite value returns magic constant', + (WidgetTester _) async { + final Duration? result = + convertNumVideoDurationToPluginDuration(double.infinity); + + expect(result, isNotNull); + expect(result, equals(jsCompatibleTimeUnset)); + expect(result!.inMilliseconds, equals(-9007199254740990)); + }); + + testWidgets('NaN value returns null', (WidgetTester _) async { + final Duration? result = + convertNumVideoDurationToPluginDuration(double.nan); + + expect(result, isNull); + }); + }); +} 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 b0118514053a..2bb234ea3660 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,6 +2,13 @@ // 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'; + // Returns the URL to load an asset from this example app as a network source. // // TODO(stuartmorgan): Convert this to a local `HttpServer` that vends the @@ -14,3 +21,36 @@ String getUrlForAssetAsNetworkSource(String assetKey) { '$assetKey' '?raw=true'; } + +@JS() +@anonymous +class _Descriptor { + // 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, + bool writable, + Object value, + }); +} + +@JS('Object.defineProperty') +external void _defineProperty( + Object object, + String property, + _Descriptor description, +); + +/// Forces a VideoElement to report "Infinity" duration. +/// +/// Uses JS Object.defineProperty to set the value of a readonly property. +void setInfinityDuration(VideoElement element) { + _defineProperty( + element, + 'duration', + _Descriptor( + writable: true, + value: double.infinity, + )); +} 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 41aba9792e23..28046f42e9a8 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 @@ -8,8 +8,11 @@ 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 'utils.dart'; + void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -190,6 +193,25 @@ void main() { expect(events, hasLength(1)); expect(events[0].eventType, VideoEventType.initialized); }); + + // Issue: https://github.com/flutter/flutter/issues/105649 + testWidgets('supports `Infinity` duration', (WidgetTester _) async { + setInfinityDuration(video); + expect(video.duration.isInfinite, isTrue); + + final Future> stream = timedStream + .where((VideoEvent event) => + event.eventType == VideoEventType.initialized) + .toList(); + + video.dispatchEvent(html.Event('canplay')); + + final List events = await stream; + + expect(events, hasLength(1)); + expect(events[0].eventType, VideoEventType.initialized); + expect(events[0].duration, equals(jsCompatibleTimeUnset)); + }); }); }); } diff --git a/packages/video_player/video_player_web/example/pubspec.yaml b/packages/video_player/video_player_web/example/pubspec.yaml index 6fb2cd07ddf1..abd299a6c1b3 100644 --- a/packages/video_player/video_player_web/example/pubspec.yaml +++ b/packages/video_player/video_player_web/example/pubspec.yaml @@ -8,6 +8,7 @@ environment: dependencies: flutter: sdk: flutter + js: ^0.6.0 video_player_web: path: ../ diff --git a/packages/video_player/video_player_web/lib/src/duration_utils.dart b/packages/video_player/video_player_web/lib/src/duration_utils.dart new file mode 100644 index 000000000000..030d6b040988 --- /dev/null +++ b/packages/video_player/video_player_web/lib/src/duration_utils.dart @@ -0,0 +1,33 @@ +// 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. + +/// The "length" of a video which doesn't have finite duration. +// See: https://github.com/flutter/flutter/issues/107882 +const Duration jsCompatibleTimeUnset = Duration( + milliseconds: -9007199254740990, // Number.MIN_SAFE_INTEGER + 1. -(2^53 - 1) +); + +/// Converts a `num` duration coming from a [VideoElement] into a [Duration] that +/// the plugin can use. +/// +/// From the documentation, `videoDuration` is "a double-precision floating-point +/// value indicating the duration of the media in seconds. +/// If no media data is available, the value `NaN` is returned. +/// If the element's media doesn't have a known duration —such as for live media +/// streams— the value of duration is `+Infinity`." +/// +/// If the `videoDuration` is finite, this method returns it as a `Duration`. +/// If the `videoDuration` is `Infinity`, the duration will be +/// `-9007199254740990` milliseconds. (See https://github.com/flutter/flutter/issues/107882) +/// If the `videoDuration` is `NaN`, this will return null. +Duration? convertNumVideoDurationToPluginDuration(num duration) { + if (duration.isFinite) { + return Duration( + milliseconds: (duration * 1000).round(), + ); + } else if (duration.isInfinite) { + return jsCompatibleTimeUnset; + } + return null; +} 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 076167383ce7..02ead1fdf93b 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 @@ -9,6 +9,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; +import 'duration_utils.dart'; + // An error code value to error name Map. // See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/code const Map _kErrorValueToErrorName = { @@ -194,13 +196,10 @@ class VideoPlayer { // Sends an [VideoEventType.initialized] [VideoEvent] with info about the wrapped video. void _sendInitialized() { - final Duration? duration = !_videoElement.duration.isNaN - ? Duration( - milliseconds: (_videoElement.duration * 1000).round(), - ) - : null; + final Duration? duration = + convertNumVideoDurationToPluginDuration(_videoElement.duration); - final Size? size = !_videoElement.videoHeight.isNaN + final Size? size = _videoElement.videoHeight.isFinite ? Size( _videoElement.videoWidth.toDouble(), _videoElement.videoHeight.toDouble(), diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 36b89abd1f31..408334114c0c 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/plugins/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.0.10 +version: 2.0.11 environment: sdk: ">=2.12.0 <3.0.0" From 9beb9a64b2a984dd91232b33533b28e45b67cc90 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 20 Jul 2022 16:15:04 -0400 Subject: [PATCH 517/844] [tool] Bypass version/changelog checks for some PRs (#6124) --- script/tool/CHANGELOG.md | 5 + .../tool/lib/src/version_check_command.dart | 54 ++++- .../tool/test/version_check_command_test.dart | 213 ++++++++++++++++++ 3 files changed, 270 insertions(+), 2 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index da14eae285e3..a84e9af63902 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,8 @@ +## NEXT + +- Bypasses version and CHANGELOG checks for Dependabot PRs for packages + that are known not to be client-affecting. + ## 0.8.8 - Allows pre-release versions in `version-check`. diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart index 246382dfae00..e914d2f080e5 100644 --- a/script/tool/lib/src/version_check_command.dart +++ b/script/tool/lib/src/version_check_command.dart @@ -569,11 +569,15 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. } if (state.needsVersionChange) { - if (_getChangeDescription().split('\n').any((String line) => + final String changeDescription = _getChangeDescription(); + if (changeDescription.split('\n').any((String line) => line.startsWith(_missingVersionChangeJustificationMarker))) { logWarning('Ignoring lack of version change due to ' '"$_missingVersionChangeJustificationMarker" in the ' 'change description.'); + } else if (_isAllowedDependabotChange(package, changeDescription)) { + logWarning('Ignoring lack of version change for Dependabot change to ' + 'a known internal dependency.'); } else { printError( 'No version change found, but the change to this package could ' @@ -587,11 +591,15 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. } if (!state.hasChangelogChange) { - if (_getChangeDescription().split('\n').any((String line) => + final String changeDescription = _getChangeDescription(); + if (changeDescription.split('\n').any((String line) => line.startsWith(_missingChangelogChangeJustificationMarker))) { logWarning('Ignoring lack of CHANGELOG update due to ' '"$_missingChangelogChangeJustificationMarker" in the ' 'change description.'); + } else if (_isAllowedDependabotChange(package, changeDescription)) { + logWarning('Ignoring lack of CHANGELOG update for Dependabot change to ' + 'a known internal dependency.'); } else { printError( 'No CHANGELOG change found. If this PR needs an exemption from ' @@ -605,4 +613,46 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. return null; } + + /// Returns true if [changeDescription] matches a Dependabot change for a + /// dependency roll that should bypass the normal version and CHANGELOG change + /// checks (for dependencies that are known not to have client impact). + bool _isAllowedDependabotChange( + RepositoryPackage package, String changeDescription) { + // Espresso exports some dependencies that are normally just internal test + // utils, so always require reviewers to check that. + if (package.directory.basename == 'espresso') { + return false; + } + + // A string that is in all Dependabot PRs, but extremely unlikely to be in + // any other PR, to identify Dependabot PRs. + const String dependabotMarker = 'Dependabot commands and options'; + // Expression to extract the name of the dependency being updated. + final RegExp dependencyRegex = + RegExp(r'Bumps? \[(.*?)\]\(.*?\) from [\d.]+ to [\d.]+'); + + // Allowed exact dependency names. + const Set allowedDependencies = { + 'junit', + 'robolectric', + }; + const Set allowedDependencyPrefixes = { + 'mockito-' // mockito-core, mockito-inline, etc. + }; + + if (changeDescription.contains(dependabotMarker)) { + final Match? match = dependencyRegex.firstMatch(changeDescription); + if (match != null) { + final String dependency = match.group(1)!; + if (allowedDependencies.contains(dependency) || + allowedDependencyPrefixes + .any((String prefix) => dependency.startsWith(prefix))) { + return true; + } + } + } + + return false; + } } diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart index 8f8d510fd106..82133328089e 100644 --- a/script/tool/test/version_check_command_test.dart +++ b/script/tool/test/version_check_command_test.dart @@ -40,6 +40,55 @@ void testAllowedVersion( } } +String _generateFakeDependabotPRDescription(String package) { + return ''' +Bumps [$package](https://github.com/foo/$package) from 1.0.0 to 2.0.0. +

+Release notes +

Sourced from $package's releases.

+
+... +
+
+
+Commits +
    +
  • ...
  • +
+
+
+ + +[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=$package&package-manager=gradle&previous-version=1.0.0&new-version=2.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-automerge-start) +[//]: # (dependabot-automerge-end) + +--- + +
+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 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) + + +
+'''; +} + class MockProcessResult extends Mock implements io.ProcessResult {} void main() { @@ -1078,6 +1127,170 @@ No CHANGELOG change: Code change is only to implementation comments. ]), ); }); + + group('dependabot', () { + test('allows missing version and CHANGELOG change for mockito', + () async { + final RepositoryPackage plugin = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); + + const String changelog = ''' +## 1.0.0 +* Some changes. +'''; + plugin.changelogFile.writeAsStringSync(changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/plugin/android/build.gradle +'''), + ]; + + final File changeDescriptionFile = + fileSystem.file('change_description.txt'); + changeDescriptionFile.writeAsStringSync( + _generateFakeDependabotPRDescription('mockito-core')); + + final List output = + await _runWithMissingChangeDetection([ + '--change-description-file=${changeDescriptionFile.path}' + ]); + + expect( + output, + containsAllInOrder([ + contains('Ignoring lack of version change for Dependabot ' + 'change to a known internal dependency.'), + contains('Ignoring lack of CHANGELOG update for Dependabot ' + 'change to a known internal dependency.'), + ]), + ); + }); + + test('allows missing version and CHANGELOG change for robolectric', + () async { + final RepositoryPackage plugin = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); + + const String changelog = ''' +## 1.0.0 +* Some changes. +'''; + plugin.changelogFile.writeAsStringSync(changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/plugin/android/build.gradle +'''), + ]; + + final File changeDescriptionFile = + fileSystem.file('change_description.txt'); + changeDescriptionFile.writeAsStringSync( + _generateFakeDependabotPRDescription('robolectric')); + + final List output = + await _runWithMissingChangeDetection([ + '--change-description-file=${changeDescriptionFile.path}' + ]); + + expect( + output, + containsAllInOrder([ + contains('Ignoring lack of version change for Dependabot ' + 'change to a known internal dependency.'), + contains('Ignoring lack of CHANGELOG update for Dependabot ' + 'change to a known internal dependency.'), + ]), + ); + }); + + test('allows missing version and CHANGELOG change for junit', () async { + final RepositoryPackage plugin = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); + + const String changelog = ''' +## 1.0.0 +* Some changes. +'''; + plugin.changelogFile.writeAsStringSync(changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/plugin/android/build.gradle +'''), + ]; + + final File changeDescriptionFile = + fileSystem.file('change_description.txt'); + changeDescriptionFile + .writeAsStringSync(_generateFakeDependabotPRDescription('junit')); + + final List output = + await _runWithMissingChangeDetection([ + '--change-description-file=${changeDescriptionFile.path}' + ]); + + expect( + output, + containsAllInOrder([ + contains('Ignoring lack of version change for Dependabot ' + 'change to a known internal dependency.'), + contains('Ignoring lack of CHANGELOG update for Dependabot ' + 'change to a known internal dependency.'), + ]), + ); + }); + + test('fails for dependencies that are not explicitly allowed', + () async { + final RepositoryPackage plugin = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); + + const String changelog = ''' +## 1.0.0 +* Some changes. +'''; + plugin.changelogFile.writeAsStringSync(changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/plugin/android/build.gradle +'''), + ]; + + final File changeDescriptionFile = + fileSystem.file('change_description.txt'); + changeDescriptionFile.writeAsStringSync( + _generateFakeDependabotPRDescription('somethingelse')); + + Error? commandError; + final List output = + await _runWithMissingChangeDetection([ + '--change-description-file=${changeDescriptionFile.path}' + ], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('No version change found'), + contains('plugin:\n' + ' Missing version change'), + ]), + ); + }); + }); }); test('allows valid against pub', () async { From e92ede2e285429fa24843c6787be1d36aafe4d65 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 20 Jul 2022 17:00:04 -0400 Subject: [PATCH 518/844] [file_selector] Enable ArgumentErrors for type groups (#6126) --- .../file_selector/file_selector/CHANGELOG.md | 16 ++++++++++++++-- packages/file_selector/file_selector/README.md | 16 ++++++++++++++++ .../file_selector/lib/file_selector.dart | 6 ++++++ .../file_selector/file_selector/pubspec.yaml | 8 ++++---- 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index db29f829771d..356e9ffe5550 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,6 +1,18 @@ -## NEXT +## 0.9.0 -* Ignores deprecation warnings for upcoming styleFrom button API changes. +* **BREAKING CHANGE**: The following methods: + * `openFile` + * `openFiles` + * `getSavePath` + + can throw `ArgumentError`s if called with any `XTypeGroup`s that + do not contain appropriate filters for the current platform. For + example, an `XTypeGroup` that only specifies `webWildCards` will + throw on non-web platforms. + + To avoid runtime errors, ensure that all `XTypeGroup`s (other than + wildcards) set filters that cover every platform your application + targets. See the README for details. ## 0.8.4+3 diff --git a/packages/file_selector/file_selector/README.md b/packages/file_selector/file_selector/README.md index f5c1de8afebc..a45153357ba2 100644 --- a/packages/file_selector/file_selector/README.md +++ b/packages/file_selector/file_selector/README.md @@ -76,5 +76,21 @@ final XFile textFile = await textFile.saveTo(path); ``` +### Filtering by file types + +Different platforms support different type group filter options. To avoid +`ArgumentError`s on some platforms, ensure that any `XTypeGroup`s you pass set +filters that cover all platforms you are targeting, or that you conditionally +pass different `XTypeGroup`s based on `Platform`. + +| | macOS | Web | Windows | +|----------------|--------|-----|-------------| +| `extensions` | ✔️ | ✔️ | ✔️ | +| `mimeTypes` | ✔️† | ✔️ | | +| `macUTIs` | ✔️ | | | +| `webWildCards` | | ✔️ | | + +† `mimeTypes` are not supported on version of macOS earlier than 11 (Big Sur). + [example]:./example [entitlement]: https://docs.flutter.dev/desktop#entitlements-and-the-app-sandbox diff --git a/packages/file_selector/file_selector/lib/file_selector.dart b/packages/file_selector/file_selector/lib/file_selector.dart index 322ae6cda483..f357af07321a 100644 --- a/packages/file_selector/file_selector/lib/file_selector.dart +++ b/packages/file_selector/file_selector/lib/file_selector.dart @@ -17,6 +17,8 @@ export 'package:file_selector_platform_interface/file_selector_platform_interfac /// options. /// - On macOS, the union of all types allowed by all of the groups will be /// allowed. +/// Throws an [ArgumentError] if any type groups do not include filters +/// supported by the current platform. /// /// [initialDirectory] is the full path to the directory that will be displayed /// when the dialog is opened. When not provided, the platform will pick an @@ -47,6 +49,8 @@ Future openFile({ /// options. /// - On macOS, the union of all types allowed by all of the groups will be /// allowed. +/// Throws an [ArgumentError] if any type groups do not include filters +/// supported by the current platform. /// /// [initialDirectory] is the full path to the directory that will be displayed /// when the dialog is opened. When not provided, the platform will pick an @@ -75,6 +79,8 @@ Future> openFiles({ /// options. /// - On macOS, the union of all types allowed by all of the groups will be /// allowed. +/// Throws an [ArgumentError] if any type groups do not include filters +/// supported by the current platform. /// /// [initialDirectory] is the full path to the directory that will be displayed /// when the dialog is opened. When not provided, the platform will pick an diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index 12a1fbc47782..51eb0a478ee8 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.8.4+3 +version: 0.9.0 environment: sdk: ">=2.12.0 <3.0.0" @@ -20,10 +20,10 @@ flutter: default_package: file_selector_windows dependencies: - file_selector_macos: ^0.8.2 + file_selector_macos: ^0.9.0 file_selector_platform_interface: ^2.0.0 - file_selector_web: ^0.8.1 - file_selector_windows: ^0.8.2 + file_selector_web: ^0.9.0 + file_selector_windows: ^0.9.0 flutter: sdk: flutter From 89eff491b1daac4e4baf11f1270c674b74137435 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 20 Jul 2022 20:15:06 -0400 Subject: [PATCH 519/844] [tool] Handle dependabot commit messages (#6127) --- .../tool/lib/src/version_check_command.dart | 10 +++- .../tool/test/version_check_command_test.dart | 58 +++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart index e914d2f080e5..30a45d33a77f 100644 --- a/script/tool/lib/src/version_check_command.dart +++ b/script/tool/lib/src/version_check_command.dart @@ -627,7 +627,12 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. // A string that is in all Dependabot PRs, but extremely unlikely to be in // any other PR, to identify Dependabot PRs. - const String dependabotMarker = 'Dependabot commands and options'; + const String dependabotPRDescriptionMarker = + 'Dependabot commands and options'; + // The same thing, but for the Dependabot commit message, to work around + // https://github.com/cirruslabs/cirrus-ci-docs/issues/1029. + const String dependabotCommitMessageMarker = + 'Signed-off-by: dependabot[bot]'; // Expression to extract the name of the dependency being updated. final RegExp dependencyRegex = RegExp(r'Bumps? \[(.*?)\]\(.*?\) from [\d.]+ to [\d.]+'); @@ -641,7 +646,8 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. 'mockito-' // mockito-core, mockito-inline, etc. }; - if (changeDescription.contains(dependabotMarker)) { + if (changeDescription.contains(dependabotPRDescriptionMarker) || + changeDescription.contains(dependabotCommitMessageMarker)) { final Match? match = dependencyRegex.firstMatch(changeDescription); if (match != null) { final String dependency = match.group(1)!; diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart index 82133328089e..4586070fd67e 100644 --- a/script/tool/test/version_check_command_test.dart +++ b/script/tool/test/version_check_command_test.dart @@ -89,6 +89,23 @@ You can trigger Dependabot actions by commenting on this PR: '''; } +String _generateFakeDependabotCommitMessage(String package) { + return ''' +Bumps [$package](https://github.com/foo/$package) from 1.0.0 to 2.0.0. +- [Release notes](https://github.com/foo/$package/releases) +- [Commits](foo/$package@v4.3.1...v4.6.1) + +--- +updated-dependencies: +- dependency-name: $package + dependency-type: direct:production + update-type: version-update:semver-minor +... + +Signed-off-by: dependabot[bot] +'''; +} + class MockProcessResult extends Mock implements io.ProcessResult {} void main() { @@ -1290,6 +1307,47 @@ packages/plugin/android/build.gradle ]), ); }); + + // Tests workaround for + // https://github.com/cirruslabs/cirrus-ci-docs/issues/1029. + test('allow list works for commit messages', () async { + final RepositoryPackage plugin = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); + + const String changelog = ''' +## 1.0.0 +* Some changes. +'''; + plugin.changelogFile.writeAsStringSync(changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/plugin/android/build.gradle +'''), + ]; + + final File changeDescriptionFile = + fileSystem.file('change_description.txt'); + changeDescriptionFile.writeAsStringSync( + _generateFakeDependabotCommitMessage('mockito-core')); + + final List output = + await _runWithMissingChangeDetection([ + '--change-description-file=${changeDescriptionFile.path}' + ]); + + expect( + output, + containsAllInOrder([ + contains('Ignoring lack of version change for Dependabot ' + 'change to a known internal dependency.'), + contains('Ignoring lack of CHANGELOG update for Dependabot ' + 'change to a known internal dependency.'), + ]), + ); + }); }); }); From dcfbdf0e175b7fbac132afeceac38249d32ff46c Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 21 Jul 2022 12:21:04 -0400 Subject: [PATCH 520/844] [webview_flutter_android] Add `Copyable` mixin and implement for all non-data and non-global classes (#6079) --- .../webview_flutter_android/CHANGELOG.md | 5 + .../lib/src/android_webview.dart | 206 ++++++++++++++++-- .../lib/src/instance_manager.dart | 21 ++ .../webview_flutter_android/pubspec.yaml | 2 +- .../test/android_webview_test.dart | 31 +++ .../test/android_webview_test.mocks.dart | 41 +++- ...iew_android_cookie_manager_test.mocks.dart | 2 +- .../webview_android_widget_test.mocks.dart | 47 +++- 8 files changed, 329 insertions(+), 26 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index a8e9a927d5a8..ceaef43c5a37 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.9.1 + +* Updates Android WebView classes as Copyable. This is a part of moving the api to handle garbage + collection automatically. See https://github.com/flutter/flutter/issues/107199. + ## 2.9.0 * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). 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 7fdcf4b2871f..005e2818a9f0 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 @@ -2,6 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(bparrishMines): Replace unused callback methods in constructors with +// variables once automatic garbage collection is fully implemented. See +// https://github.com/flutter/flutter/issues/107199. +// ignore_for_file: avoid_unused_constructor_parameters + // TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) // ignore: unnecessary_import import 'dart:typed_data'; @@ -12,6 +17,18 @@ import 'package:flutter/widgets.dart' show AndroidViewSurface; import 'android_webview.pigeon.dart'; import 'android_webview_api_impls.dart'; +import 'instance_manager.dart'; + +/// Root of the Java class hierarchy. +/// +/// See https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html. +abstract class JavaObject with Copyable { + /// Constructs a [JavaObject] without creating the associated Java object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies. + JavaObject.detached(); +} /// An Android View that displays web pages. /// @@ -32,12 +49,18 @@ import 'android_webview_api_impls.dart'; /// [Web-based content](https://developer.android.com/guide/webapps). /// /// When a [WebView] is no longer needed [release] must be called. -class WebView { +class WebView extends JavaObject { /// Constructs a new WebView. - WebView({this.useHybridComposition = false}) { + WebView({this.useHybridComposition = false}) : super.detached() { api.createFromInstance(this); } + /// Constructs a [WebView] without creating the associated Java object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies. + WebView.detached({this.useHybridComposition = false}) : super.detached(); + /// Pigeon Host Api implementation for [WebView]. @visibleForTesting static WebViewHostApiImpl api = WebViewHostApiImpl(); @@ -363,6 +386,11 @@ class WebView { WebSettings.api.disposeFromInstance(settings); return api.disposeFromInstance(this); } + + @override + WebView copy() { + return WebView.detached(useHybridComposition: useHybridComposition); + } } /// Manages cookies globally for all webviews. @@ -415,16 +443,22 @@ class CookieManager { /// obtained from [WebView.settings] is tied to the life of the WebView. If a /// WebView has been destroyed, any method call on [WebSettings] will throw an /// Exception. -class WebSettings { +class WebSettings extends JavaObject { /// Constructs a [WebSettings]. /// /// This constructor is only used for testing. An instance should be obtained /// with [WebView.settings]. @visibleForTesting - WebSettings(WebView webView) { + WebSettings(WebView webView) : super.detached() { api.createFromInstance(this, webView); } + /// Constructs a [WebSettings] without creating the associated Java object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies. + WebSettings.detached() : super.detached(); + /// Pigeon Host Api implementation for [WebSettings]. @visibleForTesting static WebSettingsHostApiImpl api = WebSettingsHostApiImpl(); @@ -546,17 +580,35 @@ class WebSettings { Future setAllowFileAccess(bool enabled) { return api.setAllowFileAccessFromInstance(this, enabled); } + + @override + WebSettings copy() { + return WebSettings.detached(); + } } /// Exposes a channel to receive calls from javaScript. /// /// See [WebView.addJavaScriptChannel]. -abstract class JavaScriptChannel { +class JavaScriptChannel extends JavaObject { /// Constructs a [JavaScriptChannel]. - JavaScriptChannel(this.channelName) { + JavaScriptChannel( + this.channelName, { + void Function(String message)? postMessage, + }) : super.detached() { AndroidWebViewFlutterApis.instance.ensureSetUp(); } + /// Constructs a [JavaScriptChannel] without creating the associated Java + /// object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies. + JavaScriptChannel.detached( + this.channelName, { + void Function(String message)? postMessage, + }) : super.detached(); + /// Pigeon Host Api implementation for [JavaScriptChannel]. @visibleForTesting static JavaScriptChannelHostApiImpl api = JavaScriptChannelHostApiImpl(); @@ -565,16 +617,65 @@ abstract class JavaScriptChannel { final String channelName; /// Callback method when javaScript calls `postMessage` on the object instance passed. - void postMessage(String message); + void postMessage(String message) {} + + @override + JavaScriptChannel copy() { + return JavaScriptChannel.detached(channelName, postMessage: postMessage); + } } /// Receive various notifications and requests for [WebView]. -abstract class WebViewClient { +class WebViewClient extends JavaObject { /// Constructs a [WebViewClient]. - WebViewClient({this.shouldOverrideUrlLoading = true}) { + WebViewClient({ + this.shouldOverrideUrlLoading = true, + void Function(WebView webView, String url)? onPageStarted, + void Function(WebView webView, String url)? onPageFinished, + void Function( + WebView webView, + WebResourceRequest request, + WebResourceError error, + )? + onReceivedRequestError, + void Function( + WebView webView, + int errorCode, + String description, + String failingUrl, + )? + onReceivedError, + void Function(WebView webView, WebResourceRequest request)? requestLoading, + void Function(WebView webView, String url)? urlLoading, + }) : super.detached() { AndroidWebViewFlutterApis.instance.ensureSetUp(); } + /// Constructs a [WebViewClient] without creating the associated Java object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies. + WebViewClient.detached({ + this.shouldOverrideUrlLoading = true, + void Function(WebView webView, String url)? onPageStarted, + void Function(WebView webView, String url)? onPageFinished, + void Function( + WebView webView, + WebResourceRequest request, + WebResourceError error, + )? + onReceivedRequestError, + void Function( + WebView webView, + int errorCode, + String description, + String failingUrl, + )? + onReceivedError, + void Function(WebView webView, WebResourceRequest request)? requestLoading, + void Function(WebView webView, String url)? urlLoading, + }) : super.detached(); + /// User authentication failed on server. /// /// See https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_AUTHENTICATION @@ -735,15 +836,54 @@ abstract class WebViewClient { /// causes the current [WebView] to abort loading the URL, while returning /// false causes the [WebView] to continue loading the URL as usual. void urlLoading(WebView webView, String url) {} + + @override + WebViewClient copy() { + return WebViewClient.detached( + shouldOverrideUrlLoading: shouldOverrideUrlLoading, + onPageStarted: onPageStarted, + onPageFinished: onPageFinished, + onReceivedRequestError: onReceivedRequestError, + onReceivedError: onReceivedError, + requestLoading: requestLoading, + urlLoading: urlLoading, + ); + } } -/// The interface to be used when content can not be handled by the rendering engine for [WebView], and should be downloaded instead. -abstract class DownloadListener { +/// The interface to be used when content can not be handled by the rendering +/// engine for [WebView], and should be downloaded instead. +class DownloadListener extends JavaObject { /// Constructs a [DownloadListener]. - DownloadListener() { + DownloadListener({ + void Function( + String url, + String userAgent, + String contentDisposition, + String mimetype, + int contentLength, + )? + onDownloadStart, + }) : super.detached() { AndroidWebViewFlutterApis.instance.ensureSetUp(); } + /// Constructs a [DownloadListener] without creating the associated Java + /// object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies. + DownloadListener.detached({ + void Function( + String url, + String userAgent, + String contentDisposition, + String mimetype, + int contentLength, + )? + onDownloadStart, + }) : super.detached(); + /// Pigeon Host Api implementation for [DownloadListener]. @visibleForTesting static DownloadListenerHostApiImpl api = DownloadListenerHostApiImpl(); @@ -755,22 +895,43 @@ abstract class DownloadListener { String contentDisposition, String mimetype, int contentLength, - ); + ) {} + + @override + DownloadListener copy() { + return DownloadListener(onDownloadStart: onDownloadStart); + } } /// Handles JavaScript dialogs, favicons, titles, and the progress for [WebView]. -abstract class WebChromeClient { +class WebChromeClient extends JavaObject { /// Constructs a [WebChromeClient]. - WebChromeClient() { + WebChromeClient({ + void Function(WebView webView, int progress)? onProgressChanged, + }) : super.detached() { AndroidWebViewFlutterApis.instance.ensureSetUp(); } + /// Constructs a [WebChromeClient] without creating the associated Java + /// object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies. + WebChromeClient.detached({ + void Function(WebView webView, int progress)? onProgressChanged, + }) : super.detached(); + /// Pigeon Host Api implementation for [WebChromeClient]. @visibleForTesting static WebChromeClientHostApiImpl api = WebChromeClientHostApiImpl(); /// Notify the host application that a file should be downloaded. void onProgressChanged(WebView webView, int progress) {} + + @override + WebChromeClient copy() { + return WebChromeClient.detached(onProgressChanged: onProgressChanged); + } } /// Encompasses parameters to the [WebViewClient.requestLoading] method. @@ -846,17 +1007,23 @@ class FlutterAssetManager { /// Manages the JavaScript storage APIs provided by the [WebView]. /// /// Wraps [WebStorage](https://developer.android.com/reference/android/webkit/WebStorage). -class WebStorage { +class WebStorage extends JavaObject { /// Constructs a [WebStorage]. /// /// This constructor is only used for testing. An instance should be obtained /// with [WebStorage.instance]. @visibleForTesting - WebStorage() { + WebStorage() : super.detached() { AndroidWebViewFlutterApis.instance.ensureSetUp(); api.createFromInstance(this); } + /// Constructs a [WebStorage] without creating the associated Java object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies. + WebStorage.detached() : super.detached(); + /// Pigeon Host Api implementation for [WebStorage]. @visibleForTesting static WebStorageHostApiImpl api = WebStorageHostApiImpl(); @@ -868,4 +1035,9 @@ class WebStorage { Future deleteAllData() { return api.deleteAllDataFromInstance(this); } + + @override + WebStorage copy() { + return WebStorage.detached(); + } } diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart b/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart index e6dd2b2c2c1d..531fffa50e81 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart @@ -2,6 +2,27 @@ // 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'; + +/// An immutable object that can provide functional copies of itself. +/// +/// All implementers are expected to be immutable as defined by the annotation. +// TODO(bparrishMines): Uncomment annotation once +// https://github.com/flutter/plugins/pull/5831 lands or when making a breaking +// change for https://github.com/flutter/flutter/issues/107199. +// @immutable +mixin Copyable { + /// Instantiates and returns a functionally identical object to oneself. + /// + /// Outside of tests, this method should only ever be called by + /// [InstanceManager]. + /// + /// Subclasses should always override their parent's implementation of this + /// method. + @protected + Copyable copy(); +} + /// Maintains instances stored to communicate with java objects. class InstanceManager { final Map _instanceIdsToInstances = {}; diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index aaa4984bc8a1..e530b1e09cd4 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/plugins/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: 2.9.0 +version: 2.9.1 environment: sdk: ">=2.14.0 <3.0.0" 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 4c63ab025702..cb2ea0fbf839 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 @@ -318,6 +318,10 @@ void main() { verify(mockWebSettingsPlatformHostApi.dispose(webSettingsInstanceId)); verify(mockPlatformHostApi.dispose(webViewInstanceId)); }); + + test('copy', () { + expect(webView.copy(), isA()); + }); }); group('WebSettings', () { @@ -444,6 +448,10 @@ void main() { true, )); }); + + test('copy', () { + expect(webSettings.copy(), isA()); + }); }); group('JavaScriptChannel', () { @@ -472,6 +480,13 @@ void main() { ); verify(mockJavaScriptChannel.postMessage('Hello, World!')); }); + + test('copy', () { + expect( + JavaScriptChannel.detached('channel').copy(), + isA(), + ); + }); }); group('WebViewClient', () { @@ -591,6 +606,10 @@ void main() { 'https://www.google.com', )); }); + + test('copy', () { + expect(WebViewClient.detached().copy(), isA()); + }); }); group('DownloadListener', () { @@ -629,6 +648,10 @@ void main() { 45, )); }); + + test('copy', () { + expect(DownloadListener.detached().copy(), isA()); + }); }); group('WebChromeClient', () { @@ -664,6 +687,10 @@ void main() { ); verify(mockWebChromeClient.onProgressChanged(mockWebView, 76)); }); + + test('copy', () { + expect(WebChromeClient.detached().copy(), isA()); + }); }); }); @@ -706,5 +733,9 @@ void main() { webStorage.deleteAllData(); verify(mockPlatformHostApi.deleteAllData(webStorageInstanceId)); }); + + test('copy', () { + expect(WebStorage.detached().copy(), isA()); + }); }); } 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 85ab6685ca34..72d1d414f915 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 @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.1.0 from annotations +// Mocks generated by Mockito 5.2.0 from annotations // in webview_flutter_android/test/android_webview_test.dart. // Do not manually edit this file. @@ -22,7 +22,19 @@ import 'test_android_webview.pigeon.dart' as _i5; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types -class _FakeWebSettings_0 extends _i1.Fake implements _i2.WebSettings {} +class _FakeDownloadListener_0 extends _i1.Fake implements _i2.DownloadListener { +} + +class _FakeJavaScriptChannel_1 extends _i1.Fake + implements _i2.JavaScriptChannel {} + +class _FakeWebChromeClient_2 extends _i1.Fake implements _i2.WebChromeClient {} + +class _FakeWebSettings_3 extends _i1.Fake implements _i2.WebSettings {} + +class _FakeWebView_4 extends _i1.Fake implements _i2.WebView {} + +class _FakeWebViewClient_5 extends _i1.Fake implements _i2.WebViewClient {} /// A class which mocks [CookieManagerHostApi]. /// @@ -47,6 +59,7 @@ class MockCookieManagerHostApi extends _i1.Mock /// A class which mocks [DownloadListener]. /// /// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable class MockDownloadListener extends _i1.Mock implements _i2.DownloadListener { MockDownloadListener() { _i1.throwOnMissingStub(this); @@ -59,11 +72,16 @@ class MockDownloadListener extends _i1.Mock implements _i2.DownloadListener { Invocation.method(#onDownloadStart, [url, userAgent, contentDisposition, mimetype, contentLength]), returnValueForMissingStub: null); + @override + _i2.DownloadListener copy() => + (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeDownloadListener_0()) as _i2.DownloadListener); } /// A class which mocks [JavaScriptChannel]. /// /// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable class MockJavaScriptChannel extends _i1.Mock implements _i2.JavaScriptChannel { MockJavaScriptChannel() { _i1.throwOnMissingStub(this); @@ -77,6 +95,10 @@ class MockJavaScriptChannel extends _i1.Mock implements _i2.JavaScriptChannel { void postMessage(String? message) => super.noSuchMethod(Invocation.method(#postMessage, [message]), returnValueForMissingStub: null); + @override + _i2.JavaScriptChannel copy() => + (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeJavaScriptChannel_1()) as _i2.JavaScriptChannel); } /// A class which mocks [TestDownloadListenerHostApi]. @@ -395,6 +417,7 @@ class MockTestAssetManagerHostApi extends _i1.Mock /// A class which mocks [WebChromeClient]. /// /// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { MockWebChromeClient() { _i1.throwOnMissingStub(this); @@ -404,11 +427,16 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { void onProgressChanged(_i2.WebView? webView, int? progress) => super .noSuchMethod(Invocation.method(#onProgressChanged, [webView, progress]), returnValueForMissingStub: null); + @override + _i2.WebChromeClient copy() => + (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWebChromeClient_2()) as _i2.WebChromeClient); } /// A class which mocks [WebView]. /// /// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable class MockWebView extends _i1.Mock implements _i2.WebView { MockWebView() { _i1.throwOnMissingStub(this); @@ -421,7 +449,7 @@ class MockWebView extends _i1.Mock implements _i2.WebView { @override _i2.WebSettings get settings => (super.noSuchMethod(Invocation.getter(#settings), - returnValue: _FakeWebSettings_0()) as _i2.WebSettings); + returnValue: _FakeWebSettings_3()) as _i2.WebSettings); @override _i4.Future loadData( {String? data, String? mimeType, String? encoding}) => @@ -554,11 +582,15 @@ class MockWebView extends _i1.Mock implements _i2.WebView { (super.noSuchMethod(Invocation.method(#release, []), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i2.WebView copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWebView_4()) as _i2.WebView); } /// A class which mocks [WebViewClient]. /// /// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable class MockWebViewClient extends _i1.Mock implements _i2.WebViewClient { MockWebViewClient() { _i1.throwOnMissingStub(this); @@ -597,4 +629,7 @@ class MockWebViewClient extends _i1.Mock implements _i2.WebViewClient { void urlLoading(_i2.WebView? webView, String? url) => super.noSuchMethod(Invocation.method(#urlLoading, [webView, url]), returnValueForMissingStub: null); + @override + _i2.WebViewClient copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWebViewClient_5()) as _i2.WebViewClient); } diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart index ff9974b1a10b..308aba4fa1b0 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.1.0 from annotations +// Mocks generated by Mockito 5.2.0 from annotations // in webview_flutter_android/test/webview_android_cookie_manager_test.dart. // Do not manually edit this file. diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart index 78e60cac1b8e..f9e8838500d5 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.1.0 from annotations +// Mocks generated by Mockito 5.2.0 from annotations // in webview_flutter_android/test/webview_android_widget_test.dart. // Do not manually edit this file. @@ -24,11 +24,23 @@ import 'package:webview_flutter_platform_interface/webview_flutter_platform_inte class _FakeWebSettings_0 extends _i1.Fake implements _i2.WebSettings {} -class _FakeJavascriptChannelRegistry_1 extends _i1.Fake - implements _i3.JavascriptChannelRegistry {} +class _FakeWebStorage_1 extends _i1.Fake implements _i2.WebStorage {} class _FakeWebView_2 extends _i1.Fake implements _i2.WebView {} +class _FakeDownloadListener_3 extends _i1.Fake implements _i2.DownloadListener { +} + +class _FakeJavascriptChannelRegistry_4 extends _i1.Fake + implements _i3.JavascriptChannelRegistry {} + +class _FakeJavaScriptChannel_5 extends _i1.Fake + implements _i2.JavaScriptChannel {} + +class _FakeWebChromeClient_6 extends _i1.Fake implements _i2.WebChromeClient {} + +class _FakeWebViewClient_7 extends _i1.Fake implements _i2.WebViewClient {} + /// A class which mocks [FlutterAssetManager]. /// /// See the documentation for Mockito's code generation for more information. @@ -52,6 +64,7 @@ class MockFlutterAssetManager extends _i1.Mock /// A class which mocks [WebSettings]. /// /// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable class MockWebSettings extends _i1.Mock implements _i2.WebSettings { MockWebSettings() { _i1.throwOnMissingStub(this); @@ -119,11 +132,15 @@ class MockWebSettings extends _i1.Mock implements _i2.WebSettings { (super.noSuchMethod(Invocation.method(#setAllowFileAccess, [enabled]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i2.WebSettings copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWebSettings_0()) as _i2.WebSettings); } /// A class which mocks [WebStorage]. /// /// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable class MockWebStorage extends _i1.Mock implements _i2.WebStorage { MockWebStorage() { _i1.throwOnMissingStub(this); @@ -134,11 +151,15 @@ class MockWebStorage extends _i1.Mock implements _i2.WebStorage { (super.noSuchMethod(Invocation.method(#deleteAllData, []), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i2.WebStorage copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWebStorage_1()) as _i2.WebStorage); } /// A class which mocks [WebView]. /// /// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable class MockWebView extends _i1.Mock implements _i2.WebView { MockWebView() { _i1.throwOnMissingStub(this); @@ -284,6 +305,9 @@ class MockWebView extends _i1.Mock implements _i2.WebView { (super.noSuchMethod(Invocation.method(#release, []), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i2.WebView copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWebView_2()) as _i2.WebView); } /// A class which mocks [WebResourceRequest]. @@ -338,6 +362,10 @@ class MockWebViewAndroidDownloadListener extends _i1.Mock Invocation.method(#onDownloadStart, [url, userAgent, contentDisposition, mimetype, contentLength]), returnValueForMissingStub: null); + @override + _i2.DownloadListener copy() => + (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeDownloadListener_3()) as _i2.DownloadListener); } /// A class which mocks [WebViewAndroidJavaScriptChannel]. @@ -352,7 +380,7 @@ class MockWebViewAndroidJavaScriptChannel extends _i1.Mock @override _i3.JavascriptChannelRegistry get javascriptChannelRegistry => (super.noSuchMethod(Invocation.getter(#javascriptChannelRegistry), - returnValue: _FakeJavascriptChannelRegistry_1()) + returnValue: _FakeJavascriptChannelRegistry_4()) as _i3.JavascriptChannelRegistry); @override String get channelName => @@ -362,6 +390,10 @@ class MockWebViewAndroidJavaScriptChannel extends _i1.Mock void postMessage(String? message) => super.noSuchMethod(Invocation.method(#postMessage, [message]), returnValueForMissingStub: null); + @override + _i2.JavaScriptChannel copy() => + (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeJavaScriptChannel_5()) as _i2.JavaScriptChannel); } /// A class which mocks [WebViewAndroidWebChromeClient]. @@ -377,6 +409,10 @@ class MockWebViewAndroidWebChromeClient extends _i1.Mock void onProgressChanged(_i2.WebView? webView, int? progress) => super .noSuchMethod(Invocation.method(#onProgressChanged, [webView, progress]), returnValueForMissingStub: null); + @override + _i2.WebChromeClient copy() => + (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWebChromeClient_6()) as _i2.WebChromeClient); } /// A class which mocks [WebViewAndroidWebViewClient]. @@ -445,6 +481,9 @@ class MockWebViewAndroidWebViewClient extends _i1.Mock void requestLoading(_i2.WebView? webView, _i2.WebResourceRequest? request) => super.noSuchMethod(Invocation.method(#requestLoading, [webView, request]), returnValueForMissingStub: null); + @override + _i2.WebViewClient copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWebViewClient_7()) as _i2.WebViewClient); } /// A class which mocks [JavascriptChannelRegistry]. From 901f6bf0dfbaf910166602c03d0e39ad6e01cf0d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Jul 2022 18:24:06 +0000 Subject: [PATCH 521/844] [in_app_pur]: Bump mockito-core from 3.6.0 to 4.6.1 in /packages/in_app_purchase/in_app_purchase/example/android/app (#5895) --- .../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 dc8718957291..441b49cf373b 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 @@ -108,7 +108,7 @@ flutter { dependencies { implementation 'com.android.billingclient:billing:3.0.2' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-core:3.6.0' + testImplementation 'org.mockito:mockito-core:4.6.1' testImplementation 'org.json:json:20180813' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' From 421c9f2f0887ed90b7f5939e95b081a2691b9e0b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Jul 2022 20:39:07 +0000 Subject: [PATCH 522/844] [camera]: Bump mockito-inline from 4.0.0 to 4.6.1 in /packages/camera/camera_android/android (#6020) --- packages/camera/camera_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android/android/build.gradle b/packages/camera/camera_android/android/build.gradle index 202767632117..1967ebaf0236 100644 --- a/packages/camera/camera_android/android/build.gradle +++ b/packages/camera/camera_android/android/build.gradle @@ -61,7 +61,7 @@ android { dependencies { compileOnly 'androidx.annotation:annotation:1.1.0' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-inline:4.0.0' + testImplementation 'org.mockito:mockito-inline:4.6.1' testImplementation 'androidx.test:core:1.3.0' testImplementation 'org.robolectric:robolectric:4.5' } From 62e378231309219454da0a6dbe1421b6142a571d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Jul 2022 20:42:09 +0000 Subject: [PATCH 523/844] [quick_actions]: Bump mockito-android from 4.3.1 to 4.6.1 in /packages/quick_actions/quick_actions_android/example/android/app (#6114) --- .../quick_actions_android/example/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/quick_actions/quick_actions_android/example/android/app/build.gradle b/packages/quick_actions/quick_actions_android/example/android/app/build.gradle index 75920e00fcab..78d1c7394ba9 100644 --- a/packages/quick_actions/quick_actions_android/example/android/app/build.gradle +++ b/packages/quick_actions/quick_actions_android/example/android/app/build.gradle @@ -63,5 +63,5 @@ dependencies { androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' androidTestImplementation 'androidx.test.ext:junit:1.0.0' androidTestImplementation 'org.mockito:mockito-core:4.3.1' - androidTestImplementation 'org.mockito:mockito-android:4.3.1' + androidTestImplementation 'org.mockito:mockito-android:4.6.1' } From 898f240889500c2bd5f5e842dde2526b85a7e0d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Jul 2022 21:04:05 +0000 Subject: [PATCH 524/844] [local_auth]: Bump mockito-inline from 3.9.0 to 4.6.1 in /packages/local_auth/local_auth_android/android (#5901) --- packages/local_auth/local_auth_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/local_auth/local_auth_android/android/build.gradle b/packages/local_auth/local_auth_android/android/build.gradle index 0486b8cea21a..9b9213dfc736 100644 --- a/packages/local_auth/local_auth_android/android/build.gradle +++ b/packages/local_auth/local_auth_android/android/build.gradle @@ -53,7 +53,7 @@ dependencies { api "androidx.biometric:biometric:1.1.0" api "androidx.fragment:fragment:1.3.2" testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-inline:3.9.0' + testImplementation 'org.mockito:mockito-inline:4.6.1' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:rules:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' From 06879a953cd5c986e13609f9c5d57b1d84be661c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Jul 2022 22:06:05 +0000 Subject: [PATCH 525/844] [in_app_pur]: Bump mockito-core from 3.6.0 to 4.6.1 in /packages/in_app_purchase/in_app_purchase_android/example/android/app (#5894) --- .../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 498355af5976..f24b1a19c944 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 @@ -108,7 +108,7 @@ flutter { dependencies { implementation 'com.android.billingclient:billing:5.0.0' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-core:3.6.0' + testImplementation 'org.mockito:mockito-core:4.6.1' testImplementation 'org.json:json:20180813' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' From 58cb3839ee38b1f7d5263ca4dc6383b56cddf750 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Jul 2022 22:09:06 +0000 Subject: [PATCH 526/844] [shared_pref]: Bump mockito-inline from 3.9.0 to 4.6.1 in /packages/shared_preferences/shared_preferences_android/android (#6027) --- .../shared_preferences_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index 76106179a8a9..770de0cfbd5c 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -43,7 +43,7 @@ android { } dependencies { testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-inline:3.9.0' + testImplementation 'org.mockito:mockito-inline:4.6.1' } From 114d8b84fca970cc8a0a7f9971288cd1b5d3b7a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Jul 2022 23:10:04 +0000 Subject: [PATCH 527/844] [video_player]: Bump mockito-core from 3.5.13 to 4.6.1 in /packages/video_player/video_player_android/example/android/app (#5896) --- .../video_player_android/example/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player_android/example/android/app/build.gradle b/packages/video_player/video_player_android/example/android/app/build.gradle index 7b3c7db80c7e..215deca1e2b0 100644 --- a/packages/video_player/video_player_android/example/android/app/build.gradle +++ b/packages/video_player/video_player_android/example/android/app/build.gradle @@ -60,7 +60,7 @@ flutter { dependencies { testImplementation 'junit:junit:4.13' testImplementation 'org.robolectric:robolectric:4.4' - testImplementation 'org.mockito:mockito-core:3.5.13' + testImplementation 'org.mockito:mockito-core:4.6.1' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } From 2fa56139e06ff147c0fcaed53fe04424bcb6ee64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Jul 2022 23:11:08 +0000 Subject: [PATCH 528/844] [sign_in]: Bump mockito-inline from 3.9.0 to 4.6.1 in /packages/google_sign_in/google_sign_in_android/android (#5897) --- .../google_sign_in/google_sign_in_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 aeef6d6e14e3..901dc8cf2147 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 @@ -51,5 +51,5 @@ dependencies { implementation 'com.google.android.gms:play-services-auth:20.0.1' implementation 'com.google.guava:guava:28.1-android' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-inline:3.9.0' + testImplementation 'org.mockito:mockito-inline:4.6.1' } From ad78836d13ab6f44bb0ffa6e223a5b655bec9e4e Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 21 Jul 2022 19:18:07 -0400 Subject: [PATCH 529/844] [webview_flutter_android] Updates the Java InstanceManager to take a listener for when an object is garbage collected (#6082) --- .../webview_flutter_android/CHANGELOG.md | 5 + .../DownloadListenerFlutterApiImpl.java | 15 +- .../DownloadListenerHostApiImpl.java | 2 +- .../webviewflutter/InstanceManager.java | 207 ++++++++++++++---- .../JavaScriptChannelFlutterApiImpl.java | 15 +- .../JavaScriptChannelHostApiImpl.java | 2 +- .../WebChromeClientFlutterApiImpl.java | 22 +- .../WebChromeClientHostApiImpl.java | 2 +- .../WebSettingsHostApiImpl.java | 5 +- .../webviewflutter/WebStorageHostApiImpl.java | 2 +- .../WebViewClientFlutterApiImpl.java | 75 ++++--- .../WebViewClientHostApiImpl.java | 2 +- .../webviewflutter/WebViewFlutterPlugin.java | 8 +- .../webviewflutter/WebViewHostApiImpl.java | 4 +- .../webviewflutter/DownloadListenerTest.java | 8 +- .../webviewflutter/InstanceManagerTest.java | 62 ++++++ .../webviewflutter/JavaScriptChannelTest.java | 8 +- .../webviewflutter/WebChromeClientTest.java | 13 +- .../webviewflutter/WebSettingsTest.java | 9 +- .../WebStorageHostApiImplTest.java | 9 +- .../webviewflutter/WebViewClientTest.java | 11 +- .../plugins/webviewflutter/WebViewTest.java | 19 +- .../webview_flutter_android/pubspec.yaml | 2 +- 23 files changed, 396 insertions(+), 111 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/InstanceManagerTest.java diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index ceaef43c5a37..5706d653a09a 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.9.2 + +* Updates the Java InstanceManager to take a listener for when an object is garbage collected. + See https://github.com/flutter/flutter/issues/107199. + ## 2.9.1 * Updates Android WebView classes as Copyable. This is a part of moving the api to handle garbage diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerFlutterApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerFlutterApiImpl.java index 2dd98c47d582..1981d8eccf12 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerFlutterApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerFlutterApiImpl.java @@ -38,7 +38,7 @@ public void onDownloadStart( long contentLength, Reply callback) { onDownloadStart( - instanceManager.getInstanceId(downloadListener), + getIdentifierForListener(downloadListener), url, userAgent, contentDisposition, @@ -54,11 +54,18 @@ public void onDownloadStart( * @param callback reply callback with return value from Dart */ public void dispose(DownloadListener downloadListener, Reply callback) { - final Long instanceId = instanceManager.removeInstance(downloadListener); - if (instanceId != null) { - dispose(instanceId, callback); + if (instanceManager.containsInstance(downloadListener)) { + dispose(getIdentifierForListener(downloadListener), callback); } else { callback.reply(null); } } + + private long getIdentifierForListener(DownloadListener listener) { + final Long identifier = instanceManager.getIdentifierForStrongReference(listener); + if (identifier == null) { + throw new IllegalStateException("Could not find identifier for DownloadListener."); + } + return identifier; + } } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerHostApiImpl.java index 9694f396ad2e..ed0c2aee4700 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerHostApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerHostApiImpl.java @@ -91,6 +91,6 @@ public DownloadListenerHostApiImpl( public void create(Long instanceId) { final DownloadListener downloadListener = downloadListenerCreator.createDownloadListener(flutterApi); - instanceManager.addInstance(downloadListener, instanceId); + instanceManager.addDartCreatedInstance(downloadListener, instanceId); } } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java index a368baf266dd..306dc20be47d 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java @@ -4,81 +4,202 @@ package io.flutter.plugins.webviewflutter; -import android.util.LongSparseArray; +import android.os.Handler; +import android.os.Looper; +import androidx.annotation.Nullable; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; import java.util.HashMap; -import java.util.Map; +import java.util.WeakHashMap; /** - * Maintains instances to intercommunicate with Dart objects. + * Maintains instances used to communicate with the corresponding objects in Dart. * - *

When an instance is added with an instanceId, either can be used to retrieve the other. + *

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 `{@link #remove(long)}` and the weak reference is deallocated, the + * `finalizationListener` 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 {@link #getIdentifierForStrongReference(Object)}), the strong reference to the + * instance is recreated. The strong reference will then need to be removed manually again. */ +@SuppressWarnings("unchecked") public class InstanceManager { - private final LongSparseArray instanceIdsToInstances = new LongSparseArray<>(); - private final Map instancesToInstanceIds = new HashMap<>(); + // Identifiers are locked to a specific range to avoid collisions with objects + // created simultaneously from Dart. + // Host uses identifiers >= 2^16 and Dart is expected to use values n where, + // 0 <= n < 2^16. + private static final long MIN_HOST_CREATED_IDENTIFIER = 65536; + private static final long CLEAR_FINALIZED_WEAK_REFERENCES_INTERVAL = 30000; + + /** Interface for listening when a weak reference of an instance is removed from the manager. */ + public interface FinalizationListener { + void onFinalize(long identifier); + } + + private final WeakHashMap identifiers = new WeakHashMap<>(); + private final HashMap> weakInstances = new HashMap<>(); + private final HashMap strongInstances = new HashMap<>(); + + private final ReferenceQueue referenceQueue = new ReferenceQueue<>(); + private final HashMap, Long> weakReferencesToIdentifiers = new HashMap<>(); + + private final Handler handler = new Handler(Looper.getMainLooper()); + + private final FinalizationListener finalizationListener; + + private long nextIdentifier = MIN_HOST_CREATED_IDENTIFIER; + private boolean isClosed = false; /** - * Add a new instance to the manager. + * Instantiate a new manager. * - *

If an instance or instanceId has already been added, it will be replaced by the new values. + *

When the manager is no longer needed, {@link #close()} must be called. * - * @param instance the new object to be added - * @param instanceId unique id of the added object + * @param finalizationListener the listener for garbage collected weak references. + * @return a new `InstanceManager`. */ - public void addInstance(Object instance, long instanceId) { - instancesToInstanceIds.put(instance, instanceId); - instanceIdsToInstances.append(instanceId, instance); + public static InstanceManager open(FinalizationListener finalizationListener) { + return new InstanceManager(finalizationListener); + } + + private InstanceManager(FinalizationListener finalizationListener) { + this.finalizationListener = finalizationListener; + handler.postDelayed( + this::releaseAllFinalizedInstances, CLEAR_FINALIZED_WEAK_REFERENCES_INTERVAL); } /** - * Remove the instance with instanceId from the manager. + * Removes `identifier` and its associated strongly referenced instance, if present, from the + * manager. * - * @param instanceId the id of the instance to be removed - * @return the removed instance if the manager contains the instanceId, otherwise null + * @param identifier the identifier paired to an instance. + * @param the expected return type. + * @return the removed instance if the manager contains the given identifier, otherwise null. */ - public Object removeInstanceWithId(long instanceId) { - final Object instance = instanceIdsToInstances.get(instanceId); - if (instance != null) { - instanceIdsToInstances.remove(instanceId); - instancesToInstanceIds.remove(instance); + @Nullable + public T remove(long identifier) { + assertManagerIsNotClosed(); + return (T) strongInstances.remove(identifier); + } + + /** + * 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 {@link #remove(long)}. + * + * @param instance an instance that may be stored in the manager. + * @return the identifier associated with `instance` if the manager contains the value, otherwise + * null. + */ + @Nullable + public Long getIdentifierForStrongReference(Object instance) { + assertManagerIsNotClosed(); + final Long identifier = identifiers.get(instance); + if (identifier != null) { + strongInstances.put(identifier, instance); } - return instance; + return identifier; + } + + /** + * 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 identifier the identifier to be paired with instance. This value must be >= 0. + */ + public void addDartCreatedInstance(Object instance, long identifier) { + assertManagerIsNotClosed(); + addInstance(instance, identifier); + } + + /** + * 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. + */ + public long addHostCreatedInstance(Object instance) { + assertManagerIsNotClosed(); + final long identifier = nextIdentifier++; + addInstance(instance, identifier); + return identifier; } /** - * Remove the instance from the manager. + * Retrieves the instance associated with identifier. * - * @param instance the instance to be removed - * @return the instanceId of the removed instance if the manager contains the value, otherwise - * null + * @param identifier the identifier paired to an instance. + * @param the expected return type. + * @return the instance associated with `identifier` if the manager contains the value, otherwise + * null. */ - public Long removeInstance(Object instance) { - final Long instanceId = instancesToInstanceIds.get(instance); - if (instanceId != null) { - instanceIdsToInstances.remove(instanceId); - instancesToInstanceIds.remove(instance); + @Nullable + public T getInstance(long identifier) { + assertManagerIsNotClosed(); + final WeakReference instance = (WeakReference) weakInstances.get(identifier); + if (instance != null) { + return instance.get(); } - return instanceId; + return (T) strongInstances.get(identifier); } /** - * Retrieve the Object paired with instanceId. + * Returns whether this manager contains the given `instance`. * - * @param instanceId the instanceId of the desired instance - * @return the instance stored with the instanceId if the manager contains the value, otherwise - * null + * @param instance the instance whose presence in this manager is to be tested. + * @return whether this manager contains the given `instance`. */ - public Object getInstance(long instanceId) { - return instanceIdsToInstances.get(instanceId); + public boolean containsInstance(Object instance) { + assertManagerIsNotClosed(); + return identifiers.containsKey(instance); } /** - * Retrieve the instanceId paired with an instance. + * Closes the manager and releases resources. * - * @param instance the value paired with the desired instanceId - * @return the instanceId paired with instance if the manager contains the value, otherwise null + *

Calling a method after calling this one will throw an {@link AssertionError}. This method + * excluded. */ - public Long getInstanceId(Object instance) { - return instancesToInstanceIds.get(instance); + public void close() { + handler.removeCallbacks(this::releaseAllFinalizedInstances); + isClosed = true; + } + + private void releaseAllFinalizedInstances() { + WeakReference reference; + while ((reference = (WeakReference) referenceQueue.poll()) != null) { + final Long identifier = weakReferencesToIdentifiers.remove(reference); + if (identifier != null) { + weakInstances.remove(identifier); + strongInstances.remove(identifier); + finalizationListener.onFinalize(identifier); + } + } + handler.postDelayed( + this::releaseAllFinalizedInstances, CLEAR_FINALIZED_WEAK_REFERENCES_INTERVAL); + } + + private void addInstance(Object instance, long identifier) { + if (identifier < 0) { + throw new IllegalArgumentException("Identifier must be >= 0."); + } + final WeakReference weakReference = new WeakReference<>(instance, referenceQueue); + identifiers.put(instance, identifier); + weakInstances.put(identifier, weakReference); + weakReferencesToIdentifiers.put(weakReference, identifier); + strongInstances.put(identifier, instance); + } + + private void assertManagerIsNotClosed() { + if (isClosed) { + throw new AssertionError("Manager has already been closed."); + } } } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannelFlutterApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannelFlutterApiImpl.java index 120f66dbdf9a..dbac83382a29 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannelFlutterApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannelFlutterApiImpl.java @@ -30,7 +30,7 @@ public JavaScriptChannelFlutterApiImpl( /** Passes arguments from {@link JavaScriptChannel#postMessage} to Dart. */ public void postMessage( JavaScriptChannel javaScriptChannel, String messageArg, Reply callback) { - super.postMessage(instanceManager.getInstanceId(javaScriptChannel), messageArg, callback); + super.postMessage(getIdentifierForJavaScriptChannel(javaScriptChannel), messageArg, callback); } /** @@ -40,11 +40,18 @@ public void postMessage( * @param callback Reply callback with return value from Dart. */ public void dispose(JavaScriptChannel javaScriptChannel, Reply callback) { - final Long instanceId = instanceManager.removeInstance(javaScriptChannel); - if (instanceId != null) { - dispose(instanceId, callback); + if (instanceManager.containsInstance(javaScriptChannel)) { + dispose(getIdentifierForJavaScriptChannel(javaScriptChannel), callback); } else { callback.reply(null); } } + + private long getIdentifierForJavaScriptChannel(JavaScriptChannel javaScriptChannel) { + final Long identifier = instanceManager.getIdentifierForStrongReference(javaScriptChannel); + if (identifier == null) { + throw new IllegalStateException("Could not find identifier for JavaScriptChannel."); + } + return identifier; + } } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannelHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannelHostApiImpl.java index 3055c9fc7f40..44e3b8aa5a2a 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannelHostApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannelHostApiImpl.java @@ -70,6 +70,6 @@ public void create(Long instanceId, String channelName) { final JavaScriptChannel javaScriptChannel = javaScriptChannelCreator.createJavaScriptChannel( flutterApi, channelName, platformThreadHandler); - instanceManager.addInstance(javaScriptChannel, instanceId); + instanceManager.addDartCreatedInstance(javaScriptChannel, instanceId); } } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientFlutterApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientFlutterApiImpl.java index 2ab9275b41c3..28d63ec82dec 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientFlutterApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientFlutterApiImpl.java @@ -32,11 +32,12 @@ public WebChromeClientFlutterApiImpl( /** Passes arguments from {@link WebChromeClient#onProgressChanged} to Dart. */ public void onProgressChanged( WebChromeClient webChromeClient, WebView webView, Long progress, Reply callback) { + final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView); + if (webViewIdentifier == null) { + throw new IllegalStateException("Could not find identifier for WebView."); + } super.onProgressChanged( - instanceManager.getInstanceId(webChromeClient), - instanceManager.getInstanceId(webView), - progress, - callback); + getIdentifierForClient(webChromeClient), webViewIdentifier, progress, callback); } /** @@ -46,11 +47,18 @@ public void onProgressChanged( * @param callback reply callback with return value from Dart */ public void dispose(WebChromeClient webChromeClient, Reply callback) { - final Long instanceId = instanceManager.removeInstance(webChromeClient); - if (instanceId != null) { - dispose(instanceId, callback); + if (instanceManager.containsInstance(webChromeClient)) { + dispose(getIdentifierForClient(webChromeClient), callback); } else { callback.reply(null); } } + + private long getIdentifierForClient(WebChromeClient webChromeClient) { + final Long identifier = instanceManager.getIdentifierForStrongReference(webChromeClient); + if (identifier == null) { + throw new IllegalStateException("Could not find identifier for WebChromeClient."); + } + return identifier; + } } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientHostApiImpl.java index d2e1e59ce179..0f50c82fa7bb 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientHostApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientHostApiImpl.java @@ -163,6 +163,6 @@ public void create(Long instanceId, Long webViewClientInstanceId) { (WebViewClient) instanceManager.getInstance(webViewClientInstanceId); final WebChromeClient webChromeClient = webChromeClientCreator.createWebChromeClient(flutterApi, webViewClient); - instanceManager.addInstance(webChromeClient, instanceId); + instanceManager.addDartCreatedInstance(webChromeClient, instanceId); } } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java index b168e206214f..5b6f9e78a8d5 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java @@ -45,12 +45,13 @@ public WebSettingsHostApiImpl( @Override public void create(Long instanceId, Long webViewInstanceId) { final WebView webView = (WebView) instanceManager.getInstance(webViewInstanceId); - instanceManager.addInstance(webSettingsCreator.createWebSettings(webView), instanceId); + instanceManager.addDartCreatedInstance( + webSettingsCreator.createWebSettings(webView), instanceId); } @Override public void dispose(Long instanceId) { - instanceManager.removeInstanceWithId(instanceId); + instanceManager.remove(instanceId); } @Override diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImpl.java index 42e7603c0279..c06f2bc5796c 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImpl.java @@ -42,7 +42,7 @@ public WebStorageHostApiImpl( @Override public void create(Long instanceId) { - instanceManager.addInstance(webStorageCreator.createWebStorage(), instanceId); + instanceManager.addDartCreatedInstance(webStorageCreator.createWebStorage(), instanceId); } @Override 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 b4885688f7ac..c23e8e79ae37 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 @@ -77,21 +77,21 @@ public WebViewClientFlutterApiImpl( /** Passes arguments from {@link WebViewClient#onPageStarted} to Dart. */ public void onPageStarted( WebViewClient webViewClient, WebView webView, String urlArg, Reply callback) { - onPageStarted( - instanceManager.getInstanceId(webViewClient), - instanceManager.getInstanceId(webView), - urlArg, - callback); + final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView); + if (webViewIdentifier == null) { + throw new IllegalStateException("Could not find identifier for WebView."); + } + onPageStarted(getIdentifierForClient(webViewClient), webViewIdentifier, urlArg, callback); } /** Passes arguments from {@link WebViewClient#onPageFinished} to Dart. */ public void onPageFinished( WebViewClient webViewClient, WebView webView, String urlArg, Reply callback) { - onPageFinished( - instanceManager.getInstanceId(webViewClient), - instanceManager.getInstanceId(webView), - urlArg, - callback); + final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView); + if (webViewIdentifier == null) { + throw new IllegalStateException("Could not find identifier for WebView."); + } + onPageFinished(getIdentifierForClient(webViewClient), webViewIdentifier, urlArg, callback); } /** @@ -105,9 +105,13 @@ public void onReceivedRequestError( WebResourceRequest request, WebResourceError error, Reply callback) { + final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView); + if (webViewIdentifier == null) { + throw new IllegalStateException("Could not find identifier for WebView."); + } onReceivedRequestError( - instanceManager.getInstanceId(webViewClient), - instanceManager.getInstanceId(webView), + getIdentifierForClient(webViewClient), + webViewIdentifier, createWebResourceRequestData(request), createWebResourceErrorData(error), callback); @@ -124,9 +128,13 @@ public void onReceivedRequestError( WebResourceRequest request, WebResourceErrorCompat error, Reply callback) { + final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView); + if (webViewIdentifier == null) { + throw new IllegalStateException("Could not find identifier for WebView."); + } onReceivedRequestError( - instanceManager.getInstanceId(webViewClient), - instanceManager.getInstanceId(webView), + getIdentifierForClient(webViewClient), + webViewIdentifier, createWebResourceRequestData(request), createWebResourceErrorData(error), callback); @@ -143,9 +151,13 @@ public void onReceivedError( String descriptionArg, String failingUrlArg, Reply callback) { + final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView); + if (webViewIdentifier == null) { + throw new IllegalStateException("Could not find identifier for WebView."); + } onReceivedError( - instanceManager.getInstanceId(webViewClient), - instanceManager.getInstanceId(webView), + getIdentifierForClient(webViewClient), + webViewIdentifier, errorCodeArg, descriptionArg, failingUrlArg, @@ -162,9 +174,13 @@ public void requestLoading( WebView webView, WebResourceRequest request, Reply callback) { + final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView); + if (webViewIdentifier == null) { + throw new IllegalStateException("Could not find identifier for WebView."); + } requestLoading( - instanceManager.getInstanceId(webViewClient), - instanceManager.getInstanceId(webView), + getIdentifierForClient(webViewClient), + webViewIdentifier, createWebResourceRequestData(request), callback); } @@ -174,11 +190,11 @@ public void requestLoading( */ public void urlLoading( WebViewClient webViewClient, WebView webView, String urlArg, Reply callback) { - urlLoading( - instanceManager.getInstanceId(webViewClient), - instanceManager.getInstanceId(webView), - urlArg, - callback); + final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView); + if (webViewIdentifier == null) { + throw new IllegalStateException("Could not find identifier for WebView."); + } + urlLoading(getIdentifierForClient(webViewClient), webViewIdentifier, urlArg, callback); } /** @@ -188,11 +204,18 @@ public void urlLoading( * @param callback reply callback with return value from Dart */ public void dispose(WebViewClient webViewClient, Reply callback) { - final Long instanceId = instanceManager.removeInstance(webViewClient); - if (instanceId != null) { - dispose(instanceId, callback); + if (instanceManager.containsInstance(webViewClient)) { + dispose(getIdentifierForClient(webViewClient), callback); } else { callback.reply(null); } } + + private long getIdentifierForClient(WebViewClient webViewClient) { + final Long identifier = instanceManager.getIdentifierForStrongReference(webViewClient); + if (identifier == null) { + throw new IllegalStateException("Could not find identifier for WebViewClient."); + } + return identifier; + } } 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 6b659fae9c0f..4833ee917d34 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 @@ -244,6 +244,6 @@ public WebViewClientHostApiImpl( public void create(Long instanceId, Boolean shouldOverrideUrlLoading) { final WebViewClient webViewClient = webViewClientCreator.createWebViewClient(flutterApi, shouldOverrideUrlLoading); - instanceManager.addInstance(webViewClient, instanceId); + instanceManager.addDartCreatedInstance(webViewClient, instanceId); } } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java index 67202ebef16d..8db976a26937 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java @@ -31,6 +31,8 @@ *

Call {@link #registerWith} to use the stable {@code io.flutter.plugin.common} package instead. */ public class WebViewFlutterPlugin implements FlutterPlugin, ActivityAware { + private InstanceManager instanceManager; + private FlutterPluginBinding pluginBinding; private WebViewHostApiImpl webViewHostApi; private JavaScriptChannelHostApiImpl javaScriptChannelHostApi; @@ -75,7 +77,7 @@ private void setUp( View containerView, FlutterAssetManager flutterAssetManager) { - InstanceManager instanceManager = new InstanceManager(); + instanceManager = InstanceManager.open(identifier -> {}); viewRegistry.registerViewFactory( "plugins.flutter.io/webview", new FlutterWebViewFactory(instanceManager)); @@ -135,7 +137,9 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { } @Override - public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {} + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + instanceManager.close(); + } @Override public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) { diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java index afc3efee80ff..56761d100133 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java @@ -335,7 +335,7 @@ public void create(Long instanceId, Boolean useHybridComposition) { : webViewProxy.createInputAwareWebView(context, containerView); displayListenerProxy.onPostWebViewInitialization(displayManager); - instanceManager.addInstance(webView, instanceId); + instanceManager.addDartCreatedInstance(webView, instanceId); } @Override @@ -343,7 +343,7 @@ public void dispose(Long instanceId) { final WebView instance = (WebView) instanceManager.getInstance(instanceId); if (instance != null) { ((Releasable) instance).release(); - instanceManager.removeInstance(instance); + instanceManager.remove(instanceId); } } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/DownloadListenerTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/DownloadListenerTest.java index 239119375e83..da25dace4517 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/DownloadListenerTest.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/DownloadListenerTest.java @@ -13,6 +13,7 @@ import android.webkit.DownloadListener; import io.flutter.plugins.webviewflutter.DownloadListenerHostApiImpl.DownloadListenerCreator; import io.flutter.plugins.webviewflutter.DownloadListenerHostApiImpl.DownloadListenerImpl; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -31,7 +32,7 @@ public class DownloadListenerTest { @Before public void setUp() { - instanceManager = new InstanceManager(); + instanceManager = InstanceManager.open(identifier -> {}); final DownloadListenerCreator downloadListenerCreator = new DownloadListenerCreator() { @@ -48,6 +49,11 @@ public DownloadListenerImpl createDownloadListener( hostApiImpl.create(0L); } + @After + public void tearDown() { + instanceManager.close(); + } + @Test public void postMessage() { downloadListener.onDownloadStart( diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/InstanceManagerTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/InstanceManagerTest.java new file mode 100644 index 000000000000..4731e2a4beb1 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/InstanceManagerTest.java @@ -0,0 +1,62 @@ +// 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.webviewflutter; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class InstanceManagerTest { + @Test + public void addDartCreatedInstance() { + final InstanceManager instanceManager = InstanceManager.open(identifier -> {}); + + final Object object = new Object(); + instanceManager.addDartCreatedInstance(object, 0); + + assertEquals(object, instanceManager.getInstance(0)); + assertEquals((Long) 0L, instanceManager.getIdentifierForStrongReference(object)); + assertTrue(instanceManager.containsInstance(object)); + + instanceManager.close(); + } + + @Test + public void addHostCreatedInstance() { + final InstanceManager instanceManager = InstanceManager.open(identifier -> {}); + + final Object object = new Object(); + long identifier = instanceManager.addHostCreatedInstance(object); + + assertNotNull(instanceManager.getInstance(identifier)); + assertEquals(object, instanceManager.getInstance(identifier)); + assertTrue(instanceManager.containsInstance(object)); + + instanceManager.close(); + } + + @Test + public void remove() { + final InstanceManager instanceManager = InstanceManager.open(identifier -> {}); + + Object object = new Object(); + instanceManager.addDartCreatedInstance(object, 0); + + assertEquals(object, instanceManager.remove(0)); + + // To allow for object to be garbage collected. + //noinspection UnusedAssignment + object = null; + + Runtime.getRuntime().gc(); + + assertNull(instanceManager.getInstance(0)); + + instanceManager.close(); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaScriptChannelTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaScriptChannelTest.java index 3de81da81bec..4bde211c6a4d 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaScriptChannelTest.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaScriptChannelTest.java @@ -12,6 +12,7 @@ import android.os.Handler; import io.flutter.plugins.webviewflutter.JavaScriptChannelHostApiImpl.JavaScriptChannelCreator; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -30,7 +31,7 @@ public class JavaScriptChannelTest { @Before public void setUp() { - instanceManager = new InstanceManager(); + instanceManager = InstanceManager.open(identifier -> {}); final JavaScriptChannelCreator javaScriptChannelCreator = new JavaScriptChannelCreator() { @@ -52,6 +53,11 @@ public JavaScriptChannel createJavaScriptChannel( hostApiImpl.create(0L, "aChannelName"); } + @After + public void tearDown() { + instanceManager.close(); + } + @Test public void postMessage() { javaScriptChannel.postMessage("A message post."); diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebChromeClientTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebChromeClientTest.java index 63cd31043799..03d48d17df91 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebChromeClientTest.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebChromeClientTest.java @@ -23,6 +23,7 @@ import android.webkit.WebViewClient; import io.flutter.plugins.webviewflutter.WebChromeClientHostApiImpl.WebChromeClientCreator; import io.flutter.plugins.webviewflutter.WebChromeClientHostApiImpl.WebChromeClientImpl; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -46,9 +47,10 @@ public class WebChromeClientTest { @Before public void setUp() { - instanceManager = new InstanceManager(); - instanceManager.addInstance(mockWebView, 0L); - instanceManager.addInstance(mockWebViewClient, 1L); + instanceManager = InstanceManager.open(identifier -> {}); + + instanceManager.addDartCreatedInstance(mockWebView, 0L); + instanceManager.addDartCreatedInstance(mockWebViewClient, 1L); final WebChromeClientCreator webChromeClientCreator = new WebChromeClientCreator() { @@ -65,6 +67,11 @@ public WebChromeClientImpl createWebChromeClient( hostApiImpl.create(2L, 1L); } + @After + public void tearDown() { + instanceManager.close(); + } + @Test public void onProgressChanged() { webChromeClient.onProgressChanged(mockWebView, 23); diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebSettingsTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebSettingsTest.java index 8ef32ddcb4ca..3217316ff563 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebSettingsTest.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebSettingsTest.java @@ -10,6 +10,7 @@ import android.webkit.WebSettings; import io.flutter.plugins.webviewflutter.WebSettingsHostApiImpl.WebSettingsCreator; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -29,12 +30,18 @@ public class WebSettingsTest { @Before public void setUp() { - testInstanceManager = new InstanceManager(); + testInstanceManager = InstanceManager.open(identifier -> {}); + when(mockWebSettingsCreator.createWebSettings(any())).thenReturn(mockWebSettings); testHostApiImpl = new WebSettingsHostApiImpl(testInstanceManager, mockWebSettingsCreator); testHostApiImpl.create(0L, 0L); } + @After + public void tearDown() { + testInstanceManager.close(); + } + @Test public void setDomStorageEnabled() { testHostApiImpl.setDomStorageEnabled(0L, true); diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImplTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImplTest.java index e2845c2842a9..b4f38f1702de 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImplTest.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebStorageHostApiImplTest.java @@ -8,6 +8,7 @@ import static org.mockito.Mockito.when; import android.webkit.WebStorage; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -27,12 +28,18 @@ public class WebStorageHostApiImplTest { @Before public void setUp() { - testInstanceManager = new InstanceManager(); + testInstanceManager = InstanceManager.open(identifier -> {}); + when(mockWebStorageCreator.createWebStorage()).thenReturn(mockWebStorage); testHostApiImpl = new WebStorageHostApiImpl(testInstanceManager, mockWebStorageCreator); testHostApiImpl.create(0L); } + @After + public void tearDown() { + testInstanceManager.close(); + } + @Test public void deleteAllData() { testHostApiImpl.deleteAllData(0L); 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 c2abd25c5a66..5d0cb701010e 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 @@ -20,6 +20,7 @@ import io.flutter.plugins.webviewflutter.WebViewClientHostApiImpl.WebViewClientCompatImpl; import io.flutter.plugins.webviewflutter.WebViewClientHostApiImpl.WebViewClientCreator; import java.util.HashMap; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -40,8 +41,9 @@ public class WebViewClientTest { @Before public void setUp() { - instanceManager = new InstanceManager(); - instanceManager.addInstance(mockWebView, 0L); + instanceManager = InstanceManager.open(identifier -> {}); + + instanceManager.addDartCreatedInstance(mockWebView, 0L); final WebViewClientCreator webViewClientCreator = new WebViewClientCreator() { @@ -60,6 +62,11 @@ public WebViewClient createWebViewClient( hostApiImpl.create(1L, true); } + @After + public void tearDown() { + instanceManager.close(); + } + @Test public void onPageStarted() { webViewClient.onPageStarted(mockWebView, "https://www.google.com", null); diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java index 5be39ab963a3..89bbd7c6c7c0 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java @@ -21,6 +21,7 @@ import io.flutter.plugins.webviewflutter.WebViewHostApiImpl.InputAwareWebViewPlatformView; import io.flutter.plugins.webviewflutter.WebViewHostApiImpl.WebViewPlatformView; import java.util.HashMap; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -43,13 +44,19 @@ public class WebViewTest { @Before public void setUp() { - testInstanceManager = new InstanceManager(); + testInstanceManager = InstanceManager.open(identifier -> {}); + when(mockWebViewProxy.createWebView(mockContext)).thenReturn(mockWebView); testHostApiImpl = new WebViewHostApiImpl(testInstanceManager, mockWebViewProxy, mockContext, null); testHostApiImpl.create(0L, true); } + @After + public void tearDown() { + testInstanceManager.close(); + } + @Test public void releaseWebView() { final WebViewPlatformView webView = new WebViewPlatformView(mockContext); @@ -308,7 +315,7 @@ public void getScrollY() { @Test public void setWebViewClient() { final WebViewClient mockWebViewClient = mock(WebViewClient.class); - testInstanceManager.addInstance(mockWebViewClient, 1L); + testInstanceManager.addDartCreatedInstance(mockWebViewClient, 1L); testHostApiImpl.setWebViewClient(0L, 1L); verify(mockWebView).setWebViewClient(mockWebViewClient); @@ -318,7 +325,7 @@ public void setWebViewClient() { public void addJavaScriptChannel() { final JavaScriptChannel javaScriptChannel = new JavaScriptChannel(mock(JavaScriptChannelFlutterApiImpl.class), "aName", null); - testInstanceManager.addInstance(javaScriptChannel, 1L); + testInstanceManager.addDartCreatedInstance(javaScriptChannel, 1L); testHostApiImpl.addJavaScriptChannel(0L, 1L); verify(mockWebView).addJavascriptInterface(javaScriptChannel, "aName"); @@ -328,7 +335,7 @@ public void addJavaScriptChannel() { public void removeJavaScriptChannel() { final JavaScriptChannel javaScriptChannel = new JavaScriptChannel(mock(JavaScriptChannelFlutterApiImpl.class), "aName", null); - testInstanceManager.addInstance(javaScriptChannel, 1L); + testInstanceManager.addDartCreatedInstance(javaScriptChannel, 1L); testHostApiImpl.removeJavaScriptChannel(0L, 1L); verify(mockWebView).removeJavascriptInterface("aName"); @@ -337,7 +344,7 @@ public void removeJavaScriptChannel() { @Test public void setDownloadListener() { final DownloadListener mockDownloadListener = mock(DownloadListener.class); - testInstanceManager.addInstance(mockDownloadListener, 1L); + testInstanceManager.addDartCreatedInstance(mockDownloadListener, 1L); testHostApiImpl.setDownloadListener(0L, 1L); verify(mockWebView).setDownloadListener(mockDownloadListener); @@ -346,7 +353,7 @@ public void setDownloadListener() { @Test public void setWebChromeClient() { final WebChromeClient mockWebChromeClient = mock(WebChromeClient.class); - testInstanceManager.addInstance(mockWebChromeClient, 1L); + testInstanceManager.addDartCreatedInstance(mockWebChromeClient, 1L); testHostApiImpl.setWebChromeClient(0L, 1L); verify(mockWebView).setWebChromeClient(mockWebChromeClient); diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index e530b1e09cd4..f76534196caf 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/plugins/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: 2.9.1 +version: 2.9.2 environment: sdk: ">=2.14.0 <3.0.0" From 62bb80aa2765ef954256db78f072e808b244a671 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Jul 2022 00:19:04 +0000 Subject: [PATCH 530/844] [video_player]: Bump mockito-inline from 3.9.0 to 4.6.1 in /packages/video_player/video_player_android/android (#5900) --- packages/video_player/video_player_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player_android/android/build.gradle b/packages/video_player/video_player_android/android/build.gradle index e8032c583802..8d3994238ba8 100644 --- a/packages/video_player/video_player_android/android/build.gradle +++ b/packages/video_player/video_player_android/android/build.gradle @@ -49,7 +49,7 @@ android { implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.18.0' testImplementation 'junit:junit:4.13.2' testImplementation 'androidx.test:core:1.3.0' - testImplementation 'org.mockito:mockito-inline:3.9.0' + testImplementation 'org.mockito:mockito-inline:4.6.1' testImplementation 'org.robolectric:robolectric:4.5' } From 4eec508436125953e7b91b4783d4ce464f701dbd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Jul 2022 00:20:05 +0000 Subject: [PATCH 531/844] [video_player]: Bump junit from 4.13 to 4.13.2 in /packages/video_player/video_player/example/android/app (#6026) --- .../video_player/video_player/example/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player/example/android/app/build.gradle b/packages/video_player/video_player/example/android/app/build.gradle index 18e77a5e555c..65605a19ac81 100644 --- a/packages/video_player/video_player/example/android/app/build.gradle +++ b/packages/video_player/video_player/example/android/app/build.gradle @@ -58,7 +58,7 @@ flutter { } dependencies { - testImplementation 'junit:junit:4.13' + testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.8.1' testImplementation 'org.mockito:mockito-core:3.5.13' androidTestImplementation 'androidx.test:runner:1.1.1' From cf8a3f3e1edb70186dd2cd5611ea9a6d744c6680 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Jul 2022 00:49:05 +0000 Subject: [PATCH 532/844] [video_player]: Bump robolectric from 4.5 to 4.8.1 in /packages/video_player/video_player_android/android (#5876) --- packages/video_player/video_player_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player_android/android/build.gradle b/packages/video_player/video_player_android/android/build.gradle index 8d3994238ba8..1165feaf5048 100644 --- a/packages/video_player/video_player_android/android/build.gradle +++ b/packages/video_player/video_player_android/android/build.gradle @@ -50,7 +50,7 @@ android { testImplementation 'junit:junit:4.13.2' testImplementation 'androidx.test:core:1.3.0' testImplementation 'org.mockito:mockito-inline:4.6.1' - testImplementation 'org.robolectric:robolectric:4.5' + testImplementation 'org.robolectric:robolectric:4.8.1' } From 947789dfc611483f5e7d7a2ae5a990d226ab0a08 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Thu, 21 Jul 2022 22:27:05 -0700 Subject: [PATCH 533/844] [video_player_web] Document video scrubbing and range requests. (#6128) --- .../video_player_web/CHANGELOG.md | 7 ++ .../video_player/video_player_web/README.md | 67 +++++++++++++++---- .../video_player_web/pubspec.yaml | 2 +- 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 603f2967a605..3ea526e4d25e 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,3 +1,10 @@ +## 2.0.12 + +* Updates the `README` with: + * Information about a common known issue: "Some videos restart when using the + seek bar/progress bar/scrubber" (Issue [#49630](https://github.com/flutter/flutter/issues/49360)) + * Links to the Autoplay information of all major browsers (Chrome/Edge, Firefox, Safari). + ## 2.0.11 * Improves handling of videos with `Infinity` duration. diff --git a/packages/video_player/video_player_web/README.md b/packages/video_player/video_player_web/README.md index 85e55ebcbe80..ce5d4720ac8e 100644 --- a/packages/video_player/video_player_web/README.md +++ b/packages/video_player/video_player_web/README.md @@ -5,20 +5,56 @@ The web implementation of [`video_player`][1]. ## Usage This package is [endorsed](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin), -which means you can simply use `video_player` -normally. This package will be automatically included in your app when you do. +which means you can simply use `video_player` normally. This package will be +automatically included in your app when you do. -## dart:io +## Limitations on the Web platform -The Web platform does **not** suppport `dart:io`, so attempts to create a `VideoPlayerController.file` will throw an `UnimplementedError`. +Video playback on the Web platform has some limitations that might surprise developers +more familiar with mobile/desktop targets. -## Autoplay -Playing videos without prior interaction with the site might be prohibited -by the browser and lead to runtime errors. See also: https://goo.gl/xX8pDD. +In no particular order: -## Mixing audio with other audio sources +### dart:io -The `VideoPlayerOptions.mixWithOthers` option can't be implemented in web, at least at the moment. If you use this option it will be silently ignored. +The web platform does **not** suppport `dart:io`, so attempts to create a `VideoPlayerController.file` +will throw an `UnimplementedError`. + +### Autoplay + +Attempts to start playing videos with an audio track (or not muted) without user +interaction with the site ("user activation") will be prohibited by the browser +and cause runtime errors in JS. + +See also: + +* [Autoplay policy in Chrome](https://developer.chrome.com/blog/autoplay/) +* MDN > [Autoplay guide for media and Web Audio APIs](https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide) +* Delivering Video Content for Safari > [Enable Video Autoplay](https://developer.apple.com/documentation/webkit/delivering_video_content_for_safari#3030251) +* More info about "user activation", in general: + * [Making user activation consistent across APIs](https://developer.chrome.com/blog/user-activation) + * HTML Spec: [Tracking user activation](https://html.spec.whatwg.org/multipage/interaction.html#sticky-activation) + +### Some videos restart when using the seek bar/progress bar/scrubber + +Certain videos will rewind to the beginning when users attempt to `seekTo` (change +the progress/scrub to) another position, instead of jumping to the desired position. +Once the video is fully stored in the browser cache, seeking will work fine after +a full page reload. + +The most common explanation for this issue is that the server where the video is +stored doesn't support [HTTP range requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests). + +> **NOTE:** Flutter web's local server (the one that powers `flutter run`) **DOES NOT** support +> range requests, so all video **assets** in `debug` mode will exhibit this behavior. + +See [Issue #49360](https://github.com/flutter/flutter/issues/49360) for more information +on how to diagnose if a server supports range requests or not. + +### Mixing audio with other audio sources + +The `VideoPlayerOptions.mixWithOthers` option can't be implemented in web, at least +at the moment. If you use this option it will be silently ignored. ## Supported Formats @@ -26,28 +62,35 @@ The `VideoPlayerOptions.mixWithOthers` option can't be implemented in web, at le ### Video codecs? -Check MDN's [**Web video codec guide**](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_codecs) to learn more about the pros and cons of each video codec. +Check MDN's [**Web video codec guide**](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_codecs) +to learn more about the pros and cons of each video codec. ### What codecs are supported? -Visit [**caniuse.com: 'video format'**](https://caniuse.com/#search=video%20format) for a breakdown of which browsers support what codecs. You can customize charts there for the users of your particular website(s). +Visit [**caniuse.com: 'video format'**](https://caniuse.com/#search=video%20format) +for a breakdown of which browsers support what codecs. You can customize charts +there for the users of your particular website(s). Here's an abridged version of the data from caniuse, for a Global audience: #### MPEG-4/H.264 + [![Data on Global support for the MPEG-4/H.264 video format](https://caniuse.bitsofco.de/image/mpeg4.png)](https://caniuse.com/#feat=mpeg4) #### WebM + [![Data on Global support for the WebM video format](https://caniuse.bitsofco.de/image/webm.png)](https://caniuse.com/#feat=webm) #### Ogg/Theora + [![Data on Global support for the Ogg/Theora video format](https://caniuse.bitsofco.de/image/ogv.png)](https://caniuse.com/#feat=ogv) #### AV1 + [![Data on Global support for the AV1 video format](https://caniuse.bitsofco.de/image/av1.png)](https://caniuse.com/#feat=av1) #### HEVC/H.265 -[![Data on Global support for the HEVC/H.265 video format](https://caniuse.bitsofco.de/image/hevc.png)](https://caniuse.com/#feat=hevc) +[![Data on Global support for the HEVC/H.265 video format](https://caniuse.bitsofco.de/image/hevc.png)](https://caniuse.com/#feat=hevc) [1]: ../video_player diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 408334114c0c..e3cbbabd482b 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/plugins/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.0.11 +version: 2.0.12 environment: sdk: ">=2.12.0 <3.0.0" From 89671392d52c602f6d08b2e4912baf1b069a6561 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Jul 2022 06:28:07 +0000 Subject: [PATCH 534/844] [google_maps]: Bump mockito-core from 3.2.4 to 4.6.1 in /packages/google_maps_flutter/google_maps_flutter/android (#5904) --- .../google_maps_flutter/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/google_maps_flutter/google_maps_flutter/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter/android/build.gradle index 24502e5193c8..2ed0674ebfe0 100644 --- a/packages/google_maps_flutter/google_maps_flutter/android/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter/android/build.gradle @@ -40,7 +40,7 @@ android { androidTestImplementation 'androidx.test:rules:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-core:3.2.4' + testImplementation 'org.mockito:mockito-core:4.6.1' testImplementation 'androidx.test:core:1.2.0' testImplementation "org.robolectric:robolectric:4.3.1" } From 5c4e0091144cf506bfdb54108a1ee181e0fc56a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Jul 2022 07:09:06 +0000 Subject: [PATCH 535/844] [in_app_pur]: Bump mockito-core from 3.6.0 to 4.6.1 in /packages/in_app_purchase/in_app_purchase_android/android (#5903) --- .../in_app_purchase_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle index 663a4dfad46e..0d9a03121d8a 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle +++ b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle @@ -56,7 +56,7 @@ dependencies { implementation 'com.android.billingclient:billing:5.0.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.json:json:20220320' - testImplementation 'org.mockito:mockito-core:4.5.1' + testImplementation 'org.mockito:mockito-core:4.6.1' androidTestImplementation 'androidx.test:runner:1.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' } From 5a2f9283fc86a8e53505f78333be961188f88dbf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Jul 2022 12:53:06 +0000 Subject: [PATCH 536/844] [video_player]: Bump mockito-core from 3.5.13 to 4.6.1 in /packages/video_player/video_player/example/android/app (#6022) --- .../video_player/video_player/example/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player/example/android/app/build.gradle b/packages/video_player/video_player/example/android/app/build.gradle index 65605a19ac81..a2658065f17d 100644 --- a/packages/video_player/video_player/example/android/app/build.gradle +++ b/packages/video_player/video_player/example/android/app/build.gradle @@ -60,7 +60,7 @@ flutter { dependencies { testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.8.1' - testImplementation 'org.mockito:mockito-core:3.5.13' + testImplementation 'org.mockito:mockito-core:4.6.1' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } From 026383a8af56455e48d97fc998375aac14f6cb59 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Jul 2022 13:41:08 +0000 Subject: [PATCH 537/844] [camera]: Bump junit from 4.12 to 4.13.2 in /packages/camera/camera_android/example/android/app (#6016) --- packages/camera/camera_android/example/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android/example/android/app/build.gradle b/packages/camera/camera_android/example/android/app/build.gradle index 476d65373723..5d6af5887012 100644 --- a/packages/camera/camera_android/example/android/app/build.gradle +++ b/packages/camera/camera_android/example/android/app/build.gradle @@ -57,7 +57,7 @@ flutter { } dependencies { - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:rules:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' From b1860e62f2ff9bc1ef15a2e3ace1cd2a13a87a7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Jul 2022 13:42:07 +0000 Subject: [PATCH 538/844] [quick_actions]: Bump mockito-core from 4.3.1 to 4.6.1 in /packages/quick_actions/quick_actions_android/example/android/app (#6113) --- .../quick_actions_android/example/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/quick_actions/quick_actions_android/example/android/app/build.gradle b/packages/quick_actions/quick_actions_android/example/android/app/build.gradle index 78d1c7394ba9..7cf87cdda485 100644 --- a/packages/quick_actions/quick_actions_android/example/android/app/build.gradle +++ b/packages/quick_actions/quick_actions_android/example/android/app/build.gradle @@ -62,6 +62,6 @@ dependencies { androidTestImplementation "androidx.test:runner:$androidXTestVersion" androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' androidTestImplementation 'androidx.test.ext:junit:1.0.0' - androidTestImplementation 'org.mockito:mockito-core:4.3.1' + androidTestImplementation 'org.mockito:mockito-core:4.6.1' androidTestImplementation 'org.mockito:mockito-android:4.6.1' } From d4c6920c523195b1a6f9d1a682bfb631ffb4f571 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 22 Jul 2022 14:18:04 -0400 Subject: [PATCH 539/844] [google_maps_flutter] Add an interface for test inspection (#6133) --- .../CHANGELOG.md | 4 + ...oogle_maps_flutter_platform_interface.dart | 1 + .../google_maps_flutter_platform.dart | 8 ++ .../google_maps_inspector_platform.dart | 117 ++++++++++++++++++ .../pubspec.yaml | 2 +- .../google_maps_inspector_platform_test.dart | 33 +++++ 6 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_inspector_platform.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_inspector_platform_test.dart 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 c1a7c95abfdb..e8da5f3aba99 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.2.1 + +* Adds a new interface for inspecting the platform map state in tests. + ## 2.2.0 * Adds new versions of `buildView` and `updateOptions` that take a new option diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/google_maps_flutter_platform_interface.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/google_maps_flutter_platform_interface.dart index b83eaf4fdfc7..6484ae6b573d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/google_maps_flutter_platform_interface.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/google_maps_flutter_platform_interface.dart @@ -6,4 +6,5 @@ export 'src/events/map_event.dart'; export 'src/method_channel/method_channel_google_maps_flutter.dart' show MethodChannelGoogleMapsFlutter; export 'src/platform_interface/google_maps_flutter_platform.dart'; +export 'src/platform_interface/google_maps_inspector_platform.dart'; export 'src/types/types.dart'; 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 b6b95018d0c4..d4621a70c249 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 @@ -443,4 +443,12 @@ abstract class GoogleMapsFlutterPlatform extends PlatformInterface { mapOptions: jsonForMapConfiguration(mapConfiguration), ); } + + /// Populates [GoogleMapsFlutterInspectorPlatform.instance] to allow + /// inspecting the platform map state. + @visibleForTesting + void enableDebugInspection() { + throw UnimplementedError( + 'enableDebugInspection() 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 new file mode 100644 index 000000000000..70e332907939 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/platform_interface/google_maps_inspector_platform.dart @@ -0,0 +1,117 @@ +// 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:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +/// The interface that platform-specific implementations of +/// `google_maps_flutter` can extend to support state inpsection in tests. +/// +/// Avoid `implements` of this interface. Using `implements` makes adding any +/// new methods here a breaking change for end users of your platform! +/// +/// Do `extends GoogleMapsInspectorPlatform` instead, so new methods +/// added here are inherited in your code with the default implementation (that +/// throws at runtime), rather than breaking your users at compile time. +abstract class GoogleMapsInspectorPlatform extends PlatformInterface { + /// Constructs a GoogleMapsFlutterPlatform. + GoogleMapsInspectorPlatform() : super(token: _token); + + static final Object _token = Object(); + + static GoogleMapsInspectorPlatform? _instance; + + /// The instance of [GoogleMapsInspectorPlatform], if any. + /// + /// This is usually populated by calling + /// [GoogleMapsFlutterPlatform.enableDebugInspection]. + static GoogleMapsInspectorPlatform? get instance => _instance; + + /// Platform-specific plugins should set this with their own platform-specific + /// class that extends [GoogleMapsInspectorPlatform] in their + /// implementation of [GoogleMapsFlutterPlatform.enableDebugInspection]. + static set instance(GoogleMapsInspectorPlatform? instance) { + if (instance != null) { + PlatformInterface.verify(instance, _token); + } + _instance = instance; + } + + /// Returns the minimum and maxmimum zoom level settings. + Future getMinMaxZoomLevels({required int mapId}) { + throw UnimplementedError('getMinMaxZoomLevels() has not been implemented.'); + } + + /// Returns true if the compass is enabled. + Future isCompassEnabled({required int mapId}) { + throw UnimplementedError('isCompassEnabled() has not been implemented.'); + } + + /// Returns true if lite mode is enabled. + Future isLiteModeEnabled({required int mapId}) { + throw UnimplementedError('isLiteModeEnabled() has not been implemented.'); + } + + /// Returns true if the map toolbar is enabled. + Future isMapToolbarEnabled({required int mapId}) { + throw UnimplementedError('isMapToolbarEnabled() has not been implemented.'); + } + + /// Returns true if the "my location" button is enabled. + Future isMyLocationButtonEnabled({required int mapId}) { + throw UnimplementedError( + 'isMyLocationButtonEnabled() has not been implemented.'); + } + + /// Returns true if the traffic overlay is enabled. + Future isTrafficEnabled({required int mapId}) { + throw UnimplementedError('isTrafficEnabled() has not been implemented.'); + } + + /// Returns true if the building layer is enabled. + Future areBuildingsEnabled({required int mapId}) { + throw UnimplementedError('areBuildingsEnabled() has not been implemented.'); + } + + /// Returns true if rotate gestures are enabled. + Future areRotateGesturesEnabled({required int mapId}) { + throw UnimplementedError( + 'areRotateGesturesEnabled() has not been implemented.'); + } + + /// Returns true if scroll gestures are enabled. + Future areScrollGesturesEnabled({required int mapId}) { + throw UnimplementedError( + 'areScrollGesturesEnabled() has not been implemented.'); + } + + /// Returns true if tilt gestures are enabled. + Future areTiltGesturesEnabled({required int mapId}) { + throw UnimplementedError( + 'areTiltGesturesEnabled() has not been implemented.'); + } + + /// Returns true if zoom controls are enabled. + Future areZoomControlsEnabled({required int mapId}) { + throw UnimplementedError( + 'areZoomControlsEnabled() has not been implemented.'); + } + + /// Returns true if zoom gestures are enabled. + Future areZoomGesturesEnabled({required int mapId}) { + throw UnimplementedError( + 'areZoomGesturesEnabled() has not been implemented.'); + } + + /// Returns information about the tile overlay with the given ID. + /// + /// The returned object will be synthesized from platform data, so will not + /// be the same Dart object as the original [TileOverlay] provided to the + /// platform interface with that ID, and not all fields (e.g., + /// [TileOverlay.tileProvider]) will be populated. + Future getTileOverlayInfo(TileOverlayId tileOverlayId, + {required int mapId}) { + throw UnimplementedError('getTileOverlayInfo() has not been implemented.'); + } +} 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 2b01e6244210..c421d13c6e64 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/plugins/tree/main/packages/google_maps_fl issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.2.0 +version: 2.2.1 environment: sdk: '>=2.12.0 <3.0.0' diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_inspector_platform_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_inspector_platform_test.dart new file mode 100644 index 000000000000..689289b6ffde --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/platform_interface/google_maps_inspector_platform_test.dart @@ -0,0 +1,33 @@ +// 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'; +import 'package:mockito/mockito.dart'; + +void main() { + // Store the initial instance before any tests change it. + final GoogleMapsInspectorPlatform? initialInstance = + GoogleMapsInspectorPlatform.instance; + + test('default instance is null', () { + expect(initialInstance, isNull); + }); + + test('cannot be implemented with `implements`', () { + expect(() { + GoogleMapsInspectorPlatform.instance = + ImplementsGoogleMapsInspectorPlatform(); + }, throwsA(isInstanceOf())); + }); + + test('can be implement with `extends`', () { + GoogleMapsInspectorPlatform.instance = ExtendsGoogleMapsInspectorPlatform(); + }); +} + +class ImplementsGoogleMapsInspectorPlatform extends Mock + implements GoogleMapsInspectorPlatform {} + +class ExtendsGoogleMapsInspectorPlatform extends GoogleMapsInspectorPlatform {} From bde6cd8f9a36a7779dfd2b1a08b20f74f9cfdf6f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Jul 2022 19:28:07 +0000 Subject: [PATCH 540/844] [local_auth]: Bump gradle from 4.1.1 to 7.2.1 in /packages/local_auth/local_auth_android/android (#5840) --- packages/local_auth/local_auth_android/CHANGELOG.md | 4 ++++ packages/local_auth/local_auth_android/android/build.gradle | 2 +- packages/local_auth/local_auth_android/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 99b337e667bc..7eed97a04fac 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.7 + +* Updates gradle version to 7.2.1. + ## 1.0.6 * Updates androidx.core version to 1.8.0. diff --git a/packages/local_auth/local_auth_android/android/build.gradle b/packages/local_auth/local_auth_android/android/build.gradle index 9b9213dfc736..eb9be4ab5f4b 100644 --- a/packages/local_auth/local_auth_android/android/build.gradle +++ b/packages/local_auth/local_auth_android/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.1.1' + classpath 'com.android.tools.build:gradle:7.2.1' } } diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index eb2638131ffb..3987d8ea4118 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.6 +version: 1.0.7 environment: sdk: ">=2.14.0 <3.0.0" From 13f0a8cae1b3149f9bd36bf076ffb8c74771661a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 22 Jul 2022 16:29:06 -0400 Subject: [PATCH 541/844] Roll Flutter from 4f9528293f54 to 096bbbe2fcbf (120 revisions) (#6134) --- .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 c3b01bfdecbf..13b462e09f03 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -4f9528293f54037993a6b2eb8f1df5604a3da954 +096bbbe2fcbf55b27e216e1b328503d0c8d18b9d From c3ee723a6d51dc9f2f38a31f280134c293fbcae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Sharma?= <737941+loic-sharma@users.noreply.github.com> Date: Fri, 22 Jul 2022 19:16:03 -0700 Subject: [PATCH 542/844] [camera_windows] Allow retrying after initialization failure (#6119) --- packages/camera/camera_windows/CHANGELOG.md | 4 + packages/camera/camera_windows/pubspec.yaml | 2 +- .../camera/camera_windows/windows/camera.cpp | 12 +- .../camera/camera_windows/windows/camera.h | 10 +- .../camera_windows/windows/camera_plugin.cpp | 8 +- .../windows/capture_controller.cpp | 14 +- .../windows/capture_controller.h | 7 +- .../windows/test/camera_plugin_test.cpp | 145 ++++++++++++------ .../windows/test/camera_test.cpp | 53 ++++++- .../windows/test/capture_controller_test.cpp | 78 +++++++++- .../camera_windows/windows/test/mocks.h | 4 +- 11 files changed, 257 insertions(+), 80 deletions(-) diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index 6883b5508318..a1e2a0733e1a 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.0+4 + +* Allows retrying camera initialization after error. + ## 0.1.0+3 * Updates the README to better explain how to use the unendorsed package. diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index 055313bbcf29..e9fe0212ad80 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_windows description: A Flutter plugin for getting information about and controlling the camera on Windows. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.1.0+3 +version: 0.1.0+4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/camera/camera_windows/windows/camera.cpp b/packages/camera/camera_windows/windows/camera.cpp index c21f8ab0af78..617f98f812ab 100644 --- a/packages/camera/camera_windows/windows/camera.cpp +++ b/packages/camera/camera_windows/windows/camera.cpp @@ -28,17 +28,17 @@ CameraImpl::~CameraImpl() { "Plugin disposed before request was handled"); } -void CameraImpl::InitCamera(flutter::TextureRegistrar* texture_registrar, +bool CameraImpl::InitCamera(flutter::TextureRegistrar* texture_registrar, flutter::BinaryMessenger* messenger, bool record_audio, ResolutionPreset resolution_preset) { auto capture_controller_factory = std::make_unique(); - InitCamera(std::move(capture_controller_factory), texture_registrar, - messenger, record_audio, resolution_preset); + return InitCamera(std::move(capture_controller_factory), texture_registrar, + messenger, record_audio, resolution_preset); } -void CameraImpl::InitCamera( +bool CameraImpl::InitCamera( std::unique_ptr capture_controller_factory, flutter::TextureRegistrar* texture_registrar, flutter::BinaryMessenger* messenger, bool record_audio, @@ -47,8 +47,8 @@ void CameraImpl::InitCamera( messenger_ = messenger; capture_controller_ = capture_controller_factory->CreateCaptureController(this); - capture_controller_->InitCaptureDevice(texture_registrar, device_id_, - record_audio, resolution_preset); + return capture_controller_->InitCaptureDevice( + texture_registrar, device_id_, record_audio, resolution_preset); } bool CameraImpl::AddPendingResult( diff --git a/packages/camera/camera_windows/windows/camera.h b/packages/camera/camera_windows/windows/camera.h index 6996231c7ab4..429f41ad6315 100644 --- a/packages/camera/camera_windows/windows/camera.h +++ b/packages/camera/camera_windows/windows/camera.h @@ -63,7 +63,9 @@ class Camera : public CaptureControllerListener { virtual camera_windows::CaptureController* GetCaptureController() = 0; // Initializes this camera and its associated capture controller. - virtual void InitCamera(flutter::TextureRegistrar* texture_registrar, + // + // Returns false if initialization fails. + virtual bool InitCamera(flutter::TextureRegistrar* texture_registrar, flutter::BinaryMessenger* messenger, bool record_audio, ResolutionPreset resolution_preset) = 0; @@ -116,7 +118,7 @@ class CameraImpl : public Camera { camera_windows::CaptureController* GetCaptureController() override { return capture_controller_.get(); } - void InitCamera(flutter::TextureRegistrar* texture_registrar, + bool InitCamera(flutter::TextureRegistrar* texture_registrar, flutter::BinaryMessenger* messenger, bool record_audio, ResolutionPreset resolution_preset) override; @@ -124,7 +126,9 @@ class CameraImpl : public Camera { // // This is a convenience method called by |InitCamera| but also used in // tests. - void InitCamera( + // + // Returns false if initialization fails. + bool InitCamera( std::unique_ptr capture_controller_factory, flutter::TextureRegistrar* texture_registrar, flutter::BinaryMessenger* messenger, bool record_audio, diff --git a/packages/camera/camera_windows/windows/camera_plugin.cpp b/packages/camera/camera_windows/windows/camera_plugin.cpp index 3b795e02047a..5503d17e702b 100644 --- a/packages/camera/camera_windows/windows/camera_plugin.cpp +++ b/packages/camera/camera_windows/windows/camera_plugin.cpp @@ -398,9 +398,11 @@ void CameraPlugin::CreateMethodHandler( resolution_preset = ResolutionPreset::kAuto; } - camera->InitCamera(texture_registrar_, messenger_, *record_audio, - resolution_preset); - cameras_.push_back(std::move(camera)); + bool initialized = camera->InitCamera(texture_registrar_, messenger_, + *record_audio, resolution_preset); + if (initialized) { + cameras_.push_back(std::move(camera)); + } } } diff --git a/packages/camera/camera_windows/windows/capture_controller.cpp b/packages/camera/camera_windows/windows/capture_controller.cpp index 084b03640bef..6c89060877ef 100644 --- a/packages/camera/camera_windows/windows/capture_controller.cpp +++ b/packages/camera/camera_windows/windows/capture_controller.cpp @@ -288,17 +288,19 @@ void CaptureControllerImpl::ResetCaptureController() { texture_handler_ = nullptr; } -void CaptureControllerImpl::InitCaptureDevice( +bool CaptureControllerImpl::InitCaptureDevice( flutter::TextureRegistrar* texture_registrar, const std::string& device_id, bool record_audio, ResolutionPreset resolution_preset) { assert(capture_controller_listener_); if (IsInitialized()) { - return capture_controller_listener_->OnCreateCaptureEngineFailed( + capture_controller_listener_->OnCreateCaptureEngineFailed( "Capture device already initialized"); + return false; } else if (capture_engine_state_ == CaptureEngineState::kInitializing) { - return capture_controller_listener_->OnCreateCaptureEngineFailed( + capture_controller_listener_->OnCreateCaptureEngineFailed( "Capture device already initializing"); + return false; } capture_engine_state_ = CaptureEngineState::kInitializing; @@ -315,7 +317,7 @@ void CaptureControllerImpl::InitCaptureDevice( capture_controller_listener_->OnCreateCaptureEngineFailed( "Failed to create camera"); ResetCaptureController(); - return; + return false; } media_foundation_started_ = true; @@ -326,8 +328,10 @@ void CaptureControllerImpl::InitCaptureDevice( capture_controller_listener_->OnCreateCaptureEngineFailed( "Failed to create camera"); ResetCaptureController(); - return; + return false; } + + return true; } void CaptureControllerImpl::TakePicture(const std::string& file_path) { diff --git a/packages/camera/camera_windows/windows/capture_controller.h b/packages/camera/camera_windows/windows/capture_controller.h index 34e378109d8f..0b7ab66cabbd 100644 --- a/packages/camera/camera_windows/windows/capture_controller.h +++ b/packages/camera/camera_windows/windows/capture_controller.h @@ -75,6 +75,9 @@ class CaptureController { // Initializes the capture controller with the specified device id. // + // Returns false if the capture controller could not be initialized + // or is already initialized. + // // texture_registrar: Pointer to Flutter TextureRegistrar instance. Used to // register texture for capture preview. // device_id: A string that holds information of camera device id to @@ -82,7 +85,7 @@ class CaptureController { // record_audio: A boolean value telling if audio should be captured on // video recording. // resolution_preset: Maximum capture resolution height. - virtual void InitCaptureDevice(TextureRegistrar* texture_registrar, + virtual bool InitCaptureDevice(TextureRegistrar* texture_registrar, const std::string& device_id, bool record_audio, ResolutionPreset resolution_preset) = 0; @@ -132,7 +135,7 @@ class CaptureControllerImpl : public CaptureController, CaptureControllerImpl& operator=(const CaptureControllerImpl&) = delete; // CaptureController - void InitCaptureDevice(TextureRegistrar* texture_registrar, + bool InitCaptureDevice(TextureRegistrar* texture_registrar, const std::string& device_id, bool record_audio, ResolutionPreset resolution_preset) override; uint32_t GetPreviewWidth() const override { return preview_frame_width_; } diff --git a/packages/camera/camera_windows/windows/test/camera_plugin_test.cpp b/packages/camera/camera_windows/windows/test/camera_plugin_test.cpp index 309268a1fb90..9cab069bbb97 100644 --- a/packages/camera/camera_windows/windows/test/camera_plugin_test.cpp +++ b/packages/camera/camera_windows/windows/test/camera_plugin_test.cpp @@ -30,6 +30,41 @@ using ::testing::Eq; using ::testing::Pointee; using ::testing::Return; +void MockInitCamera(MockCamera* camera, bool success) { + EXPECT_CALL(*camera, + HasPendingResultByType(Eq(PendingResultType::kCreateCamera))) + .Times(1) + .WillOnce(Return(false)); + + EXPECT_CALL(*camera, + AddPendingResult(Eq(PendingResultType::kCreateCamera), _)) + .Times(1) + .WillOnce([camera](PendingResultType type, + std::unique_ptr> result) { + camera->pending_result_ = std::move(result); + return true; + }); + + EXPECT_CALL(*camera, HasDeviceId(Eq(camera->device_id_))) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(*camera, InitCamera) + .Times(1) + .WillOnce([camera, success](flutter::TextureRegistrar* texture_registrar, + flutter::BinaryMessenger* messenger, + bool record_audio, + ResolutionPreset resolution_preset) { + assert(camera->pending_result_); + if (success) { + camera->pending_result_->Success(EncodableValue(1)); + return true; + } else { + camera->pending_result_->Error("camera_error", "InitCamera failed."); + return false; + } + }); +} + TEST(CameraPlugin, AvailableCamerasHandlerSuccessIfNoCameras) { std::unique_ptr texture_registrar_ = std::make_unique(); @@ -99,28 +134,7 @@ TEST(CameraPlugin, CreateHandlerCallsInitCamera) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - EXPECT_CALL(*camera, - HasPendingResultByType(Eq(PendingResultType::kCreateCamera))) - .Times(1) - .WillOnce(Return(false)); - - EXPECT_CALL(*camera, - AddPendingResult(Eq(PendingResultType::kCreateCamera), _)) - .Times(1) - .WillOnce([cam = camera.get()](PendingResultType type, - std::unique_ptr> result) { - cam->pending_result_ = std::move(result); - return true; - }); - EXPECT_CALL(*camera, InitCamera) - .Times(1) - .WillOnce([cam = camera.get()]( - flutter::TextureRegistrar* texture_registrar, - flutter::BinaryMessenger* messenger, bool record_audio, - ResolutionPreset resolution_preset) { - assert(cam->pending_result_); - return cam->pending_result_->Success(EncodableValue(1)); - }); + MockInitCamera(camera.get(), true); // Move mocked camera to the factory to be passed // for plugin with CreateCamera function. @@ -185,34 +199,7 @@ TEST(CameraPlugin, CreateHandlerErrorOnExistingDeviceId) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); - EXPECT_CALL(*camera, - HasPendingResultByType(Eq(PendingResultType::kCreateCamera))) - .Times(1) - .WillOnce(Return(false)); - - EXPECT_CALL(*camera, - AddPendingResult(Eq(PendingResultType::kCreateCamera), _)) - .Times(1) - .WillOnce([cam = camera.get()](PendingResultType type, - std::unique_ptr> result) { - cam->pending_result_ = std::move(result); - return true; - }); - EXPECT_CALL(*camera, InitCamera) - .Times(1) - .WillOnce([cam = camera.get()]( - flutter::TextureRegistrar* texture_registrar, - flutter::BinaryMessenger* messenger, bool record_audio, - ResolutionPreset resolution_preset) { - assert(cam->pending_result_); - return cam->pending_result_->Success(EncodableValue(1)); - }); - - EXPECT_CALL(*camera, HasDeviceId(Eq(MOCK_DEVICE_ID))) - .Times(1) - .WillOnce([cam = camera.get()](std::string& device_id) { - return cam->device_id_ == device_id; - }); + MockInitCamera(camera.get(), true); // Move mocked camera to the factory to be passed // for plugin with CreateCamera function. @@ -246,6 +233,64 @@ TEST(CameraPlugin, CreateHandlerErrorOnExistingDeviceId) { std::move(second_create_result)); } +TEST(CameraPlugin, CreateHandlerAllowsRetry) { + std::unique_ptr first_create_result = + std::make_unique(); + std::unique_ptr second_create_result = + std::make_unique(); + std::unique_ptr texture_registrar_ = + std::make_unique(); + std::unique_ptr messenger_ = + std::make_unique(); + std::unique_ptr camera_factory_ = + std::make_unique(); + + // The camera will fail initialization once and then succeed. + EXPECT_CALL(*camera_factory_, CreateCamera(MOCK_DEVICE_ID)) + .Times(2) + .WillOnce([](const std::string& device_id) { + std::unique_ptr first_camera = + std::make_unique(MOCK_DEVICE_ID); + + MockInitCamera(first_camera.get(), false); + + return first_camera; + }) + .WillOnce([](const std::string& device_id) { + std::unique_ptr second_camera = + std::make_unique(MOCK_DEVICE_ID); + + MockInitCamera(second_camera.get(), true); + + return second_camera; + }); + + EXPECT_CALL(*first_create_result, ErrorInternal).Times(1); + EXPECT_CALL(*first_create_result, SuccessInternal).Times(0); + + CameraPlugin plugin(texture_registrar_.get(), messenger_.get(), + std::move(camera_factory_)); + EncodableMap args = { + {EncodableValue("cameraName"), EncodableValue(MOCK_CAMERA_NAME)}, + {EncodableValue("resolutionPreset"), EncodableValue(nullptr)}, + {EncodableValue("enableAudio"), EncodableValue(true)}, + }; + + plugin.HandleMethodCall( + flutter::MethodCall("create", + std::make_unique(EncodableMap(args))), + std::move(first_create_result)); + + EXPECT_CALL(*second_create_result, ErrorInternal).Times(0); + EXPECT_CALL(*second_create_result, + SuccessInternal(Pointee(EncodableValue(1)))); + + plugin.HandleMethodCall( + flutter::MethodCall("create", + std::make_unique(EncodableMap(args))), + std::move(second_create_result)); +} + TEST(CameraPlugin, InitializeHandlerCallStartPreview) { int64_t mock_camera_id = 1234; diff --git a/packages/camera/camera_windows/windows/test/camera_test.cpp b/packages/camera/camera_windows/windows/test/camera_test.cpp index 899c1fdaea62..97e3ce113e53 100644 --- a/packages/camera/camera_windows/windows/test/camera_test.cpp +++ b/packages/camera/camera_windows/windows/test/camera_test.cpp @@ -23,6 +23,7 @@ using ::testing::_; using ::testing::Eq; using ::testing::NiceMock; using ::testing::Pointee; +using ::testing::Return; namespace test { @@ -34,17 +35,57 @@ TEST(Camera, InitCameraCreatesCaptureController) { EXPECT_CALL(*capture_controller_factory, CreateCaptureController) .Times(1) - .WillOnce( - []() { return std::make_unique>(); }); + .WillOnce([]() { + std::unique_ptr> capture_controller = + std::make_unique>(); + + EXPECT_CALL(*capture_controller, InitCaptureDevice) + .Times(1) + .WillOnce(Return(true)); + + return capture_controller; + }); EXPECT_TRUE(camera->GetCaptureController() == nullptr); // Init camera with mock capture controller factory - camera->InitCamera(std::move(capture_controller_factory), - std::make_unique().get(), - std::make_unique().get(), false, - ResolutionPreset::kAuto); + bool result = + camera->InitCamera(std::move(capture_controller_factory), + std::make_unique().get(), + std::make_unique().get(), false, + ResolutionPreset::kAuto); + EXPECT_TRUE(result); + EXPECT_TRUE(camera->GetCaptureController() != nullptr); +} +TEST(Camera, InitCameraReportsFailure) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller_factory = + std::make_unique(); + + EXPECT_CALL(*capture_controller_factory, CreateCaptureController) + .Times(1) + .WillOnce([]() { + std::unique_ptr> capture_controller = + std::make_unique>(); + + EXPECT_CALL(*capture_controller, InitCaptureDevice) + .Times(1) + .WillOnce(Return(false)); + + return capture_controller; + }); + + EXPECT_TRUE(camera->GetCaptureController() == nullptr); + + // Init camera with mock capture controller factory + bool result = + camera->InitCamera(std::move(capture_controller_factory), + std::make_unique().get(), + std::make_unique().get(), false, + ResolutionPreset::kAuto); + EXPECT_FALSE(result); EXPECT_TRUE(camera->GetCaptureController() != nullptr); } diff --git a/packages/camera/camera_windows/windows/test/capture_controller_test.cpp b/packages/camera/camera_windows/windows/test/capture_controller_test.cpp index 7520af7a4af8..083f823222b6 100644 --- a/packages/camera/camera_windows/windows/test/capture_controller_test.cpp +++ b/packages/camera/camera_windows/windows/test/capture_controller_test.cpp @@ -59,8 +59,10 @@ void MockInitCaptureController(CaptureControllerImpl* capture_controller, .Times(1); EXPECT_CALL(*engine, Initialize).Times(1); - capture_controller->InitCaptureDevice(texture_registrar, MOCK_DEVICE_ID, true, - ResolutionPreset::kAuto); + bool result = capture_controller->InitCaptureDevice( + texture_registrar, MOCK_DEVICE_ID, true, ResolutionPreset::kAuto); + + EXPECT_TRUE(result); // MockCaptureEngine::Initialize is called EXPECT_TRUE(engine->initialized_); @@ -214,6 +216,78 @@ TEST(CaptureController, engine = nullptr; } +TEST(CaptureController, InitCaptureEngineCanOnlyBeCalledOnce) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + uint64_t mock_texture_id = 1234; + + // Init capture controller once with mocks and tests + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + // Init capture controller a second time. + EXPECT_CALL(*camera, OnCreateCaptureEngineFailed).Times(1); + + bool result = capture_controller->InitCaptureDevice( + texture_registrar.get(), MOCK_DEVICE_ID, true, ResolutionPreset::kAuto); + + EXPECT_FALSE(result); + + capture_controller = nullptr; + camera = nullptr; + texture_registrar = nullptr; + engine = nullptr; +} + +TEST(CaptureController, InitCaptureEngineReportsFailure) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + ComPtr video_source = new MockMediaSource(); + ComPtr audio_source = new MockMediaSource(); + + capture_controller->SetCaptureEngine( + reinterpret_cast(engine.Get())); + capture_controller->SetVideoSource( + reinterpret_cast(video_source.Get())); + capture_controller->SetAudioSource( + reinterpret_cast(audio_source.Get())); + + EXPECT_CALL(*texture_registrar, RegisterTexture).Times(0); + EXPECT_CALL(*texture_registrar, UnregisterTexture).Times(0); + EXPECT_CALL(*camera, OnCreateCaptureEngineSucceeded).Times(0); + + EXPECT_CALL(*engine.Get(), Initialize) + .Times(1) + .WillOnce(Return(E_ACCESSDENIED)); + + EXPECT_CALL(*camera, + OnCreateCaptureEngineFailed(Eq("Failed to create camera"))) + .Times(1); + + bool result = capture_controller->InitCaptureDevice( + texture_registrar.get(), MOCK_DEVICE_ID, true, ResolutionPreset::kAuto); + + EXPECT_FALSE(result); + EXPECT_FALSE(engine->initialized_); + + capture_controller = nullptr; + camera = nullptr; + texture_registrar = nullptr; + engine = nullptr; +} + TEST(CaptureController, StartPreviewStartsProcessingSamples) { ComPtr engine = new MockCaptureEngine(); std::unique_ptr camera = diff --git a/packages/camera/camera_windows/windows/test/mocks.h b/packages/camera/camera_windows/windows/test/mocks.h index 0781989e94c2..53101f5e6c17 100644 --- a/packages/camera/camera_windows/windows/test/mocks.h +++ b/packages/camera/camera_windows/windows/test/mocks.h @@ -182,7 +182,7 @@ class MockCamera : public Camera { MOCK_METHOD(camera_windows::CaptureController*, GetCaptureController, (), (override)); - MOCK_METHOD(void, InitCamera, + MOCK_METHOD(bool, InitCamera, (flutter::TextureRegistrar * texture_registrar, flutter::BinaryMessenger* messenger, bool record_audio, ResolutionPreset resolution_preset), @@ -212,7 +212,7 @@ class MockCaptureController : public CaptureController { public: ~MockCaptureController() = default; - MOCK_METHOD(void, InitCaptureDevice, + MOCK_METHOD(bool, InitCaptureDevice, (flutter::TextureRegistrar * texture_registrar, const std::string& device_id, bool record_audio, ResolutionPreset resolution_preset), From 398f8f7f65dc9e7eb8db05580584a82c6ea97f6b Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 22 Jul 2022 22:58:04 -0400 Subject: [PATCH 543/844] [google_maps_flutter] Use an interface for test inspection (#6116) --- .../google_maps_flutter/CHANGELOG.md | 4 +- .../google_map_inspector.dart | 154 +++++--- .../integration_test/google_maps_test.dart | 354 +++++++++--------- .../lib/google_maps_flutter.dart | 1 - .../lib/src/controller.dart | 14 - .../google_maps_flutter/pubspec.yaml | 4 +- 6 files changed, 270 insertions(+), 261 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 84f7867905ff..bf7a705556ed 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,5 +1,7 @@ -## NEXT +## 2.1.9 +* Updates integration tests to use the new inspector interface. +* Removes obsolete test-only method for accessing a map controller's method channel. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). ## 2.1.8 diff --git a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart index fe3461b9142f..95ca969a51e2 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart @@ -2,87 +2,125 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) -// ignore: unnecessary_import -import 'dart:typed_data'; -import 'package:flutter/services.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; - -/// Inspect Google Maps state using the platform SDK. -/// -/// This class is primarily used for testing. The methods on this -/// class should call "getters" on the GoogleMap object or equivalent -/// on the platform side. -class GoogleMapInspector { - GoogleMapInspector(this._channel); - - final MethodChannel _channel; - - Future isCompassEnabled() async { - return await _channel.invokeMethod('map#isCompassEnabled'); +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +/// A method-channel-based implementation of [GoogleMapsInspectorPlatform], for +/// use in tests in conjunction with [MethodChannelGoogleMapsFlutter]. +// TODO(stuartmorgan): Move this into the platform implementations when +// federating the mobile implementations. +class MethodChannelGoogleMapsInspector extends GoogleMapsInspectorPlatform { + /// Creates a method-channel-based inspector instance that gets the channel + /// for a given map ID from [mapsPlatform]. + MethodChannelGoogleMapsInspector(MethodChannelGoogleMapsFlutter mapsPlatform) + : _mapsPlatform = mapsPlatform; + + final MethodChannelGoogleMapsFlutter _mapsPlatform; + + @override + Future areBuildingsEnabled({required int mapId}) async { + return (await _mapsPlatform + .channel(mapId) + .invokeMethod('map#isBuildingsEnabled'))!; } - Future isMapToolbarEnabled() async { - return await _channel.invokeMethod('map#isMapToolbarEnabled'); + @override + Future areRotateGesturesEnabled({required int mapId}) async { + return (await _mapsPlatform + .channel(mapId) + .invokeMethod('map#isRotateGesturesEnabled'))!; } - Future getMinMaxZoomLevels() async { - final List zoomLevels = - (await _channel.invokeMethod>('map#getMinMaxZoomLevels'))! - .cast(); - return MinMaxZoomPreference(zoomLevels[0], zoomLevels[1]); - } - - Future getZoomLevel() async { - final double? zoomLevel = - await _channel.invokeMethod('map#getZoomLevel'); - return zoomLevel; - } - - Future isZoomGesturesEnabled() async { - return await _channel.invokeMethod('map#isZoomGesturesEnabled'); + @override + Future areScrollGesturesEnabled({required int mapId}) async { + return (await _mapsPlatform + .channel(mapId) + .invokeMethod('map#isScrollGesturesEnabled'))!; } - Future isZoomControlsEnabled() async { - return await _channel.invokeMethod('map#isZoomControlsEnabled'); + @override + Future areTiltGesturesEnabled({required int mapId}) async { + return (await _mapsPlatform + .channel(mapId) + .invokeMethod('map#isTiltGesturesEnabled'))!; } - Future isLiteModeEnabled() async { - return await _channel.invokeMethod('map#isLiteModeEnabled'); + @override + Future areZoomControlsEnabled({required int mapId}) async { + return (await _mapsPlatform + .channel(mapId) + .invokeMethod('map#isZoomControlsEnabled'))!; } - Future isRotateGesturesEnabled() async { - return await _channel.invokeMethod('map#isRotateGesturesEnabled'); + @override + Future areZoomGesturesEnabled({required int mapId}) async { + return (await _mapsPlatform + .channel(mapId) + .invokeMethod('map#isZoomGesturesEnabled'))!; } - Future isTiltGesturesEnabled() async { - return await _channel.invokeMethod('map#isTiltGesturesEnabled'); + @override + Future getMinMaxZoomLevels({required int mapId}) async { + final List zoomLevels = (await _mapsPlatform + .channel(mapId) + .invokeMethod>('map#getMinMaxZoomLevels'))! + .cast(); + return MinMaxZoomPreference(zoomLevels[0], zoomLevels[1]); } - Future isScrollGesturesEnabled() async { - return await _channel.invokeMethod('map#isScrollGesturesEnabled'); + @override + Future getTileOverlayInfo(TileOverlayId tileOverlayId, + {required int mapId}) async { + final Map? tileInfo = await _mapsPlatform + .channel(mapId) + .invokeMapMethod( + 'map#getTileOverlayInfo', { + 'tileOverlayId': tileOverlayId.value, + }); + if (tileInfo == null) { + return null; + } + return TileOverlay( + tileOverlayId: tileOverlayId, + fadeIn: tileInfo['fadeIn']! as bool, + transparency: tileInfo['transparency']! as double, + visible: tileInfo['visible']! as bool, + // Android and iOS return different types. + zIndex: (tileInfo['zIndex']! as num).toInt(), + ); } - Future isMyLocationButtonEnabled() async { - return await _channel.invokeMethod('map#isMyLocationButtonEnabled'); + @override + Future isCompassEnabled({required int mapId}) async { + return (await _mapsPlatform + .channel(mapId) + .invokeMethod('map#isCompassEnabled'))!; } - Future isTrafficEnabled() async { - return await _channel.invokeMethod('map#isTrafficEnabled'); + @override + Future isLiteModeEnabled({required int mapId}) async { + return (await _mapsPlatform + .channel(mapId) + .invokeMethod('map#isLiteModeEnabled'))!; } - Future isBuildingsEnabled() async { - return await _channel.invokeMethod('map#isBuildingsEnabled'); + @override + Future isMapToolbarEnabled({required int mapId}) async { + return (await _mapsPlatform + .channel(mapId) + .invokeMethod('map#isMapToolbarEnabled'))!; } - Future takeSnapshot() async { - return await _channel.invokeMethod('map#takeSnapshot'); + @override + Future isMyLocationButtonEnabled({required int mapId}) async { + return (await _mapsPlatform + .channel(mapId) + .invokeMethod('map#isMyLocationButtonEnabled'))!; } - Future?> getTileOverlayInfo(String id) async { - return await _channel.invokeMapMethod( - 'map#getTileOverlayInfo', { - 'tileOverlayId': id, - }); + @override + Future isTrafficEnabled({required int mapId}) async { + return (await _mapsPlatform + .channel(mapId) + .invokeMethod('map#isTrafficEnabled'))!; } } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart index 8742ff31af42..d82a58227d8a 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart @@ -10,6 +10,7 @@ import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:integration_test/integration_test.dart'; import 'google_map_inspector.dart'; @@ -22,10 +23,19 @@ const CameraPosition _kInitialCameraPosition = void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + // TODO(stuartmorgan): Remove this once mobile implementations are federated + // and registering their own inpector implementations, and just call + // enableDebugInspection. + if (GoogleMapsFlutterPlatform.instance is MethodChannelGoogleMapsFlutter) { + GoogleMapsInspectorPlatform.instance = MethodChannelGoogleMapsInspector( + GoogleMapsFlutterPlatform.instance as MethodChannelGoogleMapsFlutter); + } else { + GoogleMapsFlutterPlatform.instance.enableDebugInspection(); + } + testWidgets('testCompassToggle', (WidgetTester tester) async { final Key key = GlobalKey(); - final Completer inspectorCompleter = - Completer(); + final Completer mapIdCompleter = Completer(); await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, child: GoogleMap( @@ -33,16 +43,15 @@ void main() { initialCameraPosition: _kInitialCameraPosition, compassEnabled: false, onMapCreated: (GoogleMapController controller) { - final GoogleMapInspector inspector = - // ignore: invalid_use_of_visible_for_testing_member - GoogleMapInspector(controller.channel!); - inspectorCompleter.complete(inspector); + mapIdCompleter.complete(controller.mapId); }, ), )); - final GoogleMapInspector inspector = await inspectorCompleter.future; - bool? compassEnabled = await inspector.isCompassEnabled(); + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool compassEnabled = await inspector.isCompassEnabled(mapId: mapId); expect(compassEnabled, false); await tester.pumpWidget(Directionality( @@ -57,14 +66,13 @@ void main() { ), )); - compassEnabled = await inspector.isCompassEnabled(); + compassEnabled = await inspector.isCompassEnabled(mapId: mapId); expect(compassEnabled, true); }); testWidgets('testMapToolbarToggle', (WidgetTester tester) async { final Key key = GlobalKey(); - final Completer inspectorCompleter = - Completer(); + final Completer mapIdCompleter = Completer(); await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, @@ -73,16 +81,15 @@ void main() { initialCameraPosition: _kInitialCameraPosition, mapToolbarEnabled: false, onMapCreated: (GoogleMapController controller) { - final GoogleMapInspector inspector = - // ignore: invalid_use_of_visible_for_testing_member - GoogleMapInspector(controller.channel!); - inspectorCompleter.complete(inspector); + mapIdCompleter.complete(controller.mapId); }, ), )); - final GoogleMapInspector inspector = await inspectorCompleter.future; - bool? mapToolbarEnabled = await inspector.isMapToolbarEnabled(); + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool mapToolbarEnabled = await inspector.isMapToolbarEnabled(mapId: mapId); expect(mapToolbarEnabled, false); await tester.pumpWidget(Directionality( @@ -97,7 +104,7 @@ void main() { ), )); - mapToolbarEnabled = await inspector.isMapToolbarEnabled(); + mapToolbarEnabled = await inspector.isMapToolbarEnabled(mapId: mapId); expect(mapToolbarEnabled, Platform.isAndroid); }); @@ -113,9 +120,8 @@ void main() { // // Thus we test iOS and Android a little differently here. final Key key = GlobalKey(); - final Completer inspectorCompleter = - Completer(); - late GoogleMapController controller; + final Completer controllerCompleter = + Completer(); const MinMaxZoomPreference initialZoomLevel = MinMaxZoomPreference(4, 8); const MinMaxZoomPreference finalZoomLevel = MinMaxZoomPreference(6, 10); @@ -127,30 +133,28 @@ void main() { initialCameraPosition: _kInitialCameraPosition, minMaxZoomPreference: initialZoomLevel, onMapCreated: (GoogleMapController c) async { - final GoogleMapInspector inspector = - // ignore: invalid_use_of_visible_for_testing_member - GoogleMapInspector(c.channel!); - controller = c; - inspectorCompleter.complete(inspector); + controllerCompleter.complete(c); }, ), )); - final GoogleMapInspector inspector = await inspectorCompleter.future; + final GoogleMapController controller = await controllerCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; if (Platform.isIOS) { final MinMaxZoomPreference zoomLevel = - await inspector.getMinMaxZoomLevels(); + await inspector.getMinMaxZoomLevels(mapId: controller.mapId); expect(zoomLevel, equals(initialZoomLevel)); } else if (Platform.isAndroid) { await controller.moveCamera(CameraUpdate.zoomTo(15)); await tester.pumpAndSettle(); - double? zoomLevel = await inspector.getZoomLevel(); + double? zoomLevel = await controller.getZoomLevel(); expect(zoomLevel, equals(initialZoomLevel.maxZoom)); await controller.moveCamera(CameraUpdate.zoomTo(1)); await tester.pumpAndSettle(); - zoomLevel = await inspector.getZoomLevel(); + zoomLevel = await controller.getZoomLevel(); expect(zoomLevel, equals(initialZoomLevel.minZoom)); } @@ -168,25 +172,24 @@ void main() { if (Platform.isIOS) { final MinMaxZoomPreference zoomLevel = - await inspector.getMinMaxZoomLevels(); + await inspector.getMinMaxZoomLevels(mapId: controller.mapId); expect(zoomLevel, equals(finalZoomLevel)); } else { await controller.moveCamera(CameraUpdate.zoomTo(15)); await tester.pumpAndSettle(); - double? zoomLevel = await inspector.getZoomLevel(); + double? zoomLevel = await controller.getZoomLevel(); expect(zoomLevel, equals(finalZoomLevel.maxZoom)); await controller.moveCamera(CameraUpdate.zoomTo(1)); await tester.pumpAndSettle(); - zoomLevel = await inspector.getZoomLevel(); + zoomLevel = await controller.getZoomLevel(); expect(zoomLevel, equals(finalZoomLevel.minZoom)); } }); testWidgets('testZoomGesturesEnabled', (WidgetTester tester) async { final Key key = GlobalKey(); - final Completer inspectorCompleter = - Completer(); + final Completer mapIdCompleter = Completer(); await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, @@ -195,16 +198,16 @@ void main() { initialCameraPosition: _kInitialCameraPosition, zoomGesturesEnabled: false, onMapCreated: (GoogleMapController controller) { - final GoogleMapInspector inspector = - // ignore: invalid_use_of_visible_for_testing_member - GoogleMapInspector(controller.channel!); - inspectorCompleter.complete(inspector); + mapIdCompleter.complete(controller.mapId); }, ), )); - final GoogleMapInspector inspector = await inspectorCompleter.future; - bool? zoomGesturesEnabled = await inspector.isZoomGesturesEnabled(); + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool zoomGesturesEnabled = + await inspector.areZoomGesturesEnabled(mapId: mapId); expect(zoomGesturesEnabled, false); await tester.pumpWidget(Directionality( @@ -219,14 +222,13 @@ void main() { ), )); - zoomGesturesEnabled = await inspector.isZoomGesturesEnabled(); + zoomGesturesEnabled = await inspector.areZoomGesturesEnabled(mapId: mapId); expect(zoomGesturesEnabled, true); }); testWidgets('testZoomControlsEnabled', (WidgetTester tester) async { final Key key = GlobalKey(); - final Completer inspectorCompleter = - Completer(); + final Completer mapIdCompleter = Completer(); await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, @@ -234,16 +236,16 @@ void main() { key: key, initialCameraPosition: _kInitialCameraPosition, onMapCreated: (GoogleMapController controller) { - final GoogleMapInspector inspector = - // ignore: invalid_use_of_visible_for_testing_member - GoogleMapInspector(controller.channel!); - inspectorCompleter.complete(inspector); + mapIdCompleter.complete(controller.mapId); }, ), )); - final GoogleMapInspector inspector = await inspectorCompleter.future; - bool? zoomControlsEnabled = await inspector.isZoomControlsEnabled(); + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool zoomControlsEnabled = + await inspector.areZoomControlsEnabled(mapId: mapId); expect(zoomControlsEnabled, !Platform.isIOS); /// Zoom Controls functionality is not available on iOS at the moment. @@ -260,15 +262,15 @@ void main() { ), )); - zoomControlsEnabled = await inspector.isZoomControlsEnabled(); + zoomControlsEnabled = + await inspector.areZoomControlsEnabled(mapId: mapId); expect(zoomControlsEnabled, false); } }); testWidgets('testLiteModeEnabled', (WidgetTester tester) async { final Key key = GlobalKey(); - final Completer inspectorCompleter = - Completer(); + final Completer mapIdCompleter = Completer(); await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, @@ -277,16 +279,15 @@ void main() { initialCameraPosition: _kInitialCameraPosition, liteModeEnabled: false, onMapCreated: (GoogleMapController controller) { - final GoogleMapInspector inspector = - // ignore: invalid_use_of_visible_for_testing_member - GoogleMapInspector(controller.channel!); - inspectorCompleter.complete(inspector); + mapIdCompleter.complete(controller.mapId); }, ), )); - final GoogleMapInspector inspector = await inspectorCompleter.future; - bool? liteModeEnabled = await inspector.isLiteModeEnabled(); + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool liteModeEnabled = await inspector.isLiteModeEnabled(mapId: mapId); expect(liteModeEnabled, false); await tester.pumpWidget(Directionality( @@ -301,14 +302,13 @@ void main() { ), )); - liteModeEnabled = await inspector.isLiteModeEnabled(); + liteModeEnabled = await inspector.isLiteModeEnabled(mapId: mapId); expect(liteModeEnabled, true); }, skip: !Platform.isAndroid); testWidgets('testRotateGesturesEnabled', (WidgetTester tester) async { final Key key = GlobalKey(); - final Completer inspectorCompleter = - Completer(); + final Completer mapIdCompleter = Completer(); await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, @@ -317,16 +317,16 @@ void main() { initialCameraPosition: _kInitialCameraPosition, rotateGesturesEnabled: false, onMapCreated: (GoogleMapController controller) { - final GoogleMapInspector inspector = - // ignore: invalid_use_of_visible_for_testing_member - GoogleMapInspector(controller.channel!); - inspectorCompleter.complete(inspector); + mapIdCompleter.complete(controller.mapId); }, ), )); - final GoogleMapInspector inspector = await inspectorCompleter.future; - bool? rotateGesturesEnabled = await inspector.isRotateGesturesEnabled(); + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool rotateGesturesEnabled = + await inspector.areRotateGesturesEnabled(mapId: mapId); expect(rotateGesturesEnabled, false); await tester.pumpWidget(Directionality( @@ -341,14 +341,14 @@ void main() { ), )); - rotateGesturesEnabled = await inspector.isRotateGesturesEnabled(); + rotateGesturesEnabled = + await inspector.areRotateGesturesEnabled(mapId: mapId); expect(rotateGesturesEnabled, true); }); testWidgets('testTiltGesturesEnabled', (WidgetTester tester) async { final Key key = GlobalKey(); - final Completer inspectorCompleter = - Completer(); + final Completer mapIdCompleter = Completer(); await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, @@ -357,16 +357,16 @@ void main() { initialCameraPosition: _kInitialCameraPosition, tiltGesturesEnabled: false, onMapCreated: (GoogleMapController controller) { - final GoogleMapInspector inspector = - // ignore: invalid_use_of_visible_for_testing_member - GoogleMapInspector(controller.channel!); - inspectorCompleter.complete(inspector); + mapIdCompleter.complete(controller.mapId); }, ), )); - final GoogleMapInspector inspector = await inspectorCompleter.future; - bool? tiltGesturesEnabled = await inspector.isTiltGesturesEnabled(); + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool tiltGesturesEnabled = + await inspector.areTiltGesturesEnabled(mapId: mapId); expect(tiltGesturesEnabled, false); await tester.pumpWidget(Directionality( @@ -381,14 +381,13 @@ void main() { ), )); - tiltGesturesEnabled = await inspector.isTiltGesturesEnabled(); + tiltGesturesEnabled = await inspector.areTiltGesturesEnabled(mapId: mapId); expect(tiltGesturesEnabled, true); }); testWidgets('testScrollGesturesEnabled', (WidgetTester tester) async { final Key key = GlobalKey(); - final Completer inspectorCompleter = - Completer(); + final Completer mapIdCompleter = Completer(); await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, @@ -397,16 +396,16 @@ void main() { initialCameraPosition: _kInitialCameraPosition, scrollGesturesEnabled: false, onMapCreated: (GoogleMapController controller) { - final GoogleMapInspector inspector = - // ignore: invalid_use_of_visible_for_testing_member - GoogleMapInspector(controller.channel!); - inspectorCompleter.complete(inspector); + mapIdCompleter.complete(controller.mapId); }, ), )); - final GoogleMapInspector inspector = await inspectorCompleter.future; - bool? scrollGesturesEnabled = await inspector.isScrollGesturesEnabled(); + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool scrollGesturesEnabled = + await inspector.areScrollGesturesEnabled(mapId: mapId); expect(scrollGesturesEnabled, false); await tester.pumpWidget(Directionality( @@ -421,7 +420,8 @@ void main() { ), )); - scrollGesturesEnabled = await inspector.isScrollGesturesEnabled(); + scrollGesturesEnabled = + await inspector.areScrollGesturesEnabled(mapId: mapId); expect(scrollGesturesEnabled, true); }); @@ -547,8 +547,7 @@ void main() { testWidgets('testTraffic', (WidgetTester tester) async { final Key key = GlobalKey(); - final Completer inspectorCompleter = - Completer(); + final Completer mapIdCompleter = Completer(); await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, @@ -557,16 +556,15 @@ void main() { initialCameraPosition: _kInitialCameraPosition, trafficEnabled: true, onMapCreated: (GoogleMapController controller) { - final GoogleMapInspector inspector = - // ignore: invalid_use_of_visible_for_testing_member - GoogleMapInspector(controller.channel!); - inspectorCompleter.complete(inspector); + mapIdCompleter.complete(controller.mapId); }, ), )); - final GoogleMapInspector inspector = await inspectorCompleter.future; - bool? isTrafficEnabled = await inspector.isTrafficEnabled(); + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool isTrafficEnabled = await inspector.isTrafficEnabled(mapId: mapId); expect(isTrafficEnabled, true); await tester.pumpWidget(Directionality( @@ -581,14 +579,13 @@ void main() { ), )); - isTrafficEnabled = await inspector.isTrafficEnabled(); + isTrafficEnabled = await inspector.isTrafficEnabled(mapId: mapId); expect(isTrafficEnabled, false); }); testWidgets('testBuildings', (WidgetTester tester) async { final Key key = GlobalKey(); - final Completer inspectorCompleter = - Completer(); + final Completer mapIdCompleter = Completer(); await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, @@ -597,24 +594,23 @@ void main() { initialCameraPosition: _kInitialCameraPosition, buildingsEnabled: true, onMapCreated: (GoogleMapController controller) { - final GoogleMapInspector inspector = - // ignore: invalid_use_of_visible_for_testing_member - GoogleMapInspector(controller.channel!); - inspectorCompleter.complete(inspector); + mapIdCompleter.complete(controller.mapId); }, ), )); - final GoogleMapInspector inspector = await inspectorCompleter.future; - final bool? isBuildingsEnabled = await inspector.isBuildingsEnabled(); + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + final bool isBuildingsEnabled = + await inspector.areBuildingsEnabled(mapId: mapId); expect(isBuildingsEnabled, true); }); // Location button tests are skipped in Android because we don't have location permission to test. testWidgets('testMyLocationButtonToggle', (WidgetTester tester) async { final Key key = GlobalKey(); - final Completer inspectorCompleter = - Completer(); + final Completer mapIdCompleter = Completer(); await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, @@ -624,16 +620,16 @@ void main() { myLocationButtonEnabled: true, myLocationEnabled: false, onMapCreated: (GoogleMapController controller) { - final GoogleMapInspector inspector = - // ignore: invalid_use_of_visible_for_testing_member - GoogleMapInspector(controller.channel!); - inspectorCompleter.complete(inspector); + mapIdCompleter.complete(controller.mapId); }, ), )); - final GoogleMapInspector inspector = await inspectorCompleter.future; - bool? myLocationButtonEnabled = await inspector.isMyLocationButtonEnabled(); + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool myLocationButtonEnabled = + await inspector.isMyLocationButtonEnabled(mapId: mapId); expect(myLocationButtonEnabled, true); await tester.pumpWidget(Directionality( @@ -649,15 +645,15 @@ void main() { ), )); - myLocationButtonEnabled = await inspector.isMyLocationButtonEnabled(); + myLocationButtonEnabled = + await inspector.isMyLocationButtonEnabled(mapId: mapId); expect(myLocationButtonEnabled, false); }, skip: Platform.isAndroid); testWidgets('testMyLocationButton initial value false', (WidgetTester tester) async { final Key key = GlobalKey(); - final Completer inspectorCompleter = - Completer(); + final Completer mapIdCompleter = Completer(); await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, @@ -667,25 +663,23 @@ void main() { myLocationButtonEnabled: false, myLocationEnabled: false, onMapCreated: (GoogleMapController controller) { - final GoogleMapInspector inspector = - // ignore: invalid_use_of_visible_for_testing_member - GoogleMapInspector(controller.channel!); - inspectorCompleter.complete(inspector); + mapIdCompleter.complete(controller.mapId); }, ), )); - final GoogleMapInspector inspector = await inspectorCompleter.future; - final bool? myLocationButtonEnabled = - await inspector.isMyLocationButtonEnabled(); + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + final bool myLocationButtonEnabled = + await inspector.isMyLocationButtonEnabled(mapId: mapId); expect(myLocationButtonEnabled, false); }, skip: Platform.isAndroid); testWidgets('testMyLocationButton initial value true', (WidgetTester tester) async { final Key key = GlobalKey(); - final Completer inspectorCompleter = - Completer(); + final Completer mapIdCompleter = Completer(); await tester.pumpWidget(Directionality( textDirection: TextDirection.ltr, @@ -695,17 +689,16 @@ void main() { myLocationButtonEnabled: true, myLocationEnabled: false, onMapCreated: (GoogleMapController controller) { - final GoogleMapInspector inspector = - // ignore: invalid_use_of_visible_for_testing_member - GoogleMapInspector(controller.channel!); - inspectorCompleter.complete(inspector); + mapIdCompleter.complete(controller.mapId); }, ), )); - final GoogleMapInspector inspector = await inspectorCompleter.future; - final bool? myLocationButtonEnabled = - await inspector.isMyLocationButtonEnabled(); + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + final bool myLocationButtonEnabled = + await inspector.isMyLocationButtonEnabled(mapId: mapId); expect(myLocationButtonEnabled, true); }, skip: Platform.isAndroid); @@ -962,8 +955,8 @@ void main() { }); testWidgets('testTakeSnapshot', (WidgetTester tester) async { - final Completer inspectorCompleter = - Completer(); + final Completer controllerCompleter = + Completer(); await tester.pumpWidget( Directionality( @@ -971,10 +964,7 @@ void main() { child: GoogleMap( initialCameraPosition: _kInitialCameraPosition, onMapCreated: (GoogleMapController controller) { - final GoogleMapInspector inspector = - // ignore: invalid_use_of_visible_for_testing_member - GoogleMapInspector(controller.channel!); - inspectorCompleter.complete(inspector); + controllerCompleter.complete(controller); }, ), ), @@ -982,8 +972,8 @@ void main() { await tester.pumpAndSettle(const Duration(seconds: 3)); - final GoogleMapInspector inspector = await inspectorCompleter.future; - final Uint8List? bytes = await inspector.takeSnapshot(); + final GoogleMapController controller = await controllerCompleter.future; + final Uint8List? bytes = await controller.takeSnapshot(); expect(bytes?.isNotEmpty, true); }, // TODO(cyanglaz): un-skip the test when we can test this on CI with API key enabled. @@ -993,8 +983,7 @@ void main() { testWidgets( 'set tileOverlay correctly', (WidgetTester tester) async { - final Completer inspectorCompleter = - Completer(); + final Completer mapIdCompleter = Completer(); final TileOverlay tileOverlay1 = TileOverlay( tileOverlayId: const TileOverlayId('tile_overlay_1'), tileProvider: _DebugTileProvider(), @@ -1019,42 +1008,40 @@ void main() { initialCameraPosition: _kInitialCameraPosition, tileOverlays: {tileOverlay1, tileOverlay2}, onMapCreated: (GoogleMapController controller) { - final GoogleMapInspector inspector = - // ignore: invalid_use_of_visible_for_testing_member - GoogleMapInspector(controller.channel!); - inspectorCompleter.complete(inspector); + mapIdCompleter.complete(controller.mapId); }, ), ), ); await tester.pumpAndSettle(const Duration(seconds: 3)); - final GoogleMapInspector inspector = await inspectorCompleter.future; + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; - final Map tileOverlayInfo1 = - (await inspector.getTileOverlayInfo('tile_overlay_1'))!; - final Map tileOverlayInfo2 = - (await inspector.getTileOverlayInfo('tile_overlay_2'))!; + final TileOverlay tileOverlayInfo1 = (await inspector + .getTileOverlayInfo(tileOverlay1.mapsId, mapId: mapId))!; + final TileOverlay tileOverlayInfo2 = (await inspector + .getTileOverlayInfo(tileOverlay2.mapsId, mapId: mapId))!; - expect(tileOverlayInfo1['visible'], isTrue); - expect(tileOverlayInfo1['fadeIn'], isTrue); - expect(tileOverlayInfo1['transparency'], - moreOrLessEquals(0.2, epsilon: 0.001)); - expect(tileOverlayInfo1['zIndex'], 2); + expect(tileOverlayInfo1.visible, isTrue); + expect(tileOverlayInfo1.fadeIn, isTrue); + expect( + tileOverlayInfo1.transparency, moreOrLessEquals(0.2, epsilon: 0.001)); + expect(tileOverlayInfo1.zIndex, 2); - expect(tileOverlayInfo2['visible'], isFalse); - expect(tileOverlayInfo2['fadeIn'], isFalse); - expect(tileOverlayInfo2['transparency'], - moreOrLessEquals(0.3, epsilon: 0.001)); - expect(tileOverlayInfo2['zIndex'], 1); + expect(tileOverlayInfo2.visible, isFalse); + expect(tileOverlayInfo2.fadeIn, isFalse); + expect( + tileOverlayInfo2.transparency, moreOrLessEquals(0.3, epsilon: 0.001)); + expect(tileOverlayInfo2.zIndex, 1); }, ); testWidgets( 'update tileOverlays correctly', (WidgetTester tester) async { - final Completer inspectorCompleter = - Completer(); + final Completer mapIdCompleter = Completer(); final Key key = GlobalKey(); final TileOverlay tileOverlay1 = TileOverlay( tileOverlayId: const TileOverlayId('tile_overlay_1'), @@ -1081,16 +1068,15 @@ void main() { initialCameraPosition: _kInitialCameraPosition, tileOverlays: {tileOverlay1, tileOverlay2}, onMapCreated: (GoogleMapController controller) { - final GoogleMapInspector inspector = - // ignore: invalid_use_of_visible_for_testing_member - GoogleMapInspector(controller.channel!); - inspectorCompleter.complete(inspector); + mapIdCompleter.complete(controller.mapId); }, ), ), ); - final GoogleMapInspector inspector = await inspectorCompleter.future; + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; final TileOverlay tileOverlay1New = TileOverlay( tileOverlayId: const TileOverlayId('tile_overlay_1'), @@ -1117,16 +1103,16 @@ void main() { await tester.pumpAndSettle(const Duration(seconds: 3)); - final Map tileOverlayInfo1 = - (await inspector.getTileOverlayInfo('tile_overlay_1'))!; - final Map? tileOverlayInfo2 = - await inspector.getTileOverlayInfo('tile_overlay_2'); + final TileOverlay tileOverlayInfo1 = (await inspector + .getTileOverlayInfo(tileOverlay1.mapsId, mapId: mapId))!; + final TileOverlay? tileOverlayInfo2 = + await inspector.getTileOverlayInfo(tileOverlay2.mapsId, mapId: mapId); - expect(tileOverlayInfo1['visible'], isFalse); - expect(tileOverlayInfo1['fadeIn'], isFalse); - expect(tileOverlayInfo1['transparency'], - moreOrLessEquals(0.3, epsilon: 0.001)); - expect(tileOverlayInfo1['zIndex'], 1); + expect(tileOverlayInfo1.visible, isFalse); + expect(tileOverlayInfo1.fadeIn, isFalse); + expect( + tileOverlayInfo1.transparency, moreOrLessEquals(0.3, epsilon: 0.001)); + expect(tileOverlayInfo1.zIndex, 1); expect(tileOverlayInfo2, isNull); }, @@ -1135,8 +1121,7 @@ void main() { testWidgets( 'remove tileOverlays correctly', (WidgetTester tester) async { - final Completer inspectorCompleter = - Completer(); + final Completer mapIdCompleter = Completer(); final Key key = GlobalKey(); final TileOverlay tileOverlay1 = TileOverlay( tileOverlayId: const TileOverlayId('tile_overlay_1'), @@ -1155,16 +1140,15 @@ void main() { initialCameraPosition: _kInitialCameraPosition, tileOverlays: {tileOverlay1}, onMapCreated: (GoogleMapController controller) { - final GoogleMapInspector inspector = - // ignore: invalid_use_of_visible_for_testing_member - GoogleMapInspector(controller.channel!); - inspectorCompleter.complete(inspector); + mapIdCompleter.complete(controller.mapId); }, ), ), ); - final GoogleMapInspector inspector = await inspectorCompleter.future; + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; await tester.pumpWidget( Directionality( @@ -1180,8 +1164,8 @@ void main() { ); await tester.pumpAndSettle(const Duration(seconds: 3)); - final Map? tileOverlayInfo1 = - await inspector.getTileOverlayInfo('tile_overlay_1'); + final TileOverlay? tileOverlayInfo1 = + await inspector.getTileOverlayInfo(tileOverlay1.mapsId, mapId: mapId); expect(tileOverlayInfo1, isNull); }, diff --git a/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart b/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart index 4eeb8572413c..751930aece67 100644 --- a/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart +++ b/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart @@ -13,7 +13,6 @@ import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; export 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart' 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 71b1434eb293..cd3d0781e471 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 @@ -35,20 +35,6 @@ class GoogleMapController { ); } - /// Used to communicate with the native platform. - /// - /// Accessible only for testing. - // TODO(dit): Remove this getter, https://github.com/flutter/flutter/issues/55504. - @visibleForTesting - MethodChannel? get channel { - if (GoogleMapsFlutterPlatform.instance is MethodChannelGoogleMapsFlutter) { - return (GoogleMapsFlutterPlatform.instance - as MethodChannelGoogleMapsFlutter) - .channel(mapId); - } - return null; - } - final _GoogleMapState _googleMapState; void _connectStreams(int mapId) { diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index 1a0dd4ebfc6f..f546c8af151a 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/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.1.8 +version: 2.1.9 environment: sdk: ">=2.14.0 <3.0.0" @@ -21,7 +21,7 @@ dependencies: flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 - google_maps_flutter_platform_interface: ^2.2.0 + google_maps_flutter_platform_interface: ^2.2.1 dev_dependencies: flutter_test: From 9772016333a4a2f015a903ea0824256c072aba36 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 23 Jul 2022 11:46:04 -0400 Subject: [PATCH 544/844] Roll Flutter from 096bbbe2fcbf to fa42451a2596 (19 revisions) (#6136) --- .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 13b462e09f03..537a19dc791e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -096bbbe2fcbf55b27e216e1b328503d0c8d18b9d +fa42451a259631d403af973c96c3212224c42c6a From 00a3f24734f45f1c00cde2cd514288db531c7da4 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 24 Jul 2022 11:54:04 -0400 Subject: [PATCH 545/844] Roll Flutter from fa42451a2596 to 6d8e365bc9da (3 revisions) (#6137) --- .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 537a19dc791e..c5d3c6b90b2c 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fa42451a259631d403af973c96c3212224c42c6a +6d8e365bc9da2bb9e1d7b2c09d47e660c9d8cc39 From edddaf16d9136ccd3bfd51d8a85800c965c90a87 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 25 Jul 2022 12:46:05 -0400 Subject: [PATCH 546/844] Roll Flutter from 6d8e365bc9da to fa39206a2a34 (11 revisions) (#6144) --- .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 c5d3c6b90b2c..b96fd39cb7b4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -6d8e365bc9da2bb9e1d7b2c09d47e660c9d8cc39 +fa39206a2a34e94a01cbbff80477c0452d02d566 From 300f970c595d6fbd05f3b169dbcf3a5e03b24af3 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 25 Jul 2022 20:18:03 -0400 Subject: [PATCH 547/844] [tool] Include `dev_dependencies` in `make-deps-path-based` (#6146) --- script/tool/CHANGELOG.md | 4 +- .../lib/src/make_deps_path_based_command.dart | 10 ++++- script/tool/pubspec.yaml | 2 +- .../make_deps_path_based_command_test.dart | 44 ++++++++++++++++++- 4 files changed, 55 insertions(+), 5 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index a84e9af63902..f0534c23a29a 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,5 +1,7 @@ -## NEXT +## 0.8.9 +- Includes `dev_dependencies` when overridding dependencies using + `make-deps-path-based`. - Bypasses version and CHANGELOG checks for Dependabot PRs for packages that are known not to be client-affecting. diff --git a/script/tool/lib/src/make_deps_path_based_command.dart b/script/tool/lib/src/make_deps_path_based_command.dart index 4bbecb4d2244..805dd68a0afe 100644 --- a/script/tool/lib/src/make_deps_path_based_command.dart +++ b/script/tool/lib/src/make_deps_path_based_command.dart @@ -154,8 +154,14 @@ class MakeDepsPathBasedCommand extends PluginCommand { throw ToolExit(_exitCannotUpdatePubspec); } - final Iterable packagesToOverride = pubspec.dependencies.keys.where( - (String packageName) => localDependencies.containsKey(packageName)); + final Iterable combinedDependencies = [ + ...pubspec.dependencies.keys, + ...pubspec.devDependencies.keys, + ]; + final Iterable packagesToOverride = combinedDependencies + .where( + (String packageName) => localDependencies.containsKey(packageName)) + .toList(); if (packagesToOverride.isNotEmpty) { final String commonBasePath = packagesDir.path; // Find the relative path to the common base. diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index b8233de11b41..a4ecbfb75e98 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.8.8 +version: 0.8.9 dependencies: args: ^2.1.0 diff --git a/script/tool/test/make_deps_path_based_command_test.dart b/script/tool/test/make_deps_path_based_command_test.dart index 2644e814f578..33d6be261e87 100644 --- a/script/tool/test/make_deps_path_based_command_test.dart +++ b/script/tool/test/make_deps_path_based_command_test.dart @@ -60,6 +60,19 @@ void main() { package.pubspecFile.writeAsStringSync(lines.join('\n')); } + /// Adds a 'dev_dependencies:' section with entries for each package in + /// [dependencies] to [package]. + void _addDevDependenciesSection( + RepositoryPackage package, Iterable devDependencies) { + final String originalContent = package.pubspecFile.readAsStringSync(); + package.pubspecFile.writeAsStringSync(''' +$originalContent + +dev_dependencies: +${devDependencies.map((String dep) => ' $dep: ^1.0.0').join('\n')} +'''); + } + test('no-ops for no plugins', () async { createFakePackage('foo', packagesDir, isFlutter: true); final RepositoryPackage packageBar = @@ -81,7 +94,7 @@ void main() { expect(packageBar.pubspecFile.readAsStringSync(), originalPubspecContents); }); - test('rewrites references', () async { + test('rewrites "dependencies" references', () async { final RepositoryPackage simplePackage = createFakePackage('foo', packagesDir, isFlutter: true); final Directory pluginGroup = packagesDir.childDirectory('bar'); @@ -142,6 +155,35 @@ void main() { ])); }); + test('rewrites "dev_dependencies" references', () async { + createFakePackage('foo', packagesDir); + final RepositoryPackage builderPackage = + createFakePackage('foo_builder', packagesDir); + + _addDevDependenciesSection(builderPackage, [ + 'foo', + ]); + + final List output = await runCapturingPrint( + runner, ['make-deps-path-based', '--target-dependencies=foo']); + + expect( + output, + containsAll([ + 'Rewriting references to: foo...', + ' Modified packages/foo_builder/pubspec.yaml', + ])); + + expect( + builderPackage.pubspecFile.readAsLinesSync(), + containsAllInOrder([ + '# FOR TESTING ONLY. DO NOT MERGE.', + 'dependency_overrides:', + ' foo:', + ' path: ../foo', + ])); + }); + // This test case ensures that running CI using this command on an interim // PR that itself used this command won't fail on the rewrite step. test('running a second time no-ops without failing', () async { From 278bc6668567adb5c20dc17a71f03ef404b0d8c8 Mon Sep 17 00:00:00 2001 From: Piotr Mitkowski Date: Tue, 26 Jul 2022 16:17:04 +0200 Subject: [PATCH 548/844] [image_picker] add requestFullMetadata for iOS (optional permissions) - platform interface changes for multi image picking (#5914) Platform interface changes for #5915 - adding possibility to disable full metadata when picking multiple images --- .../CHANGELOG.md | 7 + .../method_channel_image_picker.dart | 20 ++ .../image_picker_platform.dart | 22 ++ .../lib/src/types/image_options.dart | 41 ++++ .../src/types/multi_image_picker_options.dart | 16 ++ .../pubspec.yaml | 2 +- .../new_method_channel_image_picker_test.dart | 224 ++++++++++++++++++ 7 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/image_options.dart create mode 100644 packages/image_picker/image_picker_platform_interface/lib/src/types/multi_image_picker_options.dart diff --git a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md index 0a4e98bf7dbe..120b7b00ed15 100644 --- a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md +++ b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md @@ -1,3 +1,10 @@ +## 2.6.0 + +* Deprecates `getMultiImage` in favor of a new method `getMultiImageWithOptions`. + * Adds `requestFullMetadata` option that allows disabling extra permission requests + on certain platforms. + * Moves optional image picking parameters to `MultiImagePickerOptions` class. + ## 2.5.0 * Deprecates `getImage` in favor of a new method `getImageFromSource`. diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart index ba5d60d7a677..d215fa2684ee 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart @@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'package:image_picker_platform_interface/src/types/multi_image_picker_options.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/image_picker'); @@ -57,6 +58,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { double? maxWidth, double? maxHeight, int? imageQuality, + bool requestFullMetadata = true, }) { if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { throw ArgumentError.value( @@ -77,6 +79,7 @@ class MethodChannelImagePicker extends ImagePickerPlatform { 'maxWidth': maxWidth, 'maxHeight': maxHeight, 'imageQuality': imageQuality, + 'requestFullMetadata': requestFullMetadata, }, ); } @@ -233,6 +236,23 @@ class MethodChannelImagePicker extends ImagePickerPlatform { return paths.map((dynamic path) => XFile(path as String)).toList(); } + @override + Future> getMultiImageWithOptions({ + MultiImagePickerOptions options = const MultiImagePickerOptions(), + }) async { + final List? paths = await _getMultiImagePath( + maxWidth: options.imageOptions.maxWidth, + maxHeight: options.imageOptions.maxHeight, + imageQuality: options.imageOptions.imageQuality, + requestFullMetadata: options.imageOptions.requestFullMetadata, + ); + if (paths == null) { + return []; + } + + return paths.map((dynamic path) => XFile(path as String)).toList(); + } + @override Future getVideo({ required ImageSource source, diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart index d1d06f904fe6..a2618d5b419c 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'package:cross_file/cross_file.dart'; import 'package:image_picker_platform_interface/src/method_channel/method_channel_image_picker.dart'; +import 'package:image_picker_platform_interface/src/types/multi_image_picker_options.dart'; import 'package:image_picker_platform_interface/src/types/types.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; @@ -186,6 +187,8 @@ abstract class ImagePickerPlatform extends PlatformInterface { throw UnimplementedError('getImage() has not been implemented.'); } + /// This method is deprecated in favor of [getMultiImageWithOptions] and will be removed in a future update. + /// /// Returns a [List] with the images that were picked. /// /// The images come from the [ImageSource.gallery]. @@ -283,4 +286,23 @@ abstract class ImagePickerPlatform extends PlatformInterface { preferredCameraDevice: options.preferredCameraDevice, ); } + + /// Returns a [List] with the images that were picked. + /// + /// The images come from the [ImageSource.gallery]. + /// + /// The `options` argument controls additional settings that can be used when + /// picking an image. See [MultiImagePickerOptions] for more details. + /// + /// If no images were picked, returns an empty list. + Future> getMultiImageWithOptions({ + MultiImagePickerOptions options = const MultiImagePickerOptions(), + }) async { + final List? pickedImages = await getMultiImage( + maxWidth: options.imageOptions.maxWidth, + maxHeight: options.imageOptions.maxHeight, + imageQuality: options.imageOptions.imageQuality, + ); + return pickedImages ?? []; + } } diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/image_options.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/image_options.dart new file mode 100644 index 000000000000..2cc01c92da1d --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/image_options.dart @@ -0,0 +1,41 @@ +// 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. + +/// Specifies image-specific options for picking. +class ImageOptions { + /// Creates an instance with the given [maxHeight], [maxWidth], [imageQuality] + /// and [requestFullMetadata]. + const ImageOptions({ + this.maxHeight, + this.maxWidth, + this.imageQuality, + this.requestFullMetadata = true, + }); + + /// The maximum width of the image, in pixels. + /// + /// If null, the image will only be resized if [maxHeight] is specified. + final double? maxWidth; + + /// The maximum height of the image, in pixels. + /// + /// If null, the image will only be resized if [maxWidth] is specified. + final double? maxHeight; + + /// Modifies the quality of the image, ranging from 0-100 where 100 is the + /// original/max quality. + /// + /// Compression is only supported for certain image types such as JPEG. If + /// compression is not supported for the image that is picked, a warning + /// message will be logged. + /// + /// If null, the image will be returned with the original quality. + final int? imageQuality; + + /// If true, requests full image metadata, which may require extra permissions + /// on some platforms, (e.g., NSPhotoLibraryUsageDescription on iOS). + // + // Defaults to true. + final bool requestFullMetadata; +} diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/multi_image_picker_options.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/multi_image_picker_options.dart new file mode 100644 index 000000000000..4d7971c59a81 --- /dev/null +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/multi_image_picker_options.dart @@ -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. + +import 'package:image_picker_platform_interface/src/types/image_options.dart'; + +/// Specifies options for picking multiple images from the device's gallery. +class MultiImagePickerOptions { + /// Creates an instance with the given [imageOptions]. + const MultiImagePickerOptions({ + this.imageOptions = const ImageOptions(), + }); + + /// The image-specific options for picking. + final ImageOptions imageOptions; +} diff --git a/packages/image_picker/image_picker_platform_interface/pubspec.yaml b/packages/image_picker/image_picker_platform_interface/pubspec.yaml index 4ce1d2fc52f1..50d84f81d888 100644 --- a/packages/image_picker/image_picker_platform_interface/pubspec.yaml +++ b/packages/image_picker/image_picker_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/i 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.5.0 +version: 2.6.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart index 72ed363ef7ae..27d7016d8143 100644 --- a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart @@ -7,6 +7,8 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'package:image_picker_platform_interface/src/method_channel/method_channel_image_picker.dart'; +import 'package:image_picker_platform_interface/src/types/image_options.dart'; +import 'package:image_picker_platform_interface/src/types/multi_image_picker_options.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -244,6 +246,7 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), ], ); @@ -283,36 +286,43 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), isMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), isMethodCall('pickMultiImage', arguments: { 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': null, + 'requestFullMetadata': true, }), isMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': null, + 'requestFullMetadata': true, }), isMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': 70, + 'requestFullMetadata': true, }), isMethodCall('pickMultiImage', arguments: { 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': 70, + 'requestFullMetadata': true, }), isMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': 70, + 'requestFullMetadata': true, }), ], ); @@ -723,6 +733,7 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), ], ); @@ -762,36 +773,43 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), isMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), isMethodCall('pickMultiImage', arguments: { 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': null, + 'requestFullMetadata': true, }), isMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': null, + 'requestFullMetadata': true, }), isMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': 70, + 'requestFullMetadata': true, }), isMethodCall('pickMultiImage', arguments: { 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': 70, + 'requestFullMetadata': true, }), isMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': 70, + 'requestFullMetadata': true, }), ], ); @@ -1257,5 +1275,211 @@ void main() { ); }); }); + + group('#getMultiImageWithOptions', () { + test('calls the method correctly', () async { + returnValue = ['0', '1']; + await picker.getMultiImageWithOptions(); + + expect( + log, + [ + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('passes the width, height and imageQuality arguments correctly', + () async { + returnValue = ['0', '1']; + await picker.getMultiImageWithOptions(); + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(maxWidth: 10.0), + ), + ); + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(maxHeight: 10.0), + ), + ); + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions( + maxWidth: 10.0, + maxHeight: 20.0, + ), + ), + ); + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions( + maxWidth: 10.0, + imageQuality: 70, + ), + ), + ); + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions( + maxHeight: 10.0, + imageQuality: 70, + ), + ), + ); + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions( + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ), + ), + ); + + expect( + log, + [ + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'requestFullMetadata': true, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + 'requestFullMetadata': true, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + 'requestFullMetadata': true, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + 'requestFullMetadata': true, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + 'requestFullMetadata': true, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + 'requestFullMetadata': true, + }), + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('does not accept a negative width or height argument', () { + returnValue = ['0', '1']; + expect( + () => picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(maxWidth: -1.0), + ), + ), + throwsArgumentError, + ); + + expect( + () => picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(maxHeight: -1.0), + ), + ), + throwsArgumentError, + ); + }); + + test('does not accept an invalid imageQuality argument', () { + returnValue = ['0', '1']; + expect( + () => picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(imageQuality: -1), + ), + ), + throwsArgumentError, + ); + + expect( + () => picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(imageQuality: 101), + ), + ), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + picker.channel + .setMockMethodCallHandler((MethodCall methodCall) => null); + + expect(await picker.getMultiImage(), isNull); + expect(await picker.getMultiImage(), isNull); + }); + + test('Request full metadata argument defaults to true', () async { + returnValue = ['0', '1']; + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions(), + ); + + expect( + log, + [ + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('passes the request full metadata argument correctly', () async { + returnValue = ['0', '1']; + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(requestFullMetadata: false), + ), + ); + + expect( + log, + [ + isMethodCall('pickMultiImage', arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'requestFullMetadata': false, + }), + ], + ); + }); + }); }); } From 7f55fac531a01d0a132278087bbf33a2289bfa12 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 27 Jul 2022 10:09:46 -0400 Subject: [PATCH 549/844] Roll Flutter from fa39206a2a34 to e3d08fb6d325 (23 revisions) (#6147) * 2142b2ecf Roll Flutter Engine from cd7e2ec4037b to c31036f2381c (1 revision) (flutter/flutter#108299) * 178e44473 Revert "Add optional flag to determine assertiveness level in aria announcement for flutter web" (flutter/flutter#108262) * 8a7b35933 flutter update-packages --force-upgrade + analyzer fix (flutter/flutter#108198) * b3f5d3f85 Roll Flutter Engine from c31036f2381c to 54b0ac3059bf (1 revision) (flutter/flutter#108302) * 2f4299ad3 [flutter_tools] Remove unused parameter when connected DAP to VM Service (flutter/flutter#108285) * e74b9b5be Migrate InputDecorator to Material 3 (flutter/flutter#107943) * e3b851a63 Fix Tamil DateTime representation of AM/PM (flutter/flutter#108185) * 43d6e2b11 Roll Flutter Engine from 54b0ac3059bf to 705072522c00 (4 revisions) (flutter/flutter#108308) * 6929ad1c8 Roll Flutter Engine from 705072522c00 to 3964cf62cdf8 (1 revision) (flutter/flutter#108316) * 5f67b47e2 [iOS] Update template icons (flutter/flutter#107873) * 606954d11 Added iconSize parameter in ButtonStyle (flutter/flutter#108268) * b3814c7ff Roll Flutter Engine from 3964cf62cdf8 to 7f8925b1f6f3 (2 revisions) (flutter/flutter#108320) * ca6cecf03 Upgrade Gradle and AGP versions to 7.5/7.2 and migrate examples/tests (flutter/flutter#108197) * d155bc1ba Revert "Upgrade Gradle and AGP versions to 7.5/7.2 and migrate examples/tests (#108197)" (flutter/flutter#108349) * 4056d3ffd Explain the "patching" protocol in `KeyMessageManager.keyMessageHandler` and add an example (flutter/flutter#105280) * b035ef135 [flutter_tools] Remove more shuffles (flutter/flutter#107759) * be1485846 Roll Flutter Engine from 7f8925b1f6f3 to 8eca26d130a2 (1 revision) (flutter/flutter#108326) * 3eb638ff0 Roll Flutter Engine from 8eca26d130a2 to f1f9b4de82b6 (5 revisions) (flutter/flutter#108350) * 401b556e9 Roll Flutter Engine from f1f9b4de82b6 to 11d927ac3e9b (2 revisions) (flutter/flutter#108353) * 925bee92e Roll Flutter Engine from 11d927ac3e9b to 5dcaeae6561b (1 revision) (flutter/flutter#108356) * 5d31b07ed [flutter_tools] [dap] Ensure DAP sends app.stop/app.detach during terminate (flutter/flutter#108310) * c8b5d1093 Roll Flutter Engine from 5dcaeae6561b to 89e117b89c63 (1 revision) (flutter/flutter#108367) * e3d08fb6d Hide the debug banner in the PopupMenuButton example (flutter/flutter#108324) --- .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 b96fd39cb7b4..5fc0b4364801 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fa39206a2a34e94a01cbbff80477c0452d02d566 +e3d08fb6d325d778fe6bd02893356c30868789fb From 04e8d661b0a1a2878964aa53263529278ad92e35 Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Wed, 27 Jul 2022 08:21:04 -0700 Subject: [PATCH 550/844] [local_auth] Remove FingerprintManager uses and fix deprecations (#6059) --- .../local_auth_android/CHANGELOG.md | 4 + .../android/src/main/AndroidManifest.xml | 2 +- .../localauth/AuthenticationHelper.java | 10 +- .../plugins/localauth/LocalAuthPlugin.java | 121 ++++++-------- .../plugins/localauth/LocalAuthTest.java | 156 ++++++++++++++++-- .../local_auth_android/pubspec.yaml | 2 +- 6 files changed, 206 insertions(+), 89 deletions(-) diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 7eed97a04fac..f6d9da2c504c 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.8 + +* Removes usages of `FingerprintManager` and other `BiometricManager` deprecated method usages. + ## 1.0.7 * Updates gradle version to 7.2.1. diff --git a/packages/local_auth/local_auth_android/android/src/main/AndroidManifest.xml b/packages/local_auth/local_auth_android/android/src/main/AndroidManifest.xml index cb6cb985a986..63f75079e00d 100644 --- a/packages/local_auth/local_auth_android/android/src/main/AndroidManifest.xml +++ b/packages/local_auth/local_auth_android/android/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ - + diff --git a/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java index 2b825c6d1f31..c30f879d2c7f 100644 --- a/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java +++ b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/AuthenticationHelper.java @@ -20,6 +20,7 @@ import android.view.View; import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.biometric.BiometricManager; import androidx.biometric.BiometricPrompt; import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.DefaultLifecycleObserver; @@ -90,11 +91,17 @@ interface AuthCompletionHandler { .setConfirmationRequired((Boolean) call.argument("sensitiveTransaction")) .setConfirmationRequired((Boolean) call.argument("sensitiveTransaction")); + int allowedAuthenticators = + BiometricManager.Authenticators.BIOMETRIC_WEAK + | BiometricManager.Authenticators.BIOMETRIC_STRONG; + if (allowCredentials) { - promptBuilder.setDeviceCredentialAllowed(true); + allowedAuthenticators |= BiometricManager.Authenticators.DEVICE_CREDENTIAL; } else { promptBuilder.setNegativeButtonText((String) call.argument("cancelButton")); } + + promptBuilder.setAllowedAuthenticators(allowedAuthenticators); this.promptInfo = promptBuilder.build(); } @@ -141,7 +148,6 @@ public void onAuthenticationError(int errorCode, CharSequence errString) { break; case BiometricPrompt.ERROR_NO_SPACE: case BiometricPrompt.ERROR_NO_BIOMETRICS: - if (promptInfo.isDeviceCredentialAllowed()) return; if (call.argument("useErrorDialogs")) { showGoToSettingsDialog( (String) call.argument("biometricRequired"), diff --git a/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java index 3c5ecad16329..e8632c474030 100644 --- a/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java +++ b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java @@ -11,7 +11,6 @@ import android.app.KeyguardManager; import android.content.Context; import android.content.Intent; -import android.hardware.fingerprint.FingerprintManager; import android.os.Build; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -42,14 +41,14 @@ public class LocalAuthPlugin implements MethodCallHandler, FlutterPlugin, Activi private static final String CHANNEL_NAME = "plugins.flutter.io/local_auth_android"; private static final int LOCK_REQUEST_CODE = 221; private Activity activity; - private final AtomicBoolean authInProgress = new AtomicBoolean(false); private AuthenticationHelper authHelper; + @VisibleForTesting final AtomicBoolean authInProgress = new AtomicBoolean(false); + // These are null when not using v2 embedding. private MethodChannel channel; private Lifecycle lifecycle; private BiometricManager biometricManager; - private FingerprintManager fingerprintManager; private KeyguardManager keyguardManager; private Result lockRequestResult; private final PluginRegistry.ActivityResultListener resultListener = @@ -147,79 +146,45 @@ private void authenticate(MethodCall call, final Result result) { } authInProgress.set(true); - AuthCompletionHandler completionHandler = - new AuthCompletionHandler() { - @Override - public void onSuccess() { - authenticateSuccess(result); - } + AuthCompletionHandler completionHandler = createAuthCompletionHandler(result); - @Override - public void onFailure() { - authenticateFail(result); - } + boolean isBiometricOnly = call.argument("biometricOnly"); + boolean allowCredentials = !isBiometricOnly && canAuthenticateWithDeviceCredential(); - @Override - public void onError(String code, String error) { - if (authInProgress.compareAndSet(true, false)) { - result.error(code, error, null); - } - } - }; + sendAuthenticationRequest(call, completionHandler, allowCredentials); + return; + } - // if is biometricOnly try biometric prompt - might not work - boolean isBiometricOnly = call.argument("biometricOnly"); - if (isBiometricOnly) { - if (!canAuthenticateWithBiometrics()) { - if (!hasBiometricHardware()) { - completionHandler.onError("NoHardware", "No biometric hardware found"); - } - completionHandler.onError("NotEnrolled", "No biometrics enrolled on this device."); - return; + @VisibleForTesting + public AuthCompletionHandler createAuthCompletionHandler(final Result result) { + return new AuthCompletionHandler() { + @Override + public void onSuccess() { + authenticateSuccess(result); } - authHelper = - new AuthenticationHelper( - lifecycle, (FragmentActivity) activity, call, completionHandler, false); - authHelper.authenticate(); - return; - } - // API 29 and above - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - authHelper = - new AuthenticationHelper( - lifecycle, (FragmentActivity) activity, call, completionHandler, true); - authHelper.authenticate(); - return; - } + @Override + public void onFailure() { + authenticateFail(result); + } - // API 23 - 28 with fingerprint - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && fingerprintManager != null) { - if (fingerprintManager.hasEnrolledFingerprints()) { - authHelper = - new AuthenticationHelper( - lifecycle, (FragmentActivity) activity, call, completionHandler, false); - authHelper.authenticate(); - return; + @Override + public void onError(String code, String error) { + if (authInProgress.compareAndSet(true, false)) { + result.error(code, error, null); + } } - } + }; + } - // API 23 or higher with device credentials - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M - && keyguardManager != null - && keyguardManager.isDeviceSecure()) { - String title = call.argument("signInTitle"); - String reason = call.argument("localizedReason"); - Intent authIntent = keyguardManager.createConfirmDeviceCredentialIntent(title, reason); - - // save result for async response - lockRequestResult = result; - activity.startActivityForResult(authIntent, LOCK_REQUEST_CODE); - return; - } + @VisibleForTesting + public void sendAuthenticationRequest( + MethodCall call, AuthCompletionHandler completionHandler, boolean allowCredentials) { + authHelper = + new AuthenticationHelper( + lifecycle, (FragmentActivity) activity, call, completionHandler, allowCredentials); - // Unable to authenticate - result.error("NotSupported", "This device does not support required security features", null); + authHelper.authenticate(); } private void authenticateSuccess(Result result) { @@ -270,7 +235,8 @@ private void getEnrolledBiometrics(final Result result) { } } - private ArrayList getEnrolledBiometrics() { + @VisibleForTesting + public ArrayList getEnrolledBiometrics() { ArrayList biometrics = new ArrayList<>(); if (activity == null || activity.isFinishing()) { return biometrics; @@ -286,19 +252,28 @@ private ArrayList getEnrolledBiometrics() { return biometrics; } - private boolean isDeviceSupported() { + @VisibleForTesting + public boolean isDeviceSupported() { if (keyguardManager == null) return false; return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && keyguardManager.isDeviceSecure()); } private boolean canAuthenticateWithBiometrics() { if (biometricManager == null) return false; - return biometricManager.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS; + return biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) + == BiometricManager.BIOMETRIC_SUCCESS; } private boolean hasBiometricHardware() { if (biometricManager == null) return false; - return biometricManager.canAuthenticate() != BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE; + return biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) + != BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE; + } + + private boolean canAuthenticateWithDeviceCredential() { + if (biometricManager == null) return false; + return biometricManager.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL) + == BiometricManager.BIOMETRIC_SUCCESS; } private void isDeviceSupported(Result result) { @@ -320,10 +295,6 @@ private void setServicesFromActivity(Activity activity) { Context context = activity.getBaseContext(); biometricManager = BiometricManager.from(activity); keyguardManager = (KeyguardManager) context.getSystemService(KEYGUARD_SERVICE); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - fingerprintManager = - (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE); - } } @Override diff --git a/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java b/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java index 5fbda46b984f..0eaf31255317 100644 --- a/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java +++ b/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java @@ -4,16 +4,23 @@ package io.flutter.plugins.localauth; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Activity; +import android.app.NativeActivity; import android.content.Context; import androidx.biometric.BiometricManager; +import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.Lifecycle; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.embedding.engine.dart.DartExecutor; @@ -22,11 +29,139 @@ import io.flutter.embedding.engine.plugins.lifecycle.HiddenLifecycleReference; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugins.localauth.AuthenticationHelper.AuthCompletionHandler; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import org.junit.Test; +import org.mockito.ArgumentCaptor; public class LocalAuthTest { + @Test + public void authenticate_returnsErrorWhenAuthInProgress() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + plugin.authInProgress.set(true); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + plugin.onMethodCall(new MethodCall("authenticate", null), mockResult); + verify(mockResult).error("auth_in_progress", "Authentication in progress", null); + } + + @Test + public void authenticate_returnsErrorWithNoForegroundActivity() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + plugin.onMethodCall(new MethodCall("authenticate", null), mockResult); + verify(mockResult) + .error("no_activity", "local_auth plugin requires a foreground activity", null); + } + + @Test + public void authenticate_returnsErrorWhenActivityNotFragmentActivity() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + setPluginActivity(plugin, buildMockActivityWithContext(mock(NativeActivity.class))); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + plugin.onMethodCall(new MethodCall("authenticate", null), mockResult); + verify(mockResult) + .error( + "no_fragment_activity", + "local_auth plugin requires activity to be a FragmentActivity.", + null); + } + + @Test + public void authenticate_returnsErrorWhenDeviceNotSupported() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + setPluginActivity(plugin, buildMockActivityWithContext(mock(FragmentActivity.class))); + plugin.onMethodCall(new MethodCall("authenticate", null), mockResult); + assertFalse(plugin.authInProgress.get()); + verify(mockResult).error("NotAvailable", "Required security features not enabled", null); + } + + @Test + public void authenticate_properlyConfiguresBiometricOnlyAuthenticationRequest() { + final LocalAuthPlugin plugin = spy(new LocalAuthPlugin()); + setPluginActivity(plugin, buildMockActivityWithContext(mock(FragmentActivity.class))); + when(plugin.isDeviceSupported()).thenReturn(true); + + final BiometricManager mockBiometricManager = mock(BiometricManager.class); + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) + .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL)) + .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); + plugin.setBiometricManager(mockBiometricManager); + + ArgumentCaptor allowCredentialsCaptor = ArgumentCaptor.forClass(Boolean.class); + doNothing() + .when(plugin) + .sendAuthenticationRequest( + any(MethodCall.class), + any(AuthCompletionHandler.class), + allowCredentialsCaptor.capture()); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + HashMap arguments = new HashMap<>(); + arguments.put("biometricOnly", true); + + plugin.onMethodCall(new MethodCall("authenticate", arguments), mockResult); + assertFalse(allowCredentialsCaptor.getValue()); + } + + @Test + public void authenticate_properlyConfiguresBiometricAndDeviceCredentialAuthenticationRequest() { + final LocalAuthPlugin plugin = spy(new LocalAuthPlugin()); + setPluginActivity(plugin, buildMockActivityWithContext(mock(FragmentActivity.class))); + when(plugin.isDeviceSupported()).thenReturn(true); + + final BiometricManager mockBiometricManager = mock(BiometricManager.class); + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) + .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL)) + .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); + plugin.setBiometricManager(mockBiometricManager); + + ArgumentCaptor allowCredentialsCaptor = ArgumentCaptor.forClass(Boolean.class); + doNothing() + .when(plugin) + .sendAuthenticationRequest( + any(MethodCall.class), + any(AuthCompletionHandler.class), + allowCredentialsCaptor.capture()); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + HashMap arguments = new HashMap<>(); + arguments.put("biometricOnly", false); + + plugin.onMethodCall(new MethodCall("authenticate", arguments), mockResult); + assertTrue(allowCredentialsCaptor.getValue()); + } + + @Test + public void authenticate_properlyConfiguresDeviceCredentialOnlyAuthenticationRequest() { + final LocalAuthPlugin plugin = spy(new LocalAuthPlugin()); + setPluginActivity(plugin, buildMockActivityWithContext(mock(FragmentActivity.class))); + when(plugin.isDeviceSupported()).thenReturn(true); + + final BiometricManager mockBiometricManager = mock(BiometricManager.class); + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) + .thenReturn(BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED); + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL)) + .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); + plugin.setBiometricManager(mockBiometricManager); + + ArgumentCaptor allowCredentialsCaptor = ArgumentCaptor.forClass(Boolean.class); + doNothing() + .when(plugin) + .sendAuthenticationRequest( + any(MethodCall.class), + any(AuthCompletionHandler.class), + allowCredentialsCaptor.capture()); + final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + HashMap arguments = new HashMap<>(); + arguments.put("biometricOnly", false); + + plugin.onMethodCall(new MethodCall("authenticate", arguments), mockResult); + assertTrue(allowCredentialsCaptor.getValue()); + } + @Test public void isDeviceSupportedReturnsFalse() { final LocalAuthPlugin plugin = new LocalAuthPlugin(); @@ -40,7 +175,7 @@ public void deviceSupportsBiometrics_returnsTrueForPresentNonEnrolledBiometrics( final LocalAuthPlugin plugin = new LocalAuthPlugin(); final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); final BiometricManager mockBiometricManager = mock(BiometricManager.class); - when(mockBiometricManager.canAuthenticate()) + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) .thenReturn(BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED); plugin.setBiometricManager(mockBiometricManager); plugin.onMethodCall(new MethodCall("deviceSupportsBiometrics", null), mockResult); @@ -52,7 +187,8 @@ public void deviceSupportsBiometrics_returnsTrueForPresentEnrolledBiometrics() { final LocalAuthPlugin plugin = new LocalAuthPlugin(); final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); final BiometricManager mockBiometricManager = mock(BiometricManager.class); - when(mockBiometricManager.canAuthenticate()).thenReturn(BiometricManager.BIOMETRIC_SUCCESS); + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) + .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); plugin.setBiometricManager(mockBiometricManager); plugin.onMethodCall(new MethodCall("deviceSupportsBiometrics", null), mockResult); verify(mockResult).success(true); @@ -63,7 +199,7 @@ public void deviceSupportsBiometrics_returnsFalseForNoBiometricHardware() { final LocalAuthPlugin plugin = new LocalAuthPlugin(); final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); final BiometricManager mockBiometricManager = mock(BiometricManager.class); - when(mockBiometricManager.canAuthenticate()) + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) .thenReturn(BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE); plugin.setBiometricManager(mockBiometricManager); plugin.onMethodCall(new MethodCall("deviceSupportsBiometrics", null), mockResult); @@ -87,6 +223,7 @@ public void onDetachedFromActivity_ShouldReleaseActivity() { Context mockContext = mock(Context.class); when(mockActivity.getBaseContext()).thenReturn(mockContext); + when(mockActivity.getApplicationContext()).thenReturn(mockContext); final HiddenLifecycleReference mockLifecycleReference = mock(HiddenLifecycleReference.class); when(mockActivityBinding.getLifecycle()).thenReturn(mockLifecycleReference); @@ -124,7 +261,7 @@ public void getEnrolledBiometrics_shouldReturnError_whenNoActivity() { public void getEnrolledBiometrics_shouldReturnError_whenFinishingActivity() { final LocalAuthPlugin plugin = new LocalAuthPlugin(); final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); - final Activity mockActivity = buildMockActivity(); + final Activity mockActivity = buildMockActivityWithContext(mock(Activity.class)); when(mockActivity.isFinishing()).thenReturn(true); setPluginActivity(plugin, mockActivity); @@ -136,7 +273,7 @@ public void getEnrolledBiometrics_shouldReturnError_whenFinishingActivity() { @Test public void getEnrolledBiometrics_shouldReturnEmptyList_withoutHardwarePresent() { final LocalAuthPlugin plugin = new LocalAuthPlugin(); - setPluginActivity(plugin, buildMockActivity()); + setPluginActivity(plugin, buildMockActivityWithContext(mock(Activity.class))); final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); final BiometricManager mockBiometricManager = mock(BiometricManager.class); when(mockBiometricManager.canAuthenticate(anyInt())) @@ -150,7 +287,7 @@ public void getEnrolledBiometrics_shouldReturnEmptyList_withoutHardwarePresent() @Test public void getEnrolledBiometrics_shouldReturnEmptyList_withNoMethodsEnrolled() { final LocalAuthPlugin plugin = new LocalAuthPlugin(); - setPluginActivity(plugin, buildMockActivity()); + setPluginActivity(plugin, buildMockActivityWithContext(mock(Activity.class))); final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); final BiometricManager mockBiometricManager = mock(BiometricManager.class); when(mockBiometricManager.canAuthenticate(anyInt())) @@ -164,7 +301,7 @@ public void getEnrolledBiometrics_shouldReturnEmptyList_withNoMethodsEnrolled() @Test public void getEnrolledBiometrics_shouldOnlyAddEnrolledBiometrics() { final LocalAuthPlugin plugin = new LocalAuthPlugin(); - setPluginActivity(plugin, buildMockActivity()); + setPluginActivity(plugin, buildMockActivityWithContext(mock(Activity.class))); final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); final BiometricManager mockBiometricManager = mock(BiometricManager.class); when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) @@ -186,7 +323,7 @@ public void getEnrolledBiometrics_shouldOnlyAddEnrolledBiometrics() { @Test public void getEnrolledBiometrics_shouldAddStrongBiometrics() { final LocalAuthPlugin plugin = new LocalAuthPlugin(); - setPluginActivity(plugin, buildMockActivity()); + setPluginActivity(plugin, buildMockActivityWithContext(mock(Activity.class))); final MethodChannel.Result mockResult = mock(MethodChannel.Result.class); final BiometricManager mockBiometricManager = mock(BiometricManager.class); when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) @@ -206,8 +343,7 @@ public void getEnrolledBiometrics_shouldAddStrongBiometrics() { }); } - private Activity buildMockActivity() { - final Activity mockActivity = mock(Activity.class); + private Activity buildMockActivityWithContext(Activity mockActivity) { final Context mockContext = mock(Context.class); when(mockActivity.getBaseContext()).thenReturn(mockContext); when(mockActivity.getApplicationContext()).thenReturn(mockContext); diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index 3987d8ea4118..cfa4970e82d6 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.7 +version: 1.0.8 environment: sdk: ">=2.14.0 <3.0.0" From 8de876434eb4d1cd514e7f4b06c27edc95d587c1 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 27 Jul 2022 11:23:05 -0400 Subject: [PATCH 551/844] [webview_flutter_wkwebview] Implementation of WebKitWebViewController for WebKit (#6105) --- .../lib/src/v4/src/webkit_proxy.dart | 47 ++ .../src/v4/src/webkit_webview_controller.dart | 354 +++++++++ .../src/v4/src/webkit_webview_platform.dart | 17 + .../lib/src/v4/webview_flutter_wkwebview.dart | 7 + .../v4/webkit_webview_controller_test.dart | 746 ++++++++++++++++++ .../webkit_webview_controller_test.mocks.dart | 414 ++++++++++ 6 files changed, 1585 insertions(+) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_proxy.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_controller.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_platform.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/webview_flutter_wkwebview.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.mocks.dart diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_proxy.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_proxy.dart new file mode 100644 index 000000000000..48e6faf9abd7 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_proxy.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 '../../foundation/foundation.dart'; +import '../../web_kit/web_kit.dart'; + +/// Handles constructing objects and calling static methods for the WebKit +/// native library. +/// +/// This class provides dependency injection for the implementations of the +/// platform interface classes. Improving the ease of unit testing and/or +/// overriding the underlying WebKit classes. +/// +/// By default each function calls the default constructor of the WebKit class +/// it intends to return. +class WebKitProxy { + /// Constructs a [WebKitProxy]. + const WebKitProxy({ + this.createWebView = WKWebView.new, + this.createWebViewConfiguration = WKWebViewConfiguration.new, + this.createScriptMessageHandler = WKScriptMessageHandler.new, + }); + + /// Constructs a [WKWebView]. + final WKWebView Function( + WKWebViewConfiguration configuration, { + void Function( + String keyPath, + NSObject object, + Map change, + ) + observeValue, + }) createWebView; + + /// Constructs a [WKWebViewConfiguration]. + final WKWebViewConfiguration Function() createWebViewConfiguration; + + /// Constructs a [WKScriptMessageHandler]. + final WKScriptMessageHandler Function({ + required void Function( + WKUserContentController userContentController, + WKScriptMessage message, + ) + didReceiveScriptMessage, + }) createScriptMessageHandler; +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_controller.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_controller.dart new file mode 100644 index 000000000000..9d76b5cda04d --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_controller.dart @@ -0,0 +1,354 @@ +// 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:math'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:path/path.dart' as path; +import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; + +import '../../common/weak_reference_utils.dart'; +import '../../foundation/foundation.dart'; +import '../../web_kit/web_kit.dart'; +import 'webkit_proxy.dart'; + +/// Object specifying creation parameters for a [WebKitWebViewController]. +@immutable +class WebKitWebViewControllerCreationParams + extends PlatformWebViewControllerCreationParams { + /// Constructs a [WebKitWebViewControllerCreationParams]. + WebKitWebViewControllerCreationParams({ + @visibleForTesting WebKitProxy webKitProxy = const WebKitProxy(), + }) : _configuration = webKitProxy.createWebViewConfiguration(); + + /// Constructs a [WebKitWebViewControllerCreationParams] using a + /// [PlatformWebViewControllerCreationParams]. + WebKitWebViewControllerCreationParams.fromPlatformWebViewControllerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebViewControllerCreationParams params, { + @visibleForTesting WebKitProxy webKitProxy = const WebKitProxy(), + }) : this(webKitProxy: webKitProxy); + + final WKWebViewConfiguration _configuration; +} + +/// An implementation of [PlatformWebViewController] with the WebKit api. +class WebKitWebViewController extends PlatformWebViewController { + /// Constructs a [WebKitWebViewController]. + WebKitWebViewController( + PlatformWebViewControllerCreationParams params, { + @visibleForTesting WebKitProxy webKitProxy = const WebKitProxy(), + }) : super.implementation(params is WebKitWebViewControllerCreationParams + ? params + : WebKitWebViewControllerCreationParams + .fromPlatformWebViewControllerCreationParams(params)) { + _webView = webKitProxy.createWebView( + (params as WebKitWebViewControllerCreationParams)._configuration); + } + + late final WKWebView _webView; + + final Map _javaScriptChannelParams = + {}; + + bool _zoomEnabled = true; + + @override + Future loadFile(String absoluteFilePath) { + return _webView.loadFileUrl( + absoluteFilePath, + readAccessUrl: path.dirname(absoluteFilePath), + ); + } + + @override + Future loadFlutterAsset(String key) { + assert(key.isNotEmpty); + return _webView.loadFlutterAsset(key); + } + + @override + Future loadHtmlString(String html, {String? baseUrl}) { + return _webView.loadHtmlString(html, baseUrl: baseUrl); + } + + @override + Future loadRequest(LoadRequestParams params) { + if (!params.uri.hasScheme) { + throw ArgumentError( + 'LoadRequestParams#uri is required to have a scheme.', + ); + } + + return _webView.loadRequest(NSUrlRequest( + url: params.uri.toString(), + allHttpHeaderFields: params.headers, + httpMethod: describeEnum(params.method), + httpBody: params.body, + )); + } + + @override + Future addJavaScriptChannel( + JavaScriptChannelParams javaScriptChannelParams, + ) { + final WebKitJavaScriptChannelParams webKitParams = + javaScriptChannelParams is WebKitJavaScriptChannelParams + ? javaScriptChannelParams + : WebKitJavaScriptChannelParams.fromJavaScriptChannelParams( + javaScriptChannelParams); + + _javaScriptChannelParams[webKitParams.name] = webKitParams; + + final String wrapperSource = + 'window.${webKitParams.name} = webkit.messageHandlers.${webKitParams.name};'; + final WKUserScript wrapperScript = WKUserScript( + wrapperSource, + WKUserScriptInjectionTime.atDocumentStart, + isMainFrameOnly: false, + ); + _webView.configuration.userContentController.addUserScript(wrapperScript); + return _webView.configuration.userContentController.addScriptMessageHandler( + webKitParams._messageHandler, + webKitParams.name, + ); + } + + @override + Future removeJavaScriptChannel(String javaScriptChannelName) async { + assert(javaScriptChannelName.isNotEmpty); + if (!_javaScriptChannelParams.containsKey(javaScriptChannelName)) { + return; + } + await _resetUserScripts(removedJavaScriptChannel: javaScriptChannelName); + } + + @override + Future currentUrl() => _webView.getUrl(); + + @override + Future canGoBack() => _webView.canGoBack(); + + @override + Future canGoForward() => _webView.canGoForward(); + + @override + Future goBack() => _webView.goBack(); + + @override + Future goForward() => _webView.goForward(); + + @override + Future reload() => _webView.reload(); + + @override + Future clearCache() { + return _webView.configuration.websiteDataStore.removeDataOfTypes( + { + WKWebsiteDataType.memoryCache, + WKWebsiteDataType.diskCache, + WKWebsiteDataType.offlineWebApplicationCache, + }, + DateTime.fromMillisecondsSinceEpoch(0), + ); + } + + @override + Future clearLocalStorage() { + return _webView.configuration.websiteDataStore.removeDataOfTypes( + {WKWebsiteDataType.localStorage}, + DateTime.fromMillisecondsSinceEpoch(0), + ); + } + + @override + Future runJavaScript(String javaScript) async { + try { + await _webView.evaluateJavaScript(javaScript); + } on PlatformException catch (exception) { + // WebKit will throw an error when the type of the evaluated value is + // unsupported. This also goes for `null` and `undefined` on iOS 14+. For + // example, when running a void function. For ease of use, this specific + // error is ignored when no return value is expected. + if (exception.details is! NSError || + exception.details.code != + WKErrorCode.javaScriptResultTypeIsUnsupported) { + rethrow; + } + } + } + + @override + Future runJavaScriptReturningResult(String javaScript) async { + final Object? result = await _webView.evaluateJavaScript(javaScript); + if (result == null) { + throw ArgumentError( + 'Result of JavaScript execution returned a `null` value. ' + 'Use `runJavascript` when expecting a null return value.', + ); + } + return result.toString(); + } + + /// Controls whether inline playback of HTML5 videos is allowed. + Future setAllowsInlineMediaPlayback(bool allow) { + return _webView.configuration.setAllowsInlineMediaPlayback(allow); + } + + @override + Future getTitle() => _webView.getTitle(); + + @override + Future scrollTo(int x, int y) { + return _webView.scrollView.setContentOffset(Point( + x.toDouble(), + y.toDouble(), + )); + } + + @override + Future scrollBy(int x, int y) { + return _webView.scrollView.scrollBy(Point( + x.toDouble(), + y.toDouble(), + )); + } + + @override + Future> getScrollPosition() async { + final Point offset = await _webView.scrollView.getContentOffset(); + return Point(offset.x.round(), offset.y.round()); + } + + // TODO(bparrishMines): This is unique to iOS. Override should be removed if + // this is removed from the platform interface before webview_flutter version + // 4.0.0. + @override + Future enableGestureNavigation(bool enabled) { + return _webView.setAllowsBackForwardNavigationGestures(enabled); + } + + @override + Future setBackgroundColor(Color color) { + return Future.wait(>[ + _webView.scrollView.setBackgroundColor(color), + _webView.setOpaque(false), + _webView.setBackgroundColor(Colors.transparent), + ]); + } + + @override + Future setJavaScriptMode(JavaScriptMode javaScriptMode) { + switch (javaScriptMode) { + case JavaScriptMode.disabled: + return _webView.configuration.preferences.setJavaScriptEnabled(false); + case JavaScriptMode.unrestricted: + return _webView.configuration.preferences.setJavaScriptEnabled(true); + } + } + + @override + Future setUserAgent(String? userAgent) { + return _webView.setCustomUserAgent(userAgent); + } + + @override + Future enableZoom(bool enabled) async { + if (_zoomEnabled == enabled) { + return; + } + + _zoomEnabled = enabled; + if (enabled) { + await _resetUserScripts(); + } else { + await _disableZoom(); + } + } + + Future _disableZoom() { + const WKUserScript userScript = WKUserScript( + "var meta = document.createElement('meta');\n" + "meta.name = 'viewport';\n" + "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, " + "user-scalable=no';\n" + "var head = document.getElementsByTagName('head')[0];head.appendChild(meta);", + WKUserScriptInjectionTime.atDocumentEnd, + isMainFrameOnly: true, + ); + return _webView.configuration.userContentController + .addUserScript(userScript); + } + + // WKWebView does not support removing a single user script, so all user + // scripts and all message handlers are removed instead. And the JavaScript + // channels that shouldn't be removed are re-registered. Note that this + // workaround could interfere with exposing support for custom scripts from + // applications. + Future _resetUserScripts({String? removedJavaScriptChannel}) async { + _webView.configuration.userContentController.removeAllUserScripts(); + // TODO(bparrishMines): This can be replaced with + // `removeAllScriptMessageHandlers` once Dart supports runtime version + // checking. (e.g. The equivalent to @availability in Objective-C.) + _javaScriptChannelParams.keys.forEach( + _webView.configuration.userContentController.removeScriptMessageHandler, + ); + + _javaScriptChannelParams.remove(removedJavaScriptChannel); + + await Future.wait(>[ + for (JavaScriptChannelParams params in _javaScriptChannelParams.values) + addJavaScriptChannel(params), + // Zoom is disabled with a WKUserScript, so this adds it back if it was + // removed above. + if (!_zoomEnabled) _disableZoom(), + ]); + } +} + +/// An implementation of [JavaScriptChannelParams] with the WebKit api. +/// +/// See [WebKitWebViewController.addJavaScriptChannel]. +@immutable +class WebKitJavaScriptChannelParams extends JavaScriptChannelParams { + /// Constructs a [WebKitJavaScriptChannelParams]. + WebKitJavaScriptChannelParams({ + required super.name, + required super.onMessageReceived, + @visibleForTesting WebKitProxy webKitProxy = const WebKitProxy(), + }) : assert(name.isNotEmpty), + _messageHandler = webKitProxy.createScriptMessageHandler( + didReceiveScriptMessage: withWeakRefenceTo( + onMessageReceived, + (WeakReference weakReference) { + return ( + WKUserContentController controller, + WKScriptMessage message, + ) { + if (weakReference.target != null) { + weakReference.target!( + JavaScriptMessage(message: message.body!.toString()), + ); + } + }; + }, + ), + ); + + /// Constructs a [WebKitJavaScriptChannelParams] using a + /// [JavaScriptChannelParams]. + WebKitJavaScriptChannelParams.fromJavaScriptChannelParams( + JavaScriptChannelParams params, { + @visibleForTesting WebKitProxy webKitProxy = const WebKitProxy(), + }) : this( + name: params.name, + onMessageReceived: params.onMessageReceived, + webKitProxy: webKitProxy, + ); + + final WKScriptMessageHandler _messageHandler; +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_platform.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_platform.dart new file mode 100644 index 000000000000..b808086d56f8 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_platform.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 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; + +import 'webkit_webview_controller.dart'; + +/// Implementation of [WebViewPlatform] using the WebKit Api. +class WebKitWebViewPlatform extends WebViewPlatform { + @override + WebKitWebViewController createPlatformWebViewController( + PlatformWebViewControllerCreationParams params, + ) { + return WebKitWebViewController(params); + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/webview_flutter_wkwebview.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/webview_flutter_wkwebview.dart new file mode 100644 index 000000000000..2a593fb5a088 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/webview_flutter_wkwebview.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. + +library webview_flutter_wkwebview; + +export 'src/webkit_webview_controller.dart'; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart new file mode 100644 index 000000000000..cf5da17dd6e8 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart @@ -0,0 +1,746 @@ +// 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:math'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; +import 'package:webview_flutter_wkwebview/src/ui_kit/ui_kit.dart'; +import 'package:webview_flutter_wkwebview/src/v4/src/webkit_proxy.dart'; +import 'package:webview_flutter_wkwebview/src/v4/webview_flutter_wkwebview.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; + +import 'webkit_webview_controller_test.mocks.dart'; + +@GenerateMocks([ + UIScrollView, + WKPreferences, + WKUserContentController, + WKWebsiteDataStore, + WKWebView, + WKWebViewConfiguration, +]) +void main() { + WidgetsFlutterBinding.ensureInitialized(); + + group('WebKitWebViewController', () { + WebKitWebViewController createControllerWithMocks({ + MockUIScrollView? mockScrollView, + MockWKPreferences? mockPreferences, + MockWKUserContentController? mockUserContentController, + MockWKWebsiteDataStore? mockWebsiteDataStore, + MockWKWebView Function( + WKWebViewConfiguration configuration, { + void Function( + String keyPath, + NSObject object, + Map change, + )? + observeValue, + })? + createMockWebView, + MockWKWebViewConfiguration? mockWebViewConfiguration, + }) { + final MockWKWebViewConfiguration nonNullMockWebViewConfiguration = + mockWebViewConfiguration ?? MockWKWebViewConfiguration(); + late final MockWKWebView nonNullMockWebView; + + final PlatformWebViewControllerCreationParams controllerCreationParams = + WebKitWebViewControllerCreationParams( + webKitProxy: WebKitProxy( + createWebViewConfiguration: () => nonNullMockWebViewConfiguration, + ), + ); + + final WebKitWebViewController controller = WebKitWebViewController( + controllerCreationParams, + webKitProxy: WebKitProxy( + createWebView: ( + _, { + void Function( + String keyPath, + NSObject object, + Map change, + )? + observeValue, + }) { + nonNullMockWebView = createMockWebView == null + ? MockWKWebView() + : createMockWebView( + nonNullMockWebViewConfiguration, + observeValue: observeValue, + ); + return nonNullMockWebView; + }, + ), + ); + + when(nonNullMockWebView.scrollView) + .thenReturn(mockScrollView ?? MockUIScrollView()); + when(nonNullMockWebView.configuration) + .thenReturn(nonNullMockWebViewConfiguration); + + when(nonNullMockWebViewConfiguration.preferences) + .thenReturn(mockPreferences ?? MockWKPreferences()); + when(nonNullMockWebViewConfiguration.userContentController).thenReturn( + mockUserContentController ?? MockWKUserContentController()); + when(nonNullMockWebViewConfiguration.websiteDataStore) + .thenReturn(mockWebsiteDataStore ?? MockWKWebsiteDataStore()); + + return controller; + } + + test('loadFile', () async { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + await controller.loadFile('/path/to/file.html'); + verify(mockWebView.loadFileUrl( + '/path/to/file.html', + readAccessUrl: '/path/to', + )); + }); + + test('loadFlutterAsset', () async { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + await controller.loadFlutterAsset('test_assets/index.html'); + verify(mockWebView.loadFlutterAsset('test_assets/index.html')); + }); + + test('loadHtmlString', () async { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + const String htmlString = 'Test data.'; + await controller.loadHtmlString(htmlString, baseUrl: 'baseUrl'); + + verify(mockWebView.loadHtmlString( + 'Test data.', + baseUrl: 'baseUrl', + )); + }); + + group('loadRequest', () { + test('Throws ArgumentError for empty scheme', () async { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + expect( + () async => await controller.loadRequest( + LoadRequestParams( + uri: Uri.parse('www.google.com'), + method: LoadRequestMethod.get, + headers: const {}, + ), + ), + throwsA(isA()), + ); + }); + + test('GET without headers', () async { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + await controller.loadRequest( + LoadRequestParams( + uri: Uri.parse('https://www.google.com'), + method: LoadRequestMethod.get, + headers: const {}, + ), + ); + + final NSUrlRequest request = verify(mockWebView.loadRequest(captureAny)) + .captured + .single as NSUrlRequest; + expect(request.url, 'https://www.google.com'); + expect(request.allHttpHeaderFields, {}); + expect(request.httpMethod, 'get'); + }); + + test('GET with headers', () async { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + await controller.loadRequest( + LoadRequestParams( + uri: Uri.parse('https://www.google.com'), + method: LoadRequestMethod.get, + headers: const {'a': 'header'}, + ), + ); + + final NSUrlRequest request = verify(mockWebView.loadRequest(captureAny)) + .captured + .single as NSUrlRequest; + expect(request.url, 'https://www.google.com'); + expect(request.allHttpHeaderFields, {'a': 'header'}); + expect(request.httpMethod, 'get'); + }); + + test('POST without body', () async { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + await controller.loadRequest(LoadRequestParams( + uri: Uri.parse('https://www.google.com'), + method: LoadRequestMethod.post, + headers: const {}, + )); + + final NSUrlRequest request = verify(mockWebView.loadRequest(captureAny)) + .captured + .single as NSUrlRequest; + expect(request.url, 'https://www.google.com'); + expect(request.httpMethod, 'post'); + }); + + test('POST with body', () async { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + await controller.loadRequest(LoadRequestParams( + uri: Uri.parse('https://www.google.com'), + method: LoadRequestMethod.post, + body: Uint8List.fromList('Test Body'.codeUnits), + headers: const {}, + )); + + final NSUrlRequest request = verify(mockWebView.loadRequest(captureAny)) + .captured + .single as NSUrlRequest; + expect(request.url, 'https://www.google.com'); + expect(request.httpMethod, 'post'); + expect( + request.httpBody, + Uint8List.fromList('Test Body'.codeUnits), + ); + }); + }); + + test('canGoBack', () { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + when(mockWebView.canGoBack()).thenAnswer( + (_) => Future.value(false), + ); + expect(controller.canGoBack(), completion(false)); + }); + + test('canGoForward', () { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + when(mockWebView.canGoForward()).thenAnswer( + (_) => Future.value(true), + ); + expect(controller.canGoForward(), completion(true)); + }); + + test('goBack', () async { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + await controller.goBack(); + verify(mockWebView.goBack()); + }); + + test('goForward', () async { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + await controller.goForward(); + verify(mockWebView.goForward()); + }); + + test('reload', () async { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + await controller.reload(); + verify(mockWebView.reload()); + }); + + test('enableGestureNavigation', () async { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + await controller.enableGestureNavigation(true); + verify(mockWebView.setAllowsBackForwardNavigationGestures(true)); + }); + + test('runJavaScriptReturningResult', () { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value('returnString'), + ); + expect( + controller.runJavaScriptReturningResult('runJavaScript'), + completion('returnString'), + ); + }); + + test('runJavaScriptReturningResult throws error on null return value', () { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value(null), + ); + expect( + () => controller.runJavaScriptReturningResult('runJavaScript'), + throwsArgumentError, + ); + }); + + test('runJavaScript', () { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( + (_) => Future.value('returnString'), + ); + expect( + controller.runJavaScript('runJavaScript'), + completes, + ); + }); + + test('runJavaScript ignores exception with unsupported javaScript type', + () { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + when(mockWebView.evaluateJavaScript('runJavaScript')) + .thenThrow(PlatformException( + code: '', + details: const NSError( + code: WKErrorCode.javaScriptResultTypeIsUnsupported, + domain: '', + localizedDescription: '', + ), + )); + expect( + controller.runJavaScript('runJavaScript'), + completes, + ); + }); + + test('getTitle', () async { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + when(mockWebView.getTitle()) + .thenAnswer((_) => Future.value('Web Title')); + expect(controller.getTitle(), completion('Web Title')); + }); + + test('currentUrl', () { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + when(mockWebView.getUrl()) + .thenAnswer((_) => Future.value('myUrl.com')); + expect(controller.currentUrl(), completion('myUrl.com')); + }); + + test('scrollTo', () async { + final MockUIScrollView mockScrollView = MockUIScrollView(); + + final WebKitWebViewController controller = createControllerWithMocks( + mockScrollView: mockScrollView, + ); + + await controller.scrollTo(2, 4); + verify(mockScrollView.setContentOffset(const Point(2.0, 4.0))); + }); + + test('scrollBy', () async { + final MockUIScrollView mockScrollView = MockUIScrollView(); + + final WebKitWebViewController controller = createControllerWithMocks( + mockScrollView: mockScrollView, + ); + + await controller.scrollBy(2, 4); + verify(mockScrollView.scrollBy(const Point(2.0, 4.0))); + }); + + test('getScrollPosition', () { + final MockUIScrollView mockScrollView = MockUIScrollView(); + + final WebKitWebViewController controller = createControllerWithMocks( + mockScrollView: mockScrollView, + ); + + when(mockScrollView.getContentOffset()).thenAnswer( + (_) => Future>.value(const Point(8.0, 16.0)), + ); + expect( + controller.getScrollPosition(), + completion(const Point(8.0, 16.0)), + ); + }); + + test('disable zoom', () async { + final MockWKUserContentController mockUserContentController = + MockWKUserContentController(); + + final WebKitWebViewController controller = createControllerWithMocks( + mockUserContentController: mockUserContentController, + ); + + await controller.enableZoom(false); + + final WKUserScript zoomScript = + verify(mockUserContentController.addUserScript(captureAny)) + .captured + .first as WKUserScript; + expect(zoomScript.isMainFrameOnly, isTrue); + expect(zoomScript.injectionTime, WKUserScriptInjectionTime.atDocumentEnd); + expect( + zoomScript.source, + "var meta = document.createElement('meta');\n" + "meta.name = 'viewport';\n" + "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, " + "user-scalable=no';\n" + "var head = document.getElementsByTagName('head')[0];head.appendChild(meta);", + ); + }); + + test('setBackgroundColor', () async { + final MockWKWebView mockWebView = MockWKWebView(); + final MockUIScrollView mockScrollView = MockUIScrollView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + mockScrollView: mockScrollView, + ); + + controller.setBackgroundColor(Colors.red); + + verify(mockWebView.setOpaque(false)); + verify(mockWebView.setBackgroundColor(Colors.transparent)); + verify(mockScrollView.setBackgroundColor(Colors.red)); + }); + + test('userAgent', () async { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + await controller.setUserAgent('MyUserAgent'); + verify(mockWebView.setCustomUserAgent('MyUserAgent')); + }); + + test('enable JavaScript', () async { + final MockWKPreferences mockPreferences = MockWKPreferences(); + + final WebKitWebViewController controller = createControllerWithMocks( + mockPreferences: mockPreferences, + ); + + await controller.setJavaScriptMode(JavaScriptMode.unrestricted); + + verify(mockPreferences.setJavaScriptEnabled(true)); + }); + + test('disable JavaScript', () async { + final MockWKPreferences mockPreferences = MockWKPreferences(); + + final WebKitWebViewController controller = createControllerWithMocks( + mockPreferences: mockPreferences, + ); + + await controller.setJavaScriptMode(JavaScriptMode.disabled); + + verify(mockPreferences.setJavaScriptEnabled(false)); + }); + + test('clearCache', () { + final MockWKWebsiteDataStore mockWebsiteDataStore = + MockWKWebsiteDataStore(); + + final WebKitWebViewController controller = createControllerWithMocks( + mockWebsiteDataStore: mockWebsiteDataStore, + ); + when( + mockWebsiteDataStore.removeDataOfTypes( + { + WKWebsiteDataType.memoryCache, + WKWebsiteDataType.diskCache, + WKWebsiteDataType.offlineWebApplicationCache, + }, + DateTime.fromMillisecondsSinceEpoch(0), + ), + ).thenAnswer((_) => Future.value(false)); + + expect(controller.clearCache(), completes); + }); + + test('clearLocalStorage', () { + final MockWKWebsiteDataStore mockWebsiteDataStore = + MockWKWebsiteDataStore(); + + final WebKitWebViewController controller = createControllerWithMocks( + mockWebsiteDataStore: mockWebsiteDataStore, + ); + when( + mockWebsiteDataStore.removeDataOfTypes( + {WKWebsiteDataType.localStorage}, + DateTime.fromMillisecondsSinceEpoch(0), + ), + ).thenAnswer((_) => Future.value(false)); + + expect(controller.clearLocalStorage(), completes); + }); + + test('addJavaScriptChannel', () async { + final WebKitProxy webKitProxy = WebKitProxy( + createScriptMessageHandler: ({ + required void Function( + WKUserContentController userContentController, + WKScriptMessage message, + ) + didReceiveScriptMessage, + }) { + return WKScriptMessageHandler.detached( + didReceiveScriptMessage: didReceiveScriptMessage, + ); + }, + ); + + final WebKitJavaScriptChannelParams javaScriptChannelParams = + WebKitJavaScriptChannelParams( + name: 'name', + onMessageReceived: (JavaScriptMessage message) {}, + webKitProxy: webKitProxy, + ); + + final MockWKUserContentController mockUserContentController = + MockWKUserContentController(); + + final WebKitWebViewController controller = createControllerWithMocks( + mockUserContentController: mockUserContentController, + ); + + await controller.addJavaScriptChannel(javaScriptChannelParams); + verify(mockUserContentController.addScriptMessageHandler( + argThat(isA()), + 'name', + )); + + final WKUserScript userScript = + verify(mockUserContentController.addUserScript(captureAny)) + .captured + .single as WKUserScript; + expect(userScript.source, 'window.name = webkit.messageHandlers.name;'); + expect( + userScript.injectionTime, + WKUserScriptInjectionTime.atDocumentStart, + ); + }); + + test('removeJavaScriptChannel', () async { + final WebKitProxy webKitProxy = WebKitProxy( + createScriptMessageHandler: ({ + required void Function( + WKUserContentController userContentController, + WKScriptMessage message, + ) + didReceiveScriptMessage, + }) { + return WKScriptMessageHandler.detached( + didReceiveScriptMessage: didReceiveScriptMessage, + ); + }, + ); + + final WebKitJavaScriptChannelParams javaScriptChannelParams = + WebKitJavaScriptChannelParams( + name: 'name', + onMessageReceived: (JavaScriptMessage message) {}, + webKitProxy: webKitProxy, + ); + + final MockWKUserContentController mockUserContentController = + MockWKUserContentController(); + + final WebKitWebViewController controller = createControllerWithMocks( + mockUserContentController: mockUserContentController, + ); + + await controller.addJavaScriptChannel(javaScriptChannelParams); + reset(mockUserContentController); + + await controller.removeJavaScriptChannel('name'); + + verify(mockUserContentController.removeAllUserScripts()); + verify(mockUserContentController.removeScriptMessageHandler('name')); + + verifyNoMoreInteractions(mockUserContentController); + }); + + test('removeJavaScriptChannel with zoom disabled', () async { + final WebKitProxy webKitProxy = WebKitProxy( + createScriptMessageHandler: ({ + required void Function( + WKUserContentController userContentController, + WKScriptMessage message, + ) + didReceiveScriptMessage, + }) { + return WKScriptMessageHandler.detached( + didReceiveScriptMessage: didReceiveScriptMessage, + ); + }, + ); + + final WebKitJavaScriptChannelParams javaScriptChannelParams = + WebKitJavaScriptChannelParams( + name: 'name', + onMessageReceived: (JavaScriptMessage message) {}, + webKitProxy: webKitProxy, + ); + + final MockWKUserContentController mockUserContentController = + MockWKUserContentController(); + + final WebKitWebViewController controller = createControllerWithMocks( + mockUserContentController: mockUserContentController, + ); + + await controller.enableZoom(false); + await controller.addJavaScriptChannel(javaScriptChannelParams); + clearInteractions(mockUserContentController); + await controller.removeJavaScriptChannel('name'); + + final WKUserScript zoomScript = + verify(mockUserContentController.addUserScript(captureAny)) + .captured + .first as WKUserScript; + expect(zoomScript.isMainFrameOnly, isTrue); + expect(zoomScript.injectionTime, WKUserScriptInjectionTime.atDocumentEnd); + expect( + zoomScript.source, + "var meta = document.createElement('meta');\n" + "meta.name = 'viewport';\n" + "meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, " + "user-scalable=no';\n" + "var head = document.getElementsByTagName('head')[0];head.appendChild(meta);", + ); + }); + }); + + group('WebKitJavaScriptChannelParams', () { + test('onMessageReceived', () async { + late final WKScriptMessageHandler messageHandler; + + final WebKitProxy webKitProxy = WebKitProxy( + createScriptMessageHandler: ({ + required void Function( + WKUserContentController userContentController, + WKScriptMessage message, + ) + didReceiveScriptMessage, + }) { + messageHandler = WKScriptMessageHandler.detached( + didReceiveScriptMessage: didReceiveScriptMessage, + ); + return messageHandler; + }, + ); + + late final String callbackMessage; + WebKitJavaScriptChannelParams( + name: 'name', + onMessageReceived: (JavaScriptMessage message) { + callbackMessage = message.message; + }, + webKitProxy: webKitProxy, + ); + + messageHandler.didReceiveScriptMessage( + MockWKUserContentController(), + const WKScriptMessage(name: 'name', body: 'myMessage'), + ); + + expect(callbackMessage, 'myMessage'); + }); + }); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.mocks.dart new file mode 100644 index 000000000000..6138fdd2f4a4 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.mocks.dart @@ -0,0 +1,414 @@ +// Mocks generated by Mockito 5.2.0 from annotations +// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i5; +import 'dart:math' as _i2; +import 'dart:ui' as _i6; + +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart' + as _i7; +import 'package:webview_flutter_wkwebview/src/ui_kit/ui_kit.dart' as _i3; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart' as _i4; + +// 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: 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 + +class _FakePoint_0 extends _i1.Fake implements _i2.Point {} + +class _FakeUIScrollView_1 extends _i1.Fake implements _i3.UIScrollView {} + +class _FakeWKPreferences_2 extends _i1.Fake implements _i4.WKPreferences {} + +class _FakeWKUserContentController_3 extends _i1.Fake + implements _i4.WKUserContentController {} + +class _FakeWKHttpCookieStore_4 extends _i1.Fake + implements _i4.WKHttpCookieStore {} + +class _FakeWKWebsiteDataStore_5 extends _i1.Fake + implements _i4.WKWebsiteDataStore {} + +class _FakeWKWebViewConfiguration_6 extends _i1.Fake + implements _i4.WKWebViewConfiguration {} + +class _FakeWKWebView_7 extends _i1.Fake implements _i4.WKWebView {} + +/// A class which mocks [UIScrollView]. +/// +/// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable +class MockUIScrollView extends _i1.Mock implements _i3.UIScrollView { + MockUIScrollView() { + _i1.throwOnMissingStub(this); + } + + @override + _i5.Future<_i2.Point> getContentOffset() => (super.noSuchMethod( + Invocation.method(#getContentOffset, []), + returnValue: Future<_i2.Point>.value(_FakePoint_0())) + as _i5.Future<_i2.Point>); + @override + _i5.Future scrollBy(_i2.Point? offset) => + (super.noSuchMethod(Invocation.method(#scrollBy, [offset]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setContentOffset(_i2.Point? offset) => + (super.noSuchMethod(Invocation.method(#setContentOffset, [offset]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i3.UIScrollView copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeUIScrollView_1()) as _i3.UIScrollView); + @override + _i5.Future setBackgroundColor(_i6.Color? color) => + (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setOpaque(bool? opaque) => + (super.noSuchMethod(Invocation.method(#setOpaque, [opaque]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); +} + +/// A class which mocks [WKPreferences]. +/// +/// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable +class MockWKPreferences extends _i1.Mock implements _i4.WKPreferences { + MockWKPreferences() { + _i1.throwOnMissingStub(this); + } + + @override + _i5.Future setJavaScriptEnabled(bool? enabled) => + (super.noSuchMethod(Invocation.method(#setJavaScriptEnabled, [enabled]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i4.WKPreferences copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWKPreferences_2()) as _i4.WKPreferences); + @override + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); +} + +/// A class which mocks [WKUserContentController]. +/// +/// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable +class MockWKUserContentController extends _i1.Mock + implements _i4.WKUserContentController { + MockWKUserContentController() { + _i1.throwOnMissingStub(this); + } + + @override + _i5.Future addScriptMessageHandler( + _i4.WKScriptMessageHandler? handler, String? name) => + (super.noSuchMethod( + Invocation.method(#addScriptMessageHandler, [handler, name]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future removeScriptMessageHandler(String? name) => (super + .noSuchMethod(Invocation.method(#removeScriptMessageHandler, [name]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future removeAllScriptMessageHandlers() => (super.noSuchMethod( + Invocation.method(#removeAllScriptMessageHandlers, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future addUserScript(_i4.WKUserScript? userScript) => + (super.noSuchMethod(Invocation.method(#addUserScript, [userScript]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future removeAllUserScripts() => + (super.noSuchMethod(Invocation.method(#removeAllUserScripts, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i4.WKUserContentController copy() => + (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWKUserContentController_3()) + as _i4.WKUserContentController); + @override + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); +} + +/// A class which mocks [WKWebsiteDataStore]. +/// +/// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable +class MockWKWebsiteDataStore extends _i1.Mock + implements _i4.WKWebsiteDataStore { + MockWKWebsiteDataStore() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.WKHttpCookieStore get httpCookieStore => + (super.noSuchMethod(Invocation.getter(#httpCookieStore), + returnValue: _FakeWKHttpCookieStore_4()) as _i4.WKHttpCookieStore); + @override + _i5.Future removeDataOfTypes( + Set<_i4.WKWebsiteDataType>? dataTypes, DateTime? since) => + (super.noSuchMethod( + Invocation.method(#removeDataOfTypes, [dataTypes, since]), + returnValue: Future.value(false)) as _i5.Future); + @override + _i4.WKWebsiteDataStore copy() => + (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWKWebsiteDataStore_5()) as _i4.WKWebsiteDataStore); + @override + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); +} + +/// A class which mocks [WKWebView]. +/// +/// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable +class MockWKWebView extends _i1.Mock implements _i4.WKWebView { + MockWKWebView() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.WKWebViewConfiguration get configuration => + (super.noSuchMethod(Invocation.getter(#configuration), + returnValue: _FakeWKWebViewConfiguration_6()) + as _i4.WKWebViewConfiguration); + @override + _i3.UIScrollView get scrollView => + (super.noSuchMethod(Invocation.getter(#scrollView), + returnValue: _FakeUIScrollView_1()) as _i3.UIScrollView); + @override + _i5.Future setUIDelegate(_i4.WKUIDelegate? delegate) => + (super.noSuchMethod(Invocation.method(#setUIDelegate, [delegate]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setNavigationDelegate(_i4.WKNavigationDelegate? delegate) => + (super.noSuchMethod(Invocation.method(#setNavigationDelegate, [delegate]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future getUrl() => + (super.noSuchMethod(Invocation.method(#getUrl, []), + returnValue: Future.value()) as _i5.Future); + @override + _i5.Future getEstimatedProgress() => + (super.noSuchMethod(Invocation.method(#getEstimatedProgress, []), + returnValue: Future.value(0.0)) as _i5.Future); + @override + _i5.Future loadRequest(_i7.NSUrlRequest? request) => + (super.noSuchMethod(Invocation.method(#loadRequest, [request]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future loadHtmlString(String? string, {String? baseUrl}) => + (super.noSuchMethod( + Invocation.method(#loadHtmlString, [string], {#baseUrl: baseUrl}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future loadFileUrl(String? url, {String? readAccessUrl}) => + (super.noSuchMethod( + Invocation.method( + #loadFileUrl, [url], {#readAccessUrl: readAccessUrl}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future loadFlutterAsset(String? key) => + (super.noSuchMethod(Invocation.method(#loadFlutterAsset, [key]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future canGoBack() => + (super.noSuchMethod(Invocation.method(#canGoBack, []), + returnValue: Future.value(false)) as _i5.Future); + @override + _i5.Future canGoForward() => + (super.noSuchMethod(Invocation.method(#canGoForward, []), + returnValue: Future.value(false)) as _i5.Future); + @override + _i5.Future goBack() => + (super.noSuchMethod(Invocation.method(#goBack, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future goForward() => + (super.noSuchMethod(Invocation.method(#goForward, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future reload() => + (super.noSuchMethod(Invocation.method(#reload, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future getTitle() => + (super.noSuchMethod(Invocation.method(#getTitle, []), + returnValue: Future.value()) as _i5.Future); + @override + _i5.Future setAllowsBackForwardNavigationGestures(bool? allow) => + (super.noSuchMethod( + Invocation.method(#setAllowsBackForwardNavigationGestures, [allow]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setCustomUserAgent(String? userAgent) => + (super.noSuchMethod(Invocation.method(#setCustomUserAgent, [userAgent]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future evaluateJavaScript(String? javaScriptString) => (super + .noSuchMethod(Invocation.method(#evaluateJavaScript, [javaScriptString]), + returnValue: Future.value()) as _i5.Future); + @override + _i4.WKWebView copy() => (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWKWebView_7()) as _i4.WKWebView); + @override + _i5.Future setBackgroundColor(_i6.Color? color) => + (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setOpaque(bool? opaque) => + (super.noSuchMethod(Invocation.method(#setOpaque, [opaque]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); +} + +/// A class which mocks [WKWebViewConfiguration]. +/// +/// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable +class MockWKWebViewConfiguration extends _i1.Mock + implements _i4.WKWebViewConfiguration { + MockWKWebViewConfiguration() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.WKUserContentController get userContentController => + (super.noSuchMethod(Invocation.getter(#userContentController), + returnValue: _FakeWKUserContentController_3()) + as _i4.WKUserContentController); + @override + _i4.WKPreferences get preferences => + (super.noSuchMethod(Invocation.getter(#preferences), + returnValue: _FakeWKPreferences_2()) as _i4.WKPreferences); + @override + _i4.WKWebsiteDataStore get websiteDataStore => + (super.noSuchMethod(Invocation.getter(#websiteDataStore), + returnValue: _FakeWKWebsiteDataStore_5()) as _i4.WKWebsiteDataStore); + @override + _i5.Future setAllowsInlineMediaPlayback(bool? allow) => (super + .noSuchMethod(Invocation.method(#setAllowsInlineMediaPlayback, [allow]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future setMediaTypesRequiringUserActionForPlayback( + Set<_i4.WKAudiovisualMediaType>? types) => + (super.noSuchMethod( + Invocation.method( + #setMediaTypesRequiringUserActionForPlayback, [types]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i4.WKWebViewConfiguration copy() => + (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWKWebViewConfiguration_6()) + as _i4.WKWebViewConfiguration); + @override + _i5.Future addObserver(_i7.NSObject? observer, + {String? keyPath, Set<_i7.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Future removeObserver(_i7.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); +} From 0403bdb5dd002c53c0a97ff3ffa2912bd997bba3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Jul 2022 15:24:07 +0000 Subject: [PATCH 552/844] [quick_actions]: Bump gradle from 3.3.0 to 7.2.1 in /packages/quick_actions/quick_actions_android/android (#5836) --- packages/quick_actions/quick_actions_android/CHANGELOG.md | 4 ++++ .../quick_actions/quick_actions_android/android/build.gradle | 2 +- packages/quick_actions/quick_actions_android/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/quick_actions/quick_actions_android/CHANGELOG.md b/packages/quick_actions/quick_actions_android/CHANGELOG.md index 5b5f70946e91..5e70799e12cf 100644 --- a/packages/quick_actions/quick_actions_android/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.2 + +* Update gradle version to 7.2.1 on Android. + ## 0.6.1 * Allows Android to trigger quick actions without restarting the app. diff --git a/packages/quick_actions/quick_actions_android/android/build.gradle b/packages/quick_actions/quick_actions_android/android/build.gradle index 252a35246aa9..3ca840238567 100644 --- a/packages/quick_actions/quick_actions_android/android/build.gradle +++ b/packages/quick_actions/quick_actions_android/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:7.2.1' } } diff --git a/packages/quick_actions/quick_actions_android/pubspec.yaml b/packages/quick_actions/quick_actions_android/pubspec.yaml index 7cb274116536..836baae8e368 100644 --- a/packages/quick_actions/quick_actions_android/pubspec.yaml +++ b/packages/quick_actions/quick_actions_android/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_android description: An implementation for the Android platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.6.1 +version: 0.6.2 environment: sdk: ">=2.15.0 <3.0.0" From dccc66c40eee5ed4308263f798149609ff4fa99e Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 27 Jul 2022 11:25:06 -0400 Subject: [PATCH 553/844] [webview_flutter_wkwebview] Prevent crash from released deallocCallback (#6131) --- .../webview_flutter_wkwebview/CHANGELOG.md | 5 +++++ .../ios/RunnerTests/FWFInstanceManagerTests.m | 13 +++++++++++++ .../ios/Classes/FWFInstanceManager.m | 4 +++- .../webview_flutter_wkwebview/pubspec.yaml | 2 +- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 72fab859ea7d..5d445028f4e1 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.9.2 + +* Fixes crash when an Objective-C object in `FWFInstanceManager` is released, but the dealloc + callback is no longer available. + ## 2.9.1 * Fixes regression where the behavior for the `UIScrollView` insets were removed. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m index 2ad4bd48b2e8..c893ab51ef42 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFInstanceManagerTests.m @@ -39,4 +39,17 @@ - (void)testRemoveInstanceWithIdentifier { XCTAssertEqualObjects([instanceManager removeInstanceWithIdentifier:0], object); XCTAssertEqual([instanceManager strongInstanceCount], 0); } + +- (void)testDeallocCallbackIsIgnoredIfNull { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" + // This sets deallocCallback to nil to test that uses are null checked. + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] initWithDeallocCallback:nil]; +#pragma clang diagnostic pop + + [instanceManager addDartCreatedInstance:[[NSObject alloc] init] withIdentifier:0]; + + // Tests that this doesn't cause a EXC_BAD_ACCESS crash. + [instanceManager removeInstanceWithIdentifier:0]; +} @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m index 1fe04a39503f..e87a4037bd04 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFInstanceManager.m @@ -40,7 +40,9 @@ + (void)detachFromInstance:(NSObject *)instance { } - (void)dealloc { - _callback(_identifier); + if (_callback) { + _callback(_identifier); + } } @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 446f90973cb3..c7f5ffb7b478 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/plugins/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: 2.9.1 +version: 2.9.2 environment: sdk: ">=2.17.0 <3.0.0" From 946e9b7f7618963ff6424941736d2029575567bd Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Wed, 27 Jul 2022 08:26:06 -0700 Subject: [PATCH 554/844] Avoid map shift when scrolling on iOS (#6135) --- .../google_maps_flutter/CHANGELOG.md | 4 + .../google_maps_flutter/example/ios/Podfile | 3 + .../ios/Runner.xcodeproj/project.pbxproj | 33 ++++++++ .../ios/RunnerUITests/GoogleMapsUITests.m | 77 ++++++++++++++++++- .../example/lib/map_coordinates.dart | 74 +++++++++--------- .../ios/Classes/GoogleMapController.m | 1 + .../google_maps_flutter/pubspec.yaml | 2 +- 7 files changed, 152 insertions(+), 42 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index bf7a705556ed..9d7d542a8f83 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.10 + +* Avoids map shift when scrolling on iOS. + ## 2.1.9 * Updates integration tests to use the new inspector interface. diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile b/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile index 29bfe631a3e7..14b4bdc51c96 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile @@ -34,6 +34,9 @@ target 'Runner' do pod 'OCMock', '~> 3.9.1' end + target 'RunnerUITests' do + inherit! :search_paths + end end post_install do |installer| diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj index b37246b98a47..343e0504134c 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 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 */; }; + 4A097997B7B27CE82FFC3AB8 /* libPods-RunnerUITests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC8ED0578E8D540BBDA17645 /* libPods-RunnerUITests.a */; }; 6851F3562835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */; }; 68E4726A2836FF0C00BDDDAC /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 68E472692836FF0C00BDDDAC /* MapKit.framework */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; @@ -59,6 +60,7 @@ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTGoogleMapJSONConversionsConversionTests.m; sourceTree = ""; }; 68E472692836FF0C00BDDDAC /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/iOSSupport/System/Library/Frameworks/MapKit.framework; sourceTree = DEVELOPER_DIR; }; + 6AC1E6095B09DE4B02ECF64E /* Pods-RunnerUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerUITests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerUITests/Pods-RunnerUITests.release.xcconfig"; sourceTree = ""; }; 733AFAB37683A9DA7512F09C /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 7755F8F4BABC3D6A0BD4048B /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -75,6 +77,8 @@ 982F2A6A27BADE17003C81F4 /* PartiallyMockedMapView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PartiallyMockedMapView.h; sourceTree = ""; }; 982F2A6B27BADE17003C81F4 /* PartiallyMockedMapView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PartiallyMockedMapView.m; sourceTree = ""; }; B7AFC65E3DD5AC60D834D83D /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + DC8ED0578E8D540BBDA17645 /* libPods-RunnerUITests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerUITests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + DDDAC1342ABDF2F125577581 /* Pods-RunnerUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerUITests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerUITests/Pods-RunnerUITests.debug.xcconfig"; sourceTree = ""; }; E52C6A6210A56F027C582EF9 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; EA0E91726245EDC22B97E8B9 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; F267F68029D1A4E2E4C572A7 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -108,6 +112,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 4A097997B7B27CE82FFC3AB8 /* libPods-RunnerUITests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -120,6 +125,7 @@ 68E472692836FF0C00BDDDAC /* MapKit.framework */, 7755F8F4BABC3D6A0BD4048B /* libPods-Runner.a */, F267F68029D1A4E2E4C572A7 /* libPods-RunnerTests.a */, + DC8ED0578E8D540BBDA17645 /* libPods-RunnerUITests.a */, ); name = Frameworks; sourceTree = ""; @@ -189,6 +195,8 @@ EA0E91726245EDC22B97E8B9 /* Pods-Runner.release.xcconfig */, E52C6A6210A56F027C582EF9 /* Pods-RunnerTests.debug.xcconfig */, 733AFAB37683A9DA7512F09C /* Pods-RunnerTests.release.xcconfig */, + DDDAC1342ABDF2F125577581 /* Pods-RunnerUITests.debug.xcconfig */, + 6AC1E6095B09DE4B02ECF64E /* Pods-RunnerUITests.release.xcconfig */, ); name = Pods; sourceTree = ""; @@ -262,6 +270,7 @@ isa = PBXNativeTarget; buildConfigurationList = F7151F25265D7EE50028CB91 /* Build configuration list for PBXNativeTarget "RunnerUITests" */; buildPhases = ( + BD39F60794E9A0264D5D3752 /* [CP] Check Pods Manifest.lock */, F7151F1A265D7EE50028CB91 /* Sources */, F7151F1B265D7EE50028CB91 /* Frameworks */, F7151F1C265D7EE50028CB91 /* Resources */, @@ -413,6 +422,28 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; showEnvVarsInLog = 0; }; + BD39F60794E9A0264D5D3752 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerUITests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; D067548A17DC238B80D2BD12 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -687,6 +718,7 @@ }; F7151F26265D7EE50028CB91 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = DDDAC1342ABDF2F125577581 /* Pods-RunnerUITests.debug.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = RunnerUITests/Info.plist; @@ -700,6 +732,7 @@ }; F7151F27265D7EE50028CB91 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 6AC1E6095B09DE4B02ECF64E /* Pods-RunnerUITests.release.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = RunnerUITests/Info.plist; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerUITests/GoogleMapsUITests.m b/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerUITests/GoogleMapsUITests.m index 2f0c0fa6d615..f4cdb7c50ab2 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerUITests/GoogleMapsUITests.m +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerUITests/GoogleMapsUITests.m @@ -4,6 +4,7 @@ @import XCTest; @import os.log; +@import GoogleMaps; @interface GoogleMapsUITests : XCTestCase @property(nonatomic, strong) XCUIApplication *app; @@ -81,12 +82,80 @@ - (void)testMapCoordinatesPage { XCTFail(@"Failed due to not able to find platform view"); } - XCUIElement *getVisibleRegionBoundsButton = app.buttons[@"Get Visible Region Bounds"]; - if (![getVisibleRegionBoundsButton waitForExistenceWithTimeout:30.0]) { + XCUIElement *titleBar = app.otherElements[@"Map coordinates"]; + if (![titleBar waitForExistenceWithTimeout:30.0]) { os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); - XCTFail(@"Failed due to not able to find 'Get Visible Region Bounds''"); + XCTFail(@"Failed due to not able to find title bar"); } - [getVisibleRegionBoundsButton tap]; + + NSPredicate *visibleRegionPredicate = + [NSPredicate predicateWithFormat:@"label BEGINSWITH 'VisibleRegion'"]; + XCUIElement *visibleRegionText = + [app.staticTexts elementMatchingPredicate:visibleRegionPredicate]; + if (![visibleRegionText waitForExistenceWithTimeout:30.0]) { + os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); + XCTFail(@"Failed due to not able to find Visible Region label'"); + } + + // Validate visible region does not change when scrolled under safe areas. + // https://github.com/flutter/flutter/issues/107913 + + // Example -33.79495661816674, 151.313996873796 + CLLocationCoordinate2D originalNortheast; + // Example -33.90900557679571, 151.10800322145224 + CLLocationCoordinate2D originalSouthwest; + [self validateVisibleRegion:visibleRegionText.label + northeast:&originalNortheast + southwest:&originalSouthwest]; + XCTAssertGreaterThan(originalNortheast.latitude, originalSouthwest.latitude); + XCTAssertGreaterThan(originalNortheast.longitude, originalSouthwest.longitude); + + XCTAssertLessThan(originalNortheast.latitude, 0); + XCTAssertLessThan(originalSouthwest.latitude, 0); + XCTAssertGreaterThan(originalNortheast.longitude, 0); + XCTAssertGreaterThan(originalSouthwest.longitude, 0); + + // Drag the map upward to under the title bar. + [platformView pressForDuration:0 thenDragToElement:titleBar]; + + CLLocationCoordinate2D draggedNortheast; + CLLocationCoordinate2D draggedSouthwest; + [self validateVisibleRegion:visibleRegionText.label + northeast:&draggedNortheast + southwest:&draggedSouthwest]; + XCTAssertEqual(originalNortheast.latitude, draggedNortheast.latitude); + XCTAssertEqual(originalNortheast.longitude, draggedNortheast.longitude); + XCTAssertEqual(originalSouthwest.latitude, draggedSouthwest.latitude); + XCTAssertEqual(originalSouthwest.latitude, draggedSouthwest.latitude); +} + +- (void)validateVisibleRegion:(NSString *)label + northeast:(CLLocationCoordinate2D *)northeast + southwest:(CLLocationCoordinate2D *)southwest { + // String will be "VisibleRegion:\nnortheast: LatLng(-33.79495661816674, + // 151.313996873796),\nsouthwest: LatLng(-33.90900557679571, 151.10800322145224)" + NSScanner *scan = [NSScanner scannerWithString:label]; + + // northeast + [scan scanString:@"VisibleRegion:\nnortheast: LatLng(" intoString:NULL]; + double northeastLatitude; + [scan scanDouble:&northeastLatitude]; + [scan scanString:@", " intoString:NULL]; + XCTAssertNotEqual(northeastLatitude, 0); + double northeastLongitude; + [scan scanDouble:&northeastLongitude]; + XCTAssertNotEqual(northeastLongitude, 0); + + [scan scanString:@"),\nsouthwest: LatLng(" intoString:NULL]; + double southwestLatitude; + [scan scanDouble:&southwestLatitude]; + XCTAssertNotEqual(southwestLatitude, 0); + [scan scanString:@", " intoString:NULL]; + double southwestLongitude; + [scan scanDouble:&southwestLongitude]; + XCTAssertNotEqual(southwestLongitude, 0); + *northeast = CLLocationCoordinate2DMake(northeastLatitude, northeastLongitude); + *southwest = CLLocationCoordinate2DMake(southwestLatitude, southwestLongitude); } - (void)testMapClickPage { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart index 1179acd46ffa..12e31be8f7c7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_coordinates.dart @@ -42,33 +42,42 @@ class _MapCoordinatesBodyState extends State<_MapCoordinatesBody> { final GoogleMap googleMap = GoogleMap( onMapCreated: onMapCreated, initialCameraPosition: _kInitialPosition, + onCameraIdle: + _updateVisibleRegion, // https://github.com/flutter/flutter/issues/54758 ); - final List columnChildren = [ - Padding( - padding: const EdgeInsets.all(10.0), - child: Center( - child: SizedBox( - width: 300.0, - height: 200.0, - child: googleMap, + return NotificationListener( + onNotification: (ScrollNotification scrollState) { + _updateVisibleRegion(); + return true; + }, + child: ListView( + children: [ + Padding( + padding: const EdgeInsets.all(10.0), + child: Center( + child: SizedBox( + width: 300.0, + height: 200.0, + child: googleMap, + ), + ), ), - ), + if (mapController != null) + Center( + child: Text('VisibleRegion:' + '\nnortheast: ${_visibleRegion.northeast},' + '\nsouthwest: ${_visibleRegion.southwest}'), + ), + // Add a block at the bottom of this list to allow validation that the visible region of the map + // does not change when scrolled under the safe view on iOS. + // https://github.com/flutter/flutter/issues/107913 + Container( + width: 300, + height: 1000, + ), + ], ), - ]; - - if (mapController != null) { - final String currentVisibleRegion = 'VisibleRegion:' - '\nnortheast: ${_visibleRegion.northeast},' - '\nsouthwest: ${_visibleRegion.southwest}'; - columnChildren.add(Center(child: Text(currentVisibleRegion))); - columnChildren.add(_getVisibleRegionButton()); - } - - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: columnChildren, ); } @@ -80,19 +89,10 @@ class _MapCoordinatesBodyState extends State<_MapCoordinatesBody> { }); } - Widget _getVisibleRegionButton() { - return Padding( - padding: const EdgeInsets.all(8.0), - child: ElevatedButton( - child: const Text('Get Visible Region Bounds'), - onPressed: () async { - final LatLngBounds visibleRegion = - await mapController!.getVisibleRegion(); - setState(() { - _visibleRegion = visibleRegion; - }); - }, - ), - ); + Future _updateVisibleRegion() async { + final LatLngBounds visibleRegion = await mapController!.getVisibleRegion(); + setState(() { + _visibleRegion = visibleRegion; + }); } } diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m index 6378994819dd..3538cc4b4575 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m @@ -86,6 +86,7 @@ - (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView } }]; _mapView.delegate = weakSelf; + _mapView.paddingAdjustmentBehavior = kGMSMapViewPaddingAdjustmentBehaviorNever; _registrar = registrar; _markersController = [[FLTMarkersController alloc] initWithMethodChannel:_channel mapView:_mapView diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index f546c8af151a..917375e33eaa 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/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.1.9 +version: 2.1.10 environment: sdk: ">=2.14.0 <3.0.0" From 52a0344abee83bee5dbdc5c46eb29d1d34f61f6f Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 27 Jul 2022 13:13:05 -0400 Subject: [PATCH 555/844] [path_provider] Restore 2.8 compatibility on Android and iOS (#6039) Adds a new repo tooling command that removes dev_dependencies, which aren't needed to consume a package, only for development. Also adds a --lib-only flag to analyze to analyze only the client-facing code. This is intended for use in the legacy analyze CI steps, primarily to solve the problem that currently plugins that use Pigeon can't support a version of Flutter older than the version supported by Pigeon, because otherwise the legacy analysis CI steps fail. Adds this new command to the legacy analysis CI step, and restores the recently-removed 2.8/2.10 compatibility to path_provider. --- .cirrus.yml | 8 +- .../path_provider_android/CHANGELOG.md | 4 + .../path_provider_android/pubspec.yaml | 4 +- .../path_provider_ios/CHANGELOG.md | 4 + .../path_provider_ios/pubspec.yaml | 4 +- script/tool/CHANGELOG.md | 7 ++ script/tool/lib/src/analyze_command.dart | 26 +++-- script/tool/lib/src/main.dart | 2 + .../tool/lib/src/remove_dev_dependencies.dart | 58 ++++++++++ script/tool/pubspec.yaml | 2 +- script/tool/test/analyze_command_test.dart | 53 +++++++++ .../test/remove_dev_dependencies_test.dart | 102 ++++++++++++++++++ 12 files changed, 260 insertions(+), 14 deletions(-) create mode 100644 script/tool/lib/src/remove_dev_dependencies.dart create mode 100644 script/tool/test/remove_dev_dependencies_test.dart diff --git a/.cirrus.yml b/.cirrus.yml index 479d49384c9e..c53e66536664 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -163,8 +163,14 @@ task: matrix: CHANNEL: "2.10.5" CHANNEL: "2.8.1" + package_prep_script: + # Allow analyzing plugins that use a Pigeon version with a higher + # minimum Flutter/Dart version than the plugin itself. + - ./script/tool_runner.sh remove-dev-dependencies analyze_script: - - ./script/tool_runner.sh analyze --skip-if-not-supporting-flutter-version="$CHANNEL" --custom-analysis=script/configs/custom_analysis.yaml + # Only analyze lib/; non-client code doesn't need to work on + # all supported legacy version. + - ./script/tool_runner.sh analyze --lib-only --skip-if-not-supporting-flutter-version="$CHANNEL" --custom-analysis=script/configs/custom_analysis.yaml - name: readme_excerpts env: CIRRUS_CLONE_SUBMODULES: true diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index c40c103d5848..799c95a3bfe6 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.17 + +* Lower minimim version back to 2.8.1. + ## 2.0.16 * Fixes bug with `getExternalStoragePaths(null)`. diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index 4c228bbb43bc..ae132b06f0bc 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,11 +2,11 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.16 +version: 2.0.17 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=3.0.0" + flutter: ">=2.8.1" flutter: plugin: diff --git a/packages/path_provider/path_provider_ios/CHANGELOG.md b/packages/path_provider/path_provider_ios/CHANGELOG.md index 8569c1b35027..c9deb3f7112d 100644 --- a/packages/path_provider/path_provider_ios/CHANGELOG.md +++ b/packages/path_provider/path_provider_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.11 + +* Lower minimim version back to 2.8. + ## 2.0.10 * Switches backend to pigeon. diff --git a/packages/path_provider/path_provider_ios/pubspec.yaml b/packages/path_provider/path_provider_ios/pubspec.yaml index 16d2f2e9021e..dfa4a87d91d8 100644 --- a/packages/path_provider/path_provider_ios/pubspec.yaml +++ b/packages/path_provider/path_provider_ios/pubspec.yaml @@ -2,11 +2,11 @@ name: path_provider_ios description: iOS implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.10 +version: 2.0.11 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=3.0.0" + flutter: ">=2.8.0" flutter: plugin: diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index f0534c23a29a..ad3f35a8e9c4 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.8.10 + +- Adds a new `remove-dev-dependencies` command to remove `dev_dependencies` + entries to make legacy version analysis possible in more cases. +- Adds a `--lib-only` option to `analyze` to allow only analyzing the client + parts of a library for legacy verison compatibility. + ## 0.8.9 - Includes `dev_dependencies` when overridding dependencies using diff --git a/script/tool/lib/src/analyze_command.dart b/script/tool/lib/src/analyze_command.dart index 8778b3de9d86..54b4f33a2342 100644 --- a/script/tool/lib/src/analyze_command.dart +++ b/script/tool/lib/src/analyze_command.dart @@ -32,10 +32,13 @@ class AnalyzeCommand extends PackageLoopingCommand { valueHelp: 'dart-sdk', help: 'An optional path to a Dart SDK; this is used to override the ' 'SDK used to provide analysis.'); + argParser.addFlag(_libOnlyFlag, + help: 'Only analyze the lib/ directory of the main package, not the ' + 'entire package.'); } static const String _customAnalysisFlag = 'custom-analysis'; - + static const String _libOnlyFlag = 'lib-only'; static const String _analysisSdk = 'analysis-sdk'; late String _dartBinaryPath; @@ -104,13 +107,20 @@ class AnalyzeCommand extends PackageLoopingCommand { @override Future runForPackage(RepositoryPackage package) async { - // Analysis runs over the package and all subpackages, so all of them need - // `flutter pub get` run before analyzing. `example` packages can be - // skipped since 'flutter packages get' automatically runs `pub get` in - // examples as part of handling the parent directory. + final bool libOnly = getBoolArg(_libOnlyFlag); + + if (libOnly && !package.libDirectory.existsSync()) { + return PackageResult.skip('No lib/ directory.'); + } + + // Analysis runs over the package and all subpackages (unless only lib/ is + // being analyzed), so all of them need `flutter pub get` run before + // analyzing. `example` packages can be skipped since 'flutter packages get' + // automatically runs `pub get` in examples as part of handling the parent + // directory. final List packagesToGet = [ package, - ...await getSubpackages(package).toList(), + if (!libOnly) ...await getSubpackages(package).toList(), ]; for (final RepositoryPackage packageToGet in packagesToGet) { if (packageToGet.directory.basename != 'example' || @@ -129,8 +139,8 @@ class AnalyzeCommand extends PackageLoopingCommand { if (_hasUnexpecetdAnalysisOptions(package)) { return PackageResult.fail(['Unexpected local analysis options']); } - final int exitCode = await processRunner.runAndStream( - _dartBinaryPath, ['analyze', '--fatal-infos'], + final int exitCode = await processRunner.runAndStream(_dartBinaryPath, + ['analyze', '--fatal-infos', if (libOnly) 'lib'], workingDir: package.directory); if (exitCode != 0) { return PackageResult.fail(); diff --git a/script/tool/lib/src/main.dart b/script/tool/lib/src/main.dart index 966e7b6be56a..ea1ec067c82f 100644 --- a/script/tool/lib/src/main.dart +++ b/script/tool/lib/src/main.dart @@ -28,6 +28,7 @@ import 'publish_check_command.dart'; import 'publish_plugin_command.dart'; import 'pubspec_check_command.dart'; import 'readme_check_command.dart'; +import 'remove_dev_dependencies.dart'; import 'test_command.dart'; import 'update_excerpts_command.dart'; import 'update_release_info_command.dart'; @@ -71,6 +72,7 @@ void main(List args) { ..addCommand(PublishPluginCommand(packagesDir)) ..addCommand(PubspecCheckCommand(packagesDir)) ..addCommand(ReadmeCheckCommand(packagesDir)) + ..addCommand(RemoveDevDependenciesCommand(packagesDir)) ..addCommand(TestCommand(packagesDir)) ..addCommand(UpdateExcerptsCommand(packagesDir)) ..addCommand(UpdateReleaseInfoCommand(packagesDir)) diff --git a/script/tool/lib/src/remove_dev_dependencies.dart b/script/tool/lib/src/remove_dev_dependencies.dart new file mode 100644 index 000000000000..3085e0df85e0 --- /dev/null +++ b/script/tool/lib/src/remove_dev_dependencies.dart @@ -0,0 +1,58 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file/file.dart'; +import 'package:yaml/yaml.dart'; +import 'package:yaml_edit/yaml_edit.dart'; + +import 'common/package_looping_command.dart'; +import 'common/repository_package.dart'; + +/// A command to remove dev_dependencies, which are not used by package clients. +/// +/// This is intended for use with legacy Flutter version testing, to allow +/// running analysis (with --lib-only) with versions that are supported for +/// clients of the library, but not for development of the library. +class RemoveDevDependenciesCommand extends PackageLoopingCommand { + /// Creates a publish metadata updater command instance. + RemoveDevDependenciesCommand(Directory packagesDir) : super(packagesDir); + + @override + final String name = 'remove-dev-dependencies'; + + @override + final String description = 'Removes any dev_dependencies section from a ' + 'package, to allow more legacy testing.'; + + @override + bool get hasLongOutput => false; + + @override + PackageLoopingType get packageLoopingType => + PackageLoopingType.includeAllSubpackages; + + @override + Future runForPackage(RepositoryPackage package) async { + bool changed = false; + final YamlEditor editablePubspec = + YamlEditor(package.pubspecFile.readAsStringSync()); + const String devDependenciesKey = 'dev_dependencies'; + final YamlNode root = editablePubspec.parseAt([]); + final YamlMap? devDependencies = + (root as YamlMap)[devDependenciesKey] as YamlMap?; + if (devDependencies != null) { + changed = true; + print('${indentation}Removed dev_dependencies'); + editablePubspec.remove([devDependenciesKey]); + } + + if (changed) { + package.pubspecFile.writeAsStringSync(editablePubspec.toString()); + } + + return changed + ? PackageResult.success() + : PackageResult.skip('Nothing to remove.'); + } +} diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index a4ecbfb75e98..2eee1e3c7b15 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.8.9 +version: 0.8.10 dependencies: args: ^2.1.0 diff --git a/script/tool/test/analyze_command_test.dart b/script/tool/test/analyze_command_test.dart index a4a47a221189..d3abf0b6b8e0 100644 --- a/script/tool/test/analyze_command_test.dart +++ b/script/tool/test/analyze_command_test.dart @@ -93,6 +93,59 @@ void main() { ])); }); + test('passes lib/ directory with --lib-only', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + + await runCapturingPrint(runner, ['analyze', '--lib-only']); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall('flutter', const ['pub', 'get'], package.path), + ProcessCall('dart', const ['analyze', '--fatal-infos', 'lib'], + package.path), + ])); + }); + + test('skips when missing lib/ directory with --lib-only', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + package.libDirectory.deleteSync(); + + final List output = + await runCapturingPrint(runner, ['analyze', '--lib-only']); + + expect(processRunner.recordedCalls, isEmpty); + expect( + output, + containsAllInOrder([ + contains('SKIPPING: No lib/ directory'), + ]), + ); + }); + + test( + 'does not run flutter pub get for non-example subpackages with --lib-only', + () async { + final RepositoryPackage mainPackage = createFakePackage('a', packagesDir); + final Directory otherPackagesDir = + mainPackage.directory.childDirectory('other_packages'); + createFakePackage('subpackage1', otherPackagesDir); + createFakePackage('subpackage2', otherPackagesDir); + + await runCapturingPrint(runner, ['analyze', '--lib-only']); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall( + 'flutter', const ['pub', 'get'], mainPackage.path), + ProcessCall('dart', const ['analyze', '--fatal-infos', 'lib'], + mainPackage.path), + ])); + }); + test("don't elide a non-contained example package", () async { final RepositoryPackage plugin1 = createFakePlugin('a', packagesDir); final RepositoryPackage plugin2 = createFakePlugin('example', packagesDir); diff --git a/script/tool/test/remove_dev_dependencies_test.dart b/script/tool/test/remove_dev_dependencies_test.dart new file mode 100644 index 000000000000..6b212870c53b --- /dev/null +++ b/script/tool/test/remove_dev_dependencies_test.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:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_plugin_tools/src/remove_dev_dependencies.dart'; +import 'package:test/test.dart'; + +import 'util.dart'; + +void main() { + late FileSystem fileSystem; + late Directory packagesDir; + late CommandRunner runner; + + setUp(() { + fileSystem = MemoryFileSystem(); + packagesDir = createPackagesDirectory(fileSystem: fileSystem); + + final RemoveDevDependenciesCommand command = RemoveDevDependenciesCommand( + packagesDir, + ); + runner = CommandRunner('trim_dev_dependencies_command', + 'Test for trim_dev_dependencies_command'); + runner.addCommand(command); + }); + + void _addToPubspec(RepositoryPackage package, String addition) { + final String originalContent = package.pubspecFile.readAsStringSync(); + package.pubspecFile.writeAsStringSync(''' +$originalContent +$addition +'''); + } + + test('skips if nothing is removed', () async { + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + final List output = + await runCapturingPrint(runner, ['remove-dev-dependencies']); + + expect( + output, + containsAllInOrder([ + contains('SKIPPING: Nothing to remove.'), + ]), + ); + }); + + test('removes dev_dependencies', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + _addToPubspec(package, ''' +dev_dependencies: + some_dependency: ^2.1.8 + another_dependency: ^1.0.0 +'''); + + final List output = + await runCapturingPrint(runner, ['remove-dev-dependencies']); + + expect( + output, + containsAllInOrder([ + contains('Removed dev_dependencies'), + ]), + ); + expect(package.pubspecFile.readAsStringSync(), + isNot(contains('some_dependency:'))); + expect(package.pubspecFile.readAsStringSync(), + isNot(contains('another_dependency:'))); + }); + + test('removes from examples', () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir, version: '1.0.0'); + + final RepositoryPackage example = package.getExamples().first; + _addToPubspec(example, ''' +dev_dependencies: + some_dependency: ^2.1.8 + another_dependency: ^1.0.0 +'''); + + final List output = + await runCapturingPrint(runner, ['remove-dev-dependencies']); + + expect( + output, + containsAllInOrder([ + contains('Removed dev_dependencies'), + ]), + ); + expect(package.pubspecFile.readAsStringSync(), + isNot(contains('some_dependency:'))); + expect(package.pubspecFile.readAsStringSync(), + isNot(contains('another_dependency:'))); + }); +} From 160967b311d6b47a9aee032071c1cb560a43e196 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 27 Jul 2022 13:59:04 -0400 Subject: [PATCH 556/844] [tool] Switch PR description overrides over to labels (#6145) --- .cirrus.yml | 12 +- script/tool/CHANGELOG.md | 7 ++ .../tool/lib/src/version_check_command.dart | 104 +++++++++------- script/tool/pubspec.yaml | 2 +- .../tool/test/version_check_command_test.dart | 117 +++++++----------- 5 files changed, 115 insertions(+), 127 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index c53e66536664..84961507f89c 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -88,17 +88,17 @@ task: env: CHANGE_DESC: "$TMPDIR/change-description.txt" version_check_script: - # For pre-submit, pass the PR description to the script to allow for - # version check overrides. + # For pre-submit, pass the PR label, as well as the PR description or + # incremental commit message (for Dependabot checks), to the script to + # allow for version check overrides. # For post-submit, ignore platform version breaking version changes and - # missing version/CHANGELOG detection; the PR description isn't reliably - # part of the commit message, so using the same flags as for presubmit - # would likely result in false-positive post-submit failures. + # missing version/CHANGELOG detection since the overrides aren't + # available outside of the context of the PR. - if [[ $CIRRUS_PR == "" ]]; then - ./script/tool_runner.sh version-check --ignore-platform-interface-breaks - else - echo "$CIRRUS_CHANGE_MESSAGE" > "$CHANGE_DESC" - - ./script/tool_runner.sh version-check --check-for-missing-changes --change-description-file="$CHANGE_DESC" + - ./script/tool_runner.sh version-check --check-for-missing-changes --change-description-file="$CHANGE_DESC" --pr-labels="$CIRRUS_PR_LABELS" - fi publish_check_script: ./script/tool_runner.sh publish-check - name: format diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index ad3f35a8e9c4..667f1a188d19 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.9.0 + +* Replaces PR-description-based version/changelog/breaking change check + overrides in `version-check` with label-based overrides using a new + `pr-labels` flag, since we don't actually have reliable access to the + PR description in checks. + ## 0.8.10 - Adds a new `remove-dev-dependencies` command to remove `dev_dependencies` diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart index 30a45d33a77f..d5e9675adcb1 100644 --- a/script/tool/lib/src/version_check_command.dart +++ b/script/tool/lib/src/version_check_command.dart @@ -123,47 +123,49 @@ class VersionCheckCommand extends PackageLoopingCommand { '(e.g., PR description or commit message).\n\n' 'If supplied, this is used to allow overrides to some version ' 'checks.'); + argParser.addOption(_prLabelsArg, + help: 'A comma-separated list of labels associated with this PR, ' + 'if applicable.\n\n' + 'If supplied, this may be to allow overrides to some version ' + 'checks.'); argParser.addFlag(_checkForMissingChanges, help: 'Validates that changes to packages include CHANGELOG and ' 'version changes unless they meet an established exemption.\n\n' - 'If used with --$_changeDescriptionFile, this is should only be ' - 'used in pre-submit CI checks, to prevent the possibility of ' - 'post-submit breakage if an override justification is not ' - 'transferred into the commit message.', + 'If used with --$_prLabelsArg, this is should only be ' + 'used in pre-submit CI checks, to prevent post-submit breakage ' + 'when labels are no longer applicable.', hide: true); argParser.addFlag(_ignorePlatformInterfaceBreaks, help: 'Bypasses the check that platform interfaces do not contain ' 'breaking changes.\n\n' 'This is only intended for use in post-submit CI checks, to ' - 'prevent the possibility of post-submit breakage if a change ' - 'description justification is not transferred into the commit ' - 'message. Pre-submit checks should always use ' - '--$_changeDescriptionFile instead.', + 'prevent post-submit breakage when overriding the check with ' + 'labels. Pre-submit checks should always use ' + '--$_prLabelsArg instead.', hide: true); } static const String _againstPubFlag = 'against-pub'; static const String _changeDescriptionFile = 'change-description-file'; + static const String _prLabelsArg = 'pr-labels'; static const String _checkForMissingChanges = 'check-for-missing-changes'; static const String _ignorePlatformInterfaceBreaks = 'ignore-platform-interface-breaks'; - /// The string that must be in [_changeDescriptionFile] to allow a breaking + /// The label that must be on a PR to allow a breaking /// change to a platform interface. - static const String _breakingChangeJustificationMarker = - '## Breaking change justification'; + static const String _breakingChangeOverrideLabel = + 'override: allow breaking change'; - /// The string that must be at the start of a line in [_changeDescriptionFile] - /// to allow skipping a version change for a PR that would normally require - /// one. - static const String _missingVersionChangeJustificationMarker = - 'No version change:'; + /// The label that must be on a PR to allow skipping a version change for a PR + /// that would normally require one. + static const String _missingVersionChangeOverrideLabel = + 'override: no versioning needed'; - /// The string that must be at the start of a line in [_changeDescriptionFile] - /// to allow skipping a CHANGELOG change for a PR that would normally require - /// one. - static const String _missingChangelogChangeJustificationMarker = - 'No CHANGELOG change:'; + /// The label that must be on a PR to allow skipping a CHANGELOG change for a + /// PR that would normally require one. + static const String _missingChangelogChangeOverrideLabel = + 'override: no changelog needed'; final PubVersionFinder _pubVersionFinder; @@ -172,6 +174,7 @@ class VersionCheckCommand extends PackageLoopingCommand { late final List _changedFiles; late final String _changeDescription = _loadChangeDescription(); + late final Set _prLabels = _getPRLabels(); @override final String name = 'version-check'; @@ -498,18 +501,25 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. return true; } - if (_getChangeDescription().contains(_breakingChangeJustificationMarker)) { + if (_prLabels.contains(_breakingChangeOverrideLabel)) { logWarning( '${indentation}Allowing breaking change to ${package.displayName} ' - 'due to "$_breakingChangeJustificationMarker" in the change ' - 'description.'); + 'due to the "$_breakingChangeOverrideLabel" label.'); return true; } return false; } - String _getChangeDescription() => _changeDescription; + /// Returns the labels associated with this PR, if any, or an empty set + /// if that flag is not provided. + Set _getPRLabels() { + final String labels = getStringArg(_prLabelsArg); + if (labels.isEmpty) { + return {}; + } + return labels.split(',').map((String label) => label.trim()).toSet(); + } /// Returns the contents of the file pointed to by [_changeDescriptionFile], /// or an empty string if that flag is not provided. @@ -569,44 +579,38 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. } if (state.needsVersionChange) { - final String changeDescription = _getChangeDescription(); - if (changeDescription.split('\n').any((String line) => - line.startsWith(_missingVersionChangeJustificationMarker))) { - logWarning('Ignoring lack of version change due to ' - '"$_missingVersionChangeJustificationMarker" in the ' - 'change description.'); - } else if (_isAllowedDependabotChange(package, changeDescription)) { + if (_prLabels.contains(_missingVersionChangeOverrideLabel)) { + logWarning('Ignoring lack of version change due to the ' + '"$_missingVersionChangeOverrideLabel" label.'); + } else if (_isAllowedDependabotChange(package, _changeDescription)) { logWarning('Ignoring lack of version change for Dependabot change to ' 'a known internal dependency.'); } else { printError( 'No version change found, but the change to this package could ' 'not be verified to be exempt from version changes according to ' - 'repository policy. If this is a false positive, please ' - 'add a line starting with\n' - '$_missingVersionChangeJustificationMarker\n' - 'to your PR description with an explanation of why it is exempt.'); + 'repository policy. If this is a false positive, please comment in ' + 'the PR to explain why the PR is exempt, and add (or ask your ' + 'reviewer to add) the "$_missingVersionChangeOverrideLabel" ' + 'label.'); return 'Missing version change'; } } if (!state.hasChangelogChange) { - final String changeDescription = _getChangeDescription(); - if (changeDescription.split('\n').any((String line) => - line.startsWith(_missingChangelogChangeJustificationMarker))) { - logWarning('Ignoring lack of CHANGELOG update due to ' - '"$_missingChangelogChangeJustificationMarker" in the ' - 'change description.'); - } else if (_isAllowedDependabotChange(package, changeDescription)) { + if (_prLabels.contains(_missingChangelogChangeOverrideLabel)) { + logWarning('Ignoring lack of CHANGELOG update due to the ' + '"$_missingChangelogChangeOverrideLabel" label.'); + } else if (_isAllowedDependabotChange(package, _changeDescription)) { logWarning('Ignoring lack of CHANGELOG update for Dependabot change to ' 'a known internal dependency.'); } else { printError( 'No CHANGELOG change found. If this PR needs an exemption from ' 'the standard policy of listing all changes in the CHANGELOG, ' - 'please add a line starting with\n' - '$_missingChangelogChangeJustificationMarker\n' - 'to your PR description with an explanation of why.'); + 'comment in the PR to explain why the PR is exempt, and add (or ' + 'ask your reviewer to add) the ' + '"$_missingChangelogChangeOverrideLabel" label.'); return 'Missing CHANGELOG change'; } } @@ -617,6 +621,11 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. /// Returns true if [changeDescription] matches a Dependabot change for a /// dependency roll that should bypass the normal version and CHANGELOG change /// checks (for dependencies that are known not to have client impact). + /// + /// Depending on CI, [changeDescription] may either be the PR description, or + /// the description of the last commit (see for example discussion in + /// https://github.com/cirruslabs/cirrus-ci-docs/issues/1029), so this needs + /// to handle both. bool _isAllowedDependabotChange( RepositoryPackage package, String changeDescription) { // Espresso exports some dependencies that are normally just internal test @@ -629,8 +638,7 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. // any other PR, to identify Dependabot PRs. const String dependabotPRDescriptionMarker = 'Dependabot commands and options'; - // The same thing, but for the Dependabot commit message, to work around - // https://github.com/cirruslabs/cirrus-ci-docs/issues/1029. + // The same thing, but for the Dependabot commit message. const String dependabotCommitMessageMarker = 'Signed-off-by: dependabot[bot]'; // Expression to extract the name of the dependency being updated. diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 2eee1e3c7b15..a99be7098197 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.8.10 +version: 0.9.0 dependencies: args: ^2.1.0 diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart index 4586070fd67e..f8153750122a 100644 --- a/script/tool/test/version_check_command_test.dart +++ b/script/tool/test/version_check_command_test.dart @@ -364,35 +364,25 @@ void main() { ])); }); - test('allows breaking changes to platform interfaces with explanation', + test('allows breaking changes to platform interfaces with override label', () async { createFakePlugin('plugin_platform_interface', packagesDir, version: '2.0.0'); processRunner.mockProcessesForExecutable['git-show'] = [ MockProcess(stdout: 'version: 1.0.0'), ]; - final File changeDescriptionFile = - fileSystem.file('change_description.txt'); - changeDescriptionFile.writeAsStringSync(''' -Some general PR description -## Breaking change justification - -This is necessary because of X, Y, and Z - -## Another section'''); final List output = await runCapturingPrint(runner, [ 'version-check', '--base-sha=main', - '--change-description-file=${changeDescriptionFile.path}' + '--pr-labels=some label,override: allow breaking change,another-label' ]); expect( output, containsAllInOrder([ contains('Allowing breaking change to plugin_platform_interface ' - 'due to "## Breaking change justification" in the change ' - 'description.'), + 'due to the "override: allow breaking change" label.'), contains('Ran for 1 package(s) (1 with warnings)'), ]), ); @@ -408,42 +398,6 @@ This is necessary because of X, Y, and Z ])); }); - test('throws if a nonexistent change description file is specified', - () async { - createFakePlugin('plugin_platform_interface', packagesDir, - version: '2.0.0'); - processRunner.mockProcessesForExecutable['git-show'] = [ - MockProcess(stdout: 'version: 1.0.0'), - ]; - - Error? commandError; - final List output = await runCapturingPrint(runner, [ - 'version-check', - '--base-sha=main', - '--change-description-file=a_missing_file.txt' - ], errorHandler: (Error e) { - commandError = e; - }); - - expect(commandError, isA()); - expect( - output, - containsAllInOrder([ - contains('No such file: a_missing_file.txt'), - ]), - ); - expect( - processRunner.recordedCalls, - containsAllInOrder(const [ - ProcessCall( - 'git-show', - [ - 'main:packages/plugin_platform_interface/pubspec.yaml' - ], - null) - ])); - }); - test('allows breaking changes to platform interfaces with bypass flag', () async { createFakePlugin('plugin_platform_interface', packagesDir, @@ -965,7 +919,7 @@ packages/plugin/CHANGELOG.md ); }); - test('allows missing version change with justification', () async { + test('allows missing version change with override label', () async { final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); @@ -985,23 +939,16 @@ packages/plugin/pubspec.yaml '''), ]; - final File changeDescriptionFile = - fileSystem.file('change_description.txt'); - changeDescriptionFile.writeAsStringSync(''' -Some general PR description - -No version change: Code change is only to implementation comments. -'''); final List output = await _runWithMissingChangeDetection([ - '--change-description-file=${changeDescriptionFile.path}' + '--pr-labels=some label,override: no versioning needed,another-label' ]); expect( output, containsAllInOrder([ - contains('Ignoring lack of version change due to ' - '"No version change:" in the change description.'), + contains('Ignoring lack of version change due to the ' + '"override: no versioning needed" label.'), ]), ); }); @@ -1124,28 +1071,56 @@ packages/plugin/example/lib/foo.dart '''), ]; - final File changeDescriptionFile = - fileSystem.file('change_description.txt'); - changeDescriptionFile.writeAsStringSync(''' -Some general PR description - -No CHANGELOG change: Code change is only to implementation comments. -'''); final List output = await _runWithMissingChangeDetection([ - '--change-description-file=${changeDescriptionFile.path}' + '--pr-labels=some label,override: no changelog needed,another-label' ]); expect( output, containsAllInOrder([ - contains('Ignoring lack of CHANGELOG update due to ' - '"No CHANGELOG change:" in the change description.'), + contains('Ignoring lack of CHANGELOG update due to the ' + '"override: no changelog needed" label.'), ]), ); }); group('dependabot', () { + test('throws if a nonexistent change description file is specified', + () async { + final RepositoryPackage plugin = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); + + const String changelog = ''' +## 1.0.0 +* Some changes. +'''; + plugin.changelogFile.writeAsStringSync(changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + MockProcess(stdout: ''' +packages/plugin/android/build.gradle +'''), + ]; + + Error? commandError; + final List output = await _runWithMissingChangeDetection( + ['--change-description-file=a_missing_file.txt'], + errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('No such file: a_missing_file.txt'), + ]), + ); + }); + test('allows missing version and CHANGELOG change for mockito', () async { final RepositoryPackage plugin = @@ -1308,8 +1283,6 @@ packages/plugin/android/build.gradle ); }); - // Tests workaround for - // https://github.com/cirruslabs/cirrus-ci-docs/issues/1029. test('allow list works for commit messages', () async { final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, version: '1.0.0'); From 6d387c55642d08a765e1221690ca7c784cb6c7ae Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 28 Jul 2022 10:25:05 -0400 Subject: [PATCH 557/844] [file_selector] Convert Windows to Pigeon (#6035) --- .../file_selector_windows/CHANGELOG.md | 4 + .../windows/flutter/generated_plugins.cmake | 8 + .../lib/file_selector_windows.dart | 116 ++-- .../lib/src/messages.g.dart | 176 ++++++ .../pigeons/copyright.txt | 3 + .../pigeons/messages.dart | 53 ++ .../file_selector_windows/pubspec.yaml | 7 +- .../test/file_selector_windows_test.dart | 597 +++++++++--------- .../file_selector_windows_test.mocks.dart | 46 ++ .../file_selector_windows/test/test_api.dart | 105 +++ .../windows/CMakeLists.txt | 8 + .../windows/file_selector_plugin.cpp | 178 ++---- .../windows/file_selector_plugin.h | 14 +- .../windows/messages.g.cpp | 278 ++++++++ .../windows/messages.g.h | 149 +++++ .../windows/string_utils.cpp | 4 +- .../windows/string_utils.h | 4 +- .../test/file_selector_plugin_test.cpp | 303 ++++----- 18 files changed, 1392 insertions(+), 661 deletions(-) create mode 100644 packages/file_selector/file_selector_windows/lib/src/messages.g.dart create mode 100644 packages/file_selector/file_selector_windows/pigeons/copyright.txt create mode 100644 packages/file_selector/file_selector_windows/pigeons/messages.dart create mode 100644 packages/file_selector/file_selector_windows/test/file_selector_windows_test.mocks.dart create mode 100644 packages/file_selector/file_selector_windows/test/test_api.dart create mode 100644 packages/file_selector/file_selector_windows/windows/messages.g.cpp create mode 100644 packages/file_selector/file_selector_windows/windows/messages.g.h diff --git a/packages/file_selector/file_selector_windows/CHANGELOG.md b/packages/file_selector/file_selector_windows/CHANGELOG.md index 3f15de73b421..b0e6f0da6242 100644 --- a/packages/file_selector/file_selector_windows/CHANGELOG.md +++ b/packages/file_selector/file_selector_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.1 + +* Converts the method channel to Pigeon. + ## 0.9.0 * **BREAKING CHANGE**: Methods that take `XTypeGroup`s now throw an diff --git a/packages/file_selector/file_selector_windows/example/windows/flutter/generated_plugins.cmake b/packages/file_selector/file_selector_windows/example/windows/flutter/generated_plugins.cmake index 63eda9b7b59f..a423a02476a2 100644 --- a/packages/file_selector/file_selector_windows/example/windows/flutter/generated_plugins.cmake +++ b/packages/file_selector/file_selector_windows/example/windows/flutter/generated_plugins.cmake @@ -6,6 +6,9 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_windows ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -14,3 +17,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/file_selector/file_selector_windows/lib/file_selector_windows.dart b/packages/file_selector/file_selector_windows/lib/file_selector_windows.dart index 6b1bc89c5078..4ce248343abb 100644 --- a/packages/file_selector/file_selector_windows/lib/file_selector_windows.dart +++ b/packages/file_selector/file_selector_windows/lib/file_selector_windows.dart @@ -3,17 +3,12 @@ // found in the LICENSE file. import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; -import 'package:flutter/foundation.dart' show visibleForTesting; -import 'package:flutter/services.dart'; -const MethodChannel _channel = - MethodChannel('plugins.flutter.io/file_selector_windows'); +import 'src/messages.g.dart'; /// An implementation of [FileSelectorPlatform] for Windows. class FileSelectorWindows extends FileSelectorPlatform { - /// The MethodChannel that is being used by this implementation of the plugin. - @visibleForTesting - MethodChannel get channel => _channel; + final FileSelectorApi _hostApi = FileSelectorApi(); /// Registers the Windows implementation. static void registerWith() { @@ -26,19 +21,15 @@ class FileSelectorWindows extends FileSelectorPlatform { String? initialDirectory, String? confirmButtonText, }) async { - _validateTypeGroups(acceptedTypeGroups); - final List? path = await _channel.invokeListMethod( - 'openFile', - { - 'acceptedTypeGroups': acceptedTypeGroups - ?.map((XTypeGroup group) => group.toJSON()) - .toList(), - 'initialDirectory': initialDirectory, - 'confirmButtonText': confirmButtonText, - 'multiple': false, - }, - ); - return path == null ? null : XFile(path.first); + final List paths = await _hostApi.showOpenDialog( + SelectionOptions( + allowMultiple: false, + selectFolders: false, + allowedTypes: _typeGroupsFromXTypeGroups(acceptedTypeGroups), + ), + initialDirectory, + confirmButtonText); + return paths.isEmpty ? null : XFile(paths.first!); } @override @@ -47,19 +38,15 @@ class FileSelectorWindows extends FileSelectorPlatform { String? initialDirectory, String? confirmButtonText, }) async { - _validateTypeGroups(acceptedTypeGroups); - final List? pathList = await _channel.invokeListMethod( - 'openFile', - { - 'acceptedTypeGroups': acceptedTypeGroups - ?.map((XTypeGroup group) => group.toJSON()) - .toList(), - 'initialDirectory': initialDirectory, - 'confirmButtonText': confirmButtonText, - 'multiple': true, - }, - ); - return pathList?.map((String path) => XFile(path)).toList() ?? []; + final List paths = await _hostApi.showOpenDialog( + SelectionOptions( + allowMultiple: true, + selectFolders: false, + allowedTypes: _typeGroupsFromXTypeGroups(acceptedTypeGroups), + ), + initialDirectory, + confirmButtonText); + return paths.map((String? path) => XFile(path!)).toList(); } @override @@ -69,18 +56,16 @@ class FileSelectorWindows extends FileSelectorPlatform { String? suggestedName, String? confirmButtonText, }) async { - _validateTypeGroups(acceptedTypeGroups); - return _channel.invokeMethod( - 'getSavePath', - { - 'acceptedTypeGroups': acceptedTypeGroups - ?.map((XTypeGroup group) => group.toJSON()) - .toList(), - 'initialDirectory': initialDirectory, - 'suggestedName': suggestedName, - 'confirmButtonText': confirmButtonText, - }, - ); + final List paths = await _hostApi.showSaveDialog( + SelectionOptions( + allowMultiple: false, + selectFolders: false, + allowedTypes: _typeGroupsFromXTypeGroups(acceptedTypeGroups), + ), + initialDirectory, + suggestedName, + confirmButtonText); + return paths.isEmpty ? null : paths.first!; } @override @@ -88,28 +73,27 @@ class FileSelectorWindows extends FileSelectorPlatform { String? initialDirectory, String? confirmButtonText, }) async { - return _channel.invokeMethod( - 'getDirectoryPath', - { - 'initialDirectory': initialDirectory, - 'confirmButtonText': confirmButtonText, - }, - ); + final List paths = await _hostApi.showOpenDialog( + SelectionOptions( + allowMultiple: false, + selectFolders: true, + allowedTypes: [], + ), + initialDirectory, + confirmButtonText); + return paths.isEmpty ? null : paths.first!; } +} - /// Throws an [ArgumentError] if any of the provided type groups are not valid - /// for Windows. - void _validateTypeGroups(List? groups) { - if (groups == null) { - return; - } - for (final XTypeGroup group in groups) { - if (!group.allowsAny && (group.extensions?.isEmpty ?? true)) { - throw ArgumentError('Provided type group $group does not allow ' - 'all files, but does not set any of the Windows-supported filter ' - 'categories. "extensions" must be non-empty for Windows if ' - 'anything is non-empty.'); - } +List _typeGroupsFromXTypeGroups(List? xtypes) { + return (xtypes ?? []).map((XTypeGroup xtype) { + if (!xtype.allowsAny && (xtype.extensions?.isEmpty ?? true)) { + throw ArgumentError('Provided type group $xtype does not allow ' + 'all files, but does not set any of the Windows-supported filter ' + 'categories. "extensions" must be non-empty for Windows if ' + 'anything is non-empty.'); } - } + return TypeGroup( + label: xtype.label ?? '', extensions: xtype.extensions ?? []); + }).toList(); } diff --git a/packages/file_selector/file_selector_windows/lib/src/messages.g.dart b/packages/file_selector/file_selector_windows/lib/src/messages.g.dart new file mode 100644 index 000000000000..ad3d5af83278 --- /dev/null +++ b/packages/file_selector/file_selector_windows/lib/src/messages.g.dart @@ -0,0 +1,176 @@ +// 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 (v3.2.5), 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 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; + +class TypeGroup { + TypeGroup({ + required this.label, + required this.extensions, + }); + + String label; + List extensions; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['label'] = label; + pigeonMap['extensions'] = extensions; + return pigeonMap; + } + + static TypeGroup decode(Object message) { + final Map pigeonMap = message as Map; + return TypeGroup( + label: pigeonMap['label']! as String, + extensions: (pigeonMap['extensions'] as List?)!.cast(), + ); + } +} + +class SelectionOptions { + SelectionOptions({ + required this.allowMultiple, + required this.selectFolders, + required this.allowedTypes, + }); + + bool allowMultiple; + bool selectFolders; + List allowedTypes; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['allowMultiple'] = allowMultiple; + pigeonMap['selectFolders'] = selectFolders; + pigeonMap['allowedTypes'] = allowedTypes; + return pigeonMap; + } + + static SelectionOptions decode(Object message) { + final Map pigeonMap = message as Map; + return SelectionOptions( + allowMultiple: pigeonMap['allowMultiple']! as bool, + selectFolders: pigeonMap['selectFolders']! as bool, + allowedTypes: + (pigeonMap['allowedTypes'] as List?)!.cast(), + ); + } +} + +class _FileSelectorApiCodec extends StandardMessageCodec { + const _FileSelectorApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is SelectionOptions) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is TypeGroup) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return SelectionOptions.decode(readValue(buffer)!); + + case 129: + return TypeGroup.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +class FileSelectorApi { + /// Constructor for [FileSelectorApi]. 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. + FileSelectorApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _FileSelectorApiCodec(); + + Future> showOpenDialog(SelectionOptions arg_options, + String? arg_initialDirectory, String? arg_confirmButtonText) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FileSelectorApi.showOpenDialog', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel.send( + [arg_options, arg_initialDirectory, arg_confirmButtonText]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as List?)!.cast(); + } + } + + Future> showSaveDialog( + SelectionOptions arg_options, + String? arg_initialDirectory, + String? arg_suggestedName, + String? arg_confirmButtonText) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FileSelectorApi.showSaveDialog', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel.send([ + arg_options, + arg_initialDirectory, + arg_suggestedName, + arg_confirmButtonText + ]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as List?)!.cast(); + } + } +} diff --git a/packages/file_selector/file_selector_windows/pigeons/copyright.txt b/packages/file_selector/file_selector_windows/pigeons/copyright.txt new file mode 100644 index 000000000000..1236b63caf3a --- /dev/null +++ b/packages/file_selector/file_selector_windows/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. diff --git a/packages/file_selector/file_selector_windows/pigeons/messages.dart b/packages/file_selector/file_selector_windows/pigeons/messages.dart new file mode 100644 index 000000000000..f2c9ab71bd82 --- /dev/null +++ b/packages/file_selector/file_selector_windows/pigeons/messages.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:pigeon/pigeon.dart'; + +@ConfigurePigeon(PigeonOptions( + dartOut: 'lib/src/messages.g.dart', + dartTestOut: 'test/test_api.dart', + cppOptions: CppOptions(namespace: 'file_selector_windows'), + cppHeaderOut: 'windows/messages.g.h', + cppSourceOut: 'windows/messages.g.cpp', + copyrightHeader: 'pigeons/copyright.txt', +)) +class TypeGroup { + TypeGroup(this.label, {required this.extensions}); + + String label; + // TODO(stuartmorgan): Make the generic type non-nullable once supported. + // https://github.com/flutter/flutter/issues/97848 + // The C++ code treats all of it as non-nullable. + List extensions; +} + +class SelectionOptions { + SelectionOptions({ + this.allowMultiple = false, + this.selectFolders = false, + this.allowedTypes = const [], + }); + bool allowMultiple; + bool selectFolders; + + // TODO(stuartmorgan): Make the generic type non-nullable once supported. + // https://github.com/flutter/flutter/issues/97848 + // The C++ code treats the values as non-nullable. + List allowedTypes; +} + +@HostApi(dartHostTestHandler: 'TestFileSelectorApi') +abstract class FileSelectorApi { + List showOpenDialog( + SelectionOptions options, + String? initialDirectory, + String? confirmButtonText, + ); + List showSaveDialog( + SelectionOptions options, + String? initialDirectory, + String? suggestedName, + String? confirmButtonText, + ); +} diff --git a/packages/file_selector/file_selector_windows/pubspec.yaml b/packages/file_selector/file_selector_windows/pubspec.yaml index 13487feb8873..6e398381b27a 100644 --- a/packages/file_selector/file_selector_windows/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_windows description: Windows implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.9.0 +version: 0.9.1 environment: sdk: ">=2.12.0 <3.0.0" @@ -23,3 +23,8 @@ dependencies: sdk: flutter flutter_test: sdk: flutter + +dev_dependencies: + build_runner: 2.1.11 + mockito: ^5.1.0 + pigeon: ^3.2.5 diff --git a/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart b/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart index 48e13c2a9892..79277fae472b 100644 --- a/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart +++ b/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart @@ -4,326 +4,321 @@ import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:file_selector_windows/file_selector_windows.dart'; -import 'package:flutter/services.dart'; +import 'package:file_selector_windows/src/messages.g.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'file_selector_windows_test.mocks.dart'; +import 'test_api.dart'; + +@GenerateMocks([TestFileSelectorApi]) void main() { TestWidgetsFlutterBinding.ensureInitialized(); - group('FileSelectorWindows()', () { - final FileSelectorWindows plugin = FileSelectorWindows(); + final FileSelectorWindows plugin = FileSelectorWindows(); + late MockTestFileSelectorApi mockApi; + + setUp(() { + mockApi = MockTestFileSelectorApi(); + TestFileSelectorApi.setup(mockApi); + }); + + test('registered instance', () { + FileSelectorWindows.registerWith(); + expect(FileSelectorPlatform.instance, isA()); + }); + + group('#openFile', () { + setUp(() { + when(mockApi.showOpenDialog(any, any, any)).thenReturn(['foo']); + }); + + test('simple call works', () async { + final XFile? file = await plugin.openFile(); + + expect(file!.path, 'foo'); + final VerificationResult result = + verify(mockApi.showOpenDialog(captureAny, null, null)); + final SelectionOptions options = result.captured[0] as SelectionOptions; + expect(options.allowMultiple, false); + expect(options.selectFolders, false); + }); + + test('passes the accepted type groups correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + extensions: ['txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); + + final XTypeGroup groupTwo = XTypeGroup( + label: 'image', + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image']); + + await plugin.openFile(acceptedTypeGroups: [group, groupTwo]); + + final VerificationResult result = + verify(mockApi.showOpenDialog(captureAny, null, null)); + final SelectionOptions options = result.captured[0] as SelectionOptions; + expect( + _typeGroupListsMatch(options.allowedTypes, [ + TypeGroup(label: 'text', extensions: ['txt']), + TypeGroup(label: 'image', extensions: ['jpg']), + ]), + true); + }); + + test('passes initialDirectory correctly', () async { + await plugin.openFile(initialDirectory: '/example/directory'); + + verify(mockApi.showOpenDialog(any, '/example/directory', null)); + }); + + test('passes confirmButtonText correctly', () async { + await plugin.openFile(confirmButtonText: 'Open File'); + + verify(mockApi.showOpenDialog(any, null, 'Open File')); + }); + + test('throws for a type group that does not support Windows', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + mimeTypes: ['text/plain'], + ); + + await expectLater( + plugin.openFile(acceptedTypeGroups: [group]), + throwsArgumentError); + }); + + test('allows a wildcard group', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + ); + + await expectLater( + plugin.openFile(acceptedTypeGroups: [group]), completes); + }); + }); + + group('#openFiles', () { + setUp(() { + when(mockApi.showOpenDialog(any, any, any)) + .thenReturn(['foo', 'bar']); + }); + + test('simple call works', () async { + final List file = await plugin.openFiles(); + + expect(file[0].path, 'foo'); + expect(file[1].path, 'bar'); + final VerificationResult result = + verify(mockApi.showOpenDialog(captureAny, null, null)); + final SelectionOptions options = result.captured[0] as SelectionOptions; + expect(options.allowMultiple, true); + expect(options.selectFolders, false); + }); + + test('passes the accepted type groups correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + extensions: ['txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); + + final XTypeGroup groupTwo = XTypeGroup( + label: 'image', + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image']); + + await plugin.openFiles(acceptedTypeGroups: [group, groupTwo]); + + final VerificationResult result = + verify(mockApi.showOpenDialog(captureAny, null, null)); + final SelectionOptions options = result.captured[0] as SelectionOptions; + expect( + _typeGroupListsMatch(options.allowedTypes, [ + TypeGroup(label: 'text', extensions: ['txt']), + TypeGroup(label: 'image', extensions: ['jpg']), + ]), + true); + }); + + test('passes initialDirectory correctly', () async { + await plugin.openFiles(initialDirectory: '/example/directory'); + + verify(mockApi.showOpenDialog(any, '/example/directory', null)); + }); + + test('passes confirmButtonText correctly', () async { + await plugin.openFiles(confirmButtonText: 'Open Files'); + + verify(mockApi.showOpenDialog(any, null, 'Open Files')); + }); + + test('throws for a type group that does not support Windows', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + mimeTypes: ['text/plain'], + ); + + await expectLater( + plugin.openFiles(acceptedTypeGroups: [group]), + throwsArgumentError); + }); + + test('allows a wildcard group', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + ); + + await expectLater( + plugin.openFiles(acceptedTypeGroups: [group]), completes); + }); + }); + + group('#getDirectoryPath', () { + setUp(() { + when(mockApi.showOpenDialog(any, any, any)).thenReturn(['foo']); + }); + + test('simple call works', () async { + final String? path = await plugin.getDirectoryPath(); + + expect(path, 'foo'); + final VerificationResult result = + verify(mockApi.showOpenDialog(captureAny, null, null)); + final SelectionOptions options = result.captured[0] as SelectionOptions; + expect(options.allowMultiple, false); + expect(options.selectFolders, true); + }); + + test('passes initialDirectory correctly', () async { + await plugin.getDirectoryPath(initialDirectory: '/example/directory'); - final List log = []; + verify(mockApi.showOpenDialog(any, '/example/directory', null)); + }); + + test('passes confirmButtonText correctly', () async { + await plugin.getDirectoryPath(confirmButtonText: 'Open Directory'); + + verify(mockApi.showOpenDialog(any, null, 'Open Directory')); + }); + }); + group('#getSavePath', () { setUp(() { - plugin.channel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - return null; - }); + when(mockApi.showSaveDialog(any, any, any, any)) + .thenReturn(['foo']); + }); + + test('simple call works', () async { + final String? path = await plugin.getSavePath(); + + expect(path, 'foo'); + final VerificationResult result = + verify(mockApi.showSaveDialog(captureAny, null, null, null)); + final SelectionOptions options = result.captured[0] as SelectionOptions; + expect(options.allowMultiple, false); + expect(options.selectFolders, false); + }); - log.clear(); + test('passes the accepted type groups correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + extensions: ['txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); + + final XTypeGroup groupTwo = XTypeGroup( + label: 'image', + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image']); + + await plugin + .getSavePath(acceptedTypeGroups: [group, groupTwo]); + + final VerificationResult result = + verify(mockApi.showSaveDialog(captureAny, null, null, null)); + final SelectionOptions options = result.captured[0] as SelectionOptions; + expect( + _typeGroupListsMatch(options.allowedTypes, [ + TypeGroup(label: 'text', extensions: ['txt']), + TypeGroup(label: 'image', extensions: ['jpg']), + ]), + true); }); - test('registered instance', () { - FileSelectorWindows.registerWith(); - expect(FileSelectorPlatform.instance, isA()); + test('passes initialDirectory correctly', () async { + await plugin.getSavePath(initialDirectory: '/example/directory'); + + verify(mockApi.showSaveDialog(any, '/example/directory', null, null)); }); - group('#openFile', () { - test('passes the accepted type groups correctly', () async { - final XTypeGroup group = XTypeGroup( - label: 'text', - extensions: ['txt'], - mimeTypes: ['text/plain'], - macUTIs: ['public.text'], - ); - - final XTypeGroup groupTwo = XTypeGroup( - label: 'image', - extensions: ['jpg'], - mimeTypes: ['image/jpg'], - macUTIs: ['public.image'], - webWildCards: ['image/*']); - - await plugin - .openFile(acceptedTypeGroups: [group, groupTwo]); - - expect( - log, - [ - isMethodCall('openFile', arguments: { - 'acceptedTypeGroups': >[ - group.toJSON(), - groupTwo.toJSON() - ], - 'initialDirectory': null, - 'confirmButtonText': null, - 'multiple': false, - }), - ], - ); - }); - - test('passes initialDirectory correctly', () async { - await plugin.openFile(initialDirectory: '/example/directory'); - - expect( - log, - [ - isMethodCall('openFile', arguments: { - 'acceptedTypeGroups': null, - 'initialDirectory': '/example/directory', - 'confirmButtonText': null, - 'multiple': false, - }), - ], - ); - }); - - test('passes confirmButtonText correctly', () async { - await plugin.openFile(confirmButtonText: 'Open File'); - - expect( - log, - [ - isMethodCall('openFile', arguments: { - 'acceptedTypeGroups': null, - 'initialDirectory': null, - 'confirmButtonText': 'Open File', - 'multiple': false, - }), - ], - ); - }); - - test('throws for a type group that does not support Windows', () async { - final XTypeGroup group = XTypeGroup( - label: 'text', - mimeTypes: ['text/plain'], - ); - - await expectLater( - plugin.openFile(acceptedTypeGroups: [group]), - throwsArgumentError); - }); - - test('allows a wildcard group', () async { - final XTypeGroup group = XTypeGroup( - label: 'text', - ); - - await expectLater( - plugin.openFile(acceptedTypeGroups: [group]), - completes); - }); + test('passes suggestedName correctly', () async { + await plugin.getSavePath(suggestedName: 'baz.txt'); + + verify(mockApi.showSaveDialog(any, null, 'baz.txt', null)); }); - group('#openFiles', () { - test('passes the accepted type groups correctly', () async { - final XTypeGroup group = XTypeGroup( - label: 'text', - extensions: ['txt'], - mimeTypes: ['text/plain'], - macUTIs: ['public.text'], - ); - - final XTypeGroup groupTwo = XTypeGroup( - label: 'image', - extensions: ['jpg'], - mimeTypes: ['image/jpg'], - macUTIs: ['public.image'], - webWildCards: ['image/*']); - - await plugin - .openFiles(acceptedTypeGroups: [group, groupTwo]); - - expect( - log, - [ - isMethodCall('openFile', arguments: { - 'acceptedTypeGroups': >[ - group.toJSON(), - groupTwo.toJSON() - ], - 'initialDirectory': null, - 'confirmButtonText': null, - 'multiple': true, - }), - ], - ); - }); - - test('passes initialDirectory correctly', () async { - await plugin.openFiles(initialDirectory: '/example/directory'); - - expect( - log, - [ - isMethodCall('openFile', arguments: { - 'acceptedTypeGroups': null, - 'initialDirectory': '/example/directory', - 'confirmButtonText': null, - 'multiple': true, - }), - ], - ); - }); - - test('passes confirmButtonText correctly', () async { - await plugin.openFiles(confirmButtonText: 'Open File'); - - expect( - log, - [ - isMethodCall('openFile', arguments: { - 'acceptedTypeGroups': null, - 'initialDirectory': null, - 'confirmButtonText': 'Open File', - 'multiple': true, - }), - ], - ); - }); - - test('throws for a type group that does not support Windows', () async { - final XTypeGroup group = XTypeGroup( - label: 'text', - mimeTypes: ['text/plain'], - ); - - await expectLater( - plugin.openFiles(acceptedTypeGroups: [group]), - throwsArgumentError); - }); - - test('allows a wildcard group', () async { - final XTypeGroup group = XTypeGroup( - label: 'text', - ); - - await expectLater( - plugin.openFiles(acceptedTypeGroups: [group]), - completes); - }); + test('passes confirmButtonText correctly', () async { + await plugin.getSavePath(confirmButtonText: 'Save File'); + + verify(mockApi.showSaveDialog(any, null, null, 'Save File')); }); - group('#getSavePath', () { - test('passes the accepted type groups correctly', () async { - final XTypeGroup group = XTypeGroup( - label: 'text', - extensions: ['txt'], - mimeTypes: ['text/plain'], - macUTIs: ['public.text'], - ); - - final XTypeGroup groupTwo = XTypeGroup( - label: 'image', - extensions: ['jpg'], - mimeTypes: ['image/jpg'], - macUTIs: ['public.image'], - webWildCards: ['image/*']); - - await plugin - .getSavePath(acceptedTypeGroups: [group, groupTwo]); - - expect( - log, - [ - isMethodCall('getSavePath', arguments: { - 'acceptedTypeGroups': >[ - group.toJSON(), - groupTwo.toJSON() - ], - 'initialDirectory': null, - 'suggestedName': null, - 'confirmButtonText': null, - }), - ], - ); - }); - - test('passes initialDirectory correctly', () async { - await plugin.getSavePath(initialDirectory: '/example/directory'); - - expect( - log, - [ - isMethodCall('getSavePath', arguments: { - 'acceptedTypeGroups': null, - 'initialDirectory': '/example/directory', - 'suggestedName': null, - 'confirmButtonText': null, - }), - ], - ); - }); - - test('passes confirmButtonText correctly', () async { - await plugin.getSavePath(confirmButtonText: 'Open File'); - - expect( - log, - [ - isMethodCall('getSavePath', arguments: { - 'acceptedTypeGroups': null, - 'initialDirectory': null, - 'suggestedName': null, - 'confirmButtonText': 'Open File', - }), - ], - ); - }); - - test('throws for a type group that does not support Windows', () async { - final XTypeGroup group = XTypeGroup( - label: 'text', - mimeTypes: ['text/plain'], - ); - - await expectLater( - plugin.getSavePath(acceptedTypeGroups: [group]), - throwsArgumentError); - }); - - test('allows a wildcard group', () async { - final XTypeGroup group = XTypeGroup( - label: 'text', - ); - - await expectLater( - plugin.getSavePath(acceptedTypeGroups: [group]), - completes); - }); + test('throws for a type group that does not support Windows', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + mimeTypes: ['text/plain'], + ); + + await expectLater( + plugin.getSavePath(acceptedTypeGroups: [group]), + throwsArgumentError); }); - group('#getDirectoryPath', () { - test('passes initialDirectory correctly', () async { - await plugin.getDirectoryPath(initialDirectory: '/example/directory'); - - expect( - log, - [ - isMethodCall('getDirectoryPath', arguments: { - 'initialDirectory': '/example/directory', - 'confirmButtonText': null, - }), - ], - ); - }); - - test('passes confirmButtonText correctly', () async { - await plugin.getDirectoryPath(confirmButtonText: 'Open File'); - - expect( - log, - [ - isMethodCall('getDirectoryPath', arguments: { - 'initialDirectory': null, - 'confirmButtonText': 'Open File', - }), - ], - ); - }); + test('allows a wildcard group', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + ); + + await expectLater( + plugin.getSavePath(acceptedTypeGroups: [group]), + completes); }); }); } + +// True if the given options match. +// +// This is needed because Pigeon data classes don't have custom equality checks, +// so only match for identical instances. +bool _typeGroupListsMatch(List a, List b) { + if (a.length != b.length) { + return false; + } + for (int i = 0; i < a.length; i++) { + if (!_typeGroupsMatch(a[i], b[i])) { + return false; + } + } + return true; +} + +// True if the given type groups match. +// +// This is needed because Pigeon data classes don't have custom equality checks, +// so only match for identical instances. +bool _typeGroupsMatch(TypeGroup? a, TypeGroup? b) { + return a!.label == b!.label && listEquals(a.extensions, b.extensions); +} diff --git a/packages/file_selector/file_selector_windows/test/file_selector_windows_test.mocks.dart b/packages/file_selector/file_selector_windows/test/file_selector_windows_test.mocks.dart new file mode 100644 index 000000000000..61e17fcdfeaa --- /dev/null +++ b/packages/file_selector/file_selector_windows/test/file_selector_windows_test.mocks.dart @@ -0,0 +1,46 @@ +// Mocks generated by Mockito 5.2.0 from annotations +// in file_selector_windows/example/windows/flutter/ephemeral/.plugin_symlinks/file_selector_windows/test/file_selector_windows_test.dart. +// Do not manually edit this file. + +import 'package:file_selector_windows/src/messages.g.dart' as _i3; +import 'package:mockito/mockito.dart' as _i1; + +import 'test_api.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: 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 + +/// A class which mocks [TestFileSelectorApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestFileSelectorApi extends _i1.Mock + implements _i2.TestFileSelectorApi { + MockTestFileSelectorApi() { + _i1.throwOnMissingStub(this); + } + + @override + List showOpenDialog(_i3.SelectionOptions? options, + String? initialDirectory, String? confirmButtonText) => + (super.noSuchMethod( + Invocation.method( + #showOpenDialog, [options, initialDirectory, confirmButtonText]), + returnValue: []) as List); + @override + List showSaveDialog( + _i3.SelectionOptions? options, + String? initialDirectory, + String? suggestedName, + String? confirmButtonText) => + (super.noSuchMethod( + Invocation.method(#showSaveDialog, + [options, initialDirectory, suggestedName, confirmButtonText]), + returnValue: []) as List); +} diff --git a/packages/file_selector/file_selector_windows/test/test_api.dart b/packages/file_selector/file_selector_windows/test/test_api.dart new file mode 100644 index 000000000000..f9b979f7b854 --- /dev/null +++ b/packages/file_selector/file_selector_windows/test/test_api.dart @@ -0,0 +1,105 @@ +// 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 (v3.2.5), 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 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +// ignore: directives_ordering +import 'package:file_selector_windows/src/messages.g.dart'; + +class _TestFileSelectorApiCodec extends StandardMessageCodec { + const _TestFileSelectorApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is SelectionOptions) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is TypeGroup) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return SelectionOptions.decode(readValue(buffer)!); + + case 129: + return TypeGroup.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestFileSelectorApi { + static const MessageCodec codec = _TestFileSelectorApiCodec(); + + List showOpenDialog(SelectionOptions options, + String? initialDirectory, String? confirmButtonText); + List showSaveDialog( + SelectionOptions options, + String? initialDirectory, + String? suggestedName, + String? confirmButtonText); + static void setup(TestFileSelectorApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FileSelectorApi.showOpenDialog', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.FileSelectorApi.showOpenDialog was null.'); + final List args = (message as List?)!; + final SelectionOptions? arg_options = (args[0] as SelectionOptions?); + assert(arg_options != null, + 'Argument for dev.flutter.pigeon.FileSelectorApi.showOpenDialog was null, expected non-null SelectionOptions.'); + final String? arg_initialDirectory = (args[1] as String?); + final String? arg_confirmButtonText = (args[2] as String?); + final List output = api.showOpenDialog( + arg_options!, arg_initialDirectory, arg_confirmButtonText); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FileSelectorApi.showSaveDialog', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.FileSelectorApi.showSaveDialog was null.'); + final List args = (message as List?)!; + final SelectionOptions? arg_options = (args[0] as SelectionOptions?); + assert(arg_options != null, + 'Argument for dev.flutter.pigeon.FileSelectorApi.showSaveDialog was null, expected non-null SelectionOptions.'); + final String? arg_initialDirectory = (args[1] as String?); + final String? arg_suggestedName = (args[2] as String?); + final String? arg_confirmButtonText = (args[3] as String?); + final List output = api.showSaveDialog(arg_options!, + arg_initialDirectory, arg_suggestedName, arg_confirmButtonText); + return {'result': output}; + }); + } + } + } +} diff --git a/packages/file_selector/file_selector_windows/windows/CMakeLists.txt b/packages/file_selector/file_selector_windows/windows/CMakeLists.txt index 01c9f58b98a2..e06f3749e0f7 100644 --- a/packages/file_selector/file_selector_windows/windows/CMakeLists.txt +++ b/packages/file_selector/file_selector_windows/windows/CMakeLists.txt @@ -9,6 +9,8 @@ list(APPEND PLUGIN_SOURCES "file_dialog_controller.h" "file_selector_plugin.cpp" "file_selector_plugin.h" + "messages.g.cpp" + "messages.g.h" "string_utils.cpp" "string_utils.h" ) @@ -24,6 +26,9 @@ target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) target_include_directories(${PLUGIN_NAME} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin) +# Override apply_standard_settings for exceptions due to +# https://developercommunity.visualstudio.com/t/stdany-doesnt-link-when-exceptions-are-disabled/376072 +target_compile_definitions(${PLUGIN_NAME} PRIVATE "_HAS_EXCEPTIONS=1") # List of absolute paths to libraries that should be bundled with the plugin set(file_selector_bundled_libraries @@ -67,6 +72,9 @@ apply_standard_settings(${TEST_RUNNER}) target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") target_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin) target_link_libraries(${TEST_RUNNER} PRIVATE gtest gmock) +# Override apply_standard_settings for exceptions due to +# https://developercommunity.visualstudio.com/t/stdany-doesnt-link-when-exceptions-are-disabled/376072 +target_compile_definitions(${TEST_RUNNER} PRIVATE "_HAS_EXCEPTIONS=1") # flutter_wrapper_plugin has link dependencies on the Flutter DLL. add_custom_command(TARGET ${TEST_RUNNER} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different diff --git a/packages/file_selector/file_selector_windows/windows/file_selector_plugin.cpp b/packages/file_selector/file_selector_windows/windows/file_selector_plugin.cpp index 870bc281b6f6..7f37a1a50d50 100644 --- a/packages/file_selector/file_selector_windows/windows/file_selector_plugin.cpp +++ b/packages/file_selector/file_selector_windows/windows/file_selector_plugin.cpp @@ -33,33 +33,8 @@ using flutter::EncodableList; using flutter::EncodableMap; using flutter::EncodableValue; -// From file_selector_windows.dart -constexpr char kChannelName[] = "plugins.flutter.io/file_selector_windows"; - -constexpr char kOpenFileMethod[] = "openFile"; -constexpr char kGetSavePathMethod[] = "getSavePath"; -constexpr char kGetDirectoryPathMethod[] = "getDirectoryPath"; - -constexpr char kAcceptedTypeGroupsKey[] = "acceptedTypeGroups"; -constexpr char kConfirmButtonTextKey[] = "confirmButtonText"; -constexpr char kInitialDirectoryKey[] = "initialDirectory"; -constexpr char kMultipleKey[] = "multiple"; -constexpr char kSuggestedNameKey[] = "suggestedName"; - -// From x_type_group.dart -// Only 'extensions' are supported by Windows for filtering. -constexpr char kTypeGroupLabelKey[] = "label"; -constexpr char kTypeGroupExtensionsKey[] = "extensions"; - -// Looks for |key| in |map|, returning the associated value if it is present, or -// a nullptr if not. -const EncodableValue* ValueOrNull(const EncodableMap& map, const char* key) { - auto it = map.find(EncodableValue(key)); - if (it == map.end()) { - return nullptr; - } - return &(it->second); -} +// The kind of file dialog to show. +enum class DialogMode { open, save }; // Returns the path for |shell_item| as a UTF-8 string, or an // empty string on failure. @@ -111,7 +86,7 @@ class DialogWrapper { // Attempts to set the default folder for the dialog to |path|, // if it exists. - void SetDefaultFolder(const std::string& path) { + void SetDefaultFolder(std::string_view path) { std::wstring wide_path = Utf16FromUtf8(path); IShellItemPtr item; last_result_ = SHCreateItemFromParsingName(wide_path.c_str(), nullptr, @@ -123,13 +98,13 @@ class DialogWrapper { } // Sets the file name that is initially shown in the dialog. - void SetFileName(const std::string& name) { + void SetFileName(std::string_view name) { std::wstring wide_name = Utf16FromUtf8(name); last_result_ = dialog_controller_->SetFileName(wide_name.c_str()); } // Sets the label of the confirmation button. - void SetOkButtonLabel(const std::string& label) { + void SetOkButtonLabel(std::string_view label) { std::wstring wide_label = Utf16FromUtf8(label); last_result_ = dialog_controller_->SetOkButtonLabel(wide_label.c_str()); } @@ -161,18 +136,15 @@ class DialogWrapper { filter_names.reserve(filters.size()); for (const EncodableValue& filter_info_value : filters) { - const auto& filter_info = std::get(filter_info_value); - const auto* filter_name = std::get_if( - ValueOrNull(filter_info, kTypeGroupLabelKey)); - const auto* extensions = std::get_if( - ValueOrNull(filter_info, kTypeGroupExtensionsKey)); - filter_names.push_back(filter_name ? Utf16FromUtf8(*filter_name) : L""); + const auto& type_group = std::any_cast( + std::get(filter_info_value)); + filter_names.push_back(Utf16FromUtf8(type_group.label())); filter_extensions.push_back(L""); std::wstring& spec = filter_extensions.back(); - if (!extensions || extensions->empty()) { + if (type_group.extensions().empty()) { spec += L"*.*"; } else { - for (const EncodableValue& extension : *extensions) { + for (const EncodableValue& extension : type_group.extensions()) { if (!spec.empty()) { spec += spec_delimiter; } @@ -186,50 +158,39 @@ class DialogWrapper { static_cast(filter_specs.size()), filter_specs.data()); } - // Displays the dialog, and returns the selected file or files as an - // EncodableValue of type List (for open) or String (for save), or a null - // EncodableValue on cancel or error. - EncodableValue Show(HWND parent_window) { + // Displays the dialog, and returns the selected files, or nullopt on error. + std::optional Show(HWND parent_window) { assert(dialog_controller_); last_result_ = dialog_controller_->Show(parent_window); if (!SUCCEEDED(last_result_)) { - return EncodableValue(); + return std::nullopt; } + EncodableList files; if (is_open_dialog_) { IShellItemArrayPtr shell_items; last_result_ = dialog_controller_->GetResults(&shell_items); if (!SUCCEEDED(last_result_)) { - return EncodableValue(); + return std::nullopt; } IEnumShellItemsPtr item_enumerator; last_result_ = shell_items->EnumItems(&item_enumerator); if (!SUCCEEDED(last_result_)) { - return EncodableValue(); + return std::nullopt; } - EncodableList files; IShellItemPtr shell_item; while (item_enumerator->Next(1, &shell_item, nullptr) == S_OK) { files.push_back(EncodableValue(GetPathForShellItem(shell_item))); } - if (opening_directory_) { - // The directory option expects a String, not a List. - if (files.empty()) { - return EncodableValue(); - } - return EncodableValue(files[0]); - } else { - return EncodableValue(std::move(files)); - } } else { IShellItemPtr shell_item; last_result_ = dialog_controller_->GetResult(&shell_item); if (!SUCCEEDED(last_result_)) { - return EncodableValue(); + return std::nullopt; } - EncodableValue file(GetPathForShellItem(shell_item)); - return file; + files.push_back(EncodableValue(GetPathForShellItem(shell_item))); } + return files; } // Returns the result of the last Win32 API call related to this object. @@ -244,67 +205,54 @@ class DialogWrapper { HRESULT last_result_; }; -// Displays the open or save dialog (according to |method|) and sends the -// selected file path(s) back to the engine via |result|, or sends an -// error on failure. -// -// |result| is guaranteed to be resolved by this function. -void ShowDialog(const FileDialogControllerFactory& dialog_factory, - HWND parent_window, const std::string& method, - const EncodableMap& args, - std::unique_ptr> result) { - IID dialog_type = method.compare(kGetSavePathMethod) == 0 - ? CLSID_FileSaveDialog - : CLSID_FileOpenDialog; +ErrorOr ShowDialog( + const FileDialogControllerFactory& dialog_factory, HWND parent_window, + DialogMode mode, const SelectionOptions& options, + const std::string* initial_directory, const std::string* suggested_name, + const std::string* confirm_label) { + IID dialog_type = + mode == DialogMode::save ? CLSID_FileSaveDialog : CLSID_FileOpenDialog; DialogWrapper dialog(dialog_factory, dialog_type); if (!SUCCEEDED(dialog.last_result())) { - result->Error("System error", "Could not create dialog", - EncodableValue(dialog.last_result())); - return; + return FlutterError("System error", "Could not create dialog", + EncodableValue(dialog.last_result())); } FILEOPENDIALOGOPTIONS dialog_options = 0; - if (method.compare(kGetDirectoryPathMethod) == 0) { + if (options.select_folders()) { dialog_options |= FOS_PICKFOLDERS; } - const auto* allow_multiple_selection = - std::get_if(ValueOrNull(args, kMultipleKey)); - if (allow_multiple_selection && *allow_multiple_selection) { + if (options.allow_multiple()) { dialog_options |= FOS_ALLOWMULTISELECT; } if (dialog_options != 0) { dialog.AddOptions(dialog_options); } - const auto* initial_dir = - std::get_if(ValueOrNull(args, kInitialDirectoryKey)); - if (initial_dir) { - dialog.SetDefaultFolder(*initial_dir); + if (initial_directory) { + dialog.SetDefaultFolder(*initial_directory); } - const auto* suggested_name = - std::get_if(ValueOrNull(args, kSuggestedNameKey)); if (suggested_name) { dialog.SetFileName(*suggested_name); } - const auto* confirm_label = - std::get_if(ValueOrNull(args, kConfirmButtonTextKey)); if (confirm_label) { dialog.SetOkButtonLabel(*confirm_label); } - const auto* accepted_types = - std::get_if(ValueOrNull(args, kAcceptedTypeGroupsKey)); - if (accepted_types && !accepted_types->empty()) { - dialog.SetFileTypeFilters(*accepted_types); + + if (!options.allowed_types().empty()) { + dialog.SetFileTypeFilters(options.allowed_types()); } - EncodableValue files = dialog.Show(parent_window); - if (files.IsNull() && - dialog.last_result() != HRESULT_FROM_WIN32(ERROR_CANCELLED)) { - ; - result->Error("System error", "Could not show dialog", - EncodableValue(dialog.last_result())); + std::optional files = dialog.Show(parent_window); + if (!files) { + if (dialog.last_result() != HRESULT_FROM_WIN32(ERROR_CANCELLED)) { + return FlutterError("System error", "Could not show dialog", + EncodableValue(dialog.last_result())); + } else { + return EncodableList(); + } } - result->Success(files); + return std::move(files.value()); } // Returns the top-level window that owns |view|. @@ -317,20 +265,12 @@ HWND GetRootWindow(flutter::FlutterView* view) { // static void FileSelectorPlugin::RegisterWithRegistrar( flutter::PluginRegistrarWindows* registrar) { - auto channel = std::make_unique>( - registrar->messenger(), kChannelName, - &flutter::StandardMethodCodec::GetInstance()); - std::unique_ptr plugin = std::make_unique( [registrar] { return GetRootWindow(registrar->GetView()); }, std::make_unique()); - channel->SetMethodCallHandler( - [plugin_pointer = plugin.get()](const auto& call, auto result) { - plugin_pointer->HandleMethodCall(call, std::move(result)); - }); - + FileSelectorApi::SetUp(registrar->messenger(), plugin.get()); registrar->AddPlugin(std::move(plugin)); } @@ -342,21 +282,19 @@ FileSelectorPlugin::FileSelectorPlugin( FileSelectorPlugin::~FileSelectorPlugin() = default; -void FileSelectorPlugin::HandleMethodCall( - const flutter::MethodCall<>& method_call, - std::unique_ptr> result) { - const std::string& method_name = method_call.method_name(); - if (method_name.compare(kOpenFileMethod) == 0 || - method_name.compare(kGetSavePathMethod) == 0 || - method_name.compare(kGetDirectoryPathMethod) == 0) { - const auto* arguments = - std::get_if(method_call.arguments()); - assert(arguments); - ShowDialog(*controller_factory_, get_root_window_(), method_name, - *arguments, std::move(result)); - } else { - result->NotImplemented(); - } +ErrorOr FileSelectorPlugin::ShowOpenDialog( + const SelectionOptions& options, const std::string* initialDirectory, + const std::string* confirmButtonText) { + return ShowDialog(*controller_factory_, get_root_window_(), DialogMode::open, + options, initialDirectory, nullptr, confirmButtonText); +} + +ErrorOr FileSelectorPlugin::ShowSaveDialog( + const SelectionOptions& options, const std::string* initialDirectory, + const std::string* suggestedName, const std::string* confirmButtonText) { + return ShowDialog(*controller_factory_, get_root_window_(), DialogMode::save, + options, initialDirectory, suggestedName, + confirmButtonText); } } // namespace file_selector_windows diff --git a/packages/file_selector/file_selector_windows/windows/file_selector_plugin.h b/packages/file_selector/file_selector_windows/windows/file_selector_plugin.h index 292d312bea30..1388bfd3898d 100644 --- a/packages/file_selector/file_selector_windows/windows/file_selector_plugin.h +++ b/packages/file_selector/file_selector_windows/windows/file_selector_plugin.h @@ -10,6 +10,7 @@ #include #include "file_dialog_controller.h" +#include "messages.g.h" namespace file_selector_windows { @@ -18,7 +19,7 @@ namespace file_selector_windows { // around https://github.com/flutter/flutter/issues/90694. using FlutterRootWindowProvider = std::function; -class FileSelectorPlugin : public flutter::Plugin { +class FileSelectorPlugin : public flutter::Plugin, public FileSelectorApi { public: static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); @@ -30,9 +31,14 @@ class FileSelectorPlugin : public flutter::Plugin { virtual ~FileSelectorPlugin(); - // Called when a method is called on plugin channel; - void HandleMethodCall(const flutter::MethodCall<>& method_call, - std::unique_ptr> result); + // FileSelectorApi + ErrorOr ShowOpenDialog( + const SelectionOptions& options, const std::string* initial_directory, + const std::string* confirm_button_text) override; + ErrorOr ShowSaveDialog( + const SelectionOptions& options, const std::string* initialDirectory, + const std::string* suggestedName, + const std::string* confirmButtonText) override; private: // The provider for the root window to attach the dialog to. diff --git a/packages/file_selector/file_selector_windows/windows/messages.g.cpp b/packages/file_selector/file_selector_windows/windows/messages.g.cpp new file mode 100644 index 000000000000..04e529d8b35a --- /dev/null +++ b/packages/file_selector/file_selector_windows/windows/messages.g.cpp @@ -0,0 +1,278 @@ +// 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 (v3.2.5), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#undef _HAS_EXCEPTIONS + +#include "messages.g.h" + +#include +#include +#include +#include + +#include +#include +#include + +namespace file_selector_windows { + +/* TypeGroup */ + +const std::string& TypeGroup::label() const { return label_; } +void TypeGroup::set_label(std::string_view value_arg) { label_ = value_arg; } + +const flutter::EncodableList& TypeGroup::extensions() const { + return extensions_; +} +void TypeGroup::set_extensions(const flutter::EncodableList& value_arg) { + extensions_ = value_arg; +} + +flutter::EncodableMap TypeGroup::ToEncodableMap() const { + return flutter::EncodableMap{ + {flutter::EncodableValue("label"), flutter::EncodableValue(label_)}, + {flutter::EncodableValue("extensions"), + flutter::EncodableValue(extensions_)}, + }; +} + +TypeGroup::TypeGroup() {} + +TypeGroup::TypeGroup(flutter::EncodableMap map) { + auto& encodable_label = map.at(flutter::EncodableValue("label")); + if (const std::string* pointer_label = + std::get_if(&encodable_label)) { + label_ = *pointer_label; + } + auto& encodable_extensions = map.at(flutter::EncodableValue("extensions")); + if (const flutter::EncodableList* pointer_extensions = + std::get_if(&encodable_extensions)) { + extensions_ = *pointer_extensions; + } +} + +/* SelectionOptions */ + +bool SelectionOptions::allow_multiple() const { return allow_multiple_; } +void SelectionOptions::set_allow_multiple(bool value_arg) { + allow_multiple_ = value_arg; +} + +bool SelectionOptions::select_folders() const { return select_folders_; } +void SelectionOptions::set_select_folders(bool value_arg) { + select_folders_ = value_arg; +} + +const flutter::EncodableList& SelectionOptions::allowed_types() const { + return allowed_types_; +} +void SelectionOptions::set_allowed_types( + const flutter::EncodableList& value_arg) { + allowed_types_ = value_arg; +} + +flutter::EncodableMap SelectionOptions::ToEncodableMap() const { + return flutter::EncodableMap{ + {flutter::EncodableValue("allowMultiple"), + flutter::EncodableValue(allow_multiple_)}, + {flutter::EncodableValue("selectFolders"), + flutter::EncodableValue(select_folders_)}, + {flutter::EncodableValue("allowedTypes"), + flutter::EncodableValue(allowed_types_)}, + }; +} + +SelectionOptions::SelectionOptions() {} + +SelectionOptions::SelectionOptions(flutter::EncodableMap map) { + auto& encodable_allow_multiple = + map.at(flutter::EncodableValue("allowMultiple")); + if (const bool* pointer_allow_multiple = + std::get_if(&encodable_allow_multiple)) { + allow_multiple_ = *pointer_allow_multiple; + } + auto& encodable_select_folders = + map.at(flutter::EncodableValue("selectFolders")); + if (const bool* pointer_select_folders = + std::get_if(&encodable_select_folders)) { + select_folders_ = *pointer_select_folders; + } + auto& encodable_allowed_types = + map.at(flutter::EncodableValue("allowedTypes")); + if (const flutter::EncodableList* pointer_allowed_types = + std::get_if(&encodable_allowed_types)) { + allowed_types_ = *pointer_allowed_types; + } +} + +FileSelectorApiCodecSerializer::FileSelectorApiCodecSerializer() {} +flutter::EncodableValue FileSelectorApiCodecSerializer::ReadValueOfType( + uint8_t type, flutter::ByteStreamReader* stream) const { + switch (type) { + case 128: + return flutter::CustomEncodableValue( + SelectionOptions(std::get(ReadValue(stream)))); + + case 129: + return flutter::CustomEncodableValue( + TypeGroup(std::get(ReadValue(stream)))); + + default: + return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + } +} + +void FileSelectorApiCodecSerializer::WriteValue( + const flutter::EncodableValue& value, + flutter::ByteStreamWriter* stream) const { + if (const flutter::CustomEncodableValue* custom_value = + std::get_if(&value)) { + if (custom_value->type() == typeid(SelectionOptions)) { + stream->WriteByte(128); + WriteValue( + std::any_cast(*custom_value).ToEncodableMap(), + stream); + return; + } + if (custom_value->type() == typeid(TypeGroup)) { + stream->WriteByte(129); + WriteValue(std::any_cast(*custom_value).ToEncodableMap(), + stream); + return; + } + } + flutter::StandardCodecSerializer::WriteValue(value, stream); +} + +/** The codec used by FileSelectorApi. */ +const flutter::StandardMessageCodec& FileSelectorApi::GetCodec() { + return flutter::StandardMessageCodec::GetInstance( + &FileSelectorApiCodecSerializer::GetInstance()); +} + +/** Sets up an instance of `FileSelectorApi` to handle messages through the + * `binary_messenger`. */ +void FileSelectorApi::SetUp(flutter::BinaryMessenger* binary_messenger, + FileSelectorApi* api) { + { + auto channel = + std::make_unique>( + binary_messenger, + "dev.flutter.pigeon.FileSelectorApi.showOpenDialog", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler( + [api](const flutter::EncodableValue& message, + const flutter::MessageReply& reply) { + flutter::EncodableMap wrapped; + try { + const auto& args = std::get(message); + const auto& encodable_options_arg = args.at(0); + if (encodable_options_arg.IsNull()) { + wrapped.emplace(flutter::EncodableValue("error"), + WrapError("options_arg unexpectedly null.")); + reply(wrapped); + return; + } + const auto& options_arg = std::any_cast( + std::get( + encodable_options_arg)); + const auto& encodable_initial_directory_arg = args.at(1); + const auto* initial_directory_arg = + std::get_if(&encodable_initial_directory_arg); + const auto& encodable_confirm_button_text_arg = args.at(2); + const auto* confirm_button_text_arg = + std::get_if(&encodable_confirm_button_text_arg); + ErrorOr output = api->ShowOpenDialog( + options_arg, initial_directory_arg, confirm_button_text_arg); + if (output.has_error()) { + wrapped.emplace(flutter::EncodableValue("error"), + WrapError(output.error())); + } else { + wrapped.emplace( + flutter::EncodableValue("result"), + flutter::EncodableValue(std::move(output).TakeValue())); + } + } catch (const std::exception& exception) { + wrapped.emplace(flutter::EncodableValue("error"), + WrapError(exception.what())); + } + reply(wrapped); + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = + std::make_unique>( + binary_messenger, + "dev.flutter.pigeon.FileSelectorApi.showSaveDialog", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler( + [api](const flutter::EncodableValue& message, + const flutter::MessageReply& reply) { + flutter::EncodableMap wrapped; + try { + const auto& args = std::get(message); + const auto& encodable_options_arg = args.at(0); + if (encodable_options_arg.IsNull()) { + wrapped.emplace(flutter::EncodableValue("error"), + WrapError("options_arg unexpectedly null.")); + reply(wrapped); + return; + } + const auto& options_arg = std::any_cast( + std::get( + encodable_options_arg)); + const auto& encodable_initial_directory_arg = args.at(1); + const auto* initial_directory_arg = + std::get_if(&encodable_initial_directory_arg); + const auto& encodable_suggested_name_arg = args.at(2); + const auto* suggested_name_arg = + std::get_if(&encodable_suggested_name_arg); + const auto& encodable_confirm_button_text_arg = args.at(3); + const auto* confirm_button_text_arg = + std::get_if(&encodable_confirm_button_text_arg); + ErrorOr output = api->ShowSaveDialog( + options_arg, initial_directory_arg, suggested_name_arg, + confirm_button_text_arg); + if (output.has_error()) { + wrapped.emplace(flutter::EncodableValue("error"), + WrapError(output.error())); + } else { + wrapped.emplace( + flutter::EncodableValue("result"), + flutter::EncodableValue(std::move(output).TakeValue())); + } + } catch (const std::exception& exception) { + wrapped.emplace(flutter::EncodableValue("error"), + WrapError(exception.what())); + } + reply(wrapped); + }); + } else { + channel->SetMessageHandler(nullptr); + } + } +} + +flutter::EncodableMap FileSelectorApi::WrapError( + std::string_view error_message) { + return flutter::EncodableMap( + {{flutter::EncodableValue("message"), + flutter::EncodableValue(std::string(error_message))}, + {flutter::EncodableValue("code"), flutter::EncodableValue("Error")}, + {flutter::EncodableValue("details"), flutter::EncodableValue()}}); +} +flutter::EncodableMap FileSelectorApi::WrapError(const FlutterError& error) { + return flutter::EncodableMap( + {{flutter::EncodableValue("message"), + flutter::EncodableValue(error.message())}, + {flutter::EncodableValue("code"), flutter::EncodableValue(error.code())}, + {flutter::EncodableValue("details"), error.details()}}); +} + +} // namespace file_selector_windows diff --git a/packages/file_selector/file_selector_windows/windows/messages.g.h b/packages/file_selector/file_selector_windows/windows/messages.g.h new file mode 100644 index 000000000000..fb496d2d66e2 --- /dev/null +++ b/packages/file_selector/file_selector_windows/windows/messages.g.h @@ -0,0 +1,149 @@ +// 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 (v3.2.5), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#ifndef PIGEON_MESSAGES_G_FILE_SELECTOR_WINDOWS_H_ +#define PIGEON_MESSAGES_G_FILE_SELECTOR_WINDOWS_H_ +#include +#include +#include +#include + +#include +#include +#include + +namespace file_selector_windows { + +/* Generated class from Pigeon. */ + +class FlutterError { + public: + FlutterError(const std::string& code) : code_(code) {} + FlutterError(const std::string& code, const std::string& message) + : code_(code), message_(message) {} + FlutterError(const std::string& code, const std::string& message, + const flutter::EncodableValue& details) + : code_(code), message_(message), details_(details) {} + + const std::string& code() const { return code_; } + const std::string& message() const { return message_; } + const flutter::EncodableValue& details() const { return details_; } + + private: + std::string code_; + std::string message_; + flutter::EncodableValue details_; +}; + +template +class ErrorOr { + public: + ErrorOr(const T& rhs) { new (&v_) T(rhs); } + ErrorOr(const T&& rhs) { v_ = std::move(rhs); } + ErrorOr(const FlutterError& rhs) { new (&v_) FlutterError(rhs); } + ErrorOr(const FlutterError&& rhs) { v_ = std::move(rhs); } + + bool has_error() const { return std::holds_alternative(v_); } + const T& value() const { return std::get(v_); }; + const FlutterError& error() const { return std::get(v_); }; + + private: + friend class FileSelectorApi; + ErrorOr() = default; + T TakeValue() && { return std::get(std::move(v_)); } + + std::variant v_; +}; + +/* Generated class from Pigeon that represents data sent in messages. */ +class TypeGroup { + public: + TypeGroup(); + const std::string& label() const; + void set_label(std::string_view value_arg); + + const flutter::EncodableList& extensions() const; + void set_extensions(const flutter::EncodableList& value_arg); + + private: + TypeGroup(flutter::EncodableMap map); + flutter::EncodableMap ToEncodableMap() const; + friend class FileSelectorApi; + friend class FileSelectorApiCodecSerializer; + std::string label_; + flutter::EncodableList extensions_; +}; + +/* Generated class from Pigeon that represents data sent in messages. */ +class SelectionOptions { + public: + SelectionOptions(); + bool allow_multiple() const; + void set_allow_multiple(bool value_arg); + + bool select_folders() const; + void set_select_folders(bool value_arg); + + const flutter::EncodableList& allowed_types() const; + void set_allowed_types(const flutter::EncodableList& value_arg); + + private: + SelectionOptions(flutter::EncodableMap map); + flutter::EncodableMap ToEncodableMap() const; + friend class FileSelectorApi; + friend class FileSelectorApiCodecSerializer; + bool allow_multiple_; + bool select_folders_; + flutter::EncodableList allowed_types_; +}; + +class FileSelectorApiCodecSerializer : public flutter::StandardCodecSerializer { + public: + inline static FileSelectorApiCodecSerializer& GetInstance() { + static FileSelectorApiCodecSerializer sInstance; + return sInstance; + } + + FileSelectorApiCodecSerializer(); + + public: + void WriteValue(const flutter::EncodableValue& value, + flutter::ByteStreamWriter* stream) const override; + + protected: + flutter::EncodableValue ReadValueOfType( + uint8_t type, flutter::ByteStreamReader* stream) const override; +}; + +/* Generated class from Pigeon that represents a handler of messages from + * Flutter. */ +class FileSelectorApi { + public: + FileSelectorApi(const FileSelectorApi&) = delete; + FileSelectorApi& operator=(const FileSelectorApi&) = delete; + virtual ~FileSelectorApi(){}; + virtual ErrorOr ShowOpenDialog( + const SelectionOptions& options, const std::string* initial_directory, + const std::string* confirm_button_text) = 0; + virtual ErrorOr ShowSaveDialog( + const SelectionOptions& options, const std::string* initial_directory, + const std::string* suggested_name, + const std::string* confirm_button_text) = 0; + + /** The codec used by FileSelectorApi. */ + static const flutter::StandardMessageCodec& GetCodec(); + /** Sets up an instance of `FileSelectorApi` to handle messages through the + * `binary_messenger`. */ + static void SetUp(flutter::BinaryMessenger* binary_messenger, + FileSelectorApi* api); + static flutter::EncodableMap WrapError(std::string_view error_message); + static flutter::EncodableMap WrapError(const FlutterError& error); + + protected: + FileSelectorApi() = default; +}; +} // namespace file_selector_windows +#endif // PIGEON_MESSAGES_G_FILE_SELECTOR_WINDOWS_H_ diff --git a/packages/file_selector/file_selector_windows/windows/string_utils.cpp b/packages/file_selector/file_selector_windows/windows/string_utils.cpp index 933500f34445..6fa7c18403a7 100644 --- a/packages/file_selector/file_selector_windows/windows/string_utils.cpp +++ b/packages/file_selector/file_selector_windows/windows/string_utils.cpp @@ -12,7 +12,7 @@ namespace file_selector_windows { // Converts the given UTF-16 string to UTF-8. -std::string Utf8FromUtf16(const std::wstring& utf16_string) { +std::string Utf8FromUtf16(std::wstring_view utf16_string) { if (utf16_string.empty()) { return std::string(); } @@ -35,7 +35,7 @@ std::string Utf8FromUtf16(const std::wstring& utf16_string) { } // Converts the given UTF-8 string to UTF-16. -std::wstring Utf16FromUtf8(const std::string& utf8_string) { +std::wstring Utf16FromUtf8(std::string_view utf8_string) { if (utf8_string.empty()) { return std::wstring(); } diff --git a/packages/file_selector/file_selector_windows/windows/string_utils.h b/packages/file_selector/file_selector_windows/windows/string_utils.h index 74c7d4f93934..2323a5a589d8 100644 --- a/packages/file_selector/file_selector_windows/windows/string_utils.h +++ b/packages/file_selector/file_selector_windows/windows/string_utils.h @@ -11,10 +11,10 @@ namespace file_selector_windows { // Converts the given UTF-16 string to UTF-8. -std::string Utf8FromUtf16(const std::wstring& utf16_string); +std::string Utf8FromUtf16(std::wstring_view utf16_string); // Converts the given UTF-8 string to UTF-16. -std::wstring Utf16FromUtf8(const std::string& utf8_string); +std::wstring Utf16FromUtf8(std::string_view utf8_string); } // namespace file_selector_windows diff --git a/packages/file_selector/file_selector_windows/windows/test/file_selector_plugin_test.cpp b/packages/file_selector/file_selector_windows/windows/test/file_selector_plugin_test.cpp index 20e1bba2794f..f3d130bb1787 100644 --- a/packages/file_selector/file_selector_windows/windows/test/file_selector_plugin_test.cpp +++ b/packages/file_selector/file_selector_windows/windows/test/file_selector_plugin_test.cpp @@ -26,25 +26,38 @@ namespace test { namespace { +using flutter::CustomEncodableValue; using flutter::EncodableList; using flutter::EncodableMap; using flutter::EncodableValue; -using flutter::MethodCall; -using ::testing::DoAll; -using ::testing::Pointee; -using ::testing::Return; -using ::testing::SetArgPointee; - -class MockMethodResult : public flutter::MethodResult<> { - public: - MOCK_METHOD(void, SuccessInternal, (const EncodableValue* result), - (override)); - MOCK_METHOD(void, ErrorInternal, - (const std::string& error_code, const std::string& error_message, - const EncodableValue* details), - (override)); - MOCK_METHOD(void, NotImplementedInternal, (), (override)); + +// These structs and classes are a workaround for +// https://github.com/flutter/flutter/issues/104286 and +// https://github.com/flutter/flutter/issues/104653. +struct AllowMultipleArg { + bool value = false; + AllowMultipleArg(bool val) : value(val) {} +}; +struct SelectFoldersArg { + bool value = false; + SelectFoldersArg(bool val) : value(val) {} }; +SelectionOptions CreateOptions(AllowMultipleArg allow_multiple, + SelectFoldersArg select_folders, + const EncodableList& allowed_types) { + SelectionOptions options; + options.set_allow_multiple(allow_multiple.value); + options.set_select_folders(select_folders.value); + options.set_allowed_types(allowed_types); + return options; +} +TypeGroup CreateTypeGroup(std::string_view label, + const EncodableList& extensions) { + TypeGroup group; + group.set_label(label); + group.set_extensions(extensions); + return group; +} } // namespace @@ -55,9 +68,6 @@ TEST(FileSelectorPlugin, TestOpenSimple) { ::SHCreateShellItemArrayFromShellItem(fake_selected_file.file(), IID_PPV_ARGS(&fake_result_array)); - std::unique_ptr result = - std::make_unique(); - bool shown = false; MockShow show_validator = [&shown, fake_result_array, fake_window]( const TestFileDialogController& dialog, @@ -73,20 +83,21 @@ TEST(FileSelectorPlugin, TestOpenSimple) { return MockShowResult(fake_result_array); }; - EncodableValue expected_paths(EncodableList({ - EncodableValue(Utf8FromUtf16(fake_selected_file.path())), - })); - // Expect the mock path. - EXPECT_CALL(*result, SuccessInternal(Pointee(expected_paths))); FileSelectorPlugin plugin( [fake_window] { return fake_window; }, std::make_unique(show_validator)); - plugin.HandleMethodCall( - MethodCall("openFile", std::make_unique(EncodableMap())), - std::move(result)); + ErrorOr result = plugin.ShowOpenDialog( + CreateOptions(AllowMultipleArg(false), SelectFoldersArg(false), + EncodableList()), + nullptr, nullptr); EXPECT_TRUE(shown); + ASSERT_FALSE(result.has_error()); + const EncodableList& paths = result.value(); + EXPECT_EQ(paths.size(), 1); + EXPECT_EQ(std::get(paths[0]), + Utf8FromUtf16(fake_selected_file.path())); } TEST(FileSelectorPlugin, TestOpenWithArguments) { @@ -96,9 +107,6 @@ TEST(FileSelectorPlugin, TestOpenWithArguments) { ::SHCreateShellItemArrayFromShellItem(fake_selected_file.file(), IID_PPV_ARGS(&fake_result_array)); - std::unique_ptr result = - std::make_unique(); - bool shown = false; MockShow show_validator = [&shown, fake_result_array, fake_window]( const TestFileDialogController& dialog, @@ -108,33 +116,28 @@ TEST(FileSelectorPlugin, TestOpenWithArguments) { // Validate arguments. EXPECT_EQ(dialog.GetDefaultFolderPath(), L"C:\\Program Files"); - EXPECT_EQ(dialog.GetFileName(), L"a name"); EXPECT_EQ(dialog.GetOkButtonLabel(), L"Open it!"); return MockShowResult(fake_result_array); }; - EncodableValue expected_paths(EncodableList({ - EncodableValue(Utf8FromUtf16(fake_selected_file.path())), - })); - // Expect the mock path. - EXPECT_CALL(*result, SuccessInternal(Pointee(expected_paths))); FileSelectorPlugin plugin( [fake_window] { return fake_window; }, std::make_unique(show_validator)); - plugin.HandleMethodCall( - MethodCall( - "openFile", - std::make_unique(EncodableMap({ - // This directory must exist. - {EncodableValue("initialDirectory"), - EncodableValue("C:\\Program Files")}, - {EncodableValue("suggestedName"), EncodableValue("a name")}, - {EncodableValue("confirmButtonText"), EncodableValue("Open it!")}, - }))), - std::move(result)); + // This directory must exist. + std::string initial_directory("C:\\Program Files"); + std::string confirm_button("Open it!"); + ErrorOr result = plugin.ShowOpenDialog( + CreateOptions(AllowMultipleArg(false), SelectFoldersArg(false), + EncodableList()), + &initial_directory, &confirm_button); EXPECT_TRUE(shown); + ASSERT_FALSE(result.has_error()); + const EncodableList& paths = result.value(); + EXPECT_EQ(paths.size(), 1); + EXPECT_EQ(std::get(paths[0]), + Utf8FromUtf16(fake_selected_file.path())); } TEST(FileSelectorPlugin, TestOpenMultiple) { @@ -149,9 +152,6 @@ TEST(FileSelectorPlugin, TestOpenMultiple) { ::SHCreateShellItemArrayFromIDLists(2, fake_selected_files, &fake_result_array); - std::unique_ptr result = - std::make_unique(); - bool shown = false; MockShow show_validator = [&shown, fake_result_array, fake_window]( const TestFileDialogController& dialog, @@ -167,24 +167,23 @@ TEST(FileSelectorPlugin, TestOpenMultiple) { return MockShowResult(fake_result_array); }; - EncodableValue expected_paths(EncodableList({ - EncodableValue(Utf8FromUtf16(fake_selected_file_1.path())), - EncodableValue(Utf8FromUtf16(fake_selected_file_2.path())), - })); - // Expect the mock path. - EXPECT_CALL(*result, SuccessInternal(Pointee(expected_paths))); FileSelectorPlugin plugin( [fake_window] { return fake_window; }, std::make_unique(show_validator)); - plugin.HandleMethodCall( - MethodCall("openFile", - std::make_unique(EncodableMap({ - {EncodableValue("multiple"), EncodableValue(true)}, - }))), - std::move(result)); + ErrorOr result = plugin.ShowOpenDialog( + CreateOptions(AllowMultipleArg(true), SelectFoldersArg(false), + EncodableList()), + nullptr, nullptr); EXPECT_TRUE(shown); + ASSERT_FALSE(result.has_error()); + const EncodableList& paths = result.value(); + EXPECT_EQ(paths.size(), 2); + EXPECT_EQ(std::get(paths[0]), + Utf8FromUtf16(fake_selected_file_1.path())); + EXPECT_EQ(std::get(paths[1]), + Utf8FromUtf16(fake_selected_file_2.path())); } TEST(FileSelectorPlugin, TestOpenWithFilter) { @@ -194,27 +193,19 @@ TEST(FileSelectorPlugin, TestOpenWithFilter) { ::SHCreateShellItemArrayFromShellItem(fake_selected_file.file(), IID_PPV_ARGS(&fake_result_array)); - std::unique_ptr result = - std::make_unique(); - - const EncodableValue text_group = EncodableValue(EncodableMap({ - {EncodableValue("label"), EncodableValue("Text")}, - {EncodableValue("extensions"), EncodableValue(EncodableList({ - EncodableValue("txt"), - EncodableValue("json"), - }))}, - })); - const EncodableValue image_group = EncodableValue(EncodableMap({ - {EncodableValue("label"), EncodableValue("Images")}, - {EncodableValue("extensions"), EncodableValue(EncodableList({ - EncodableValue("png"), - EncodableValue("gif"), - EncodableValue("jpeg"), - }))}, - })); - const EncodableValue any_group = EncodableValue(EncodableMap({ - {EncodableValue("label"), EncodableValue("Any")}, - })); + const EncodableValue text_group = + CustomEncodableValue(CreateTypeGroup("Text", EncodableList({ + EncodableValue("txt"), + EncodableValue("json"), + }))); + const EncodableValue image_group = + CustomEncodableValue(CreateTypeGroup("Images", EncodableList({ + EncodableValue("png"), + EncodableValue("gif"), + EncodableValue("jpeg"), + }))); + const EncodableValue any_group = + CustomEncodableValue(CreateTypeGroup("Any", EncodableList())); bool shown = false; MockShow show_validator = [&shown, fake_result_array, fake_window]( @@ -237,35 +228,30 @@ TEST(FileSelectorPlugin, TestOpenWithFilter) { return MockShowResult(fake_result_array); }; - EncodableValue expected_paths(EncodableList({ - EncodableValue(Utf8FromUtf16(fake_selected_file.path())), - })); - // Expect the mock path. - EXPECT_CALL(*result, SuccessInternal(Pointee(expected_paths))); FileSelectorPlugin plugin( [fake_window] { return fake_window; }, std::make_unique(show_validator)); - plugin.HandleMethodCall( - MethodCall("openFile", std::make_unique(EncodableMap({ - {EncodableValue("acceptedTypeGroups"), - EncodableValue(EncodableList({ - text_group, - image_group, - any_group, - }))}, - }))), - std::move(result)); + ErrorOr result = plugin.ShowOpenDialog( + CreateOptions(AllowMultipleArg(false), SelectFoldersArg(false), + EncodableList({ + text_group, + image_group, + any_group, + })), + nullptr, nullptr); EXPECT_TRUE(shown); + ASSERT_FALSE(result.has_error()); + const EncodableList& paths = result.value(); + EXPECT_EQ(paths.size(), 1); + EXPECT_EQ(std::get(paths[0]), + Utf8FromUtf16(fake_selected_file.path())); } TEST(FileSelectorPlugin, TestOpenCancel) { const HWND fake_window = reinterpret_cast(1337); - std::unique_ptr result = - std::make_unique(); - bool shown = false; MockShow show_validator = [&shown, fake_window]( const TestFileDialogController& dialog, @@ -273,28 +259,25 @@ TEST(FileSelectorPlugin, TestOpenCancel) { shown = true; return MockShowResult(); }; - // Cancel should return a null for the paths. - EncodableValue expected_paths; - // Expect the mock path. - EXPECT_CALL(*result, SuccessInternal(Pointee(expected_paths))); FileSelectorPlugin plugin( [fake_window] { return fake_window; }, std::make_unique(show_validator)); - plugin.HandleMethodCall( - MethodCall("openFile", std::make_unique(EncodableMap())), - std::move(result)); + ErrorOr result = plugin.ShowOpenDialog( + CreateOptions(AllowMultipleArg(false), SelectFoldersArg(false), + EncodableList()), + nullptr, nullptr); EXPECT_TRUE(shown); + ASSERT_FALSE(result.has_error()); + const EncodableList& paths = result.value(); + EXPECT_EQ(paths.size(), 0); } TEST(FileSelectorPlugin, TestSaveSimple) { const HWND fake_window = reinterpret_cast(1337); ScopedTestShellItem fake_selected_file; - std::unique_ptr result = - std::make_unique(); - bool shown = false; MockShow show_validator = [&shown, fake_result = fake_selected_file.file(), fake_window]( @@ -310,28 +293,27 @@ TEST(FileSelectorPlugin, TestSaveSimple) { return MockShowResult(fake_result); }; - EncodableValue expected_path(Utf8FromUtf16(fake_selected_file.path())); - // Expect the mock path. - EXPECT_CALL(*result, SuccessInternal(Pointee(expected_path))); FileSelectorPlugin plugin( [fake_window] { return fake_window; }, std::make_unique(show_validator)); - plugin.HandleMethodCall( - MethodCall("getSavePath", - std::make_unique(EncodableMap())), - std::move(result)); + ErrorOr result = plugin.ShowSaveDialog( + CreateOptions(AllowMultipleArg(false), SelectFoldersArg(false), + EncodableList()), + nullptr, nullptr, nullptr); EXPECT_TRUE(shown); + ASSERT_FALSE(result.has_error()); + const EncodableList& paths = result.value(); + EXPECT_EQ(paths.size(), 1); + EXPECT_EQ(std::get(paths[0]), + Utf8FromUtf16(fake_selected_file.path())); } TEST(FileSelectorPlugin, TestSaveWithArguments) { const HWND fake_window = reinterpret_cast(1337); ScopedTestShellItem fake_selected_file; - std::unique_ptr result = - std::make_unique(); - bool shown = false; MockShow show_validator = [&shown, fake_result = fake_selected_file.file(), fake_window]( @@ -341,37 +323,35 @@ TEST(FileSelectorPlugin, TestSaveWithArguments) { // Validate arguments. EXPECT_EQ(dialog.GetDefaultFolderPath(), L"C:\\Program Files"); + EXPECT_EQ(dialog.GetFileName(), L"a name"); EXPECT_EQ(dialog.GetOkButtonLabel(), L"Save it!"); return MockShowResult(fake_result); }; - EncodableValue expected_path(Utf8FromUtf16(fake_selected_file.path())); - // Expect the mock path. - EXPECT_CALL(*result, SuccessInternal(Pointee(expected_path))); FileSelectorPlugin plugin( [fake_window] { return fake_window; }, std::make_unique(show_validator)); - plugin.HandleMethodCall( - MethodCall( - "getSavePath", - std::make_unique(EncodableMap({ - // This directory must exist. - {EncodableValue("initialDirectory"), - EncodableValue("C:\\Program Files")}, - {EncodableValue("confirmButtonText"), EncodableValue("Save it!")}, - }))), - std::move(result)); + // This directory must exist. + std::string initial_directory("C:\\Program Files"); + std::string suggested_name("a name"); + std::string confirm_button("Save it!"); + ErrorOr result = plugin.ShowSaveDialog( + CreateOptions(AllowMultipleArg(false), SelectFoldersArg(false), + EncodableList()), + &initial_directory, &suggested_name, &confirm_button); EXPECT_TRUE(shown); + ASSERT_FALSE(result.has_error()); + const EncodableList& paths = result.value(); + EXPECT_EQ(paths.size(), 1); + EXPECT_EQ(std::get(paths[0]), + Utf8FromUtf16(fake_selected_file.path())); } TEST(FileSelectorPlugin, TestSaveCancel) { const HWND fake_window = reinterpret_cast(1337); - std::unique_ptr result = - std::make_unique(); - bool shown = false; MockShow show_validator = [&shown, fake_window]( const TestFileDialogController& dialog, @@ -379,20 +359,19 @@ TEST(FileSelectorPlugin, TestSaveCancel) { shown = true; return MockShowResult(); }; - // Cancel should return a null for the path. - EncodableValue expected_path; - // Expect the mock path. - EXPECT_CALL(*result, SuccessInternal(Pointee(expected_path))); FileSelectorPlugin plugin( [fake_window] { return fake_window; }, std::make_unique(show_validator)); - plugin.HandleMethodCall( - MethodCall("getSavePath", - std::make_unique(EncodableMap())), - std::move(result)); + ErrorOr result = plugin.ShowSaveDialog( + CreateOptions(AllowMultipleArg(false), SelectFoldersArg(false), + EncodableList()), + nullptr, nullptr, nullptr); EXPECT_TRUE(shown); + ASSERT_FALSE(result.has_error()); + const EncodableList& paths = result.value(); + EXPECT_EQ(paths.size(), 0); } TEST(FileSelectorPlugin, TestGetDirectorySimple) { @@ -405,9 +384,6 @@ TEST(FileSelectorPlugin, TestGetDirectorySimple) { ::SHCreateShellItemArrayFromShellItem(fake_selected_directory, IID_PPV_ARGS(&fake_result_array)); - std::unique_ptr result = - std::make_unique(); - bool shown = false; MockShow show_validator = [&shown, fake_result_array, fake_window]( const TestFileDialogController& dialog, @@ -423,27 +399,25 @@ TEST(FileSelectorPlugin, TestGetDirectorySimple) { return MockShowResult(fake_result_array); }; - EncodableValue expected_path("C:\\Program Files"); - // Expect the mock path. - EXPECT_CALL(*result, SuccessInternal(Pointee(expected_path))); FileSelectorPlugin plugin( [fake_window] { return fake_window; }, std::make_unique(show_validator)); - plugin.HandleMethodCall( - MethodCall("getDirectoryPath", - std::make_unique(EncodableMap())), - std::move(result)); + ErrorOr result = plugin.ShowOpenDialog( + CreateOptions(AllowMultipleArg(false), SelectFoldersArg(true), + EncodableList()), + nullptr, nullptr); EXPECT_TRUE(shown); + ASSERT_FALSE(result.has_error()); + const EncodableList& paths = result.value(); + EXPECT_EQ(paths.size(), 1); + EXPECT_EQ(std::get(paths[0]), "C:\\Program Files"); } TEST(FileSelectorPlugin, TestGetDirectoryCancel) { const HWND fake_window = reinterpret_cast(1337); - std::unique_ptr result = - std::make_unique(); - bool shown = false; MockShow show_validator = [&shown, fake_window]( const TestFileDialogController& dialog, @@ -451,20 +425,19 @@ TEST(FileSelectorPlugin, TestGetDirectoryCancel) { shown = true; return MockShowResult(); }; - // Cancel should return a null for the path. - EncodableValue expected_path; - // Expect the mock path. - EXPECT_CALL(*result, SuccessInternal(Pointee(expected_path))); FileSelectorPlugin plugin( [fake_window] { return fake_window; }, std::make_unique(show_validator)); - plugin.HandleMethodCall( - MethodCall("getDirectoryPath", - std::make_unique(EncodableMap())), - std::move(result)); + ErrorOr result = plugin.ShowOpenDialog( + CreateOptions(AllowMultipleArg(false), SelectFoldersArg(true), + EncodableList()), + nullptr, nullptr); EXPECT_TRUE(shown); + ASSERT_FALSE(result.has_error()); + const EncodableList& paths = result.value(); + EXPECT_EQ(paths.size(), 0); } } // namespace test From 5b44c711b4b1722c723448920a17e69afa72cd04 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 28 Jul 2022 11:45:06 -0400 Subject: [PATCH 558/844] Roll Flutter from e3d08fb6d325 to 1aa44652e4c0 (67 revisions) (#6152) --- .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 5fc0b4364801..38ac12d087d3 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e3d08fb6d325d778fe6bd02893356c30868789fb +1aa44652e4c00e62bdd57d7de49495feee6f1f1a From e183aec328ead613b4ccc3c1f541327d0acdc80b Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 28 Jul 2022 09:58:58 -0700 Subject: [PATCH 559/844] migrate to new mockito API (#6153) --- .../integration_test/google_maps_controller_test.dart | 8 ++++---- .../example/integration_test/google_maps_plugin_test.dart | 2 +- .../google_maps_flutter_web/example/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) 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 dd2520d418f6..b44853aacb54 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 @@ -21,10 +21,10 @@ import 'google_maps_controller_test.mocks.dart'; const double _acceptableDelta = 0.0000000001; @GenerateMocks([], customMocks: >[ - MockSpec(returnNullOnMissingStub: true), - MockSpec(returnNullOnMissingStub: true), - MockSpec(returnNullOnMissingStub: true), - MockSpec(returnNullOnMissingStub: true), + MockSpec(onMissingStub: OnMissingStub.returnDefault), + MockSpec(onMissingStub: OnMissingStub.returnDefault), + MockSpec(onMissingStub: OnMissingStub.returnDefault), + MockSpec(onMissingStub: OnMissingStub.returnDefault), ]) /// Test Google Map Controller diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart index e66a3f47c78c..b4786c73b711 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart @@ -17,7 +17,7 @@ import 'package:mockito/mockito.dart'; import 'google_maps_plugin_test.mocks.dart'; @GenerateMocks([], customMocks: >[ - MockSpec(returnNullOnMissingStub: true), + MockSpec(onMissingStub: OnMissingStub.returnDefault), ]) /// Test GoogleMapsPlugin 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 fb6359fe5b8f..82c36e22b15f 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 @@ -24,4 +24,4 @@ dev_dependencies: http: ^0.13.0 integration_test: sdk: flutter - mockito: ^5.0.0 + mockito: ^5.3.0 From 257eacb1e2aa573d2305d38f55838274f6d4ed30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Sharma?= <737941+loic-sharma@users.noreply.github.com> Date: Thu, 28 Jul 2022 16:16:05 -0700 Subject: [PATCH 560/844] [camera_windows] Improve several error handling scenarios (#6149) --- packages/camera/camera_windows/CHANGELOG.md | 4 + .../camera_windows/lib/camera_windows.dart | 2 +- packages/camera/camera_windows/pubspec.yaml | 2 +- .../camera/camera_windows/windows/camera.cpp | 4 +- .../camera/camera_windows/windows/camera.h | 4 +- .../windows/capture_controller.cpp | 25 +- .../camera_windows/windows/photo_handler.h | 2 +- .../camera_windows/windows/preview_handler.h | 2 +- .../camera_windows/windows/record_handler.cpp | 1 + .../camera_windows/windows/record_handler.h | 4 +- .../windows/test/camera_test.cpp | 32 +- .../windows/test/capture_controller_test.cpp | 619 +++++++++++++++--- script/tool/README.md | 2 + .../lib/src/common/package_state_utils.dart | 2 +- 14 files changed, 575 insertions(+), 130 deletions(-) diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index a1e2a0733e1a..a7b8023e4c52 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.0+5 + +* Fixes bugs in in error handling. + ## 0.1.0+4 * Allows retrying camera initialization after error. diff --git a/packages/camera/camera_windows/lib/camera_windows.dart b/packages/camera/camera_windows/lib/camera_windows.dart index d998863d43a7..14134479994b 100644 --- a/packages/camera/camera_windows/lib/camera_windows.dart +++ b/packages/camera/camera_windows/lib/camera_windows.dart @@ -22,7 +22,7 @@ class CameraWindows extends CameraPlatform { final MethodChannel pluginChannel = const MethodChannel('plugins.flutter.io/camera_windows'); - /// Camera specific method channels to allow comminicating with specific cameras. + /// Camera specific method channels to allow communicating with specific cameras. final Map _cameraChannels = {}; /// The controller that broadcasts events coming from handleCameraMethodCall diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index e9fe0212ad80..690ac9fe1f90 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_windows description: A Flutter plugin for getting information about and controlling the camera on Windows. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.1.0+4 +version: 0.1.0+5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/camera/camera_windows/windows/camera.cpp b/packages/camera/camera_windows/windows/camera.cpp index 617f98f812ab..794b20dce2ed 100644 --- a/packages/camera/camera_windows/windows/camera.cpp +++ b/packages/camera/camera_windows/windows/camera.cpp @@ -85,9 +85,9 @@ bool CameraImpl::HasPendingResultByType(PendingResultType type) const { } void CameraImpl::SendErrorForPendingResults(const std::string& error_code, - const std::string& descripion) { + const std::string& description) { for (const auto& pending_result : pending_results_) { - pending_result.second->Error(error_code, descripion); + pending_result.second->Error(error_code, description); } pending_results_.clear(); } diff --git a/packages/camera/camera_windows/windows/camera.h b/packages/camera/camera_windows/windows/camera.h index 429f41ad6315..7374228ac9cc 100644 --- a/packages/camera/camera_windows/windows/camera.h +++ b/packages/camera/camera_windows/windows/camera.h @@ -139,9 +139,9 @@ class CameraImpl : public Camera { // error ID and description. Pending results are cleared in the process. // // error_code: A string error code describing the error. - // error_message: A user-readable error message (optional). + // description: A user-readable error message (optional). void SendErrorForPendingResults(const std::string& error_code, - const std::string& descripion); + const std::string& description); // Called when camera is disposed. // Sends camera closing message to the cameras method channel. diff --git a/packages/camera/camera_windows/windows/capture_controller.cpp b/packages/camera/camera_windows/windows/capture_controller.cpp index 6c89060877ef..0294ce8fb6c8 100644 --- a/packages/camera/camera_windows/windows/capture_controller.cpp +++ b/packages/camera/camera_windows/windows/capture_controller.cpp @@ -237,6 +237,8 @@ HRESULT CaptureControllerImpl::CreateCaptureEngine() { return hr; } + // Check MF_CAPTURE_ENGINE_INITIALIZED event handling + // for response process. hr = capture_engine_->Initialize(capture_engine_callback_handler_.Get(), attributes.Get(), audio_source_.Get(), video_source_.Get()); @@ -244,7 +246,7 @@ HRESULT CaptureControllerImpl::CreateCaptureEngine() { } void CaptureControllerImpl::ResetCaptureController() { - if (record_handler_) { + if (record_handler_ && record_handler_->CanStop()) { if (record_handler_->IsContinuousRecording()) { StopRecord(); } else if (record_handler_->IsTimedRecording()) { @@ -391,7 +393,7 @@ uint32_t CaptureControllerImpl::GetMaxPreviewHeight() const { } } -// Finds best mediat type for given source stream index and max height; +// Finds best media type for given source stream index and max height; bool FindBestMediaType(DWORD source_stream_index, IMFCaptureSource* source, IMFMediaType** target_media_type, uint32_t max_height, uint32_t* target_frame_width, @@ -533,8 +535,6 @@ void CaptureControllerImpl::StopRecord() { // Check MF_CAPTURE_ENGINE_RECORD_STOPPED event handling for response // process. if (!record_handler_->StopRecord(capture_engine_.Get())) { - // Destroy record handler on error cases to make sure state is resetted. - record_handler_ = nullptr; return OnRecordStopped(false, "Failed to stop video recording"); } } @@ -578,6 +578,8 @@ void CaptureControllerImpl::StartPreview() { texture_handler_->UpdateTextureSize(preview_frame_width_, preview_frame_height_); + // TODO(loic-sharma): This does not handle duplicate calls properly. + // See: https://github.com/flutter/flutter/issues/108404 if (!preview_handler_) { preview_handler_ = std::make_unique(); } else if (preview_handler_->IsInitialized()) { @@ -605,7 +607,7 @@ void CaptureControllerImpl::StartPreview() { void CaptureControllerImpl::StopPreview() { assert(capture_engine_); - if (!IsInitialized() && !preview_handler_) { + if (!IsInitialized() || !preview_handler_) { return; } @@ -619,7 +621,7 @@ void CaptureControllerImpl::StopPreview() { void CaptureControllerImpl::PausePreview() { assert(capture_controller_listener_); - if (!preview_handler_ && !preview_handler_->IsInitialized()) { + if (!preview_handler_ || !preview_handler_->IsInitialized()) { return capture_controller_listener_->OnPausePreviewFailed( "Preview not started"); } @@ -638,7 +640,7 @@ void CaptureControllerImpl::PausePreview() { void CaptureControllerImpl::ResumePreview() { assert(capture_controller_listener_); - if (!preview_handler_ && !preview_handler_->IsInitialized()) { + if (!preview_handler_ || !preview_handler_->IsInitialized()) { return capture_controller_listener_->OnResumePreviewFailed( "Preview not started"); } @@ -722,6 +724,13 @@ void CaptureControllerImpl::OnPicture(bool success, const std::string& error) { void CaptureControllerImpl::OnCaptureEngineInitialized( bool success, const std::string& error) { if (capture_controller_listener_) { + if (!success) { + capture_controller_listener_->OnCreateCaptureEngineFailed( + "Failed to initialize capture engine"); + ResetCaptureController(); + return; + } + // Create texture handler and register new texture. texture_handler_ = std::make_unique(texture_registrar_); @@ -848,7 +857,7 @@ void CaptureControllerImpl::UpdateCaptureTime(uint64_t capture_time_us) { } if (preview_handler_ && preview_handler_->IsStarting()) { - // Informs that first frame is captured succeffully and preview has + // Informs that first frame is captured successfully and preview has // started. OnPreviewStarted(true, ""); } diff --git a/packages/camera/camera_windows/windows/photo_handler.h b/packages/camera/camera_windows/windows/photo_handler.h index ef0d98bfc45f..c12643bef85b 100644 --- a/packages/camera/camera_windows/windows/photo_handler.h +++ b/packages/camera/camera_windows/windows/photo_handler.h @@ -51,7 +51,7 @@ class PhotoHandler { bool TakePhoto(const std::string& file_path, IMFCaptureEngine* capture_engine, IMFMediaType* base_media_type); - // Set the photo handler recording state to: kIdel. + // Set the photo handler recording state to: kIdle. void OnPhotoTaken(); // Returns true if photo state is kIdle. diff --git a/packages/camera/camera_windows/windows/preview_handler.h b/packages/camera/camera_windows/windows/preview_handler.h index 97b85fc28568..a10ba634967c 100644 --- a/packages/camera/camera_windows/windows/preview_handler.h +++ b/packages/camera/camera_windows/windows/preview_handler.h @@ -75,7 +75,7 @@ class PreviewHandler { // Returns true if preview state is running or paused. bool IsInitialized() const { - return preview_state_ == PreviewState::kRunning && + return preview_state_ == PreviewState::kRunning || preview_state_ == PreviewState::kPaused; } diff --git a/packages/camera/camera_windows/windows/record_handler.cpp b/packages/camera/camera_windows/windows/record_handler.cpp index 1cb258e162a5..2e16527df06a 100644 --- a/packages/camera/camera_windows/windows/record_handler.cpp +++ b/packages/camera/camera_windows/windows/record_handler.cpp @@ -238,6 +238,7 @@ void RecordHandler::OnRecordStopped() { recording_duration_us_ = 0; max_video_duration_ms_ = -1; recording_state_ = RecordState::kNotStarted; + type_ = RecordingType::kNone; } } diff --git a/packages/camera/camera_windows/windows/record_handler.h b/packages/camera/camera_windows/windows/record_handler.h index 0daa7f6546a1..4cbe499360ea 100644 --- a/packages/camera/camera_windows/windows/record_handler.h +++ b/packages/camera/camera_windows/windows/record_handler.h @@ -16,6 +16,8 @@ namespace camera_windows { using Microsoft::WRL::ComPtr; enum class RecordingType { + // Camera is not recording. + kNone, // Recording continues until it is stopped with a separate stop command. kContinuous, // Recording stops automatically after requested record time is passed. @@ -109,7 +111,7 @@ class RecordHandler { uint64_t recording_duration_us_ = 0; std::string file_path_; RecordState recording_state_ = RecordState::kNotStarted; - RecordingType type_; + RecordingType type_ = RecordingType::kNone; ComPtr record_sink_; }; diff --git a/packages/camera/camera_windows/windows/test/camera_test.cpp b/packages/camera/camera_windows/windows/test/camera_test.cpp index 97e3ce113e53..50f49536a915 100644 --- a/packages/camera/camera_windows/windows/test/camera_test.cpp +++ b/packages/camera/camera_windows/windows/test/camera_test.cpp @@ -131,7 +131,7 @@ TEST(Camera, OnCreateCaptureEngineSucceededReturnsCameraId) { camera->OnCreateCaptureEngineSucceeded(texture_id); } -TEST(Camera, OnCreateCaptureEngineFailedReturnsError) { +TEST(Camera, CreateCaptureEngineReportsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); std::unique_ptr result = @@ -140,7 +140,7 @@ TEST(Camera, OnCreateCaptureEngineFailedReturnsError) { std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kCreateCamera, std::move(result)); @@ -169,7 +169,7 @@ TEST(Camera, OnStartPreviewSucceededReturnsFrameSize) { camera->OnStartPreviewSucceeded(width, height); } -TEST(Camera, OnStartPreviewFailedReturnsError) { +TEST(Camera, StartPreviewReportsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); std::unique_ptr result = @@ -178,7 +178,7 @@ TEST(Camera, OnStartPreviewFailedReturnsError) { std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kInitialize, std::move(result)); @@ -199,7 +199,7 @@ TEST(Camera, OnPausePreviewSucceededReturnsSuccess) { camera->OnPausePreviewSucceeded(); } -TEST(Camera, OnPausePreviewFailedReturnsError) { +TEST(Camera, PausePreviewReportsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); std::unique_ptr result = @@ -208,7 +208,7 @@ TEST(Camera, OnPausePreviewFailedReturnsError) { std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kPausePreview, std::move(result)); @@ -230,7 +230,7 @@ TEST(Camera, OnResumePreviewSucceededReturnsSuccess) { camera->OnResumePreviewSucceeded(); } -TEST(Camera, OnResumePreviewFailedReturnsError) { +TEST(Camera, ResumePreviewReportsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); std::unique_ptr result = @@ -239,7 +239,7 @@ TEST(Camera, OnResumePreviewFailedReturnsError) { std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kResumePreview, std::move(result)); @@ -261,7 +261,7 @@ TEST(Camera, OnStartRecordSucceededReturnsSuccess) { camera->OnStartRecordSucceeded(); } -TEST(Camera, OnStartRecordFailedReturnsError) { +TEST(Camera, StartRecordReportsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); std::unique_ptr result = @@ -270,7 +270,7 @@ TEST(Camera, OnStartRecordFailedReturnsError) { std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kStartRecord, std::move(result)); @@ -293,7 +293,7 @@ TEST(Camera, OnStopRecordSucceededReturnsSuccess) { camera->OnStopRecordSucceeded(file_path); } -TEST(Camera, OnStopRecordFailedReturnsError) { +TEST(Camera, StopRecordReportsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); std::unique_ptr result = @@ -302,7 +302,7 @@ TEST(Camera, OnStopRecordFailedReturnsError) { std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kStopRecord, std::move(result)); @@ -315,7 +315,7 @@ TEST(Camera, OnTakePictureSucceededReturnsSuccess) { std::unique_ptr result = std::make_unique(); - std::string file_path = "C:\temp\filename.jpeg"; + std::string file_path = "C:\\temp\\filename.jpeg"; EXPECT_CALL(*result, ErrorInternal).Times(0); EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(file_path)))); @@ -325,7 +325,7 @@ TEST(Camera, OnTakePictureSucceededReturnsSuccess) { camera->OnTakePictureSucceeded(file_path); } -TEST(Camera, OnTakePictureFailedReturnsError) { +TEST(Camera, TakePictureReportsError) { std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); std::unique_ptr result = @@ -334,7 +334,7 @@ TEST(Camera, OnTakePictureFailedReturnsError) { std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); - EXPECT_CALL(*result, ErrorInternal(_, Eq(error_text), _)); + EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kTakePicture, std::move(result)); @@ -350,7 +350,7 @@ TEST(Camera, OnVideoRecordSucceededInvokesCameraChannelEvent) { std::unique_ptr binary_messenger = std::make_unique(); - std::string file_path = "C:\temp\filename.mp4"; + std::string file_path = "C:\\temp\\filename.mp4"; int64_t camera_id = 12345; std::string camera_channel = std::string("plugins.flutter.io/camera_windows/camera") + diff --git a/packages/camera/camera_windows/windows/test/capture_controller_test.cpp b/packages/camera/camera_windows/windows/test/capture_controller_test.cpp index 083f823222b6..776c150edaee 100644 --- a/packages/camera/camera_windows/windows/test/capture_controller_test.cpp +++ b/packages/camera/camera_windows/windows/test/capture_controller_test.cpp @@ -70,35 +70,10 @@ void MockInitCaptureController(CaptureControllerImpl* capture_controller, engine->CreateFakeEvent(S_OK, MF_CAPTURE_ENGINE_INITIALIZED); } -void MockStartPreview(CaptureControllerImpl* capture_controller, - MockCaptureSource* capture_source, - MockCapturePreviewSink* preview_sink, - MockTextureRegistrar* texture_registrar, - MockCaptureEngine* engine, MockCamera* camera, - std::unique_ptr mock_source_buffer, - uint32_t mock_source_buffer_size, - uint32_t mock_preview_width, uint32_t mock_preview_height, - int64_t mock_texture_id) { - EXPECT_CALL(*engine, GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PREVIEW, _)) - .Times(1) - .WillOnce([src_sink = preview_sink](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, - IMFCaptureSink** target_sink) { - *target_sink = src_sink; - src_sink->AddRef(); - return S_OK; - }); - - EXPECT_CALL(*preview_sink, RemoveAllStreams).Times(1).WillOnce(Return(S_OK)); - EXPECT_CALL(*preview_sink, AddStream).Times(1).WillOnce(Return(S_OK)); - EXPECT_CALL(*preview_sink, SetSampleCallback) - .Times(1) - .WillOnce([sink = preview_sink]( - DWORD dwStreamSinkIndex, - IMFCaptureEngineOnSampleCallback* pCallback) -> HRESULT { - sink->sample_callback_ = pCallback; - return S_OK; - }); - +void MockAvailableMediaTypes(MockCaptureEngine* engine, + MockCaptureSource* capture_source, + uint32_t mock_preview_width, + uint32_t mock_preview_height) { EXPECT_CALL(*engine, GetSource) .Times(1) .WillOnce( @@ -142,6 +117,39 @@ void MockStartPreview(CaptureControllerImpl* capture_controller, (*media_type)->AddRef(); return S_OK; }); +} + +void MockStartPreview(CaptureControllerImpl* capture_controller, + MockCapturePreviewSink* preview_sink, + MockTextureRegistrar* texture_registrar, + MockCaptureEngine* engine, MockCamera* camera, + std::unique_ptr mock_source_buffer, + uint32_t mock_source_buffer_size, + uint32_t mock_preview_width, uint32_t mock_preview_height, + int64_t mock_texture_id) { + EXPECT_CALL(*engine, GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PREVIEW, _)) + .Times(1) + .WillOnce([src_sink = preview_sink](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, + IMFCaptureSink** target_sink) { + *target_sink = src_sink; + src_sink->AddRef(); + return S_OK; + }); + + EXPECT_CALL(*preview_sink, RemoveAllStreams).Times(1).WillOnce(Return(S_OK)); + EXPECT_CALL(*preview_sink, AddStream).Times(1).WillOnce(Return(S_OK)); + EXPECT_CALL(*preview_sink, SetSampleCallback) + .Times(1) + .WillOnce([sink = preview_sink]( + DWORD dwStreamSinkIndex, + IMFCaptureEngineOnSampleCallback* pCallback) -> HRESULT { + sink->sample_callback_ = pCallback; + return S_OK; + }); + + ComPtr capture_source = new MockCaptureSource(); + MockAvailableMediaTypes(engine, capture_source.Get(), mock_preview_width, + mock_preview_height); EXPECT_CALL(*engine, StartPreview()).Times(1).WillOnce(Return(S_OK)); @@ -169,6 +177,21 @@ void MockStartPreview(CaptureControllerImpl* capture_controller, mock_source_buffer_size); } +void MockPhotoSink(MockCaptureEngine* engine, + MockCapturePhotoSink* photo_sink) { + EXPECT_CALL(*engine, GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PHOTO, _)) + .Times(1) + .WillOnce([src_sink = photo_sink](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, + IMFCaptureSink** target_sink) { + *target_sink = src_sink; + src_sink->AddRef(); + return S_OK; + }); + EXPECT_CALL(*photo_sink, RemoveAllStreams).Times(1).WillOnce(Return(S_OK)); + EXPECT_CALL(*photo_sink, AddStream).Times(1).WillOnce(Return(S_OK)); + EXPECT_CALL(*photo_sink, SetOutputFileName).Times(1).WillOnce(Return(S_OK)); +} + void MockRecordStart(CaptureControllerImpl* capture_controller, MockCaptureEngine* engine, MockCaptureRecordSink* record_sink, MockCamera* camera, @@ -204,7 +227,7 @@ TEST(CaptureController, std::unique_ptr texture_registrar = std::make_unique(); - uint64_t mock_texture_id = 1234; + int64_t mock_texture_id = 1234; // Init capture controller with mocks and tests MockInitCaptureController(capture_controller.get(), texture_registrar.get(), @@ -225,7 +248,7 @@ TEST(CaptureController, InitCaptureEngineCanOnlyBeCalledOnce) { std::unique_ptr texture_registrar = std::make_unique(); - uint64_t mock_texture_id = 1234; + int64_t mock_texture_id = 1234; // Init capture controller once with mocks and tests MockInitCaptureController(capture_controller.get(), texture_registrar.get(), @@ -264,14 +287,12 @@ TEST(CaptureController, InitCaptureEngineReportsFailure) { capture_controller->SetAudioSource( reinterpret_cast(audio_source.Get())); + // Cause initialization to fail + EXPECT_CALL(*engine.Get(), Initialize).Times(1).WillOnce(Return(E_FAIL)); + EXPECT_CALL(*texture_registrar, RegisterTexture).Times(0); EXPECT_CALL(*texture_registrar, UnregisterTexture).Times(0); EXPECT_CALL(*camera, OnCreateCaptureEngineSucceeded).Times(0); - - EXPECT_CALL(*engine.Get(), Initialize) - .Times(1) - .WillOnce(Return(E_ACCESSDENIED)); - EXPECT_CALL(*camera, OnCreateCaptureEngineFailed(Eq("Failed to create camera"))) .Times(1); @@ -288,6 +309,60 @@ TEST(CaptureController, InitCaptureEngineReportsFailure) { engine = nullptr; } +TEST(CaptureController, ReportsInitializedErrorEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + EXPECT_CALL(*camera, OnCreateCaptureEngineFailed( + Eq("Failed to initialize capture engine"))) + .Times(1); + EXPECT_CALL(*camera, OnCreateCaptureEngineSucceeded).Times(0); + + // Send initialization failed event + engine->CreateFakeEvent(E_FAIL, MF_CAPTURE_ENGINE_INITIALIZED); + + capture_controller = nullptr; + camera = nullptr; + texture_registrar = nullptr; + engine = nullptr; +} + +TEST(CaptureController, ReportsCaptureEngineErrorEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + EXPECT_CALL(*(camera.get()), OnCaptureError(Eq("Unspecified error"))) + .Times(1); + + // Send error event. + engine->CreateFakeEvent(E_FAIL, MF_CAPTURE_ENGINE_ERROR); + + capture_controller = nullptr; + camera = nullptr; + texture_registrar = nullptr; + engine = nullptr; +} + TEST(CaptureController, StartPreviewStartsProcessingSamples) { ComPtr engine = new MockCaptureEngine(); std::unique_ptr camera = @@ -297,14 +372,13 @@ TEST(CaptureController, StartPreviewStartsProcessingSamples) { std::unique_ptr texture_registrar = std::make_unique(); - uint64_t mock_texture_id = 1234; + int64_t mock_texture_id = 1234; // Initialize capture controller to be able to start preview MockInitCaptureController(capture_controller.get(), texture_registrar.get(), engine.Get(), camera.get(), mock_texture_id); ComPtr preview_sink = new MockCapturePreviewSink(); - ComPtr capture_source = new MockCaptureSource(); // Let's keep these small for mock texture data. Two pixels should be // enough. @@ -332,11 +406,10 @@ TEST(CaptureController, StartPreviewStartsProcessingSamples) { } // Start preview and run preview tests - MockStartPreview(capture_controller.get(), capture_source.Get(), - preview_sink.Get(), texture_registrar.get(), engine.Get(), - camera.get(), std::move(mock_source_buffer), - mock_texture_data_size, mock_preview_width, - mock_preview_height, mock_texture_id); + MockStartPreview(capture_controller.get(), preview_sink.Get(), + texture_registrar.get(), engine.Get(), camera.get(), + std::move(mock_source_buffer), mock_texture_data_size, + mock_preview_width, mock_preview_height, mock_texture_id); // Test texture processing EXPECT_TRUE(texture_registrar->texture_); @@ -377,7 +450,7 @@ TEST(CaptureController, StartPreviewStartsProcessingSamples) { texture_registrar = nullptr; } -TEST(CaptureController, StartRecordSuccess) { +TEST(CaptureController, ReportsStartPreviewError) { ComPtr engine = new MockCaptureEngine(); std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); @@ -386,23 +459,83 @@ TEST(CaptureController, StartRecordSuccess) { std::unique_ptr texture_registrar = std::make_unique(); - uint64_t mock_texture_id = 1234; + int64_t mock_texture_id = 1234; // Initialize capture controller to be able to start preview MockInitCaptureController(capture_controller.get(), texture_registrar.get(), engine.Get(), camera.get(), mock_texture_id); - ComPtr preview_sink = new MockCapturePreviewSink(); ComPtr capture_source = new MockCaptureSource(); + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); - std::unique_ptr mock_source_buffer = - std::make_unique(0); + // Cause start preview to fail + EXPECT_CALL(*engine.Get(), GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PREVIEW, _)) + .Times(1) + .WillOnce(Return(E_FAIL)); - // Start preview to be able to start record - MockStartPreview(capture_controller.get(), capture_source.Get(), - preview_sink.Get(), texture_registrar.get(), engine.Get(), - camera.get(), std::move(mock_source_buffer), 0, 1, 1, - mock_texture_id); + EXPECT_CALL(*engine.Get(), StartPreview).Times(0); + EXPECT_CALL(*engine.Get(), StopPreview).Times(0); + EXPECT_CALL(*camera, OnStartPreviewSucceeded).Times(0); + EXPECT_CALL(*camera, + OnStartPreviewFailed(Eq("Failed to start video preview"))) + .Times(1); + + capture_controller->StartPreview(); + + capture_controller = nullptr; + engine = nullptr; + camera = nullptr; + texture_registrar = nullptr; +} + +// TODO(loic-sharma): Test duplicate calls to start preview. + +TEST(CaptureController, IgnoresStartPreviewErrorEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + EXPECT_CALL(*camera, OnStartPreviewFailed).Times(0); + EXPECT_CALL(*camera, OnCreateCaptureEngineSucceeded).Times(0); + + // Send a start preview error event + engine->CreateFakeEvent(E_FAIL, MF_CAPTURE_ENGINE_PREVIEW_STARTED); + + capture_controller = nullptr; + camera = nullptr; + texture_registrar = nullptr; + engine = nullptr; +} + +TEST(CaptureController, StartRecordSuccess) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); // Start record ComPtr record_sink = new MockCaptureRecordSink(); @@ -422,7 +555,7 @@ TEST(CaptureController, StartRecordSuccess) { record_sink = nullptr; } -TEST(CaptureController, StopRecordSuccess) { +TEST(CaptureController, ReportsStartRecordError) { ComPtr engine = new MockCaptureEngine(); std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); @@ -431,23 +564,119 @@ TEST(CaptureController, StopRecordSuccess) { std::unique_ptr texture_registrar = std::make_unique(); - uint64_t mock_texture_id = 1234; + int64_t mock_texture_id = 1234; // Initialize capture controller to be able to start preview MockInitCaptureController(capture_controller.get(), texture_registrar.get(), engine.Get(), camera.get(), mock_texture_id); - ComPtr preview_sink = new MockCapturePreviewSink(); ComPtr capture_source = new MockCaptureSource(); - std::unique_ptr mock_source_buffer = - std::make_unique(0); + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); - // Start preview to be able to start record - MockStartPreview(capture_controller.get(), capture_source.Get(), - preview_sink.Get(), texture_registrar.get(), engine.Get(), - camera.get(), std::move(mock_source_buffer), 0, 1, 1, - mock_texture_id); + // Cause start record to fail + EXPECT_CALL(*engine.Get(), GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_RECORD, _)) + .Times(1) + .WillOnce(Return(E_FAIL)); + + EXPECT_CALL(*engine.Get(), StartRecord).Times(0); + EXPECT_CALL(*engine.Get(), StopRecord).Times(0); + EXPECT_CALL(*camera, OnStartRecordSucceeded).Times(0); + EXPECT_CALL(*camera, + OnStartRecordFailed(Eq("Failed to start video recording"))) + .Times(1); + + capture_controller->StartRecord("mock_path", -1); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; +} + +TEST(CaptureController, ReportsStartRecordErrorEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + // Start record + ComPtr record_sink = new MockCaptureRecordSink(); + std::string mock_path_to_video = "mock_path_to_video"; + + EXPECT_CALL(*engine.Get(), StartRecord()).Times(1).WillOnce(Return(S_OK)); + + EXPECT_CALL(*engine.Get(), GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_RECORD, _)) + .Times(1) + .WillOnce([src_sink = record_sink](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, + IMFCaptureSink** target_sink) { + *target_sink = src_sink.Get(); + src_sink->AddRef(); + return S_OK; + }); + + EXPECT_CALL(*record_sink.Get(), RemoveAllStreams) + .Times(1) + .WillOnce(Return(S_OK)); + EXPECT_CALL(*record_sink.Get(), AddStream) + .Times(2) + .WillRepeatedly(Return(S_OK)); + EXPECT_CALL(*record_sink.Get(), SetOutputFileName) + .Times(1) + .WillOnce(Return(S_OK)); + + capture_controller->StartRecord(mock_path_to_video, -1); + + // Send a start record failed event + EXPECT_CALL(*camera, OnStartRecordSucceeded).Times(0); + EXPECT_CALL(*camera, OnStartRecordFailed(Eq("Unspecified error"))).Times(1); + + engine->CreateFakeEvent(E_FAIL, MF_CAPTURE_ENGINE_RECORD_STARTED); + + // Destructor shouldn't attempt to stop the recording that failed to start. + EXPECT_CALL(*engine.Get(), StopRecord).Times(0); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + record_sink = nullptr; +} + +TEST(CaptureController, StopRecordSuccess) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); // Start record ComPtr record_sink = new MockCaptureRecordSink(); @@ -463,6 +692,8 @@ TEST(CaptureController, StopRecordSuccess) { // OnStopRecordSucceeded should be called with mocked file path EXPECT_CALL(*camera, OnStopRecordSucceeded(Eq(mock_path_to_video))).Times(1); + EXPECT_CALL(*camera, OnStopRecordFailed).Times(0); + engine->CreateFakeEvent(S_OK, MF_CAPTURE_ENGINE_RECORD_STOPPED); capture_controller = nullptr; @@ -472,7 +703,7 @@ TEST(CaptureController, StopRecordSuccess) { record_sink = nullptr; } -TEST(CaptureController, TakePictureSuccess) { +TEST(CaptureController, ReportsStopRecordError) { ComPtr engine = new MockCaptureEngine(); std::unique_ptr camera = std::make_unique(MOCK_DEVICE_ID); @@ -481,42 +712,103 @@ TEST(CaptureController, TakePictureSuccess) { std::unique_ptr texture_registrar = std::make_unique(); - uint64_t mock_texture_id = 1234; + int64_t mock_texture_id = 1234; // Initialize capture controller to be able to start preview MockInitCaptureController(capture_controller.get(), texture_registrar.get(), engine.Get(), camera.get(), mock_texture_id); - ComPtr preview_sink = new MockCapturePreviewSink(); ComPtr capture_source = new MockCaptureSource(); - std::unique_ptr mock_source_buffer = - std::make_unique(0); + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); - // Start preview to be able to start record - MockStartPreview(capture_controller.get(), capture_source.Get(), - preview_sink.Get(), texture_registrar.get(), engine.Get(), - camera.get(), std::move(mock_source_buffer), 0, 1, 1, - mock_texture_id); + // Start record + ComPtr record_sink = new MockCaptureRecordSink(); + MockRecordStart(capture_controller.get(), engine.Get(), record_sink.Get(), + camera.get(), "mock_path_to_video"); - // Init photo sink tests - ComPtr photo_sink = new MockCapturePhotoSink(); - EXPECT_CALL(*(engine.Get()), GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PHOTO, _)) - .Times(1) - .WillOnce( - [src_sink = photo_sink.Get()](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, - IMFCaptureSink** target_sink) { - *target_sink = src_sink; - src_sink->AddRef(); - return S_OK; - }); - EXPECT_CALL(*(photo_sink.Get()), RemoveAllStreams) - .Times(1) - .WillOnce(Return(S_OK)); - EXPECT_CALL(*(photo_sink.Get()), AddStream).Times(1).WillOnce(Return(S_OK)); - EXPECT_CALL(*(photo_sink.Get()), SetOutputFileName) + // Cause stop record to fail + EXPECT_CALL(*(engine.Get()), StopRecord(true, false)) .Times(1) - .WillOnce(Return(S_OK)); + .WillOnce(Return(E_FAIL)); + + EXPECT_CALL(*camera, OnStopRecordSucceeded).Times(0); + EXPECT_CALL(*camera, OnStopRecordFailed(Eq("Failed to stop video recording"))) + .Times(1); + + capture_controller->StopRecord(); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + record_sink = nullptr; +} + +TEST(CaptureController, ReportsStopRecordErrorEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + // Start record + ComPtr record_sink = new MockCaptureRecordSink(); + std::string mock_path_to_video = "mock_path_to_video"; + MockRecordStart(capture_controller.get(), engine.Get(), record_sink.Get(), + camera.get(), mock_path_to_video); + + // Send a stop record failure event + EXPECT_CALL(*camera, OnStopRecordSucceeded).Times(0); + EXPECT_CALL(*camera, OnStopRecordFailed(Eq("Unspecified error"))).Times(1); + + engine->CreateFakeEvent(E_FAIL, MF_CAPTURE_ENGINE_RECORD_STOPPED); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + record_sink = nullptr; +} + +TEST(CaptureController, TakePictureSuccess) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to take picture + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + ComPtr photo_sink = new MockCapturePhotoSink(); + + // Initialize photo sink + MockPhotoSink(engine.Get(), photo_sink.Get()); // Request photo std::string mock_path_to_photo = "mock_path_to_photo"; @@ -525,6 +817,7 @@ TEST(CaptureController, TakePictureSuccess) { // OnTakePictureSucceeded should be called with mocked file path EXPECT_CALL(*camera, OnTakePictureSucceeded(Eq(mock_path_to_photo))).Times(1); + EXPECT_CALL(*camera, OnTakePictureFailed).Times(0); engine->CreateFakeEvent(S_OK, MF_CAPTURE_ENGINE_PHOTO_TAKEN); capture_controller = nullptr; @@ -534,6 +827,90 @@ TEST(CaptureController, TakePictureSuccess) { photo_sink = nullptr; } +TEST(CaptureController, ReportsTakePictureError) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to take picture + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + ComPtr photo_sink = new MockCapturePhotoSink(); + + // Initialize photo sink + MockPhotoSink(engine.Get(), photo_sink.Get()); + + // Cause take picture to fail + EXPECT_CALL(*(engine.Get()), TakePhoto).Times(1).WillOnce(Return(E_FAIL)); + + EXPECT_CALL(*camera, OnTakePictureSucceeded).Times(0); + EXPECT_CALL(*camera, OnTakePictureFailed(Eq("Failed to take photo"))) + .Times(1); + + capture_controller->TakePicture("mock_path_to_photo"); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + photo_sink = nullptr; +} + +TEST(CaptureController, ReportsPhotoTakenErrorEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to take picture + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + ComPtr photo_sink = new MockCapturePhotoSink(); + + // Initialize photo sink + MockPhotoSink(engine.Get(), photo_sink.Get()); + + // Request photo + std::string mock_path_to_photo = "mock_path_to_photo"; + EXPECT_CALL(*(engine.Get()), TakePhoto()).Times(1).WillOnce(Return(S_OK)); + capture_controller->TakePicture(mock_path_to_photo); + + // Send take picture failed event + EXPECT_CALL(*camera, OnTakePictureSucceeded).Times(0); + EXPECT_CALL(*camera, OnTakePictureFailed(Eq("Unspecified error"))).Times(1); + + engine->CreateFakeEvent(E_FAIL, MF_CAPTURE_ENGINE_PHOTO_TAKEN); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + photo_sink = nullptr; +} + TEST(CaptureController, PauseResumePreviewSuccess) { ComPtr engine = new MockCaptureEngine(); std::unique_ptr camera = @@ -543,23 +920,21 @@ TEST(CaptureController, PauseResumePreviewSuccess) { std::unique_ptr texture_registrar = std::make_unique(); - uint64_t mock_texture_id = 1234; + int64_t mock_texture_id = 1234; // Initialize capture controller to be able to start preview MockInitCaptureController(capture_controller.get(), texture_registrar.get(), engine.Get(), camera.get(), mock_texture_id); ComPtr preview_sink = new MockCapturePreviewSink(); - ComPtr capture_source = new MockCaptureSource(); std::unique_ptr mock_source_buffer = std::make_unique(0); // Start preview to be able to start record - MockStartPreview(capture_controller.get(), capture_source.Get(), - preview_sink.Get(), texture_registrar.get(), engine.Get(), - camera.get(), std::move(mock_source_buffer), 0, 1, 1, - mock_texture_id); + MockStartPreview(capture_controller.get(), preview_sink.Get(), + texture_registrar.get(), engine.Get(), camera.get(), + std::move(mock_source_buffer), 0, 1, 1, mock_texture_id); EXPECT_CALL(*camera, OnPausePreviewSucceeded()).Times(1); capture_controller->PausePreview(); @@ -573,5 +948,57 @@ TEST(CaptureController, PauseResumePreviewSuccess) { camera = nullptr; } +TEST(CaptureController, PausePreviewFailsIfPreviewNotStarted) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + // Pause preview fails if not started + EXPECT_CALL(*camera, OnPausePreviewFailed(Eq("Preview not started"))) + .Times(1); + + capture_controller->PausePreview(); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; +} + +TEST(CaptureController, ResumePreviewFailsIfPreviewNotStarted) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + // Resume preview fails if not started. + EXPECT_CALL(*camera, OnResumePreviewFailed(Eq("Preview not started"))) + .Times(1); + + capture_controller->ResumePreview(); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; +} + } // namespace test } // namespace camera_windows diff --git a/script/tool/README.md b/script/tool/README.md index d3beb53a1103..225cda2ef265 100644 --- a/script/tool/README.md +++ b/script/tool/README.md @@ -105,6 +105,8 @@ cd dart run ./script/tool/bin/flutter_plugin_tools.dart native-test --ios --android --no-integration --packages plugin_name # Run all tests for macOS: dart run ./script/tool/bin/flutter_plugin_tools.dart native-test --macos --packages plugin_name +# Run all tests for Windows: +dart run ./script/tool/bin/flutter_plugin_tools.dart native-test --windows --packages plugin_name ``` ### Update README.md from Example Sources diff --git a/script/tool/lib/src/common/package_state_utils.dart b/script/tool/lib/src/common/package_state_utils.dart index 437bbf6df370..a03d643bdab0 100644 --- a/script/tool/lib/src/common/package_state_utils.dart +++ b/script/tool/lib/src/common/package_state_utils.dart @@ -35,7 +35,7 @@ class PackageChangeState { /// [changedPaths] should be a list of POSIX-style paths from a common root, /// and [relativePackagePath] should be the path to [package] from that same /// root. Commonly these will come from `gitVersionFinder.getChangedFiles()` -/// and `getRelativePoixPath(package.directory, gitDir.path)` respectively; +/// and `getRelativePosixPath(package.directory, gitDir.path)` respectively; /// they are arguments mainly to allow for caching the changed paths for an /// entire command run. PackageChangeState checkPackageChangeState( From 9a1749b65de6966eb94d402a5ab1ace5eaacc18c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 29 Jul 2022 14:15:05 -0400 Subject: [PATCH 561/844] Roll Flutter from 1aa44652e4c0 to a62ee852950a (32 revisions) (#6156) --- .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 38ac12d087d3..0e12c0e5fca9 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -1aa44652e4c00e62bdd57d7de49495feee6f1f1a +a62ee852950a2f2b69c2ffe733eb82e56cc616b9 From 2494a46a5e16464a5bd048937db1b6c4bf48b966 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 1 Aug 2022 07:03:05 -0400 Subject: [PATCH 562/844] Roll Flutter from a62ee852950a to ab47f01ff173 (29 revisions) (#6162) --- .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 0e12c0e5fca9..4cd2df5e0e55 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a62ee852950a2f2b69c2ffe733eb82e56cc616b9 +ab47f01ff1738fbdf9ecbe24252837a1638b9e65 From a6d42f1e01d358b6fd7d83539b0d5d98c168b197 Mon Sep 17 00:00:00 2001 From: Sahil Sonawane Date: Mon, 1 Aug 2022 17:31:04 +0530 Subject: [PATCH 563/844] [local_auth] Replace `USE_FINGERPRINT` to `USE_BIOMETRIC` post deprecation in example and README (#6032) --- packages/local_auth/local_auth/CHANGELOG.md | 4 ++++ packages/local_auth/local_auth/README.md | 4 ++-- .../example/android/app/src/main/AndroidManifest.xml | 2 +- packages/local_auth/local_auth/pubspec.yaml | 2 +- .../example/android/app/src/main/AndroidManifest.xml | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 1aae73d7393e..975d171889cc 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.1 + +* Replaces `USE_FINGERPRINT` permission with `USE_BIOMETRIC` in README and example project. + ## 2.1.0 * Adds Windows support. diff --git a/packages/local_auth/local_auth/README.md b/packages/local_auth/local_auth/README.md index 3077f8e60a1f..a68692ea940a 100644 --- a/packages/local_auth/local_auth/README.md +++ b/packages/local_auth/local_auth/README.md @@ -235,12 +235,12 @@ Note that `local_auth` requires the use of a `FragmentActivity` instead of an ### Permissions Update your project's `AndroidManifest.xml` file to include the -`USE_FINGERPRINT` permissions: +`USE_BIOMETRIC` permissions: ```xml - + ``` diff --git a/packages/local_auth/local_auth/example/android/app/src/main/AndroidManifest.xml b/packages/local_auth/local_auth/example/android/app/src/main/AndroidManifest.xml index 8c091772107a..4acc4eb87ed6 100644 --- a/packages/local_auth/local_auth/example/android/app/src/main/AndroidManifest.xml +++ b/packages/local_auth/local_auth/example/android/app/src/main/AndroidManifest.xml @@ -2,7 +2,7 @@ package="io.flutter.plugins.localauthexample"> - + =2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_android/example/android/app/src/main/AndroidManifest.xml b/packages/local_auth/local_auth_android/example/android/app/src/main/AndroidManifest.xml index 8c091772107a..4acc4eb87ed6 100644 --- a/packages/local_auth/local_auth_android/example/android/app/src/main/AndroidManifest.xml +++ b/packages/local_auth/local_auth_android/example/android/app/src/main/AndroidManifest.xml @@ -2,7 +2,7 @@ package="io.flutter.plugins.localauthexample"> - + Date: Mon, 1 Aug 2022 10:35:04 -0700 Subject: [PATCH 564/844] [camera_windows] Use `CameraAccessDenied` error code for camera permission errors (#6154) --- packages/camera/camera_windows/CHANGELOG.md | 6 + packages/camera/camera_windows/pubspec.yaml | 2 +- .../camera/camera_windows/windows/camera.cpp | 71 ++- .../camera/camera_windows/windows/camera.h | 26 +- .../windows/capture_controller.cpp | 170 ++++--- .../windows/capture_controller.h | 17 +- .../windows/capture_controller_listener.h | 48 +- .../camera_windows/windows/photo_handler.cpp | 14 +- .../camera_windows/windows/photo_handler.h | 6 +- .../windows/preview_handler.cpp | 22 +- .../camera_windows/windows/preview_handler.h | 10 +- .../camera_windows/windows/record_handler.cpp | 24 +- .../camera_windows/windows/record_handler.h | 10 +- .../windows/test/camera_test.cpp | 158 +++++- .../windows/test/capture_controller_test.cpp | 456 +++++++++++++++++- .../camera_windows/windows/test/mocks.h | 34 +- 16 files changed, 871 insertions(+), 203 deletions(-) diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index a7b8023e4c52..ef0fad497f45 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.2.0 + +**BREAKING CHANGES**: + * `CameraException.code` now has value `"CameraAccessDenied"` if camera access permission was denied. + * `CameraException.code` now has value `"camera_error"` if error occurs during capture. + ## 0.1.0+5 * Fixes bugs in in error handling. diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index 690ac9fe1f90..9cf1793ad1a8 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_windows description: A Flutter plugin for getting information about and controlling the camera on Windows. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.1.0+5 +version: 0.2.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/camera/camera_windows/windows/camera.cpp b/packages/camera/camera_windows/windows/camera.cpp index 794b20dce2ed..6a0944747908 100644 --- a/packages/camera/camera_windows/windows/camera.cpp +++ b/packages/camera/camera_windows/windows/camera.cpp @@ -16,6 +16,25 @@ constexpr char kVideoRecordedEvent[] = "video_recorded"; constexpr char kCameraClosingEvent[] = "camera_closing"; constexpr char kErrorEvent[] = "error"; +// Camera error codes +constexpr char kCameraAccessDenied[] = "CameraAccessDenied"; +constexpr char kCameraError[] = "camera_error"; +constexpr char kPluginDisposed[] = "plugin_disposed"; + +std::string GetErrorCode(CameraResult result) { + assert(result != CameraResult::kSuccess); + + switch (result) { + case CameraResult::kAccessDenied: + return kCameraAccessDenied; + + case CameraResult::kSuccess: + case CameraResult::kError: + default: + return kCameraError; + } +} + CameraImpl::CameraImpl(const std::string& device_id) : device_id_(device_id), Camera(device_id) {} @@ -24,7 +43,7 @@ CameraImpl::~CameraImpl() { OnCameraClosing(); capture_controller_ = nullptr; - SendErrorForPendingResults("plugin_disposed", + SendErrorForPendingResults(kPluginDisposed, "Plugin disposed before request was handled"); } @@ -121,11 +140,13 @@ void CameraImpl::OnCreateCaptureEngineSucceeded(int64_t texture_id) { } } -void CameraImpl::OnCreateCaptureEngineFailed(const std::string& error) { +void CameraImpl::OnCreateCaptureEngineFailed(CameraResult result, + const std::string& error) { auto pending_result = GetPendingResultByType(PendingResultType::kCreateCamera); if (pending_result) { - pending_result->Error("camera_error", error); + std::string error_code = GetErrorCode(result); + pending_result->Error(error_code, error); } } @@ -141,10 +162,12 @@ void CameraImpl::OnStartPreviewSucceeded(int32_t width, int32_t height) { } }; -void CameraImpl::OnStartPreviewFailed(const std::string& error) { +void CameraImpl::OnStartPreviewFailed(CameraResult result, + const std::string& error) { auto pending_result = GetPendingResultByType(PendingResultType::kInitialize); if (pending_result) { - pending_result->Error("camera_error", error); + std::string error_code = GetErrorCode(result); + pending_result->Error(error_code, error); } }; @@ -156,11 +179,13 @@ void CameraImpl::OnResumePreviewSucceeded() { } } -void CameraImpl::OnResumePreviewFailed(const std::string& error) { +void CameraImpl::OnResumePreviewFailed(CameraResult result, + const std::string& error) { auto pending_result = GetPendingResultByType(PendingResultType::kResumePreview); if (pending_result) { - pending_result->Error("camera_error", error); + std::string error_code = GetErrorCode(result); + pending_result->Error(error_code, error); } } @@ -172,11 +197,13 @@ void CameraImpl::OnPausePreviewSucceeded() { } } -void CameraImpl::OnPausePreviewFailed(const std::string& error) { +void CameraImpl::OnPausePreviewFailed(CameraResult result, + const std::string& error) { auto pending_result = GetPendingResultByType(PendingResultType::kPausePreview); if (pending_result) { - pending_result->Error("camera_error", error); + std::string error_code = GetErrorCode(result); + pending_result->Error(error_code, error); } } @@ -187,10 +214,12 @@ void CameraImpl::OnStartRecordSucceeded() { } }; -void CameraImpl::OnStartRecordFailed(const std::string& error) { +void CameraImpl::OnStartRecordFailed(CameraResult result, + const std::string& error) { auto pending_result = GetPendingResultByType(PendingResultType::kStartRecord); if (pending_result) { - pending_result->Error("camera_error", error); + std::string error_code = GetErrorCode(result); + pending_result->Error(error_code, error); } }; @@ -201,10 +230,12 @@ void CameraImpl::OnStopRecordSucceeded(const std::string& file_path) { } }; -void CameraImpl::OnStopRecordFailed(const std::string& error) { +void CameraImpl::OnStopRecordFailed(CameraResult result, + const std::string& error) { auto pending_result = GetPendingResultByType(PendingResultType::kStopRecord); if (pending_result) { - pending_result->Error("camera_error", error); + std::string error_code = GetErrorCode(result); + pending_result->Error(error_code, error); } }; @@ -215,11 +246,13 @@ void CameraImpl::OnTakePictureSucceeded(const std::string& file_path) { } }; -void CameraImpl::OnTakePictureFailed(const std::string& error) { +void CameraImpl::OnTakePictureFailed(CameraResult result, + const std::string& error) { auto pending_take_picture_result = GetPendingResultByType(PendingResultType::kTakePicture); if (pending_take_picture_result) { - pending_take_picture_result->Error("camera_error", error); + std::string error_code = GetErrorCode(result); + pending_take_picture_result->Error(error_code, error); } }; @@ -238,9 +271,10 @@ void CameraImpl::OnVideoRecordSucceeded(const std::string& file_path, } } -void CameraImpl::OnVideoRecordFailed(const std::string& error){}; +void CameraImpl::OnVideoRecordFailed(CameraResult result, + const std::string& error){}; -void CameraImpl::OnCaptureError(const std::string& error) { +void CameraImpl::OnCaptureError(CameraResult result, const std::string& error) { if (messenger_ && camera_id_ >= 0) { auto channel = GetMethodChannel(); @@ -250,7 +284,8 @@ void CameraImpl::OnCaptureError(const std::string& error) { channel->InvokeMethod(kErrorEvent, std::move(message_data)); } - SendErrorForPendingResults("capture_error", error); + std::string error_code = GetErrorCode(result); + SendErrorForPendingResults(error_code, error); } void CameraImpl::OnCameraClosing() { diff --git a/packages/camera/camera_windows/windows/camera.h b/packages/camera/camera_windows/windows/camera.h index 7374228ac9cc..8508da1924d0 100644 --- a/packages/camera/camera_windows/windows/camera.h +++ b/packages/camera/camera_windows/windows/camera.h @@ -87,23 +87,31 @@ class CameraImpl : public Camera { // CaptureControllerListener void OnCreateCaptureEngineSucceeded(int64_t texture_id) override; - void OnCreateCaptureEngineFailed(const std::string& error) override; + void OnCreateCaptureEngineFailed(CameraResult result, + const std::string& error) override; void OnStartPreviewSucceeded(int32_t width, int32_t height) override; - void OnStartPreviewFailed(const std::string& error) override; + void OnStartPreviewFailed(CameraResult result, + const std::string& error) override; void OnPausePreviewSucceeded() override; - void OnPausePreviewFailed(const std::string& error) override; + void OnPausePreviewFailed(CameraResult result, + const std::string& error) override; void OnResumePreviewSucceeded() override; - void OnResumePreviewFailed(const std::string& error) override; + void OnResumePreviewFailed(CameraResult result, + const std::string& error) override; void OnStartRecordSucceeded() override; - void OnStartRecordFailed(const std::string& error) override; + void OnStartRecordFailed(CameraResult result, + const std::string& error) override; void OnStopRecordSucceeded(const std::string& file_path) override; - void OnStopRecordFailed(const std::string& error) override; + void OnStopRecordFailed(CameraResult result, + const std::string& error) override; void OnTakePictureSucceeded(const std::string& file_path) override; - void OnTakePictureFailed(const std::string& error) override; + void OnTakePictureFailed(CameraResult result, + const std::string& error) override; void OnVideoRecordSucceeded(const std::string& file_path, int64_t video_duration) override; - void OnVideoRecordFailed(const std::string& error) override; - void OnCaptureError(const std::string& error) override; + void OnVideoRecordFailed(CameraResult result, + const std::string& error) override; + void OnCaptureError(CameraResult result, const std::string& error) override; // Camera bool HasDeviceId(std::string& device_id) const override { diff --git a/packages/camera/camera_windows/windows/capture_controller.cpp b/packages/camera/camera_windows/windows/capture_controller.cpp index 0294ce8fb6c8..384c86ac109b 100644 --- a/packages/camera/camera_windows/windows/capture_controller.cpp +++ b/packages/camera/camera_windows/windows/capture_controller.cpp @@ -22,6 +22,15 @@ namespace camera_windows { using Microsoft::WRL::ComPtr; +CameraResult GetCameraResult(HRESULT hr) { + if (SUCCEEDED(hr)) { + return CameraResult::kSuccess; + } + + return hr == E_ACCESSDENIED ? CameraResult::kAccessDenied + : CameraResult::kError; +} + CaptureControllerImpl::CaptureControllerImpl( CaptureControllerListener* listener) : capture_controller_listener_(listener), CaptureController(){}; @@ -297,11 +306,11 @@ bool CaptureControllerImpl::InitCaptureDevice( if (IsInitialized()) { capture_controller_listener_->OnCreateCaptureEngineFailed( - "Capture device already initialized"); + CameraResult::kError, "Capture device already initialized"); return false; } else if (capture_engine_state_ == CaptureEngineState::kInitializing) { capture_controller_listener_->OnCreateCaptureEngineFailed( - "Capture device already initializing"); + CameraResult::kError, "Capture device already initializing"); return false; } @@ -317,7 +326,7 @@ bool CaptureControllerImpl::InitCaptureDevice( if (FAILED(hr)) { capture_controller_listener_->OnCreateCaptureEngineFailed( - "Failed to create camera"); + GetCameraResult(hr), "Failed to create camera"); ResetCaptureController(); return false; } @@ -328,7 +337,7 @@ bool CaptureControllerImpl::InitCaptureDevice( HRESULT hr = CreateCaptureEngine(); if (FAILED(hr)) { capture_controller_listener_->OnCreateCaptureEngineFailed( - "Failed to create camera"); + GetCameraResult(hr), "Failed to create camera"); ResetCaptureController(); return false; } @@ -341,29 +350,34 @@ void CaptureControllerImpl::TakePicture(const std::string& file_path) { assert(capture_engine_); if (!IsInitialized()) { - return OnPicture(false, "Not initialized"); + return OnPicture(CameraResult::kError, "Not initialized"); } + HRESULT hr = S_OK; + if (!base_capture_media_type_) { // Enumerates mediatypes and finds media type for video capture. - if (FAILED(FindBaseMediaTypes())) { - return OnPicture(false, "Failed to initialize photo capture"); + hr = FindBaseMediaTypes(); + if (FAILED(hr)) { + return OnPicture(GetCameraResult(hr), + "Failed to initialize photo capture"); } } if (!photo_handler_) { photo_handler_ = std::make_unique(); } else if (photo_handler_->IsTakingPhoto()) { - return OnPicture(false, "Photo already requested"); + return OnPicture(CameraResult::kError, "Photo already requested"); } // Check MF_CAPTURE_ENGINE_PHOTO_TAKEN event handling // for response process. - if (!photo_handler_->TakePhoto(file_path, capture_engine_.Get(), - base_capture_media_type_.Get())) { + hr = photo_handler_->TakePhoto(file_path, capture_engine_.Get(), + base_capture_media_type_.Get()); + if (FAILED(hr)) { // Destroy photo handler on error cases to make sure state is resetted. photo_handler_ = nullptr; - return OnPicture(false, "Failed to take photo"); + return OnPicture(GetCameraResult(hr), "Failed to take photo"); } } @@ -487,15 +501,19 @@ void CaptureControllerImpl::StartRecord(const std::string& file_path, assert(capture_engine_); if (!IsInitialized()) { - return OnRecordStarted(false, + return OnRecordStarted(CameraResult::kError, "Camera not initialized. Camera should be " "disposed and reinitialized."); } + HRESULT hr = S_OK; + if (!base_capture_media_type_) { // Enumerates mediatypes and finds media type for video capture. - if (FAILED(FindBaseMediaTypes())) { - return OnRecordStarted(false, "Failed to initialize video recording"); + hr = FindBaseMediaTypes(); + if (FAILED(hr)) { + return OnRecordStarted(GetCameraResult(hr), + "Failed to initialize video recording"); } } @@ -503,19 +521,21 @@ void CaptureControllerImpl::StartRecord(const std::string& file_path, record_handler_ = std::make_unique(record_audio_); } else if (!record_handler_->CanStart()) { return OnRecordStarted( - false, + CameraResult::kError, "Recording cannot be started. Previous recording must be stopped " "first."); } // Check MF_CAPTURE_ENGINE_RECORD_STARTED event handling for response // process. - if (!record_handler_->StartRecord(file_path, max_video_duration_ms, + hr = record_handler_->StartRecord(file_path, max_video_duration_ms, capture_engine_.Get(), - base_capture_media_type_.Get())) { + base_capture_media_type_.Get()); + if (FAILED(hr)) { // Destroy record handler on error cases to make sure state is resetted. record_handler_ = nullptr; - return OnRecordStarted(false, "Failed to start video recording"); + return OnRecordStarted(GetCameraResult(hr), + "Failed to start video recording"); } } @@ -523,19 +543,22 @@ void CaptureControllerImpl::StopRecord() { assert(capture_controller_listener_); if (!IsInitialized()) { - return OnRecordStopped(false, + return OnRecordStopped(CameraResult::kError, "Camera not initialized. Camera should be " "disposed and reinitialized."); } if (!record_handler_ && !record_handler_->CanStop()) { - return OnRecordStopped(false, "Recording cannot be stopped."); + return OnRecordStopped(CameraResult::kError, + "Recording cannot be stopped."); } // Check MF_CAPTURE_ENGINE_RECORD_STOPPED event handling for response // process. - if (!record_handler_->StopRecord(capture_engine_.Get())) { - return OnRecordStopped(false, "Failed to stop video recording"); + HRESULT hr = record_handler_->StopRecord(capture_engine_.Get()); + if (FAILED(hr)) { + return OnRecordStopped(GetCameraResult(hr), + "Failed to stop video recording"); } } @@ -547,11 +570,12 @@ void CaptureControllerImpl::StopTimedRecord() { return; } - if (!record_handler_->StopRecord(capture_engine_.Get())) { + HRESULT hr = record_handler_->StopRecord(capture_engine_.Get()); + if (FAILED(hr)) { // Destroy record handler on error cases to make sure state is resetted. record_handler_ = nullptr; return capture_controller_listener_->OnVideoRecordFailed( - "Failed to record video"); + GetCameraResult(hr), "Failed to record video"); } } @@ -563,15 +587,19 @@ void CaptureControllerImpl::StartPreview() { assert(texture_handler_); if (!IsInitialized() || !texture_handler_) { - return OnPreviewStarted(false, + return OnPreviewStarted(CameraResult::kError, "Camera not initialized. Camera should be " "disposed and reinitialized."); } + HRESULT hr = S_OK; + if (!base_preview_media_type_) { // Enumerates mediatypes and finds media type for video capture. - if (FAILED(FindBaseMediaTypes())) { - return OnPreviewStarted(false, "Failed to initialize video preview"); + hr = FindBaseMediaTypes(); + if (FAILED(hr)) { + return OnPreviewStarted(GetCameraResult(hr), + "Failed to initialize video preview"); } } @@ -583,19 +611,22 @@ void CaptureControllerImpl::StartPreview() { if (!preview_handler_) { preview_handler_ = std::make_unique(); } else if (preview_handler_->IsInitialized()) { - return OnPreviewStarted(true, ""); + return OnPreviewStarted(CameraResult::kSuccess, ""); } else { - return OnPreviewStarted(false, "Preview already exists"); + return OnPreviewStarted(CameraResult::kError, "Preview already exists"); } // Check MF_CAPTURE_ENGINE_PREVIEW_STARTED event handling for response // process. - if (!preview_handler_->StartPreview(capture_engine_.Get(), + hr = preview_handler_->StartPreview(capture_engine_.Get(), base_preview_media_type_.Get(), - capture_engine_callback_handler_.Get())) { + capture_engine_callback_handler_.Get()); + + if (FAILED(hr)) { // Destroy preview handler on error cases to make sure state is resetted. preview_handler_ = nullptr; - return OnPreviewStarted(false, "Failed to start video preview"); + return OnPreviewStarted(GetCameraResult(hr), + "Failed to start video preview"); } } @@ -604,15 +635,15 @@ void CaptureControllerImpl::StartPreview() { // pausing and resuming the preview. // Check MF_CAPTURE_ENGINE_PREVIEW_STOPPED event handling for response // process. -void CaptureControllerImpl::StopPreview() { +HRESULT CaptureControllerImpl::StopPreview() { assert(capture_engine_); if (!IsInitialized() || !preview_handler_) { - return; + return S_OK; } // Requests to stop preview. - preview_handler_->StopPreview(capture_engine_.Get()); + return preview_handler_->StopPreview(capture_engine_.Get()); } // Marks preview as paused. @@ -623,14 +654,14 @@ void CaptureControllerImpl::PausePreview() { if (!preview_handler_ || !preview_handler_->IsInitialized()) { return capture_controller_listener_->OnPausePreviewFailed( - "Preview not started"); + CameraResult::kError, "Preview not started"); } if (preview_handler_->PausePreview()) { capture_controller_listener_->OnPausePreviewSucceeded(); } else { capture_controller_listener_->OnPausePreviewFailed( - "Failed to pause preview"); + CameraResult::kError, "Failed to pause preview"); } } @@ -642,14 +673,14 @@ void CaptureControllerImpl::ResumePreview() { if (!preview_handler_ || !preview_handler_->IsInitialized()) { return capture_controller_listener_->OnResumePreviewFailed( - "Preview not started"); + CameraResult::kError, "Preview not started"); } if (preview_handler_->ResumePreview()) { capture_controller_listener_->OnResumePreviewSucceeded(); } else { capture_controller_listener_->OnResumePreviewFailed( - "Failed to pause preview"); + CameraResult::kError, "Failed to pause preview"); } } @@ -677,22 +708,23 @@ void CaptureControllerImpl::OnEvent(IMFMediaEvent* event) { error = Utf8FromUtf16(err.ErrorMessage()); } + CameraResult event_result = GetCameraResult(event_hr); if (extended_type_guid == MF_CAPTURE_ENGINE_ERROR) { - OnCaptureEngineError(event_hr, error); + OnCaptureEngineError(event_result, error); } else if (extended_type_guid == MF_CAPTURE_ENGINE_INITIALIZED) { - OnCaptureEngineInitialized(SUCCEEDED(event_hr), error); + OnCaptureEngineInitialized(event_result, error); } else if (extended_type_guid == MF_CAPTURE_ENGINE_PREVIEW_STARTED) { // Preview is marked as started after first frame is captured. // This is because, CaptureEngine might inform that preview is started // even if error is thrown right after. } else if (extended_type_guid == MF_CAPTURE_ENGINE_PREVIEW_STOPPED) { - OnPreviewStopped(SUCCEEDED(event_hr), error); + OnPreviewStopped(event_result, error); } else if (extended_type_guid == MF_CAPTURE_ENGINE_RECORD_STARTED) { - OnRecordStarted(SUCCEEDED(event_hr), error); + OnRecordStarted(event_result, error); } else if (extended_type_guid == MF_CAPTURE_ENGINE_RECORD_STOPPED) { - OnRecordStopped(SUCCEEDED(event_hr), error); + OnRecordStopped(event_result, error); } else if (extended_type_guid == MF_CAPTURE_ENGINE_PHOTO_TAKEN) { - OnPicture(SUCCEEDED(event_hr), error); + OnPicture(event_result, error); } else if (extended_type_guid == MF_CAPTURE_ENGINE_CAMERA_STREAM_BLOCKED) { // TODO: Inform capture state to flutter. } else if (extended_type_guid == @@ -703,8 +735,9 @@ void CaptureControllerImpl::OnEvent(IMFMediaEvent* event) { } // Handles Picture event and informs CaptureControllerListener. -void CaptureControllerImpl::OnPicture(bool success, const std::string& error) { - if (success && photo_handler_) { +void CaptureControllerImpl::OnPicture(CameraResult result, + const std::string& error) { + if (result == CameraResult::kSuccess && photo_handler_) { if (capture_controller_listener_) { std::string path = photo_handler_->GetPhotoPath(); capture_controller_listener_->OnTakePictureSucceeded(path); @@ -712,7 +745,7 @@ void CaptureControllerImpl::OnPicture(bool success, const std::string& error) { photo_handler_->OnPhotoTaken(); } else { if (capture_controller_listener_) { - capture_controller_listener_->OnTakePictureFailed(error); + capture_controller_listener_->OnTakePictureFailed(result, error); } // Destroy photo handler on error cases to make sure state is resetted. photo_handler_ = nullptr; @@ -722,11 +755,11 @@ void CaptureControllerImpl::OnPicture(bool success, const std::string& error) { // Handles CaptureEngineInitialized event and informs // CaptureControllerListener. void CaptureControllerImpl::OnCaptureEngineInitialized( - bool success, const std::string& error) { + CameraResult result, const std::string& error) { if (capture_controller_listener_) { - if (!success) { + if (result != CameraResult::kSuccess) { capture_controller_listener_->OnCreateCaptureEngineFailed( - "Failed to initialize capture engine"); + result, "Failed to initialize capture engine"); ResetCaptureController(); return; } @@ -740,7 +773,7 @@ void CaptureControllerImpl::OnCaptureEngineInitialized( capture_engine_state_ = CaptureEngineState::kInitialized; } else { capture_controller_listener_->OnCreateCaptureEngineFailed( - "Failed to create texture_id"); + CameraResult::kError, "Failed to create texture_id"); // Reset state ResetCaptureController(); } @@ -748,10 +781,10 @@ void CaptureControllerImpl::OnCaptureEngineInitialized( } // Handles CaptureEngineError event and informs CaptureControllerListener. -void CaptureControllerImpl::OnCaptureEngineError(HRESULT hr, +void CaptureControllerImpl::OnCaptureEngineError(CameraResult result, const std::string& error) { if (capture_controller_listener_) { - capture_controller_listener_->OnCaptureError(error); + capture_controller_listener_->OnCaptureError(result, error); } // TODO: If MF_CAPTURE_ENGINE_ERROR is returned, @@ -761,9 +794,9 @@ void CaptureControllerImpl::OnCaptureEngineError(HRESULT hr, // Handles PreviewStarted event and informs CaptureControllerListener. // This should be called only after first frame has been received or // in error cases. -void CaptureControllerImpl::OnPreviewStarted(bool success, +void CaptureControllerImpl::OnPreviewStarted(CameraResult result, const std::string& error) { - if (preview_handler_ && success) { + if (preview_handler_ && result == CameraResult::kSuccess) { preview_handler_->OnPreviewStarted(); } else { // Destroy preview handler on error cases to make sure state is resetted. @@ -771,17 +804,18 @@ void CaptureControllerImpl::OnPreviewStarted(bool success, } if (capture_controller_listener_) { - if (success && preview_frame_width_ > 0 && preview_frame_height_ > 0) { + if (result == CameraResult::kSuccess && preview_frame_width_ > 0 && + preview_frame_height_ > 0) { capture_controller_listener_->OnStartPreviewSucceeded( preview_frame_width_, preview_frame_height_); } else { - capture_controller_listener_->OnStartPreviewFailed(error); + capture_controller_listener_->OnStartPreviewFailed(result, error); } } }; // Handles PreviewStopped event. -void CaptureControllerImpl::OnPreviewStopped(bool success, +void CaptureControllerImpl::OnPreviewStopped(CameraResult result, const std::string& error) { // Preview handler is destroyed if preview is stopped as it // does not have any use anymore. @@ -789,16 +823,16 @@ void CaptureControllerImpl::OnPreviewStopped(bool success, }; // Handles RecordStarted event and informs CaptureControllerListener. -void CaptureControllerImpl::OnRecordStarted(bool success, +void CaptureControllerImpl::OnRecordStarted(CameraResult result, const std::string& error) { - if (success && record_handler_) { + if (result == CameraResult::kSuccess && record_handler_) { record_handler_->OnRecordStarted(); if (capture_controller_listener_) { capture_controller_listener_->OnStartRecordSucceeded(); } } else { if (capture_controller_listener_) { - capture_controller_listener_->OnStartRecordFailed(error); + capture_controller_listener_->OnStartRecordFailed(result, error); } // Destroy record handler on error cases to make sure state is resetted. @@ -807,13 +841,13 @@ void CaptureControllerImpl::OnRecordStarted(bool success, }; // Handles RecordStopped event and informs CaptureControllerListener. -void CaptureControllerImpl::OnRecordStopped(bool success, +void CaptureControllerImpl::OnRecordStopped(CameraResult result, const std::string& error) { if (capture_controller_listener_ && record_handler_) { // Always calls OnStopRecord listener methods // to handle separate stop record request for timed records. - if (success) { + if (result == CameraResult::kSuccess) { std::string path = record_handler_->GetRecordPath(); capture_controller_listener_->OnStopRecordSucceeded(path); if (record_handler_->IsTimedRecording()) { @@ -821,14 +855,14 @@ void CaptureControllerImpl::OnRecordStopped(bool success, path, (record_handler_->GetRecordedDuration() / 1000)); } } else { - capture_controller_listener_->OnStopRecordFailed(error); + capture_controller_listener_->OnStopRecordFailed(result, error); if (record_handler_->IsTimedRecording()) { - capture_controller_listener_->OnVideoRecordFailed(error); + capture_controller_listener_->OnVideoRecordFailed(result, error); } } } - if (success && record_handler_) { + if (result == CameraResult::kSuccess && record_handler_) { record_handler_->OnRecordStopped(); } else { // Destroy record handler on error cases to make sure state is resetted. @@ -859,7 +893,7 @@ void CaptureControllerImpl::UpdateCaptureTime(uint64_t capture_time_us) { if (preview_handler_ && preview_handler_->IsStarting()) { // Informs that first frame is captured successfully and preview has // started. - OnPreviewStarted(true, ""); + OnPreviewStarted(CameraResult::kSuccess, ""); } // Checks if max_video_duration_ms is passed. diff --git a/packages/camera/camera_windows/windows/capture_controller.h b/packages/camera/camera_windows/windows/capture_controller.h index 0b7ab66cabbd..9536be70c50a 100644 --- a/packages/camera/camera_windows/windows/capture_controller.h +++ b/packages/camera/camera_windows/windows/capture_controller.h @@ -207,28 +207,29 @@ class CaptureControllerImpl : public CaptureController, void StopTimedRecord(); // Stops preview. Called internally on camera reset and dispose. - void StopPreview(); + HRESULT StopPreview(); // Handles capture engine initalization event. - void OnCaptureEngineInitialized(bool success, const std::string& error); + void OnCaptureEngineInitialized(CameraResult result, + const std::string& error); // Handles capture engine errors. - void OnCaptureEngineError(HRESULT hr, const std::string& error); + void OnCaptureEngineError(CameraResult result, const std::string& error); // Handles picture events. - void OnPicture(bool success, const std::string& error); + void OnPicture(CameraResult result, const std::string& error); // Handles preview started events. - void OnPreviewStarted(bool success, const std::string& error); + void OnPreviewStarted(CameraResult result, const std::string& error); // Handles preview stopped events. - void OnPreviewStopped(bool success, const std::string& error); + void OnPreviewStopped(CameraResult result, const std::string& error); // Handles record started events. - void OnRecordStarted(bool success, const std::string& error); + void OnRecordStarted(CameraResult result, const std::string& error); // Handles record stopped events. - void OnRecordStopped(bool success, const std::string& error); + void OnRecordStopped(CameraResult result, const std::string& error); bool media_foundation_started_ = false; bool record_audio_ = false; diff --git a/packages/camera/camera_windows/windows/capture_controller_listener.h b/packages/camera/camera_windows/windows/capture_controller_listener.h index 0e713ea7af18..bc7a173925a8 100644 --- a/packages/camera/camera_windows/windows/capture_controller_listener.h +++ b/packages/camera/camera_windows/windows/capture_controller_listener.h @@ -9,6 +9,18 @@ namespace camera_windows { +// Results that can occur when interacting with the camera. +enum class CameraResult { + // Camera operation succeeded. + kSuccess, + + // Camera operation failed. + kError, + + // Camera access permission is denied. + kAccessDenied, +}; + // Interface for classes that receives callbacks on events from the associated // |CaptureController|. class CaptureControllerListener { @@ -22,8 +34,10 @@ class CaptureControllerListener { // Called by CaptureController if initializing the capture engine fails. // + // result: The kind of result. // error: A string describing the error. - virtual void OnCreateCaptureEngineFailed(const std::string& error) = 0; + virtual void OnCreateCaptureEngineFailed(CameraResult result, + const std::string& error) = 0; // Called by CaptureController on successfully started preview. // @@ -33,32 +47,40 @@ class CaptureControllerListener { // Called by CaptureController if starting the preview fails. // + // result: The kind of result. // error: A string describing the error. - virtual void OnStartPreviewFailed(const std::string& error) = 0; + virtual void OnStartPreviewFailed(CameraResult result, + const std::string& error) = 0; // Called by CaptureController on successfully paused preview. virtual void OnPausePreviewSucceeded() = 0; // Called by CaptureController if pausing the preview fails. // + // result: The kind of result. // error: A string describing the error. - virtual void OnPausePreviewFailed(const std::string& error) = 0; + virtual void OnPausePreviewFailed(CameraResult result, + const std::string& error) = 0; // Called by CaptureController on successfully resumed preview. virtual void OnResumePreviewSucceeded() = 0; // Called by CaptureController if resuming the preview fails. // + // result: The kind of result. // error: A string describing the error. - virtual void OnResumePreviewFailed(const std::string& error) = 0; + virtual void OnResumePreviewFailed(CameraResult result, + const std::string& error) = 0; // Called by CaptureController on successfully started recording. virtual void OnStartRecordSucceeded() = 0; // Called by CaptureController if starting the recording fails. // + // result: The kind of result. // error: A string describing the error. - virtual void OnStartRecordFailed(const std::string& error) = 0; + virtual void OnStartRecordFailed(CameraResult result, + const std::string& error) = 0; // Called by CaptureController on successfully stopped recording. // @@ -67,8 +89,10 @@ class CaptureControllerListener { // Called by CaptureController if stopping the recording fails. // + // result: The kind of result. // error: A string describing the error. - virtual void OnStopRecordFailed(const std::string& error) = 0; + virtual void OnStopRecordFailed(CameraResult result, + const std::string& error) = 0; // Called by CaptureController on successfully captured picture. // @@ -77,8 +101,10 @@ class CaptureControllerListener { // Called by CaptureController if taking picture fails. // + // result: The kind of result. // error: A string describing the error. - virtual void OnTakePictureFailed(const std::string& error) = 0; + virtual void OnTakePictureFailed(CameraResult result, + const std::string& error) = 0; // Called by CaptureController when timed recording is successfully recorded. // @@ -89,14 +115,18 @@ class CaptureControllerListener { // Called by CaptureController if timed recording fails. // + // result: The kind of result. // error: A string describing the error. - virtual void OnVideoRecordFailed(const std::string& error) = 0; + virtual void OnVideoRecordFailed(CameraResult result, + const std::string& error) = 0; // Called by CaptureController if capture engine returns error. // For example when camera is disconnected while on use. // + // result: The kind of result. // error: A string describing the error. - virtual void OnCaptureError(const std::string& error) = 0; + virtual void OnCaptureError(CameraResult result, + const std::string& error) = 0; }; } // namespace camera_windows diff --git a/packages/camera/camera_windows/windows/photo_handler.cpp b/packages/camera/camera_windows/windows/photo_handler.cpp index 10df230c2cf2..479f0d3c5ac2 100644 --- a/packages/camera/camera_windows/windows/photo_handler.cpp +++ b/packages/camera/camera_windows/windows/photo_handler.cpp @@ -116,21 +116,23 @@ HRESULT PhotoHandler::InitPhotoSink(IMFCaptureEngine* capture_engine, return hr; } -bool PhotoHandler::TakePhoto(const std::string& file_path, - IMFCaptureEngine* capture_engine, - IMFMediaType* base_media_type) { +HRESULT PhotoHandler::TakePhoto(const std::string& file_path, + IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type) { assert(!file_path.empty()); assert(capture_engine); assert(base_media_type); file_path_ = file_path; - if (FAILED(InitPhotoSink(capture_engine, base_media_type))) { - return false; + HRESULT hr = InitPhotoSink(capture_engine, base_media_type); + if (FAILED(hr)) { + return hr; } photo_state_ = PhotoState::kTakingPhoto; - return SUCCEEDED(capture_engine->TakePhoto()); + + return capture_engine->TakePhoto(); } void PhotoHandler::OnPhotoTaken() { diff --git a/packages/camera/camera_windows/windows/photo_handler.h b/packages/camera/camera_windows/windows/photo_handler.h index c12643bef85b..4d6ddf1a55b8 100644 --- a/packages/camera/camera_windows/windows/photo_handler.h +++ b/packages/camera/camera_windows/windows/photo_handler.h @@ -41,15 +41,15 @@ class PhotoHandler { // to take photo. // // Sets photo state to: kTakingPhoto. - // Returns false if photo cannot be taken. // // capture_engine: A pointer to capture engine instance. // Called to take the photo. // base_media_type: A pointer to base media type used as a base // for the actual photo capture media type. // file_path: A string that hold file path for photo capture. - bool TakePhoto(const std::string& file_path, IMFCaptureEngine* capture_engine, - IMFMediaType* base_media_type); + HRESULT TakePhoto(const std::string& file_path, + IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type); // Set the photo handler recording state to: kIdle. void OnPhotoTaken(); diff --git a/packages/camera/camera_windows/windows/preview_handler.cpp b/packages/camera/camera_windows/windows/preview_handler.cpp index d7fb2721259c..538754c3e9e2 100644 --- a/packages/camera/camera_windows/windows/preview_handler.cpp +++ b/packages/camera/camera_windows/windows/preview_handler.cpp @@ -113,29 +113,31 @@ HRESULT PreviewHandler::InitPreviewSink( return hr; } -bool PreviewHandler::StartPreview(IMFCaptureEngine* capture_engine, - IMFMediaType* base_media_type, - CaptureEngineListener* sample_callback) { +HRESULT PreviewHandler::StartPreview(IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type, + CaptureEngineListener* sample_callback) { assert(capture_engine); assert(base_media_type); - if (FAILED( - InitPreviewSink(capture_engine, base_media_type, sample_callback))) { - return false; + HRESULT hr = + InitPreviewSink(capture_engine, base_media_type, sample_callback); + + if (FAILED(hr)) { + return hr; } preview_state_ = PreviewState::kStarting; - return SUCCEEDED(capture_engine->StartPreview()); + return capture_engine->StartPreview(); } -bool PreviewHandler::StopPreview(IMFCaptureEngine* capture_engine) { +HRESULT PreviewHandler::StopPreview(IMFCaptureEngine* capture_engine) { if (preview_state_ == PreviewState::kStarting || preview_state_ == PreviewState::kRunning || preview_state_ == PreviewState::kPaused) { preview_state_ = PreviewState::kStopping; - return SUCCEEDED(capture_engine->StopPreview()); + return capture_engine->StopPreview(); } - return false; + return E_FAIL; } bool PreviewHandler::PausePreview() { diff --git a/packages/camera/camera_windows/windows/preview_handler.h b/packages/camera/camera_windows/windows/preview_handler.h index a10ba634967c..311cf5a76c2f 100644 --- a/packages/camera/camera_windows/windows/preview_handler.h +++ b/packages/camera/camera_windows/windows/preview_handler.h @@ -45,7 +45,6 @@ class PreviewHandler { // Initializes preview sink and requests capture engine to start previewing. // Sets preview state to: starting. - // Returns false if recording cannot be started. // // capture_engine: A pointer to capture engine instance. Used to start // the actual recording. @@ -53,16 +52,15 @@ class PreviewHandler { // for the actual video capture media type. // sample_callback: A pointer to capture engine listener. // This is set as sample callback for preview sink. - bool StartPreview(IMFCaptureEngine* capture_engine, - IMFMediaType* base_media_type, - CaptureEngineListener* sample_callback); + HRESULT StartPreview(IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type, + CaptureEngineListener* sample_callback); // Stops existing recording. - // Returns false if recording cannot be stopped. // // capture_engine: A pointer to capture engine instance. Used to stop // the ongoing recording. - bool StopPreview(IMFCaptureEngine* capture_engine); + HRESULT StopPreview(IMFCaptureEngine* capture_engine); // Set the preview handler recording state to: paused. bool PausePreview(); diff --git a/packages/camera/camera_windows/windows/record_handler.cpp b/packages/camera/camera_windows/windows/record_handler.cpp index 2e16527df06a..0f7192533fdd 100644 --- a/packages/camera/camera_windows/windows/record_handler.cpp +++ b/packages/camera/camera_windows/windows/record_handler.cpp @@ -192,10 +192,10 @@ HRESULT RecordHandler::InitRecordSink(IMFCaptureEngine* capture_engine, return hr; } -bool RecordHandler::StartRecord(const std::string& file_path, - int64_t max_duration, - IMFCaptureEngine* capture_engine, - IMFMediaType* base_media_type) { +HRESULT RecordHandler::StartRecord(const std::string& file_path, + int64_t max_duration, + IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type) { assert(!file_path.empty()); assert(capture_engine); assert(base_media_type); @@ -206,23 +206,21 @@ bool RecordHandler::StartRecord(const std::string& file_path, recording_start_timestamp_us_ = -1; recording_duration_us_ = 0; - if (FAILED(InitRecordSink(capture_engine, base_media_type))) { - return false; + HRESULT hr = InitRecordSink(capture_engine, base_media_type); + if (FAILED(hr)) { + return hr; } recording_state_ = RecordState::kStarting; - capture_engine->StartRecord(); - - return true; + return capture_engine->StartRecord(); } -bool RecordHandler::StopRecord(IMFCaptureEngine* capture_engine) { +HRESULT RecordHandler::StopRecord(IMFCaptureEngine* capture_engine) { if (recording_state_ == RecordState::kRunning) { recording_state_ = RecordState::kStopping; - HRESULT hr = capture_engine->StopRecord(true, false); - return SUCCEEDED(hr); + return capture_engine->StopRecord(true, false); } - return false; + return E_FAIL; } void RecordHandler::OnRecordStarted() { diff --git a/packages/camera/camera_windows/windows/record_handler.h b/packages/camera/camera_windows/windows/record_handler.h index 4cbe499360ea..0c87bf9cec64 100644 --- a/packages/camera/camera_windows/windows/record_handler.h +++ b/packages/camera/camera_windows/windows/record_handler.h @@ -45,7 +45,6 @@ class RecordHandler { // Initializes record sink and requests capture engine to start recording. // // Sets record state to: starting. - // Returns false if recording cannot be started. // // file_path: A string that hold file path for video capture. // max_duration: A int64 value of maximun recording duration. @@ -55,16 +54,15 @@ class RecordHandler { // the actual recording. // base_media_type: A pointer to base media type used as a base // for the actual video capture media type. - bool StartRecord(const std::string& file_path, int64_t max_duration, - IMFCaptureEngine* capture_engine, - IMFMediaType* base_media_type); + HRESULT StartRecord(const std::string& file_path, int64_t max_duration, + IMFCaptureEngine* capture_engine, + IMFMediaType* base_media_type); // Stops existing recording. - // Returns false if recording cannot be stopped. // // capture_engine: A pointer to capture engine instance. Used to stop // the ongoing recording. - bool StopRecord(IMFCaptureEngine* capture_engine); + HRESULT StopRecord(IMFCaptureEngine* capture_engine); // Set the record handler recording state to: running. void OnRecordStarted(); diff --git a/packages/camera/camera_windows/windows/test/camera_test.cpp b/packages/camera/camera_windows/windows/test/camera_test.cpp index 50f49536a915..158a2c26c027 100644 --- a/packages/camera/camera_windows/windows/test/camera_test.cpp +++ b/packages/camera/camera_windows/windows/test/camera_test.cpp @@ -137,14 +137,31 @@ TEST(Camera, CreateCaptureEngineReportsError) { std::unique_ptr result = std::make_unique(); - std::string error_text = "error_text"; + const std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kCreateCamera, std::move(result)); - camera->OnCreateCaptureEngineFailed(error_text); + camera->OnCreateCaptureEngineFailed(CameraResult::kError, error_text); +} + +TEST(Camera, CreateCaptureEngineReportsAccessDenied) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + const std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, + ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kCreateCamera, std::move(result)); + + camera->OnCreateCaptureEngineFailed(CameraResult::kAccessDenied, error_text); } TEST(Camera, OnStartPreviewSucceededReturnsFrameSize) { @@ -175,14 +192,31 @@ TEST(Camera, StartPreviewReportsError) { std::unique_ptr result = std::make_unique(); - std::string error_text = "error_text"; + const std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kInitialize, std::move(result)); - camera->OnStartPreviewFailed(error_text); + camera->OnStartPreviewFailed(CameraResult::kError, error_text); +} + +TEST(Camera, StartPreviewReportsAccessDenied) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + const std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, + ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kInitialize, std::move(result)); + + camera->OnStartPreviewFailed(CameraResult::kAccessDenied, error_text); } TEST(Camera, OnPausePreviewSucceededReturnsSuccess) { @@ -205,14 +239,31 @@ TEST(Camera, PausePreviewReportsError) { std::unique_ptr result = std::make_unique(); - std::string error_text = "error_text"; + const std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kPausePreview, std::move(result)); - camera->OnPausePreviewFailed(error_text); + camera->OnPausePreviewFailed(CameraResult::kError, error_text); +} + +TEST(Camera, PausePreviewReportsAccessDenied) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + const std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, + ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kPausePreview, std::move(result)); + + camera->OnPausePreviewFailed(CameraResult::kAccessDenied, error_text); } TEST(Camera, OnResumePreviewSucceededReturnsSuccess) { @@ -236,7 +287,7 @@ TEST(Camera, ResumePreviewReportsError) { std::unique_ptr result = std::make_unique(); - std::string error_text = "error_text"; + const std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); @@ -244,7 +295,25 @@ TEST(Camera, ResumePreviewReportsError) { camera->AddPendingResult(PendingResultType::kResumePreview, std::move(result)); - camera->OnResumePreviewFailed(error_text); + camera->OnResumePreviewFailed(CameraResult::kError, error_text); +} + +TEST(Camera, OnResumePreviewPermissionFailureReturnsError) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + const std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, + ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kResumePreview, + std::move(result)); + + camera->OnResumePreviewFailed(CameraResult::kAccessDenied, error_text); } TEST(Camera, OnStartRecordSucceededReturnsSuccess) { @@ -267,14 +336,31 @@ TEST(Camera, StartRecordReportsError) { std::unique_ptr result = std::make_unique(); - std::string error_text = "error_text"; + const std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kStartRecord, std::move(result)); - camera->OnStartRecordFailed(error_text); + camera->OnStartRecordFailed(CameraResult::kError, error_text); +} + +TEST(Camera, StartRecordReportsAccessDenied) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + const std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, + ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kStartRecord, std::move(result)); + + camera->OnStartRecordFailed(CameraResult::kAccessDenied, error_text); } TEST(Camera, OnStopRecordSucceededReturnsSuccess) { @@ -283,7 +369,7 @@ TEST(Camera, OnStopRecordSucceededReturnsSuccess) { std::unique_ptr result = std::make_unique(); - std::string file_path = "C:\temp\filename.mp4"; + const std::string file_path = "C:\temp\filename.mp4"; EXPECT_CALL(*result, ErrorInternal).Times(0); EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(file_path)))); @@ -299,14 +385,31 @@ TEST(Camera, StopRecordReportsError) { std::unique_ptr result = std::make_unique(); - std::string error_text = "error_text"; + const std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kStopRecord, std::move(result)); - camera->OnStopRecordFailed(error_text); + camera->OnStopRecordFailed(CameraResult::kError, error_text); +} + +TEST(Camera, StopRecordReportsAccessDenied) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + const std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, + ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kStopRecord, std::move(result)); + + camera->OnStopRecordFailed(CameraResult::kAccessDenied, error_text); } TEST(Camera, OnTakePictureSucceededReturnsSuccess) { @@ -315,7 +418,7 @@ TEST(Camera, OnTakePictureSucceededReturnsSuccess) { std::unique_ptr result = std::make_unique(); - std::string file_path = "C:\\temp\\filename.jpeg"; + const std::string file_path = "C:\\temp\\filename.jpeg"; EXPECT_CALL(*result, ErrorInternal).Times(0); EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(file_path)))); @@ -331,14 +434,31 @@ TEST(Camera, TakePictureReportsError) { std::unique_ptr result = std::make_unique(); - std::string error_text = "error_text"; + const std::string error_text = "error_text"; EXPECT_CALL(*result, SuccessInternal).Times(0); EXPECT_CALL(*result, ErrorInternal(Eq("camera_error"), Eq(error_text), _)); camera->AddPendingResult(PendingResultType::kTakePicture, std::move(result)); - camera->OnTakePictureFailed(error_text); + camera->OnTakePictureFailed(CameraResult::kError, error_text); +} + +TEST(Camera, TakePictureReportsAccessDenied) { + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr result = + std::make_unique(); + + const std::string error_text = "error_text"; + + EXPECT_CALL(*result, SuccessInternal).Times(0); + EXPECT_CALL(*result, + ErrorInternal(Eq("CameraAccessDenied"), Eq(error_text), _)); + + camera->AddPendingResult(PendingResultType::kTakePicture, std::move(result)); + + camera->OnTakePictureFailed(CameraResult::kAccessDenied, error_text); } TEST(Camera, OnVideoRecordSucceededInvokesCameraChannelEvent) { @@ -350,12 +470,12 @@ TEST(Camera, OnVideoRecordSucceededInvokesCameraChannelEvent) { std::unique_ptr binary_messenger = std::make_unique(); - std::string file_path = "C:\\temp\\filename.mp4"; - int64_t camera_id = 12345; + const std::string file_path = "C:\\temp\\filename.mp4"; + const int64_t camera_id = 12345; std::string camera_channel = std::string("plugins.flutter.io/camera_windows/camera") + std::to_string(camera_id); - int64_t video_duration = 1000000; + const int64_t video_duration = 1000000; EXPECT_CALL(*capture_controller_factory, CreateCaptureController) .Times(1) diff --git a/packages/camera/camera_windows/windows/test/capture_controller_test.cpp b/packages/camera/camera_windows/windows/test/capture_controller_test.cpp index 776c150edaee..4662d3340456 100644 --- a/packages/camera/camera_windows/windows/test/capture_controller_test.cpp +++ b/packages/camera/camera_windows/windows/test/capture_controller_test.cpp @@ -294,7 +294,52 @@ TEST(CaptureController, InitCaptureEngineReportsFailure) { EXPECT_CALL(*texture_registrar, UnregisterTexture).Times(0); EXPECT_CALL(*camera, OnCreateCaptureEngineSucceeded).Times(0); EXPECT_CALL(*camera, - OnCreateCaptureEngineFailed(Eq("Failed to create camera"))) + OnCreateCaptureEngineFailed(Eq(CameraResult::kError), + Eq("Failed to create camera"))) + .Times(1); + + bool result = capture_controller->InitCaptureDevice( + texture_registrar.get(), MOCK_DEVICE_ID, true, ResolutionPreset::kAuto); + + EXPECT_FALSE(result); + EXPECT_FALSE(engine->initialized_); + + capture_controller = nullptr; + camera = nullptr; + texture_registrar = nullptr; + engine = nullptr; +} + +TEST(CaptureController, InitCaptureEngineReportsAccessDenied) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + ComPtr video_source = new MockMediaSource(); + ComPtr audio_source = new MockMediaSource(); + + capture_controller->SetCaptureEngine( + reinterpret_cast(engine.Get())); + capture_controller->SetVideoSource( + reinterpret_cast(video_source.Get())); + capture_controller->SetAudioSource( + reinterpret_cast(audio_source.Get())); + + // Cause initialization to fail + EXPECT_CALL(*engine.Get(), Initialize) + .Times(1) + .WillOnce(Return(E_ACCESSDENIED)); + + EXPECT_CALL(*texture_registrar, RegisterTexture).Times(0); + EXPECT_CALL(*texture_registrar, UnregisterTexture).Times(0); + EXPECT_CALL(*camera, OnCreateCaptureEngineSucceeded).Times(0); + EXPECT_CALL(*camera, + OnCreateCaptureEngineFailed(Eq(CameraResult::kAccessDenied), + Eq("Failed to create camera"))) .Times(1); bool result = capture_controller->InitCaptureDevice( @@ -324,6 +369,7 @@ TEST(CaptureController, ReportsInitializedErrorEvent) { engine.Get(), camera.get(), mock_texture_id); EXPECT_CALL(*camera, OnCreateCaptureEngineFailed( + Eq(CameraResult::kError), Eq("Failed to initialize capture engine"))) .Times(1); EXPECT_CALL(*camera, OnCreateCaptureEngineSucceeded).Times(0); @@ -337,6 +383,35 @@ TEST(CaptureController, ReportsInitializedErrorEvent) { engine = nullptr; } +TEST(CaptureController, ReportsInitializedAccessDeniedEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + EXPECT_CALL(*camera, OnCreateCaptureEngineFailed( + Eq(CameraResult::kAccessDenied), + Eq("Failed to initialize capture engine"))) + .Times(1); + EXPECT_CALL(*camera, OnCreateCaptureEngineSucceeded).Times(0); + + // Send initialization failed event + engine->CreateFakeEvent(E_ACCESSDENIED, MF_CAPTURE_ENGINE_INITIALIZED); + + capture_controller = nullptr; + camera = nullptr; + texture_registrar = nullptr; + engine = nullptr; +} + TEST(CaptureController, ReportsCaptureEngineErrorEvent) { ComPtr engine = new MockCaptureEngine(); std::unique_ptr camera = @@ -351,7 +426,8 @@ TEST(CaptureController, ReportsCaptureEngineErrorEvent) { MockInitCaptureController(capture_controller.get(), texture_registrar.get(), engine.Get(), camera.get(), mock_texture_id); - EXPECT_CALL(*(camera.get()), OnCaptureError(Eq("Unspecified error"))) + EXPECT_CALL(*(camera.get()), + OnCaptureError(Eq(CameraResult::kError), Eq("Unspecified error"))) .Times(1); // Send error event. @@ -363,6 +439,33 @@ TEST(CaptureController, ReportsCaptureEngineErrorEvent) { engine = nullptr; } +TEST(CaptureController, ReportsCaptureEngineAccessDeniedEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + EXPECT_CALL(*(camera.get()), OnCaptureError(Eq(CameraResult::kAccessDenied), + Eq("Access is denied."))) + .Times(1); + + // Send error event. + engine->CreateFakeEvent(E_ACCESSDENIED, MF_CAPTURE_ENGINE_ERROR); + + capture_controller = nullptr; + camera = nullptr; + texture_registrar = nullptr; + engine = nullptr; +} + TEST(CaptureController, StartPreviewStartsProcessingSamples) { ComPtr engine = new MockCaptureEngine(); std::unique_ptr camera = @@ -477,7 +580,8 @@ TEST(CaptureController, ReportsStartPreviewError) { EXPECT_CALL(*engine.Get(), StopPreview).Times(0); EXPECT_CALL(*camera, OnStartPreviewSucceeded).Times(0); EXPECT_CALL(*camera, - OnStartPreviewFailed(Eq("Failed to start video preview"))) + OnStartPreviewFailed(Eq(CameraResult::kError), + Eq("Failed to start video preview"))) .Times(1); capture_controller->StartPreview(); @@ -517,6 +621,45 @@ TEST(CaptureController, IgnoresStartPreviewErrorEvent) { engine = nullptr; } +TEST(CaptureController, ReportsStartPreviewAccessDenied) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + // Cause start preview to fail + EXPECT_CALL(*engine.Get(), GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PREVIEW, _)) + .Times(1) + .WillOnce(Return(E_ACCESSDENIED)); + + EXPECT_CALL(*engine.Get(), StartPreview).Times(0); + EXPECT_CALL(*engine.Get(), StopPreview).Times(0); + EXPECT_CALL(*camera, OnStartPreviewSucceeded).Times(0); + EXPECT_CALL(*camera, + OnStartPreviewFailed(Eq(CameraResult::kAccessDenied), + Eq("Failed to start video preview"))) + .Times(1); + + capture_controller->StartPreview(); + + capture_controller = nullptr; + engine = nullptr; + camera = nullptr; + texture_registrar = nullptr; +} + TEST(CaptureController, StartRecordSuccess) { ComPtr engine = new MockCaptureEngine(); std::unique_ptr camera = @@ -584,7 +727,49 @@ TEST(CaptureController, ReportsStartRecordError) { EXPECT_CALL(*engine.Get(), StopRecord).Times(0); EXPECT_CALL(*camera, OnStartRecordSucceeded).Times(0); EXPECT_CALL(*camera, - OnStartRecordFailed(Eq("Failed to start video recording"))) + OnStartRecordFailed(Eq(CameraResult::kError), + Eq("Failed to start video recording"))) + .Times(1); + + capture_controller->StartRecord("mock_path", -1); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; +} + +TEST(CaptureController, ReportsStartRecordAccessDenied) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + // Cause start record to fail + EXPECT_CALL(*engine.Get(), GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_RECORD, _)) + .Times(1) + .WillOnce(Return(E_ACCESSDENIED)); + + EXPECT_CALL(*engine.Get(), StartRecord).Times(0); + EXPECT_CALL(*engine.Get(), StopRecord).Times(0); + EXPECT_CALL(*camera, OnStartRecordSucceeded).Times(0); + EXPECT_CALL(*camera, + OnStartRecordFailed(Eq(CameraResult::kAccessDenied), + Eq("Failed to start video recording"))) .Times(1); capture_controller->StartRecord("mock_path", -1); @@ -644,7 +829,9 @@ TEST(CaptureController, ReportsStartRecordErrorEvent) { // Send a start record failed event EXPECT_CALL(*camera, OnStartRecordSucceeded).Times(0); - EXPECT_CALL(*camera, OnStartRecordFailed(Eq("Unspecified error"))).Times(1); + EXPECT_CALL(*camera, OnStartRecordFailed(Eq(CameraResult::kError), + Eq("Unspecified error"))) + .Times(1); engine->CreateFakeEvent(E_FAIL, MF_CAPTURE_ENGINE_RECORD_STARTED); @@ -658,6 +845,71 @@ TEST(CaptureController, ReportsStartRecordErrorEvent) { record_sink = nullptr; } +TEST(CaptureController, ReportsStartRecordAccessDeniedEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + // Start record + ComPtr record_sink = new MockCaptureRecordSink(); + std::string mock_path_to_video = "mock_path_to_video"; + + EXPECT_CALL(*engine.Get(), StartRecord()).Times(1).WillOnce(Return(S_OK)); + + EXPECT_CALL(*engine.Get(), GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_RECORD, _)) + .Times(1) + .WillOnce([src_sink = record_sink](MF_CAPTURE_ENGINE_SINK_TYPE sink_type, + IMFCaptureSink** target_sink) { + *target_sink = src_sink.Get(); + src_sink->AddRef(); + return S_OK; + }); + + EXPECT_CALL(*record_sink.Get(), RemoveAllStreams) + .Times(1) + .WillOnce(Return(S_OK)); + EXPECT_CALL(*record_sink.Get(), AddStream) + .Times(2) + .WillRepeatedly(Return(S_OK)); + EXPECT_CALL(*record_sink.Get(), SetOutputFileName) + .Times(1) + .WillOnce(Return(S_OK)); + + // Send a start record failed event + capture_controller->StartRecord(mock_path_to_video, -1); + + EXPECT_CALL(*camera, OnStartRecordSucceeded).Times(0); + EXPECT_CALL(*camera, OnStartRecordFailed(Eq(CameraResult::kAccessDenied), + Eq("Access is denied."))) + .Times(1); + + engine->CreateFakeEvent(E_ACCESSDENIED, MF_CAPTURE_ENGINE_RECORD_STARTED); + + // Destructor shouldn't attempt to stop the recording that failed to start. + EXPECT_CALL(*engine.Get(), StopRecord).Times(0); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + record_sink = nullptr; +} + TEST(CaptureController, StopRecordSuccess) { ComPtr engine = new MockCaptureEngine(); std::unique_ptr camera = @@ -734,7 +986,52 @@ TEST(CaptureController, ReportsStopRecordError) { .WillOnce(Return(E_FAIL)); EXPECT_CALL(*camera, OnStopRecordSucceeded).Times(0); - EXPECT_CALL(*camera, OnStopRecordFailed(Eq("Failed to stop video recording"))) + EXPECT_CALL(*camera, OnStopRecordFailed(Eq(CameraResult::kError), + Eq("Failed to stop video recording"))) + .Times(1); + + capture_controller->StopRecord(); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + record_sink = nullptr; +} + +TEST(CaptureController, ReportsStopRecordAccessDenied) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + // Start record + ComPtr record_sink = new MockCaptureRecordSink(); + MockRecordStart(capture_controller.get(), engine.Get(), record_sink.Get(), + camera.get(), "mock_path_to_video"); + + // Cause stop record to fail + EXPECT_CALL(*(engine.Get()), StopRecord(true, false)) + .Times(1) + .WillOnce(Return(E_ACCESSDENIED)); + + EXPECT_CALL(*camera, OnStopRecordSucceeded).Times(0); + EXPECT_CALL(*camera, OnStopRecordFailed(Eq(CameraResult::kAccessDenied), + Eq("Failed to stop video recording"))) .Times(1); capture_controller->StopRecord(); @@ -774,7 +1071,9 @@ TEST(CaptureController, ReportsStopRecordErrorEvent) { // Send a stop record failure event EXPECT_CALL(*camera, OnStopRecordSucceeded).Times(0); - EXPECT_CALL(*camera, OnStopRecordFailed(Eq("Unspecified error"))).Times(1); + EXPECT_CALL(*camera, OnStopRecordFailed(Eq(CameraResult::kError), + Eq("Unspecified error"))) + .Times(1); engine->CreateFakeEvent(E_FAIL, MF_CAPTURE_ENGINE_RECORD_STOPPED); @@ -785,6 +1084,47 @@ TEST(CaptureController, ReportsStopRecordErrorEvent) { record_sink = nullptr; } +TEST(CaptureController, ReportsStopRecordAccessDeniedEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to start preview + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + // Start record + ComPtr record_sink = new MockCaptureRecordSink(); + std::string mock_path_to_video = "mock_path_to_video"; + MockRecordStart(capture_controller.get(), engine.Get(), record_sink.Get(), + camera.get(), mock_path_to_video); + + // Send a stop record failure event + EXPECT_CALL(*camera, OnStopRecordSucceeded).Times(0); + EXPECT_CALL(*camera, OnStopRecordFailed(Eq(CameraResult::kAccessDenied), + Eq("Access is denied."))) + .Times(1); + + engine->CreateFakeEvent(E_ACCESSDENIED, MF_CAPTURE_ENGINE_RECORD_STOPPED); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + record_sink = nullptr; +} + TEST(CaptureController, TakePictureSuccess) { ComPtr engine = new MockCaptureEngine(); std::unique_ptr camera = @@ -856,7 +1196,52 @@ TEST(CaptureController, ReportsTakePictureError) { EXPECT_CALL(*(engine.Get()), TakePhoto).Times(1).WillOnce(Return(E_FAIL)); EXPECT_CALL(*camera, OnTakePictureSucceeded).Times(0); - EXPECT_CALL(*camera, OnTakePictureFailed(Eq("Failed to take photo"))) + EXPECT_CALL(*camera, OnTakePictureFailed(Eq(CameraResult::kError), + Eq("Failed to take photo"))) + .Times(1); + + capture_controller->TakePicture("mock_path_to_photo"); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + photo_sink = nullptr; +} + +TEST(CaptureController, ReportsTakePictureAccessDenied) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to take picture + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + ComPtr photo_sink = new MockCapturePhotoSink(); + + // Initialize photo sink + MockPhotoSink(engine.Get(), photo_sink.Get()); + + // Cause take picture to fail. + EXPECT_CALL(*(engine.Get()), TakePhoto) + .Times(1) + .WillOnce(Return(E_ACCESSDENIED)); + + EXPECT_CALL(*camera, OnTakePictureSucceeded).Times(0); + EXPECT_CALL(*camera, OnTakePictureFailed(Eq(CameraResult::kAccessDenied), + Eq("Failed to take photo"))) .Times(1); capture_controller->TakePicture("mock_path_to_photo"); @@ -900,7 +1285,9 @@ TEST(CaptureController, ReportsPhotoTakenErrorEvent) { // Send take picture failed event EXPECT_CALL(*camera, OnTakePictureSucceeded).Times(0); - EXPECT_CALL(*camera, OnTakePictureFailed(Eq("Unspecified error"))).Times(1); + EXPECT_CALL(*camera, OnTakePictureFailed(Eq(CameraResult::kError), + Eq("Unspecified error"))) + .Times(1); engine->CreateFakeEvent(E_FAIL, MF_CAPTURE_ENGINE_PHOTO_TAKEN); @@ -911,6 +1298,51 @@ TEST(CaptureController, ReportsPhotoTakenErrorEvent) { photo_sink = nullptr; } +TEST(CaptureController, ReportsPhotoTakenAccessDeniedEvent) { + ComPtr engine = new MockCaptureEngine(); + std::unique_ptr camera = + std::make_unique(MOCK_DEVICE_ID); + std::unique_ptr capture_controller = + std::make_unique(camera.get()); + std::unique_ptr texture_registrar = + std::make_unique(); + + int64_t mock_texture_id = 1234; + + // Initialize capture controller to be able to take picture + MockInitCaptureController(capture_controller.get(), texture_registrar.get(), + engine.Get(), camera.get(), mock_texture_id); + + ComPtr capture_source = new MockCaptureSource(); + + // Prepare fake media types + MockAvailableMediaTypes(engine.Get(), capture_source.Get(), 1, 1); + + ComPtr photo_sink = new MockCapturePhotoSink(); + + // Initialize photo sink + MockPhotoSink(engine.Get(), photo_sink.Get()); + + // Request photo + std::string mock_path_to_photo = "mock_path_to_photo"; + EXPECT_CALL(*(engine.Get()), TakePhoto()).Times(1).WillOnce(Return(S_OK)); + capture_controller->TakePicture(mock_path_to_photo); + + // Send take picture failed event + EXPECT_CALL(*camera, OnTakePictureSucceeded).Times(0); + EXPECT_CALL(*camera, OnTakePictureFailed(Eq(CameraResult::kAccessDenied), + Eq("Access is denied."))) + .Times(1); + + engine->CreateFakeEvent(E_ACCESSDENIED, MF_CAPTURE_ENGINE_PHOTO_TAKEN); + + capture_controller = nullptr; + texture_registrar = nullptr; + engine = nullptr; + camera = nullptr; + photo_sink = nullptr; +} + TEST(CaptureController, PauseResumePreviewSuccess) { ComPtr engine = new MockCaptureEngine(); std::unique_ptr camera = @@ -963,7 +1395,8 @@ TEST(CaptureController, PausePreviewFailsIfPreviewNotStarted) { engine.Get(), camera.get(), mock_texture_id); // Pause preview fails if not started - EXPECT_CALL(*camera, OnPausePreviewFailed(Eq("Preview not started"))) + EXPECT_CALL(*camera, OnPausePreviewFailed(Eq(CameraResult::kError), + Eq("Preview not started"))) .Times(1); capture_controller->PausePreview(); @@ -989,7 +1422,8 @@ TEST(CaptureController, ResumePreviewFailsIfPreviewNotStarted) { engine.Get(), camera.get(), mock_texture_id); // Resume preview fails if not started. - EXPECT_CALL(*camera, OnResumePreviewFailed(Eq("Preview not started"))) + EXPECT_CALL(*camera, OnResumePreviewFailed(Eq(CameraResult::kError), + Eq("Preview not started"))) .Times(1); capture_controller->ResumePreview(); diff --git a/packages/camera/camera_windows/windows/test/mocks.h b/packages/camera/camera_windows/windows/test/mocks.h index 53101f5e6c17..678b75899bc2 100644 --- a/packages/camera/camera_windows/windows/test/mocks.h +++ b/packages/camera/camera_windows/windows/test/mocks.h @@ -134,41 +134,43 @@ class MockCamera : public Camera { (override)); MOCK_METHOD(std::unique_ptr>, GetPendingResultByType, (PendingResultType type)); - MOCK_METHOD(void, OnCreateCaptureEngineFailed, (const std::string& error), - (override)); + MOCK_METHOD(void, OnCreateCaptureEngineFailed, + (CameraResult result, const std::string& error), (override)); MOCK_METHOD(void, OnStartPreviewSucceeded, (int32_t width, int32_t height), (override)); - MOCK_METHOD(void, OnStartPreviewFailed, (const std::string& error), - (override)); + MOCK_METHOD(void, OnStartPreviewFailed, + (CameraResult result, const std::string& error), (override)); MOCK_METHOD(void, OnResumePreviewSucceeded, (), (override)); - MOCK_METHOD(void, OnResumePreviewFailed, (const std::string& error), - (override)); + MOCK_METHOD(void, OnResumePreviewFailed, + (CameraResult result, const std::string& error), (override)); MOCK_METHOD(void, OnPausePreviewSucceeded, (), (override)); - MOCK_METHOD(void, OnPausePreviewFailed, (const std::string& error), - (override)); + MOCK_METHOD(void, OnPausePreviewFailed, + (CameraResult result, const std::string& error), (override)); MOCK_METHOD(void, OnStartRecordSucceeded, (), (override)); - MOCK_METHOD(void, OnStartRecordFailed, (const std::string& error), - (override)); + MOCK_METHOD(void, OnStartRecordFailed, + (CameraResult result, const std::string& error), (override)); MOCK_METHOD(void, OnStopRecordSucceeded, (const std::string& file_path), (override)); - MOCK_METHOD(void, OnStopRecordFailed, (const std::string& error), (override)); + MOCK_METHOD(void, OnStopRecordFailed, + (CameraResult result, const std::string& error), (override)); MOCK_METHOD(void, OnTakePictureSucceeded, (const std::string& file_path), (override)); - MOCK_METHOD(void, OnTakePictureFailed, (const std::string& error), - (override)); + MOCK_METHOD(void, OnTakePictureFailed, + (CameraResult result, const std::string& error), (override)); MOCK_METHOD(void, OnVideoRecordSucceeded, (const std::string& file_path, int64_t video_duration), (override)); - MOCK_METHOD(void, OnVideoRecordFailed, (const std::string& error), - (override)); - MOCK_METHOD(void, OnCaptureError, (const std::string& error), (override)); + MOCK_METHOD(void, OnVideoRecordFailed, + (CameraResult result, const std::string& error), (override)); + MOCK_METHOD(void, OnCaptureError, + (CameraResult result, const std::string& error), (override)); MOCK_METHOD(bool, HasDeviceId, (std::string & device_id), (const override)); MOCK_METHOD(bool, HasCameraId, (int64_t camera_id), (const override)); From fa5eea33384a3afc3bb8cf4a21eee0cb14de3748 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 2 Aug 2022 13:55:04 -0400 Subject: [PATCH 565/844] [webview_flutter_android] Updates the Dart InstanceManager to take a listener for when an object is garbage collected (#6148) --- .../webview_flutter_android/CHANGELOG.md | 5 + .../lib/src/android_webview.dart | 5 + .../lib/src/android_webview_api_impls.dart | 297 +++++++++--------- .../lib/src/instance_manager.dart | 188 +++++++++-- .../lib/webview_android.dart | 5 +- .../lib/webview_surface_android.dart | 5 +- .../webview_flutter_android/pubspec.yaml | 2 +- .../test/android_webview_test.dart | 58 ++-- .../test/android_webview_test.mocks.dart | 5 - .../test/instance_manager_test.dart | 140 ++++++++- .../test/webview_android_widget_test.dart | 2 +- .../webview_android_widget_test.mocks.dart | 3 - 12 files changed, 480 insertions(+), 235 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 5706d653a09a..dc9850e195dd 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.9.3 + +* Updates the Dart InstanceManager to take a listener for when an object is garbage collected. + See https://github.com/flutter/flutter/issues/107199. + ## 2.9.2 * Updates the Java InstanceManager to take a listener for when an object is garbage collected. 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 005e2818a9f0..f2e305fe85c5 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 @@ -28,6 +28,11 @@ abstract class JavaObject with Copyable { /// This should only be used by subclasses created by this library or to /// create copies. JavaObject.detached(); + + /// Global instance of [InstanceManager]. + static final InstanceManager globalInstanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); } /// An Android View that displays web pages. 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 b1268f4f9cb0..c3e2096bbe0f 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 @@ -88,28 +88,27 @@ class WebViewHostApiImpl extends WebViewHostApi { WebViewHostApiImpl({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : super(binaryMessenger: binaryMessenger) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; /// Helper method to convert instances ids to objects. - Future createFromInstance(WebView instance) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - return create(instanceId, instance.useHybridComposition); - } + Future createFromInstance(WebView instance) { + return create( + instanceManager.addDartCreatedInstance(instance), + instance.useHybridComposition, + ); } /// Helper method to convert instances ids to objects. Future disposeFromInstance(WebView instance) async { - final int? instanceId = instanceManager.getInstanceId(instance); + final int? instanceId = instanceManager.getIdentifier(instance); if (instanceId != null) { + instanceManager.remove(instanceId); await dispose(instanceId); } - instanceManager.removeInstance(instance); } /// Helper method to convert the instances ids to objects. @@ -120,7 +119,7 @@ class WebViewHostApiImpl extends WebViewHostApi { String? encoding, ) { return loadData( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, data, mimeType, encoding, @@ -137,7 +136,7 @@ class WebViewHostApiImpl extends WebViewHostApi { String? historyUrl, ) { return loadDataWithBaseUrl( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, baseUrl, data, mimeType, @@ -152,7 +151,7 @@ class WebViewHostApiImpl extends WebViewHostApi { String url, Map headers, ) { - return loadUrl(instanceManager.getInstanceId(instance)!, url, headers); + return loadUrl(instanceManager.getIdentifier(instance)!, url, headers); } /// Helper method to convert instances ids to objects. @@ -161,43 +160,43 @@ class WebViewHostApiImpl extends WebViewHostApi { String url, Uint8List data, ) { - return postUrl(instanceManager.getInstanceId(instance)!, url, data); + return postUrl(instanceManager.getIdentifier(instance)!, url, data); } /// Helper method to convert instances ids to objects. Future getUrlFromInstance(WebView instance) { - return getUrl(instanceManager.getInstanceId(instance)!); + return getUrl(instanceManager.getIdentifier(instance)!); } /// Helper method to convert instances ids to objects. Future canGoBackFromInstance(WebView instance) { - return canGoBack(instanceManager.getInstanceId(instance)!); + return canGoBack(instanceManager.getIdentifier(instance)!); } /// Helper method to convert instances ids to objects. Future canGoForwardFromInstance(WebView instance) { - return canGoForward(instanceManager.getInstanceId(instance)!); + return canGoForward(instanceManager.getIdentifier(instance)!); } /// Helper method to convert instances ids to objects. Future goBackFromInstance(WebView instance) { - return goBack(instanceManager.getInstanceId(instance)!); + return goBack(instanceManager.getIdentifier(instance)!); } /// Helper method to convert instances ids to objects. Future goForwardFromInstance(WebView instance) { - return goForward(instanceManager.getInstanceId(instance)!); + return goForward(instanceManager.getIdentifier(instance)!); } /// Helper method to convert instances ids to objects. Future reloadFromInstance(WebView instance) { - return reload(instanceManager.getInstanceId(instance)!); + return reload(instanceManager.getIdentifier(instance)!); } /// Helper method to convert instances ids to objects. Future clearCacheFromInstance(WebView instance, bool includeDiskFiles) { return clearCache( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, includeDiskFiles, ); } @@ -208,34 +207,34 @@ class WebViewHostApiImpl extends WebViewHostApi { String javascriptString, ) { return evaluateJavascript( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, javascriptString, ); } /// Helper method to convert instances ids to objects. Future getTitleFromInstance(WebView instance) { - return getTitle(instanceManager.getInstanceId(instance)!); + return getTitle(instanceManager.getIdentifier(instance)!); } /// Helper method to convert instances ids to objects. Future scrollToFromInstance(WebView instance, int x, int y) { - return scrollTo(instanceManager.getInstanceId(instance)!, x, y); + return scrollTo(instanceManager.getIdentifier(instance)!, x, y); } /// Helper method to convert instances ids to objects. Future scrollByFromInstance(WebView instance, int x, int y) { - return scrollBy(instanceManager.getInstanceId(instance)!, x, y); + return scrollBy(instanceManager.getIdentifier(instance)!, x, y); } /// Helper method to convert instances ids to objects. Future getScrollXFromInstance(WebView instance) { - return getScrollX(instanceManager.getInstanceId(instance)!); + return getScrollX(instanceManager.getIdentifier(instance)!); } /// Helper method to convert instances ids to objects. Future getScrollYFromInstance(WebView instance) { - return getScrollY(instanceManager.getInstanceId(instance)!); + return getScrollY(instanceManager.getIdentifier(instance)!); } /// Helper method to convert instances ids to objects. @@ -244,8 +243,8 @@ class WebViewHostApiImpl extends WebViewHostApi { WebViewClient webViewClient, ) { return setWebViewClient( - instanceManager.getInstanceId(instance)!, - instanceManager.getInstanceId(webViewClient)!, + instanceManager.getIdentifier(instance)!, + instanceManager.getIdentifier(webViewClient)!, ); } @@ -255,8 +254,8 @@ class WebViewHostApiImpl extends WebViewHostApi { JavaScriptChannel javaScriptChannel, ) { return addJavaScriptChannel( - instanceManager.getInstanceId(instance)!, - instanceManager.getInstanceId(javaScriptChannel)!, + instanceManager.getIdentifier(instance)!, + instanceManager.getIdentifier(javaScriptChannel)!, ); } @@ -266,8 +265,8 @@ class WebViewHostApiImpl extends WebViewHostApi { JavaScriptChannel javaScriptChannel, ) { return removeJavaScriptChannel( - instanceManager.getInstanceId(instance)!, - instanceManager.getInstanceId(javaScriptChannel)!, + instanceManager.getIdentifier(instance)!, + instanceManager.getIdentifier(javaScriptChannel)!, ); } @@ -277,8 +276,8 @@ class WebViewHostApiImpl extends WebViewHostApi { DownloadListener? listener, ) { return setDownloadListener( - instanceManager.getInstanceId(instance)!, - listener != null ? instanceManager.getInstanceId(listener) : null, + instanceManager.getIdentifier(instance)!, + listener != null ? instanceManager.getIdentifier(listener) : null, ); } @@ -288,14 +287,14 @@ class WebViewHostApiImpl extends WebViewHostApi { WebChromeClient? client, ) { return setWebChromeClient( - instanceManager.getInstanceId(instance)!, - client != null ? instanceManager.getInstanceId(client) : null, + instanceManager.getIdentifier(instance)!, + client != null ? instanceManager.getIdentifier(client) : null, ); } /// Helper method to convert instances ids to objects. Future setBackgroundColorFromInstance(WebView instance, int color) { - return setBackgroundColor(instanceManager.getInstanceId(instance)!, color); + return setBackgroundColor(instanceManager.getIdentifier(instance)!, color); } } @@ -305,28 +304,25 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { WebSettingsHostApiImpl({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : super(binaryMessenger: binaryMessenger) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; /// Helper method to convert instances ids to objects. - Future createFromInstance(WebSettings instance, WebView webView) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - return create( - instanceId, - instanceManager.getInstanceId(webView)!, - ); - } + Future createFromInstance(WebSettings instance, WebView webView) { + return create( + instanceManager.addDartCreatedInstance(instance), + instanceManager.getIdentifier(webView)!, + ); } /// Helper method to convert instances ids to objects. Future disposeFromInstance(WebSettings instance) async { - final int? instanceId = instanceManager.removeInstance(instance); + final int? instanceId = instanceManager.getIdentifier(instance); if (instanceId != null) { + instanceManager.remove(instanceId); return dispose(instanceId); } } @@ -336,7 +332,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { WebSettings instance, bool flag, ) { - return setDomStorageEnabled(instanceManager.getInstanceId(instance)!, flag); + return setDomStorageEnabled(instanceManager.getIdentifier(instance)!, flag); } /// Helper method to convert instances ids to objects. @@ -345,7 +341,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { bool flag, ) { return setJavaScriptCanOpenWindowsAutomatically( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, flag, ); } @@ -356,7 +352,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { bool support, ) { return setSupportMultipleWindows( - instanceManager.getInstanceId(instance)!, support); + instanceManager.getIdentifier(instance)!, support); } /// Helper method to convert instances ids to objects. @@ -365,7 +361,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { bool flag, ) { return setJavaScriptEnabled( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, flag, ); } @@ -376,7 +372,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { String? userAgentString, ) { return setUserAgentString( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, userAgentString, ); } @@ -387,7 +383,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { bool require, ) { return setMediaPlaybackRequiresUserGesture( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, require, ); } @@ -397,7 +393,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { WebSettings instance, bool support, ) { - return setSupportZoom(instanceManager.getInstanceId(instance)!, support); + return setSupportZoom(instanceManager.getIdentifier(instance)!, support); } /// Helper method to convert instances ids to objects. @@ -406,7 +402,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { bool overview, ) { return setLoadWithOverviewMode( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, overview, ); } @@ -416,7 +412,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { WebSettings instance, bool use, ) { - return setUseWideViewPort(instanceManager.getInstanceId(instance)!, use); + return setUseWideViewPort(instanceManager.getIdentifier(instance)!, use); } /// Helper method to convert instances ids to objects. @@ -425,7 +421,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { bool enabled, ) { return setDisplayZoomControls( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, enabled, ); } @@ -436,7 +432,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { bool enabled, ) { return setBuiltInZoomControls( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, enabled, ); } @@ -447,7 +443,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { bool enabled, ) { return setAllowFileAccess( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, enabled, ); } @@ -459,18 +455,20 @@ class JavaScriptChannelHostApiImpl extends JavaScriptChannelHostApi { JavaScriptChannelHostApiImpl({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : super(binaryMessenger: binaryMessenger) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; /// Helper method to convert instances ids to objects. Future createFromInstance(JavaScriptChannel instance) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - return create(instanceId, instance.channelName); + if (instanceManager.getIdentifier(instance) == null) { + final int identifier = instanceManager.addDartCreatedInstance(instance); + await create( + identifier, + instance.channelName, + ); } } } @@ -478,22 +476,21 @@ class JavaScriptChannelHostApiImpl extends JavaScriptChannelHostApi { /// Flutter api implementation for [JavaScriptChannel]. class JavaScriptChannelFlutterApiImpl extends JavaScriptChannelFlutterApi { /// Constructs a [JavaScriptChannelFlutterApiImpl]. - JavaScriptChannelFlutterApiImpl({InstanceManager? instanceManager}) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + JavaScriptChannelFlutterApiImpl({InstanceManager? instanceManager}) + : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; @override void dispose(int instanceId) { - instanceManager.removeInstance(instanceId); + instanceManager.remove(instanceId); } @override void postMessage(int instanceId, String message) { - final JavaScriptChannel? instance = - instanceManager.getInstance(instanceId) as JavaScriptChannel?; + final JavaScriptChannel? instance = instanceManager + .getInstanceWithWeakReference(instanceId) as JavaScriptChannel?; assert( instance != null, 'InstanceManager does not contain an JavaScriptChannel with instanceId: $instanceId', @@ -508,18 +505,17 @@ class WebViewClientHostApiImpl extends WebViewClientHostApi { WebViewClientHostApiImpl({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : super(binaryMessenger: binaryMessenger) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; /// Helper method to convert instances ids to objects. Future createFromInstance(WebViewClient instance) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - return create(instanceId, instance.shouldOverrideUrlLoading); + if (instanceManager.getIdentifier(instance) == null) { + final int identifier = instanceManager.addDartCreatedInstance(instance); + return create(identifier, instance.shouldOverrideUrlLoading); } } } @@ -527,24 +523,23 @@ class WebViewClientHostApiImpl extends WebViewClientHostApi { /// Flutter api implementation for [WebViewClient]. class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi { /// Constructs a [WebViewClientFlutterApiImpl]. - WebViewClientFlutterApiImpl({InstanceManager? instanceManager}) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + WebViewClientFlutterApiImpl({InstanceManager? instanceManager}) + : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; @override void dispose(int instanceId) { - instanceManager.removeInstance(instanceId); + instanceManager.remove(instanceId); } @override void onPageFinished(int instanceId, int webViewInstanceId, String url) { - final WebViewClient? instance = - instanceManager.getInstance(instanceId) as WebViewClient?; - final WebView? webViewInstance = - instanceManager.getInstance(webViewInstanceId) as WebView?; + 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', @@ -558,10 +553,10 @@ class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi { @override void onPageStarted(int instanceId, int webViewInstanceId, String url) { - final WebViewClient? instance = - instanceManager.getInstance(instanceId) as WebViewClient?; - final WebView? webViewInstance = - instanceManager.getInstance(webViewInstanceId) as WebView?; + 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', @@ -581,10 +576,10 @@ class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi { String description, String failingUrl, ) { - final WebViewClient? instance = - instanceManager.getInstance(instanceId) as WebViewClient?; - final WebView? webViewInstance = - instanceManager.getInstance(webViewInstanceId) as WebView?; + 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', @@ -609,10 +604,10 @@ class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi { WebResourceRequestData request, WebResourceErrorData error, ) { - final WebViewClient? instance = - instanceManager.getInstance(instanceId) as WebViewClient?; - final WebView? webViewInstance = - instanceManager.getInstance(webViewInstanceId) as WebView?; + 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', @@ -634,10 +629,10 @@ class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi { int webViewInstanceId, WebResourceRequestData request, ) { - final WebViewClient? instance = - instanceManager.getInstance(instanceId) as WebViewClient?; - final WebView? webViewInstance = - instanceManager.getInstance(webViewInstanceId) as WebView?; + 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', @@ -655,10 +650,10 @@ class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi { int webViewInstanceId, String url, ) { - final WebViewClient? instance = - instanceManager.getInstance(instanceId) as WebViewClient?; - final WebView? webViewInstance = - instanceManager.getInstance(webViewInstanceId) as WebView?; + 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', @@ -677,18 +672,17 @@ class DownloadListenerHostApiImpl extends DownloadListenerHostApi { DownloadListenerHostApiImpl({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : super(binaryMessenger: binaryMessenger) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; /// Helper method to convert instances ids to objects. Future createFromInstance(DownloadListener instance) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - return create(instanceId); + if (instanceManager.getIdentifier(instance) == null) { + final int identifier = instanceManager.addDartCreatedInstance(instance); + return create(identifier); } } } @@ -696,16 +690,15 @@ class DownloadListenerHostApiImpl extends DownloadListenerHostApi { /// Flutter api implementation for [DownloadListener]. class DownloadListenerFlutterApiImpl extends DownloadListenerFlutterApi { /// Constructs a [DownloadListenerFlutterApiImpl]. - DownloadListenerFlutterApiImpl({InstanceManager? instanceManager}) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + DownloadListenerFlutterApiImpl({InstanceManager? instanceManager}) + : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; @override void dispose(int instanceId) { - instanceManager.removeInstance(instanceId); + instanceManager.remove(instanceId); } @override @@ -717,8 +710,8 @@ class DownloadListenerFlutterApiImpl extends DownloadListenerFlutterApi { String mimetype, int contentLength, ) { - final DownloadListener? instance = - instanceManager.getInstance(instanceId) as DownloadListener?; + final DownloadListener? instance = instanceManager + .getInstanceWithWeakReference(instanceId) as DownloadListener?; assert( instance != null, 'InstanceManager does not contain an DownloadListener with instanceId: $instanceId', @@ -739,21 +732,23 @@ class WebChromeClientHostApiImpl extends WebChromeClientHostApi { WebChromeClientHostApiImpl({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : super(binaryMessenger: binaryMessenger) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; /// Helper method to convert instances ids to objects. Future createFromInstance( WebChromeClient instance, WebViewClient webViewClient, ) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - return create(instanceId, instanceManager.getInstanceId(webViewClient)!); + if (instanceManager.getIdentifier(instance) == null) { + final int identifier = instanceManager.addDartCreatedInstance(instance); + return create( + identifier, + instanceManager.getIdentifier(webViewClient)!, + ); } } } @@ -761,24 +756,23 @@ class WebChromeClientHostApiImpl extends WebChromeClientHostApi { /// Flutter api implementation for [DownloadListener]. class WebChromeClientFlutterApiImpl extends WebChromeClientFlutterApi { /// Constructs a [DownloadListenerFlutterApiImpl]. - WebChromeClientFlutterApiImpl({InstanceManager? instanceManager}) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + WebChromeClientFlutterApiImpl({InstanceManager? instanceManager}) + : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; @override void dispose(int instanceId) { - instanceManager.removeInstance(instanceId); + instanceManager.remove(instanceId); } @override void onProgressChanged(int instanceId, int webViewInstanceId, int progress) { - final WebChromeClient? instance = - instanceManager.getInstance(instanceId) as WebChromeClient?; - final WebView? webViewInstance = - instanceManager.getInstance(webViewInstanceId) as WebView?; + final WebChromeClient? instance = instanceManager + .getInstanceWithWeakReference(instanceId) as WebChromeClient?; + final WebView? webViewInstance = instanceManager + .getInstanceWithWeakReference(webViewInstanceId) as WebView?; assert( instance != null, 'InstanceManager does not contain an WebChromeClient with instanceId: $instanceId', @@ -797,23 +791,22 @@ class WebStorageHostApiImpl extends WebStorageHostApi { WebStorageHostApiImpl({ BinaryMessenger? binaryMessenger, InstanceManager? instanceManager, - }) : super(binaryMessenger: binaryMessenger) { - this.instanceManager = instanceManager ?? InstanceManager.instance; - } + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); /// Maintains instances stored to communicate with java objects. - late final InstanceManager instanceManager; + final InstanceManager instanceManager; /// Helper method to convert instances ids to objects. Future createFromInstance(WebStorage instance) async { - final int? instanceId = instanceManager.tryAddInstance(instance); - if (instanceId != null) { - return create(instanceId); + if (instanceManager.getIdentifier(instance) == null) { + final int identifier = instanceManager.addDartCreatedInstance(instance); + return create(identifier); } } /// Helper method to convert instances ids to objects. Future deleteAllDataFromInstance(WebStorage instance) { - return deleteAllData(instanceManager.getInstanceId(instance)!); + return deleteAllData(instanceManager.getIdentifier(instance)!); } } diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart b/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart index 531fffa50e81..5892823aabd3 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart @@ -23,51 +23,179 @@ mixin Copyable { Copyable copy(); } -/// Maintains instances stored to communicate with java objects. +/// 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 InstanceManager { - final Map _instanceIdsToInstances = {}; - final Map _instancesToInstanceIds = {}; + /// Constructs an [InstanceManager]. + InstanceManager({required void Function(int) onWeakReferenceRemoved}) { + this.onWeakReferenceRemoved = (int identifier) { + _weakInstances.remove(identifier); + onWeakReferenceRemoved(identifier); + }; + _finalizer = Finalizer(this.onWeakReferenceRemoved); + } - int _nextInstanceId = 0; + // 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; + + // 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; + + /// 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(Copyable instance) { + assert(getIdentifier(instance) == null); - /// Global instance of [InstanceManager]. - static final InstanceManager instance = InstanceManager(); + final int identifier = _nextUniqueIdentifier(); + _addInstanceWithIdentifier(instance, identifier); + return identifier; + } - /// Attempt to add a new instance. + /// 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. /// - /// Returns new if [instance] has already been added. Otherwise, it is added - /// with a new instance id. - int? tryAddInstance(Object instance) { - if (_instancesToInstanceIds.containsKey(instance)) { + /// This does not remove the the strong referenced instance associated with + /// [instance]. This can be done with [remove]. + int? removeWeakReference(Copyable instance) { + final int? identifier = getIdentifier(instance); + if (identifier == null) { return null; } - final int instanceId = _nextInstanceId++; - _instancesToInstanceIds[instance] = instanceId; - _instanceIdsToInstances[instanceId] = instance; - return instanceId; + _identifiers[instance] = null; + _finalizer.detach(instance); + onWeakReferenceRemoved(identifier); + + return identifier; } - /// Remove the instance from the manager. + /// 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. /// - /// Returns null if the instance is removed. Otherwise, return the instanceId - /// of the removed instance. - int? removeInstance(Object instance) { - final int? instanceId = _instancesToInstanceIds[instance]; - if (instanceId != null) { - _instancesToInstanceIds.remove(instance); - _instanceIdsToInstances.remove(instanceId); + /// This does not remove the the weak referenced instance associtated 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 Copyable? weakInstance = _weakInstances[identifier]?.target; + + if (weakInstance == null) { + final Copyable? strongInstance = _strongInstances[identifier]; + if (strongInstance != null) { + final Copyable copy = strongInstance.copy(); + _identifiers[copy] = identifier; + _weakInstances[identifier] = WeakReference(copy); + _finalizer.attach(copy, identifier, detach: copy); + return copy as T; + } + return strongInstance as T?; } - return instanceId; + + return weakInstance as T; + } + + /// Retrieves the identifier associated with instance. + int? getIdentifier(Copyable 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(Copyable instance, int identifier) { + assert(!containsIdentifier(identifier)); + assert(getIdentifier(instance) == null); + assert(identifier >= 0); + _addInstanceWithIdentifier(instance, identifier); + } + + void _addInstanceWithIdentifier(Copyable instance, int identifier) { + _identifiers[instance] = identifier; + _weakInstances[identifier] = WeakReference(instance); + _finalizer.attach(instance, identifier, detach: instance); + + final Copyable copy = instance.copy(); + _identifiers[copy] = identifier; + _strongInstances[identifier] = copy; } - /// Retrieve the Object paired with instanceId. - Object? getInstance(int instanceId) { - return _instanceIdsToInstances[instanceId]; + /// Whether this manager contains the given [identifier]. + bool containsIdentifier(int identifier) { + return _weakInstances.containsKey(identifier) || + _strongInstances.containsKey(identifier); } - /// Retrieve the instanceId paired with instance. - int? getInstanceId(Object instance) { - return _instancesToInstanceIds[instance]; + int _nextUniqueIdentifier() { + late int identifier; + do { + identifier = _nextIdentifier; + _nextIdentifier = (_nextIdentifier + 1) % _maxDartCreatedIdentifier; + } while (containsIdentifier(identifier)); + return identifier; } } diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart index 1f0eb7bd7ded..47740247db73 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart @@ -11,7 +11,6 @@ import 'package:flutter/widgets.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'src/android_webview.dart'; -import 'src/instance_manager.dart'; import 'webview_android_widget.dart'; /// Builds an Android webview. @@ -55,8 +54,8 @@ class AndroidWebView implements WebViewPlatform { gestureRecognizers: gestureRecognizers, layoutDirection: Directionality.maybeOf(context) ?? TextDirection.rtl, - creationParams: - InstanceManager.instance.getInstanceId(controller.webView), + creationParams: JavaObject.globalInstanceManager + .getIdentifier(controller.webView), creationParamsCodec: const StandardMessageCodec(), ), ); diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart index 61ec11012881..e89fb7d1512f 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart @@ -10,7 +10,6 @@ import 'package:flutter/widgets.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'src/android_webview.dart'; -import 'src/instance_manager.dart'; import 'webview_android.dart'; import 'webview_android_widget.dart'; @@ -74,8 +73,8 @@ class SurfaceAndroidWebView extends AndroidWebView { // directionality. layoutDirection: Directionality.maybeOf(context) ?? TextDirection.ltr, - webViewIdentifier: - InstanceManager.instance.getInstanceId(controller.webView)!, + webViewIdentifier: JavaObject.globalInstanceManager + .getIdentifier(controller.webView)!, ) ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated) ..addOnPlatformViewCreatedListener((int id) { diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index f76534196caf..a60c4f9cc313 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/plugins/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: 2.9.2 +version: 2.9.3 environment: sdk: ">=2.14.0 <3.0.0" 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 cb2ea0fbf839..9d479e62161a 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 @@ -45,11 +45,11 @@ void main() { mockPlatformHostApi = MockTestWebViewHostApi(); TestWebViewHostApi.setup(mockPlatformHostApi); - instanceManager = InstanceManager(); + instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {}); WebView.api = WebViewHostApiImpl(instanceManager: instanceManager); webView = WebView(); - webViewInstanceId = instanceManager.getInstanceId(webView)!; + webViewInstanceId = instanceManager.getIdentifier(webView)!; }); test('create', () { @@ -206,11 +206,12 @@ void main() { ); final WebViewClient mockWebViewClient = MockWebViewClient(); + when(mockWebViewClient.copy()).thenReturn(MockWebViewClient()); when(mockWebViewClient.shouldOverrideUrlLoading).thenReturn(false); webView.setWebViewClient(mockWebViewClient); final int webViewClientInstanceId = - instanceManager.getInstanceId(mockWebViewClient)!; + instanceManager.getIdentifier(mockWebViewClient)!; verify(mockPlatformHostApi.setWebViewClient( webViewInstanceId, webViewClientInstanceId, @@ -224,12 +225,13 @@ void main() { ); final JavaScriptChannel mockJavaScriptChannel = MockJavaScriptChannel(); + when(mockJavaScriptChannel.copy()).thenReturn(MockJavaScriptChannel()); when(mockJavaScriptChannel.channelName).thenReturn('aChannel'); webView.addJavaScriptChannel(mockJavaScriptChannel); final int javaScriptChannelInstanceId = - instanceManager.getInstanceId(mockJavaScriptChannel)!; + instanceManager.getIdentifier(mockJavaScriptChannel)!; verify(mockPlatformHostApi.addJavaScriptChannel( webViewInstanceId, javaScriptChannelInstanceId, @@ -243,6 +245,7 @@ void main() { ); final JavaScriptChannel mockJavaScriptChannel = MockJavaScriptChannel(); + when(mockJavaScriptChannel.copy()).thenReturn(MockJavaScriptChannel()); when(mockJavaScriptChannel.channelName).thenReturn('aChannel'); expect( @@ -254,7 +257,7 @@ void main() { webView.removeJavaScriptChannel(mockJavaScriptChannel); final int javaScriptChannelInstanceId = - instanceManager.getInstanceId(mockJavaScriptChannel)!; + instanceManager.getIdentifier(mockJavaScriptChannel)!; verify(mockPlatformHostApi.removeJavaScriptChannel( webViewInstanceId, javaScriptChannelInstanceId, @@ -268,10 +271,11 @@ void main() { ); final DownloadListener mockDownloadListener = MockDownloadListener(); + when(mockDownloadListener.copy()).thenReturn(MockDownloadListener()); webView.setDownloadListener(mockDownloadListener); final int downloadListenerInstanceId = - instanceManager.getInstanceId(mockDownloadListener)!; + instanceManager.getIdentifier(mockDownloadListener)!; verify(mockPlatformHostApi.setDownloadListener( webViewInstanceId, downloadListenerInstanceId, @@ -285,6 +289,7 @@ void main() { instanceManager: instanceManager, ); final WebViewClient mockWebViewClient = MockWebViewClient(); + when(mockWebViewClient.copy()).thenReturn(MockWebViewClient()); when(mockWebViewClient.shouldOverrideUrlLoading).thenReturn(false); webView.setWebViewClient(mockWebViewClient); @@ -294,10 +299,11 @@ void main() { ); final WebChromeClient mockWebChromeClient = MockWebChromeClient(); + when(mockWebChromeClient.copy()).thenReturn(MockWebChromeClient()); webView.setWebChromeClient(mockWebChromeClient); final int webChromeClientInstanceId = - instanceManager.getInstanceId(mockWebChromeClient)!; + instanceManager.getIdentifier(mockWebChromeClient)!; verify(mockPlatformHostApi.setWebChromeClient( webViewInstanceId, webChromeClientInstanceId, @@ -312,7 +318,7 @@ void main() { WebSettings.api = WebSettingsHostApiImpl(instanceManager: instanceManager); final int webSettingsInstanceId = - instanceManager.getInstanceId(webView.settings)!; + instanceManager.getIdentifier(webView.settings)!; webView.release(); verify(mockWebSettingsPlatformHostApi.dispose(webSettingsInstanceId)); @@ -333,7 +339,7 @@ void main() { late int webSettingsInstanceId; setUp(() { - instanceManager = InstanceManager(); + instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {}); TestWebViewHostApi.setup(MockTestWebViewHostApi()); WebView.api = WebViewHostApiImpl(instanceManager: instanceManager); @@ -346,7 +352,7 @@ void main() { ); webSettings = WebSettings(WebView()); - webSettingsInstanceId = instanceManager.getInstanceId(webSettings)!; + webSettingsInstanceId = instanceManager.getIdentifier(webSettings)!; }); test('create', () { @@ -463,14 +469,16 @@ void main() { late int mockJavaScriptChannelInstanceId; setUp(() { - instanceManager = InstanceManager(); + instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {}); flutterApi = JavaScriptChannelFlutterApiImpl( instanceManager: instanceManager, ); mockJavaScriptChannel = MockJavaScriptChannel(); + when(mockJavaScriptChannel.copy()).thenReturn(MockJavaScriptChannel()); + mockJavaScriptChannelInstanceId = - instanceManager.tryAddInstance(mockJavaScriptChannel)!; + instanceManager.addDartCreatedInstance(mockJavaScriptChannel); }); test('postMessage', () { @@ -501,17 +509,20 @@ void main() { late int mockWebViewInstanceId; setUp(() { - instanceManager = InstanceManager(); + instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {}); flutterApi = WebViewClientFlutterApiImpl( instanceManager: instanceManager, ); mockWebViewClient = MockWebViewClient(); + when(mockWebViewClient.copy()).thenReturn(MockWebViewClient()); mockWebViewClientInstanceId = - instanceManager.tryAddInstance(mockWebViewClient)!; + instanceManager.addDartCreatedInstance(mockWebViewClient); mockWebView = MockWebView(); - mockWebViewInstanceId = instanceManager.tryAddInstance(mockWebView)!; + when(mockWebView.copy()).thenReturn(MockWebView()); + mockWebViewInstanceId = + instanceManager.addDartCreatedInstance(mockWebView); }); test('onPageStarted', () { @@ -621,14 +632,15 @@ void main() { late int mockDownloadListenerInstanceId; setUp(() { - instanceManager = InstanceManager(); + instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {}); flutterApi = DownloadListenerFlutterApiImpl( instanceManager: instanceManager, ); mockDownloadListener = MockDownloadListener(); + when(mockDownloadListener.copy()).thenReturn(MockDownloadListener()); mockDownloadListenerInstanceId = - instanceManager.tryAddInstance(mockDownloadListener)!; + instanceManager.addDartCreatedInstance(mockDownloadListener); }); test('onPageStarted', () { @@ -666,17 +678,21 @@ void main() { late int mockWebViewInstanceId; setUp(() { - instanceManager = InstanceManager(); + instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {}); flutterApi = WebChromeClientFlutterApiImpl( instanceManager: instanceManager, ); mockWebChromeClient = MockWebChromeClient(); + when(mockWebChromeClient.copy()).thenReturn(MockWebChromeClient()); + mockWebChromeClientInstanceId = - instanceManager.tryAddInstance(mockWebChromeClient)!; + instanceManager.addDartCreatedInstance(mockWebChromeClient); mockWebView = MockWebView(); - mockWebViewInstanceId = instanceManager.tryAddInstance(mockWebView)!; + when(mockWebView.copy()).thenReturn(MockWebView()); + mockWebViewInstanceId = + instanceManager.addDartCreatedInstance(mockWebView); }); test('onPageStarted', () { @@ -722,7 +738,7 @@ void main() { webStorage = WebStorage(); webStorageInstanceId = - WebStorage.api.instanceManager.getInstanceId(webStorage)!; + WebStorage.api.instanceManager.getIdentifier(webStorage)!; }); test('create', () { 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 72d1d414f915..db877e5870b6 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 @@ -59,7 +59,6 @@ class MockCookieManagerHostApi extends _i1.Mock /// A class which mocks [DownloadListener]. /// /// See the documentation for Mockito's code generation for more information. -// ignore: must_be_immutable class MockDownloadListener extends _i1.Mock implements _i2.DownloadListener { MockDownloadListener() { _i1.throwOnMissingStub(this); @@ -81,7 +80,6 @@ class MockDownloadListener extends _i1.Mock implements _i2.DownloadListener { /// A class which mocks [JavaScriptChannel]. /// /// See the documentation for Mockito's code generation for more information. -// ignore: must_be_immutable class MockJavaScriptChannel extends _i1.Mock implements _i2.JavaScriptChannel { MockJavaScriptChannel() { _i1.throwOnMissingStub(this); @@ -417,7 +415,6 @@ class MockTestAssetManagerHostApi extends _i1.Mock /// A class which mocks [WebChromeClient]. /// /// See the documentation for Mockito's code generation for more information. -// ignore: must_be_immutable class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { MockWebChromeClient() { _i1.throwOnMissingStub(this); @@ -436,7 +433,6 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { /// A class which mocks [WebView]. /// /// See the documentation for Mockito's code generation for more information. -// ignore: must_be_immutable class MockWebView extends _i1.Mock implements _i2.WebView { MockWebView() { _i1.throwOnMissingStub(this); @@ -590,7 +586,6 @@ class MockWebView extends _i1.Mock implements _i2.WebView { /// A class which mocks [WebViewClient]. /// /// See the documentation for Mockito's code generation for more information. -// ignore: must_be_immutable class MockWebViewClient extends _i1.Mock implements _i2.WebViewClient { MockWebViewClient() { _i1.throwOnMissingStub(this); diff --git a/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart b/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart index 3aeb005efb86..dd3dcca29fe8 100644 --- a/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart @@ -7,29 +7,137 @@ import 'package:webview_flutter_android/src/instance_manager.dart'; void main() { group('InstanceManager', () { - late InstanceManager testInstanceManager; + test('addHostCreatedInstance', () { + final CopyableObject object = CopyableObject(); - setUp(() { - testInstanceManager = InstanceManager(); + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance(object, 0); + + expect(instanceManager.getIdentifier(object), 0); + expect( + instanceManager.getInstanceWithWeakReference(0), + object, + ); + }); + + test('addHostCreatedInstance prevents already used objects and ids', () { + final CopyableObject object = CopyableObject(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance(object, 0); + + expect( + () => instanceManager.addHostCreatedInstance(object, 0), + throwsAssertionError, + ); + + expect( + () => instanceManager.addHostCreatedInstance(CopyableObject(), 0), + throwsAssertionError, + ); + }); + + test('addFlutterCreatedInstance', () { + final CopyableObject object = CopyableObject(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addDartCreatedInstance(object); + + final int? instanceId = instanceManager.getIdentifier(object); + expect(instanceId, isNotNull); + expect( + instanceManager.getInstanceWithWeakReference(instanceId!), + object, + ); + }); + + test('removeWeakReference', () { + final CopyableObject object = CopyableObject(); + + int? weakInstanceId; + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (int instanceId) { + weakInstanceId = instanceId; + }); + + instanceManager.addHostCreatedInstance(object, 0); + + expect(instanceManager.removeWeakReference(object), 0); + expect( + instanceManager.getInstanceWithWeakReference(0), + isA(), + ); + expect(weakInstanceId, 0); + }); + + test('removeWeakReference removes only weak reference', () { + final CopyableObject object = CopyableObject(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance(object, 0); + + expect(instanceManager.removeWeakReference(object), 0); + final CopyableObject copy = instanceManager.getInstanceWithWeakReference( + 0, + )!; + expect(identical(object, copy), isFalse); + }); + + test('removeStrongReference', () { + final CopyableObject object = CopyableObject(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance(object, 0); + instanceManager.removeWeakReference(object); + expect(instanceManager.remove(0), isA()); + expect(instanceManager.containsIdentifier(0), isFalse); }); - test('tryAddInstance', () { - final Object object = Object(); + test('removeStrongReference removes only strong reference', () { + final CopyableObject object = CopyableObject(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); - expect(testInstanceManager.tryAddInstance(object), 0); - expect(testInstanceManager.getInstanceId(object), 0); - expect(testInstanceManager.getInstance(0), object); - expect(testInstanceManager.tryAddInstance(object), null); + instanceManager.addHostCreatedInstance(object, 0); + expect(instanceManager.remove(0), isA()); + expect( + instanceManager.getInstanceWithWeakReference(0), + object, + ); }); - test('removeInstance', () { - final Object object = Object(); - testInstanceManager.tryAddInstance(object); + test('getInstance can add a new weak reference', () { + final CopyableObject object = CopyableObject(); - expect(testInstanceManager.removeInstance(object), 0); - expect(testInstanceManager.getInstanceId(object), null); - expect(testInstanceManager.getInstance(0), null); - expect(testInstanceManager.removeInstance(object), null); + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance(object, 0); + instanceManager.removeWeakReference(object); + + final CopyableObject newWeakCopy = + instanceManager.getInstanceWithWeakReference( + 0, + )!; + expect(identical(object, newWeakCopy), isFalse); }); }); } + +class CopyableObject with Copyable { + @override + Copyable copy() { + return CopyableObject(); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart index 2432b35a4814..2fa75af72d69 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart @@ -828,7 +828,7 @@ void main() { // of WebView itstelf. mockPlatformHostApi = MockTestWebViewHostApi(); TestWebViewHostApi.setup(mockPlatformHostApi); - instanceManager = InstanceManager(); + instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {}); android_webview.WebView.api = WebViewHostApiImpl(instanceManager: instanceManager); }); diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart index f9e8838500d5..04c2d778ed19 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart @@ -64,7 +64,6 @@ class MockFlutterAssetManager extends _i1.Mock /// A class which mocks [WebSettings]. /// /// See the documentation for Mockito's code generation for more information. -// ignore: must_be_immutable class MockWebSettings extends _i1.Mock implements _i2.WebSettings { MockWebSettings() { _i1.throwOnMissingStub(this); @@ -140,7 +139,6 @@ class MockWebSettings extends _i1.Mock implements _i2.WebSettings { /// A class which mocks [WebStorage]. /// /// See the documentation for Mockito's code generation for more information. -// ignore: must_be_immutable class MockWebStorage extends _i1.Mock implements _i2.WebStorage { MockWebStorage() { _i1.throwOnMissingStub(this); @@ -159,7 +157,6 @@ class MockWebStorage extends _i1.Mock implements _i2.WebStorage { /// A class which mocks [WebView]. /// /// See the documentation for Mockito's code generation for more information. -// ignore: must_be_immutable class MockWebView extends _i1.Mock implements _i2.WebView { MockWebView() { _i1.throwOnMissingStub(this); From 5626f1fa49894f636f2cd55446c902cab9d47c02 Mon Sep 17 00:00:00 2001 From: Piotr Mitkowski Date: Tue, 2 Aug 2022 20:46:05 +0200 Subject: [PATCH 566/844] [image_picker] Add missing exports for multi image picking types (#6155) --- .../image_picker/image_picker_platform_interface/CHANGELOG.md | 4 ++++ .../lib/src/method_channel/method_channel_image_picker.dart | 1 - .../lib/src/platform_interface/image_picker_platform.dart | 1 - .../image_picker_platform_interface/lib/src/types/types.dart | 2 ++ .../image_picker/image_picker_platform_interface/pubspec.yaml | 2 +- .../test/new_method_channel_image_picker_test.dart | 3 --- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md index 120b7b00ed15..fda959a21579 100644 --- a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md +++ b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.6.1 + +* Exports new types added for `getMultiImageWithOptions` in 2.6.0. + ## 2.6.0 * Deprecates `getMultiImage` in favor of a new method `getMultiImageWithOptions`. diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart index d215fa2684ee..80d33807b70d 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart @@ -8,7 +8,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; -import 'package:image_picker_platform_interface/src/types/multi_image_picker_options.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/image_picker'); diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart index a2618d5b419c..f704025f581b 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'package:cross_file/cross_file.dart'; import 'package:image_picker_platform_interface/src/method_channel/method_channel_image_picker.dart'; -import 'package:image_picker_platform_interface/src/types/multi_image_picker_options.dart'; import 'package:image_picker_platform_interface/src/types/types.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart index dad86c5d1ba1..fbe12e8e825a 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/types.dart @@ -3,9 +3,11 @@ // found in the LICENSE file. export 'camera_device.dart'; +export 'image_options.dart'; export 'image_picker_options.dart'; export 'image_source.dart'; export 'lost_data_response.dart'; +export 'multi_image_picker_options.dart'; export 'picked_file/picked_file.dart'; export 'retrieve_type.dart'; diff --git a/packages/image_picker/image_picker_platform_interface/pubspec.yaml b/packages/image_picker/image_picker_platform_interface/pubspec.yaml index 50d84f81d888..567914316c1a 100644 --- a/packages/image_picker/image_picker_platform_interface/pubspec.yaml +++ b/packages/image_picker/image_picker_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/i 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.6.0 +version: 2.6.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart index 27d7016d8143..1ddddee079b7 100644 --- a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart @@ -4,11 +4,8 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; - import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'package:image_picker_platform_interface/src/method_channel/method_channel_image_picker.dart'; -import 'package:image_picker_platform_interface/src/types/image_options.dart'; -import 'package:image_picker_platform_interface/src/types/multi_image_picker_options.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); From a054dc63995e3abfe59a24004f7d2a0ad5d6d080 Mon Sep 17 00:00:00 2001 From: Phil Quitslund Date: Tue, 2 Aug 2022 16:27:05 -0700 Subject: [PATCH 567/844] [url_launcher] fix noop toString() diagnostics (#6173) --- packages/url_launcher/url_launcher/lib/src/link.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/url_launcher/url_launcher/lib/src/link.dart b/packages/url_launcher/url_launcher/lib/src/link.dart index 8c0c18e820e3..91f7389ff251 100644 --- a/packages/url_launcher/url_launcher/lib/src/link.dart +++ b/packages/url_launcher/url_launcher/lib/src/link.dart @@ -130,7 +130,7 @@ class DefaultLinkDelegate extends StatelessWidget { ); } else { FlutterError.reportError(FlutterErrorDetails( - exception: 'Could not launch link ${url.toString()}', + exception: 'Could not launch link $url', stack: StackTrace.current, library: 'url_launcher', context: ErrorDescription('during launching a link'), From 90e14468b6b10279d2ddc0be4eed3e7d43efcc52 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 2 Aug 2022 20:32:06 -0400 Subject: [PATCH 568/844] Roll Flutter from ab47f01ff173 to ac1aa511ca94 (13 revisions) (#6168) --- .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 4cd2df5e0e55..4d5cdb6b485f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ab47f01ff1738fbdf9ecbe24252837a1638b9e65 +ac1aa511ca94f46c7e80b94dafd521de35e808e5 From e74c42028d399116cc50f94ff1b0c0a729f7c6e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Aug 2022 11:12:23 +0000 Subject: [PATCH 569/844] [local_auth]: Bump fragment from 1.3.2 to 1.5.1 in /packages/local_auth/local_auth_android/android (#6165) --- packages/local_auth/local_auth_android/CHANGELOG.md | 4 ++++ packages/local_auth/local_auth_android/android/build.gradle | 2 +- packages/local_auth/local_auth_android/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index f6d9da2c504c..6a308ee95285 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.9 + +* Updates androidx.fragment version to 1.5.1. + ## 1.0.8 * Removes usages of `FingerprintManager` and other `BiometricManager` deprecated method usages. diff --git a/packages/local_auth/local_auth_android/android/build.gradle b/packages/local_auth/local_auth_android/android/build.gradle index eb9be4ab5f4b..8d2f40245f2d 100644 --- a/packages/local_auth/local_auth_android/android/build.gradle +++ b/packages/local_auth/local_auth_android/android/build.gradle @@ -51,7 +51,7 @@ android { dependencies { api "androidx.core:core:1.8.0" api "androidx.biometric:biometric:1.1.0" - api "androidx.fragment:fragment:1.3.2" + api "androidx.fragment:fragment:1.5.1" testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:4.6.1' androidTestImplementation 'androidx.test:runner:1.2.0' diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index cfa4970e82d6..b36b92ed3c23 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.8 +version: 1.0.9 environment: sdk: ">=2.14.0 <3.0.0" From 1292de1fadd7d1a4fe5f911c5afde10e6dc38a31 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 3 Aug 2022 11:54:06 -0400 Subject: [PATCH 570/844] Roll Flutter from ac1aa511ca94 to 183d3e410102 (46 revisions) (#6177) --- .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 4d5cdb6b485f..e677ec80ecb8 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ac1aa511ca94f46c7e80b94dafd521de35e808e5 +183d3e410102f4347eedce60950379d86991323a From 5c5d7ae2da0eff29815ae19d0ca3cbd0f5dc48bf Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 3 Aug 2022 15:52:05 -0400 Subject: [PATCH 571/844] [various] Remove `pedantic` (#6179) --- packages/espresso/example/pubspec.yaml | 1 - packages/espresso/pubspec.yaml | 1 - packages/file_selector/file_selector/pubspec.yaml | 1 - .../file_selector/file_selector_platform_interface/pubspec.yaml | 1 - packages/file_selector/file_selector_web/pubspec.yaml | 1 - packages/flutter_plugin_android_lifecycle/example/pubspec.yaml | 1 - packages/flutter_plugin_android_lifecycle/pubspec.yaml | 1 - packages/google_maps_flutter/google_maps_flutter/pubspec.yaml | 2 -- .../google_maps_flutter_platform_interface/pubspec.yaml | 1 - .../in_app_purchase_platform_interface/pubspec.yaml | 1 - packages/ios_platform_images/pubspec.yaml | 1 - packages/local_auth/local_auth/example/pubspec.yaml | 1 - packages/path_provider/path_provider_macos/example/pubspec.yaml | 1 - packages/plugin_platform_interface/pubspec.yaml | 1 - packages/quick_actions/quick_actions/example/pubspec.yaml | 1 - .../quick_actions/quick_actions_platform_interface/pubspec.yaml | 1 - .../shared_preferences/shared_preferences/example/pubspec.yaml | 1 - packages/shared_preferences/shared_preferences/pubspec.yaml | 1 - .../shared_preferences_android/example/pubspec.yaml | 1 - .../shared_preferences_ios/example/pubspec.yaml | 1 - .../shared_preferences_linux/example/pubspec.yaml | 1 - .../shared_preferences_macos/example/pubspec.yaml | 1 - .../shared_preferences_platform_interface/pubspec.yaml | 1 - packages/shared_preferences/shared_preferences_web/pubspec.yaml | 1 - .../shared_preferences_windows/example/pubspec.yaml | 1 - packages/webview_flutter/webview_flutter/example/pubspec.yaml | 1 - packages/webview_flutter/webview_flutter/pubspec.yaml | 1 - .../webview_flutter_android/example/pubspec.yaml | 1 - packages/webview_flutter/webview_flutter_android/pubspec.yaml | 1 - .../webview_flutter_platform_interface/pubspec.yaml | 1 - .../webview_flutter/webview_flutter_web/example/pubspec.yaml | 1 - .../webview_flutter_wkwebview/example/pubspec.yaml | 1 - packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml | 1 - 33 files changed, 34 deletions(-) diff --git a/packages/espresso/example/pubspec.yaml b/packages/espresso/example/pubspec.yaml index c896585be839..1e668b518aa0 100644 --- a/packages/espresso/example/pubspec.yaml +++ b/packages/espresso/example/pubspec.yaml @@ -22,7 +22,6 @@ dev_dependencies: sdk: flutter flutter_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/espresso/pubspec.yaml b/packages/espresso/pubspec.yaml index 644d0ab5980a..724d5fed4c33 100644 --- a/packages/espresso/pubspec.yaml +++ b/packages/espresso/pubspec.yaml @@ -23,4 +23,3 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index 51eb0a478ee8..627a5921b163 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -30,6 +30,5 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 plugin_platform_interface: ^2.0.0 test: ^1.16.3 diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml index 4ba84782d436..405864414e9a 100644 --- a/packages/file_selector/file_selector_platform_interface/pubspec.yaml +++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml @@ -20,5 +20,4 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 test: ^1.16.3 diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index a764cae0245f..fc9e9e217b62 100644 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -26,4 +26,3 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/flutter_plugin_android_lifecycle/example/pubspec.yaml b/packages/flutter_plugin_android_lifecycle/example/pubspec.yaml index 0c88cd2c5531..e732497eee95 100644 --- a/packages/flutter_plugin_android_lifecycle/example/pubspec.yaml +++ b/packages/flutter_plugin_android_lifecycle/example/pubspec.yaml @@ -21,7 +21,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.8.0 flutter: uses-material-design: true diff --git a/packages/flutter_plugin_android_lifecycle/pubspec.yaml b/packages/flutter_plugin_android_lifecycle/pubspec.yaml index df8930dd9442..155d6faea5d0 100644 --- a/packages/flutter_plugin_android_lifecycle/pubspec.yaml +++ b/packages/flutter_plugin_android_lifecycle/pubspec.yaml @@ -22,4 +22,3 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.8.0 diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index 917375e33eaa..b0cff92b2b64 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -26,7 +26,5 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - - pedantic: ^1.10.0 plugin_platform_interface: ^2.0.0 stream_transform: ^2.0.0 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 c421d13c6e64..f1463c71230a 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 @@ -22,4 +22,3 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.0.0 - pedantic: ^1.10.0 diff --git a/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml index 98698b80718c..ee8d29461d82 100644 --- a/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml @@ -19,4 +19,3 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.0.0 - pedantic: ^1.10.0 diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index c4397c551c27..64875ecad88b 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -21,4 +21,3 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/local_auth/local_auth/example/pubspec.yaml b/packages/local_auth/local_auth/example/pubspec.yaml index 305005b34364..3819cd1c0f15 100644 --- a/packages/local_auth/local_auth/example/pubspec.yaml +++ b/packages/local_auth/local_auth/example/pubspec.yaml @@ -23,7 +23,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/path_provider/path_provider_macos/example/pubspec.yaml b/packages/path_provider/path_provider_macos/example/pubspec.yaml index 42ed28b818d6..65ecf3ab0423 100644 --- a/packages/path_provider/path_provider_macos/example/pubspec.yaml +++ b/packages/path_provider/path_provider_macos/example/pubspec.yaml @@ -23,7 +23,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.8.0 flutter: uses-material-design: true diff --git a/packages/plugin_platform_interface/pubspec.yaml b/packages/plugin_platform_interface/pubspec.yaml index 1b601db1a388..f4800d444e66 100644 --- a/packages/plugin_platform_interface/pubspec.yaml +++ b/packages/plugin_platform_interface/pubspec.yaml @@ -25,5 +25,4 @@ dependencies: dev_dependencies: mockito: ^5.0.0 - pedantic: ^1.10.0 test: ^1.16.0 diff --git a/packages/quick_actions/quick_actions/example/pubspec.yaml b/packages/quick_actions/quick_actions/example/pubspec.yaml index 64e61b71e720..79ab57cd4fc8 100644 --- a/packages/quick_actions/quick_actions/example/pubspec.yaml +++ b/packages/quick_actions/quick_actions/example/pubspec.yaml @@ -23,7 +23,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml index c465b2aaf99b..5303f8b90958 100644 --- a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml +++ b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml @@ -19,4 +19,3 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.0.1 - pedantic: ^1.11.0 diff --git a/packages/shared_preferences/shared_preferences/example/pubspec.yaml b/packages/shared_preferences/shared_preferences/example/pubspec.yaml index 4ec5cbbb471f..fd22f13172f8 100644 --- a/packages/shared_preferences/shared_preferences/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/example/pubspec.yaml @@ -22,7 +22,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index a1cea06f5a04..d5a9109834ea 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -43,4 +43,3 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml index d23270ba386f..1ea27cb381be 100644 --- a/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml @@ -23,7 +23,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml index 9f5f7124669d..fb7b1e7316f5 100644 --- a/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml @@ -23,7 +23,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml index 4d44d4e69f93..1b3772f0eeea 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml @@ -22,7 +22,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml index b9dfb75c92e7..64d13fe31a5f 100644 --- a/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml @@ -23,7 +23,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml b/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml index 43669d624f2d..9dce94c616c4 100644 --- a/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml @@ -15,4 +15,3 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/shared_preferences/shared_preferences_web/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/pubspec.yaml index c50958363d16..cfe712999910 100644 --- a/packages/shared_preferences/shared_preferences_web/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/pubspec.yaml @@ -26,4 +26,3 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pedantic: ^1.10.0 diff --git a/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml index c7a0eb82cc07..7bca1fb4ce45 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml @@ -22,7 +22,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/webview_flutter/webview_flutter/example/pubspec.yaml b/packages/webview_flutter/webview_flutter/example/pubspec.yaml index f1da7cd17b7e..7ff55bccf432 100644 --- a/packages/webview_flutter/webview_flutter/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/example/pubspec.yaml @@ -26,7 +26,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index fc1e50f16e24..5c9363e42acc 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -30,4 +30,3 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.0.16 - pedantic: ^1.10.0 diff --git a/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml index b457ca6b860a..db72a146c110 100644 --- a/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml @@ -27,7 +27,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index a60c4f9cc313..9a7caabb3fe3 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -28,5 +28,4 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.1.0 - pedantic: ^1.10.0 pigeon: ^3.0.3 diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml index c14df52371a8..2c1fb0975866 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml @@ -21,4 +21,3 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.0.0 - pedantic: ^1.10.0 diff --git a/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml index a98df799e92c..fbeb44923d83 100644 --- a/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml @@ -26,7 +26,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml index b8c2464eb051..1daa96854f90 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml @@ -27,7 +27,6 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - pedantic: ^1.10.0 flutter: uses-material-design: true diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index c7f5ffb7b478..9837519810b1 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -28,5 +28,4 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.1.0 - pedantic: ^1.10.0 pigeon: ^3.0.3 From 43110c268c6ec71b98b091e889c127e769606247 Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Wed, 3 Aug 2022 12:55:33 -0700 Subject: [PATCH 572/844] [in_app_purchase] Fix incorrect json key in `queryPurchasesAsync` response (#6174) --- .../in_app_purchase_android/CHANGELOG.md | 4 ++ .../inapppurchase/MethodCallHandlerImpl.java | 2 +- .../inapppurchase/MethodCallHandlerTest.java | 60 +++++++++++++++++++ .../in_app_purchase_android/pubspec.yaml | 2 +- 4 files changed, 66 insertions(+), 2 deletions(-) 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 6f476436b3d6..c369b8f4d199 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.3+2 + +* Fixes incorrect json key in `queryPurchasesAsync` that fixes restore purchases functionality. + ## 0.2.3+1 * Updates `json_serializable` to fix warnings in generated code. 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 ab12b2db8630..bdf52dc40e55 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 @@ -320,7 +320,7 @@ public void onQueryPurchasesResponse( // as success is implied by calling this callback. serialized.put("responseCode", BillingClient.BillingResponseCode.OK); serialized.put("billingResult", Translator.fromBillingResult(billingResult)); - serialized.put("purchaseList", fromPurchasesList(purchasesList)); + serialized.put("purchasesList", fromPurchasesList(purchasesList)); result.success(serialized); } }); 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 e99ff46dd2cc..ffebb2544a13 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 @@ -31,6 +31,7 @@ import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.refEq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -56,7 +57,9 @@ import com.android.billingclient.api.Purchase; import com.android.billingclient.api.PurchaseHistoryRecord; import com.android.billingclient.api.PurchaseHistoryResponseListener; +import com.android.billingclient.api.PurchasesResponseListener; import com.android.billingclient.api.QueryPurchaseHistoryParams; +import com.android.billingclient.api.QueryPurchasesParams; import com.android.billingclient.api.SkuDetails; import com.android.billingclient.api.SkuDetailsParams; import com.android.billingclient.api.SkuDetailsResponseListener; @@ -64,9 +67,12 @@ import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.Result; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import org.json.JSONException; import org.junit.Before; import org.junit.Test; @@ -74,6 +80,8 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; public class MethodCallHandlerTest { private MethodCallHandlerImpl methodChannelHandler; @@ -592,6 +600,58 @@ public void queryPurchases_clientDisconnected() { verify(result, never()).success(any()); } + @Test + public void queryPurchases_returns_success() throws Exception { + establishConnectedBillingClient(null, null); + + CountDownLatch lock = new CountDownLatch(1); + doAnswer( + new Answer() { + public Object answer(InvocationOnMock invocation) { + lock.countDown(); + return null; + } + }) + .when(result) + .success(any(HashMap.class)); + + ArgumentCaptor purchasesResponseListenerArgumentCaptor = + ArgumentCaptor.forClass(PurchasesResponseListener.class); + doAnswer( + new Answer() { + public Object answer(InvocationOnMock invocation) { + BillingResult.Builder resultBuilder = + BillingResult.newBuilder() + .setResponseCode(BillingClient.BillingResponseCode.OK) + .setDebugMessage("hello message"); + purchasesResponseListenerArgumentCaptor + .getValue() + .onQueryPurchasesResponse(resultBuilder.build(), new ArrayList()); + return null; + } + }) + .when(mockBillingClient) + .queryPurchasesAsync( + any(QueryPurchasesParams.class), purchasesResponseListenerArgumentCaptor.capture()); + + HashMap arguments = new HashMap<>(); + arguments.put("skuType", SkuType.INAPP); + methodChannelHandler.onMethodCall(new MethodCall(QUERY_PURCHASES_ASYNC, arguments), result); + + lock.await(5000, TimeUnit.MILLISECONDS); + + verify(result, never()).error(any(), any(), any()); + + ArgumentCaptor hashMapCaptor = ArgumentCaptor.forClass(HashMap.class); + verify(result, times(1)).success(hashMapCaptor.capture()); + + HashMap map = hashMapCaptor.getValue(); + assert (map.containsKey("responseCode")); + assert (map.containsKey("billingResult")); + assert (map.containsKey("purchasesList")); + assert ((int) map.get("responseCode") == 0); + } + @Test public void queryPurchaseHistoryAsync() { // Set up an established billing client and all our mocked responses 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 84bd36a8096b..3b334a6848ed 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/plugins/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.2.3+1 +version: 0.2.3+2 environment: sdk: ">=2.14.0 <3.0.0" From edc6c0eb747fe9b8063aed896cb2d9790b2878fb Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 3 Aug 2022 18:08:05 -0400 Subject: [PATCH 573/844] [google_maps_flutter] Federate mobile implementations (#6171) --- .github/dependabot.yml | 12 +- CODEOWNERS | 4 +- .../google_maps_flutter/CHANGELOG.md | 4 + .../android/settings.gradle | 1 - .../integration_test/google_maps_test.dart | 14 +- .../google_maps_flutter/example/ios/Podfile | 8 - .../lib/google_maps_flutter.dart | 1 + .../lib/src/google_map.dart | 7 +- .../google_maps_flutter/pubspec.yaml | 14 +- .../test/google_map_test.dart | 35 - .../google_maps_flutter_android/AUTHORS | 67 + .../google_maps_flutter_android/CHANGELOG.md | 4 + .../google_maps_flutter_android/LICENSE | 25 + .../google_maps_flutter_android/README.md | 12 + .../android/build.gradle | 0 .../android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 0 .../plugins/googlemaps/CircleBuilder.java | 0 .../plugins/googlemaps/CircleController.java | 0 .../plugins/googlemaps/CircleOptionsSink.java | 0 .../plugins/googlemaps/CirclesController.java | 0 .../flutter/plugins/googlemaps/Convert.java | 0 .../plugins/googlemaps/GoogleMapBuilder.java | 0 .../googlemaps/GoogleMapController.java | 3 +- .../plugins/googlemaps/GoogleMapFactory.java | 0 .../plugins/googlemaps/GoogleMapListener.java | 0 .../googlemaps/GoogleMapOptionsSink.java | 0 .../plugins/googlemaps/GoogleMapsPlugin.java | 2 +- .../plugins/googlemaps/LifecycleProvider.java | 0 .../plugins/googlemaps/MarkerBuilder.java | 0 .../plugins/googlemaps/MarkerController.java | 0 .../plugins/googlemaps/MarkerOptionsSink.java | 0 .../plugins/googlemaps/MarkersController.java | 0 .../plugins/googlemaps/PolygonBuilder.java | 0 .../plugins/googlemaps/PolygonController.java | 0 .../googlemaps/PolygonOptionsSink.java | 0 .../googlemaps/PolygonsController.java | 0 .../plugins/googlemaps/PolylineBuilder.java | 0 .../googlemaps/PolylineController.java | 0 .../googlemaps/PolylineOptionsSink.java | 0 .../googlemaps/PolylinesController.java | 0 .../googlemaps/TileOverlayBuilder.java | 0 .../googlemaps/TileOverlayController.java | 0 .../plugins/googlemaps/TileOverlaySink.java | 0 .../googlemaps/TileOverlaysController.java | 0 .../googlemaps/TileProviderController.java | 0 .../plugins/googlemaps/CircleBuilderTest.java | 0 .../googlemaps/CircleControllerTest.java | 0 .../googlemaps/GoogleMapControllerTest.java | 0 .../googlemaps/MarkersControllerTest.java | 0 .../googlemaps/PolygonBuilderTest.java | 0 .../googlemaps/PolygonControllerTest.java | 0 .../googlemaps/PolylineBuilderTest.java | 0 .../googlemaps/PolylineControllerTest.java | 0 .../org.mockito.plugins.MockMaker | 0 .../example/.metadata | 8 + .../example/README.md | 3 + .../example/android/app/build.gradle | 73 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../flutter/plugins/DartIntegrationTest.java | 14 + .../googlemapsexample/GoogleMapsTest.java | 0 .../googlemapsexample/MainActivityTest.java | 19 + .../android/app/src/debug/AndroidManifest.xml | 17 + .../GoogleMapsTestActivity.java | 20 + .../android/app/src/main/AndroidManifest.xml | 30 + .../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/styles.xml | 8 + .../example/android/build.gradle | 29 + .../example/android/gradle.properties | 5 + .../gradle/wrapper/gradle-wrapper.properties | 6 + .../example/android/settings.gradle | 15 + .../example/assets/2.0x/red_square.png | Bin 0 -> 304 bytes .../example/assets/3.0x/red_square.png | Bin 0 -> 312 bytes .../example/assets/night_mode.json | 162 +++ .../example/assets/red_square.png | Bin 0 -> 195 bytes .../integration_test/google_maps_test.dart | 1189 +++++++++++++++++ .../example/lib/animate_camera.dart | 171 +++ .../example/lib/example_google_map.dart | 538 ++++++++ .../example/lib/lite_mode.dart | 47 + .../example/lib/main.dart | 78 ++ .../example/lib/map_click.dart | 106 ++ .../example/lib/map_coordinates.dart | 100 ++ .../example/lib/map_ui.dart | 358 +++++ .../example/lib/marker_icons.dart | 98 ++ .../example/lib/move_camera.dart | 171 +++ .../example/lib/padding.dart | 181 +++ .../example/lib/page.dart | 15 + .../example/lib/place_circle.dart | 232 ++++ .../example/lib/place_marker.dart | 422 ++++++ .../example/lib/place_polygon.dart | 306 +++++ .../example/lib/place_polyline.dart | 325 +++++ .../example/lib/scrolling_map.dart | 114 ++ .../example/lib/snapshot.dart | 76 ++ .../example/lib/tile_overlay.dart | 156 +++ .../example/pubspec.yaml | 33 + .../example/test_driver/integration_test.dart | 7 + .../lib/google_maps_flutter_android.dart | 5 + .../src/google_map_inspector_android.dart} | 57 +- .../lib/src/google_maps_flutter_android.dart | 696 ++++++++++ .../google_maps_flutter_android/pubspec.yaml | 31 + .../google_maps_flutter_android_test.dart | 153 +++ .../google_maps_flutter_ios/AUTHORS | 67 + .../google_maps_flutter_ios/CHANGELOG.md | 4 + .../google_maps_flutter_ios/LICENSE | 25 + .../google_maps_flutter_ios/README.md | 12 + .../google_maps_flutter_ios/example/.metadata | 8 + .../google_maps_flutter_ios/example/README.md | 3 + .../example/assets/2.0x/red_square.png | Bin 0 -> 304 bytes .../example/assets/3.0x/red_square.png | Bin 0 -> 312 bytes .../example/assets/night_mode.json | 162 +++ .../example/assets/red_square.png | Bin 0 -> 195 bytes .../integration_test/google_maps_test.dart | 1095 +++++++++++++++ .../ios/Flutter/AppFrameworkInfo.plist | 30 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + .../example/ios/Podfile | 49 + .../ios/Runner.xcodeproj/project.pbxproj | 789 +++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/xcschemes/Runner.xcscheme | 107 ++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/ios/Runner/AppDelegate.h | 9 + .../example/ios/Runner/AppDelegate.m | 27 + .../AppIcon.appiconset/Contents.json | 122 ++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 11112 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 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 .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../example/ios/Runner/Info.plist | 51 + .../example/ios/Runner/main.m | 13 + ...TGoogleMapJSONConversionsConversionTests.m | 4 +- .../example/ios/RunnerTests/GoogleMapsTests.m | 4 +- .../example/ios/RunnerTests/Info.plist | 0 .../ios/RunnerTests/PartiallyMockedMapView.h | 0 .../ios/RunnerTests/PartiallyMockedMapView.m | 0 .../ios/RunnerUITests/GoogleMapsUITests.m | 0 .../example/ios/RunnerUITests/Info.plist | 0 .../example/lib/animate_camera.dart | 171 +++ .../example/lib/example_google_map.dart | 538 ++++++++ .../example/lib/lite_mode.dart | 47 + .../example/lib/main.dart | 73 + .../example/lib/map_click.dart | 106 ++ .../example/lib/map_coordinates.dart | 100 ++ .../example/lib/map_ui.dart | 358 +++++ .../example/lib/marker_icons.dart | 98 ++ .../example/lib/move_camera.dart | 171 +++ .../example/lib/padding.dart | 181 +++ .../example/lib/page.dart | 15 + .../example/lib/place_circle.dart | 232 ++++ .../example/lib/place_marker.dart | 422 ++++++ .../example/lib/place_polygon.dart | 306 +++++ .../example/lib/place_polyline.dart | 325 +++++ .../example/lib/scrolling_map.dart | 114 ++ .../example/lib/snapshot.dart | 76 ++ .../example/lib/tile_overlay.dart | 156 +++ .../example/pubspec.yaml | 31 + .../example/test_driver/integration_test.dart | 7 + .../ios/Assets/.gitkeep | 0 .../ios/Classes/FLTGoogleMapJSONConversions.h | 0 .../ios/Classes/FLTGoogleMapJSONConversions.m | 0 .../FLTGoogleMapTileOverlayController.h | 0 .../FLTGoogleMapTileOverlayController.m | 0 .../ios/Classes/FLTGoogleMapsPlugin.h | 0 .../ios/Classes/FLTGoogleMapsPlugin.m | 2 +- .../ios/Classes/GoogleMapCircleController.h | 0 .../ios/Classes/GoogleMapCircleController.m | 0 .../ios/Classes/GoogleMapController.h | 0 .../ios/Classes/GoogleMapController.m | 2 +- .../ios/Classes/GoogleMapController_Test.h | 0 .../ios/Classes/GoogleMapMarkerController.h | 0 .../ios/Classes/GoogleMapMarkerController.m | 0 .../ios/Classes/GoogleMapPolygonController.h | 0 .../ios/Classes/GoogleMapPolygonController.m | 0 .../ios/Classes/GoogleMapPolylineController.h | 0 .../ios/Classes/GoogleMapPolylineController.m | 0 .../google_maps_flutter_ios-umbrella.h} | 6 +- .../google_maps_flutter_ios.modulemap} | 4 +- .../ios/google_maps_flutter_ios.podspec} | 8 +- .../lib/google_maps_flutter_ios.dart | 5 + .../lib/src/google_map_inspector_ios.dart | 113 ++ .../lib/src/google_maps_flutter_ios.dart | 644 +++++++++ .../google_maps_flutter_ios/pubspec.yaml | 29 + .../test/google_maps_flutter_ios_test.dart | 124 ++ 206 files changed, 13351 insertions(+), 119 deletions(-) delete mode 100644 packages/google_maps_flutter/google_maps_flutter/android/settings.gradle create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/AUTHORS create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/LICENSE create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/README.md rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/build.gradle (100%) create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/android/settings.gradle rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/AndroidManifest.xml (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/CircleBuilder.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/CircleController.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/CircleOptionsSink.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/CirclesController.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/Convert.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapBuilder.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java (99%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapFactory.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapListener.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapOptionsSink.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapsPlugin.java (98%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/LifecycleProvider.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/MarkerBuilder.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/MarkerController.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/MarkerOptionsSink.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/MarkersController.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/PolygonBuilder.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/PolygonController.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/PolygonOptionsSink.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/PolygonsController.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/PolylineBuilder.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/PolylineController.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/PolylineOptionsSink.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/PolylinesController.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlayBuilder.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlayController.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaySink.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaysController.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/main/java/io/flutter/plugins/googlemaps/TileProviderController.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/test/java/io/flutter/plugins/googlemaps/CircleBuilderTest.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/test/java/io/flutter/plugins/googlemaps/MarkersControllerTest.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/test/java/io/flutter/plugins/googlemaps/PolygonBuilderTest.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/test/java/io/flutter/plugins/googlemaps/PolylineBuilderTest.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker (100%) create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/.metadata create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/README.md create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/android/app/build.gradle create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_android}/example/android/app/src/androidTest/java/io/flutter/plugins/googlemapsexample/GoogleMapsTest.java (100%) create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlemapsexample/MainActivityTest.java create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/debug/AndroidManifest.xml create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/debug/java/io/flutter/plugins/googlemapsexample/GoogleMapsTestActivity.java create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/AndroidManifest.xml create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/drawable/launch_background.xml create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/values/styles.xml create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/android/build.gradle create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle.properties create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/android/settings.gradle create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/assets/2.0x/red_square.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/assets/3.0x/red_square.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/assets/night_mode.json create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/assets/red_square.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/lib/animate_camera.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/lib/example_google_map.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/lib/lite_mode.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/lib/main.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_click.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_coordinates.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_ui.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/lib/marker_icons.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/lib/move_camera.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/lib/padding.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/lib/page.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_circle.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_marker.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_polygon.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_polyline.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/lib/scrolling_map.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/lib/snapshot.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/lib/tile_overlay.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/pubspec.yaml create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/test_driver/integration_test.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/lib/google_maps_flutter_android.dart rename packages/google_maps_flutter/{google_maps_flutter/example/integration_test/google_map_inspector.dart => google_maps_flutter_android/lib/src/google_map_inspector_android.dart} (67%) create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_maps_flutter_android.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/AUTHORS create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/LICENSE create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/README.md create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/.metadata create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/README.md create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/assets/2.0x/red_square.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/assets/3.0x/red_square.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/assets/night_mode.json create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/assets/red_square.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/integration_test/google_maps_test.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Flutter/Debug.xcconfig create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Flutter/Release.xcconfig create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Podfile create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/AppDelegate.h create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/AppDelegate.m create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Info.plist create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/main.m rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/example/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m (99%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/example/ios/RunnerTests/GoogleMapsTests.m (94%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/example/ios/RunnerTests/Info.plist (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/example/ios/RunnerTests/PartiallyMockedMapView.h (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/example/ios/RunnerTests/PartiallyMockedMapView.m (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/example/ios/RunnerUITests/GoogleMapsUITests.m (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/example/ios/RunnerUITests/Info.plist (100%) create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/lib/animate_camera.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/lib/example_google_map.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/lib/lite_mode.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/lib/main.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_click.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_coordinates.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_ui.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/lib/marker_icons.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/lib/move_camera.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/lib/padding.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/lib/page.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_circle.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_marker.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_polygon.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_polyline.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/lib/scrolling_map.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/lib/snapshot.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/lib/tile_overlay.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/pubspec.yaml create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/test_driver/integration_test.dart rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/ios/Assets/.gitkeep (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/ios/Classes/FLTGoogleMapJSONConversions.h (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/ios/Classes/FLTGoogleMapJSONConversions.m (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/ios/Classes/FLTGoogleMapTileOverlayController.h (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/ios/Classes/FLTGoogleMapTileOverlayController.m (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/ios/Classes/FLTGoogleMapsPlugin.h (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/ios/Classes/FLTGoogleMapsPlugin.m (89%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/ios/Classes/GoogleMapCircleController.h (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/ios/Classes/GoogleMapCircleController.m (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/ios/Classes/GoogleMapController.h (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/ios/Classes/GoogleMapController.m (99%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/ios/Classes/GoogleMapController_Test.h (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/ios/Classes/GoogleMapMarkerController.h (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/ios/Classes/GoogleMapMarkerController.m (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/ios/Classes/GoogleMapPolygonController.h (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/ios/Classes/GoogleMapPolygonController.m (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/ios/Classes/GoogleMapPolylineController.h (100%) rename packages/google_maps_flutter/{google_maps_flutter => google_maps_flutter_ios}/ios/Classes/GoogleMapPolylineController.m (100%) rename packages/google_maps_flutter/{google_maps_flutter/ios/Classes/google_maps_flutter-umbrella.h => google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h} (63%) rename packages/google_maps_flutter/{google_maps_flutter/ios/Classes/google_maps_flutter.modulemap => google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios.modulemap} (52%) rename packages/google_maps_flutter/{google_maps_flutter/ios/google_maps_flutter.podspec => google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec} (89%) create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/lib/google_maps_flutter_ios.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_map_inspector_ios.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 33094a27e9a4..d1b3525c8ed0 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -57,7 +57,7 @@ updates: open-pull-requests-limit: 10 - package-ecosystem: "gradle" - directory: "/packages/google_maps_flutter/google_maps_flutter/android" + directory: "/packages/google_maps_flutter/google_maps_flutter/example/android/app" commit-message: prefix: "[google_maps]" schedule: @@ -65,7 +65,15 @@ updates: open-pull-requests-limit: 10 - package-ecosystem: "gradle" - directory: "/packages/google_maps_flutter/google_maps_flutter/example/android/app" + directory: "/packages/google_maps_flutter/google_maps_flutter_android/android" + commit-message: + prefix: "[google_maps]" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/google_maps_flutter/google_maps_flutter_android/example/android/app" commit-message: prefix: "[google_maps]" schedule: diff --git a/CODEOWNERS b/CODEOWNERS index 9efe4a55e037..db8187d7d3f3 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -30,7 +30,7 @@ packages/**/*_web/** @ditman packages/camera/camera_android/** @camsim99 packages/espresso/** @GaryQian packages/flutter_plugin_android_lifecycle/** @GaryQian -packages/google_maps_flutter/google_maps_flutter/android/** @GaryQian +packages/google_maps_flutter/google_maps_flutter_android/** @GaryQian packages/google_sign_in/google_sign_in_android/** @camsim99 packages/image_picker/image_picker_android/** @GaryQian packages/in_app_purchase/in_app_purchase_android/** @GaryQian @@ -42,7 +42,7 @@ packages/video_player/video_player_android/** @camsim99 # - iOS packages/camera/camera_avfoundation/** @hellohuanlin -packages/google_maps_flutter/google_maps_flutter/ios/** @cyanglaz +packages/google_maps_flutter/google_maps_flutter_ios/** @cyanglaz packages/google_sign_in/google_sign_in_ios/** @jmagman packages/image_picker/image_picker_ios/** @cyanglaz packages/in_app_purchase/in_app_purchase_storekit/** @cyanglaz diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 9d7d542a8f83..86cefdd2f594 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Moves Android and iOS implementations to federated packages. + ## 2.1.10 * Avoids map shift when scrolling on iOS. diff --git a/packages/google_maps_flutter/google_maps_flutter/android/settings.gradle b/packages/google_maps_flutter/google_maps_flutter/android/settings.gradle deleted file mode 100644 index dbceadf4c145..000000000000 --- a/packages/google_maps_flutter/google_maps_flutter/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'google_maps_flutter' diff --git a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart index d82a58227d8a..1d7494c5fe68 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart @@ -13,8 +13,6 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:integration_test/integration_test.dart'; -import 'google_map_inspector.dart'; - const LatLng _kInitialMapCenter = LatLng(0, 0); const double _kInitialZoomLevel = 5; const CameraPosition _kInitialCameraPosition = @@ -22,16 +20,7 @@ const CameraPosition _kInitialCameraPosition = void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - // TODO(stuartmorgan): Remove this once mobile implementations are federated - // and registering their own inpector implementations, and just call - // enableDebugInspection. - if (GoogleMapsFlutterPlatform.instance is MethodChannelGoogleMapsFlutter) { - GoogleMapsInspectorPlatform.instance = MethodChannelGoogleMapsInspector( - GoogleMapsFlutterPlatform.instance as MethodChannelGoogleMapsFlutter); - } else { - GoogleMapsFlutterPlatform.instance.enableDebugInspection(); - } + GoogleMapsFlutterPlatform.instance.enableDebugInspection(); testWidgets('testCompassToggle', (WidgetTester tester) async { final Key key = GlobalKey(); @@ -474,7 +463,6 @@ void main() { .round()); } await tester.binding.setSurfaceSize(null); - AndroidGoogleMapsFlutter.useAndroidViewSurface = false; }); testWidgets('testGetVisibleRegion', (WidgetTester tester) async { diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile b/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile index 14b4bdc51c96..8df8fef0a781 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile +++ b/packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile @@ -29,14 +29,6 @@ flutter_ios_podfile_setup target 'Runner' do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) - target 'RunnerTests' do - inherit! :search_paths - - pod 'OCMock', '~> 3.9.1' - end - target 'RunnerUITests' do - inherit! :search_paths - end end post_install do |installer| diff --git a/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart b/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart index 751930aece67..a4be120b2117 100644 --- a/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart +++ b/packages/google_maps_flutter/google_maps_flutter/lib/google_maps_flutter.dart @@ -13,6 +13,7 @@ import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_android/google_maps_flutter_android.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; export 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart' 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 b76d103a99f4..e037d7e464fd 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 @@ -40,6 +40,9 @@ class UnknownMapObjectIdError extends Error { } /// Android specific settings for [GoogleMap]. +// TODO(stuartmorgan): Deprecate this in favor of pointing people who want to +// change this to using the Android implementation Dart class directly. This +// should be done as part of switching the default to hybrid composition. class AndroidGoogleMapsFlutter { AndroidGoogleMapsFlutter._(); @@ -55,7 +58,7 @@ class AndroidGoogleMapsFlutter { static bool get useAndroidViewSurface { final GoogleMapsFlutterPlatform platform = GoogleMapsFlutterPlatform.instance; - if (platform is MethodChannelGoogleMapsFlutter) { + if (platform is GoogleMapsFlutterAndroid) { return platform.useAndroidViewSurface; } return false; @@ -73,7 +76,7 @@ class AndroidGoogleMapsFlutter { static set useAndroidViewSurface(bool useAndroidViewSurface) { final GoogleMapsFlutterPlatform platform = GoogleMapsFlutterPlatform.instance; - if (platform is MethodChannelGoogleMapsFlutter) { + if (platform is GoogleMapsFlutterAndroid) { platform.useAndroidViewSurface = useAndroidViewSurface; } } diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index b0cff92b2b64..8d4e49f0ab4a 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -3,6 +3,9 @@ description: A Flutter plugin for integrating Google Maps in iOS and Android app repository: https://github.com/flutter/plugins/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.1.10 +# Temporarily disable publishing to allow moving Android and iOS +# implementations. +publish_to: none environment: sdk: ">=2.14.0 <3.0.0" @@ -12,15 +15,18 @@ flutter: plugin: platforms: android: - package: io.flutter.plugins.googlemaps - pluginClass: GoogleMapsPlugin + default_package: google_maps_flutter_android ios: - pluginClass: FLTGoogleMapsPlugin + default_package: google_maps_flutter_ios dependencies: flutter: sdk: flutter - flutter_plugin_android_lifecycle: ^2.0.1 + # Temporary path dependencies to allow moving Android and iOS implementations. + google_maps_flutter_android: + path: ../google_maps_flutter_android + google_maps_flutter_ios: + path: ../google_maps_flutter_ios google_maps_flutter_platform_interface: ^2.2.1 dev_dependencies: 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 003ae06d9877..2b754afbd359 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 @@ -6,7 +6,6 @@ import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; -import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'fake_maps_controllers.dart'; @@ -588,38 +587,4 @@ void main() { expect(platformGoogleMap.buildingsEnabled, true); }); - - testWidgets( - 'Default Android widget is AndroidView', - (WidgetTester tester) async { - await tester.pumpWidget( - const Directionality( - textDirection: TextDirection.ltr, - child: GoogleMap( - initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)), - ), - ), - ); - - expect(find.byType(AndroidView), findsOneWidget); - }, - ); - - testWidgets('Use PlatformViewLink on Android', (WidgetTester tester) async { - final MethodChannelGoogleMapsFlutter platform = - GoogleMapsFlutterPlatform.instance as MethodChannelGoogleMapsFlutter; - platform.useAndroidViewSurface = true; - - await tester.pumpWidget( - const Directionality( - textDirection: TextDirection.ltr, - child: GoogleMap( - initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)), - ), - ), - ); - - expect(find.byType(PlatformViewLink), findsOneWidget); - platform.useAndroidViewSurface = false; - }); } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/AUTHORS b/packages/google_maps_flutter/google_maps_flutter_android/AUTHORS new file mode 100644 index 000000000000..9f1b53ee2667 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/AUTHORS @@ -0,0 +1,67 @@ +# 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. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> +Taha Tesser diff --git a/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md new file mode 100644 index 000000000000..bd2f99c9446c --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md @@ -0,0 +1,4 @@ +## 2.1.10 + +* Splits Android implementation out of `google_maps_flutter` as a federated + implementation. diff --git a/packages/google_maps_flutter/google_maps_flutter_android/LICENSE b/packages/google_maps_flutter/google_maps_flutter_android/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/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/google_maps_flutter/google_maps_flutter_android/README.md b/packages/google_maps_flutter/google_maps_flutter_android/README.md new file mode 100644 index 000000000000..5ac1df04a4df --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/README.md @@ -0,0 +1,12 @@ +# google\_maps\_flutter\_android + +The Android implementation of [`google_maps_flutter`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use +`google_maps_flutter` normally. This package will be automatically included in +your app when you do. + +[1]: https://pub.dev/packages/google_maps_flutter +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/google_maps_flutter/google_maps_flutter/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/build.gradle rename to packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/settings.gradle b/packages/google_maps_flutter/google_maps_flutter_android/android/settings.gradle new file mode 100644 index 000000000000..d873c7abe92c --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'google_maps_flutter_android' diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/AndroidManifest.xml b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/AndroidManifest.xml similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/AndroidManifest.xml rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/AndroidManifest.xml diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleBuilder.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CircleBuilder.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleBuilder.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CircleBuilder.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CircleController.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleController.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CircleController.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleOptionsSink.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CircleOptionsSink.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CircleOptionsSink.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CircleOptionsSink.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CirclesController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CirclesController.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/CirclesController.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/CirclesController.java diff --git a/packages/google_maps_flutter/google_maps_flutter/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 similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/Convert.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/Convert.java diff --git a/packages/google_maps_flutter/google_maps_flutter/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 similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapBuilder.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapBuilder.java diff --git a/packages/google_maps_flutter/google_maps_flutter/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 similarity index 99% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapController.java index 2c2287cf59d4..66d3e283b8df 100644 --- a/packages/google_maps_flutter/google_maps_flutter/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 @@ -96,7 +96,8 @@ final class GoogleMapController this.options = options; this.mapView = new MapView(context, options); this.density = context.getResources().getDisplayMetrics().density; - methodChannel = new MethodChannel(binaryMessenger, "plugins.flutter.io/google_maps_" + id); + methodChannel = + new MethodChannel(binaryMessenger, "plugins.flutter.dev/google_maps_android_" + id); methodChannel.setMethodCallHandler(this); this.lifecycleProvider = lifecycleProvider; this.markersController = new MarkersController(methodChannel); diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapFactory.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapFactory.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapFactory.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapFactory.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapListener.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapListener.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapListener.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapListener.java diff --git a/packages/google_maps_flutter/google_maps_flutter/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 similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapOptionsSink.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapOptionsSink.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapsPlugin.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapsPlugin.java similarity index 98% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapsPlugin.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapsPlugin.java index 763cd9e3e72e..715b357566da 100644 --- a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapsPlugin.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/GoogleMapsPlugin.java @@ -28,7 +28,7 @@ public class GoogleMapsPlugin implements FlutterPlugin, ActivityAware { @Nullable private Lifecycle lifecycle; - private static final String VIEW_TYPE = "plugins.flutter.io/google_maps"; + private static final String VIEW_TYPE = "plugins.flutter.dev/google_maps_android"; @SuppressWarnings("deprecation") public static void registerWith( diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/LifecycleProvider.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/LifecycleProvider.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/LifecycleProvider.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/LifecycleProvider.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/MarkerBuilder.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkerBuilder.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/MarkerBuilder.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkerBuilder.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/MarkerController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkerController.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/MarkerController.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkerController.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/MarkerOptionsSink.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkerOptionsSink.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/MarkerOptionsSink.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkerOptionsSink.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/MarkersController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkersController.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/MarkersController.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/MarkersController.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonBuilder.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonBuilder.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonBuilder.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonBuilder.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonController.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonController.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonController.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonOptionsSink.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonOptionsSink.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonOptionsSink.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonOptionsSink.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonsController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonsController.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolygonsController.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolygonsController.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolylineBuilder.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylineBuilder.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolylineBuilder.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylineBuilder.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolylineController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylineController.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolylineController.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylineController.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolylineOptionsSink.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylineOptionsSink.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolylineOptionsSink.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylineOptionsSink.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolylinesController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylinesController.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/PolylinesController.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/PolylinesController.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlayBuilder.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlayBuilder.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlayBuilder.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlayBuilder.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlayController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlayController.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlayController.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlayController.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaySink.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaySink.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaySink.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaySink.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaysController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaysController.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaysController.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileOverlaysController.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileProviderController.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileProviderController.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/main/java/io/flutter/plugins/googlemaps/TileProviderController.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/main/java/io/flutter/plugins/googlemaps/TileProviderController.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleBuilderTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/CircleBuilderTest.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleBuilderTest.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/CircleBuilderTest.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/CircleControllerTest.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/GoogleMapControllerTest.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/MarkersControllerTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/MarkersControllerTest.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/MarkersControllerTest.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/MarkersControllerTest.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonBuilderTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolygonBuilderTest.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonBuilderTest.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolygonBuilderTest.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineBuilderTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolylineBuilderTest.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineBuilderTest.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolylineBuilderTest.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java diff --git a/packages/google_maps_flutter/google_maps_flutter/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker rename to packages/google_maps_flutter/google_maps_flutter_android/android/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/.metadata b/packages/google_maps_flutter/google_maps_flutter_android/example/.metadata new file mode 100644 index 000000000000..46e884ce48d1 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/.metadata @@ -0,0 +1,8 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 3ea4d06340a97a1e9d7cae97567c64e0569dcaa2 + channel: beta diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/README.md b/packages/google_maps_flutter/google_maps_flutter_android/example/README.md new file mode 100644 index 000000000000..c8852649b065 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/README.md @@ -0,0 +1,3 @@ +# google_maps_flutter_example + +Demonstrates how to use the google_maps_flutter plugin. diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/build.gradle b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/build.gradle new file mode 100644 index 000000000000..f6d29f63fadc --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/build.gradle @@ -0,0 +1,73 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 31 + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + applicationId "io.flutter.plugins.googlemapsexample" + minSdkVersion 20 + targetSdkVersion 28 + multiDexEnabled true + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + defaultConfig { + manifestPlaceholders = [mapsApiKey: "$System.env.MAPS_API_KEY"] + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } + + testOptions { + unitTests { + includeAndroidResources = true + } + } + + 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.2.0' + testImplementation 'com.google.android.gms:play-services-maps:17.0.0' + } +} + +flutter { + source '../..' +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..9a4163a4f5ee --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java new file mode 100644 index 000000000000..0f4298dca155 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java @@ -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. + +package io.flutter.plugins; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DartIntegrationTest {} diff --git a/packages/google_maps_flutter/google_maps_flutter/example/android/app/src/androidTest/java/io/flutter/plugins/googlemapsexample/GoogleMapsTest.java b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlemapsexample/GoogleMapsTest.java similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/example/android/app/src/androidTest/java/io/flutter/plugins/googlemapsexample/GoogleMapsTest.java rename to packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlemapsexample/GoogleMapsTest.java diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlemapsexample/MainActivityTest.java b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlemapsexample/MainActivityTest.java new file mode 100644 index 000000000000..244a22b6c6c8 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/androidTest/java/io/flutter/plugins/googlemapsexample/MainActivityTest.java @@ -0,0 +1,19 @@ +// 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.googlemaps; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.integration_test.FlutterTestRunner; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.plugins.DartIntegrationTest; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@DartIntegrationTest +@RunWith(FlutterTestRunner.class) +public class MainActivityTest { + @Rule + public ActivityTestRule rule = new ActivityTestRule<>(FlutterActivity.class); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/debug/AndroidManifest.xml b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000000..9c1f83d3cec5 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/debug/java/io/flutter/plugins/googlemapsexample/GoogleMapsTestActivity.java b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/debug/java/io/flutter/plugins/googlemapsexample/GoogleMapsTestActivity.java new file mode 100644 index 000000000000..e183a7c75c4e --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/debug/java/io/flutter/plugins/googlemapsexample/GoogleMapsTestActivity.java @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.googlemapsexample; + +import androidx.annotation.NonNull; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.engine.FlutterEngine; + +// Makes the FlutterEngine accessible for testing. +public class GoogleMapsTestActivity extends FlutterActivity { + public FlutterEngine engine; + + @Override + public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { + super.configureFlutterEngine(flutterEngine); + engine = flutterEngine; + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/AndroidManifest.xml b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..815074bfad96 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/drawable/launch_background.xml b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 000000000000..304732f88420 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/google_maps_flutter/google_maps_flutter_android/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/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/google_maps_flutter/google_maps_flutter_android/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/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/google_maps_flutter/google_maps_flutter_android/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/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/google_maps_flutter/google_maps_flutter_android/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/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/values/styles.xml b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000000..00fa4417cfbe --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter_android/example/android/build.gradle new file mode 100644 index 000000000000..4d8d45d13a0b --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.5.4' + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle.properties b/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle.properties new file mode 100644 index 000000000000..207beb63fb48 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle.properties @@ -0,0 +1,5 @@ +org.gradle.jvmargs=-Xmx1536M +android.enableR8=true +android.useAndroidX=true +android.enableJetifier=true + diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..9ec7236c631e --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.1-all.zip diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/settings.gradle b/packages/google_maps_flutter/google_maps_flutter_android/example/android/settings.gradle new file mode 100644 index 000000000000..5a2f14fb18f6 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/assets/2.0x/red_square.png b/packages/google_maps_flutter/google_maps_flutter_android/example/assets/2.0x/red_square.png new file mode 100644 index 0000000000000000000000000000000000000000..0f82237796bf8fd2f178f9e758330b88cf715db2 GIT binary patch literal 304 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeK3?y%aJ*@^(Ea{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8Uxs6XMFi@E-`O@66Nz ziZCX5ySp&{XVSd~vL>4nJh^c}wqi2xH2cRH(iKnkC`(qX^4o2nTuqlgyLPDM{ zjv*GOlM^IZ7bl4HG(F^GV0pm6cSViEBhjN@7W>RdP`(kYX@0FtpS)Fwr$M z2r)FZGBC0-FwiwH2a=`jO&~8KH00)|WTsW3YcRAjHic*~j#{}AsDZ)L)z4*}Q$iB} Dk-bi6 literal 0 HcmV?d00001 diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/assets/3.0x/red_square.png b/packages/google_maps_flutter/google_maps_flutter_android/example/assets/3.0x/red_square.png new file mode 100644 index 0000000000000000000000000000000000000000..7e2739974e7bb4101cc42f520ece096cb2f5ade7 GIT binary patch literal 312 zcmeAS@N?(olHy`uVBq!ia0vp^6F``e8A#skDEJMeSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=YPMC&ZPZf#LuE|04GK z{y-7NByV>Y#{W#Z_kbME0*}aI1_m)z5N7lYQuzQBWH0gbb!C6d!o|U8+TNu22`D7x z>EamTas2H;Lq;I)(1Nf2xxD-f7#JR0XW-hvz-VXkX+BU~wZt`|BqgyV)hf9t6-Y4{ z85mmX8kp!B8iW{{S{WExnHuXFm|GbbJl~bP0 Hl+XkKwG2>~ literal 0 HcmV?d00001 diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/assets/night_mode.json b/packages/google_maps_flutter/google_maps_flutter_android/example/assets/night_mode.json new file mode 100644 index 000000000000..1f16e003a920 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/assets/night_mode.json @@ -0,0 +1,162 @@ +[ + { + "elementType": "geometry", + "stylers": [ + { + "color": "#242f3e" + } + ] + }, + { + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#746855" + } + ] + }, + { + "elementType": "labels.text.stroke", + "stylers": [ + { + "color": "#242f3e" + } + ] + }, + { + "featureType": "administrative.locality", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#d59563" + } + ] + }, + { + "featureType": "poi", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#d59563" + } + ] + }, + { + "featureType": "poi.park", + "elementType": "geometry", + "stylers": [ + { + "color": "#263c3f" + } + ] + }, + { + "featureType": "poi.park", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#6b9a76" + } + ] + }, + { + "featureType": "road", + "elementType": "geometry", + "stylers": [ + { + "color": "#38414e" + } + ] + }, + { + "featureType": "road", + "elementType": "geometry.stroke", + "stylers": [ + { + "color": "#212a37" + } + ] + }, + { + "featureType": "road", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#9ca5b3" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "geometry", + "stylers": [ + { + "color": "#746855" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "geometry.stroke", + "stylers": [ + { + "color": "#1f2835" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#f3d19c" + } + ] + }, + { + "featureType": "transit", + "elementType": "geometry", + "stylers": [ + { + "color": "#2f3948" + } + ] + }, + { + "featureType": "transit.station", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#d59563" + } + ] + }, + { + "featureType": "water", + "elementType": "geometry", + "stylers": [ + { + "color": "#17263c" + } + ] + }, + { + "featureType": "water", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#515c6d" + } + ] + }, + { + "featureType": "water", + "elementType": "labels.text.stroke", + "stylers": [ + { + "color": "#17263c" + } + ] + } +] + diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/assets/red_square.png b/packages/google_maps_flutter/google_maps_flutter_android/example/assets/red_square.png new file mode 100644 index 0000000000000000000000000000000000000000..650a2dee711d0d404163de8d0e479d68e31d1662 GIT binary patch literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=CqbAk63)r1AkM7~$#S7?R=q_WVZP1_OZu2ihZzoWI>~S6qbP0l+XkKvphU~ literal 0 HcmV?d00001 diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart new file mode 100644 index 000000000000..00c51eb30b5a --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart @@ -0,0 +1,1189 @@ +// 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:async'; +import 'dart:io'; +import 'dart:typed_data'; +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:google_maps_flutter_example/example_google_map.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'package:integration_test/integration_test.dart'; + +const LatLng _kInitialMapCenter = LatLng(0, 0); +const double _kInitialZoomLevel = 5; +const CameraPosition _kInitialCameraPosition = + CameraPosition(target: _kInitialMapCenter, zoom: _kInitialZoomLevel); + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + GoogleMapsFlutterPlatform.instance.enableDebugInspection(); + + testWidgets('testCompassToggle', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + compassEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool compassEnabled = await inspector.isCompassEnabled(mapId: mapId); + expect(compassEnabled, false); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + compassEnabled: true, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + )); + + compassEnabled = await inspector.isCompassEnabled(mapId: mapId); + expect(compassEnabled, true); + }); + + testWidgets('testMapToolbarToggle', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + mapToolbarEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool mapToolbarEnabled = await inspector.isMapToolbarEnabled(mapId: mapId); + expect(mapToolbarEnabled, false); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + mapToolbarEnabled: true, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + )); + + mapToolbarEnabled = await inspector.isMapToolbarEnabled(mapId: mapId); + expect(mapToolbarEnabled, true); + }); + + testWidgets('updateMinMaxZoomLevels', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer controllerCompleter = + Completer(); + + const MinMaxZoomPreference initialZoomLevel = MinMaxZoomPreference(4, 8); + const MinMaxZoomPreference finalZoomLevel = MinMaxZoomPreference(6, 10); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + minMaxZoomPreference: initialZoomLevel, + onMapCreated: (ExampleGoogleMapController c) async { + controllerCompleter.complete(c); + }, + ), + )); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + + // On Android, zooming with zoomTo is constrained by the min/max. + await controller.moveCamera(CameraUpdate.zoomTo(15)); + await tester.pumpAndSettle(); + double? zoomLevel = await controller.getZoomLevel(); + expect(zoomLevel, equals(initialZoomLevel.maxZoom)); + + await controller.moveCamera(CameraUpdate.zoomTo(1)); + await tester.pumpAndSettle(); + zoomLevel = await controller.getZoomLevel(); + expect(zoomLevel, equals(initialZoomLevel.minZoom)); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + minMaxZoomPreference: finalZoomLevel, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + )); + + await controller.moveCamera(CameraUpdate.zoomTo(15)); + await tester.pumpAndSettle(); + zoomLevel = await controller.getZoomLevel(); + expect(zoomLevel, equals(finalZoomLevel.maxZoom)); + + await controller.moveCamera(CameraUpdate.zoomTo(1)); + await tester.pumpAndSettle(); + zoomLevel = await controller.getZoomLevel(); + expect(zoomLevel, equals(finalZoomLevel.minZoom)); + }); + + testWidgets('testZoomGesturesEnabled', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + zoomGesturesEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool zoomGesturesEnabled = + await inspector.areZoomGesturesEnabled(mapId: mapId); + expect(zoomGesturesEnabled, false); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + zoomGesturesEnabled: true, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + )); + + zoomGesturesEnabled = await inspector.areZoomGesturesEnabled(mapId: mapId); + expect(zoomGesturesEnabled, true); + }); + + testWidgets('testZoomControlsEnabled', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool zoomControlsEnabled = + await inspector.areZoomControlsEnabled(mapId: mapId); + expect(zoomControlsEnabled, true); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + zoomControlsEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + )); + + zoomControlsEnabled = await inspector.areZoomControlsEnabled(mapId: mapId); + expect(zoomControlsEnabled, false); + }); + + testWidgets('testLiteModeEnabled', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + liteModeEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool liteModeEnabled = await inspector.isLiteModeEnabled(mapId: mapId); + expect(liteModeEnabled, false); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + liteModeEnabled: true, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + )); + + liteModeEnabled = await inspector.isLiteModeEnabled(mapId: mapId); + expect(liteModeEnabled, true); + }); + + testWidgets('testRotateGesturesEnabled', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + rotateGesturesEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool rotateGesturesEnabled = + await inspector.areRotateGesturesEnabled(mapId: mapId); + expect(rotateGesturesEnabled, false); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + rotateGesturesEnabled: true, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + )); + + rotateGesturesEnabled = + await inspector.areRotateGesturesEnabled(mapId: mapId); + expect(rotateGesturesEnabled, true); + }); + + testWidgets('testTiltGesturesEnabled', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + tiltGesturesEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool tiltGesturesEnabled = + await inspector.areTiltGesturesEnabled(mapId: mapId); + expect(tiltGesturesEnabled, false); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + tiltGesturesEnabled: true, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + )); + + tiltGesturesEnabled = await inspector.areTiltGesturesEnabled(mapId: mapId); + expect(tiltGesturesEnabled, true); + }); + + testWidgets('testScrollGesturesEnabled', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + scrollGesturesEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool scrollGesturesEnabled = + await inspector.areScrollGesturesEnabled(mapId: mapId); + expect(scrollGesturesEnabled, false); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + scrollGesturesEnabled: true, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + )); + + scrollGesturesEnabled = + await inspector.areScrollGesturesEnabled(mapId: mapId); + expect(scrollGesturesEnabled, true); + }); + + testWidgets('testInitialCenterLocationAtCenter', (WidgetTester tester) async { + await tester.binding.setSurfaceSize(const Size(800, 600)); + + final Completer mapControllerCompleter = + Completer(); + final Key key = GlobalKey(); + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + mapControllerCompleter.complete(controller); + }, + ), + ), + ); + final ExampleGoogleMapController mapController = + await mapControllerCompleter.future; + + await tester.pumpAndSettle(); + + // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and + // `mapControllerCompleter.complete(controller)` above should happen in + // `mapRendered`. + // https://github.com/flutter/flutter/issues/54758 + await Future.delayed(const Duration(seconds: 1)); + + final ScreenCoordinate coordinate = + await mapController.getScreenCoordinate(_kInitialCameraPosition.target); + final Rect rect = tester.getRect(find.byKey(key)); + expect( + coordinate.x, + ((rect.center.dx - rect.topLeft.dx) * + tester.binding.window.devicePixelRatio) + .round()); + expect( + coordinate.y, + ((rect.center.dy - rect.topLeft.dy) * + tester.binding.window.devicePixelRatio) + .round()); + await tester.binding.setSurfaceSize(null); + }); + + testWidgets('testGetVisibleRegion', (WidgetTester tester) async { + final Key key = GlobalKey(); + final LatLngBounds zeroLatLngBounds = LatLngBounds( + southwest: const LatLng(0, 0), northeast: const LatLng(0, 0)); + + final Completer mapControllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + mapControllerCompleter.complete(controller); + }, + ), + )); + await tester.pumpAndSettle(); + + final ExampleGoogleMapController mapController = + await mapControllerCompleter.future; + + final LatLngBounds firstVisibleRegion = + await mapController.getVisibleRegion(); + + expect(firstVisibleRegion, isNotNull); + expect(firstVisibleRegion.southwest, isNotNull); + expect(firstVisibleRegion.northeast, isNotNull); + expect(firstVisibleRegion, isNot(zeroLatLngBounds)); + expect(firstVisibleRegion.contains(_kInitialMapCenter), isTrue); + + // Making a new `LatLngBounds` about (10, 10) distance south west to the `firstVisibleRegion`. + // The size of the `LatLngBounds` is 10 by 10. + final LatLng southWest = LatLng(firstVisibleRegion.southwest.latitude - 20, + firstVisibleRegion.southwest.longitude - 20); + final LatLng northEast = LatLng(firstVisibleRegion.southwest.latitude - 10, + firstVisibleRegion.southwest.longitude - 10); + final LatLng newCenter = LatLng( + (northEast.latitude + southWest.latitude) / 2, + (northEast.longitude + southWest.longitude) / 2, + ); + + expect(firstVisibleRegion.contains(northEast), isFalse); + expect(firstVisibleRegion.contains(southWest), isFalse); + + final LatLngBounds latLngBounds = + LatLngBounds(southwest: southWest, northeast: northEast); + + // TODO(iskakaushik): non-zero padding is needed for some device configurations + // https://github.com/flutter/flutter/issues/30575 + const double padding = 0; + await mapController + .moveCamera(CameraUpdate.newLatLngBounds(latLngBounds, padding)); + await tester.pumpAndSettle(const Duration(seconds: 3)); + + final LatLngBounds secondVisibleRegion = + await mapController.getVisibleRegion(); + + expect(secondVisibleRegion, isNotNull); + expect(secondVisibleRegion.southwest, isNotNull); + expect(secondVisibleRegion.northeast, isNotNull); + expect(secondVisibleRegion, isNot(zeroLatLngBounds)); + + expect(firstVisibleRegion, isNot(secondVisibleRegion)); + expect(secondVisibleRegion.contains(newCenter), isTrue); + }); + + testWidgets('testTraffic', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + trafficEnabled: true, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool isTrafficEnabled = await inspector.isTrafficEnabled(mapId: mapId); + expect(isTrafficEnabled, true); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + trafficEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + )); + + isTrafficEnabled = await inspector.isTrafficEnabled(mapId: mapId); + expect(isTrafficEnabled, false); + }); + + testWidgets('testBuildings', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + buildingsEnabled: true, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + final bool isBuildingsEnabled = + await inspector.areBuildingsEnabled(mapId: mapId); + expect(isBuildingsEnabled, true); + }); + + testWidgets('testMyLocationButtonToggle', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + myLocationButtonEnabled: true, + myLocationEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool myLocationButtonEnabled = + await inspector.isMyLocationButtonEnabled(mapId: mapId); + expect(myLocationButtonEnabled, true); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + myLocationButtonEnabled: false, + myLocationEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + )); + + myLocationButtonEnabled = + await inspector.isMyLocationButtonEnabled(mapId: mapId); + expect(myLocationButtonEnabled, false); + }, + // Location button tests are skipped in Android because we don't have location permission to test. + skip: true); + + testWidgets('testMyLocationButton initial value false', + (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + myLocationButtonEnabled: false, + myLocationEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + final bool myLocationButtonEnabled = + await inspector.isMyLocationButtonEnabled(mapId: mapId); + expect(myLocationButtonEnabled, false); + }, + // Location button tests are skipped in Android because we don't have location permission to test. + skip: true); + + testWidgets('testMyLocationButton initial value true', + (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + myLocationButtonEnabled: true, + myLocationEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + final bool myLocationButtonEnabled = + await inspector.isMyLocationButtonEnabled(mapId: mapId); + expect(myLocationButtonEnabled, true); + }, + // Location button tests are skipped in Android because we don't have location permission to test. + skip: true); + + testWidgets('testSetMapStyle valid Json String', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + controllerCompleter.complete(controller); + }, + ), + )); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + const String mapStyle = + '[{"elementType":"geometry","stylers":[{"color":"#242f3e"}]}]'; + await controller.setMapStyle(mapStyle); + }); + + testWidgets('testSetMapStyle invalid Json String', + (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + controllerCompleter.complete(controller); + }, + ), + )); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + + try { + await controller.setMapStyle('invalid_value'); + fail('expected MapStyleException'); + } on MapStyleException catch (e) { + expect(e.cause, isNotNull); + } + }); + + testWidgets('testSetMapStyle null string', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + controllerCompleter.complete(controller); + }, + ), + )); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + await controller.setMapStyle(null); + }); + + testWidgets('testGetLatLng', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + controllerCompleter.complete(controller); + }, + ), + )); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + + await tester.pumpAndSettle(); + // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen + // in `mapRendered`. + // https://github.com/flutter/flutter/issues/54758 + await Future.delayed(const Duration(seconds: 1)); + + final LatLngBounds visibleRegion = await controller.getVisibleRegion(); + final LatLng topLeft = + await controller.getLatLng(const ScreenCoordinate(x: 0, y: 0)); + final LatLng northWest = LatLng( + visibleRegion.northeast.latitude, + visibleRegion.southwest.longitude, + ); + + expect(topLeft, northWest); + }); + + testWidgets('testGetZoomLevel', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + controllerCompleter.complete(controller); + }, + ), + )); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + + await tester.pumpAndSettle(); + // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen + // in `mapRendered`. + // https://github.com/flutter/flutter/issues/54758 + await Future.delayed(const Duration(seconds: 1)); + + double zoom = await controller.getZoomLevel(); + expect(zoom, _kInitialZoomLevel); + + await controller.moveCamera(CameraUpdate.zoomTo(7)); + await tester.pumpAndSettle(); + zoom = await controller.getZoomLevel(); + expect(zoom, equals(7)); + }); + + testWidgets('testScreenCoordinate', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + controllerCompleter.complete(controller); + }, + ), + )); + final ExampleGoogleMapController controller = + await controllerCompleter.future; + + await tester.pumpAndSettle(); + // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen + // in `mapRendered`. + // https://github.com/flutter/flutter/issues/54758 + await Future.delayed(const Duration(seconds: 1)); + + final LatLngBounds visibleRegion = await controller.getVisibleRegion(); + final LatLng northWest = LatLng( + visibleRegion.northeast.latitude, + visibleRegion.southwest.longitude, + ); + final ScreenCoordinate topLeft = + await controller.getScreenCoordinate(northWest); + expect(topLeft, const ScreenCoordinate(x: 0, y: 0)); + }); + + testWidgets('testResizeWidget', (WidgetTester tester) async { + final Completer controllerCompleter = + Completer(); + final ExampleGoogleMap map = ExampleGoogleMap( + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) async { + controllerCompleter.complete(controller); + }, + ); + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: MaterialApp( + home: Scaffold( + body: SizedBox(height: 100, width: 100, child: map))))); + final ExampleGoogleMapController controller = + await controllerCompleter.future; + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: MaterialApp( + home: Scaffold( + body: SizedBox(height: 400, width: 400, child: map))))); + + await tester.pumpAndSettle(); + // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen + // in `mapRendered`. + // https://github.com/flutter/flutter/issues/54758 + await Future.delayed(const Duration(seconds: 1)); + + // Simple call to make sure that the app hasn't crashed. + final LatLngBounds bounds1 = await controller.getVisibleRegion(); + final LatLngBounds bounds2 = await controller.getVisibleRegion(); + expect(bounds1, bounds2); + }); + + testWidgets('testToggleInfoWindow', (WidgetTester tester) async { + const Marker marker = Marker( + markerId: MarkerId('marker'), + infoWindow: InfoWindow(title: 'InfoWindow')); + final Set markers = {marker}; + + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + initialCameraPosition: const CameraPosition(target: LatLng(10.0, 15.0)), + markers: markers, + onMapCreated: (ExampleGoogleMapController googleMapController) { + controllerCompleter.complete(googleMapController); + }, + ), + )); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + + bool iwVisibleStatus = + await controller.isMarkerInfoWindowShown(marker.markerId); + expect(iwVisibleStatus, false); + + await controller.showMarkerInfoWindow(marker.markerId); + iwVisibleStatus = await controller.isMarkerInfoWindowShown(marker.markerId); + expect(iwVisibleStatus, true); + + await controller.hideMarkerInfoWindow(marker.markerId); + iwVisibleStatus = await controller.isMarkerInfoWindowShown(marker.markerId); + expect(iwVisibleStatus, false); + }); + + testWidgets('fromAssetImage', (WidgetTester tester) async { + const double pixelRatio = 2; + const ImageConfiguration imageConfiguration = + ImageConfiguration(devicePixelRatio: pixelRatio); + final BitmapDescriptor mip = await BitmapDescriptor.fromAssetImage( + imageConfiguration, 'red_square.png'); + final BitmapDescriptor scaled = await BitmapDescriptor.fromAssetImage( + imageConfiguration, 'red_square.png', + mipmaps: false); + expect((mip.toJson() as List)[2], 1); + expect((scaled.toJson() as List)[2], 2); + }); + + testWidgets('testTakeSnapshot', (WidgetTester tester) async { + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + controllerCompleter.complete(controller); + }, + ), + ), + ); + + await tester.pumpAndSettle(const Duration(seconds: 3)); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + final Uint8List? bytes = await controller.takeSnapshot(); + expect(bytes?.isNotEmpty, true); + }, + // TODO(cyanglaz): un-skip the test when we can test this on CI with API key enabled. + // https://github.com/flutter/flutter/issues/57057 + skip: Platform.isAndroid); + + testWidgets( + 'set tileOverlay correctly', + (WidgetTester tester) async { + final Completer mapIdCompleter = Completer(); + final TileOverlay tileOverlay1 = TileOverlay( + tileOverlayId: const TileOverlayId('tile_overlay_1'), + tileProvider: _DebugTileProvider(), + zIndex: 2, + visible: true, + transparency: 0.2, + fadeIn: true, + ); + + final TileOverlay tileOverlay2 = TileOverlay( + tileOverlayId: const TileOverlayId('tile_overlay_2'), + tileProvider: _DebugTileProvider(), + zIndex: 1, + visible: false, + transparency: 0.3, + fadeIn: false, + ); + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + initialCameraPosition: _kInitialCameraPosition, + tileOverlays: {tileOverlay1, tileOverlay2}, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + ), + ); + await tester.pumpAndSettle(const Duration(seconds: 3)); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + + final TileOverlay tileOverlayInfo1 = (await inspector + .getTileOverlayInfo(tileOverlay1.mapsId, mapId: mapId))!; + final TileOverlay tileOverlayInfo2 = (await inspector + .getTileOverlayInfo(tileOverlay2.mapsId, mapId: mapId))!; + + expect(tileOverlayInfo1.visible, isTrue); + expect(tileOverlayInfo1.fadeIn, isTrue); + expect( + tileOverlayInfo1.transparency, moreOrLessEquals(0.2, epsilon: 0.001)); + expect(tileOverlayInfo1.zIndex, 2); + + expect(tileOverlayInfo2.visible, isFalse); + expect(tileOverlayInfo2.fadeIn, isFalse); + expect( + tileOverlayInfo2.transparency, moreOrLessEquals(0.3, epsilon: 0.001)); + expect(tileOverlayInfo2.zIndex, 1); + }, + ); + + testWidgets( + 'update tileOverlays correctly', + (WidgetTester tester) async { + final Completer mapIdCompleter = Completer(); + final Key key = GlobalKey(); + final TileOverlay tileOverlay1 = TileOverlay( + tileOverlayId: const TileOverlayId('tile_overlay_1'), + tileProvider: _DebugTileProvider(), + zIndex: 2, + visible: true, + transparency: 0.2, + fadeIn: true, + ); + + final TileOverlay tileOverlay2 = TileOverlay( + tileOverlayId: const TileOverlayId('tile_overlay_2'), + tileProvider: _DebugTileProvider(), + zIndex: 3, + visible: true, + transparency: 0.5, + fadeIn: true, + ); + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + tileOverlays: {tileOverlay1, tileOverlay2}, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + ), + ); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + + final TileOverlay tileOverlay1New = TileOverlay( + tileOverlayId: const TileOverlayId('tile_overlay_1'), + tileProvider: _DebugTileProvider(), + zIndex: 1, + visible: false, + transparency: 0.3, + fadeIn: false, + ); + + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + tileOverlays: {tileOverlay1New}, + onMapCreated: (ExampleGoogleMapController controller) { + fail('update: OnMapCreated should get called only once.'); + }, + ), + ), + ); + + await tester.pumpAndSettle(const Duration(seconds: 3)); + + final TileOverlay tileOverlayInfo1 = (await inspector + .getTileOverlayInfo(tileOverlay1.mapsId, mapId: mapId))!; + final TileOverlay? tileOverlayInfo2 = + await inspector.getTileOverlayInfo(tileOverlay2.mapsId, mapId: mapId); + + expect(tileOverlayInfo1.visible, isFalse); + expect(tileOverlayInfo1.fadeIn, isFalse); + expect( + tileOverlayInfo1.transparency, moreOrLessEquals(0.3, epsilon: 0.001)); + expect(tileOverlayInfo1.zIndex, 1); + + expect(tileOverlayInfo2, isNull); + }, + ); + + testWidgets( + 'remove tileOverlays correctly', + (WidgetTester tester) async { + final Completer mapIdCompleter = Completer(); + final Key key = GlobalKey(); + final TileOverlay tileOverlay1 = TileOverlay( + tileOverlayId: const TileOverlayId('tile_overlay_1'), + tileProvider: _DebugTileProvider(), + zIndex: 2, + visible: true, + transparency: 0.2, + fadeIn: true, + ); + + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + tileOverlays: {tileOverlay1}, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + ), + ); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + ), + ); + + await tester.pumpAndSettle(const Duration(seconds: 3)); + final TileOverlay? tileOverlayInfo1 = + await inspector.getTileOverlayInfo(tileOverlay1.mapsId, mapId: mapId); + + expect(tileOverlayInfo1, isNull); + }, + ); +} + +class _DebugTileProvider implements TileProvider { + _DebugTileProvider() { + boxPaint.isAntiAlias = true; + boxPaint.color = Colors.blue; + boxPaint.strokeWidth = 2.0; + boxPaint.style = PaintingStyle.stroke; + } + + static const int width = 100; + static const int height = 100; + static final Paint boxPaint = Paint(); + static const TextStyle textStyle = TextStyle( + color: Colors.red, + fontSize: 20, + ); + + @override + Future getTile(int x, int y, int? zoom) async { + final ui.PictureRecorder recorder = ui.PictureRecorder(); + final Canvas canvas = Canvas(recorder); + final TextSpan textSpan = TextSpan( + text: '$x,$y', + style: textStyle, + ); + final TextPainter textPainter = TextPainter( + text: textSpan, + textDirection: TextDirection.ltr, + ); + textPainter.layout( + minWidth: 0.0, + maxWidth: width.toDouble(), + ); + const Offset offset = Offset(0, 0); + textPainter.paint(canvas, offset); + canvas.drawRect( + Rect.fromLTRB(0, 0, width.toDouble(), width.toDouble()), boxPaint); + final ui.Picture picture = recorder.endRecording(); + final Uint8List byteData = await picture + .toImage(width, height) + .then((ui.Image image) => + image.toByteData(format: ui.ImageByteFormat.png)) + .then((ByteData? byteData) => byteData!.buffer.asUint8List()); + return Tile(width, height, byteData); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/animate_camera.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/animate_camera.dart new file mode 100644 index 000000000000..c34a3ba4b2fe --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/animate_camera.dart @@ -0,0 +1,171 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +class AnimateCameraPage extends GoogleMapExampleAppPage { + const AnimateCameraPage({Key? key}) + : super(const Icon(Icons.map), 'Camera control, animated', key: key); + + @override + Widget build(BuildContext context) { + return const AnimateCamera(); + } +} + +class AnimateCamera extends StatefulWidget { + const AnimateCamera({Key? key}) : super(key: key); + @override + State createState() => AnimateCameraState(); +} + +class AnimateCameraState extends State { + ExampleGoogleMapController? mapController; + + // ignore: use_setters_to_change_properties + void _onMapCreated(ExampleGoogleMapController controller) { + mapController = controller; + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: SizedBox( + width: 300.0, + height: 200.0, + child: ExampleGoogleMap( + onMapCreated: _onMapCreated, + initialCameraPosition: + const CameraPosition(target: LatLng(0.0, 0.0)), + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + children: [ + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.newCameraPosition( + const CameraPosition( + bearing: 270.0, + target: LatLng(51.5160895, -0.1294527), + tilt: 30.0, + zoom: 17.0, + ), + ), + ); + }, + child: const Text('newCameraPosition'), + ), + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.newLatLng( + const LatLng(56.1725505, 10.1850512), + ), + ); + }, + child: const Text('newLatLng'), + ), + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.newLatLngBounds( + LatLngBounds( + southwest: const LatLng(-38.483935, 113.248673), + northeast: const LatLng(-8.982446, 153.823821), + ), + 10.0, + ), + ); + }, + child: const Text('newLatLngBounds'), + ), + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.newLatLngZoom( + const LatLng(37.4231613, -122.087159), + 11.0, + ), + ); + }, + child: const Text('newLatLngZoom'), + ), + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.scrollBy(150.0, -225.0), + ); + }, + child: const Text('scrollBy'), + ), + ], + ), + Column( + children: [ + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.zoomBy( + -0.5, + const Offset(30.0, 20.0), + ), + ); + }, + child: const Text('zoomBy with focus'), + ), + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.zoomBy(-0.5), + ); + }, + child: const Text('zoomBy'), + ), + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.zoomIn(), + ); + }, + child: const Text('zoomIn'), + ), + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.zoomOut(), + ); + }, + child: const Text('zoomOut'), + ), + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.zoomTo(16.0), + ); + }, + child: const Text('zoomTo'), + ), + ], + ), + ], + ) + ], + ); + } +} 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 new file mode 100644 index 000000000000..e2c713163a2a --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/example_google_map.dart @@ -0,0 +1,538 @@ +// 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:async'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import +import 'dart:typed_data'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +// This is a pared down version of the Dart code from the app-facing package, +// to allow running the same examples for package-local testing. +// TODO(stuartmorgan): Consider extracting this to a shared package. See also +// https://github.com/flutter/flutter/issues/46716. + +/// Controller for a single ExampleGoogleMap instance running on the host platform. +class ExampleGoogleMapController { + ExampleGoogleMapController._( + this._googleMapState, { + required this.mapId, + }) { + _connectStreams(mapId); + } + + /// The mapId for this controller + final int mapId; + + /// Initialize control of a [ExampleGoogleMap] with [id]. + /// + /// Mainly for internal use when instantiating a [ExampleGoogleMapController] passed + /// in [ExampleGoogleMap.onMapCreated] callback. + static Future _init( + int id, + CameraPosition initialCameraPosition, + _ExampleGoogleMapState googleMapState, + ) async { + await GoogleMapsFlutterPlatform.instance.init(id); + return ExampleGoogleMapController._( + googleMapState, + mapId: id, + ); + } + + final _ExampleGoogleMapState _googleMapState; + + void _connectStreams(int mapId) { + if (_googleMapState.widget.onCameraMoveStarted != null) { + GoogleMapsFlutterPlatform.instance + .onCameraMoveStarted(mapId: mapId) + .listen((_) => _googleMapState.widget.onCameraMoveStarted!()); + } + if (_googleMapState.widget.onCameraMove != null) { + GoogleMapsFlutterPlatform.instance.onCameraMove(mapId: mapId).listen( + (CameraMoveEvent e) => _googleMapState.widget.onCameraMove!(e.value)); + } + if (_googleMapState.widget.onCameraIdle != null) { + GoogleMapsFlutterPlatform.instance + .onCameraIdle(mapId: mapId) + .listen((_) => _googleMapState.widget.onCameraIdle!()); + } + GoogleMapsFlutterPlatform.instance + .onMarkerTap(mapId: mapId) + .listen((MarkerTapEvent e) => _googleMapState.onMarkerTap(e.value)); + GoogleMapsFlutterPlatform.instance.onMarkerDragStart(mapId: mapId).listen( + (MarkerDragStartEvent e) => + _googleMapState.onMarkerDragStart(e.value, e.position)); + GoogleMapsFlutterPlatform.instance.onMarkerDrag(mapId: mapId).listen( + (MarkerDragEvent e) => + _googleMapState.onMarkerDrag(e.value, e.position)); + GoogleMapsFlutterPlatform.instance.onMarkerDragEnd(mapId: mapId).listen( + (MarkerDragEndEvent e) => + _googleMapState.onMarkerDragEnd(e.value, e.position)); + GoogleMapsFlutterPlatform.instance.onInfoWindowTap(mapId: mapId).listen( + (InfoWindowTapEvent e) => _googleMapState.onInfoWindowTap(e.value)); + GoogleMapsFlutterPlatform.instance + .onPolylineTap(mapId: mapId) + .listen((PolylineTapEvent e) => _googleMapState.onPolylineTap(e.value)); + GoogleMapsFlutterPlatform.instance + .onPolygonTap(mapId: mapId) + .listen((PolygonTapEvent e) => _googleMapState.onPolygonTap(e.value)); + GoogleMapsFlutterPlatform.instance + .onCircleTap(mapId: mapId) + .listen((CircleTapEvent e) => _googleMapState.onCircleTap(e.value)); + GoogleMapsFlutterPlatform.instance + .onTap(mapId: mapId) + .listen((MapTapEvent e) => _googleMapState.onTap(e.position)); + GoogleMapsFlutterPlatform.instance.onLongPress(mapId: mapId).listen( + (MapLongPressEvent e) => _googleMapState.onLongPress(e.position)); + } + + /// Updates configuration options of the map user interface. + Future _updateMapConfiguration(MapConfiguration update) { + return GoogleMapsFlutterPlatform.instance + .updateMapConfiguration(update, mapId: mapId); + } + + /// Updates marker configuration. + Future _updateMarkers(MarkerUpdates markerUpdates) { + return GoogleMapsFlutterPlatform.instance + .updateMarkers(markerUpdates, mapId: mapId); + } + + /// Updates polygon configuration. + Future _updatePolygons(PolygonUpdates polygonUpdates) { + return GoogleMapsFlutterPlatform.instance + .updatePolygons(polygonUpdates, mapId: mapId); + } + + /// Updates polyline configuration. + Future _updatePolylines(PolylineUpdates polylineUpdates) { + return GoogleMapsFlutterPlatform.instance + .updatePolylines(polylineUpdates, mapId: mapId); + } + + /// Updates circle configuration. + Future _updateCircles(CircleUpdates circleUpdates) { + return GoogleMapsFlutterPlatform.instance + .updateCircles(circleUpdates, mapId: mapId); + } + + /// Updates tile overlays configuration. + Future _updateTileOverlays(Set newTileOverlays) { + return GoogleMapsFlutterPlatform.instance + .updateTileOverlays(newTileOverlays: newTileOverlays, mapId: mapId); + } + + /// Clears the tile cache so that all tiles will be requested again from the + /// [TileProvider]. + Future clearTileCache(TileOverlayId tileOverlayId) async { + return GoogleMapsFlutterPlatform.instance + .clearTileCache(tileOverlayId, mapId: mapId); + } + + /// Starts an animated change of the map camera position. + Future animateCamera(CameraUpdate cameraUpdate) { + return GoogleMapsFlutterPlatform.instance + .animateCamera(cameraUpdate, mapId: mapId); + } + + /// Changes the map camera position. + Future moveCamera(CameraUpdate cameraUpdate) { + return GoogleMapsFlutterPlatform.instance + .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); + } + + /// Return [ScreenCoordinate] of the [LatLng] in the current map view. + Future getScreenCoordinate(LatLng latLng) { + return GoogleMapsFlutterPlatform.instance + .getScreenCoordinate(latLng, mapId: mapId); + } + + /// Returns [LatLng] corresponding to the [ScreenCoordinate] in the current map view. + Future getLatLng(ScreenCoordinate screenCoordinate) { + return GoogleMapsFlutterPlatform.instance + .getLatLng(screenCoordinate, mapId: mapId); + } + + /// Programmatically show the Info Window for a [Marker]. + Future showMarkerInfoWindow(MarkerId markerId) { + return GoogleMapsFlutterPlatform.instance + .showMarkerInfoWindow(markerId, mapId: mapId); + } + + /// Programmatically hide the Info Window for a [Marker]. + Future hideMarkerInfoWindow(MarkerId markerId) { + return GoogleMapsFlutterPlatform.instance + .hideMarkerInfoWindow(markerId, mapId: mapId); + } + + /// Returns `true` when the [InfoWindow] is showing, `false` otherwise. + Future isMarkerInfoWindowShown(MarkerId markerId) { + return GoogleMapsFlutterPlatform.instance + .isMarkerInfoWindowShown(markerId, mapId: mapId); + } + + /// Returns the current zoom level of the map + Future getZoomLevel() { + return GoogleMapsFlutterPlatform.instance.getZoomLevel(mapId: mapId); + } + + /// Returns the image bytes of the map + Future takeSnapshot() { + return GoogleMapsFlutterPlatform.instance.takeSnapshot(mapId: mapId); + } + + /// Disposes of the platform resources + void dispose() { + GoogleMapsFlutterPlatform.instance.dispose(mapId: mapId); + } +} + +// The next map ID to create. +int _nextMapCreationId = 0; + +/// A widget which displays a map with data obtained from the Google Maps service. +class ExampleGoogleMap extends StatefulWidget { + /// Creates a widget displaying data from Google Maps services. + /// + /// [AssertionError] will be thrown if [initialCameraPosition] is null; + const ExampleGoogleMap({ + Key? key, + required this.initialCameraPosition, + this.onMapCreated, + this.gestureRecognizers = const >{}, + this.compassEnabled = true, + this.mapToolbarEnabled = true, + this.cameraTargetBounds = CameraTargetBounds.unbounded, + this.mapType = MapType.normal, + this.minMaxZoomPreference = MinMaxZoomPreference.unbounded, + this.rotateGesturesEnabled = true, + this.scrollGesturesEnabled = true, + this.zoomControlsEnabled = true, + this.zoomGesturesEnabled = true, + this.liteModeEnabled = false, + this.tiltGesturesEnabled = true, + this.myLocationEnabled = false, + this.myLocationButtonEnabled = true, + this.layoutDirection, + + /// If no padding is specified default padding will be 0. + this.padding = const EdgeInsets.all(0), + this.indoorViewEnabled = false, + this.trafficEnabled = false, + this.buildingsEnabled = true, + this.markers = const {}, + this.polygons = const {}, + this.polylines = const {}, + this.circles = const {}, + this.onCameraMoveStarted, + this.tileOverlays = const {}, + this.onCameraMove, + this.onCameraIdle, + this.onTap, + this.onLongPress, + }) : super(key: key); + + /// Callback method for when the map is ready to be used. + /// + /// Used to receive a [ExampleGoogleMapController] for this [ExampleGoogleMap]. + final void Function(ExampleGoogleMapController controller)? onMapCreated; + + /// The initial position of the map's camera. + final CameraPosition initialCameraPosition; + + /// True if the map should show a compass when rotated. + final bool compassEnabled; + + /// True if the map should show a toolbar when you interact with the map. Android only. + final bool mapToolbarEnabled; + + /// Geographical bounding box for the camera target. + final CameraTargetBounds cameraTargetBounds; + + /// Type of map tiles to be rendered. + final MapType mapType; + + /// The layout direction to use for the embedded view. + final TextDirection? layoutDirection; + + /// Preferred bounds for the camera zoom level. + /// + /// Actual bounds depend on map data and device. + final MinMaxZoomPreference minMaxZoomPreference; + + /// True if the map view should respond to rotate gestures. + final bool rotateGesturesEnabled; + + /// True if the map view should respond to scroll gestures. + final bool scrollGesturesEnabled; + + /// True if the map view should show zoom controls. This includes two buttons + /// to zoom in and zoom out. The default value is to show zoom controls. + final bool zoomControlsEnabled; + + /// True if the map view should respond to zoom gestures. + final bool zoomGesturesEnabled; + + /// True if the map view should be in lite mode. Android only. + final bool liteModeEnabled; + + /// True if the map view should respond to tilt gestures. + final bool tiltGesturesEnabled; + + /// Padding to be set on map. + final EdgeInsets padding; + + /// Markers to be placed on the map. + final Set markers; + + /// Polygons to be placed on the map. + final Set polygons; + + /// Polylines to be placed on the map. + final Set polylines; + + /// Circles to be placed on the map. + final Set circles; + + /// Tile overlays to be placed on the map. + final Set tileOverlays; + + /// Called when the camera starts moving. + final VoidCallback? onCameraMoveStarted; + + /// Called repeatedly as the camera continues to move after an + /// onCameraMoveStarted call. + final CameraPositionCallback? onCameraMove; + + /// Called when camera movement has ended, there are no pending + /// animations and the user has stopped interacting with the map. + final VoidCallback? onCameraIdle; + + /// Called every time a [ExampleGoogleMap] is tapped. + final ArgumentCallback? onTap; + + /// Called every time a [ExampleGoogleMap] is long pressed. + final ArgumentCallback? onLongPress; + + /// True if a "My Location" layer should be shown on the map. + final bool myLocationEnabled; + + /// Enables or disables the my-location button. + final bool myLocationButtonEnabled; + + /// Enables or disables the indoor view from the map + final bool indoorViewEnabled; + + /// Enables or disables the traffic layer of the map + final bool trafficEnabled; + + /// Enables or disables showing 3D buildings where available + final bool buildingsEnabled; + + /// Which gestures should be consumed by the map. + final Set> gestureRecognizers; + + /// Creates a [State] for this [ExampleGoogleMap]. + @override + State createState() => _ExampleGoogleMapState(); +} + +class _ExampleGoogleMapState extends State { + final int _mapId = _nextMapCreationId++; + + final Completer _controller = + Completer(); + + Map _markers = {}; + Map _polygons = {}; + Map _polylines = {}; + Map _circles = {}; + late MapConfiguration _mapConfiguration; + + @override + Widget build(BuildContext context) { + return GoogleMapsFlutterPlatform.instance.buildViewWithConfiguration( + _mapId, + onPlatformViewCreated, + widgetConfiguration: MapWidgetConfiguration( + textDirection: widget.layoutDirection ?? + Directionality.maybeOf(context) ?? + TextDirection.ltr, + initialCameraPosition: widget.initialCameraPosition, + gestureRecognizers: widget.gestureRecognizers, + ), + mapObjects: MapObjects( + markers: widget.markers, + polygons: widget.polygons, + polylines: widget.polylines, + circles: widget.circles, + ), + mapConfiguration: _mapConfiguration, + ); + } + + @override + void initState() { + super.initState(); + _mapConfiguration = _configurationFromMapWidget(widget); + _markers = keyByMarkerId(widget.markers); + _polygons = keyByPolygonId(widget.polygons); + _polylines = keyByPolylineId(widget.polylines); + _circles = keyByCircleId(widget.circles); + } + + @override + void dispose() { + _controller.future + .then((ExampleGoogleMapController controller) => controller.dispose()); + super.dispose(); + } + + @override + void didUpdateWidget(ExampleGoogleMap oldWidget) { + super.didUpdateWidget(oldWidget); + _updateOptions(); + _updateMarkers(); + _updatePolygons(); + _updatePolylines(); + _updateCircles(); + _updateTileOverlays(); + } + + Future _updateOptions() async { + final MapConfiguration newConfig = _configurationFromMapWidget(widget); + final MapConfiguration updates = newConfig.diffFrom(_mapConfiguration); + if (updates.isEmpty) { + return; + } + final ExampleGoogleMapController controller = await _controller.future; + controller._updateMapConfiguration(updates); + _mapConfiguration = newConfig; + } + + Future _updateMarkers() async { + final ExampleGoogleMapController controller = await _controller.future; + controller._updateMarkers( + MarkerUpdates.from(_markers.values.toSet(), widget.markers)); + _markers = keyByMarkerId(widget.markers); + } + + Future _updatePolygons() async { + final ExampleGoogleMapController controller = await _controller.future; + controller._updatePolygons( + PolygonUpdates.from(_polygons.values.toSet(), widget.polygons)); + _polygons = keyByPolygonId(widget.polygons); + } + + Future _updatePolylines() async { + final ExampleGoogleMapController controller = await _controller.future; + controller._updatePolylines( + PolylineUpdates.from(_polylines.values.toSet(), widget.polylines)); + _polylines = keyByPolylineId(widget.polylines); + } + + Future _updateCircles() async { + final ExampleGoogleMapController controller = await _controller.future; + controller._updateCircles( + CircleUpdates.from(_circles.values.toSet(), widget.circles)); + _circles = keyByCircleId(widget.circles); + } + + Future _updateTileOverlays() async { + final ExampleGoogleMapController controller = await _controller.future; + controller._updateTileOverlays(widget.tileOverlays); + } + + Future onPlatformViewCreated(int id) async { + final ExampleGoogleMapController controller = + await ExampleGoogleMapController._init( + id, + widget.initialCameraPosition, + this, + ); + _controller.complete(controller); + _updateTileOverlays(); + widget.onMapCreated?.call(controller); + } + + void onMarkerTap(MarkerId markerId) { + _markers[markerId]!.onTap?.call(); + } + + void onMarkerDragStart(MarkerId markerId, LatLng position) { + _markers[markerId]!.onDragStart?.call(position); + } + + void onMarkerDrag(MarkerId markerId, LatLng position) { + _markers[markerId]!.onDrag?.call(position); + } + + void onMarkerDragEnd(MarkerId markerId, LatLng position) { + _markers[markerId]!.onDragEnd?.call(position); + } + + void onPolygonTap(PolygonId polygonId) { + _polygons[polygonId]!.onTap?.call(); + } + + void onPolylineTap(PolylineId polylineId) { + _polylines[polylineId]!.onTap?.call(); + } + + void onCircleTap(CircleId circleId) { + _circles[circleId]!.onTap?.call(); + } + + void onInfoWindowTap(MarkerId markerId) { + _markers[markerId]!.infoWindow.onTap?.call(); + } + + void onTap(LatLng position) { + widget.onTap?.call(position); + } + + void onLongPress(LatLng position) { + widget.onLongPress?.call(position); + } +} + +/// Builds a [MapConfiguration] from the given [map]. +MapConfiguration _configurationFromMapWidget(ExampleGoogleMap map) { + return MapConfiguration( + compassEnabled: map.compassEnabled, + mapToolbarEnabled: map.mapToolbarEnabled, + cameraTargetBounds: map.cameraTargetBounds, + mapType: map.mapType, + minMaxZoomPreference: map.minMaxZoomPreference, + rotateGesturesEnabled: map.rotateGesturesEnabled, + scrollGesturesEnabled: map.scrollGesturesEnabled, + tiltGesturesEnabled: map.tiltGesturesEnabled, + trackCameraPosition: map.onCameraMove != null, + zoomControlsEnabled: map.zoomControlsEnabled, + zoomGesturesEnabled: map.zoomGesturesEnabled, + liteModeEnabled: map.liteModeEnabled, + myLocationEnabled: map.myLocationEnabled, + myLocationButtonEnabled: map.myLocationButtonEnabled, + padding: map.padding, + indoorViewEnabled: map.indoorViewEnabled, + trafficEnabled: map.trafficEnabled, + buildingsEnabled: map.buildingsEnabled, + ); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/lite_mode.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/lite_mode.dart new file mode 100644 index 000000000000..f7bead951f5d --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/lite_mode.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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +const CameraPosition _kInitialPosition = + CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); + +class LiteModePage extends GoogleMapExampleAppPage { + const LiteModePage({Key? key}) + : super(const Icon(Icons.map), 'Lite mode', key: key); + + @override + Widget build(BuildContext context) { + return const _LiteModeBody(); + } +} + +class _LiteModeBody extends StatelessWidget { + const _LiteModeBody(); + + @override + Widget build(BuildContext context) { + return const Card( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 30.0), + child: Center( + child: SizedBox( + width: 300.0, + height: 300.0, + child: ExampleGoogleMap( + initialCameraPosition: _kInitialPosition, + liteModeEnabled: true, + ), + ), + ), + ), + ); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/main.dart new file mode 100644 index 000000000000..6b96e6f0dff8 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/main.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/material.dart'; +import 'package:google_maps_flutter_android/google_maps_flutter_android.dart'; +import 'package:google_maps_flutter_example/lite_mode.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'animate_camera.dart'; +import 'map_click.dart'; +import 'map_coordinates.dart'; +import 'map_ui.dart'; +import 'marker_icons.dart'; +import 'move_camera.dart'; +import 'padding.dart'; +import 'page.dart'; +import 'place_circle.dart'; +import 'place_marker.dart'; +import 'place_polygon.dart'; +import 'place_polyline.dart'; +import 'scrolling_map.dart'; +import 'snapshot.dart'; +import 'tile_overlay.dart'; + +final List _allPages = [ + const MapUiPage(), + const MapCoordinatesPage(), + const MapClickPage(), + const AnimateCameraPage(), + const MoveCameraPage(), + const PlaceMarkerPage(), + const MarkerIconsPage(), + const ScrollingMapPage(), + const PlacePolylinePage(), + const PlacePolygonPage(), + const PlaceCirclePage(), + const PaddingPage(), + const SnapshotPage(), + const LiteModePage(), + const TileOverlayPage(), +]; + +/// MapsDemo is the Main Application. +class MapsDemo extends StatelessWidget { + /// Default Constructor + const MapsDemo({Key? key}) : super(key: key); + + void _pushPage(BuildContext context, GoogleMapExampleAppPage page) { + Navigator.of(context).push(MaterialPageRoute( + builder: (_) => Scaffold( + appBar: AppBar(title: Text(page.title)), + body: page, + ))); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('GoogleMaps examples')), + body: ListView.builder( + itemCount: _allPages.length, + itemBuilder: (_, int index) => ListTile( + leading: _allPages[index].leading, + title: Text(_allPages[index].title), + onTap: () => _pushPage(context, _allPages[index]), + ), + ), + ); + } +} + +void main() { + final GoogleMapsFlutterPlatform platform = GoogleMapsFlutterPlatform.instance; + // Default to Hybrid Composition for the example. + (platform as GoogleMapsFlutterAndroid).useAndroidViewSurface = true; + runApp(const MaterialApp(home: MapsDemo())); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_click.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_click.dart new file mode 100644 index 000000000000..20718d9751ab --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_click.dart @@ -0,0 +1,106 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +const CameraPosition _kInitialPosition = + CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); + +class MapClickPage extends GoogleMapExampleAppPage { + const MapClickPage({Key? key}) + : super(const Icon(Icons.mouse), 'Map click', key: key); + + @override + Widget build(BuildContext context) { + return const _MapClickBody(); + } +} + +class _MapClickBody extends StatefulWidget { + const _MapClickBody(); + + @override + State createState() => _MapClickBodyState(); +} + +class _MapClickBodyState extends State<_MapClickBody> { + _MapClickBodyState(); + + ExampleGoogleMapController? mapController; + LatLng? _lastTap; + LatLng? _lastLongPress; + + @override + Widget build(BuildContext context) { + final ExampleGoogleMap googleMap = ExampleGoogleMap( + onMapCreated: onMapCreated, + initialCameraPosition: _kInitialPosition, + onTap: (LatLng pos) { + setState(() { + _lastTap = pos; + }); + }, + onLongPress: (LatLng pos) { + setState(() { + _lastLongPress = pos; + }); + }, + ); + + final List columnChildren = [ + Padding( + padding: const EdgeInsets.all(10.0), + child: Center( + child: SizedBox( + width: 300.0, + height: 200.0, + child: googleMap, + ), + ), + ), + ]; + + if (mapController != null) { + final String lastTap = 'Tap:\n${_lastTap ?? ""}\n'; + final String lastLongPress = 'Long press:\n${_lastLongPress ?? ""}'; + columnChildren.add(Center( + child: Text( + lastTap, + textAlign: TextAlign.center, + ))); + columnChildren.add(Center( + child: Text( + _lastTap != null ? 'Tapped' : '', + textAlign: TextAlign.center, + ))); + columnChildren.add(Center( + child: Text( + lastLongPress, + textAlign: TextAlign.center, + ))); + columnChildren.add(Center( + child: Text( + _lastLongPress != null ? 'Long pressed' : '', + textAlign: TextAlign.center, + ))); + } + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: columnChildren, + ); + } + + Future onMapCreated(ExampleGoogleMapController controller) async { + setState(() { + mapController = controller; + }); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_coordinates.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_coordinates.dart new file mode 100644 index 000000000000..185a97e08f00 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_coordinates.dart @@ -0,0 +1,100 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +const CameraPosition _kInitialPosition = + CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); + +class MapCoordinatesPage extends GoogleMapExampleAppPage { + const MapCoordinatesPage({Key? key}) + : super(const Icon(Icons.map), 'Map coordinates', key: key); + + @override + Widget build(BuildContext context) { + return const _MapCoordinatesBody(); + } +} + +class _MapCoordinatesBody extends StatefulWidget { + const _MapCoordinatesBody(); + + @override + State createState() => _MapCoordinatesBodyState(); +} + +class _MapCoordinatesBodyState extends State<_MapCoordinatesBody> { + _MapCoordinatesBodyState(); + + ExampleGoogleMapController? mapController; + LatLngBounds _visibleRegion = LatLngBounds( + southwest: const LatLng(0, 0), + northeast: const LatLng(0, 0), + ); + + @override + Widget build(BuildContext context) { + final ExampleGoogleMap googleMap = ExampleGoogleMap( + onMapCreated: onMapCreated, + initialCameraPosition: _kInitialPosition, + onCameraIdle: + _updateVisibleRegion, // https://github.com/flutter/flutter/issues/54758 + ); + + return NotificationListener( + onNotification: (ScrollNotification scrollState) { + _updateVisibleRegion(); + return true; + }, + child: ListView( + children: [ + Padding( + padding: const EdgeInsets.all(10.0), + child: Center( + child: SizedBox( + width: 300.0, + height: 200.0, + child: googleMap, + ), + ), + ), + if (mapController != null) + Center( + child: Text('VisibleRegion:' + '\nnortheast: ${_visibleRegion.northeast},' + '\nsouthwest: ${_visibleRegion.southwest}'), + ), + // Add a block at the bottom of this list to allow validation that the visible region of the map + // does not change when scrolled under the safe view on iOS. + // https://github.com/flutter/flutter/issues/107913 + Container( + width: 300, + height: 1000, + ), + ], + ), + ); + } + + Future onMapCreated(ExampleGoogleMapController controller) async { + final LatLngBounds visibleRegion = await controller.getVisibleRegion(); + setState(() { + mapController = controller; + _visibleRegion = visibleRegion; + }); + } + + Future _updateVisibleRegion() async { + final LatLngBounds visibleRegion = await mapController!.getVisibleRegion(); + setState(() { + _visibleRegion = visibleRegion; + }); + } +} 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 new file mode 100644 index 000000000000..6c38e14d86a5 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_ui.dart @@ -0,0 +1,358 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show rootBundle; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +final LatLngBounds sydneyBounds = LatLngBounds( + southwest: const LatLng(-34.022631, 150.620685), + northeast: const LatLng(-33.571835, 151.325952), +); + +class MapUiPage extends GoogleMapExampleAppPage { + const MapUiPage({Key? key}) + : super(const Icon(Icons.map), 'User interface', key: key); + + @override + Widget build(BuildContext context) { + return const MapUiBody(); + } +} + +class MapUiBody extends StatefulWidget { + const MapUiBody({Key? key}) : super(key: key); + + @override + State createState() => MapUiBodyState(); +} + +class MapUiBodyState extends State { + MapUiBodyState(); + + static const CameraPosition _kInitialPosition = CameraPosition( + target: LatLng(-33.852, 151.211), + zoom: 11.0, + ); + + CameraPosition _position = _kInitialPosition; + bool _isMapCreated = false; + final bool _isMoving = false; + bool _compassEnabled = true; + bool _mapToolbarEnabled = true; + CameraTargetBounds _cameraTargetBounds = CameraTargetBounds.unbounded; + MinMaxZoomPreference _minMaxZoomPreference = MinMaxZoomPreference.unbounded; + MapType _mapType = MapType.normal; + bool _rotateGesturesEnabled = true; + bool _scrollGesturesEnabled = true; + bool _tiltGesturesEnabled = true; + bool _zoomControlsEnabled = false; + bool _zoomGesturesEnabled = true; + bool _indoorViewEnabled = true; + bool _myLocationEnabled = true; + bool _myTrafficEnabled = false; + bool _myLocationButtonEnabled = true; + late ExampleGoogleMapController _controller; + bool _nightMode = false; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + Widget _compassToggler() { + return TextButton( + child: Text('${_compassEnabled ? 'disable' : 'enable'} compass'), + onPressed: () { + setState(() { + _compassEnabled = !_compassEnabled; + }); + }, + ); + } + + Widget _mapToolbarToggler() { + return TextButton( + child: Text('${_mapToolbarEnabled ? 'disable' : 'enable'} map toolbar'), + onPressed: () { + setState(() { + _mapToolbarEnabled = !_mapToolbarEnabled; + }); + }, + ); + } + + Widget _latLngBoundsToggler() { + return TextButton( + child: Text( + _cameraTargetBounds.bounds == null + ? 'bound camera target' + : 'release camera target', + ), + onPressed: () { + setState(() { + _cameraTargetBounds = _cameraTargetBounds.bounds == null + ? CameraTargetBounds(sydneyBounds) + : CameraTargetBounds.unbounded; + }); + }, + ); + } + + Widget _zoomBoundsToggler() { + return TextButton( + child: Text(_minMaxZoomPreference.minZoom == null + ? 'bound zoom' + : 'release zoom'), + onPressed: () { + setState(() { + _minMaxZoomPreference = _minMaxZoomPreference.minZoom == null + ? const MinMaxZoomPreference(12.0, 16.0) + : MinMaxZoomPreference.unbounded; + }); + }, + ); + } + + Widget _mapTypeCycler() { + final MapType nextType = + MapType.values[(_mapType.index + 1) % MapType.values.length]; + return TextButton( + child: Text('change map type to $nextType'), + onPressed: () { + setState(() { + _mapType = nextType; + }); + }, + ); + } + + Widget _rotateToggler() { + return TextButton( + child: Text('${_rotateGesturesEnabled ? 'disable' : 'enable'} rotate'), + onPressed: () { + setState(() { + _rotateGesturesEnabled = !_rotateGesturesEnabled; + }); + }, + ); + } + + Widget _scrollToggler() { + return TextButton( + child: Text('${_scrollGesturesEnabled ? 'disable' : 'enable'} scroll'), + onPressed: () { + setState(() { + _scrollGesturesEnabled = !_scrollGesturesEnabled; + }); + }, + ); + } + + Widget _tiltToggler() { + return TextButton( + child: Text('${_tiltGesturesEnabled ? 'disable' : 'enable'} tilt'), + onPressed: () { + setState(() { + _tiltGesturesEnabled = !_tiltGesturesEnabled; + }); + }, + ); + } + + Widget _zoomToggler() { + return TextButton( + child: Text('${_zoomGesturesEnabled ? 'disable' : 'enable'} zoom'), + onPressed: () { + setState(() { + _zoomGesturesEnabled = !_zoomGesturesEnabled; + }); + }, + ); + } + + Widget _zoomControlsToggler() { + return TextButton( + child: + Text('${_zoomControlsEnabled ? 'disable' : 'enable'} zoom controls'), + onPressed: () { + setState(() { + _zoomControlsEnabled = !_zoomControlsEnabled; + }); + }, + ); + } + + Widget _indoorViewToggler() { + return TextButton( + child: Text('${_indoorViewEnabled ? 'disable' : 'enable'} indoor'), + onPressed: () { + setState(() { + _indoorViewEnabled = !_indoorViewEnabled; + }); + }, + ); + } + + Widget _myLocationToggler() { + return TextButton( + child: Text( + '${_myLocationEnabled ? 'disable' : 'enable'} my location marker'), + onPressed: () { + setState(() { + _myLocationEnabled = !_myLocationEnabled; + }); + }, + ); + } + + Widget _myLocationButtonToggler() { + return TextButton( + child: Text( + '${_myLocationButtonEnabled ? 'disable' : 'enable'} my location button'), + onPressed: () { + setState(() { + _myLocationButtonEnabled = !_myLocationButtonEnabled; + }); + }, + ); + } + + Widget _myTrafficToggler() { + return TextButton( + child: Text('${_myTrafficEnabled ? 'disable' : 'enable'} my traffic'), + onPressed: () { + setState(() { + _myTrafficEnabled = !_myTrafficEnabled; + }); + }, + ); + } + + Future _getFileData(String path) async { + return await 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); + } + }, + ); + } + + @override + Widget build(BuildContext context) { + final ExampleGoogleMap googleMap = ExampleGoogleMap( + onMapCreated: onMapCreated, + initialCameraPosition: _kInitialPosition, + compassEnabled: _compassEnabled, + mapToolbarEnabled: _mapToolbarEnabled, + cameraTargetBounds: _cameraTargetBounds, + minMaxZoomPreference: _minMaxZoomPreference, + mapType: _mapType, + rotateGesturesEnabled: _rotateGesturesEnabled, + scrollGesturesEnabled: _scrollGesturesEnabled, + tiltGesturesEnabled: _tiltGesturesEnabled, + zoomGesturesEnabled: _zoomGesturesEnabled, + zoomControlsEnabled: _zoomControlsEnabled, + indoorViewEnabled: _indoorViewEnabled, + myLocationEnabled: _myLocationEnabled, + myLocationButtonEnabled: _myLocationButtonEnabled, + trafficEnabled: _myTrafficEnabled, + onCameraMove: _updateCameraPosition, + ); + + final List columnChildren = [ + Padding( + padding: const EdgeInsets.all(10.0), + child: Center( + child: SizedBox( + width: 300.0, + height: 200.0, + child: googleMap, + ), + ), + ), + ]; + + if (_isMapCreated) { + columnChildren.add( + Expanded( + child: ListView( + children: [ + Text('camera bearing: ${_position.bearing}'), + Text( + 'camera target: ${_position.target.latitude.toStringAsFixed(4)},' + '${_position.target.longitude.toStringAsFixed(4)}'), + Text('camera zoom: ${_position.zoom}'), + Text('camera tilt: ${_position.tilt}'), + Text(_isMoving ? '(Camera moving)' : '(Camera idle)'), + _compassToggler(), + _mapToolbarToggler(), + _latLngBoundsToggler(), + _mapTypeCycler(), + _zoomBoundsToggler(), + _rotateToggler(), + _scrollToggler(), + _tiltToggler(), + _zoomToggler(), + _zoomControlsToggler(), + _indoorViewToggler(), + _myLocationToggler(), + _myLocationButtonToggler(), + _myTrafficToggler(), + _nightModeToggler(), + ], + ), + ), + ); + } + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: columnChildren, + ); + } + + void _updateCameraPosition(CameraPosition position) { + setState(() { + _position = position; + }); + } + + void onMapCreated(ExampleGoogleMapController controller) { + setState(() { + _controller = controller; + _isMapCreated = true; + }); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/marker_icons.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/marker_icons.dart new file mode 100644 index 000000000000..fe28eb680596 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/marker_icons.dart @@ -0,0 +1,98 @@ +// 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. + +// ignore_for_file: public_member_api_docs +// ignore_for_file: unawaited_futures + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +class MarkerIconsPage extends GoogleMapExampleAppPage { + const MarkerIconsPage({Key? key}) + : super(const Icon(Icons.image), 'Marker icons', key: key); + + @override + Widget build(BuildContext context) { + return const MarkerIconsBody(); + } +} + +class MarkerIconsBody extends StatefulWidget { + const MarkerIconsBody({Key? key}) : super(key: key); + + @override + State createState() => MarkerIconsBodyState(); +} + +const LatLng _kMapCenter = LatLng(52.4478, -3.5402); + +class MarkerIconsBodyState extends State { + ExampleGoogleMapController? controller; + BitmapDescriptor? _markerIcon; + + @override + Widget build(BuildContext context) { + _createMarkerImageFromAsset(context); + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: SizedBox( + width: 350.0, + height: 300.0, + child: ExampleGoogleMap( + initialCameraPosition: const CameraPosition( + target: _kMapCenter, + zoom: 7.0, + ), + markers: {_createMarker()}, + onMapCreated: _onMapCreated, + ), + ), + ) + ], + ); + } + + Marker _createMarker() { + if (_markerIcon != null) { + return Marker( + markerId: const MarkerId('marker_1'), + position: _kMapCenter, + icon: _markerIcon!, + ); + } else { + return const Marker( + markerId: MarkerId('marker_1'), + position: _kMapCenter, + ); + } + } + + Future _createMarkerImageFromAsset(BuildContext context) async { + if (_markerIcon == null) { + final ImageConfiguration imageConfiguration = + createLocalImageConfiguration(context, size: const Size.square(48)); + BitmapDescriptor.fromAssetImage( + imageConfiguration, 'assets/red_square.png') + .then(_updateBitmap); + } + } + + void _updateBitmap(BitmapDescriptor bitmap) { + setState(() { + _markerIcon = bitmap; + }); + } + + void _onMapCreated(ExampleGoogleMapController controllerParam) { + setState(() { + controller = controllerParam; + }); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/move_camera.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/move_camera.dart new file mode 100644 index 000000000000..7f44d89518dc --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/move_camera.dart @@ -0,0 +1,171 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +class MoveCameraPage extends GoogleMapExampleAppPage { + const MoveCameraPage({Key? key}) + : super(const Icon(Icons.map), 'Camera control', key: key); + + @override + Widget build(BuildContext context) { + return const MoveCamera(); + } +} + +class MoveCamera extends StatefulWidget { + const MoveCamera({Key? key}) : super(key: key); + @override + State createState() => MoveCameraState(); +} + +class MoveCameraState extends State { + ExampleGoogleMapController? mapController; + + // ignore: use_setters_to_change_properties + void _onMapCreated(ExampleGoogleMapController controller) { + mapController = controller; + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: SizedBox( + width: 300.0, + height: 200.0, + child: ExampleGoogleMap( + onMapCreated: _onMapCreated, + initialCameraPosition: + const CameraPosition(target: LatLng(0.0, 0.0)), + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + children: [ + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.newCameraPosition( + const CameraPosition( + bearing: 270.0, + target: LatLng(51.5160895, -0.1294527), + tilt: 30.0, + zoom: 17.0, + ), + ), + ); + }, + child: const Text('newCameraPosition'), + ), + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.newLatLng( + const LatLng(56.1725505, 10.1850512), + ), + ); + }, + child: const Text('newLatLng'), + ), + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.newLatLngBounds( + LatLngBounds( + southwest: const LatLng(-38.483935, 113.248673), + northeast: const LatLng(-8.982446, 153.823821), + ), + 10.0, + ), + ); + }, + child: const Text('newLatLngBounds'), + ), + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.newLatLngZoom( + const LatLng(37.4231613, -122.087159), + 11.0, + ), + ); + }, + child: const Text('newLatLngZoom'), + ), + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.scrollBy(150.0, -225.0), + ); + }, + child: const Text('scrollBy'), + ), + ], + ), + Column( + children: [ + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.zoomBy( + -0.5, + const Offset(30.0, 20.0), + ), + ); + }, + child: const Text('zoomBy with focus'), + ), + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.zoomBy(-0.5), + ); + }, + child: const Text('zoomBy'), + ), + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.zoomIn(), + ); + }, + child: const Text('zoomIn'), + ), + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.zoomOut(), + ); + }, + child: const Text('zoomOut'), + ), + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.zoomTo(16.0), + ); + }, + child: const Text('zoomTo'), + ), + ], + ), + ], + ) + ], + ); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/padding.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/padding.dart new file mode 100644 index 000000000000..d346549b9d40 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/padding.dart @@ -0,0 +1,181 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +class PaddingPage extends GoogleMapExampleAppPage { + const PaddingPage({Key? key}) + : super(const Icon(Icons.map), 'Add padding to the map', key: key); + + @override + Widget build(BuildContext context) { + return const MarkerIconsBody(); + } +} + +class MarkerIconsBody extends StatefulWidget { + const MarkerIconsBody({Key? key}) : super(key: key); + + @override + State createState() => MarkerIconsBodyState(); +} + +const LatLng _kMapCenter = LatLng(52.4478, -3.5402); + +class MarkerIconsBodyState extends State { + ExampleGoogleMapController? controller; + + EdgeInsets _padding = const EdgeInsets.all(0); + + @override + Widget build(BuildContext context) { + final ExampleGoogleMap googleMap = ExampleGoogleMap( + onMapCreated: _onMapCreated, + initialCameraPosition: const CameraPosition( + target: _kMapCenter, + zoom: 7.0, + ), + padding: _padding, + ); + + final List columnChildren = [ + Padding( + padding: const EdgeInsets.all(10.0), + child: Center( + child: SizedBox( + width: 300.0, + height: 200.0, + child: googleMap, + ), + ), + ), + const Padding( + padding: EdgeInsets.only(top: 20), + child: Center( + child: Text( + 'Enter Padding Below', + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + ), + ), + ), + ]; + + columnChildren.addAll([_paddingInput(), _buttons()]); + + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: columnChildren, + ); + } + + void _onMapCreated(ExampleGoogleMapController controllerParam) { + setState(() { + controller = controllerParam; + }); + } + + final TextEditingController _topController = TextEditingController(); + final TextEditingController _bottomController = TextEditingController(); + final TextEditingController _leftController = TextEditingController(); + final TextEditingController _rightController = TextEditingController(); + + Widget _paddingInput() { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + children: [ + Flexible( + flex: 2, + child: TextField( + controller: _topController, + keyboardType: TextInputType.number, + textAlign: TextAlign.center, + decoration: const InputDecoration( + hintText: 'Top', + ), + ), + ), + const Spacer(), + Flexible( + flex: 2, + child: TextField( + controller: _bottomController, + keyboardType: TextInputType.number, + textAlign: TextAlign.center, + decoration: const InputDecoration( + hintText: 'Bottom', + ), + ), + ), + const Spacer(), + Flexible( + flex: 2, + child: TextField( + controller: _leftController, + keyboardType: TextInputType.number, + textAlign: TextAlign.center, + decoration: const InputDecoration( + hintText: 'Left', + ), + ), + ), + const Spacer(), + Flexible( + flex: 2, + child: TextField( + controller: _rightController, + keyboardType: TextInputType.number, + textAlign: TextAlign.center, + decoration: const InputDecoration( + hintText: 'Right', + ), + ), + ), + ], + ), + ); + } + + Widget _buttons() { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + TextButton( + child: const Text('Set Padding'), + onPressed: () { + setState(() { + _padding = EdgeInsets.fromLTRB( + double.tryParse(_leftController.value.text) ?? 0, + double.tryParse(_topController.value.text) ?? 0, + double.tryParse(_rightController.value.text) ?? 0, + double.tryParse(_bottomController.value.text) ?? 0); + }); + }, + ), + TextButton( + child: const Text('Reset Padding'), + onPressed: () { + setState(() { + _topController.clear(); + _bottomController.clear(); + _leftController.clear(); + _rightController.clear(); + _padding = const EdgeInsets.all(0); + }); + }, + ) + ], + ), + ); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/page.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/page.dart new file mode 100644 index 000000000000..eb01ab07a6f3 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/page.dart @@ -0,0 +1,15 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; + +abstract class GoogleMapExampleAppPage extends StatelessWidget { + const GoogleMapExampleAppPage(this.leading, this.title, {Key? key}) + : super(key: key); + + final Widget leading; + final String title; +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_circle.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_circle.dart new file mode 100644 index 000000000000..9dc5760afa1f --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_circle.dart @@ -0,0 +1,232 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +class PlaceCirclePage extends GoogleMapExampleAppPage { + const PlaceCirclePage({Key? key}) + : super(const Icon(Icons.linear_scale), 'Place circle', key: key); + + @override + Widget build(BuildContext context) { + return const PlaceCircleBody(); + } +} + +class PlaceCircleBody extends StatefulWidget { + const PlaceCircleBody({Key? key}) : super(key: key); + + @override + State createState() => PlaceCircleBodyState(); +} + +class PlaceCircleBodyState extends State { + PlaceCircleBodyState(); + + ExampleGoogleMapController? controller; + Map circles = {}; + int _circleIdCounter = 1; + CircleId? selectedCircle; + + // Values when toggling circle color + int fillColorsIndex = 0; + int strokeColorsIndex = 0; + List colors = [ + Colors.purple, + Colors.red, + Colors.green, + Colors.pink, + ]; + + // Values when toggling circle stroke width + int widthsIndex = 0; + List widths = [10, 20, 5]; + + // ignore: use_setters_to_change_properties + void _onMapCreated(ExampleGoogleMapController controller) { + this.controller = controller; + } + + @override + void dispose() { + super.dispose(); + } + + void _onCircleTapped(CircleId circleId) { + setState(() { + selectedCircle = circleId; + }); + } + + void _remove(CircleId circleId) { + setState(() { + if (circles.containsKey(circleId)) { + circles.remove(circleId); + } + if (circleId == selectedCircle) { + selectedCircle = null; + } + }); + } + + void _add() { + final int circleCount = circles.length; + + if (circleCount == 12) { + return; + } + + final String circleIdVal = 'circle_id_$_circleIdCounter'; + _circleIdCounter++; + final CircleId circleId = CircleId(circleIdVal); + + final Circle circle = Circle( + circleId: circleId, + consumeTapEvents: true, + strokeColor: Colors.orange, + fillColor: Colors.green, + strokeWidth: 5, + center: _createCenter(), + radius: 50000, + onTap: () { + _onCircleTapped(circleId); + }, + ); + + setState(() { + circles[circleId] = circle; + }); + } + + void _toggleVisible(CircleId circleId) { + final Circle circle = circles[circleId]!; + setState(() { + circles[circleId] = circle.copyWith( + visibleParam: !circle.visible, + ); + }); + } + + void _changeFillColor(CircleId circleId) { + final Circle circle = circles[circleId]!; + setState(() { + circles[circleId] = circle.copyWith( + fillColorParam: colors[++fillColorsIndex % colors.length], + ); + }); + } + + void _changeStrokeColor(CircleId circleId) { + final Circle circle = circles[circleId]!; + setState(() { + circles[circleId] = circle.copyWith( + strokeColorParam: colors[++strokeColorsIndex % colors.length], + ); + }); + } + + void _changeStrokeWidth(CircleId circleId) { + final Circle circle = circles[circleId]!; + setState(() { + circles[circleId] = circle.copyWith( + strokeWidthParam: widths[++widthsIndex % widths.length], + ); + }); + } + + @override + Widget build(BuildContext context) { + final CircleId? selectedId = selectedCircle; + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: SizedBox( + width: 350.0, + height: 300.0, + child: ExampleGoogleMap( + initialCameraPosition: const CameraPosition( + target: LatLng(52.4478, -3.5402), + zoom: 7.0, + ), + circles: Set.of(circles.values), + onMapCreated: _onMapCreated, + ), + ), + ), + Expanded( + child: SingleChildScrollView( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Row( + children: [ + Column( + children: [ + TextButton( + onPressed: _add, + child: const Text('add'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _remove(selectedId), + child: const Text('remove'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), + ), + ], + ), + Column( + children: [ + TextButton( + onPressed: (selectedId == null) + ? null + : () => _changeStrokeWidth(selectedId), + child: const Text('change stroke width'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _changeStrokeColor(selectedId), + child: const Text('change stroke color'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _changeFillColor(selectedId), + child: const Text('change fill color'), + ), + ], + ) + ], + ) + ], + ), + ), + ), + ], + ); + } + + LatLng _createCenter() { + final double offset = _circleIdCounter.ceilToDouble(); + return _createLatLng(51.4816 + offset * 0.2, -3.1791); + } + + LatLng _createLatLng(double lat, double lng) { + return LatLng(lat, lng); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_marker.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_marker.dart new file mode 100644 index 000000000000..b7d2a690c3e8 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_marker.dart @@ -0,0 +1,422 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; +import 'dart:math'; +import 'dart:typed_data'; +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +class PlaceMarkerPage extends GoogleMapExampleAppPage { + const PlaceMarkerPage({Key? key}) + : super(const Icon(Icons.place), 'Place marker', key: key); + + @override + Widget build(BuildContext context) { + return const PlaceMarkerBody(); + } +} + +class PlaceMarkerBody extends StatefulWidget { + const PlaceMarkerBody({Key? key}) : super(key: key); + + @override + State createState() => PlaceMarkerBodyState(); +} + +typedef MarkerUpdateAction = Marker Function(Marker marker); + +class PlaceMarkerBodyState extends State { + PlaceMarkerBodyState(); + static const LatLng center = LatLng(-33.86711, 151.1947171); + + ExampleGoogleMapController? controller; + Map markers = {}; + MarkerId? selectedMarker; + int _markerIdCounter = 1; + LatLng? markerPosition; + + // ignore: use_setters_to_change_properties + void _onMapCreated(ExampleGoogleMapController controller) { + this.controller = controller; + } + + @override + void dispose() { + super.dispose(); + } + + void _onMarkerTapped(MarkerId markerId) { + final Marker? tappedMarker = markers[markerId]; + if (tappedMarker != null) { + setState(() { + final MarkerId? previousMarkerId = selectedMarker; + if (previousMarkerId != null && markers.containsKey(previousMarkerId)) { + final Marker resetOld = markers[previousMarkerId]! + .copyWith(iconParam: BitmapDescriptor.defaultMarker); + markers[previousMarkerId] = resetOld; + } + selectedMarker = markerId; + final Marker newMarker = tappedMarker.copyWith( + iconParam: BitmapDescriptor.defaultMarkerWithHue( + BitmapDescriptor.hueGreen, + ), + ); + markers[markerId] = newMarker; + + markerPosition = null; + }); + } + } + + Future _onMarkerDrag(MarkerId markerId, LatLng newPosition) async { + setState(() { + markerPosition = newPosition; + }); + } + + Future _onMarkerDragEnd(MarkerId markerId, LatLng newPosition) async { + final Marker? tappedMarker = markers[markerId]; + if (tappedMarker != null) { + setState(() { + markerPosition = null; + }); + await showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + actions: [ + TextButton( + child: const Text('OK'), + onPressed: () => Navigator.of(context).pop(), + ) + ], + content: Padding( + padding: const EdgeInsets.symmetric(vertical: 66), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text('Old position: ${tappedMarker.position}'), + Text('New position: $newPosition'), + ], + ))); + }); + } + } + + void _add() { + final int markerCount = markers.length; + + if (markerCount == 12) { + return; + } + + final String markerIdVal = 'marker_id_$_markerIdCounter'; + _markerIdCounter++; + final MarkerId markerId = MarkerId(markerIdVal); + + final Marker marker = Marker( + markerId: markerId, + position: LatLng( + center.latitude + sin(_markerIdCounter * pi / 6.0) / 20.0, + center.longitude + cos(_markerIdCounter * pi / 6.0) / 20.0, + ), + infoWindow: InfoWindow(title: markerIdVal, snippet: '*'), + onTap: () => _onMarkerTapped(markerId), + onDragEnd: (LatLng position) => _onMarkerDragEnd(markerId, position), + onDrag: (LatLng position) => _onMarkerDrag(markerId, position), + ); + + setState(() { + markers[markerId] = marker; + }); + } + + void _remove(MarkerId markerId) { + setState(() { + if (markers.containsKey(markerId)) { + markers.remove(markerId); + } + }); + } + + void _changePosition(MarkerId markerId) { + final Marker marker = markers[markerId]!; + final LatLng current = marker.position; + final Offset offset = Offset( + center.latitude - current.latitude, + center.longitude - current.longitude, + ); + setState(() { + markers[markerId] = marker.copyWith( + positionParam: LatLng( + center.latitude + offset.dy, + center.longitude + offset.dx, + ), + ); + }); + } + + void _changeAnchor(MarkerId markerId) { + final Marker marker = markers[markerId]!; + final Offset currentAnchor = marker.anchor; + final Offset newAnchor = Offset(1.0 - currentAnchor.dy, currentAnchor.dx); + setState(() { + markers[markerId] = marker.copyWith( + anchorParam: newAnchor, + ); + }); + } + + Future _changeInfoAnchor(MarkerId markerId) async { + final Marker marker = markers[markerId]!; + final Offset currentAnchor = marker.infoWindow.anchor; + final Offset newAnchor = Offset(1.0 - currentAnchor.dy, currentAnchor.dx); + setState(() { + markers[markerId] = marker.copyWith( + infoWindowParam: marker.infoWindow.copyWith( + anchorParam: newAnchor, + ), + ); + }); + } + + Future _toggleDraggable(MarkerId markerId) async { + final Marker marker = markers[markerId]!; + setState(() { + markers[markerId] = marker.copyWith( + draggableParam: !marker.draggable, + ); + }); + } + + Future _toggleFlat(MarkerId markerId) async { + final Marker marker = markers[markerId]!; + setState(() { + markers[markerId] = marker.copyWith( + flatParam: !marker.flat, + ); + }); + } + + Future _changeInfo(MarkerId markerId) async { + final Marker marker = markers[markerId]!; + final String newSnippet = '${marker.infoWindow.snippet!}*'; + setState(() { + markers[markerId] = marker.copyWith( + infoWindowParam: marker.infoWindow.copyWith( + snippetParam: newSnippet, + ), + ); + }); + } + + Future _changeAlpha(MarkerId markerId) async { + final Marker marker = markers[markerId]!; + final double current = marker.alpha; + setState(() { + markers[markerId] = marker.copyWith( + alphaParam: current < 0.1 ? 1.0 : current * 0.75, + ); + }); + } + + Future _changeRotation(MarkerId markerId) async { + final Marker marker = markers[markerId]!; + final double current = marker.rotation; + setState(() { + markers[markerId] = marker.copyWith( + rotationParam: current == 330.0 ? 0.0 : current + 30.0, + ); + }); + } + + Future _toggleVisible(MarkerId markerId) async { + final Marker marker = markers[markerId]!; + setState(() { + markers[markerId] = marker.copyWith( + visibleParam: !marker.visible, + ); + }); + } + + Future _changeZIndex(MarkerId markerId) async { + final Marker marker = markers[markerId]!; + final double current = marker.zIndex; + setState(() { + markers[markerId] = marker.copyWith( + zIndexParam: current == 12.0 ? 0.0 : current + 1.0, + ); + }); + } + + void _setMarkerIcon(MarkerId markerId, BitmapDescriptor assetIcon) { + final Marker marker = markers[markerId]!; + setState(() { + markers[markerId] = marker.copyWith( + iconParam: assetIcon, + ); + }); + } + + Future _getAssetIcon(BuildContext context) async { + final Completer bitmapIcon = + Completer(); + final ImageConfiguration config = createLocalImageConfiguration(context); + + const AssetImage('assets/red_square.png') + .resolve(config) + .addListener(ImageStreamListener((ImageInfo image, bool sync) async { + final ByteData? bytes = + await image.image.toByteData(format: ImageByteFormat.png); + if (bytes == null) { + bitmapIcon.completeError(Exception('Unable to encode icon')); + return; + } + final BitmapDescriptor bitmap = + BitmapDescriptor.fromBytes(bytes.buffer.asUint8List()); + bitmapIcon.complete(bitmap); + })); + + return await bitmapIcon.future; + } + + @override + Widget build(BuildContext context) { + final MarkerId? selectedId = selectedMarker; + return Stack(children: [ + Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded( + child: ExampleGoogleMap( + onMapCreated: _onMapCreated, + initialCameraPosition: const CameraPosition( + target: LatLng(-33.852, 151.211), + zoom: 11.0, + ), + markers: Set.of(markers.values), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + TextButton( + onPressed: _add, + child: const Text('Add'), + ), + TextButton( + onPressed: + selectedId == null ? null : () => _remove(selectedId), + child: const Text('Remove'), + ), + ], + ), + Wrap( + alignment: WrapAlignment.spaceEvenly, + children: [ + TextButton( + onPressed: + selectedId == null ? null : () => _changeInfo(selectedId), + child: const Text('change info'), + ), + TextButton( + onPressed: selectedId == null + ? null + : () => _changeInfoAnchor(selectedId), + child: const Text('change info anchor'), + ), + TextButton( + onPressed: + selectedId == null ? null : () => _changeAlpha(selectedId), + child: const Text('change alpha'), + ), + TextButton( + onPressed: + selectedId == null ? null : () => _changeAnchor(selectedId), + child: const Text('change anchor'), + ), + TextButton( + onPressed: selectedId == null + ? null + : () => _toggleDraggable(selectedId), + child: const Text('toggle draggable'), + ), + TextButton( + onPressed: + selectedId == null ? null : () => _toggleFlat(selectedId), + child: const Text('toggle flat'), + ), + TextButton( + onPressed: selectedId == null + ? null + : () => _changePosition(selectedId), + child: const Text('change position'), + ), + TextButton( + onPressed: selectedId == null + ? null + : () => _changeRotation(selectedId), + child: const Text('change rotation'), + ), + TextButton( + onPressed: selectedId == null + ? null + : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), + ), + TextButton( + onPressed: + selectedId == null ? null : () => _changeZIndex(selectedId), + child: const Text('change zIndex'), + ), + TextButton( + onPressed: selectedId == null + ? null + : () { + _getAssetIcon(context).then( + (BitmapDescriptor icon) { + _setMarkerIcon(selectedId, icon); + }, + ); + }, + child: const Text('set marker icon'), + ), + ], + ), + ], + ), + Visibility( + visible: markerPosition != null, + child: Container( + color: Colors.white70, + height: 30, + padding: const EdgeInsets.only(left: 12, right: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + mainAxisSize: MainAxisSize.max, + children: [ + if (markerPosition == null) + Container() + else + Expanded(child: Text('lat: ${markerPosition!.latitude}')), + if (markerPosition == null) + Container() + else + Expanded(child: Text('lng: ${markerPosition!.longitude}')), + ], + ), + ), + ), + ]); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_polygon.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_polygon.dart new file mode 100644 index 000000000000..b41cb5d3ccb1 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_polygon.dart @@ -0,0 +1,306 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +class PlacePolygonPage extends GoogleMapExampleAppPage { + const PlacePolygonPage({Key? key}) + : super(const Icon(Icons.linear_scale), 'Place polygon', key: key); + + @override + Widget build(BuildContext context) { + return const PlacePolygonBody(); + } +} + +class PlacePolygonBody extends StatefulWidget { + const PlacePolygonBody({Key? key}) : super(key: key); + + @override + State createState() => PlacePolygonBodyState(); +} + +class PlacePolygonBodyState extends State { + PlacePolygonBodyState(); + + ExampleGoogleMapController? controller; + Map polygons = {}; + Map polygonOffsets = {}; + int _polygonIdCounter = 0; + PolygonId? selectedPolygon; + + // Values when toggling polygon color + int strokeColorsIndex = 0; + int fillColorsIndex = 0; + List colors = [ + Colors.purple, + Colors.red, + Colors.green, + Colors.pink, + ]; + + // Values when toggling polygon width + int widthsIndex = 0; + List widths = [10, 20, 5]; + + // ignore: use_setters_to_change_properties + void _onMapCreated(ExampleGoogleMapController controller) { + this.controller = controller; + } + + @override + void dispose() { + super.dispose(); + } + + void _onPolygonTapped(PolygonId polygonId) { + setState(() { + selectedPolygon = polygonId; + }); + } + + void _remove(PolygonId polygonId) { + setState(() { + if (polygons.containsKey(polygonId)) { + polygons.remove(polygonId); + } + selectedPolygon = null; + }); + } + + void _add() { + final int polygonCount = polygons.length; + + if (polygonCount == 12) { + return; + } + + final String polygonIdVal = 'polygon_id_$_polygonIdCounter'; + final PolygonId polygonId = PolygonId(polygonIdVal); + + final Polygon polygon = Polygon( + polygonId: polygonId, + consumeTapEvents: true, + strokeColor: Colors.orange, + strokeWidth: 5, + fillColor: Colors.green, + points: _createPoints(), + onTap: () { + _onPolygonTapped(polygonId); + }, + ); + + setState(() { + polygons[polygonId] = polygon; + polygonOffsets[polygonId] = _polygonIdCounter.ceilToDouble(); + // increment _polygonIdCounter to have unique polygon id each time + _polygonIdCounter++; + }); + } + + void _toggleGeodesic(PolygonId polygonId) { + final Polygon polygon = polygons[polygonId]!; + setState(() { + polygons[polygonId] = polygon.copyWith( + geodesicParam: !polygon.geodesic, + ); + }); + } + + void _toggleVisible(PolygonId polygonId) { + final Polygon polygon = polygons[polygonId]!; + setState(() { + polygons[polygonId] = polygon.copyWith( + visibleParam: !polygon.visible, + ); + }); + } + + void _changeStrokeColor(PolygonId polygonId) { + final Polygon polygon = polygons[polygonId]!; + setState(() { + polygons[polygonId] = polygon.copyWith( + strokeColorParam: colors[++strokeColorsIndex % colors.length], + ); + }); + } + + void _changeFillColor(PolygonId polygonId) { + final Polygon polygon = polygons[polygonId]!; + setState(() { + polygons[polygonId] = polygon.copyWith( + fillColorParam: colors[++fillColorsIndex % colors.length], + ); + }); + } + + void _changeWidth(PolygonId polygonId) { + final Polygon polygon = polygons[polygonId]!; + setState(() { + polygons[polygonId] = polygon.copyWith( + strokeWidthParam: widths[++widthsIndex % widths.length], + ); + }); + } + + void _addHoles(PolygonId polygonId) { + final Polygon polygon = polygons[polygonId]!; + setState(() { + polygons[polygonId] = + polygon.copyWith(holesParam: _createHoles(polygonId)); + }); + } + + void _removeHoles(PolygonId polygonId) { + final Polygon polygon = polygons[polygonId]!; + setState(() { + polygons[polygonId] = polygon.copyWith( + holesParam: >[], + ); + }); + } + + @override + Widget build(BuildContext context) { + final PolygonId? selectedId = selectedPolygon; + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: SizedBox( + width: 350.0, + height: 300.0, + child: ExampleGoogleMap( + initialCameraPosition: const CameraPosition( + target: LatLng(52.4478, -3.5402), + zoom: 7.0, + ), + polygons: Set.of(polygons.values), + onMapCreated: _onMapCreated, + ), + ), + ), + Expanded( + child: SingleChildScrollView( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Row( + children: [ + Column( + children: [ + TextButton( + onPressed: _add, + child: const Text('add'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _remove(selectedId), + child: const Text('remove'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _toggleGeodesic(selectedId), + child: const Text('toggle geodesic'), + ), + ], + ), + Column( + children: [ + TextButton( + onPressed: (selectedId == null) + ? null + : ((polygons[selectedId]!.holes.isNotEmpty) + ? null + : () => _addHoles(selectedId)), + child: const Text('add holes'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : ((polygons[selectedId]!.holes.isEmpty) + ? null + : () => _removeHoles(selectedId)), + child: const Text('remove holes'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _changeWidth(selectedId), + child: const Text('change stroke width'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _changeStrokeColor(selectedId), + child: const Text('change stroke color'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _changeFillColor(selectedId), + child: const Text('change fill color'), + ), + ], + ) + ], + ) + ], + ), + ), + ), + ], + ); + } + + List _createPoints() { + final List points = []; + final double offset = _polygonIdCounter.ceilToDouble(); + points.add(_createLatLng(51.2395 + offset, -3.4314)); + points.add(_createLatLng(53.5234 + offset, -3.5314)); + points.add(_createLatLng(52.4351 + offset, -4.5235)); + points.add(_createLatLng(52.1231 + offset, -5.0829)); + return points; + } + + List> _createHoles(PolygonId polygonId) { + final List> holes = >[]; + final double offset = polygonOffsets[polygonId]!; + + final List hole1 = []; + hole1.add(_createLatLng(51.8395 + offset, -3.8814)); + hole1.add(_createLatLng(52.0234 + offset, -3.9914)); + hole1.add(_createLatLng(52.1351 + offset, -4.4435)); + hole1.add(_createLatLng(52.0231 + offset, -4.5829)); + holes.add(hole1); + + final List hole2 = []; + hole2.add(_createLatLng(52.2395 + offset, -3.6814)); + hole2.add(_createLatLng(52.4234 + offset, -3.7914)); + hole2.add(_createLatLng(52.5351 + offset, -4.2435)); + hole2.add(_createLatLng(52.4231 + offset, -4.3829)); + holes.add(hole2); + + return holes; + } + + LatLng _createLatLng(double lat, double lng) { + return LatLng(lat, lng); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_polyline.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_polyline.dart new file mode 100644 index 000000000000..004206b9f6cc --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_polyline.dart @@ -0,0 +1,325 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +class PlacePolylinePage extends GoogleMapExampleAppPage { + const PlacePolylinePage({Key? key}) + : super(const Icon(Icons.linear_scale), 'Place polyline', key: key); + + @override + Widget build(BuildContext context) { + return const PlacePolylineBody(); + } +} + +class PlacePolylineBody extends StatefulWidget { + const PlacePolylineBody({Key? key}) : super(key: key); + + @override + State createState() => PlacePolylineBodyState(); +} + +class PlacePolylineBodyState extends State { + PlacePolylineBodyState(); + + ExampleGoogleMapController? controller; + Map polylines = {}; + int _polylineIdCounter = 0; + PolylineId? selectedPolyline; + + // Values when toggling polyline color + int colorsIndex = 0; + List colors = [ + Colors.purple, + Colors.red, + Colors.green, + Colors.pink, + ]; + + // Values when toggling polyline width + int widthsIndex = 0; + List widths = [10, 20, 5]; + + int jointTypesIndex = 0; + List jointTypes = [ + JointType.mitered, + JointType.bevel, + JointType.round + ]; + + // Values when toggling polyline end cap type + int endCapsIndex = 0; + List endCaps = [Cap.buttCap, Cap.squareCap, Cap.roundCap]; + + // Values when toggling polyline start cap type + int startCapsIndex = 0; + List startCaps = [Cap.buttCap, Cap.squareCap, Cap.roundCap]; + + // Values when toggling polyline pattern + int patternsIndex = 0; + List> patterns = >[ + [], + [ + PatternItem.dash(30.0), + PatternItem.gap(20.0), + PatternItem.dot, + PatternItem.gap(20.0) + ], + [PatternItem.dash(30.0), PatternItem.gap(20.0)], + [PatternItem.dot, PatternItem.gap(10.0)], + ]; + + // ignore: use_setters_to_change_properties + void _onMapCreated(ExampleGoogleMapController controller) { + this.controller = controller; + } + + @override + void dispose() { + super.dispose(); + } + + void _onPolylineTapped(PolylineId polylineId) { + setState(() { + selectedPolyline = polylineId; + }); + } + + void _remove(PolylineId polylineId) { + setState(() { + if (polylines.containsKey(polylineId)) { + polylines.remove(polylineId); + } + selectedPolyline = null; + }); + } + + void _add() { + final int polylineCount = polylines.length; + + if (polylineCount == 12) { + return; + } + + final String polylineIdVal = 'polyline_id_$_polylineIdCounter'; + _polylineIdCounter++; + final PolylineId polylineId = PolylineId(polylineIdVal); + + final Polyline polyline = Polyline( + polylineId: polylineId, + consumeTapEvents: true, + color: Colors.orange, + width: 5, + points: _createPoints(), + onTap: () { + _onPolylineTapped(polylineId); + }, + ); + + setState(() { + polylines[polylineId] = polyline; + }); + } + + void _toggleGeodesic(PolylineId polylineId) { + final Polyline polyline = polylines[polylineId]!; + setState(() { + polylines[polylineId] = polyline.copyWith( + geodesicParam: !polyline.geodesic, + ); + }); + } + + void _toggleVisible(PolylineId polylineId) { + final Polyline polyline = polylines[polylineId]!; + setState(() { + polylines[polylineId] = polyline.copyWith( + visibleParam: !polyline.visible, + ); + }); + } + + void _changeColor(PolylineId polylineId) { + final Polyline polyline = polylines[polylineId]!; + setState(() { + polylines[polylineId] = polyline.copyWith( + colorParam: colors[++colorsIndex % colors.length], + ); + }); + } + + void _changeWidth(PolylineId polylineId) { + final Polyline polyline = polylines[polylineId]!; + setState(() { + polylines[polylineId] = polyline.copyWith( + widthParam: widths[++widthsIndex % widths.length], + ); + }); + } + + void _changeJointType(PolylineId polylineId) { + final Polyline polyline = polylines[polylineId]!; + setState(() { + polylines[polylineId] = polyline.copyWith( + jointTypeParam: jointTypes[++jointTypesIndex % jointTypes.length], + ); + }); + } + + void _changeEndCap(PolylineId polylineId) { + final Polyline polyline = polylines[polylineId]!; + setState(() { + polylines[polylineId] = polyline.copyWith( + endCapParam: endCaps[++endCapsIndex % endCaps.length], + ); + }); + } + + void _changeStartCap(PolylineId polylineId) { + final Polyline polyline = polylines[polylineId]!; + setState(() { + polylines[polylineId] = polyline.copyWith( + startCapParam: startCaps[++startCapsIndex % startCaps.length], + ); + }); + } + + void _changePattern(PolylineId polylineId) { + final Polyline polyline = polylines[polylineId]!; + setState(() { + polylines[polylineId] = polyline.copyWith( + patternsParam: patterns[++patternsIndex % patterns.length], + ); + }); + } + + @override + Widget build(BuildContext context) { + final bool isIOS = !kIsWeb && defaultTargetPlatform == TargetPlatform.iOS; + + final PolylineId? selectedId = selectedPolyline; + + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: SizedBox( + width: 350.0, + height: 300.0, + child: ExampleGoogleMap( + initialCameraPosition: const CameraPosition( + target: LatLng(53.1721, -3.5402), + zoom: 7.0, + ), + polylines: Set.of(polylines.values), + onMapCreated: _onMapCreated, + ), + ), + ), + Expanded( + child: SingleChildScrollView( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Row( + children: [ + Column( + children: [ + TextButton( + onPressed: _add, + child: const Text('add'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _remove(selectedId), + child: const Text('remove'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _toggleGeodesic(selectedId), + child: const Text('toggle geodesic'), + ), + ], + ), + Column( + children: [ + TextButton( + onPressed: (selectedId == null) + ? null + : () => _changeWidth(selectedId), + child: const Text('change width'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _changeColor(selectedId), + child: const Text('change color'), + ), + TextButton( + onPressed: isIOS || (selectedId == null) + ? null + : () => _changeStartCap(selectedId), + child: const Text('change start cap [Android only]'), + ), + TextButton( + onPressed: isIOS || (selectedId == null) + ? null + : () => _changeEndCap(selectedId), + child: const Text('change end cap [Android only]'), + ), + TextButton( + onPressed: isIOS || (selectedId == null) + ? null + : () => _changeJointType(selectedId), + child: const Text('change joint type [Android only]'), + ), + TextButton( + onPressed: isIOS || (selectedId == null) + ? null + : () => _changePattern(selectedId), + child: const Text('change pattern [Android only]'), + ), + ], + ) + ], + ) + ], + ), + ), + ), + ], + ); + } + + List _createPoints() { + final List points = []; + final double offset = _polylineIdCounter.ceilToDouble(); + points.add(_createLatLng(51.4816 + offset, -3.1791)); + points.add(_createLatLng(53.0430 + offset, -2.9925)); + points.add(_createLatLng(53.1396 + offset, -4.2739)); + points.add(_createLatLng(52.4153 + offset, -4.0829)); + return points; + } + + LatLng _createLatLng(double lat, double lng) { + return LatLng(lat, lng); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/scrolling_map.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/scrolling_map.dart new file mode 100644 index 000000000000..7a9b75cd1224 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/scrolling_map.dart @@ -0,0 +1,114 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +const LatLng _center = LatLng(32.080664, 34.9563837); + +class ScrollingMapPage extends GoogleMapExampleAppPage { + const ScrollingMapPage({Key? key}) + : super(const Icon(Icons.map), 'Scrolling map', key: key); + + @override + Widget build(BuildContext context) { + return const ScrollingMapBody(); + } +} + +class ScrollingMapBody extends StatelessWidget { + const ScrollingMapBody({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ListView( + children: [ + Card( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 30.0), + child: Column( + children: [ + const Padding( + padding: EdgeInsets.only(bottom: 12.0), + child: Text('This map consumes all touch events.'), + ), + Center( + child: SizedBox( + width: 300.0, + height: 300.0, + child: ExampleGoogleMap( + initialCameraPosition: const CameraPosition( + target: _center, + zoom: 11.0, + ), + gestureRecognizers: // + >{ + Factory( + () => EagerGestureRecognizer(), + ), + }, + ), + ), + ), + ], + ), + ), + ), + Card( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 30.0), + child: Column( + children: [ + const Text("This map doesn't consume the vertical drags."), + const Padding( + padding: EdgeInsets.only(bottom: 12.0), + child: + Text('It still gets other gestures (e.g scale or tap).'), + ), + Center( + child: SizedBox( + width: 300.0, + height: 300.0, + child: ExampleGoogleMap( + initialCameraPosition: const CameraPosition( + target: _center, + zoom: 11.0, + ), + markers: { + Marker( + markerId: const MarkerId('test_marker_id'), + position: LatLng( + _center.latitude, + _center.longitude, + ), + infoWindow: const InfoWindow( + title: 'An interesting location', + snippet: '*', + ), + ), + }, + gestureRecognizers: < + Factory>{ + Factory( + () => ScaleGestureRecognizer(), + ), + }, + ), + ), + ), + ], + ), + ), + ), + ], + ); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/snapshot.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/snapshot.dart new file mode 100644 index 000000000000..56a90a8e49f6 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/snapshot.dart @@ -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. + +// ignore_for_file: public_member_api_docs + +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +const CameraPosition _kInitialPosition = + CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); + +class SnapshotPage extends GoogleMapExampleAppPage { + const SnapshotPage({Key? key}) + : super(const Icon(Icons.camera_alt), 'Take a snapshot of the map', + key: key); + + @override + Widget build(BuildContext context) { + return _SnapshotBody(); + } +} + +class _SnapshotBody extends StatefulWidget { + @override + _SnapshotBodyState createState() => _SnapshotBodyState(); +} + +class _SnapshotBodyState extends State<_SnapshotBody> { + ExampleGoogleMapController? _mapController; + Uint8List? _imageBytes; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SizedBox( + height: 180, + child: ExampleGoogleMap( + onMapCreated: onMapCreated, + initialCameraPosition: _kInitialPosition, + ), + ), + TextButton( + child: const Text('Take a snapshot'), + onPressed: () async { + final Uint8List? imageBytes = + await _mapController?.takeSnapshot(); + setState(() { + _imageBytes = imageBytes; + }); + }, + ), + Container( + decoration: BoxDecoration(color: Colors.blueGrey[50]), + height: 180, + child: _imageBytes != null ? Image.memory(_imageBytes!) : null, + ), + ], + ), + ); + } + + // ignore: use_setters_to_change_properties + void onMapCreated(ExampleGoogleMapController controller) { + _mapController = controller; + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/tile_overlay.dart new file mode 100644 index 000000000000..555f037b474a --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/tile_overlay.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. + +// ignore_for_file: public_member_api_docs + +import 'dart:typed_data'; +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +class TileOverlayPage extends GoogleMapExampleAppPage { + const TileOverlayPage({Key? key}) + : super(const Icon(Icons.map), 'Tile overlay', key: key); + + @override + Widget build(BuildContext context) { + return const TileOverlayBody(); + } +} + +class TileOverlayBody extends StatefulWidget { + const TileOverlayBody({Key? key}) : super(key: key); + + @override + State createState() => TileOverlayBodyState(); +} + +class TileOverlayBodyState extends State { + TileOverlayBodyState(); + + ExampleGoogleMapController? controller; + TileOverlay? _tileOverlay; + + // ignore: use_setters_to_change_properties + void _onMapCreated(ExampleGoogleMapController controller) { + this.controller = controller; + } + + @override + void dispose() { + super.dispose(); + } + + void _removeTileOverlay() { + setState(() { + _tileOverlay = null; + }); + } + + void _addTileOverlay() { + final TileOverlay tileOverlay = TileOverlay( + tileOverlayId: const TileOverlayId('tile_overlay_1'), + tileProvider: _DebugTileProvider(), + ); + setState(() { + _tileOverlay = tileOverlay; + }); + } + + void _clearTileCache() { + if (_tileOverlay != null && controller != null) { + controller!.clearTileCache(_tileOverlay!.tileOverlayId); + } + } + + @override + Widget build(BuildContext context) { + final Set overlays = { + if (_tileOverlay != null) _tileOverlay!, + }; + return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: SizedBox( + width: 350.0, + height: 300.0, + child: ExampleGoogleMap( + initialCameraPosition: const CameraPosition( + target: LatLng(59.935460, 30.325177), + zoom: 7.0, + ), + tileOverlays: overlays, + onMapCreated: _onMapCreated, + ), + ), + ), + TextButton( + onPressed: _addTileOverlay, + child: const Text('Add tile overlay'), + ), + TextButton( + onPressed: _removeTileOverlay, + child: const Text('Remove tile overlay'), + ), + TextButton( + onPressed: _clearTileCache, + child: const Text('Clear tile cache'), + ), + ], + ); + } +} + +class _DebugTileProvider implements TileProvider { + _DebugTileProvider() { + boxPaint.isAntiAlias = true; + boxPaint.color = Colors.blue; + boxPaint.strokeWidth = 2.0; + boxPaint.style = PaintingStyle.stroke; + } + + static const int width = 100; + static const int height = 100; + static final Paint boxPaint = Paint(); + static const TextStyle textStyle = TextStyle( + color: Colors.red, + fontSize: 20, + ); + + @override + Future getTile(int x, int y, int? zoom) async { + final ui.PictureRecorder recorder = ui.PictureRecorder(); + final Canvas canvas = Canvas(recorder); + final TextSpan textSpan = TextSpan( + text: '$x,$y', + style: textStyle, + ); + final TextPainter textPainter = TextPainter( + text: textSpan, + textDirection: TextDirection.ltr, + ); + textPainter.layout( + minWidth: 0.0, + maxWidth: width.toDouble(), + ); + const Offset offset = Offset(0, 0); + textPainter.paint(canvas, offset); + canvas.drawRect( + Rect.fromLTRB(0, 0, width.toDouble(), width.toDouble()), boxPaint); + final ui.Picture picture = recorder.endRecording(); + final Uint8List byteData = await picture + .toImage(width, height) + .then((ui.Image image) => + image.toByteData(format: ui.ImageByteFormat.png)) + .then((ByteData? byteData) => byteData!.buffer.asUint8List()); + return Tile(width, height, byteData); + } +} 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 new file mode 100644 index 000000000000..fc1059bbdcd5 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/pubspec.yaml @@ -0,0 +1,33 @@ +name: google_maps_flutter_example +description: Demonstrates how to use the google_maps_flutter plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +dependencies: + cupertino_icons: ^0.1.0 + flutter: + sdk: flutter + flutter_plugin_android_lifecycle: ^2.0.1 + google_maps_flutter_android: + # When depending on this package from a real application you should use: + # google_maps_flutter_android: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # 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.2.1 + +dev_dependencies: + espresso: ^0.1.0+2 + flutter_driver: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true + assets: + - assets/ diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/test_driver/integration_test.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/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/google_maps_flutter/google_maps_flutter_android/lib/google_maps_flutter_android.dart b/packages/google_maps_flutter/google_maps_flutter_android/lib/google_maps_flutter_android.dart new file mode 100644 index 000000000000..edd231efc691 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/lib/google_maps_flutter_android.dart @@ -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. + +export 'src/google_maps_flutter_android.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_map_inspector_android.dart similarity index 67% rename from packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart rename to packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_map_inspector_android.dart index 95ca969a51e2..4e0cad78e869 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_map_inspector.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_map_inspector_android.dart @@ -2,66 +2,59 @@ // 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/services.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; -/// A method-channel-based implementation of [GoogleMapsInspectorPlatform], for -/// use in tests in conjunction with [MethodChannelGoogleMapsFlutter]. -// TODO(stuartmorgan): Move this into the platform implementations when -// federating the mobile implementations. -class MethodChannelGoogleMapsInspector extends GoogleMapsInspectorPlatform { +/// An Android of implementation of [GoogleMapsInspectorPlatform]. +@visibleForTesting +class GoogleMapsInspectorAndroid extends GoogleMapsInspectorPlatform { /// Creates a method-channel-based inspector instance that gets the channel - /// for a given map ID from [mapsPlatform]. - MethodChannelGoogleMapsInspector(MethodChannelGoogleMapsFlutter mapsPlatform) - : _mapsPlatform = mapsPlatform; + /// for a given map ID from [channelProvider]. + GoogleMapsInspectorAndroid(MethodChannel? Function(int mapId) channelProvider) + : _channelProvider = channelProvider; - final MethodChannelGoogleMapsFlutter _mapsPlatform; + final MethodChannel? Function(int mapId) _channelProvider; @override Future areBuildingsEnabled({required int mapId}) async { - return (await _mapsPlatform - .channel(mapId) + return (await _channelProvider(mapId)! .invokeMethod('map#isBuildingsEnabled'))!; } @override Future areRotateGesturesEnabled({required int mapId}) async { - return (await _mapsPlatform - .channel(mapId) + return (await _channelProvider(mapId)! .invokeMethod('map#isRotateGesturesEnabled'))!; } @override Future areScrollGesturesEnabled({required int mapId}) async { - return (await _mapsPlatform - .channel(mapId) + return (await _channelProvider(mapId)! .invokeMethod('map#isScrollGesturesEnabled'))!; } @override Future areTiltGesturesEnabled({required int mapId}) async { - return (await _mapsPlatform - .channel(mapId) + return (await _channelProvider(mapId)! .invokeMethod('map#isTiltGesturesEnabled'))!; } @override Future areZoomControlsEnabled({required int mapId}) async { - return (await _mapsPlatform - .channel(mapId) + return (await _channelProvider(mapId)! .invokeMethod('map#isZoomControlsEnabled'))!; } @override Future areZoomGesturesEnabled({required int mapId}) async { - return (await _mapsPlatform - .channel(mapId) + return (await _channelProvider(mapId)! .invokeMethod('map#isZoomGesturesEnabled'))!; } @override Future getMinMaxZoomLevels({required int mapId}) async { - final List zoomLevels = (await _mapsPlatform - .channel(mapId) + final List zoomLevels = (await _channelProvider(mapId)! .invokeMethod>('map#getMinMaxZoomLevels'))! .cast(); return MinMaxZoomPreference(zoomLevels[0], zoomLevels[1]); @@ -70,8 +63,7 @@ class MethodChannelGoogleMapsInspector extends GoogleMapsInspectorPlatform { @override Future getTileOverlayInfo(TileOverlayId tileOverlayId, {required int mapId}) async { - final Map? tileInfo = await _mapsPlatform - .channel(mapId) + final Map? tileInfo = await _channelProvider(mapId)! .invokeMapMethod( 'map#getTileOverlayInfo', { 'tileOverlayId': tileOverlayId.value, @@ -91,36 +83,31 @@ class MethodChannelGoogleMapsInspector extends GoogleMapsInspectorPlatform { @override Future isCompassEnabled({required int mapId}) async { - return (await _mapsPlatform - .channel(mapId) + return (await _channelProvider(mapId)! .invokeMethod('map#isCompassEnabled'))!; } @override Future isLiteModeEnabled({required int mapId}) async { - return (await _mapsPlatform - .channel(mapId) + return (await _channelProvider(mapId)! .invokeMethod('map#isLiteModeEnabled'))!; } @override Future isMapToolbarEnabled({required int mapId}) async { - return (await _mapsPlatform - .channel(mapId) + return (await _channelProvider(mapId)! .invokeMethod('map#isMapToolbarEnabled'))!; } @override Future isMyLocationButtonEnabled({required int mapId}) async { - return (await _mapsPlatform - .channel(mapId) + return (await _channelProvider(mapId)! .invokeMethod('map#isMyLocationButtonEnabled'))!; } @override Future isTrafficEnabled({required int mapId}) async { - return (await _mapsPlatform - .channel(mapId) + return (await _channelProvider(mapId)! .invokeMethod('map#isTrafficEnabled'))!; } } 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 new file mode 100644 index 000000000000..2c11053baa68 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/lib/src/google_maps_flutter_android.dart @@ -0,0 +1,696 @@ +// 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:async'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import +import 'dart:typed_data'; + +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:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'package:stream_transform/stream_transform.dart'; + +import 'google_map_inspector_android.dart'; + +// TODO(stuartmorgan): Remove the dependency on platform interface toJson +// methods. Channel serialization details should all be package-internal. + +/// Error thrown when an unknown map ID is provided to a method channel API. +class UnknownMapIDError extends Error { + /// Creates an assertion error with the provided [mapId] and optional + /// [message]. + UnknownMapIDError(this.mapId, [this.message]); + + /// The unknown ID. + final int mapId; + + /// Message describing the assertion error. + final Object? message; + + @override + String toString() { + if (message != null) { + return 'Unknown map ID $mapId: ${Error.safeToString(message)}'; + } + return 'Unknown map ID $mapId'; + } +} + +/// An implementation of [GoogleMapsFlutterPlatform] for Android. +class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { + /// Registers the Android implementation of GoogleMapsFlutterPlatform. + static void registerWith() { + GoogleMapsFlutterPlatform.instance = GoogleMapsFlutterAndroid(); + } + + // Keep a collection of id -> channel + // Every method call passes the int mapId + final Map _channels = {}; + + /// Accesses the MethodChannel associated to the passed mapId. + MethodChannel _channel(int mapId) { + final MethodChannel? channel = _channels[mapId]; + if (channel == null) { + throw UnknownMapIDError(mapId); + } + return channel; + } + + // Keep a collection of mapId to a map of TileOverlays. + final Map> _tileOverlays = + >{}; + + /// Returns the channel for [mapId], creating it if it doesn't already exist. + @visibleForTesting + MethodChannel ensureChannelInitialized(int mapId) { + MethodChannel? channel = _channels[mapId]; + if (channel == null) { + channel = MethodChannel('plugins.flutter.dev/google_maps_android_$mapId'); + channel.setMethodCallHandler( + (MethodCall call) => _handleMethodCall(call, mapId)); + _channels[mapId] = channel; + } + return channel; + } + + @override + Future init(int mapId) { + final MethodChannel channel = ensureChannelInitialized(mapId); + return channel.invokeMethod('map#waitForMap'); + } + + @override + void dispose({required int mapId}) { + // Noop! + } + + // The controller we need to broadcast the different events coming + // from handleMethodCall. + // + // It is a `broadcast` because multiple controllers will connect to + // different stream views of this Controller. + final StreamController> _mapEventStreamController = + StreamController>.broadcast(); + + // Returns a filtered view of the events in the _controller, by mapId. + Stream> _events(int mapId) => + _mapEventStreamController.stream + .where((MapEvent event) => event.mapId == mapId); + + @override + Stream onCameraMoveStarted({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onCameraMove({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onCameraIdle({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onMarkerTap({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onInfoWindowTap({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onMarkerDragStart({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onMarkerDrag({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onMarkerDragEnd({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onPolylineTap({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onPolygonTap({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onCircleTap({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onTap({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onLongPress({required int mapId}) { + return _events(mapId).whereType(); + } + + Future _handleMethodCall(MethodCall call, int mapId) async { + switch (call.method) { + case 'camera#onMoveStarted': + _mapEventStreamController.add(CameraMoveStartedEvent(mapId)); + break; + case 'camera#onMove': + _mapEventStreamController.add(CameraMoveEvent( + mapId, + CameraPosition.fromMap(call.arguments['position'])!, + )); + break; + case 'camera#onIdle': + _mapEventStreamController.add(CameraIdleEvent(mapId)); + break; + case 'marker#onTap': + _mapEventStreamController.add(MarkerTapEvent( + mapId, + MarkerId(call.arguments['markerId'] as String), + )); + break; + case 'marker#onDragStart': + _mapEventStreamController.add(MarkerDragStartEvent( + mapId, + LatLng.fromJson(call.arguments['position'])!, + MarkerId(call.arguments['markerId'] as String), + )); + break; + case 'marker#onDrag': + _mapEventStreamController.add(MarkerDragEvent( + mapId, + LatLng.fromJson(call.arguments['position'])!, + MarkerId(call.arguments['markerId'] as String), + )); + break; + case 'marker#onDragEnd': + _mapEventStreamController.add(MarkerDragEndEvent( + mapId, + LatLng.fromJson(call.arguments['position'])!, + MarkerId(call.arguments['markerId'] as String), + )); + break; + case 'infoWindow#onTap': + _mapEventStreamController.add(InfoWindowTapEvent( + mapId, + MarkerId(call.arguments['markerId'] as String), + )); + break; + case 'polyline#onTap': + _mapEventStreamController.add(PolylineTapEvent( + mapId, + PolylineId(call.arguments['polylineId'] as String), + )); + break; + case 'polygon#onTap': + _mapEventStreamController.add(PolygonTapEvent( + mapId, + PolygonId(call.arguments['polygonId'] as String), + )); + break; + case 'circle#onTap': + _mapEventStreamController.add(CircleTapEvent( + mapId, + CircleId(call.arguments['circleId'] as String), + )); + break; + case 'map#onTap': + _mapEventStreamController.add(MapTapEvent( + mapId, + LatLng.fromJson(call.arguments['position'])!, + )); + break; + case 'map#onLongPress': + _mapEventStreamController.add(MapLongPressEvent( + mapId, + LatLng.fromJson(call.arguments['position'])!, + )); + break; + case 'tileOverlay#getTile': + final Map? tileOverlaysForThisMap = + _tileOverlays[mapId]; + final String tileOverlayId = call.arguments['tileOverlayId'] as String; + final TileOverlay? tileOverlay = + tileOverlaysForThisMap?[TileOverlayId(tileOverlayId)]; + final TileProvider? tileProvider = tileOverlay?.tileProvider; + if (tileProvider == null) { + return TileProvider.noTile.toJson(); + } + final Tile tile = await tileProvider.getTile( + call.arguments['x'] as int, + call.arguments['y'] as int, + call.arguments['zoom'] as int?, + ); + return tile.toJson(); + default: + throw MissingPluginException(); + } + } + + @override + Future updateMapOptions( + Map optionsUpdate, { + required int mapId, + }) { + assert(optionsUpdate != null); + return _channel(mapId).invokeMethod( + 'map#update', + { + 'options': optionsUpdate, + }, + ); + } + + @override + Future updateMarkers( + MarkerUpdates markerUpdates, { + required int mapId, + }) { + assert(markerUpdates != null); + return _channel(mapId).invokeMethod( + 'markers#update', + markerUpdates.toJson(), + ); + } + + @override + Future updatePolygons( + PolygonUpdates polygonUpdates, { + required int mapId, + }) { + assert(polygonUpdates != null); + return _channel(mapId).invokeMethod( + 'polygons#update', + polygonUpdates.toJson(), + ); + } + + @override + Future updatePolylines( + PolylineUpdates polylineUpdates, { + required int mapId, + }) { + assert(polylineUpdates != null); + return _channel(mapId).invokeMethod( + 'polylines#update', + polylineUpdates.toJson(), + ); + } + + @override + Future updateCircles( + CircleUpdates circleUpdates, { + required int mapId, + }) { + assert(circleUpdates != null); + return _channel(mapId).invokeMethod( + 'circles#update', + circleUpdates.toJson(), + ); + } + + @override + Future updateTileOverlays({ + required Set newTileOverlays, + required int mapId, + }) { + final Map? currentTileOverlays = + _tileOverlays[mapId]; + final Set previousSet = currentTileOverlays != null + ? currentTileOverlays.values.toSet() + : {}; + final _TileOverlayUpdates updates = + _TileOverlayUpdates.from(previousSet, newTileOverlays); + _tileOverlays[mapId] = keyTileOverlayId(newTileOverlays); + return _channel(mapId).invokeMethod( + 'tileOverlays#update', + updates.toJson(), + ); + } + + @override + Future clearTileCache( + TileOverlayId tileOverlayId, { + required int mapId, + }) { + return _channel(mapId) + .invokeMethod('tileOverlays#clearTileCache', { + 'tileOverlayId': tileOverlayId.value, + }); + } + + @override + Future animateCamera( + CameraUpdate cameraUpdate, { + required int mapId, + }) { + return _channel(mapId) + .invokeMethod('camera#animate', { + 'cameraUpdate': cameraUpdate.toJson(), + }); + } + + @override + Future moveCamera( + CameraUpdate cameraUpdate, { + required int mapId, + }) { + return _channel(mapId).invokeMethod('camera#move', { + 'cameraUpdate': cameraUpdate.toJson(), + }); + } + + @override + Future setMapStyle( + String? mapStyle, { + required int mapId, + }) async { + final List successAndError = (await _channel(mapId) + .invokeMethod>('map#setStyle', mapStyle))!; + final bool success = successAndError[0] as bool; + if (!success) { + throw MapStyleException(successAndError[1] as String); + } + } + + @override + Future getVisibleRegion({ + required int mapId, + }) async { + final Map latLngBounds = (await _channel(mapId) + .invokeMapMethod('map#getVisibleRegion'))!; + final LatLng southwest = LatLng.fromJson(latLngBounds['southwest'])!; + final LatLng northeast = LatLng.fromJson(latLngBounds['northeast'])!; + + return LatLngBounds(northeast: northeast, southwest: southwest); + } + + @override + Future getScreenCoordinate( + LatLng latLng, { + required int mapId, + }) async { + final Map point = (await _channel(mapId) + .invokeMapMethod( + 'map#getScreenCoordinate', latLng.toJson()))!; + + return ScreenCoordinate(x: point['x']!, y: point['y']!); + } + + @override + Future getLatLng( + ScreenCoordinate screenCoordinate, { + required int mapId, + }) async { + final List latLng = (await _channel(mapId) + .invokeMethod>( + 'map#getLatLng', screenCoordinate.toJson()))!; + return LatLng(latLng[0] as double, latLng[1] as double); + } + + @override + Future showMarkerInfoWindow( + MarkerId markerId, { + required int mapId, + }) { + assert(markerId != null); + return _channel(mapId).invokeMethod( + 'markers#showInfoWindow', {'markerId': markerId.value}); + } + + @override + Future hideMarkerInfoWindow( + MarkerId markerId, { + required int mapId, + }) { + assert(markerId != null); + return _channel(mapId).invokeMethod( + 'markers#hideInfoWindow', {'markerId': markerId.value}); + } + + @override + Future isMarkerInfoWindowShown( + MarkerId markerId, { + required int mapId, + }) async { + assert(markerId != null); + return (await _channel(mapId).invokeMethod( + 'markers#isInfoWindowShown', + {'markerId': markerId.value}))!; + } + + @override + Future getZoomLevel({ + required int mapId, + }) async { + return (await _channel(mapId).invokeMethod('map#getZoomLevel'))!; + } + + @override + Future takeSnapshot({ + required int mapId, + }) { + return _channel(mapId).invokeMethod('map#takeSnapshot'); + } + + /// Set [GoogleMapsFlutterPlatform] to use [AndroidViewSurface] to build the Google Maps widget. + /// + /// This implementation uses hybrid composition to render the Google Maps + /// Widget on Android. This comes at the cost of some performance on Android + /// versions below 10. See + /// https://flutter.dev/docs/development/platform-integration/platform-views#performance for more + /// information. + /// + /// If set to true, the google map widget should be built with + /// [buildViewWithTextDirection] instead of [buildView]. + /// + /// Defaults to false. + bool useAndroidViewSurface = false; + + Widget _buildView( + int creationId, + PlatformViewCreatedCallback onPlatformViewCreated, { + required MapWidgetConfiguration widgetConfiguration, + MapObjects mapObjects = const MapObjects(), + Map mapOptions = const {}, + }) { + final Map creationParams = { + 'initialCameraPosition': + widgetConfiguration.initialCameraPosition.toMap(), + 'options': mapOptions, + 'markersToAdd': serializeMarkerSet(mapObjects.markers), + 'polygonsToAdd': serializePolygonSet(mapObjects.polygons), + 'polylinesToAdd': serializePolylineSet(mapObjects.polylines), + 'circlesToAdd': serializeCircleSet(mapObjects.circles), + 'tileOverlaysToAdd': serializeTileOverlaySet(mapObjects.tileOverlays), + }; + + const String viewType = 'plugins.flutter.dev/google_maps_android'; + if (useAndroidViewSurface) { + return PlatformViewLink( + viewType: viewType, + surfaceFactory: ( + BuildContext context, + PlatformViewController controller, + ) { + return AndroidViewSurface( + controller: controller as AndroidViewController, + gestureRecognizers: widgetConfiguration.gestureRecognizers, + hitTestBehavior: PlatformViewHitTestBehavior.opaque, + ); + }, + onCreatePlatformView: (PlatformViewCreationParams params) { + final SurfaceAndroidViewController controller = + PlatformViewsService.initSurfaceAndroidView( + id: params.id, + viewType: viewType, + layoutDirection: widgetConfiguration.textDirection, + creationParams: creationParams, + creationParamsCodec: const StandardMessageCodec(), + onFocus: () => params.onFocusChanged(true), + ); + controller.addOnPlatformViewCreatedListener( + params.onPlatformViewCreated, + ); + controller.addOnPlatformViewCreatedListener( + onPlatformViewCreated, + ); + + controller.create(); + return controller; + }, + ); + } else { + return AndroidView( + viewType: viewType, + onPlatformViewCreated: onPlatformViewCreated, + gestureRecognizers: widgetConfiguration.gestureRecognizers, + creationParams: creationParams, + creationParamsCodec: const StandardMessageCodec(), + ); + } + } + + @override + Widget buildViewWithConfiguration( + int creationId, + PlatformViewCreatedCallback onPlatformViewCreated, { + required MapWidgetConfiguration widgetConfiguration, + MapConfiguration mapConfiguration = const MapConfiguration(), + MapObjects mapObjects = const MapObjects(), + }) { + return _buildView( + creationId, + onPlatformViewCreated, + widgetConfiguration: widgetConfiguration, + mapObjects: mapObjects, + mapOptions: _jsonForMapConfiguration(mapConfiguration), + ); + } + + @override + Widget buildViewWithTextDirection( + int creationId, + PlatformViewCreatedCallback onPlatformViewCreated, { + required CameraPosition initialCameraPosition, + required TextDirection textDirection, + Set markers = const {}, + Set polygons = const {}, + Set polylines = const {}, + Set circles = const {}, + Set tileOverlays = const {}, + Set>? gestureRecognizers, + Map mapOptions = const {}, + }) { + return _buildView( + creationId, + onPlatformViewCreated, + widgetConfiguration: MapWidgetConfiguration( + initialCameraPosition: initialCameraPosition, + textDirection: textDirection), + mapObjects: MapObjects( + markers: markers, + polygons: polygons, + polylines: polylines, + circles: circles, + tileOverlays: tileOverlays), + mapOptions: mapOptions, + ); + } + + @override + Widget buildView( + int creationId, + PlatformViewCreatedCallback onPlatformViewCreated, { + required CameraPosition initialCameraPosition, + Set markers = const {}, + Set polygons = const {}, + Set polylines = const {}, + Set circles = const {}, + Set tileOverlays = const {}, + Set>? gestureRecognizers, + Map mapOptions = const {}, + }) { + return buildViewWithTextDirection( + creationId, + onPlatformViewCreated, + initialCameraPosition: initialCameraPosition, + textDirection: TextDirection.ltr, + markers: markers, + polygons: polygons, + polylines: polylines, + circles: circles, + tileOverlays: tileOverlays, + gestureRecognizers: gestureRecognizers, + mapOptions: mapOptions, + ); + } + + @override + @visibleForTesting + void enableDebugInspection() { + GoogleMapsInspectorPlatform.instance = + GoogleMapsInspectorAndroid((int mapId) => _channel(mapId)); + } +} + +Map _jsonForMapConfiguration(MapConfiguration config) { + final EdgeInsets? padding = config.padding; + return { + if (config.compassEnabled != null) 'compassEnabled': config.compassEnabled!, + if (config.mapToolbarEnabled != null) + 'mapToolbarEnabled': config.mapToolbarEnabled!, + if (config.cameraTargetBounds != null) + 'cameraTargetBounds': config.cameraTargetBounds!.toJson(), + if (config.mapType != null) 'mapType': config.mapType!.index, + if (config.minMaxZoomPreference != null) + 'minMaxZoomPreference': config.minMaxZoomPreference!.toJson(), + if (config.rotateGesturesEnabled != null) + 'rotateGesturesEnabled': config.rotateGesturesEnabled!, + if (config.scrollGesturesEnabled != null) + 'scrollGesturesEnabled': config.scrollGesturesEnabled!, + if (config.tiltGesturesEnabled != null) + 'tiltGesturesEnabled': config.tiltGesturesEnabled!, + if (config.zoomControlsEnabled != null) + 'zoomControlsEnabled': config.zoomControlsEnabled!, + if (config.zoomGesturesEnabled != null) + 'zoomGesturesEnabled': config.zoomGesturesEnabled!, + if (config.liteModeEnabled != null) + 'liteModeEnabled': config.liteModeEnabled!, + if (config.trackCameraPosition != null) + 'trackCameraPosition': config.trackCameraPosition!, + if (config.myLocationEnabled != null) + 'myLocationEnabled': config.myLocationEnabled!, + if (config.myLocationButtonEnabled != null) + 'myLocationButtonEnabled': config.myLocationButtonEnabled!, + if (padding != null) + 'padding': [ + padding.top, + padding.left, + padding.bottom, + padding.right, + ], + if (config.indoorViewEnabled != null) + 'indoorEnabled': config.indoorViewEnabled!, + if (config.trafficEnabled != null) 'trafficEnabled': config.trafficEnabled!, + if (config.buildingsEnabled != null) + 'buildingsEnabled': config.buildingsEnabled!, + }; +} + +/// Update specification for a set of [TileOverlay]s. +// TODO(stuartmorgan): Fix the missing export of this class in the platform +// interface, and remove this copy. +class _TileOverlayUpdates extends MapsObjectUpdates { + /// Computes [TileOverlayUpdates] given previous and current [TileOverlay]s. + _TileOverlayUpdates.from(Set previous, Set current) + : super.from(previous, current, objectName: 'tileOverlay'); + + /// Set of TileOverlays to be added in this update. + Set get tileOverlaysToAdd => objectsToAdd; + + /// Set of TileOverlayIds to be removed in this update. + Set get tileOverlayIdsToRemove => + objectIdsToRemove.cast(); + + /// Set of TileOverlays to be changed in this update. + Set get tileOverlaysToChange => objectsToChange; +} diff --git a/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml new file mode 100644 index 000000000000..a0417d5dafbc --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml @@ -0,0 +1,31 @@ +name: google_maps_flutter_android +description: Android implementation of the google_maps_flutter plugin. +repository: https://github.com/flutter/plugins/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.1.10 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +flutter: + plugin: + implements: google_maps_flutter + platforms: + android: + package: io.flutter.plugins.googlemaps + pluginClass: GoogleMapsPlugin + dartPluginClass: GoogleMapsFlutterAndroid + +dependencies: + flutter: + sdk: flutter + flutter_plugin_android_lifecycle: ^2.0.1 + google_maps_flutter_platform_interface: ^2.2.1 + stream_transform: ^2.0.0 + +dev_dependencies: + async: ^2.5.0 + flutter_test: + sdk: flutter + plugin_platform_interface: ^2.0.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart b/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart new file mode 100644 index 000000000000..cba23d072dbf --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart @@ -0,0 +1,153 @@ +// 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:async/async.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:google_maps_flutter_android/google_maps_flutter_android.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + late List log; + + setUp(() async { + log = []; + }); + + /// Initializes a map with the given ID and canned responses, logging all + /// calls to [log]. + void configureMockMap( + GoogleMapsFlutterAndroid maps, { + required int mapId, + required Future? Function(MethodCall call) handler, + }) { + maps + .ensureChannelInitialized(mapId) + .setMockMethodCallHandler((MethodCall methodCall) { + log.add(methodCall.method); + return handler(methodCall); + }); + } + + Future sendPlatformMessage( + int mapId, String method, Map data) async { + final ByteData byteData = + const StandardMethodCodec().encodeMethodCall(MethodCall(method, data)); + await TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger + .handlePlatformMessage('plugins.flutter.dev/google_maps_android_$mapId', + byteData, (ByteData? data) {}); + } + + test('registers instance', () async { + GoogleMapsFlutterAndroid.registerWith(); + expect(GoogleMapsFlutterPlatform.instance, isA()); + }); + + // Calls each method that uses invokeMethod with a return type other than + // void to ensure that the casting/nullability handling succeeds. + // + // TODO(stuartmorgan): Remove this once there is real test coverage of + // each method, since that would cover this issue. + test('non-void invokeMethods handle types correctly', () async { + const int mapId = 0; + final GoogleMapsFlutterAndroid maps = GoogleMapsFlutterAndroid(); + configureMockMap(maps, mapId: mapId, + handler: (MethodCall methodCall) async { + switch (methodCall.method) { + case 'map#getLatLng': + return [1.0, 2.0]; + case 'markers#isInfoWindowShown': + return true; + case 'map#getZoomLevel': + return 2.5; + case 'map#takeSnapshot': + return null; + } + }); + + await maps.getLatLng(const ScreenCoordinate(x: 0, y: 0), mapId: mapId); + await maps.isMarkerInfoWindowShown(const MarkerId(''), mapId: mapId); + await maps.getZoomLevel(mapId: mapId); + await maps.takeSnapshot(mapId: mapId); + // Check that all the invokeMethod calls happened. + expect(log, [ + 'map#getLatLng', + 'markers#isInfoWindowShown', + 'map#getZoomLevel', + 'map#takeSnapshot', + ]); + }); + + test('markers send drag event to correct streams', () async { + const int mapId = 1; + final Map jsonMarkerDragStartEvent = { + 'mapId': mapId, + 'markerId': 'drag-start-marker', + 'position': [1.0, 1.0] + }; + final Map jsonMarkerDragEvent = { + 'mapId': mapId, + 'markerId': 'drag-marker', + 'position': [1.0, 1.0] + }; + final Map jsonMarkerDragEndEvent = { + 'mapId': mapId, + 'markerId': 'drag-end-marker', + 'position': [1.0, 1.0] + }; + + final GoogleMapsFlutterAndroid maps = GoogleMapsFlutterAndroid(); + maps.ensureChannelInitialized(mapId); + + final StreamQueue markerDragStartStream = + StreamQueue(maps.onMarkerDragStart(mapId: mapId)); + final StreamQueue markerDragStream = + StreamQueue(maps.onMarkerDrag(mapId: mapId)); + final StreamQueue markerDragEndStream = + StreamQueue(maps.onMarkerDragEnd(mapId: mapId)); + + await sendPlatformMessage( + mapId, 'marker#onDragStart', jsonMarkerDragStartEvent); + await sendPlatformMessage(mapId, 'marker#onDrag', jsonMarkerDragEvent); + await sendPlatformMessage( + mapId, 'marker#onDragEnd', jsonMarkerDragEndEvent); + + expect((await markerDragStartStream.next).value.value, + equals('drag-start-marker')); + expect((await markerDragStream.next).value.value, equals('drag-marker')); + expect((await markerDragEndStream.next).value.value, + equals('drag-end-marker')); + }); + + test( + 'Default widget is AndroidView', + () async { + final GoogleMapsFlutterAndroid maps = GoogleMapsFlutterAndroid(); + final Widget widget = maps.buildViewWithConfiguration(1, (int _) {}, + widgetConfiguration: const MapWidgetConfiguration( + initialCameraPosition: + CameraPosition(target: LatLng(0, 0), zoom: 1), + textDirection: TextDirection.ltr)); + + expect(widget, isA()); + }, + ); + + testWidgets('Use PlatformViewLink when using surface view', + (WidgetTester tester) async { + final GoogleMapsFlutterAndroid maps = GoogleMapsFlutterAndroid(); + maps.useAndroidViewSurface = true; + + final Widget widget = maps.buildViewWithConfiguration(1, (int _) {}, + widgetConfiguration: const MapWidgetConfiguration( + initialCameraPosition: + CameraPosition(target: LatLng(0, 0), zoom: 1), + textDirection: TextDirection.ltr)); + + expect(widget, isA()); + }); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/AUTHORS b/packages/google_maps_flutter/google_maps_flutter_ios/AUTHORS new file mode 100644 index 000000000000..9f1b53ee2667 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/AUTHORS @@ -0,0 +1,67 @@ +# 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. +The Chromium Authors +German Saprykin +Benjamin Sauer +larsenthomasj@gmail.com +Ali Bitek +Pol Batlló +Anatoly Pulyaevskiy +Hayden Flinner +Stefano Rodriguez +Salvatore Giordano +Brian Armstrong +Paul DeMarco +Fabricio Nogueira +Simon Lightfoot +Ashton Thomas +Thomas Danner +Diego Velásquez +Hajime Nakamura +Tuyển Vũ Xuân +Miguel Ruivo +Sarthak Verma +Mike Diarmid +Invertase +Elliot Hesp +Vince Varga +Aawaz Gyawali +EUI Limited +Katarina Sheremet +Thomas Stockx +Sarbagya Dhaubanjar +Ozkan Eksi +Rishab Nayak +ko2ic +Jonathan Younger +Jose Sanchez +Debkanchan Samadder +Audrius Karosevicius +Lukasz Piliszczuk +SoundReply Solutions GmbH +Rafal Wachol +Pau Picas +Christian Weder +Alexandru Tuca +Christian Weder +Rhodes Davis Jr. +Luigi Agosti +Quentin Le Guennec +Koushik Ravikumar +Nissim Dsilva +Giancarlo Rocha +Ryo Miyake +Théo Champion +Kazuki Yamaguchi +Eitan Schwartz +Chris Rutkowski +Juan Alvarez +Aleksandr Yurkovskiy +Anton Borries +Alex Li +Rahul Raj <64.rahulraj@gmail.com> +Taha Tesser diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md new file mode 100644 index 000000000000..e4b243a063a8 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md @@ -0,0 +1,4 @@ +## 2.1.10 + +* Splits iOS implementation out of `google_maps_flutter` as a federated + implementation. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/LICENSE b/packages/google_maps_flutter/google_maps_flutter_ios/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/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/google_maps_flutter/google_maps_flutter_ios/README.md b/packages/google_maps_flutter/google_maps_flutter_ios/README.md new file mode 100644 index 000000000000..cd5d3f1b7e6e --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/README.md @@ -0,0 +1,12 @@ +# google\_maps\_flutter\_ios + +The iOS implementation of [`google_maps_flutter`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use +`google_maps_flutter` normally. This package will be automatically included in +your app when you do. + +[1]: https://pub.dev/packages/google_maps_flutter +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/.metadata b/packages/google_maps_flutter/google_maps_flutter_ios/example/.metadata new file mode 100644 index 000000000000..46e884ce48d1 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/.metadata @@ -0,0 +1,8 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 3ea4d06340a97a1e9d7cae97567c64e0569dcaa2 + channel: beta diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/README.md b/packages/google_maps_flutter/google_maps_flutter_ios/example/README.md new file mode 100644 index 000000000000..c8852649b065 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/README.md @@ -0,0 +1,3 @@ +# google_maps_flutter_example + +Demonstrates how to use the google_maps_flutter plugin. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/2.0x/red_square.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/2.0x/red_square.png new file mode 100644 index 0000000000000000000000000000000000000000..0f82237796bf8fd2f178f9e758330b88cf715db2 GIT binary patch literal 304 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeK3?y%aJ*@^(Ea{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8Uxs6XMFi@E-`O@66Nz ziZCX5ySp&{XVSd~vL>4nJh^c}wqi2xH2cRH(iKnkC`(qX^4o2nTuqlgyLPDM{ zjv*GOlM^IZ7bl4HG(F^GV0pm6cSViEBhjN@7W>RdP`(kYX@0FtpS)Fwr$M z2r)FZGBC0-FwiwH2a=`jO&~8KH00)|WTsW3YcRAjHic*~j#{}AsDZ)L)z4*}Q$iB} Dk-bi6 literal 0 HcmV?d00001 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/3.0x/red_square.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/3.0x/red_square.png new file mode 100644 index 0000000000000000000000000000000000000000..7e2739974e7bb4101cc42f520ece096cb2f5ade7 GIT binary patch literal 312 zcmeAS@N?(olHy`uVBq!ia0vp^6F``e8A#skDEJMeSkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=YPMC&ZPZf#LuE|04GK z{y-7NByV>Y#{W#Z_kbME0*}aI1_m)z5N7lYQuzQBWH0gbb!C6d!o|U8+TNu22`D7x z>EamTas2H;Lq;I)(1Nf2xxD-f7#JR0XW-hvz-VXkX+BU~wZt`|BqgyV)hf9t6-Y4{ z85mmX8kp!B8iW{{S{WExnHuXFm|GbbJl~bP0 Hl+XkKwG2>~ literal 0 HcmV?d00001 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/night_mode.json b/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/night_mode.json new file mode 100644 index 000000000000..1f16e003a920 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/night_mode.json @@ -0,0 +1,162 @@ +[ + { + "elementType": "geometry", + "stylers": [ + { + "color": "#242f3e" + } + ] + }, + { + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#746855" + } + ] + }, + { + "elementType": "labels.text.stroke", + "stylers": [ + { + "color": "#242f3e" + } + ] + }, + { + "featureType": "administrative.locality", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#d59563" + } + ] + }, + { + "featureType": "poi", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#d59563" + } + ] + }, + { + "featureType": "poi.park", + "elementType": "geometry", + "stylers": [ + { + "color": "#263c3f" + } + ] + }, + { + "featureType": "poi.park", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#6b9a76" + } + ] + }, + { + "featureType": "road", + "elementType": "geometry", + "stylers": [ + { + "color": "#38414e" + } + ] + }, + { + "featureType": "road", + "elementType": "geometry.stroke", + "stylers": [ + { + "color": "#212a37" + } + ] + }, + { + "featureType": "road", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#9ca5b3" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "geometry", + "stylers": [ + { + "color": "#746855" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "geometry.stroke", + "stylers": [ + { + "color": "#1f2835" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#f3d19c" + } + ] + }, + { + "featureType": "transit", + "elementType": "geometry", + "stylers": [ + { + "color": "#2f3948" + } + ] + }, + { + "featureType": "transit.station", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#d59563" + } + ] + }, + { + "featureType": "water", + "elementType": "geometry", + "stylers": [ + { + "color": "#17263c" + } + ] + }, + { + "featureType": "water", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#515c6d" + } + ] + }, + { + "featureType": "water", + "elementType": "labels.text.stroke", + "stylers": [ + { + "color": "#17263c" + } + ] + } +] + diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/red_square.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/assets/red_square.png new file mode 100644 index 0000000000000000000000000000000000000000..650a2dee711d0d404163de8d0e479d68e31d1662 GIT binary patch literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=CqbAk63)r1AkM7~$#S7?R=q_WVZP1_OZu2ihZzoWI>~S6qbP0l+XkKvphU~ literal 0 HcmV?d00001 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/integration_test/google_maps_test.dart new file mode 100644 index 000000000000..a2299e6989d4 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/integration_test/google_maps_test.dart @@ -0,0 +1,1095 @@ +// 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:async'; +import 'dart:typed_data'; +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:google_maps_flutter_example/example_google_map.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'package:integration_test/integration_test.dart'; + +const LatLng _kInitialMapCenter = LatLng(0, 0); +const double _kInitialZoomLevel = 5; +const CameraPosition _kInitialCameraPosition = + CameraPosition(target: _kInitialMapCenter, zoom: _kInitialZoomLevel); + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + GoogleMapsFlutterPlatform.instance.enableDebugInspection(); + + testWidgets('testCompassToggle', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + compassEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool compassEnabled = await inspector.isCompassEnabled(mapId: mapId); + expect(compassEnabled, false); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + compassEnabled: true, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + )); + + compassEnabled = await inspector.isCompassEnabled(mapId: mapId); + expect(compassEnabled, true); + }); + + testWidgets('testMapToolbar returns false', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + mapToolbarEnabled: true, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + final bool mapToolbarEnabled = + await inspector.isMapToolbarEnabled(mapId: mapId); + // This is only supported on Android, so should always return false. + expect(mapToolbarEnabled, false); + }); + + testWidgets('updateMinMaxZoomLevels', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer controllerCompleter = + Completer(); + + const MinMaxZoomPreference initialZoomLevel = MinMaxZoomPreference(4, 8); + const MinMaxZoomPreference finalZoomLevel = MinMaxZoomPreference(6, 10); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + minMaxZoomPreference: initialZoomLevel, + onMapCreated: (ExampleGoogleMapController c) async { + controllerCompleter.complete(c); + }, + ), + )); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + + MinMaxZoomPreference zoomLevel = + await inspector.getMinMaxZoomLevels(mapId: controller.mapId); + expect(zoomLevel, equals(initialZoomLevel)); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + minMaxZoomPreference: finalZoomLevel, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + )); + + zoomLevel = await inspector.getMinMaxZoomLevels(mapId: controller.mapId); + expect(zoomLevel, equals(finalZoomLevel)); + }); + + testWidgets('testZoomGesturesEnabled', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + zoomGesturesEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool zoomGesturesEnabled = + await inspector.areZoomGesturesEnabled(mapId: mapId); + expect(zoomGesturesEnabled, false); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + zoomGesturesEnabled: true, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + )); + + zoomGesturesEnabled = await inspector.areZoomGesturesEnabled(mapId: mapId); + expect(zoomGesturesEnabled, true); + }); + + testWidgets('testZoomControlsEnabled', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + final bool zoomControlsEnabled = + await inspector.areZoomControlsEnabled(mapId: mapId); + + /// Zoom Controls functionality is not available on iOS at the moment. + expect(zoomControlsEnabled, false); + }); + + testWidgets('testRotateGesturesEnabled', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + rotateGesturesEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool rotateGesturesEnabled = + await inspector.areRotateGesturesEnabled(mapId: mapId); + expect(rotateGesturesEnabled, false); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + rotateGesturesEnabled: true, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + )); + + rotateGesturesEnabled = + await inspector.areRotateGesturesEnabled(mapId: mapId); + expect(rotateGesturesEnabled, true); + }); + + testWidgets('testTiltGesturesEnabled', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + tiltGesturesEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool tiltGesturesEnabled = + await inspector.areTiltGesturesEnabled(mapId: mapId); + expect(tiltGesturesEnabled, false); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + tiltGesturesEnabled: true, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + )); + + tiltGesturesEnabled = await inspector.areTiltGesturesEnabled(mapId: mapId); + expect(tiltGesturesEnabled, true); + }); + + testWidgets('testScrollGesturesEnabled', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + scrollGesturesEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool scrollGesturesEnabled = + await inspector.areScrollGesturesEnabled(mapId: mapId); + expect(scrollGesturesEnabled, false); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + scrollGesturesEnabled: true, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + )); + + scrollGesturesEnabled = + await inspector.areScrollGesturesEnabled(mapId: mapId); + expect(scrollGesturesEnabled, true); + }); + + testWidgets('testInitialCenterLocationAtCenter', (WidgetTester tester) async { + await tester.binding.setSurfaceSize(const Size(800, 600)); + + final Completer mapControllerCompleter = + Completer(); + final Key key = GlobalKey(); + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + mapControllerCompleter.complete(controller); + }, + ), + ), + ); + final ExampleGoogleMapController mapController = + await mapControllerCompleter.future; + + await tester.pumpAndSettle(); + + // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen + // in `mapRendered`. + // https://github.com/flutter/flutter/issues/54758 + await Future.delayed(const Duration(seconds: 1)); + + final ScreenCoordinate coordinate = + await mapController.getScreenCoordinate(_kInitialCameraPosition.target); + final Rect rect = tester.getRect(find.byKey(key)); + expect(coordinate.x, (rect.center.dx - rect.topLeft.dx).round()); + expect(coordinate.y, (rect.center.dy - rect.topLeft.dy).round()); + + await tester.binding.setSurfaceSize(null); + }); + + testWidgets('testGetVisibleRegion', (WidgetTester tester) async { + final Key key = GlobalKey(); + final LatLngBounds zeroLatLngBounds = LatLngBounds( + southwest: const LatLng(0, 0), northeast: const LatLng(0, 0)); + + final Completer mapControllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + mapControllerCompleter.complete(controller); + }, + ), + )); + await tester.pumpAndSettle(); + + final ExampleGoogleMapController mapController = + await mapControllerCompleter.future; + + final LatLngBounds firstVisibleRegion = + await mapController.getVisibleRegion(); + + expect(firstVisibleRegion, isNotNull); + expect(firstVisibleRegion.southwest, isNotNull); + expect(firstVisibleRegion.northeast, isNotNull); + expect(firstVisibleRegion, isNot(zeroLatLngBounds)); + expect(firstVisibleRegion.contains(_kInitialMapCenter), isTrue); + + // Making a new `LatLngBounds` about (10, 10) distance south west to the `firstVisibleRegion`. + // The size of the `LatLngBounds` is 10 by 10. + final LatLng southWest = LatLng(firstVisibleRegion.southwest.latitude - 20, + firstVisibleRegion.southwest.longitude - 20); + final LatLng northEast = LatLng(firstVisibleRegion.southwest.latitude - 10, + firstVisibleRegion.southwest.longitude - 10); + final LatLng newCenter = LatLng( + (northEast.latitude + southWest.latitude) / 2, + (northEast.longitude + southWest.longitude) / 2, + ); + + expect(firstVisibleRegion.contains(northEast), isFalse); + expect(firstVisibleRegion.contains(southWest), isFalse); + + final LatLngBounds latLngBounds = + LatLngBounds(southwest: southWest, northeast: northEast); + + // TODO(iskakaushik): non-zero padding is needed for some device configurations + // https://github.com/flutter/flutter/issues/30575 + const double padding = 0; + await mapController + .moveCamera(CameraUpdate.newLatLngBounds(latLngBounds, padding)); + await tester.pumpAndSettle(const Duration(seconds: 3)); + + final LatLngBounds secondVisibleRegion = + await mapController.getVisibleRegion(); + + expect(secondVisibleRegion, isNotNull); + expect(secondVisibleRegion.southwest, isNotNull); + expect(secondVisibleRegion.northeast, isNotNull); + expect(secondVisibleRegion, isNot(zeroLatLngBounds)); + + expect(firstVisibleRegion, isNot(secondVisibleRegion)); + expect(secondVisibleRegion.contains(newCenter), isTrue); + }); + + testWidgets('testTraffic', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + trafficEnabled: true, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool isTrafficEnabled = await inspector.isTrafficEnabled(mapId: mapId); + expect(isTrafficEnabled, true); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + trafficEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + )); + + isTrafficEnabled = await inspector.isTrafficEnabled(mapId: mapId); + expect(isTrafficEnabled, false); + }); + + testWidgets('testBuildings', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + buildingsEnabled: true, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + final bool isBuildingsEnabled = + await inspector.areBuildingsEnabled(mapId: mapId); + expect(isBuildingsEnabled, true); + }); + + testWidgets('testMyLocationButtonToggle', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + myLocationButtonEnabled: true, + myLocationEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + bool myLocationButtonEnabled = + await inspector.isMyLocationButtonEnabled(mapId: mapId); + expect(myLocationButtonEnabled, true); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + myLocationButtonEnabled: false, + myLocationEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + )); + + myLocationButtonEnabled = + await inspector.isMyLocationButtonEnabled(mapId: mapId); + expect(myLocationButtonEnabled, false); + }); + + testWidgets('testMyLocationButton initial value false', + (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + myLocationButtonEnabled: false, + myLocationEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + final bool myLocationButtonEnabled = + await inspector.isMyLocationButtonEnabled(mapId: mapId); + expect(myLocationButtonEnabled, false); + }); + + testWidgets('testMyLocationButton initial value true', + (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + myLocationButtonEnabled: true, + myLocationEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + final bool myLocationButtonEnabled = + await inspector.isMyLocationButtonEnabled(mapId: mapId); + expect(myLocationButtonEnabled, true); + }); + + testWidgets('testSetMapStyle valid Json String', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + controllerCompleter.complete(controller); + }, + ), + )); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + const String mapStyle = + '[{"elementType":"geometry","stylers":[{"color":"#242f3e"}]}]'; + await controller.setMapStyle(mapStyle); + }); + + testWidgets('testSetMapStyle invalid Json String', + (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + controllerCompleter.complete(controller); + }, + ), + )); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + + try { + await controller.setMapStyle('invalid_value'); + fail('expected MapStyleException'); + } on MapStyleException catch (e) { + expect(e.cause, isNotNull); + } + }); + + testWidgets('testSetMapStyle null string', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + controllerCompleter.complete(controller); + }, + ), + )); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + await controller.setMapStyle(null); + }); + + testWidgets('testGetLatLng', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + controllerCompleter.complete(controller); + }, + ), + )); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + + await tester.pumpAndSettle(); + // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen + // in `mapRendered`. + // https://github.com/flutter/flutter/issues/54758 + await Future.delayed(const Duration(seconds: 1)); + + final LatLngBounds visibleRegion = await controller.getVisibleRegion(); + final LatLng topLeft = + await controller.getLatLng(const ScreenCoordinate(x: 0, y: 0)); + final LatLng northWest = LatLng( + visibleRegion.northeast.latitude, + visibleRegion.southwest.longitude, + ); + + expect(topLeft, northWest); + }); + + testWidgets('testGetZoomLevel', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + controllerCompleter.complete(controller); + }, + ), + )); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + + await tester.pumpAndSettle(); + // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen + // in `mapRendered`. + // https://github.com/flutter/flutter/issues/54758 + await Future.delayed(const Duration(seconds: 1)); + + double zoom = await controller.getZoomLevel(); + expect(zoom, _kInitialZoomLevel); + + await controller.moveCamera(CameraUpdate.zoomTo(7)); + await tester.pumpAndSettle(); + zoom = await controller.getZoomLevel(); + expect(zoom, equals(7)); + }); + + testWidgets('testScreenCoordinate', (WidgetTester tester) async { + final Key key = GlobalKey(); + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + controllerCompleter.complete(controller); + }, + ), + )); + final ExampleGoogleMapController controller = + await controllerCompleter.future; + + await tester.pumpAndSettle(); + // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen + // in `mapRendered`. + // https://github.com/flutter/flutter/issues/54758 + await Future.delayed(const Duration(seconds: 1)); + + final LatLngBounds visibleRegion = await controller.getVisibleRegion(); + final LatLng northWest = LatLng( + visibleRegion.northeast.latitude, + visibleRegion.southwest.longitude, + ); + final ScreenCoordinate topLeft = + await controller.getScreenCoordinate(northWest); + expect(topLeft, const ScreenCoordinate(x: 0, y: 0)); + }); + + testWidgets('testResizeWidget', (WidgetTester tester) async { + final Completer controllerCompleter = + Completer(); + final ExampleGoogleMap map = ExampleGoogleMap( + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) async { + controllerCompleter.complete(controller); + }, + ); + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: MaterialApp( + home: Scaffold( + body: SizedBox(height: 100, width: 100, child: map))))); + final ExampleGoogleMapController controller = + await controllerCompleter.future; + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: MaterialApp( + home: Scaffold( + body: SizedBox(height: 400, width: 400, child: map))))); + + await tester.pumpAndSettle(); + // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen + // in `mapRendered`. + // https://github.com/flutter/flutter/issues/54758 + await Future.delayed(const Duration(seconds: 1)); + + // Simple call to make sure that the app hasn't crashed. + final LatLngBounds bounds1 = await controller.getVisibleRegion(); + final LatLngBounds bounds2 = await controller.getVisibleRegion(); + expect(bounds1, bounds2); + }); + + testWidgets('testToggleInfoWindow', (WidgetTester tester) async { + const Marker marker = Marker( + markerId: MarkerId('marker'), + infoWindow: InfoWindow(title: 'InfoWindow')); + final Set markers = {marker}; + + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + initialCameraPosition: const CameraPosition(target: LatLng(10.0, 15.0)), + markers: markers, + onMapCreated: (ExampleGoogleMapController googleMapController) { + controllerCompleter.complete(googleMapController); + }, + ), + )); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + + bool iwVisibleStatus = + await controller.isMarkerInfoWindowShown(marker.markerId); + expect(iwVisibleStatus, false); + + await controller.showMarkerInfoWindow(marker.markerId); + iwVisibleStatus = await controller.isMarkerInfoWindowShown(marker.markerId); + expect(iwVisibleStatus, true); + + await controller.hideMarkerInfoWindow(marker.markerId); + iwVisibleStatus = await controller.isMarkerInfoWindowShown(marker.markerId); + expect(iwVisibleStatus, false); + }); + + testWidgets('fromAssetImage', (WidgetTester tester) async { + const double pixelRatio = 2; + const ImageConfiguration imageConfiguration = + ImageConfiguration(devicePixelRatio: pixelRatio); + final BitmapDescriptor mip = await BitmapDescriptor.fromAssetImage( + imageConfiguration, 'red_square.png'); + final BitmapDescriptor scaled = await BitmapDescriptor.fromAssetImage( + imageConfiguration, 'red_square.png', + mipmaps: false); + expect((mip.toJson() as List)[2], 1); + expect((scaled.toJson() as List)[2], 2); + }); + + testWidgets('testTakeSnapshot', (WidgetTester tester) async { + final Completer controllerCompleter = + Completer(); + + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + controllerCompleter.complete(controller); + }, + ), + ), + ); + + await tester.pumpAndSettle(const Duration(seconds: 3)); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + final Uint8List? bytes = await controller.takeSnapshot(); + expect(bytes?.isNotEmpty, true); + }); + + testWidgets( + 'set tileOverlay correctly', + (WidgetTester tester) async { + final Completer mapIdCompleter = Completer(); + final TileOverlay tileOverlay1 = TileOverlay( + tileOverlayId: const TileOverlayId('tile_overlay_1'), + tileProvider: _DebugTileProvider(), + zIndex: 2, + visible: true, + transparency: 0.2, + fadeIn: true, + ); + + final TileOverlay tileOverlay2 = TileOverlay( + tileOverlayId: const TileOverlayId('tile_overlay_2'), + tileProvider: _DebugTileProvider(), + zIndex: 1, + visible: false, + transparency: 0.3, + fadeIn: false, + ); + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + initialCameraPosition: _kInitialCameraPosition, + tileOverlays: {tileOverlay1, tileOverlay2}, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + ), + ); + await tester.pumpAndSettle(const Duration(seconds: 3)); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + + final TileOverlay tileOverlayInfo1 = (await inspector + .getTileOverlayInfo(tileOverlay1.mapsId, mapId: mapId))!; + final TileOverlay tileOverlayInfo2 = (await inspector + .getTileOverlayInfo(tileOverlay2.mapsId, mapId: mapId))!; + + expect(tileOverlayInfo1.visible, isTrue); + expect(tileOverlayInfo1.fadeIn, isTrue); + expect( + tileOverlayInfo1.transparency, moreOrLessEquals(0.2, epsilon: 0.001)); + expect(tileOverlayInfo1.zIndex, 2); + + expect(tileOverlayInfo2.visible, isFalse); + expect(tileOverlayInfo2.fadeIn, isFalse); + expect( + tileOverlayInfo2.transparency, moreOrLessEquals(0.3, epsilon: 0.001)); + expect(tileOverlayInfo2.zIndex, 1); + }, + ); + + testWidgets( + 'update tileOverlays correctly', + (WidgetTester tester) async { + final Completer mapIdCompleter = Completer(); + final Key key = GlobalKey(); + final TileOverlay tileOverlay1 = TileOverlay( + tileOverlayId: const TileOverlayId('tile_overlay_1'), + tileProvider: _DebugTileProvider(), + zIndex: 2, + visible: true, + transparency: 0.2, + fadeIn: true, + ); + + final TileOverlay tileOverlay2 = TileOverlay( + tileOverlayId: const TileOverlayId('tile_overlay_2'), + tileProvider: _DebugTileProvider(), + zIndex: 3, + visible: true, + transparency: 0.5, + fadeIn: true, + ); + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + tileOverlays: {tileOverlay1, tileOverlay2}, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + ), + ); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + + final TileOverlay tileOverlay1New = TileOverlay( + tileOverlayId: const TileOverlayId('tile_overlay_1'), + tileProvider: _DebugTileProvider(), + zIndex: 1, + visible: false, + transparency: 0.3, + fadeIn: false, + ); + + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + tileOverlays: {tileOverlay1New}, + onMapCreated: (ExampleGoogleMapController controller) { + fail('update: OnMapCreated should get called only once.'); + }, + ), + ), + ); + + await tester.pumpAndSettle(const Duration(seconds: 3)); + + final TileOverlay tileOverlayInfo1 = (await inspector + .getTileOverlayInfo(tileOverlay1.mapsId, mapId: mapId))!; + final TileOverlay? tileOverlayInfo2 = + await inspector.getTileOverlayInfo(tileOverlay2.mapsId, mapId: mapId); + + expect(tileOverlayInfo1.visible, isFalse); + expect(tileOverlayInfo1.fadeIn, isFalse); + expect( + tileOverlayInfo1.transparency, moreOrLessEquals(0.3, epsilon: 0.001)); + expect(tileOverlayInfo1.zIndex, 1); + + expect(tileOverlayInfo2, isNull); + }, + ); + + testWidgets( + 'remove tileOverlays correctly', + (WidgetTester tester) async { + final Completer mapIdCompleter = Completer(); + final Key key = GlobalKey(); + final TileOverlay tileOverlay1 = TileOverlay( + tileOverlayId: const TileOverlayId('tile_overlay_1'), + tileProvider: _DebugTileProvider(), + zIndex: 2, + visible: true, + transparency: 0.2, + fadeIn: true, + ); + + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + tileOverlays: {tileOverlay1}, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + ), + ); + + final int mapId = await mapIdCompleter.future; + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + onMapCreated: (ExampleGoogleMapController controller) { + fail('OnMapCreated should get called only once.'); + }, + ), + ), + ); + + await tester.pumpAndSettle(const Duration(seconds: 3)); + final TileOverlay? tileOverlayInfo1 = + await inspector.getTileOverlayInfo(tileOverlay1.mapsId, mapId: mapId); + + expect(tileOverlayInfo1, isNull); + }, + ); +} + +class _DebugTileProvider implements TileProvider { + _DebugTileProvider() { + boxPaint.isAntiAlias = true; + boxPaint.color = Colors.blue; + boxPaint.strokeWidth = 2.0; + boxPaint.style = PaintingStyle.stroke; + } + + static const int width = 100; + static const int height = 100; + static final Paint boxPaint = Paint(); + static const TextStyle textStyle = TextStyle( + color: Colors.red, + fontSize: 20, + ); + + @override + Future getTile(int x, int y, int? zoom) async { + final ui.PictureRecorder recorder = ui.PictureRecorder(); + final Canvas canvas = Canvas(recorder); + final TextSpan textSpan = TextSpan( + text: '$x,$y', + style: textStyle, + ); + final TextPainter textPainter = TextPainter( + text: textSpan, + textDirection: TextDirection.ltr, + ); + textPainter.layout( + minWidth: 0.0, + maxWidth: width.toDouble(), + ); + const Offset offset = Offset(0, 0); + textPainter.paint(canvas, offset); + canvas.drawRect( + Rect.fromLTRB(0, 0, width.toDouble(), width.toDouble()), boxPaint); + final ui.Picture picture = recorder.endRecording(); + final Uint8List byteData = await picture + .toImage(width, height) + .then((ui.Image image) => + image.toByteData(format: ui.ImageByteFormat.png)) + .then((ByteData? byteData) => byteData!.buffer.asUint8List()); + return Tile(width, height, byteData); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Flutter/AppFrameworkInfo.plist b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000000..3a9c234f96d4 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + UIRequiredDeviceCapabilities + + arm64 + + MinimumOSVersion + 9.0 + + diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Flutter/Debug.xcconfig b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000000..e8efba114687 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/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/google_maps_flutter/google_maps_flutter_ios/example/ios/Flutter/Release.xcconfig b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000000..399e9340e6f6 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/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/google_maps_flutter/google_maps_flutter_ios/example/ios/Podfile b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Podfile new file mode 100644 index 000000000000..14b4bdc51c96 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Podfile @@ -0,0 +1,49 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.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 + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + + pod 'OCMock', '~> 3.9.1' + end + target 'RunnerUITests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + target.build_configurations.each do |build_configuration| + build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386' + end + end +end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..343e0504134c --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,789 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 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 */; }; + 4A097997B7B27CE82FFC3AB8 /* libPods-RunnerUITests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC8ED0578E8D540BBDA17645 /* libPods-RunnerUITests.a */; }; + 6851F3562835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */; }; + 68E4726A2836FF0C00BDDDAC /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 68E472692836FF0C00BDDDAC /* MapKit.framework */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 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 */; }; + 982F2A6C27BADE17003C81F4 /* PartiallyMockedMapView.m in Sources */ = {isa = PBXBuildFile; fileRef = 982F2A6B27BADE17003C81F4 /* PartiallyMockedMapView.m */; }; + F7151F13265D7ED70028CB91 /* GoogleMapsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */; }; + F7151F21265D7EE50028CB91 /* GoogleMapsUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = F7151F20265D7EE50028CB91 /* GoogleMapsUITests.m */; }; + FC8F35FC8CD533B128950487 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F267F68029D1A4E2E4C572A7 /* libPods-RunnerTests.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + F7151F15265D7ED70028CB91 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; + F7151F23265D7EE50028CB91 /* 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 = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTGoogleMapJSONConversionsConversionTests.m; sourceTree = ""; }; + 68E472692836FF0C00BDDDAC /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/iOSSupport/System/Library/Frameworks/MapKit.framework; sourceTree = DEVELOPER_DIR; }; + 6AC1E6095B09DE4B02ECF64E /* Pods-RunnerUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerUITests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerUITests/Pods-RunnerUITests.release.xcconfig"; sourceTree = ""; }; + 733AFAB37683A9DA7512F09C /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 7755F8F4BABC3D6A0BD4048B /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; 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; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 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 = ""; }; + 982F2A6A27BADE17003C81F4 /* PartiallyMockedMapView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PartiallyMockedMapView.h; sourceTree = ""; }; + 982F2A6B27BADE17003C81F4 /* PartiallyMockedMapView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PartiallyMockedMapView.m; sourceTree = ""; }; + B7AFC65E3DD5AC60D834D83D /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + DC8ED0578E8D540BBDA17645 /* libPods-RunnerUITests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerUITests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + DDDAC1342ABDF2F125577581 /* Pods-RunnerUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerUITests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerUITests/Pods-RunnerUITests.debug.xcconfig"; sourceTree = ""; }; + E52C6A6210A56F027C582EF9 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + EA0E91726245EDC22B97E8B9 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + F267F68029D1A4E2E4C572A7 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + F7151F10265D7ED70028CB91 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleMapsTests.m; sourceTree = ""; }; + F7151F14265D7ED70028CB91 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F7151F1E265D7EE50028CB91 /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + F7151F20265D7EE50028CB91 /* GoogleMapsUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleMapsUITests.m; sourceTree = ""; }; + F7151F22265D7EE50028CB91 /* 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 = ( + 4510D964F3B1259FEDD3ABA6 /* libPods-Runner.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F7151F0D265D7ED70028CB91 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 68E4726A2836FF0C00BDDDAC /* MapKit.framework in Frameworks */, + FC8F35FC8CD533B128950487 /* libPods-RunnerTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F7151F1B265D7EE50028CB91 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4A097997B7B27CE82FFC3AB8 /* libPods-RunnerUITests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1E7CF0857EFC88FC263CF3B2 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 68E472692836FF0C00BDDDAC /* MapKit.framework */, + 7755F8F4BABC3D6A0BD4048B /* libPods-Runner.a */, + F267F68029D1A4E2E4C572A7 /* libPods-RunnerTests.a */, + DC8ED0578E8D540BBDA17645 /* libPods-RunnerUITests.a */, + ); + name = Frameworks; + 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 */, + F7151F11265D7ED70028CB91 /* RunnerTests */, + F7151F1F265D7EE50028CB91 /* RunnerUITests */, + 97C146EF1CF9000F007C117D /* Products */, + A189CFE5474BF8A07908B2E0 /* Pods */, + 1E7CF0857EFC88FC263CF3B2 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + F7151F10265D7ED70028CB91 /* RunnerTests.xctest */, + F7151F1E265D7EE50028CB91 /* RunnerUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + A189CFE5474BF8A07908B2E0 /* Pods */ = { + isa = PBXGroup; + children = ( + B7AFC65E3DD5AC60D834D83D /* Pods-Runner.debug.xcconfig */, + EA0E91726245EDC22B97E8B9 /* Pods-Runner.release.xcconfig */, + E52C6A6210A56F027C582EF9 /* Pods-RunnerTests.debug.xcconfig */, + 733AFAB37683A9DA7512F09C /* Pods-RunnerTests.release.xcconfig */, + DDDAC1342ABDF2F125577581 /* Pods-RunnerUITests.debug.xcconfig */, + 6AC1E6095B09DE4B02ECF64E /* Pods-RunnerUITests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + F7151F11265D7ED70028CB91 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */, + F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */, + 982F2A6A27BADE17003C81F4 /* PartiallyMockedMapView.h */, + 982F2A6B27BADE17003C81F4 /* PartiallyMockedMapView.m */, + F7151F14265D7ED70028CB91 /* Info.plist */, + ); + path = RunnerTests; + sourceTree = ""; + }; + F7151F1F265D7EE50028CB91 /* RunnerUITests */ = { + isa = PBXGroup; + children = ( + F7151F20265D7EE50028CB91 /* GoogleMapsUITests.m */, + F7151F22265D7EE50028CB91 /* Info.plist */, + ); + path = RunnerUITests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 74BF216DF17B0C7F983459BD /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + BB6BD9A1101E970BEF85B6D2 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; + F7151F0F265D7ED70028CB91 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F7151F19265D7ED70028CB91 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + D067548A17DC238B80D2BD12 /* [CP] Check Pods Manifest.lock */, + F7151F0C265D7ED70028CB91 /* Sources */, + F7151F0D265D7ED70028CB91 /* Frameworks */, + F7151F0E265D7ED70028CB91 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + F7151F16265D7ED70028CB91 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = F7151F10265D7ED70028CB91 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + F7151F1D265D7EE50028CB91 /* RunnerUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F7151F25265D7EE50028CB91 /* Build configuration list for PBXNativeTarget "RunnerUITests" */; + buildPhases = ( + BD39F60794E9A0264D5D3752 /* [CP] Check Pods Manifest.lock */, + F7151F1A265D7EE50028CB91 /* Sources */, + F7151F1B265D7EE50028CB91 /* Frameworks */, + F7151F1C265D7EE50028CB91 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + F7151F24265D7EE50028CB91 /* PBXTargetDependency */, + ); + name = RunnerUITests; + productName = RunnerUITests; + productReference = F7151F1E265D7EE50028CB91 /* RunnerUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1320; + ORGANIZATIONNAME = "The Flutter Authors"; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + }; + F7151F0F265D7ED70028CB91 = { + CreatedOnToolsVersion = 12.5; + ProvisioningStyle = Automatic; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + F7151F1D265D7EE50028CB91 = { + CreatedOnToolsVersion = 12.5; + ProvisioningStyle = Automatic; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + F7151F0F265D7ED70028CB91 /* RunnerTests */, + F7151F1D265D7EE50028CB91 /* RunnerUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 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; + }; + F7151F0E265D7ED70028CB91 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F7151F1C265D7EE50028CB91 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 74BF216DF17B0C7F983459BD /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + 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"; + }; + BB6BD9A1101E970BEF85B6D2 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", + "${PODS_ROOT}/GoogleMaps/Maps/Frameworks/GoogleMaps.framework/Resources/GoogleMaps.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleMaps.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + BD39F60794E9A0264D5D3752 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerUITests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + D067548A17DC238B80D2BD12 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F7151F0C265D7ED70028CB91 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F7151F13265D7ED70028CB91 /* GoogleMapsTests.m in Sources */, + 6851F3562835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m in Sources */, + 982F2A6C27BADE17003C81F4 /* PartiallyMockedMapView.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F7151F1A265D7EE50028CB91 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F7151F21265D7EE50028CB91 /* GoogleMapsUITests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + F7151F16265D7ED70028CB91 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = F7151F15265D7ED70028CB91 /* PBXContainerItemProxy */; + }; + F7151F24265D7EE50028CB91 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = F7151F23265D7EE50028CB91 /* 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 */ + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = 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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + 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; + 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 = 9.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; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = 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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + 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; + 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 = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.googleMobileMapsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.googleMobileMapsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + F7151F17265D7ED70028CB91 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E52C6A6210A56F027C582EF9 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Debug; + }; + F7151F18265D7ED70028CB91 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 733AFAB37683A9DA7512F09C /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; + INFOPLIST_FILE = RunnerTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Release; + }; + F7151F26265D7EE50028CB91 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DDDAC1342ABDF2F125577581 /* Pods-RunnerUITests.debug.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = RunnerUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = Runner; + }; + name = Debug; + }; + F7151F27265D7EE50028CB91 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6AC1E6095B09DE4B02ECF64E /* Pods-RunnerUITests.release.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = RunnerUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = Runner; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F7151F19265D7ED70028CB91 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F7151F17265D7ED70028CB91 /* Debug */, + F7151F18265D7ED70028CB91 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F7151F25265D7EE50028CB91 /* Build configuration list for PBXNativeTarget "RunnerUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F7151F26265D7EE50028CB91 /* Debug */, + F7151F27265D7EE50028CB91 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..919434a6254f --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..c983bfc640ff --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/AppDelegate.h b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/AppDelegate.h new file mode 100644 index 000000000000..9bc6c56e34f9 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/AppDelegate.h @@ -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. + +#import +#import + +@interface AppDelegate : FlutterAppDelegate +@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/AppDelegate.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/AppDelegate.m new file mode 100644 index 000000000000..55733442b4cf --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/AppDelegate.m @@ -0,0 +1,27 @@ +// 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 "AppDelegate.h" +#import "GeneratedPluginRegistrant.h" + +@import GoogleMaps; + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Provide the GoogleMaps API key. + NSString *mapsApiKey = [[NSProcessInfo processInfo] environment][@"MAPS_API_KEY"]; + if ([mapsApiKey length] == 0) { + mapsApiKey = @"YOUR KEY HERE"; + } + [GMSServices provideAPIKey:mapsApiKey]; + + // Register Flutter plugins. + [GeneratedPluginRegistrant registerWithRegistry:self]; + + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..d36b1fab2d9d --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/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/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..3d43d11e66f4de3da27ed045ca4fe38ad8b48094 GIT binary patch literal 11112 zcmeHN3sh5A)((b(k1DoWZSj%R+R=^`Y(b;ElB$1^R>iT7q6h&WAVr806i~>Gqn6rM z>3}bMG&oq%DIriqR35=rtEdos5L6z)YC*Xq0U-$_+Il@RaU zXYX%+``hR28`(B*uJ6G9&iz>|)PS%!)9N`7=LcmcxH}k69HPyT-%S zH7+jBCC<%76cg_H-n41cTqnKn`u_V9p~XaTLUe3s{KRPSTeK6apP4Jg%VQ$e#72ms zxyWzmGSRwN?=fRgpx!?W&ZsrLfuhAsRxm%;_|P@3@3~BJwY4ZVBJ3f&$5x>`^fD?d zI+z!v#$!gz%FtL*%mR^Uwa*8LJFZ_;X!y$cD??W#c)31l@ervOa_Qk86R{HJiZb$f z&&&0xYmB{@D@yl~^l5IXtB_ou{xFiYP(Jr<9Ce{jCN z<3Rf2TD%}_N?y>bgWq|{`RKd}n>P4e8Z-D+(fn^4)+|pv$DcR&i+RHNhv$71F*McT zl`phYBlb;wO`b7)*10XF6UXhY9`@UR*6-#(Zp`vyU(__*te6xYtV&N0(zjMtev{tZ zapmGin===teMXjsS0>CYxUy<2izOKOPai0}!B9+6q$s3CF8W{xUwz?A0ADO5&BsiB z{SFt|KehNd-S#eiDq!y&+mW9N_!wH-i~q|oNm=mEzkx}B?Ehe%q$tK8f=QY#*6rH9 zNHHaG(9WBqzP!!TMEktSVuh$i$4A^b25LK}&1*4W?ul*5pZYjL1OZ@X9?3W7Y|T6} z1SXx0Wn-|!A;fZGGlYn9a1Jz5^8)~v#mXhmm>um{QiGG459N}L<&qyD+sy_ixD@AP zW0XV6w#3(JW>TEV}MD=O0O>k5H>p#&|O zD2mGf0Cz7+>l7`NuzGobt;(o@vb9YiOpHN8QJ9Uva|i7R?7nnq;L_iq+ZqPv*oGu! zN@GuJ9fm;yrEFga63m?1qy|5&fd32<%$yP$llh}Udrp>~fb>M>R55I@BsGYhCj8m1 zC=ziFh4@hoytpfrJlr}FsV|C(aV4PZ^8^`G29(+!Bk8APa#PemJqkF zE{IzwPaE)I&r`OxGk*vPErm6sGKaQJ&6FODW$;gAl_4b_j!oH4yE@ zP~Cl4?kp>Ccc~Nm+0kjIb`U0N7}zrQEN5!Ju|}t}LeXi!baZOyhlWha5lq{Ld2rdo zGz7hAJQt<6^cxXTe0xZjmADL85cC&H+~Lt2siIIh{$~+U#&#^{Ub22IA|ea6 z5j12XLc`~dh$$1>3o0Cgvo*ybi$c*z>n=5L&X|>Wy1~eagk;lcEnf^2^2xB=e58Z` z@Rw{1ssK)NRV+2O6c<8qFl%efHE;uy!mq(Xi1P*H2}LMi z3EqWN2U?eW{J$lSFxDJg-=&RH!=6P9!y|S~gmjg)gPKGMxq6r9cNIhW` zS})-obO}Ao_`;=>@fAwU&=|5$J;?~!s4LN2&XiMXEl>zk9M}tVEg#kkIkbKp%Ig2QJ2aCILCM1E=aN*iuz>;q#T_I7aVM=E4$m_#OWLnXQnFUnu?~(X>$@NP zBJ@Zw>@bmErSuW7SR2=6535wh-R`WZ+5dLqwTvw}Ks8~4F#hh0$Qn^l-z=;>D~St( z-1yEjCCgd*z5qXa*bJ7H2Tk54KiX&=Vd}z?%dcc z`N8oeYUKe17&|B5A-++RHh8WQ%;gN{vf%05@jZF%wn1Z_yk#M~Cn(i@MB_mpcbLj5 zR#QAtC`k=tZ*h|){Mjz`7bNL zGWOW=bjQhX@`Vw^xn#cVwn28c2D9vOb0TLLy~-?-%gOyHSeJ9a>P}5OF5$n}k-pvUa*pvLw)KvG~>QjNWS3LY1f*OkFwPZ5qC@+3^Bt=HZbf`alKY#{pn zdY}NEIgo1sd)^TPxVzO{uvU$|Z-jkK0p1x##LexgQ$zx1^bNPOG*u2RmZkIM!zFVz zz|IsP3I?qrlmjGS2w_(azCvGTnf~flqogV@Q%mH{76uLU(>UB zQZ?*ys3BO&TV{Pj_qEa-hkH7mOMe_Bnu3%CXCgu90XNKf$N)PUc3Ei-&~@tT zI^49Lm^+=TrI=h4h=W@jW{GjWd{_kVuSzAL6Pi@HKYYnnNbtcYdIRww+jY$(30=#p8*if(mzbvau z00#}4Qf+gH&ce_&8y3Z@CZV>b%&Zr7xuPSSqOmoaP@arwPrMx^jQBQQi>YvBUdpBn zI``MZ3I3HLqp)@vk^E|~)zw$0$VI_RPsL9u(kqulmS`tnb%4U)hm{)h@bG*jw@Y*#MX;Th1wu3TrO}Srn_+YWYesEgkO1 zv?P8uWB)is;#&=xBBLf+y5e4?%y>_8$1KwkAJ8UcW|0CIz89{LydfJKr^RF=JFPi}MAv|ecbuZ!YcTSxsD$(Pr#W*oytl?@+2 zXBFb32Kf_G3~EgOS7C`8w!tx}DcCT%+#qa76VSbnHo;4(oJ7)}mm?b5V65ir`7Z}s zR2)m15b#E}z_2@rf34wo!M^CnVoi# ze+S(IK({C6u=Sm{1>F~?)8t&fZpOOPcby;I3jO;7^xmLKM(<%i-nyj9mgw9F1Lq4|DZUHZ4)V9&6fQM(ZxbG{h+}(koiTu`SQw6#6q2Yg z-d+1+MRp$zYT2neIR2cKij2!R;C~ooQ3<;^8)_Gch&ZyEtiQwmF0Mb_)6)4lVEBF< zklXS7hvtu30uJR`3OzcqUNOdYsfrKSGkIQAk|4=&#ggxdU4^Y(;)$8}fQ>lTgQdJ{ zzie8+1$3@E;|a`kzuFh9Se}%RHTmBg)h$eH;gttjL_)pO^10?!bNev6{mLMaQpY<< z7M^ZXrg>tw;vU@9H=khbff?@nu)Yw4G% zGxobPTUR2p_ed7Lvx?dkrN^>Cv$Axuwk;Wj{5Z@#$sK@f4{7SHg%2bpcS{(~s;L(mz@9r$cK@m~ef&vf%1@ z@8&@LLO2lQso|bJD6}+_L1*D^}>oqg~$NipL>QlP3 zM#ATSy@ycMkKs5-0X8nFAtMhO_=$DlWR+@EaZ}`YduRD4A2@!at3NYRHmlENea9IF zN*s>mi?zy*Vv+F+&4-o`Wj}P3mLGM*&M(z|;?d82>hQkkY?e-hJ47mWOLCPL*MO04 z3lE(n2RM=IIo;Z?I=sKJ_h=iJHbQ2<}WW0b@I6Qf-{T=Qn#@N0yG5xH&ofEy^mZMPzd22nR`t!Q)VkNgf*VOxE z$XhOunG3ZN#`Ks$Hp~}`OX5vmHP={GYUJ+-g0%PS$*Qi5+-40M47zJ24vK1#? zb$s^%r?+>#lw$mpZaMa1aO%wlPm3~cno_(S%U&-R;6eK(@`CjswAW2)HfZ>ptItaZ|XqQ z&sHVVL>WCe|E4iPb2~gS5ITs6xfg(kmt&3$YcI=zTuqj37t|+9ojCr(G^ul#p{>k) zM94pI>~5VZ$!*Qurq<@RIXgP3sx-2kL$1Q~da%rnNIh?)&+c~*&e~CYPDhPYjb+Xu zKg5w^XB3(_9{Waa4E(-J-Kq_u6t_k?a8kEHqai-N-4#`SRerO!h}!cS%SMC<)tGix zOzVP^_t!HN&HIPL-ZpcgWitHM&yFRC7!k4zSI+-<_uQ}|tX)n{Ib;X>Xx>i_d*KkH zCzogKQFpP1408_2!ofU|iBq2R8hW6G zuqJs9Tyw{u%-uWczPLkM!MfKfflt+NK9Vk8E!C>AsJwNDRoe2~cL+UvqNP|5J8t)( z0$iMa!jhudJ+fqFn+um&@Oj6qXJd_3-l`S^I1#0fnt!z3?D*hAHr*u(*wR@`4O z#avrtg%s`Fh{?$FtBFM^$@@hW!8ZfF4;=n0<8In&X}-Rp=cd0TqT_ne46$j^r}FzE z26vX^!PzScuQfFfl1HEZ{zL?G88mcc76zHGizWiykBf4m83Z${So-+dZ~YGhm*RO7 zB1gdIdqnFi?qw+lPRFW5?}CQ3Me3G^muvll&4iN+*5#_mmIu;loULMwb4lu9U*dFM z-Sr**(0Ei~u=$3<6>C-G6z4_LNCx||6YtjS)<;hf)YJTPKXW+w%hhCTUAInIse9>r zl2YU6nRb$u-FJlWN*{{%sm_gi_UP5{=?5}5^D2vPzM=oPfNw~azZQ#P zl5z8RtSSiTIpEohC15i-Q1Bk{3&ElsD0uGAOxvbk29VUDmmA0w;^v`W#0`};O3DVE z&+-ca*`YcN%z*#VXWK9Qa-OEME#fykF%|7o=1Y+eF;Rtv0W4~kKRDx9YBHOWhC%^I z$Jec0cC7o37}Xt}cu)NH5R}NT+=2Nap*`^%O)vz?+{PV<2~qX%TzdJOGeKj5_QjqR&a3*K@= P-1+_A+?hGkL;m(J7kc&K literal 0 HcmV?d00001 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..28c6bf03016f6c994b70f38d1b7346e5831b531f GIT binary patch literal 564 zcmV-40?Yl0P)Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV7ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 zcmds$`#;kQ7{|XelZftyR5~xW7?MLxS4^|Hw3&P7^y)@A9Fj{Xm1~_CIV^XZ%SLBn zA;!r`GqGHg=7>xrB{?psZQs88ZaedDoagm^KF{a*>G|dJWRSe^I$DNW008I^+;Kjt z>9p3GNR^I;v>5_`+91i(*G;u5|L+Bu6M=(afLjtkya#yZ175|z$pU~>2#^Z_pCZ7o z1c6UNcv2B3?; zX%qdxCXQpdKRz=#b*q0P%b&o)5ZrNZt7$fiETSK_VaY=mb4GK`#~0K#~9^ zcY!`#Af+4h?UMR-gMKOmpuYeN5P*RKF!(tb`)oe0j2BH1l?=>y#S5pMqkx6i{*=V9JF%>N8`ewGhRE(|WohnD59R^$_36{4>S zDFlPC5|k?;SPsDo87!B{6*7eqmMdU|QZ84>6)Kd9wNfh90=y=TFQay-0__>=<4pk& zYDjgIhL-jQ9o>z32K)BgAH+HxamL{ZL~ozu)Qqe@a`FpH=oQRA8=L-m-1dam(Ix2V z?du;LdMO+ooBelr^_y4{|44tmgH^2hSzPFd;U^!1p>6d|o)(-01z{i&Kj@)z-yfWQ)V#3Uo!_U}q3u`(fOs`_f^ueFii1xBNUB z6MecwJN$CqV&vhc+)b(p4NzGGEgwWNs z@*lUV6LaduZH)4_g!cE<2G6#+hJrWd5(|p1Z;YJ7ifVHv+n49btR}dq?HHDjl{m$T z!jLZcGkb&XS2OG~u%&R$(X+Z`CWec%QKt>NGYvd5g20)PU(dOn^7%@6kQb}C(%=vr z{?RP(z~C9DPnL{q^@pVw@|Vx~@3v!9dCaBtbh2EdtoNHm4kGxp>i#ct)7p|$QJs+U z-a3qtcPvhihub?wnJqEt>zC@)2suY?%-96cYCm$Q8R%-8$PZYsx3~QOLMDf(piXMm zB=<63yQk1AdOz#-qsEDX>>c)EES%$owHKue;?B3)8aRd}m~_)>SL3h2(9X;|+2#7X z+#2)NpD%qJvCQ0a-uzZLmz*ms+l*N}w)3LRQ*6>|Ub-fyptY(keUxw+)jfwF5K{L9 z|Cl_w=`!l_o><384d&?)$6Nh(GAm=4p_;{qVn#hI8lqewW7~wUlyBM-4Z|)cZr?Rh z=xZ&Ol>4(CU85ea(CZ^aO@2N18K>ftl8>2MqetAR53_JA>Fal`^)1Y--Am~UDa4th zKfCYpcXky$XSFDWBMIl(q=Mxj$iMBX=|j9P)^fDmF(5(5$|?Cx}DKEJa&XZP%OyE`*GvvYQ4PV&!g2|L^Q z?YG}tx;sY@GzMmsY`7r$P+F_YLz)(e}% zyakqFB<6|x9R#TdoP{R$>o7y(-`$$p0NxJ6?2B8tH)4^yF(WhqGZlM3=9Ibs$%U1w zWzcss*_c0=v_+^bfb`kBFsI`d;ElwiU%frgRB%qBjn@!0U2zZehBn|{%uNIKBA7n= zzE`nnwTP85{g;8AkYxA68>#muXa!G>xH22D1I*SiD~7C?7Za+9y7j1SHiuSkKK*^O zsZ==KO(Ua#?YUpXl{ViynyT#Hzk=}5X$e04O@fsMQjb}EMuPWFO0e&8(2N(29$@Vd zn1h8Yd>6z(*p^E{c(L0Lg=wVdupg!z@WG;E0k|4a%s7Up5C0c)55XVK*|x9RQeZ1J@1v9MX;>n34(i>=YE@Iur`0Vah(inE3VUFZNqf~tSz{1fz3Fsn_x4F>o(Yo;kpqvBe-sbwH(*Y zu$JOl0b83zu$JMvy<#oH^Wl>aWL*?aDwnS0iEAwC?DK@aT)GHRLhnz2WCvf3Ba;o=aY7 z2{Asu5MEjGOY4O#Ggz@@J;q*0`kd2n8I3BeNuMmYZf{}pg=jTdTCrIIYuW~luKecn z+E-pHY%ohj@uS0%^ z&(OxwPFPD$+#~`H?fMvi9geVLci(`K?Kj|w{rZ9JgthFHV+=6vMbK~0)Ea<&WY-NC zy-PnZft_k2tfeQ*SuC=nUj4H%SQ&Y$gbH4#2sT0cU0SdFs=*W*4hKGpuR1{)mV;Qf5pw4? zfiQgy0w3fC*w&Bj#{&=7033qFR*<*61B4f9K%CQvxEn&bsWJ{&winp;FP!KBj=(P6 z4Z_n4L7cS;ao2)ax?Tm|I1pH|uLpDSRVghkA_UtFFuZ0b2#>!8;>-_0ELjQSD-DRd z4im;599VHDZYtnWZGAB25W-e(2VrzEh|etsv2YoP#VbIZ{aFkwPrzJ#JvCvA*mXS& z`}Q^v9(W4GiSs}#s7BaN!WA2bniM$0J(#;MR>uIJ^uvgD3GS^%*ikdW6-!VFUU?JV zZc2)4cMsX@j z5HQ^e3BUzOdm}yC-xA%SY``k$rbfk z;CHqifhU*jfGM@DkYCecD9vl*qr58l6x<8URB=&%{!Cu3RO*MrKZ4VO}V6R0a zZw3Eg^0iKWM1dcTYZ0>N899=r6?+adUiBKPciJw}L$=1f4cs^bio&cr9baLF>6#BM z(F}EXe-`F=f_@`A7+Q&|QaZ??Txp_dB#lg!NH=t3$G8&06MFhwR=Iu*Im0s_b2B@| znW>X}sy~m#EW)&6E&!*0%}8UAS)wjt+A(io#wGI@Z2S+Ms1Cxl%YVE800007ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 zcmVjJBgitF5mAp-i>4+KS_oR{|13AP->1TD4=w)g|)JHOx|a2Wk1Va z!k)vP$UcQ#mdj%wNQoaJ!w>jv_6&JPyutpQps?s5dmDQ>`%?Bvj>o<%kYG!YW6H-z zu`g$@mp`;qDR!51QaS}|ZToSuAGcJ7$2HF0z`ln4t!#Yg46>;vGG9N9{V@9z#}6v* zfP?}r6b{*-C*)(S>NECI_E~{QYzN5SXRmVnP<=gzP+_Sp(Aza_hKlZ{C1D&l*(7IKXxQC1Z9#6wx}YrGcn~g%;icdw>T0Rf^w0{ z$_wn1J+C0@!jCV<%Go5LA45e{5gY9PvZp8uM$=1}XDI+9m7!A95L>q>>oe0$nC->i zeexUIvq%Uk<-$>DiDb?!In)lAmtuMWxvWlk`2>4lNuhSsjAf2*2tjT`y;@d}($o)S zn(+W&hJ1p0xy@oxP%AM15->wPLp{H!k)BdBD$toBpJh+crWdsNV)qsHaqLg2_s|Ih z`8E9z{E3sA!}5aKu?T!#enD(wLw?IT?k-yWVHZ8Akz4k5(TZJN^zZgm&zM28sfTD2BYJ|Fde3Xzh;;S` z=GXTnY4Xc)8nYoz6&vF;P7{xRF-{|2Xs5>a5)@BrnQ}I(_x7Cgpx#5&Td^4Q9_FnQ zX5so*;#8-J8#c$OlA&JyPp$LKUhC~-e~Ij!L%uSMu!-VZG7Hx-L{m2DVR2i=GR(_% zCVD!4N`I)&Q5S`?P&fQZ=4#Dgt_v2-DzkT}K(9gF0L(owe-Id$Rc2qZVLqI_M_DyO z9@LC#U28_LU{;wGZ&))}0R2P4MhajKCd^K#D+JJ&JIXZ_p#@+7J9A&P<0kdRujtQ_ zOy>3=C$kgi6$0pW06KaLz!21oOryKM3ZUOWqppndxfH}QpgjEJ`j7Tzn5bk6K&@RA?vl##y z$?V~1E(!wB5rH`>3nc&@)|#<1dN2cMzzm=PGhQ|Yppne(C-Vlt450IXc`J4R0W@I7 zd1e5uW6juvO%ni(WX7BsKx3MLngO7rHO;^R5I~0^nE^9^E_eYLgiR9&KnJ)pBbfno zSVnW$0R+&6jOOsZ82}nJ126+c|%svPo;TeUku<2G7%?$oft zyaO;tVo}(W)VsTUhq^XmFi#2z%-W9a{7mXn{uzivYQ_d6b7VJG{77naW(vHt-uhnY zVN#d!JTqVh(7r-lhtXVU6o})aZbDt_;&wJVGl2FKYFBFpU-#9U)z#(A%=IVnqytR$SY-sO( z($oNE09{D^@OuYPz&w~?9>Fl5`g9u&ecFGhqX=^#fmR=we0CJw+5xna*@oHnkahk+ z9aWeE3v|An+O5%?4fA&$Fgu~H_YmqR!yIU!bFCk4!#pAj%(lI(A5n)n@Id#M)O9Yx zJU9oKy{sRAIV3=5>(s8n{8ryJ!;ho}%pn6hZKTKbqk=&m=f*UnK$zW3YQP*)pw$O* zIfLA^!-bmBl6%d_n$#tP8Zd_(XdA*z*WH|E_yILwjtI~;jK#v-6jMl^?<%Y%`gvpwv&cFb$||^v4D&V=aNy?NGo620jL3VZnA%s zH~I|qPzB~e(;p;b^gJr7Ure#7?8%F0m4vzzPy^^(q4q1OdthF}Fi*RmVZN1OwTsAP zn9CZP`FazX3^kG(KodIZ=Kty8DLTy--UKfa1$6XugS zk%6v$Kmxt6U!YMx0JQ)0qX*{CXwZZk$vEROidEc7=J-1;peNat!vS<3P-FT5po>iE z!l3R+<`#x|+_hw!HjQGV=8!q|76y8L7N8gP3$%0kfush|u0uU^?dKBaeRSBUpOZ0c z62;D&Mdn2}N}xHRFTRI?zRv=>=AjHgH}`2k4WK=#AHB)UFrR-J87GgX*x5fL^W2#d z=(%K8-oZfMO=i{aWRDg=FX}UubM4eotRDcn;OR#{3q=*?3mE3_oJ-~prjhxh%PgQT zyn)Qozaq0@o&|LEgS{Ind4Swsr;b`u185hZPOBLL<`d2%^Yp1?oL)=jnLi;Zo0ZDliTtQ^b5SmfIMe{T==zZkbvn$KTQGlbG8w}s@M3TZnde;1Am46P3juKb zl9GU&3F=q`>j!`?SyH#r@O59%@aMX^rx}Nxe<>NqpUp5=lX1ojGDIR*-D^SDuvCKF z?3$xG(gVUsBERef_YjPFl^rU9EtD{pt z0CXwpN7BN3!8>hajGaTVk-wl=9rxmfWtIhC{mheHgStLi^+Nz12a?4r(fz)?3A%at zMlvQmL<2-R)-@G1wJ0^zQK%mR=r4d{Y3fHp){nWXUL#|CqXl(+v+qDh>FkF9`eWrW zfr^D%LNfOcTNvtx0JXR35J0~Jpi2#P3Q&80w+nqNfc}&G0A~*)lGHKv=^FE+b(37|)zL;KLF>oiGfb(?&1 zV3XRu!Sw>@quKiab%g6jun#oZ%!>V#A%+lNc?q>6+VvyAn=kf_6z^(TZUa4Eelh{{ zqFX-#dY(EV@7l$NE&kv9u9BR8&Ojd#ZGJ6l8_BW}^r?DIS_rU2(XaGOK z225E@kH5Opf+CgD^{y29jD4gHbGf{1MD6ggQ&%>UG4WyPh5q_tb`{@_34B?xfSO*| zZv8!)q;^o-bz`MuxXk*G^}(6)ACb@=Lfs`Hxoh>`Y0NE8QRQ!*p|SH@{r8=%RKd4p z+#Ty^-0kb=-H-O`nAA3_6>2z(D=~Tbs(n8LHxD0`R0_ATFqp-SdY3(bZ3;VUM?J=O zKCNsxsgt@|&nKMC=*+ZqmLHhX1KHbAJs{nGVMs6~TiF%Q)P@>!koa$%oS zjXa=!5>P`vC-a}ln!uH1ooeI&v?=?v7?1n~P(wZ~0>xWxd_Aw;+}9#eULM7M8&E?Y zC-ZLhi3RoM92SXUb-5i-Lmt5_rfjE{6y^+24`y$1lywLyHO!)Boa7438K4#iLe?rh z2O~YGSgFUBH?og*6=r9rme=peP~ah`(8Zt7V)j5!V0KPFf_mebo3z95U8(up$-+EA^9dTRLq>Yl)YMBuch9%=e5B`Vnb>o zt03=kq;k2TgGe4|lGne&zJa~h(UGutjP_zr?a7~#b)@15XNA>Dj(m=gg2Q5V4-$)D|Q9}R#002ovPDHLkV1o7DH3k3x literal 0 HcmV?d00001 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 zcmV-m2cP(fP)x~L`~4d)Rspd&<9kFh{hn*KP1LP0~$;u(LfAu zp%fx&qLBcRHx$G|3q(bv@+b;o0*D|jwD-Q9uQR(l*ST}s+uPgQ-MeFwZ#GS?b332? z&Tk$&_miXn3IGq)AmQ)3sisq{raD4(k*bHvpCe-TdWq^NRTEVM)i9xbgQ&ccnUVx* zEY%vS%gDcSg=!tuIK8$Th2_((_h^+7;R|G{n06&O2#6%LK`a}n?h_fL18btz<@lFG za}xS}u?#DBMB> zw^b($1Z)`9G?eP95EKi&$eOy@K%h;ryrR3la%;>|o*>CgB(s>dDcNOXg}CK9SPmD? zmr-s{0wRmxUnbDrYfRvnZ@d z6johZ2sMX{YkGSKWd}m|@V7`Degt-43=2M?+jR%8{(H$&MLLmS;-|JxnX2pnz;el1jsvqQz}pGSF<`mqEXRQ5sC4#BbwnB_4` zc5bFE-Gb#JV3tox9fp-vVEN{(tOCpRse`S+@)?%pz+zVJXSooTrNCUg`R6`hxwb{) zC@{O6MKY8tfZ5@!yy=p5Y|#+myRL=^{tc(6YgAnkg3I(Cd!r5l;|;l-MQ8B`;*SCE z{u)uP^C$lOPM z5d~UhKhRRmvv{LIa^|oavk1$QiEApSrP@~Jjbg`<*dW4TO?4qG%a%sTPUFz(QtW5( zM)lA+5)0TvH~aBaOAs|}?u2FO;yc-CZ1gNM1dAxJ?%m?YsGR`}-xk2*dxC}r5j$d* zE!#Vtbo69h>V4V`BL%_&$} z+oJAo@jQ^Tk`;%xw-4G>hhb&)B?##U+(6Fi7nno`C<|#PVA%$Y{}N-?(Gc$1%tr4Pc}}hm~yY#fTOe!@v9s-ik$dX~|ygArPhByaXn8 zpI^FUjNWMsTFKTP3X7m?UK)3m zp6rI^_zxRYrx6_QmhoWoDR`fp4R7gu6;gdO)!KexaoO2D88F9x#TM1(9Bn7g;|?|o z)~$n&Lh#hCP6_LOPD>a)NmhW})LADx2kq=X7}7wYRj-0?dXr&bHaRWCfSqvzFa=sn z-8^gSyn-RmH=BZ{AJZ~!8n5621GbUJV7Qvs%JNv&$%Q17s_X%s-41vAPfIR>;x0Wlqr5?09S>x#%Qkt>?(&XjFRY}*L6BeQ3 z<6XEBh^S7>AbwGm@XP{RkeEKj6@_o%oV?hDuUpUJ+r#JZO?!IUc;r0R?>mi)*ZpQ) z#((dn=A#i_&EQn|hd)N$#A*fjBFuiHcYvo?@y1 z5|fV=a^a~d!c-%ZbMNqkMKiSzM{Yq=7_c&1H!mXk60Uv32dV;vMg&-kQ)Q{+PFtwc zj|-uQ;b^gts??J*9VxxOro}W~Q9j4Em|zSRv)(WSO9$F$s=Ydu%Q+5DOid~lwk&we zY%W(Z@ofdwPHncEZzZgmqS|!gTj3wQq9rxQy+^eNYKr1mj&?tm@wkO*9@UtnRMG>c aR{jt9+;fr}hV%pg00001^@s67{VYS000c7NklQEG_j zup^)eW&WUIApqy$=APz8jE@awGp)!bsTjDbrJO`$x^ZR^dr;>)LW>{ zs70vpsD38v)19rI=GNk1b(0?Js9~rjsQsu*K;@SD40RB-3^gKU-MYC7G!Bw{fZsqp zih4iIi;Hr_xZ033Iu{sQxLS=}yBXgLMn40d++>aQ0#%8D1EbGZp7+ z5=mK?t31BkVYbGOxE9`i748x`YgCMwL$qMsChbSGSE1`p{nSmadR zcQ#R)(?!~dmtD0+D2!K zR9%!Xp1oOJzm(vbLvT^$IKp@+W2=-}qTzTgVtQ!#Y7Gxz}stUIm<1;oBQ^Sh2X{F4ibaOOx;5ZGSNK z0maF^@(UtV$=p6DXLgRURwF95C=|U8?osGhgOED*b z7woJ_PWXBD>V-NjQAm{~T%sjyJ{5tn2f{G%?J!KRSrrGvQ1(^`YLA5B!~eycY(e5_ z*%aa{at13SxC(=7JT7$IQF~R3sy`Nn%EMv!$-8ZEAryB*yB1k&stni)=)8-ODo41g zkJu~roIgAih94tb=YsL%iH5@^b~kU9M-=aqgXIrbtxMpFy5mekFm#edF9z7RQ6V}R zBIhbXs~pMzt0VWy1Fi$^fh+1xxLDoK09&5&MJl(q#THjPm(0=z2H2Yfm^a&E)V+a5 zbi>08u;bJsDRUKR9(INSc7XyuWv(JsD+BB*0hS)FO&l&7MdViuur@-<-EHw>kHRGY zqoT}3fDv2-m{NhBG8X}+rgOEZ;amh*DqN?jEfQdqxdj08`Sr=C-KmT)qU1 z+9Cl)a1mgXxhQiHVB}l`m;-RpmKy?0*|yl?FXvJkFxuu!fKlcmz$kN(a}i*saM3nr z0!;a~_%Xqy24IxA2rz<+08=B-Q|2PT)O4;EaxP^6qixOv7-cRh?*T?zZU`{nIM-at zTKYWr9rJ=tppQ9I#Z#mLgINVB!pO-^FOcvFw6NhV0gztuO?g ztoA*C-52Q-Z-P#xB4HAY3KQVd%dz1S4PA3vHp0aa=zAO?FCt zC_GaTyVBg2F!bBr3U@Zy2iJgIAt>1sf$JWA9kh{;L+P*HfUBX1Zy{4MgNbDfBV_ly z!y#+753arsZUt@366jIC0klaC@ckuk!qu=pAyf7&QmiBUT^L1&tOHzsK)4n|pmrVT zs2($4=?s~VejTFHbFdDOwG;_58LkIj1Fh@{glkO#F1>a==ymJS$z;gdedT1zPx4Kj ztjS`y_C}%af-RtpehdQDt3a<=W5C4$)9W@QAse;WUry$WYmr51ml9lkeunUrE`-3e zmq1SgSOPNEE-Mf+AGJ$g0M;3@w!$Ej;hMh=v=I+Lpz^n%Pg^MgwyqOkNyu2c^of)C z1~ALor3}}+RiF*K4+4{(1%1j3pif1>sv0r^mTZ?5Jd-It!tfPfiG_p$AY*Vfak%FG z4z#;wLtw&E&?}w+eKG^=#jF7HQzr8rV0mY<1YAJ_uGz~$E13p?F^fPSzXSn$8UcI$ z8er9{5w5iv0qf8%70zV71T1IBB1N}R5Kp%NO0=5wJalZt8;xYp;b{1K) zHY>2wW-`Sl{=NpR%iu3(u6l&)rc%%cSA#aV7WCowfbFR4wcc{LQZv~o1u_`}EJA3>ki`?9CKYTA!rhO)if*zRdd}Kn zEPfYbhoVE~!FI_2YbC5qAj1kq;xP6%J8+?2PAs?`V3}nyFVD#sV3+uP`pi}{$l9U^ zSz}_M9f7RgnnRhaoIJgT8us!1aB&4!*vYF07Hp&}L zCRlop0oK4DL@ISz{2_BPlezc;xj2|I z23RlDNpi9LgTG_#(w%cMaS)%N`e>~1&a3<{Xy}>?WbF>OOLuO+j&hc^YohQ$4F&ze z+hwnro1puQjnKm;vFG~o>`kCeUIlkA-2tI?WBKCFLMBY=J{hpSsQ=PDtU$=duS_hq zHpymHt^uuV1q@uc4bFb{MdG*|VoW@15Osrqt2@8ll0qO=j*uOXn{M0UJX#SUztui9FN4)K3{9!y8PC-AHHvpVTU;x|-7P+taAtyglk#rjlH2 z5Gq8ik}BPaGiM{#Woyg;*&N9R2{J0V+WGB69cEtH7F?U~Kbi6ksi*`CFXsi931q7Y zGO82?whBhN%w1iDetv%~wM*Y;E^)@Vl?VDj-f*RX>{;o_=$fU!&KAXbuadYZ46Zbg z&6jMF=49$uL^73y;;N5jaHYv)BTyfh&`qVLYn?`o6BCA_z-0niZz=qPG!vonK3MW_ zo$V96zM!+kJRs{P-5-rQVse0VBH*n6A58)4uc&gfHMa{gIhV2fGf{st>E8sKyP-$8zp~wJX^A*@DI&-;8>gANXZj zU)R+Y)PB?=)a|Kj>8NXEu^S_h^7R`~Q&7*Kn!xyvzVv&^>?^iu;S~R2e-2fJx-oUb cX)(b1KSk$MOV07*qoM6N<$f&6$jw%VRuvdN2+38CZWny1cRtlsl+0_KtW)EU14Ei(F!UtWuj4IK+3{sK@>rh zs1Z;=(DD&U6+tlyL?UnHVN^&g6QhFi2#HS+*qz;(>63G(`|jRtW|nz$Pv7qTovP!^ zP_jES{mr@O-02w%!^a?^1ZP!_KmQiz0L~jZ=W@Qt`8wzOoclQsAS<5YdH;a(4bGLE zk8s}1If(PSIgVi!XE!5kA?~z*sobvNyohr;=Q_@h2@$6Flyej3J)D-6YfheRGl`HEcPk|~huT_2-U?PfL=4BPV)f1o!%rQ!NMt_MYw-5bUSwQ9Z&zC>u zOrl~UJglJNa%f50Ok}?WB{on`Ci`p^Y!xBA?m@rcJXLxtrE0FhRF3d*ir>yzO|BD$ z3V}HpFcCh6bTzY}Nt_(W%QYd3NG)jJ4<`F<1Od) zfQblTdC&h2lCz`>y?>|9o2CdvC8qZeIZt%jN;B7Hdn2l*k4M4MFEtq`q_#5?}c$b$pf_3y{Y!cRDafZBEj-*OD|gz#PBDeu3QoueOesLzB+O zxjf2wvf6Wwz>@AiOo2mO4=TkAV+g~%_n&R;)l#!cBxjuoD$aS-`IIJv7cdX%2{WT7 zOm%5rs(wqyPE^k5SIpUZ!&Lq4<~%{*>_Hu$2|~Xa;iX*tz8~G6O3uFOS?+)tWtdi| zV2b#;zRN!m@H&jd=!$7YY6_}|=!IU@=SjvGDFtL;aCtw06U;-v^0%k0FOyESt z1Wv$={b_H&8FiRV?MrzoHWd>%v6KTRU;-v^Miiz+@q`(BoT!+<37CKhoKb)|8!+RG z6BQFU^@fRW;s8!mOf2QViKQGk0TVER6EG1`#;Nm39Do^PoT!+<37AD!%oJe86(=et zZ~|sLzU>V-qYiU6V8$0GmU7_K8|Fd0B?+9Un1BhKAz#V~Fk^`mJtlCX#{^8^M8!me z8Yg;8-~>!e<-iG;h*0B1kBKm}hItVGY6WnjVpgnTTAC$rqQ^v)4KvOtpY|sIj@WYg zyw##ZZ5AC2IKNC;^hwg9BPk0wLStlmBr;E|$5GoAo$&Ui_;S9WY62n3)i49|T%C#i017z3J=$RF|KyZWnci*@lW4 z=AKhNN6+m`Q!V3Ye68|8y@%=am>YD0nG99M)NWc20%)gwO!96j7muR}Fr&54SxKP2 zP30S~lt=a*qDlbu3+Av57=9v&vr<6g0&`!8E2fq>I|EJGKs}t|{h7+KT@)LfIV-3K zK)r_fr2?}FFyn*MYoLC>oV-J~eavL2ho4a4^r{E-8m2hi>~hA?_vIG4a*KT;2eyl1 zh_hUvUJpNCFwBvRq5BI*srSle>c6%n`#VNsyC|MGa{(P&08p=C9+WUw9Hl<1o9T4M zdD=_C0F7#o8A_bRR?sFNmU0R6tW`ElnF8p53IdHo#S9(JoZCz}fHwJ6F<&?qrpVqE zte|m%89JQD+XwaPU#%#lVs-@-OL);|MdfINd6!XwP2h(eyafTUsoRkA%&@fe?9m@jw-v(yTTiV2(*fthQH9}SqmsRPVnwwbV$1E(_lkmo&S zF-truCU914_$jpqjr(>Ha4HkM4YMT>m~NosUu&UZ>zirfHo%N6PPs9^_o$WqPA0#5 z%tG>qFCL+b*0s?sZ;Sht0nE7Kl>OVXy=gjWxxK;OJ3yGd7-pZf7JYNcZo2*1SF`u6 zHJyRRxGw9mDlOiXqVMsNe#WX`fC`vrtjSQ%KmLcl(lC>ZOQzG^%iql2w-f_K@r?OE zwCICifM#L-HJyc7Gm>Ern?+Sk3&|Khmu4(~3qa$(m6Ub^U0E5RHq49za|XklN#?kP zl;EstdW?(_4D>kwjWy2f!LM)y?F94kyU3`W!6+AyId-89v}sXJpuic^NLL7GJItl~ zsiuB98AI-(#Mnm|=A-R6&2fwJ0JVSY#Q>&3$zFh|@;#%0qeF=j5Ajq@4i0tIIW z&}sk$&fGwoJpe&u-JeGLi^r?dO`m=y(QO{@h zQqAC7$rvz&5+mo3IqE?h=a~6m>%r5Quapvzq;{y~p zJpyXOBgD9VrW7@#p6l7O?o3feml(DtSL>D^R) zZUY%T2b0-vBAFN7VB;M88!~HuOXi4KcI6aRQ&h|XQ0A?m%j2=l1f0cGP}h(oVfJ`N zz#PpmFC*ieab)zJK<4?^k=g%OjPnkANzbAbmGZHoVRk*mTfm75s_cWVa`l*f$B@xu z5E*?&@seIo#*Y~1rBm!7sF9~~u6Wrj5oICUOuz}CS)jdNIznfzCA(stJ(7$c^e5wN z?lt>eYgbA!kvAR7zYSD&*r1$b|(@;9dcZ^67R0 zXAXJKa|5Sdmj!g578Nwt6d$sXuc&MWezA0Whd`94$h{{?1IwXP4)Tx4obDK%xoFZ_Z zjjHJ_P@R_e5blG@yEjnaJb`l;s%Lb2&=8$&Ct-fV`E^4CUs)=jTk!I}2d&n!f@)bm z@ z_4Dc86+3l2*p|~;o-Sb~oXb_RuLmoifDU^&Te$*FevycC0*nE3Xws8gsWp|Rj2>SM zns)qcYj?^2sd8?N!_w~4v+f-HCF|a$TNZDoNl$I1Uq87euoNgKb6&r26TNrfkUa@o zfdiFA@p{K&mH3b8i!lcoz)V{n8Q@g(vR4ns4r6w;K z>1~ecQR0-<^J|Ndg5fvVUM9g;lbu-){#ghGw(fg>L zh)T5Ljb%lWE;V9L!;Cqk>AV1(rULYF07ZBJbGb9qbSoLAd;in9{)95YqX$J43-dY7YU*k~vrM25 zxh5_IqO0LYZW%oxQ5HOzmk4x{atE*vipUk}sh88$b2tn?!ujEHn`tQLe&vo}nMb&{ zio`xzZ&GG6&ZyN3jnaQy#iVqXE9VT(3tWY$n-)uWDQ|tc{`?fq2F`oQ{;d3aWPg4Hp-(iE{ry>MIPWL> iW8Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/google_maps_flutter/google_maps_flutter_ios/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/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/google_maps_flutter/google_maps_flutter_ios/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/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 000000000000..89c2725b70f1 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000000..f2e259c7c939 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Base.lproj/Main.storyboard b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Info.plist b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Info.plist new file mode 100644 index 000000000000..0fa9c73c5d42 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/Info.plist @@ -0,0 +1,51 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + google_maps_flutter_example + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSLocationWhenInUseUsageDescription + This app needs your location to test the location feature of the Google Maps plugin. + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/main.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/main.m new file mode 100644 index 000000000000..f143297b30d6 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/Runner/main.m @@ -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 +#import +#import "AppDelegate.h" + +int main(int argc, char *argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m similarity index 99% rename from packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m rename to packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m index bf226feb2341..bb9020d983c4 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import google_maps_flutter; -@import google_maps_flutter.Test; +@import google_maps_flutter_ios; +@import google_maps_flutter_ios.Test; @import XCTest; @import MapKit; @import GoogleMaps; diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/GoogleMapsTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/GoogleMapsTests.m similarity index 94% rename from packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/GoogleMapsTests.m rename to packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/GoogleMapsTests.m index f03dca24fe7c..a8768e18d73b 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/GoogleMapsTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/GoogleMapsTests.m @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -@import google_maps_flutter; -@import google_maps_flutter.Test; +@import google_maps_flutter_ios; +@import google_maps_flutter_ios.Test; @import XCTest; #import diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/Info.plist b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/Info.plist similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/Info.plist rename to packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/Info.plist diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.h b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/PartiallyMockedMapView.h similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.h rename to packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/PartiallyMockedMapView.h diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/PartiallyMockedMapView.m similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerTests/PartiallyMockedMapView.m rename to packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/PartiallyMockedMapView.m diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerUITests/GoogleMapsUITests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerUITests/GoogleMapsUITests.m similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerUITests/GoogleMapsUITests.m rename to packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerUITests/GoogleMapsUITests.m diff --git a/packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerUITests/Info.plist b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerUITests/Info.plist similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/example/ios/RunnerUITests/Info.plist rename to packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerUITests/Info.plist diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/animate_camera.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/animate_camera.dart new file mode 100644 index 000000000000..c34a3ba4b2fe --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/animate_camera.dart @@ -0,0 +1,171 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +class AnimateCameraPage extends GoogleMapExampleAppPage { + const AnimateCameraPage({Key? key}) + : super(const Icon(Icons.map), 'Camera control, animated', key: key); + + @override + Widget build(BuildContext context) { + return const AnimateCamera(); + } +} + +class AnimateCamera extends StatefulWidget { + const AnimateCamera({Key? key}) : super(key: key); + @override + State createState() => AnimateCameraState(); +} + +class AnimateCameraState extends State { + ExampleGoogleMapController? mapController; + + // ignore: use_setters_to_change_properties + void _onMapCreated(ExampleGoogleMapController controller) { + mapController = controller; + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: SizedBox( + width: 300.0, + height: 200.0, + child: ExampleGoogleMap( + onMapCreated: _onMapCreated, + initialCameraPosition: + const CameraPosition(target: LatLng(0.0, 0.0)), + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + children: [ + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.newCameraPosition( + const CameraPosition( + bearing: 270.0, + target: LatLng(51.5160895, -0.1294527), + tilt: 30.0, + zoom: 17.0, + ), + ), + ); + }, + child: const Text('newCameraPosition'), + ), + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.newLatLng( + const LatLng(56.1725505, 10.1850512), + ), + ); + }, + child: const Text('newLatLng'), + ), + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.newLatLngBounds( + LatLngBounds( + southwest: const LatLng(-38.483935, 113.248673), + northeast: const LatLng(-8.982446, 153.823821), + ), + 10.0, + ), + ); + }, + child: const Text('newLatLngBounds'), + ), + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.newLatLngZoom( + const LatLng(37.4231613, -122.087159), + 11.0, + ), + ); + }, + child: const Text('newLatLngZoom'), + ), + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.scrollBy(150.0, -225.0), + ); + }, + child: const Text('scrollBy'), + ), + ], + ), + Column( + children: [ + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.zoomBy( + -0.5, + const Offset(30.0, 20.0), + ), + ); + }, + child: const Text('zoomBy with focus'), + ), + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.zoomBy(-0.5), + ); + }, + child: const Text('zoomBy'), + ), + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.zoomIn(), + ); + }, + child: const Text('zoomIn'), + ), + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.zoomOut(), + ); + }, + child: const Text('zoomOut'), + ), + TextButton( + onPressed: () { + mapController?.animateCamera( + CameraUpdate.zoomTo(16.0), + ); + }, + child: const Text('zoomTo'), + ), + ], + ), + ], + ) + ], + ); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/example_google_map.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/example_google_map.dart new file mode 100644 index 000000000000..e2c713163a2a --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/example_google_map.dart @@ -0,0 +1,538 @@ +// 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:async'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import +import 'dart:typed_data'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +// This is a pared down version of the Dart code from the app-facing package, +// to allow running the same examples for package-local testing. +// TODO(stuartmorgan): Consider extracting this to a shared package. See also +// https://github.com/flutter/flutter/issues/46716. + +/// Controller for a single ExampleGoogleMap instance running on the host platform. +class ExampleGoogleMapController { + ExampleGoogleMapController._( + this._googleMapState, { + required this.mapId, + }) { + _connectStreams(mapId); + } + + /// The mapId for this controller + final int mapId; + + /// Initialize control of a [ExampleGoogleMap] with [id]. + /// + /// Mainly for internal use when instantiating a [ExampleGoogleMapController] passed + /// in [ExampleGoogleMap.onMapCreated] callback. + static Future _init( + int id, + CameraPosition initialCameraPosition, + _ExampleGoogleMapState googleMapState, + ) async { + await GoogleMapsFlutterPlatform.instance.init(id); + return ExampleGoogleMapController._( + googleMapState, + mapId: id, + ); + } + + final _ExampleGoogleMapState _googleMapState; + + void _connectStreams(int mapId) { + if (_googleMapState.widget.onCameraMoveStarted != null) { + GoogleMapsFlutterPlatform.instance + .onCameraMoveStarted(mapId: mapId) + .listen((_) => _googleMapState.widget.onCameraMoveStarted!()); + } + if (_googleMapState.widget.onCameraMove != null) { + GoogleMapsFlutterPlatform.instance.onCameraMove(mapId: mapId).listen( + (CameraMoveEvent e) => _googleMapState.widget.onCameraMove!(e.value)); + } + if (_googleMapState.widget.onCameraIdle != null) { + GoogleMapsFlutterPlatform.instance + .onCameraIdle(mapId: mapId) + .listen((_) => _googleMapState.widget.onCameraIdle!()); + } + GoogleMapsFlutterPlatform.instance + .onMarkerTap(mapId: mapId) + .listen((MarkerTapEvent e) => _googleMapState.onMarkerTap(e.value)); + GoogleMapsFlutterPlatform.instance.onMarkerDragStart(mapId: mapId).listen( + (MarkerDragStartEvent e) => + _googleMapState.onMarkerDragStart(e.value, e.position)); + GoogleMapsFlutterPlatform.instance.onMarkerDrag(mapId: mapId).listen( + (MarkerDragEvent e) => + _googleMapState.onMarkerDrag(e.value, e.position)); + GoogleMapsFlutterPlatform.instance.onMarkerDragEnd(mapId: mapId).listen( + (MarkerDragEndEvent e) => + _googleMapState.onMarkerDragEnd(e.value, e.position)); + GoogleMapsFlutterPlatform.instance.onInfoWindowTap(mapId: mapId).listen( + (InfoWindowTapEvent e) => _googleMapState.onInfoWindowTap(e.value)); + GoogleMapsFlutterPlatform.instance + .onPolylineTap(mapId: mapId) + .listen((PolylineTapEvent e) => _googleMapState.onPolylineTap(e.value)); + GoogleMapsFlutterPlatform.instance + .onPolygonTap(mapId: mapId) + .listen((PolygonTapEvent e) => _googleMapState.onPolygonTap(e.value)); + GoogleMapsFlutterPlatform.instance + .onCircleTap(mapId: mapId) + .listen((CircleTapEvent e) => _googleMapState.onCircleTap(e.value)); + GoogleMapsFlutterPlatform.instance + .onTap(mapId: mapId) + .listen((MapTapEvent e) => _googleMapState.onTap(e.position)); + GoogleMapsFlutterPlatform.instance.onLongPress(mapId: mapId).listen( + (MapLongPressEvent e) => _googleMapState.onLongPress(e.position)); + } + + /// Updates configuration options of the map user interface. + Future _updateMapConfiguration(MapConfiguration update) { + return GoogleMapsFlutterPlatform.instance + .updateMapConfiguration(update, mapId: mapId); + } + + /// Updates marker configuration. + Future _updateMarkers(MarkerUpdates markerUpdates) { + return GoogleMapsFlutterPlatform.instance + .updateMarkers(markerUpdates, mapId: mapId); + } + + /// Updates polygon configuration. + Future _updatePolygons(PolygonUpdates polygonUpdates) { + return GoogleMapsFlutterPlatform.instance + .updatePolygons(polygonUpdates, mapId: mapId); + } + + /// Updates polyline configuration. + Future _updatePolylines(PolylineUpdates polylineUpdates) { + return GoogleMapsFlutterPlatform.instance + .updatePolylines(polylineUpdates, mapId: mapId); + } + + /// Updates circle configuration. + Future _updateCircles(CircleUpdates circleUpdates) { + return GoogleMapsFlutterPlatform.instance + .updateCircles(circleUpdates, mapId: mapId); + } + + /// Updates tile overlays configuration. + Future _updateTileOverlays(Set newTileOverlays) { + return GoogleMapsFlutterPlatform.instance + .updateTileOverlays(newTileOverlays: newTileOverlays, mapId: mapId); + } + + /// Clears the tile cache so that all tiles will be requested again from the + /// [TileProvider]. + Future clearTileCache(TileOverlayId tileOverlayId) async { + return GoogleMapsFlutterPlatform.instance + .clearTileCache(tileOverlayId, mapId: mapId); + } + + /// Starts an animated change of the map camera position. + Future animateCamera(CameraUpdate cameraUpdate) { + return GoogleMapsFlutterPlatform.instance + .animateCamera(cameraUpdate, mapId: mapId); + } + + /// Changes the map camera position. + Future moveCamera(CameraUpdate cameraUpdate) { + return GoogleMapsFlutterPlatform.instance + .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); + } + + /// Return [ScreenCoordinate] of the [LatLng] in the current map view. + Future getScreenCoordinate(LatLng latLng) { + return GoogleMapsFlutterPlatform.instance + .getScreenCoordinate(latLng, mapId: mapId); + } + + /// Returns [LatLng] corresponding to the [ScreenCoordinate] in the current map view. + Future getLatLng(ScreenCoordinate screenCoordinate) { + return GoogleMapsFlutterPlatform.instance + .getLatLng(screenCoordinate, mapId: mapId); + } + + /// Programmatically show the Info Window for a [Marker]. + Future showMarkerInfoWindow(MarkerId markerId) { + return GoogleMapsFlutterPlatform.instance + .showMarkerInfoWindow(markerId, mapId: mapId); + } + + /// Programmatically hide the Info Window for a [Marker]. + Future hideMarkerInfoWindow(MarkerId markerId) { + return GoogleMapsFlutterPlatform.instance + .hideMarkerInfoWindow(markerId, mapId: mapId); + } + + /// Returns `true` when the [InfoWindow] is showing, `false` otherwise. + Future isMarkerInfoWindowShown(MarkerId markerId) { + return GoogleMapsFlutterPlatform.instance + .isMarkerInfoWindowShown(markerId, mapId: mapId); + } + + /// Returns the current zoom level of the map + Future getZoomLevel() { + return GoogleMapsFlutterPlatform.instance.getZoomLevel(mapId: mapId); + } + + /// Returns the image bytes of the map + Future takeSnapshot() { + return GoogleMapsFlutterPlatform.instance.takeSnapshot(mapId: mapId); + } + + /// Disposes of the platform resources + void dispose() { + GoogleMapsFlutterPlatform.instance.dispose(mapId: mapId); + } +} + +// The next map ID to create. +int _nextMapCreationId = 0; + +/// A widget which displays a map with data obtained from the Google Maps service. +class ExampleGoogleMap extends StatefulWidget { + /// Creates a widget displaying data from Google Maps services. + /// + /// [AssertionError] will be thrown if [initialCameraPosition] is null; + const ExampleGoogleMap({ + Key? key, + required this.initialCameraPosition, + this.onMapCreated, + this.gestureRecognizers = const >{}, + this.compassEnabled = true, + this.mapToolbarEnabled = true, + this.cameraTargetBounds = CameraTargetBounds.unbounded, + this.mapType = MapType.normal, + this.minMaxZoomPreference = MinMaxZoomPreference.unbounded, + this.rotateGesturesEnabled = true, + this.scrollGesturesEnabled = true, + this.zoomControlsEnabled = true, + this.zoomGesturesEnabled = true, + this.liteModeEnabled = false, + this.tiltGesturesEnabled = true, + this.myLocationEnabled = false, + this.myLocationButtonEnabled = true, + this.layoutDirection, + + /// If no padding is specified default padding will be 0. + this.padding = const EdgeInsets.all(0), + this.indoorViewEnabled = false, + this.trafficEnabled = false, + this.buildingsEnabled = true, + this.markers = const {}, + this.polygons = const {}, + this.polylines = const {}, + this.circles = const {}, + this.onCameraMoveStarted, + this.tileOverlays = const {}, + this.onCameraMove, + this.onCameraIdle, + this.onTap, + this.onLongPress, + }) : super(key: key); + + /// Callback method for when the map is ready to be used. + /// + /// Used to receive a [ExampleGoogleMapController] for this [ExampleGoogleMap]. + final void Function(ExampleGoogleMapController controller)? onMapCreated; + + /// The initial position of the map's camera. + final CameraPosition initialCameraPosition; + + /// True if the map should show a compass when rotated. + final bool compassEnabled; + + /// True if the map should show a toolbar when you interact with the map. Android only. + final bool mapToolbarEnabled; + + /// Geographical bounding box for the camera target. + final CameraTargetBounds cameraTargetBounds; + + /// Type of map tiles to be rendered. + final MapType mapType; + + /// The layout direction to use for the embedded view. + final TextDirection? layoutDirection; + + /// Preferred bounds for the camera zoom level. + /// + /// Actual bounds depend on map data and device. + final MinMaxZoomPreference minMaxZoomPreference; + + /// True if the map view should respond to rotate gestures. + final bool rotateGesturesEnabled; + + /// True if the map view should respond to scroll gestures. + final bool scrollGesturesEnabled; + + /// True if the map view should show zoom controls. This includes two buttons + /// to zoom in and zoom out. The default value is to show zoom controls. + final bool zoomControlsEnabled; + + /// True if the map view should respond to zoom gestures. + final bool zoomGesturesEnabled; + + /// True if the map view should be in lite mode. Android only. + final bool liteModeEnabled; + + /// True if the map view should respond to tilt gestures. + final bool tiltGesturesEnabled; + + /// Padding to be set on map. + final EdgeInsets padding; + + /// Markers to be placed on the map. + final Set markers; + + /// Polygons to be placed on the map. + final Set polygons; + + /// Polylines to be placed on the map. + final Set polylines; + + /// Circles to be placed on the map. + final Set circles; + + /// Tile overlays to be placed on the map. + final Set tileOverlays; + + /// Called when the camera starts moving. + final VoidCallback? onCameraMoveStarted; + + /// Called repeatedly as the camera continues to move after an + /// onCameraMoveStarted call. + final CameraPositionCallback? onCameraMove; + + /// Called when camera movement has ended, there are no pending + /// animations and the user has stopped interacting with the map. + final VoidCallback? onCameraIdle; + + /// Called every time a [ExampleGoogleMap] is tapped. + final ArgumentCallback? onTap; + + /// Called every time a [ExampleGoogleMap] is long pressed. + final ArgumentCallback? onLongPress; + + /// True if a "My Location" layer should be shown on the map. + final bool myLocationEnabled; + + /// Enables or disables the my-location button. + final bool myLocationButtonEnabled; + + /// Enables or disables the indoor view from the map + final bool indoorViewEnabled; + + /// Enables or disables the traffic layer of the map + final bool trafficEnabled; + + /// Enables or disables showing 3D buildings where available + final bool buildingsEnabled; + + /// Which gestures should be consumed by the map. + final Set> gestureRecognizers; + + /// Creates a [State] for this [ExampleGoogleMap]. + @override + State createState() => _ExampleGoogleMapState(); +} + +class _ExampleGoogleMapState extends State { + final int _mapId = _nextMapCreationId++; + + final Completer _controller = + Completer(); + + Map _markers = {}; + Map _polygons = {}; + Map _polylines = {}; + Map _circles = {}; + late MapConfiguration _mapConfiguration; + + @override + Widget build(BuildContext context) { + return GoogleMapsFlutterPlatform.instance.buildViewWithConfiguration( + _mapId, + onPlatformViewCreated, + widgetConfiguration: MapWidgetConfiguration( + textDirection: widget.layoutDirection ?? + Directionality.maybeOf(context) ?? + TextDirection.ltr, + initialCameraPosition: widget.initialCameraPosition, + gestureRecognizers: widget.gestureRecognizers, + ), + mapObjects: MapObjects( + markers: widget.markers, + polygons: widget.polygons, + polylines: widget.polylines, + circles: widget.circles, + ), + mapConfiguration: _mapConfiguration, + ); + } + + @override + void initState() { + super.initState(); + _mapConfiguration = _configurationFromMapWidget(widget); + _markers = keyByMarkerId(widget.markers); + _polygons = keyByPolygonId(widget.polygons); + _polylines = keyByPolylineId(widget.polylines); + _circles = keyByCircleId(widget.circles); + } + + @override + void dispose() { + _controller.future + .then((ExampleGoogleMapController controller) => controller.dispose()); + super.dispose(); + } + + @override + void didUpdateWidget(ExampleGoogleMap oldWidget) { + super.didUpdateWidget(oldWidget); + _updateOptions(); + _updateMarkers(); + _updatePolygons(); + _updatePolylines(); + _updateCircles(); + _updateTileOverlays(); + } + + Future _updateOptions() async { + final MapConfiguration newConfig = _configurationFromMapWidget(widget); + final MapConfiguration updates = newConfig.diffFrom(_mapConfiguration); + if (updates.isEmpty) { + return; + } + final ExampleGoogleMapController controller = await _controller.future; + controller._updateMapConfiguration(updates); + _mapConfiguration = newConfig; + } + + Future _updateMarkers() async { + final ExampleGoogleMapController controller = await _controller.future; + controller._updateMarkers( + MarkerUpdates.from(_markers.values.toSet(), widget.markers)); + _markers = keyByMarkerId(widget.markers); + } + + Future _updatePolygons() async { + final ExampleGoogleMapController controller = await _controller.future; + controller._updatePolygons( + PolygonUpdates.from(_polygons.values.toSet(), widget.polygons)); + _polygons = keyByPolygonId(widget.polygons); + } + + Future _updatePolylines() async { + final ExampleGoogleMapController controller = await _controller.future; + controller._updatePolylines( + PolylineUpdates.from(_polylines.values.toSet(), widget.polylines)); + _polylines = keyByPolylineId(widget.polylines); + } + + Future _updateCircles() async { + final ExampleGoogleMapController controller = await _controller.future; + controller._updateCircles( + CircleUpdates.from(_circles.values.toSet(), widget.circles)); + _circles = keyByCircleId(widget.circles); + } + + Future _updateTileOverlays() async { + final ExampleGoogleMapController controller = await _controller.future; + controller._updateTileOverlays(widget.tileOverlays); + } + + Future onPlatformViewCreated(int id) async { + final ExampleGoogleMapController controller = + await ExampleGoogleMapController._init( + id, + widget.initialCameraPosition, + this, + ); + _controller.complete(controller); + _updateTileOverlays(); + widget.onMapCreated?.call(controller); + } + + void onMarkerTap(MarkerId markerId) { + _markers[markerId]!.onTap?.call(); + } + + void onMarkerDragStart(MarkerId markerId, LatLng position) { + _markers[markerId]!.onDragStart?.call(position); + } + + void onMarkerDrag(MarkerId markerId, LatLng position) { + _markers[markerId]!.onDrag?.call(position); + } + + void onMarkerDragEnd(MarkerId markerId, LatLng position) { + _markers[markerId]!.onDragEnd?.call(position); + } + + void onPolygonTap(PolygonId polygonId) { + _polygons[polygonId]!.onTap?.call(); + } + + void onPolylineTap(PolylineId polylineId) { + _polylines[polylineId]!.onTap?.call(); + } + + void onCircleTap(CircleId circleId) { + _circles[circleId]!.onTap?.call(); + } + + void onInfoWindowTap(MarkerId markerId) { + _markers[markerId]!.infoWindow.onTap?.call(); + } + + void onTap(LatLng position) { + widget.onTap?.call(position); + } + + void onLongPress(LatLng position) { + widget.onLongPress?.call(position); + } +} + +/// Builds a [MapConfiguration] from the given [map]. +MapConfiguration _configurationFromMapWidget(ExampleGoogleMap map) { + return MapConfiguration( + compassEnabled: map.compassEnabled, + mapToolbarEnabled: map.mapToolbarEnabled, + cameraTargetBounds: map.cameraTargetBounds, + mapType: map.mapType, + minMaxZoomPreference: map.minMaxZoomPreference, + rotateGesturesEnabled: map.rotateGesturesEnabled, + scrollGesturesEnabled: map.scrollGesturesEnabled, + tiltGesturesEnabled: map.tiltGesturesEnabled, + trackCameraPosition: map.onCameraMove != null, + zoomControlsEnabled: map.zoomControlsEnabled, + zoomGesturesEnabled: map.zoomGesturesEnabled, + liteModeEnabled: map.liteModeEnabled, + myLocationEnabled: map.myLocationEnabled, + myLocationButtonEnabled: map.myLocationButtonEnabled, + padding: map.padding, + indoorViewEnabled: map.indoorViewEnabled, + trafficEnabled: map.trafficEnabled, + buildingsEnabled: map.buildingsEnabled, + ); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/lite_mode.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/lite_mode.dart new file mode 100644 index 000000000000..f7bead951f5d --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/lite_mode.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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +const CameraPosition _kInitialPosition = + CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); + +class LiteModePage extends GoogleMapExampleAppPage { + const LiteModePage({Key? key}) + : super(const Icon(Icons.map), 'Lite mode', key: key); + + @override + Widget build(BuildContext context) { + return const _LiteModeBody(); + } +} + +class _LiteModeBody extends StatelessWidget { + const _LiteModeBody(); + + @override + Widget build(BuildContext context) { + return const Card( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 30.0), + child: Center( + child: SizedBox( + width: 300.0, + height: 300.0, + child: ExampleGoogleMap( + initialCameraPosition: _kInitialPosition, + liteModeEnabled: true, + ), + ), + ), + ), + ); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/main.dart new file mode 100644 index 000000000000..c02e4afe428d --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/main.dart @@ -0,0 +1,73 @@ +// 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:google_maps_flutter_example/lite_mode.dart'; + +import 'animate_camera.dart'; +import 'map_click.dart'; +import 'map_coordinates.dart'; +import 'map_ui.dart'; +import 'marker_icons.dart'; +import 'move_camera.dart'; +import 'padding.dart'; +import 'page.dart'; +import 'place_circle.dart'; +import 'place_marker.dart'; +import 'place_polygon.dart'; +import 'place_polyline.dart'; +import 'scrolling_map.dart'; +import 'snapshot.dart'; +import 'tile_overlay.dart'; + +final List _allPages = [ + const MapUiPage(), + const MapCoordinatesPage(), + const MapClickPage(), + const AnimateCameraPage(), + const MoveCameraPage(), + const PlaceMarkerPage(), + const MarkerIconsPage(), + const ScrollingMapPage(), + const PlacePolylinePage(), + const PlacePolygonPage(), + const PlaceCirclePage(), + const PaddingPage(), + const SnapshotPage(), + const LiteModePage(), + const TileOverlayPage(), +]; + +/// MapsDemo is the Main Application. +class MapsDemo extends StatelessWidget { + /// Default Constructor + const MapsDemo({Key? key}) : super(key: key); + + void _pushPage(BuildContext context, GoogleMapExampleAppPage page) { + Navigator.of(context).push(MaterialPageRoute( + builder: (_) => Scaffold( + appBar: AppBar(title: Text(page.title)), + body: page, + ))); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('GoogleMaps examples')), + body: ListView.builder( + itemCount: _allPages.length, + itemBuilder: (_, int index) => ListTile( + leading: _allPages[index].leading, + title: Text(_allPages[index].title), + onTap: () => _pushPage(context, _allPages[index]), + ), + ), + ); + } +} + +void main() { + runApp(const MaterialApp(home: MapsDemo())); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_click.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_click.dart new file mode 100644 index 000000000000..20718d9751ab --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_click.dart @@ -0,0 +1,106 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +const CameraPosition _kInitialPosition = + CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); + +class MapClickPage extends GoogleMapExampleAppPage { + const MapClickPage({Key? key}) + : super(const Icon(Icons.mouse), 'Map click', key: key); + + @override + Widget build(BuildContext context) { + return const _MapClickBody(); + } +} + +class _MapClickBody extends StatefulWidget { + const _MapClickBody(); + + @override + State createState() => _MapClickBodyState(); +} + +class _MapClickBodyState extends State<_MapClickBody> { + _MapClickBodyState(); + + ExampleGoogleMapController? mapController; + LatLng? _lastTap; + LatLng? _lastLongPress; + + @override + Widget build(BuildContext context) { + final ExampleGoogleMap googleMap = ExampleGoogleMap( + onMapCreated: onMapCreated, + initialCameraPosition: _kInitialPosition, + onTap: (LatLng pos) { + setState(() { + _lastTap = pos; + }); + }, + onLongPress: (LatLng pos) { + setState(() { + _lastLongPress = pos; + }); + }, + ); + + final List columnChildren = [ + Padding( + padding: const EdgeInsets.all(10.0), + child: Center( + child: SizedBox( + width: 300.0, + height: 200.0, + child: googleMap, + ), + ), + ), + ]; + + if (mapController != null) { + final String lastTap = 'Tap:\n${_lastTap ?? ""}\n'; + final String lastLongPress = 'Long press:\n${_lastLongPress ?? ""}'; + columnChildren.add(Center( + child: Text( + lastTap, + textAlign: TextAlign.center, + ))); + columnChildren.add(Center( + child: Text( + _lastTap != null ? 'Tapped' : '', + textAlign: TextAlign.center, + ))); + columnChildren.add(Center( + child: Text( + lastLongPress, + textAlign: TextAlign.center, + ))); + columnChildren.add(Center( + child: Text( + _lastLongPress != null ? 'Long pressed' : '', + textAlign: TextAlign.center, + ))); + } + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: columnChildren, + ); + } + + Future onMapCreated(ExampleGoogleMapController controller) async { + setState(() { + mapController = controller; + }); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_coordinates.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_coordinates.dart new file mode 100644 index 000000000000..185a97e08f00 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_coordinates.dart @@ -0,0 +1,100 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +const CameraPosition _kInitialPosition = + CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); + +class MapCoordinatesPage extends GoogleMapExampleAppPage { + const MapCoordinatesPage({Key? key}) + : super(const Icon(Icons.map), 'Map coordinates', key: key); + + @override + Widget build(BuildContext context) { + return const _MapCoordinatesBody(); + } +} + +class _MapCoordinatesBody extends StatefulWidget { + const _MapCoordinatesBody(); + + @override + State createState() => _MapCoordinatesBodyState(); +} + +class _MapCoordinatesBodyState extends State<_MapCoordinatesBody> { + _MapCoordinatesBodyState(); + + ExampleGoogleMapController? mapController; + LatLngBounds _visibleRegion = LatLngBounds( + southwest: const LatLng(0, 0), + northeast: const LatLng(0, 0), + ); + + @override + Widget build(BuildContext context) { + final ExampleGoogleMap googleMap = ExampleGoogleMap( + onMapCreated: onMapCreated, + initialCameraPosition: _kInitialPosition, + onCameraIdle: + _updateVisibleRegion, // https://github.com/flutter/flutter/issues/54758 + ); + + return NotificationListener( + onNotification: (ScrollNotification scrollState) { + _updateVisibleRegion(); + return true; + }, + child: ListView( + children: [ + Padding( + padding: const EdgeInsets.all(10.0), + child: Center( + child: SizedBox( + width: 300.0, + height: 200.0, + child: googleMap, + ), + ), + ), + if (mapController != null) + Center( + child: Text('VisibleRegion:' + '\nnortheast: ${_visibleRegion.northeast},' + '\nsouthwest: ${_visibleRegion.southwest}'), + ), + // Add a block at the bottom of this list to allow validation that the visible region of the map + // does not change when scrolled under the safe view on iOS. + // https://github.com/flutter/flutter/issues/107913 + Container( + width: 300, + height: 1000, + ), + ], + ), + ); + } + + Future onMapCreated(ExampleGoogleMapController controller) async { + final LatLngBounds visibleRegion = await controller.getVisibleRegion(); + setState(() { + mapController = controller; + _visibleRegion = visibleRegion; + }); + } + + Future _updateVisibleRegion() async { + final LatLngBounds visibleRegion = await mapController!.getVisibleRegion(); + setState(() { + _visibleRegion = visibleRegion; + }); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_ui.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_ui.dart new file mode 100644 index 000000000000..6c38e14d86a5 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_ui.dart @@ -0,0 +1,358 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart' show rootBundle; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +final LatLngBounds sydneyBounds = LatLngBounds( + southwest: const LatLng(-34.022631, 150.620685), + northeast: const LatLng(-33.571835, 151.325952), +); + +class MapUiPage extends GoogleMapExampleAppPage { + const MapUiPage({Key? key}) + : super(const Icon(Icons.map), 'User interface', key: key); + + @override + Widget build(BuildContext context) { + return const MapUiBody(); + } +} + +class MapUiBody extends StatefulWidget { + const MapUiBody({Key? key}) : super(key: key); + + @override + State createState() => MapUiBodyState(); +} + +class MapUiBodyState extends State { + MapUiBodyState(); + + static const CameraPosition _kInitialPosition = CameraPosition( + target: LatLng(-33.852, 151.211), + zoom: 11.0, + ); + + CameraPosition _position = _kInitialPosition; + bool _isMapCreated = false; + final bool _isMoving = false; + bool _compassEnabled = true; + bool _mapToolbarEnabled = true; + CameraTargetBounds _cameraTargetBounds = CameraTargetBounds.unbounded; + MinMaxZoomPreference _minMaxZoomPreference = MinMaxZoomPreference.unbounded; + MapType _mapType = MapType.normal; + bool _rotateGesturesEnabled = true; + bool _scrollGesturesEnabled = true; + bool _tiltGesturesEnabled = true; + bool _zoomControlsEnabled = false; + bool _zoomGesturesEnabled = true; + bool _indoorViewEnabled = true; + bool _myLocationEnabled = true; + bool _myTrafficEnabled = false; + bool _myLocationButtonEnabled = true; + late ExampleGoogleMapController _controller; + bool _nightMode = false; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + Widget _compassToggler() { + return TextButton( + child: Text('${_compassEnabled ? 'disable' : 'enable'} compass'), + onPressed: () { + setState(() { + _compassEnabled = !_compassEnabled; + }); + }, + ); + } + + Widget _mapToolbarToggler() { + return TextButton( + child: Text('${_mapToolbarEnabled ? 'disable' : 'enable'} map toolbar'), + onPressed: () { + setState(() { + _mapToolbarEnabled = !_mapToolbarEnabled; + }); + }, + ); + } + + Widget _latLngBoundsToggler() { + return TextButton( + child: Text( + _cameraTargetBounds.bounds == null + ? 'bound camera target' + : 'release camera target', + ), + onPressed: () { + setState(() { + _cameraTargetBounds = _cameraTargetBounds.bounds == null + ? CameraTargetBounds(sydneyBounds) + : CameraTargetBounds.unbounded; + }); + }, + ); + } + + Widget _zoomBoundsToggler() { + return TextButton( + child: Text(_minMaxZoomPreference.minZoom == null + ? 'bound zoom' + : 'release zoom'), + onPressed: () { + setState(() { + _minMaxZoomPreference = _minMaxZoomPreference.minZoom == null + ? const MinMaxZoomPreference(12.0, 16.0) + : MinMaxZoomPreference.unbounded; + }); + }, + ); + } + + Widget _mapTypeCycler() { + final MapType nextType = + MapType.values[(_mapType.index + 1) % MapType.values.length]; + return TextButton( + child: Text('change map type to $nextType'), + onPressed: () { + setState(() { + _mapType = nextType; + }); + }, + ); + } + + Widget _rotateToggler() { + return TextButton( + child: Text('${_rotateGesturesEnabled ? 'disable' : 'enable'} rotate'), + onPressed: () { + setState(() { + _rotateGesturesEnabled = !_rotateGesturesEnabled; + }); + }, + ); + } + + Widget _scrollToggler() { + return TextButton( + child: Text('${_scrollGesturesEnabled ? 'disable' : 'enable'} scroll'), + onPressed: () { + setState(() { + _scrollGesturesEnabled = !_scrollGesturesEnabled; + }); + }, + ); + } + + Widget _tiltToggler() { + return TextButton( + child: Text('${_tiltGesturesEnabled ? 'disable' : 'enable'} tilt'), + onPressed: () { + setState(() { + _tiltGesturesEnabled = !_tiltGesturesEnabled; + }); + }, + ); + } + + Widget _zoomToggler() { + return TextButton( + child: Text('${_zoomGesturesEnabled ? 'disable' : 'enable'} zoom'), + onPressed: () { + setState(() { + _zoomGesturesEnabled = !_zoomGesturesEnabled; + }); + }, + ); + } + + Widget _zoomControlsToggler() { + return TextButton( + child: + Text('${_zoomControlsEnabled ? 'disable' : 'enable'} zoom controls'), + onPressed: () { + setState(() { + _zoomControlsEnabled = !_zoomControlsEnabled; + }); + }, + ); + } + + Widget _indoorViewToggler() { + return TextButton( + child: Text('${_indoorViewEnabled ? 'disable' : 'enable'} indoor'), + onPressed: () { + setState(() { + _indoorViewEnabled = !_indoorViewEnabled; + }); + }, + ); + } + + Widget _myLocationToggler() { + return TextButton( + child: Text( + '${_myLocationEnabled ? 'disable' : 'enable'} my location marker'), + onPressed: () { + setState(() { + _myLocationEnabled = !_myLocationEnabled; + }); + }, + ); + } + + Widget _myLocationButtonToggler() { + return TextButton( + child: Text( + '${_myLocationButtonEnabled ? 'disable' : 'enable'} my location button'), + onPressed: () { + setState(() { + _myLocationButtonEnabled = !_myLocationButtonEnabled; + }); + }, + ); + } + + Widget _myTrafficToggler() { + return TextButton( + child: Text('${_myTrafficEnabled ? 'disable' : 'enable'} my traffic'), + onPressed: () { + setState(() { + _myTrafficEnabled = !_myTrafficEnabled; + }); + }, + ); + } + + Future _getFileData(String path) async { + return await 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); + } + }, + ); + } + + @override + Widget build(BuildContext context) { + final ExampleGoogleMap googleMap = ExampleGoogleMap( + onMapCreated: onMapCreated, + initialCameraPosition: _kInitialPosition, + compassEnabled: _compassEnabled, + mapToolbarEnabled: _mapToolbarEnabled, + cameraTargetBounds: _cameraTargetBounds, + minMaxZoomPreference: _minMaxZoomPreference, + mapType: _mapType, + rotateGesturesEnabled: _rotateGesturesEnabled, + scrollGesturesEnabled: _scrollGesturesEnabled, + tiltGesturesEnabled: _tiltGesturesEnabled, + zoomGesturesEnabled: _zoomGesturesEnabled, + zoomControlsEnabled: _zoomControlsEnabled, + indoorViewEnabled: _indoorViewEnabled, + myLocationEnabled: _myLocationEnabled, + myLocationButtonEnabled: _myLocationButtonEnabled, + trafficEnabled: _myTrafficEnabled, + onCameraMove: _updateCameraPosition, + ); + + final List columnChildren = [ + Padding( + padding: const EdgeInsets.all(10.0), + child: Center( + child: SizedBox( + width: 300.0, + height: 200.0, + child: googleMap, + ), + ), + ), + ]; + + if (_isMapCreated) { + columnChildren.add( + Expanded( + child: ListView( + children: [ + Text('camera bearing: ${_position.bearing}'), + Text( + 'camera target: ${_position.target.latitude.toStringAsFixed(4)},' + '${_position.target.longitude.toStringAsFixed(4)}'), + Text('camera zoom: ${_position.zoom}'), + Text('camera tilt: ${_position.tilt}'), + Text(_isMoving ? '(Camera moving)' : '(Camera idle)'), + _compassToggler(), + _mapToolbarToggler(), + _latLngBoundsToggler(), + _mapTypeCycler(), + _zoomBoundsToggler(), + _rotateToggler(), + _scrollToggler(), + _tiltToggler(), + _zoomToggler(), + _zoomControlsToggler(), + _indoorViewToggler(), + _myLocationToggler(), + _myLocationButtonToggler(), + _myTrafficToggler(), + _nightModeToggler(), + ], + ), + ), + ); + } + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: columnChildren, + ); + } + + void _updateCameraPosition(CameraPosition position) { + setState(() { + _position = position; + }); + } + + void onMapCreated(ExampleGoogleMapController controller) { + setState(() { + _controller = controller; + _isMapCreated = true; + }); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/marker_icons.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/marker_icons.dart new file mode 100644 index 000000000000..fe28eb680596 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/marker_icons.dart @@ -0,0 +1,98 @@ +// 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. + +// ignore_for_file: public_member_api_docs +// ignore_for_file: unawaited_futures + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +class MarkerIconsPage extends GoogleMapExampleAppPage { + const MarkerIconsPage({Key? key}) + : super(const Icon(Icons.image), 'Marker icons', key: key); + + @override + Widget build(BuildContext context) { + return const MarkerIconsBody(); + } +} + +class MarkerIconsBody extends StatefulWidget { + const MarkerIconsBody({Key? key}) : super(key: key); + + @override + State createState() => MarkerIconsBodyState(); +} + +const LatLng _kMapCenter = LatLng(52.4478, -3.5402); + +class MarkerIconsBodyState extends State { + ExampleGoogleMapController? controller; + BitmapDescriptor? _markerIcon; + + @override + Widget build(BuildContext context) { + _createMarkerImageFromAsset(context); + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: SizedBox( + width: 350.0, + height: 300.0, + child: ExampleGoogleMap( + initialCameraPosition: const CameraPosition( + target: _kMapCenter, + zoom: 7.0, + ), + markers: {_createMarker()}, + onMapCreated: _onMapCreated, + ), + ), + ) + ], + ); + } + + Marker _createMarker() { + if (_markerIcon != null) { + return Marker( + markerId: const MarkerId('marker_1'), + position: _kMapCenter, + icon: _markerIcon!, + ); + } else { + return const Marker( + markerId: MarkerId('marker_1'), + position: _kMapCenter, + ); + } + } + + Future _createMarkerImageFromAsset(BuildContext context) async { + if (_markerIcon == null) { + final ImageConfiguration imageConfiguration = + createLocalImageConfiguration(context, size: const Size.square(48)); + BitmapDescriptor.fromAssetImage( + imageConfiguration, 'assets/red_square.png') + .then(_updateBitmap); + } + } + + void _updateBitmap(BitmapDescriptor bitmap) { + setState(() { + _markerIcon = bitmap; + }); + } + + void _onMapCreated(ExampleGoogleMapController controllerParam) { + setState(() { + controller = controllerParam; + }); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/move_camera.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/move_camera.dart new file mode 100644 index 000000000000..7f44d89518dc --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/move_camera.dart @@ -0,0 +1,171 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +class MoveCameraPage extends GoogleMapExampleAppPage { + const MoveCameraPage({Key? key}) + : super(const Icon(Icons.map), 'Camera control', key: key); + + @override + Widget build(BuildContext context) { + return const MoveCamera(); + } +} + +class MoveCamera extends StatefulWidget { + const MoveCamera({Key? key}) : super(key: key); + @override + State createState() => MoveCameraState(); +} + +class MoveCameraState extends State { + ExampleGoogleMapController? mapController; + + // ignore: use_setters_to_change_properties + void _onMapCreated(ExampleGoogleMapController controller) { + mapController = controller; + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: SizedBox( + width: 300.0, + height: 200.0, + child: ExampleGoogleMap( + onMapCreated: _onMapCreated, + initialCameraPosition: + const CameraPosition(target: LatLng(0.0, 0.0)), + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + children: [ + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.newCameraPosition( + const CameraPosition( + bearing: 270.0, + target: LatLng(51.5160895, -0.1294527), + tilt: 30.0, + zoom: 17.0, + ), + ), + ); + }, + child: const Text('newCameraPosition'), + ), + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.newLatLng( + const LatLng(56.1725505, 10.1850512), + ), + ); + }, + child: const Text('newLatLng'), + ), + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.newLatLngBounds( + LatLngBounds( + southwest: const LatLng(-38.483935, 113.248673), + northeast: const LatLng(-8.982446, 153.823821), + ), + 10.0, + ), + ); + }, + child: const Text('newLatLngBounds'), + ), + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.newLatLngZoom( + const LatLng(37.4231613, -122.087159), + 11.0, + ), + ); + }, + child: const Text('newLatLngZoom'), + ), + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.scrollBy(150.0, -225.0), + ); + }, + child: const Text('scrollBy'), + ), + ], + ), + Column( + children: [ + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.zoomBy( + -0.5, + const Offset(30.0, 20.0), + ), + ); + }, + child: const Text('zoomBy with focus'), + ), + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.zoomBy(-0.5), + ); + }, + child: const Text('zoomBy'), + ), + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.zoomIn(), + ); + }, + child: const Text('zoomIn'), + ), + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.zoomOut(), + ); + }, + child: const Text('zoomOut'), + ), + TextButton( + onPressed: () { + mapController?.moveCamera( + CameraUpdate.zoomTo(16.0), + ); + }, + child: const Text('zoomTo'), + ), + ], + ), + ], + ) + ], + ); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/padding.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/padding.dart new file mode 100644 index 000000000000..d346549b9d40 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/padding.dart @@ -0,0 +1,181 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +class PaddingPage extends GoogleMapExampleAppPage { + const PaddingPage({Key? key}) + : super(const Icon(Icons.map), 'Add padding to the map', key: key); + + @override + Widget build(BuildContext context) { + return const MarkerIconsBody(); + } +} + +class MarkerIconsBody extends StatefulWidget { + const MarkerIconsBody({Key? key}) : super(key: key); + + @override + State createState() => MarkerIconsBodyState(); +} + +const LatLng _kMapCenter = LatLng(52.4478, -3.5402); + +class MarkerIconsBodyState extends State { + ExampleGoogleMapController? controller; + + EdgeInsets _padding = const EdgeInsets.all(0); + + @override + Widget build(BuildContext context) { + final ExampleGoogleMap googleMap = ExampleGoogleMap( + onMapCreated: _onMapCreated, + initialCameraPosition: const CameraPosition( + target: _kMapCenter, + zoom: 7.0, + ), + padding: _padding, + ); + + final List columnChildren = [ + Padding( + padding: const EdgeInsets.all(10.0), + child: Center( + child: SizedBox( + width: 300.0, + height: 200.0, + child: googleMap, + ), + ), + ), + const Padding( + padding: EdgeInsets.only(top: 20), + child: Center( + child: Text( + 'Enter Padding Below', + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + ), + ), + ), + ]; + + columnChildren.addAll([_paddingInput(), _buttons()]); + + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: columnChildren, + ); + } + + void _onMapCreated(ExampleGoogleMapController controllerParam) { + setState(() { + controller = controllerParam; + }); + } + + final TextEditingController _topController = TextEditingController(); + final TextEditingController _bottomController = TextEditingController(); + final TextEditingController _leftController = TextEditingController(); + final TextEditingController _rightController = TextEditingController(); + + Widget _paddingInput() { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + children: [ + Flexible( + flex: 2, + child: TextField( + controller: _topController, + keyboardType: TextInputType.number, + textAlign: TextAlign.center, + decoration: const InputDecoration( + hintText: 'Top', + ), + ), + ), + const Spacer(), + Flexible( + flex: 2, + child: TextField( + controller: _bottomController, + keyboardType: TextInputType.number, + textAlign: TextAlign.center, + decoration: const InputDecoration( + hintText: 'Bottom', + ), + ), + ), + const Spacer(), + Flexible( + flex: 2, + child: TextField( + controller: _leftController, + keyboardType: TextInputType.number, + textAlign: TextAlign.center, + decoration: const InputDecoration( + hintText: 'Left', + ), + ), + ), + const Spacer(), + Flexible( + flex: 2, + child: TextField( + controller: _rightController, + keyboardType: TextInputType.number, + textAlign: TextAlign.center, + decoration: const InputDecoration( + hintText: 'Right', + ), + ), + ), + ], + ), + ); + } + + Widget _buttons() { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + TextButton( + child: const Text('Set Padding'), + onPressed: () { + setState(() { + _padding = EdgeInsets.fromLTRB( + double.tryParse(_leftController.value.text) ?? 0, + double.tryParse(_topController.value.text) ?? 0, + double.tryParse(_rightController.value.text) ?? 0, + double.tryParse(_bottomController.value.text) ?? 0); + }); + }, + ), + TextButton( + child: const Text('Reset Padding'), + onPressed: () { + setState(() { + _topController.clear(); + _bottomController.clear(); + _leftController.clear(); + _rightController.clear(); + _padding = const EdgeInsets.all(0); + }); + }, + ) + ], + ), + ); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/page.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/page.dart new file mode 100644 index 000000000000..eb01ab07a6f3 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/page.dart @@ -0,0 +1,15 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; + +abstract class GoogleMapExampleAppPage extends StatelessWidget { + const GoogleMapExampleAppPage(this.leading, this.title, {Key? key}) + : super(key: key); + + final Widget leading; + final String title; +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_circle.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_circle.dart new file mode 100644 index 000000000000..9dc5760afa1f --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_circle.dart @@ -0,0 +1,232 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +class PlaceCirclePage extends GoogleMapExampleAppPage { + const PlaceCirclePage({Key? key}) + : super(const Icon(Icons.linear_scale), 'Place circle', key: key); + + @override + Widget build(BuildContext context) { + return const PlaceCircleBody(); + } +} + +class PlaceCircleBody extends StatefulWidget { + const PlaceCircleBody({Key? key}) : super(key: key); + + @override + State createState() => PlaceCircleBodyState(); +} + +class PlaceCircleBodyState extends State { + PlaceCircleBodyState(); + + ExampleGoogleMapController? controller; + Map circles = {}; + int _circleIdCounter = 1; + CircleId? selectedCircle; + + // Values when toggling circle color + int fillColorsIndex = 0; + int strokeColorsIndex = 0; + List colors = [ + Colors.purple, + Colors.red, + Colors.green, + Colors.pink, + ]; + + // Values when toggling circle stroke width + int widthsIndex = 0; + List widths = [10, 20, 5]; + + // ignore: use_setters_to_change_properties + void _onMapCreated(ExampleGoogleMapController controller) { + this.controller = controller; + } + + @override + void dispose() { + super.dispose(); + } + + void _onCircleTapped(CircleId circleId) { + setState(() { + selectedCircle = circleId; + }); + } + + void _remove(CircleId circleId) { + setState(() { + if (circles.containsKey(circleId)) { + circles.remove(circleId); + } + if (circleId == selectedCircle) { + selectedCircle = null; + } + }); + } + + void _add() { + final int circleCount = circles.length; + + if (circleCount == 12) { + return; + } + + final String circleIdVal = 'circle_id_$_circleIdCounter'; + _circleIdCounter++; + final CircleId circleId = CircleId(circleIdVal); + + final Circle circle = Circle( + circleId: circleId, + consumeTapEvents: true, + strokeColor: Colors.orange, + fillColor: Colors.green, + strokeWidth: 5, + center: _createCenter(), + radius: 50000, + onTap: () { + _onCircleTapped(circleId); + }, + ); + + setState(() { + circles[circleId] = circle; + }); + } + + void _toggleVisible(CircleId circleId) { + final Circle circle = circles[circleId]!; + setState(() { + circles[circleId] = circle.copyWith( + visibleParam: !circle.visible, + ); + }); + } + + void _changeFillColor(CircleId circleId) { + final Circle circle = circles[circleId]!; + setState(() { + circles[circleId] = circle.copyWith( + fillColorParam: colors[++fillColorsIndex % colors.length], + ); + }); + } + + void _changeStrokeColor(CircleId circleId) { + final Circle circle = circles[circleId]!; + setState(() { + circles[circleId] = circle.copyWith( + strokeColorParam: colors[++strokeColorsIndex % colors.length], + ); + }); + } + + void _changeStrokeWidth(CircleId circleId) { + final Circle circle = circles[circleId]!; + setState(() { + circles[circleId] = circle.copyWith( + strokeWidthParam: widths[++widthsIndex % widths.length], + ); + }); + } + + @override + Widget build(BuildContext context) { + final CircleId? selectedId = selectedCircle; + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: SizedBox( + width: 350.0, + height: 300.0, + child: ExampleGoogleMap( + initialCameraPosition: const CameraPosition( + target: LatLng(52.4478, -3.5402), + zoom: 7.0, + ), + circles: Set.of(circles.values), + onMapCreated: _onMapCreated, + ), + ), + ), + Expanded( + child: SingleChildScrollView( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Row( + children: [ + Column( + children: [ + TextButton( + onPressed: _add, + child: const Text('add'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _remove(selectedId), + child: const Text('remove'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), + ), + ], + ), + Column( + children: [ + TextButton( + onPressed: (selectedId == null) + ? null + : () => _changeStrokeWidth(selectedId), + child: const Text('change stroke width'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _changeStrokeColor(selectedId), + child: const Text('change stroke color'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _changeFillColor(selectedId), + child: const Text('change fill color'), + ), + ], + ) + ], + ) + ], + ), + ), + ), + ], + ); + } + + LatLng _createCenter() { + final double offset = _circleIdCounter.ceilToDouble(); + return _createLatLng(51.4816 + offset * 0.2, -3.1791); + } + + LatLng _createLatLng(double lat, double lng) { + return LatLng(lat, lng); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_marker.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_marker.dart new file mode 100644 index 000000000000..b7d2a690c3e8 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_marker.dart @@ -0,0 +1,422 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; +import 'dart:math'; +import 'dart:typed_data'; +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +class PlaceMarkerPage extends GoogleMapExampleAppPage { + const PlaceMarkerPage({Key? key}) + : super(const Icon(Icons.place), 'Place marker', key: key); + + @override + Widget build(BuildContext context) { + return const PlaceMarkerBody(); + } +} + +class PlaceMarkerBody extends StatefulWidget { + const PlaceMarkerBody({Key? key}) : super(key: key); + + @override + State createState() => PlaceMarkerBodyState(); +} + +typedef MarkerUpdateAction = Marker Function(Marker marker); + +class PlaceMarkerBodyState extends State { + PlaceMarkerBodyState(); + static const LatLng center = LatLng(-33.86711, 151.1947171); + + ExampleGoogleMapController? controller; + Map markers = {}; + MarkerId? selectedMarker; + int _markerIdCounter = 1; + LatLng? markerPosition; + + // ignore: use_setters_to_change_properties + void _onMapCreated(ExampleGoogleMapController controller) { + this.controller = controller; + } + + @override + void dispose() { + super.dispose(); + } + + void _onMarkerTapped(MarkerId markerId) { + final Marker? tappedMarker = markers[markerId]; + if (tappedMarker != null) { + setState(() { + final MarkerId? previousMarkerId = selectedMarker; + if (previousMarkerId != null && markers.containsKey(previousMarkerId)) { + final Marker resetOld = markers[previousMarkerId]! + .copyWith(iconParam: BitmapDescriptor.defaultMarker); + markers[previousMarkerId] = resetOld; + } + selectedMarker = markerId; + final Marker newMarker = tappedMarker.copyWith( + iconParam: BitmapDescriptor.defaultMarkerWithHue( + BitmapDescriptor.hueGreen, + ), + ); + markers[markerId] = newMarker; + + markerPosition = null; + }); + } + } + + Future _onMarkerDrag(MarkerId markerId, LatLng newPosition) async { + setState(() { + markerPosition = newPosition; + }); + } + + Future _onMarkerDragEnd(MarkerId markerId, LatLng newPosition) async { + final Marker? tappedMarker = markers[markerId]; + if (tappedMarker != null) { + setState(() { + markerPosition = null; + }); + await showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + actions: [ + TextButton( + child: const Text('OK'), + onPressed: () => Navigator.of(context).pop(), + ) + ], + content: Padding( + padding: const EdgeInsets.symmetric(vertical: 66), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text('Old position: ${tappedMarker.position}'), + Text('New position: $newPosition'), + ], + ))); + }); + } + } + + void _add() { + final int markerCount = markers.length; + + if (markerCount == 12) { + return; + } + + final String markerIdVal = 'marker_id_$_markerIdCounter'; + _markerIdCounter++; + final MarkerId markerId = MarkerId(markerIdVal); + + final Marker marker = Marker( + markerId: markerId, + position: LatLng( + center.latitude + sin(_markerIdCounter * pi / 6.0) / 20.0, + center.longitude + cos(_markerIdCounter * pi / 6.0) / 20.0, + ), + infoWindow: InfoWindow(title: markerIdVal, snippet: '*'), + onTap: () => _onMarkerTapped(markerId), + onDragEnd: (LatLng position) => _onMarkerDragEnd(markerId, position), + onDrag: (LatLng position) => _onMarkerDrag(markerId, position), + ); + + setState(() { + markers[markerId] = marker; + }); + } + + void _remove(MarkerId markerId) { + setState(() { + if (markers.containsKey(markerId)) { + markers.remove(markerId); + } + }); + } + + void _changePosition(MarkerId markerId) { + final Marker marker = markers[markerId]!; + final LatLng current = marker.position; + final Offset offset = Offset( + center.latitude - current.latitude, + center.longitude - current.longitude, + ); + setState(() { + markers[markerId] = marker.copyWith( + positionParam: LatLng( + center.latitude + offset.dy, + center.longitude + offset.dx, + ), + ); + }); + } + + void _changeAnchor(MarkerId markerId) { + final Marker marker = markers[markerId]!; + final Offset currentAnchor = marker.anchor; + final Offset newAnchor = Offset(1.0 - currentAnchor.dy, currentAnchor.dx); + setState(() { + markers[markerId] = marker.copyWith( + anchorParam: newAnchor, + ); + }); + } + + Future _changeInfoAnchor(MarkerId markerId) async { + final Marker marker = markers[markerId]!; + final Offset currentAnchor = marker.infoWindow.anchor; + final Offset newAnchor = Offset(1.0 - currentAnchor.dy, currentAnchor.dx); + setState(() { + markers[markerId] = marker.copyWith( + infoWindowParam: marker.infoWindow.copyWith( + anchorParam: newAnchor, + ), + ); + }); + } + + Future _toggleDraggable(MarkerId markerId) async { + final Marker marker = markers[markerId]!; + setState(() { + markers[markerId] = marker.copyWith( + draggableParam: !marker.draggable, + ); + }); + } + + Future _toggleFlat(MarkerId markerId) async { + final Marker marker = markers[markerId]!; + setState(() { + markers[markerId] = marker.copyWith( + flatParam: !marker.flat, + ); + }); + } + + Future _changeInfo(MarkerId markerId) async { + final Marker marker = markers[markerId]!; + final String newSnippet = '${marker.infoWindow.snippet!}*'; + setState(() { + markers[markerId] = marker.copyWith( + infoWindowParam: marker.infoWindow.copyWith( + snippetParam: newSnippet, + ), + ); + }); + } + + Future _changeAlpha(MarkerId markerId) async { + final Marker marker = markers[markerId]!; + final double current = marker.alpha; + setState(() { + markers[markerId] = marker.copyWith( + alphaParam: current < 0.1 ? 1.0 : current * 0.75, + ); + }); + } + + Future _changeRotation(MarkerId markerId) async { + final Marker marker = markers[markerId]!; + final double current = marker.rotation; + setState(() { + markers[markerId] = marker.copyWith( + rotationParam: current == 330.0 ? 0.0 : current + 30.0, + ); + }); + } + + Future _toggleVisible(MarkerId markerId) async { + final Marker marker = markers[markerId]!; + setState(() { + markers[markerId] = marker.copyWith( + visibleParam: !marker.visible, + ); + }); + } + + Future _changeZIndex(MarkerId markerId) async { + final Marker marker = markers[markerId]!; + final double current = marker.zIndex; + setState(() { + markers[markerId] = marker.copyWith( + zIndexParam: current == 12.0 ? 0.0 : current + 1.0, + ); + }); + } + + void _setMarkerIcon(MarkerId markerId, BitmapDescriptor assetIcon) { + final Marker marker = markers[markerId]!; + setState(() { + markers[markerId] = marker.copyWith( + iconParam: assetIcon, + ); + }); + } + + Future _getAssetIcon(BuildContext context) async { + final Completer bitmapIcon = + Completer(); + final ImageConfiguration config = createLocalImageConfiguration(context); + + const AssetImage('assets/red_square.png') + .resolve(config) + .addListener(ImageStreamListener((ImageInfo image, bool sync) async { + final ByteData? bytes = + await image.image.toByteData(format: ImageByteFormat.png); + if (bytes == null) { + bitmapIcon.completeError(Exception('Unable to encode icon')); + return; + } + final BitmapDescriptor bitmap = + BitmapDescriptor.fromBytes(bytes.buffer.asUint8List()); + bitmapIcon.complete(bitmap); + })); + + return await bitmapIcon.future; + } + + @override + Widget build(BuildContext context) { + final MarkerId? selectedId = selectedMarker; + return Stack(children: [ + Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Expanded( + child: ExampleGoogleMap( + onMapCreated: _onMapCreated, + initialCameraPosition: const CameraPosition( + target: LatLng(-33.852, 151.211), + zoom: 11.0, + ), + markers: Set.of(markers.values), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + TextButton( + onPressed: _add, + child: const Text('Add'), + ), + TextButton( + onPressed: + selectedId == null ? null : () => _remove(selectedId), + child: const Text('Remove'), + ), + ], + ), + Wrap( + alignment: WrapAlignment.spaceEvenly, + children: [ + TextButton( + onPressed: + selectedId == null ? null : () => _changeInfo(selectedId), + child: const Text('change info'), + ), + TextButton( + onPressed: selectedId == null + ? null + : () => _changeInfoAnchor(selectedId), + child: const Text('change info anchor'), + ), + TextButton( + onPressed: + selectedId == null ? null : () => _changeAlpha(selectedId), + child: const Text('change alpha'), + ), + TextButton( + onPressed: + selectedId == null ? null : () => _changeAnchor(selectedId), + child: const Text('change anchor'), + ), + TextButton( + onPressed: selectedId == null + ? null + : () => _toggleDraggable(selectedId), + child: const Text('toggle draggable'), + ), + TextButton( + onPressed: + selectedId == null ? null : () => _toggleFlat(selectedId), + child: const Text('toggle flat'), + ), + TextButton( + onPressed: selectedId == null + ? null + : () => _changePosition(selectedId), + child: const Text('change position'), + ), + TextButton( + onPressed: selectedId == null + ? null + : () => _changeRotation(selectedId), + child: const Text('change rotation'), + ), + TextButton( + onPressed: selectedId == null + ? null + : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), + ), + TextButton( + onPressed: + selectedId == null ? null : () => _changeZIndex(selectedId), + child: const Text('change zIndex'), + ), + TextButton( + onPressed: selectedId == null + ? null + : () { + _getAssetIcon(context).then( + (BitmapDescriptor icon) { + _setMarkerIcon(selectedId, icon); + }, + ); + }, + child: const Text('set marker icon'), + ), + ], + ), + ], + ), + Visibility( + visible: markerPosition != null, + child: Container( + color: Colors.white70, + height: 30, + padding: const EdgeInsets.only(left: 12, right: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + mainAxisSize: MainAxisSize.max, + children: [ + if (markerPosition == null) + Container() + else + Expanded(child: Text('lat: ${markerPosition!.latitude}')), + if (markerPosition == null) + Container() + else + Expanded(child: Text('lng: ${markerPosition!.longitude}')), + ], + ), + ), + ), + ]); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_polygon.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_polygon.dart new file mode 100644 index 000000000000..b41cb5d3ccb1 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_polygon.dart @@ -0,0 +1,306 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +class PlacePolygonPage extends GoogleMapExampleAppPage { + const PlacePolygonPage({Key? key}) + : super(const Icon(Icons.linear_scale), 'Place polygon', key: key); + + @override + Widget build(BuildContext context) { + return const PlacePolygonBody(); + } +} + +class PlacePolygonBody extends StatefulWidget { + const PlacePolygonBody({Key? key}) : super(key: key); + + @override + State createState() => PlacePolygonBodyState(); +} + +class PlacePolygonBodyState extends State { + PlacePolygonBodyState(); + + ExampleGoogleMapController? controller; + Map polygons = {}; + Map polygonOffsets = {}; + int _polygonIdCounter = 0; + PolygonId? selectedPolygon; + + // Values when toggling polygon color + int strokeColorsIndex = 0; + int fillColorsIndex = 0; + List colors = [ + Colors.purple, + Colors.red, + Colors.green, + Colors.pink, + ]; + + // Values when toggling polygon width + int widthsIndex = 0; + List widths = [10, 20, 5]; + + // ignore: use_setters_to_change_properties + void _onMapCreated(ExampleGoogleMapController controller) { + this.controller = controller; + } + + @override + void dispose() { + super.dispose(); + } + + void _onPolygonTapped(PolygonId polygonId) { + setState(() { + selectedPolygon = polygonId; + }); + } + + void _remove(PolygonId polygonId) { + setState(() { + if (polygons.containsKey(polygonId)) { + polygons.remove(polygonId); + } + selectedPolygon = null; + }); + } + + void _add() { + final int polygonCount = polygons.length; + + if (polygonCount == 12) { + return; + } + + final String polygonIdVal = 'polygon_id_$_polygonIdCounter'; + final PolygonId polygonId = PolygonId(polygonIdVal); + + final Polygon polygon = Polygon( + polygonId: polygonId, + consumeTapEvents: true, + strokeColor: Colors.orange, + strokeWidth: 5, + fillColor: Colors.green, + points: _createPoints(), + onTap: () { + _onPolygonTapped(polygonId); + }, + ); + + setState(() { + polygons[polygonId] = polygon; + polygonOffsets[polygonId] = _polygonIdCounter.ceilToDouble(); + // increment _polygonIdCounter to have unique polygon id each time + _polygonIdCounter++; + }); + } + + void _toggleGeodesic(PolygonId polygonId) { + final Polygon polygon = polygons[polygonId]!; + setState(() { + polygons[polygonId] = polygon.copyWith( + geodesicParam: !polygon.geodesic, + ); + }); + } + + void _toggleVisible(PolygonId polygonId) { + final Polygon polygon = polygons[polygonId]!; + setState(() { + polygons[polygonId] = polygon.copyWith( + visibleParam: !polygon.visible, + ); + }); + } + + void _changeStrokeColor(PolygonId polygonId) { + final Polygon polygon = polygons[polygonId]!; + setState(() { + polygons[polygonId] = polygon.copyWith( + strokeColorParam: colors[++strokeColorsIndex % colors.length], + ); + }); + } + + void _changeFillColor(PolygonId polygonId) { + final Polygon polygon = polygons[polygonId]!; + setState(() { + polygons[polygonId] = polygon.copyWith( + fillColorParam: colors[++fillColorsIndex % colors.length], + ); + }); + } + + void _changeWidth(PolygonId polygonId) { + final Polygon polygon = polygons[polygonId]!; + setState(() { + polygons[polygonId] = polygon.copyWith( + strokeWidthParam: widths[++widthsIndex % widths.length], + ); + }); + } + + void _addHoles(PolygonId polygonId) { + final Polygon polygon = polygons[polygonId]!; + setState(() { + polygons[polygonId] = + polygon.copyWith(holesParam: _createHoles(polygonId)); + }); + } + + void _removeHoles(PolygonId polygonId) { + final Polygon polygon = polygons[polygonId]!; + setState(() { + polygons[polygonId] = polygon.copyWith( + holesParam: >[], + ); + }); + } + + @override + Widget build(BuildContext context) { + final PolygonId? selectedId = selectedPolygon; + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: SizedBox( + width: 350.0, + height: 300.0, + child: ExampleGoogleMap( + initialCameraPosition: const CameraPosition( + target: LatLng(52.4478, -3.5402), + zoom: 7.0, + ), + polygons: Set.of(polygons.values), + onMapCreated: _onMapCreated, + ), + ), + ), + Expanded( + child: SingleChildScrollView( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Row( + children: [ + Column( + children: [ + TextButton( + onPressed: _add, + child: const Text('add'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _remove(selectedId), + child: const Text('remove'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _toggleGeodesic(selectedId), + child: const Text('toggle geodesic'), + ), + ], + ), + Column( + children: [ + TextButton( + onPressed: (selectedId == null) + ? null + : ((polygons[selectedId]!.holes.isNotEmpty) + ? null + : () => _addHoles(selectedId)), + child: const Text('add holes'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : ((polygons[selectedId]!.holes.isEmpty) + ? null + : () => _removeHoles(selectedId)), + child: const Text('remove holes'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _changeWidth(selectedId), + child: const Text('change stroke width'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _changeStrokeColor(selectedId), + child: const Text('change stroke color'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _changeFillColor(selectedId), + child: const Text('change fill color'), + ), + ], + ) + ], + ) + ], + ), + ), + ), + ], + ); + } + + List _createPoints() { + final List points = []; + final double offset = _polygonIdCounter.ceilToDouble(); + points.add(_createLatLng(51.2395 + offset, -3.4314)); + points.add(_createLatLng(53.5234 + offset, -3.5314)); + points.add(_createLatLng(52.4351 + offset, -4.5235)); + points.add(_createLatLng(52.1231 + offset, -5.0829)); + return points; + } + + List> _createHoles(PolygonId polygonId) { + final List> holes = >[]; + final double offset = polygonOffsets[polygonId]!; + + final List hole1 = []; + hole1.add(_createLatLng(51.8395 + offset, -3.8814)); + hole1.add(_createLatLng(52.0234 + offset, -3.9914)); + hole1.add(_createLatLng(52.1351 + offset, -4.4435)); + hole1.add(_createLatLng(52.0231 + offset, -4.5829)); + holes.add(hole1); + + final List hole2 = []; + hole2.add(_createLatLng(52.2395 + offset, -3.6814)); + hole2.add(_createLatLng(52.4234 + offset, -3.7914)); + hole2.add(_createLatLng(52.5351 + offset, -4.2435)); + hole2.add(_createLatLng(52.4231 + offset, -4.3829)); + holes.add(hole2); + + return holes; + } + + LatLng _createLatLng(double lat, double lng) { + return LatLng(lat, lng); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_polyline.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_polyline.dart new file mode 100644 index 000000000000..004206b9f6cc --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_polyline.dart @@ -0,0 +1,325 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +class PlacePolylinePage extends GoogleMapExampleAppPage { + const PlacePolylinePage({Key? key}) + : super(const Icon(Icons.linear_scale), 'Place polyline', key: key); + + @override + Widget build(BuildContext context) { + return const PlacePolylineBody(); + } +} + +class PlacePolylineBody extends StatefulWidget { + const PlacePolylineBody({Key? key}) : super(key: key); + + @override + State createState() => PlacePolylineBodyState(); +} + +class PlacePolylineBodyState extends State { + PlacePolylineBodyState(); + + ExampleGoogleMapController? controller; + Map polylines = {}; + int _polylineIdCounter = 0; + PolylineId? selectedPolyline; + + // Values when toggling polyline color + int colorsIndex = 0; + List colors = [ + Colors.purple, + Colors.red, + Colors.green, + Colors.pink, + ]; + + // Values when toggling polyline width + int widthsIndex = 0; + List widths = [10, 20, 5]; + + int jointTypesIndex = 0; + List jointTypes = [ + JointType.mitered, + JointType.bevel, + JointType.round + ]; + + // Values when toggling polyline end cap type + int endCapsIndex = 0; + List endCaps = [Cap.buttCap, Cap.squareCap, Cap.roundCap]; + + // Values when toggling polyline start cap type + int startCapsIndex = 0; + List startCaps = [Cap.buttCap, Cap.squareCap, Cap.roundCap]; + + // Values when toggling polyline pattern + int patternsIndex = 0; + List> patterns = >[ + [], + [ + PatternItem.dash(30.0), + PatternItem.gap(20.0), + PatternItem.dot, + PatternItem.gap(20.0) + ], + [PatternItem.dash(30.0), PatternItem.gap(20.0)], + [PatternItem.dot, PatternItem.gap(10.0)], + ]; + + // ignore: use_setters_to_change_properties + void _onMapCreated(ExampleGoogleMapController controller) { + this.controller = controller; + } + + @override + void dispose() { + super.dispose(); + } + + void _onPolylineTapped(PolylineId polylineId) { + setState(() { + selectedPolyline = polylineId; + }); + } + + void _remove(PolylineId polylineId) { + setState(() { + if (polylines.containsKey(polylineId)) { + polylines.remove(polylineId); + } + selectedPolyline = null; + }); + } + + void _add() { + final int polylineCount = polylines.length; + + if (polylineCount == 12) { + return; + } + + final String polylineIdVal = 'polyline_id_$_polylineIdCounter'; + _polylineIdCounter++; + final PolylineId polylineId = PolylineId(polylineIdVal); + + final Polyline polyline = Polyline( + polylineId: polylineId, + consumeTapEvents: true, + color: Colors.orange, + width: 5, + points: _createPoints(), + onTap: () { + _onPolylineTapped(polylineId); + }, + ); + + setState(() { + polylines[polylineId] = polyline; + }); + } + + void _toggleGeodesic(PolylineId polylineId) { + final Polyline polyline = polylines[polylineId]!; + setState(() { + polylines[polylineId] = polyline.copyWith( + geodesicParam: !polyline.geodesic, + ); + }); + } + + void _toggleVisible(PolylineId polylineId) { + final Polyline polyline = polylines[polylineId]!; + setState(() { + polylines[polylineId] = polyline.copyWith( + visibleParam: !polyline.visible, + ); + }); + } + + void _changeColor(PolylineId polylineId) { + final Polyline polyline = polylines[polylineId]!; + setState(() { + polylines[polylineId] = polyline.copyWith( + colorParam: colors[++colorsIndex % colors.length], + ); + }); + } + + void _changeWidth(PolylineId polylineId) { + final Polyline polyline = polylines[polylineId]!; + setState(() { + polylines[polylineId] = polyline.copyWith( + widthParam: widths[++widthsIndex % widths.length], + ); + }); + } + + void _changeJointType(PolylineId polylineId) { + final Polyline polyline = polylines[polylineId]!; + setState(() { + polylines[polylineId] = polyline.copyWith( + jointTypeParam: jointTypes[++jointTypesIndex % jointTypes.length], + ); + }); + } + + void _changeEndCap(PolylineId polylineId) { + final Polyline polyline = polylines[polylineId]!; + setState(() { + polylines[polylineId] = polyline.copyWith( + endCapParam: endCaps[++endCapsIndex % endCaps.length], + ); + }); + } + + void _changeStartCap(PolylineId polylineId) { + final Polyline polyline = polylines[polylineId]!; + setState(() { + polylines[polylineId] = polyline.copyWith( + startCapParam: startCaps[++startCapsIndex % startCaps.length], + ); + }); + } + + void _changePattern(PolylineId polylineId) { + final Polyline polyline = polylines[polylineId]!; + setState(() { + polylines[polylineId] = polyline.copyWith( + patternsParam: patterns[++patternsIndex % patterns.length], + ); + }); + } + + @override + Widget build(BuildContext context) { + final bool isIOS = !kIsWeb && defaultTargetPlatform == TargetPlatform.iOS; + + final PolylineId? selectedId = selectedPolyline; + + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: SizedBox( + width: 350.0, + height: 300.0, + child: ExampleGoogleMap( + initialCameraPosition: const CameraPosition( + target: LatLng(53.1721, -3.5402), + zoom: 7.0, + ), + polylines: Set.of(polylines.values), + onMapCreated: _onMapCreated, + ), + ), + ), + Expanded( + child: SingleChildScrollView( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Row( + children: [ + Column( + children: [ + TextButton( + onPressed: _add, + child: const Text('add'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _remove(selectedId), + child: const Text('remove'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _toggleVisible(selectedId), + child: const Text('toggle visible'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _toggleGeodesic(selectedId), + child: const Text('toggle geodesic'), + ), + ], + ), + Column( + children: [ + TextButton( + onPressed: (selectedId == null) + ? null + : () => _changeWidth(selectedId), + child: const Text('change width'), + ), + TextButton( + onPressed: (selectedId == null) + ? null + : () => _changeColor(selectedId), + child: const Text('change color'), + ), + TextButton( + onPressed: isIOS || (selectedId == null) + ? null + : () => _changeStartCap(selectedId), + child: const Text('change start cap [Android only]'), + ), + TextButton( + onPressed: isIOS || (selectedId == null) + ? null + : () => _changeEndCap(selectedId), + child: const Text('change end cap [Android only]'), + ), + TextButton( + onPressed: isIOS || (selectedId == null) + ? null + : () => _changeJointType(selectedId), + child: const Text('change joint type [Android only]'), + ), + TextButton( + onPressed: isIOS || (selectedId == null) + ? null + : () => _changePattern(selectedId), + child: const Text('change pattern [Android only]'), + ), + ], + ) + ], + ) + ], + ), + ), + ), + ], + ); + } + + List _createPoints() { + final List points = []; + final double offset = _polylineIdCounter.ceilToDouble(); + points.add(_createLatLng(51.4816 + offset, -3.1791)); + points.add(_createLatLng(53.0430 + offset, -2.9925)); + points.add(_createLatLng(53.1396 + offset, -4.2739)); + points.add(_createLatLng(52.4153 + offset, -4.0829)); + return points; + } + + LatLng _createLatLng(double lat, double lng) { + return LatLng(lat, lng); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/scrolling_map.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/scrolling_map.dart new file mode 100644 index 000000000000..7a9b75cd1224 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/scrolling_map.dart @@ -0,0 +1,114 @@ +// 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. + +// ignore_for_file: public_member_api_docs + +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +const LatLng _center = LatLng(32.080664, 34.9563837); + +class ScrollingMapPage extends GoogleMapExampleAppPage { + const ScrollingMapPage({Key? key}) + : super(const Icon(Icons.map), 'Scrolling map', key: key); + + @override + Widget build(BuildContext context) { + return const ScrollingMapBody(); + } +} + +class ScrollingMapBody extends StatelessWidget { + const ScrollingMapBody({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ListView( + children: [ + Card( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 30.0), + child: Column( + children: [ + const Padding( + padding: EdgeInsets.only(bottom: 12.0), + child: Text('This map consumes all touch events.'), + ), + Center( + child: SizedBox( + width: 300.0, + height: 300.0, + child: ExampleGoogleMap( + initialCameraPosition: const CameraPosition( + target: _center, + zoom: 11.0, + ), + gestureRecognizers: // + >{ + Factory( + () => EagerGestureRecognizer(), + ), + }, + ), + ), + ), + ], + ), + ), + ), + Card( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 30.0), + child: Column( + children: [ + const Text("This map doesn't consume the vertical drags."), + const Padding( + padding: EdgeInsets.only(bottom: 12.0), + child: + Text('It still gets other gestures (e.g scale or tap).'), + ), + Center( + child: SizedBox( + width: 300.0, + height: 300.0, + child: ExampleGoogleMap( + initialCameraPosition: const CameraPosition( + target: _center, + zoom: 11.0, + ), + markers: { + Marker( + markerId: const MarkerId('test_marker_id'), + position: LatLng( + _center.latitude, + _center.longitude, + ), + infoWindow: const InfoWindow( + title: 'An interesting location', + snippet: '*', + ), + ), + }, + gestureRecognizers: < + Factory>{ + Factory( + () => ScaleGestureRecognizer(), + ), + }, + ), + ), + ), + ], + ), + ), + ), + ], + ); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/snapshot.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/snapshot.dart new file mode 100644 index 000000000000..56a90a8e49f6 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/snapshot.dart @@ -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. + +// ignore_for_file: public_member_api_docs + +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +const CameraPosition _kInitialPosition = + CameraPosition(target: LatLng(-33.852, 151.211), zoom: 11.0); + +class SnapshotPage extends GoogleMapExampleAppPage { + const SnapshotPage({Key? key}) + : super(const Icon(Icons.camera_alt), 'Take a snapshot of the map', + key: key); + + @override + Widget build(BuildContext context) { + return _SnapshotBody(); + } +} + +class _SnapshotBody extends StatefulWidget { + @override + _SnapshotBodyState createState() => _SnapshotBodyState(); +} + +class _SnapshotBodyState extends State<_SnapshotBody> { + ExampleGoogleMapController? _mapController; + Uint8List? _imageBytes; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SizedBox( + height: 180, + child: ExampleGoogleMap( + onMapCreated: onMapCreated, + initialCameraPosition: _kInitialPosition, + ), + ), + TextButton( + child: const Text('Take a snapshot'), + onPressed: () async { + final Uint8List? imageBytes = + await _mapController?.takeSnapshot(); + setState(() { + _imageBytes = imageBytes; + }); + }, + ), + Container( + decoration: BoxDecoration(color: Colors.blueGrey[50]), + height: 180, + child: _imageBytes != null ? Image.memory(_imageBytes!) : null, + ), + ], + ), + ); + } + + // ignore: use_setters_to_change_properties + void onMapCreated(ExampleGoogleMapController controller) { + _mapController = controller; + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/tile_overlay.dart new file mode 100644 index 000000000000..555f037b474a --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/tile_overlay.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. + +// ignore_for_file: public_member_api_docs + +import 'dart:typed_data'; +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +class TileOverlayPage extends GoogleMapExampleAppPage { + const TileOverlayPage({Key? key}) + : super(const Icon(Icons.map), 'Tile overlay', key: key); + + @override + Widget build(BuildContext context) { + return const TileOverlayBody(); + } +} + +class TileOverlayBody extends StatefulWidget { + const TileOverlayBody({Key? key}) : super(key: key); + + @override + State createState() => TileOverlayBodyState(); +} + +class TileOverlayBodyState extends State { + TileOverlayBodyState(); + + ExampleGoogleMapController? controller; + TileOverlay? _tileOverlay; + + // ignore: use_setters_to_change_properties + void _onMapCreated(ExampleGoogleMapController controller) { + this.controller = controller; + } + + @override + void dispose() { + super.dispose(); + } + + void _removeTileOverlay() { + setState(() { + _tileOverlay = null; + }); + } + + void _addTileOverlay() { + final TileOverlay tileOverlay = TileOverlay( + tileOverlayId: const TileOverlayId('tile_overlay_1'), + tileProvider: _DebugTileProvider(), + ); + setState(() { + _tileOverlay = tileOverlay; + }); + } + + void _clearTileCache() { + if (_tileOverlay != null && controller != null) { + controller!.clearTileCache(_tileOverlay!.tileOverlayId); + } + } + + @override + Widget build(BuildContext context) { + final Set overlays = { + if (_tileOverlay != null) _tileOverlay!, + }; + return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: SizedBox( + width: 350.0, + height: 300.0, + child: ExampleGoogleMap( + initialCameraPosition: const CameraPosition( + target: LatLng(59.935460, 30.325177), + zoom: 7.0, + ), + tileOverlays: overlays, + onMapCreated: _onMapCreated, + ), + ), + ), + TextButton( + onPressed: _addTileOverlay, + child: const Text('Add tile overlay'), + ), + TextButton( + onPressed: _removeTileOverlay, + child: const Text('Remove tile overlay'), + ), + TextButton( + onPressed: _clearTileCache, + child: const Text('Clear tile cache'), + ), + ], + ); + } +} + +class _DebugTileProvider implements TileProvider { + _DebugTileProvider() { + boxPaint.isAntiAlias = true; + boxPaint.color = Colors.blue; + boxPaint.strokeWidth = 2.0; + boxPaint.style = PaintingStyle.stroke; + } + + static const int width = 100; + static const int height = 100; + static final Paint boxPaint = Paint(); + static const TextStyle textStyle = TextStyle( + color: Colors.red, + fontSize: 20, + ); + + @override + Future getTile(int x, int y, int? zoom) async { + final ui.PictureRecorder recorder = ui.PictureRecorder(); + final Canvas canvas = Canvas(recorder); + final TextSpan textSpan = TextSpan( + text: '$x,$y', + style: textStyle, + ); + final TextPainter textPainter = TextPainter( + text: textSpan, + textDirection: TextDirection.ltr, + ); + textPainter.layout( + minWidth: 0.0, + maxWidth: width.toDouble(), + ); + const Offset offset = Offset(0, 0); + textPainter.paint(canvas, offset); + canvas.drawRect( + Rect.fromLTRB(0, 0, width.toDouble(), width.toDouble()), boxPaint); + final ui.Picture picture = recorder.endRecording(); + final Uint8List byteData = await picture + .toImage(width, height) + .then((ui.Image image) => + image.toByteData(format: ui.ImageByteFormat.png)) + .then((ByteData? byteData) => byteData!.buffer.asUint8List()); + return Tile(width, height, byteData); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/example/pubspec.yaml new file mode 100644 index 000000000000..b4fc5db16cfd --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/pubspec.yaml @@ -0,0 +1,31 @@ +name: google_maps_flutter_example +description: Demonstrates how to use the google_maps_flutter plugin. +publish_to: none + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +dependencies: + cupertino_icons: ^0.1.0 + flutter: + sdk: flutter + flutter_plugin_android_lifecycle: ^2.0.1 + google_maps_flutter_ios: + # When depending on this package from a real application you should use: + # google_maps_flutter_ios: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # 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: ../ + +dev_dependencies: + flutter_driver: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true + assets: + - assets/ diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/test_driver/integration_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/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/google_maps_flutter/google_maps_flutter/ios/Assets/.gitkeep b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Assets/.gitkeep similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/ios/Assets/.gitkeep rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Assets/.gitkeep diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapJSONConversions.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapJSONConversions.h rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapJSONConversions.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapJSONConversions.m rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.h similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.h rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.h diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapTileOverlayController.m rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.h similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.h rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.h diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.m similarity index 89% rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.m rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.m index d62f19ced4f3..70bde9022a0d 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/FLTGoogleMapsPlugin.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.m @@ -11,7 +11,7 @@ @implementation FLTGoogleMapsPlugin + (void)registerWithRegistrar:(NSObject *)registrar { FLTGoogleMapFactory *googleMapFactory = [[FLTGoogleMapFactory alloc] initWithRegistrar:registrar]; [registrar registerViewFactory:googleMapFactory - withId:@"plugins.flutter.io/google_maps" + withId:@"plugins.flutter.dev/google_maps_ios" gestureRecognizersBlockingPolicy: FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded]; } diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.h rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapCircleController.m rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.h rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m similarity index 99% rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m index 3538cc4b4575..41c5d77f9cf1 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m @@ -76,7 +76,7 @@ - (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView // https://github.com/flutter/flutter/issues/104121 [self interpretMapOptions:args[@"options"]]; NSString *channelName = - [NSString stringWithFormat:@"plugins.flutter.io/google_maps_%lld", viewId]; + [NSString stringWithFormat:@"plugins.flutter.dev/google_maps_ios_%lld", viewId]; _channel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:registrar.messenger]; __weak __typeof__(self) weakSelf = self; diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController_Test.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController_Test.h similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController_Test.h rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController_Test.h diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.h rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapMarkerController.m rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.h similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.h rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.h diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolygonController.m rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.h similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.h rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.h diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m similarity index 100% rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapPolylineController.m rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter-umbrella.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h similarity index 63% rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter-umbrella.h rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h index 9969e716c26b..791c3aaea6c3 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter-umbrella.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h @@ -3,9 +3,9 @@ // found in the LICENSE file. #import -#import -#import -#import +#import +#import +#import FOUNDATION_EXPORT double google_maps_flutterVersionNumber; FOUNDATION_EXPORT const unsigned char google_maps_flutterVersionString[]; diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter.modulemap b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios.modulemap similarity index 52% rename from packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter.modulemap rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios.modulemap index 19513f4a7602..699e6753db38 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/google_maps_flutter.modulemap +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios.modulemap @@ -1,5 +1,5 @@ -framework module google_maps_flutter { - umbrella header "google_maps_flutter-umbrella.h" +framework module google_maps_flutter_ios { + umbrella header "google_maps_flutter_ios-umbrella.h" export * module * { export * } diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/google_maps_flutter.podspec b/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec similarity index 89% rename from packages/google_maps_flutter/google_maps_flutter/ios/google_maps_flutter.podspec rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec index e34919c30484..14be02f372e4 100644 --- a/packages/google_maps_flutter/google_maps_flutter/ios/google_maps_flutter.podspec +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec @@ -2,7 +2,7 @@ # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html # Pod::Spec.new do |s| - s.name = 'google_maps_flutter' + s.name = 'google_maps_flutter_ios' s.version = '0.0.1' s.summary = 'Google Maps for Flutter' s.description = <<-DESC @@ -12,11 +12,11 @@ Downloaded by pub (not CocoaPods). s.homepage = 'https://github.com/flutter/plugins' s.license = { :type => 'BSD', :file => '../LICENSE' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter' } - s.documentation_url = 'https://pub.dev/packages/google_maps_flutter' + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/google_maps_flutter/google_maps_flutter/ios' } + s.documentation_url = 'https://pub.dev/packages/google_maps_flutter_ios' s.source_files = 'Classes/**/*.{h,m}' s.public_header_files = 'Classes/**/*.h' - s.module_map = 'Classes/google_maps_flutter.modulemap' + s.module_map = 'Classes/google_maps_flutter_ios.modulemap' s.dependency 'Flutter' s.dependency 'GoogleMaps' s.static_framework = true diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/google_maps_flutter_ios.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/google_maps_flutter_ios.dart new file mode 100644 index 000000000000..c4aabbb8919f --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/google_maps_flutter_ios.dart @@ -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. + +export 'src/google_maps_flutter_ios.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_map_inspector_ios.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_map_inspector_ios.dart new file mode 100644 index 000000000000..8fae1a35e316 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_map_inspector_ios.dart @@ -0,0 +1,113 @@ +// 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/services.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +/// An Android of implementation of [GoogleMapsInspectorPlatform]. +@visibleForTesting +class GoogleMapsInspectorIOS extends GoogleMapsInspectorPlatform { + /// Creates a method-channel-based inspector instance that gets the channel + /// for a given map ID from [channelProvider]. + GoogleMapsInspectorIOS(MethodChannel? Function(int mapId) channelProvider) + : _channelProvider = channelProvider; + + final MethodChannel? Function(int mapId) _channelProvider; + + @override + Future areBuildingsEnabled({required int mapId}) async { + return (await _channelProvider(mapId)! + .invokeMethod('map#isBuildingsEnabled'))!; + } + + @override + Future areRotateGesturesEnabled({required int mapId}) async { + return (await _channelProvider(mapId)! + .invokeMethod('map#isRotateGesturesEnabled'))!; + } + + @override + Future areScrollGesturesEnabled({required int mapId}) async { + return (await _channelProvider(mapId)! + .invokeMethod('map#isScrollGesturesEnabled'))!; + } + + @override + Future areTiltGesturesEnabled({required int mapId}) async { + return (await _channelProvider(mapId)! + .invokeMethod('map#isTiltGesturesEnabled'))!; + } + + @override + Future areZoomControlsEnabled({required int mapId}) async { + return (await _channelProvider(mapId)! + .invokeMethod('map#isZoomControlsEnabled'))!; + } + + @override + Future areZoomGesturesEnabled({required int mapId}) async { + return (await _channelProvider(mapId)! + .invokeMethod('map#isZoomGesturesEnabled'))!; + } + + @override + Future getMinMaxZoomLevels({required int mapId}) async { + final List zoomLevels = (await _channelProvider(mapId)! + .invokeMethod>('map#getMinMaxZoomLevels'))! + .cast(); + return MinMaxZoomPreference(zoomLevels[0], zoomLevels[1]); + } + + @override + Future getTileOverlayInfo(TileOverlayId tileOverlayId, + {required int mapId}) async { + final Map? tileInfo = await _channelProvider(mapId)! + .invokeMapMethod( + 'map#getTileOverlayInfo', { + 'tileOverlayId': tileOverlayId.value, + }); + if (tileInfo == null) { + return null; + } + return TileOverlay( + tileOverlayId: tileOverlayId, + fadeIn: tileInfo['fadeIn']! as bool, + transparency: tileInfo['transparency']! as double, + visible: tileInfo['visible']! as bool, + // Android and iOS return different types. + zIndex: (tileInfo['zIndex']! as num).toInt(), + ); + } + + @override + Future isCompassEnabled({required int mapId}) async { + return (await _channelProvider(mapId)! + .invokeMethod('map#isCompassEnabled'))!; + } + + @override + Future isLiteModeEnabled({required int mapId}) async { + return (await _channelProvider(mapId)! + .invokeMethod('map#isLiteModeEnabled'))!; + } + + @override + Future isMapToolbarEnabled({required int mapId}) async { + return (await _channelProvider(mapId)! + .invokeMethod('map#isMapToolbarEnabled'))!; + } + + @override + Future isMyLocationButtonEnabled({required int mapId}) async { + return (await _channelProvider(mapId)! + .invokeMethod('map#isMyLocationButtonEnabled'))!; + } + + @override + Future isTrafficEnabled({required int mapId}) async { + return (await _channelProvider(mapId)! + .invokeMethod('map#isTrafficEnabled'))!; + } +} 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 new file mode 100644 index 000000000000..5298377763aa --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart @@ -0,0 +1,644 @@ +// 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:async'; +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import +import 'dart:typed_data'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'package:stream_transform/stream_transform.dart'; + +import 'google_map_inspector_ios.dart'; + +// TODO(stuartmorgan): Remove the dependency on platform interface toJson +// methods. Channel serialization details should all be package-internal. + +/// Error thrown when an unknown map ID is provided to a method channel API. +class UnknownMapIDError extends Error { + /// Creates an assertion error with the provided [mapId] and optional + /// [message]. + UnknownMapIDError(this.mapId, [this.message]); + + /// The unknown ID. + final int mapId; + + /// Message describing the assertion error. + final Object? message; + + @override + String toString() { + if (message != null) { + return 'Unknown map ID $mapId: ${Error.safeToString(message)}'; + } + return 'Unknown map ID $mapId'; + } +} + +/// An implementation of [GoogleMapsFlutterPlatform] for iOS. +class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { + /// Registers the iOS implementation of GoogleMapsFlutterPlatform. + static void registerWith() { + GoogleMapsFlutterPlatform.instance = GoogleMapsFlutterIOS(); + } + + // Keep a collection of id -> channel + // Every method call passes the int mapId + final Map _channels = {}; + + /// Accesses the MethodChannel associated to the passed mapId. + MethodChannel _channel(int mapId) { + final MethodChannel? channel = _channels[mapId]; + if (channel == null) { + throw UnknownMapIDError(mapId); + } + return channel; + } + + // Keep a collection of mapId to a map of TileOverlays. + final Map> _tileOverlays = + >{}; + + /// Returns the channel for [mapId], creating it if it doesn't already exist. + @visibleForTesting + MethodChannel ensureChannelInitialized(int mapId) { + MethodChannel? channel = _channels[mapId]; + if (channel == null) { + channel = MethodChannel('plugins.flutter.dev/google_maps_ios_$mapId'); + channel.setMethodCallHandler( + (MethodCall call) => _handleMethodCall(call, mapId)); + _channels[mapId] = channel; + } + return channel; + } + + @override + Future init(int mapId) { + final MethodChannel channel = ensureChannelInitialized(mapId); + return channel.invokeMethod('map#waitForMap'); + } + + @override + void dispose({required int mapId}) { + // Noop! + } + + // The controller we need to broadcast the different events coming + // from handleMethodCall. + // + // It is a `broadcast` because multiple controllers will connect to + // different stream views of this Controller. + final StreamController> _mapEventStreamController = + StreamController>.broadcast(); + + // Returns a filtered view of the events in the _controller, by mapId. + Stream> _events(int mapId) => + _mapEventStreamController.stream + .where((MapEvent event) => event.mapId == mapId); + + @override + Stream onCameraMoveStarted({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onCameraMove({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onCameraIdle({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onMarkerTap({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onInfoWindowTap({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onMarkerDragStart({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onMarkerDrag({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onMarkerDragEnd({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onPolylineTap({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onPolygonTap({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onCircleTap({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onTap({required int mapId}) { + return _events(mapId).whereType(); + } + + @override + Stream onLongPress({required int mapId}) { + return _events(mapId).whereType(); + } + + Future _handleMethodCall(MethodCall call, int mapId) async { + switch (call.method) { + case 'camera#onMoveStarted': + _mapEventStreamController.add(CameraMoveStartedEvent(mapId)); + break; + case 'camera#onMove': + _mapEventStreamController.add(CameraMoveEvent( + mapId, + CameraPosition.fromMap(call.arguments['position'])!, + )); + break; + case 'camera#onIdle': + _mapEventStreamController.add(CameraIdleEvent(mapId)); + break; + case 'marker#onTap': + _mapEventStreamController.add(MarkerTapEvent( + mapId, + MarkerId(call.arguments['markerId'] as String), + )); + break; + case 'marker#onDragStart': + _mapEventStreamController.add(MarkerDragStartEvent( + mapId, + LatLng.fromJson(call.arguments['position'])!, + MarkerId(call.arguments['markerId'] as String), + )); + break; + case 'marker#onDrag': + _mapEventStreamController.add(MarkerDragEvent( + mapId, + LatLng.fromJson(call.arguments['position'])!, + MarkerId(call.arguments['markerId'] as String), + )); + break; + case 'marker#onDragEnd': + _mapEventStreamController.add(MarkerDragEndEvent( + mapId, + LatLng.fromJson(call.arguments['position'])!, + MarkerId(call.arguments['markerId'] as String), + )); + break; + case 'infoWindow#onTap': + _mapEventStreamController.add(InfoWindowTapEvent( + mapId, + MarkerId(call.arguments['markerId'] as String), + )); + break; + case 'polyline#onTap': + _mapEventStreamController.add(PolylineTapEvent( + mapId, + PolylineId(call.arguments['polylineId'] as String), + )); + break; + case 'polygon#onTap': + _mapEventStreamController.add(PolygonTapEvent( + mapId, + PolygonId(call.arguments['polygonId'] as String), + )); + break; + case 'circle#onTap': + _mapEventStreamController.add(CircleTapEvent( + mapId, + CircleId(call.arguments['circleId'] as String), + )); + break; + case 'map#onTap': + _mapEventStreamController.add(MapTapEvent( + mapId, + LatLng.fromJson(call.arguments['position'])!, + )); + break; + case 'map#onLongPress': + _mapEventStreamController.add(MapLongPressEvent( + mapId, + LatLng.fromJson(call.arguments['position'])!, + )); + break; + case 'tileOverlay#getTile': + final Map? tileOverlaysForThisMap = + _tileOverlays[mapId]; + final String tileOverlayId = call.arguments['tileOverlayId'] as String; + final TileOverlay? tileOverlay = + tileOverlaysForThisMap?[TileOverlayId(tileOverlayId)]; + final TileProvider? tileProvider = tileOverlay?.tileProvider; + if (tileProvider == null) { + return TileProvider.noTile.toJson(); + } + final Tile tile = await tileProvider.getTile( + call.arguments['x'] as int, + call.arguments['y'] as int, + call.arguments['zoom'] as int?, + ); + return tile.toJson(); + default: + throw MissingPluginException(); + } + } + + @override + Future updateMapOptions( + Map optionsUpdate, { + required int mapId, + }) { + assert(optionsUpdate != null); + return _channel(mapId).invokeMethod( + 'map#update', + { + 'options': optionsUpdate, + }, + ); + } + + @override + Future updateMarkers( + MarkerUpdates markerUpdates, { + required int mapId, + }) { + assert(markerUpdates != null); + return _channel(mapId).invokeMethod( + 'markers#update', + markerUpdates.toJson(), + ); + } + + @override + Future updatePolygons( + PolygonUpdates polygonUpdates, { + required int mapId, + }) { + assert(polygonUpdates != null); + return _channel(mapId).invokeMethod( + 'polygons#update', + polygonUpdates.toJson(), + ); + } + + @override + Future updatePolylines( + PolylineUpdates polylineUpdates, { + required int mapId, + }) { + assert(polylineUpdates != null); + return _channel(mapId).invokeMethod( + 'polylines#update', + polylineUpdates.toJson(), + ); + } + + @override + Future updateCircles( + CircleUpdates circleUpdates, { + required int mapId, + }) { + assert(circleUpdates != null); + return _channel(mapId).invokeMethod( + 'circles#update', + circleUpdates.toJson(), + ); + } + + @override + Future updateTileOverlays({ + required Set newTileOverlays, + required int mapId, + }) { + final Map? currentTileOverlays = + _tileOverlays[mapId]; + final Set previousSet = currentTileOverlays != null + ? currentTileOverlays.values.toSet() + : {}; + final _TileOverlayUpdates updates = + _TileOverlayUpdates.from(previousSet, newTileOverlays); + _tileOverlays[mapId] = keyTileOverlayId(newTileOverlays); + return _channel(mapId).invokeMethod( + 'tileOverlays#update', + updates.toJson(), + ); + } + + @override + Future clearTileCache( + TileOverlayId tileOverlayId, { + required int mapId, + }) { + return _channel(mapId) + .invokeMethod('tileOverlays#clearTileCache', { + 'tileOverlayId': tileOverlayId.value, + }); + } + + @override + Future animateCamera( + CameraUpdate cameraUpdate, { + required int mapId, + }) { + return _channel(mapId) + .invokeMethod('camera#animate', { + 'cameraUpdate': cameraUpdate.toJson(), + }); + } + + @override + Future moveCamera( + CameraUpdate cameraUpdate, { + required int mapId, + }) { + return _channel(mapId).invokeMethod('camera#move', { + 'cameraUpdate': cameraUpdate.toJson(), + }); + } + + @override + Future setMapStyle( + String? mapStyle, { + required int mapId, + }) async { + final List successAndError = (await _channel(mapId) + .invokeMethod>('map#setStyle', mapStyle))!; + final bool success = successAndError[0] as bool; + if (!success) { + throw MapStyleException(successAndError[1] as String); + } + } + + @override + Future getVisibleRegion({ + required int mapId, + }) async { + final Map latLngBounds = (await _channel(mapId) + .invokeMapMethod('map#getVisibleRegion'))!; + final LatLng southwest = LatLng.fromJson(latLngBounds['southwest'])!; + final LatLng northeast = LatLng.fromJson(latLngBounds['northeast'])!; + + return LatLngBounds(northeast: northeast, southwest: southwest); + } + + @override + Future getScreenCoordinate( + LatLng latLng, { + required int mapId, + }) async { + final Map point = (await _channel(mapId) + .invokeMapMethod( + 'map#getScreenCoordinate', latLng.toJson()))!; + + return ScreenCoordinate(x: point['x']!, y: point['y']!); + } + + @override + Future getLatLng( + ScreenCoordinate screenCoordinate, { + required int mapId, + }) async { + final List latLng = (await _channel(mapId) + .invokeMethod>( + 'map#getLatLng', screenCoordinate.toJson()))!; + return LatLng(latLng[0] as double, latLng[1] as double); + } + + @override + Future showMarkerInfoWindow( + MarkerId markerId, { + required int mapId, + }) { + assert(markerId != null); + return _channel(mapId).invokeMethod( + 'markers#showInfoWindow', {'markerId': markerId.value}); + } + + @override + Future hideMarkerInfoWindow( + MarkerId markerId, { + required int mapId, + }) { + assert(markerId != null); + return _channel(mapId).invokeMethod( + 'markers#hideInfoWindow', {'markerId': markerId.value}); + } + + @override + Future isMarkerInfoWindowShown( + MarkerId markerId, { + required int mapId, + }) async { + assert(markerId != null); + return (await _channel(mapId).invokeMethod( + 'markers#isInfoWindowShown', + {'markerId': markerId.value}))!; + } + + @override + Future getZoomLevel({ + required int mapId, + }) async { + return (await _channel(mapId).invokeMethod('map#getZoomLevel'))!; + } + + @override + Future takeSnapshot({ + required int mapId, + }) { + return _channel(mapId).invokeMethod('map#takeSnapshot'); + } + + Widget _buildView( + int creationId, + PlatformViewCreatedCallback onPlatformViewCreated, { + required MapWidgetConfiguration widgetConfiguration, + MapObjects mapObjects = const MapObjects(), + Map mapOptions = const {}, + }) { + final Map creationParams = { + 'initialCameraPosition': + widgetConfiguration.initialCameraPosition.toMap(), + 'options': mapOptions, + 'markersToAdd': serializeMarkerSet(mapObjects.markers), + 'polygonsToAdd': serializePolygonSet(mapObjects.polygons), + 'polylinesToAdd': serializePolylineSet(mapObjects.polylines), + 'circlesToAdd': serializeCircleSet(mapObjects.circles), + 'tileOverlaysToAdd': serializeTileOverlaySet(mapObjects.tileOverlays), + }; + + return UiKitView( + viewType: 'plugins.flutter.dev/google_maps_ios', + onPlatformViewCreated: onPlatformViewCreated, + gestureRecognizers: widgetConfiguration.gestureRecognizers, + creationParams: creationParams, + creationParamsCodec: const StandardMessageCodec(), + ); + } + + @override + Widget buildViewWithConfiguration( + int creationId, + PlatformViewCreatedCallback onPlatformViewCreated, { + required MapWidgetConfiguration widgetConfiguration, + MapConfiguration mapConfiguration = const MapConfiguration(), + MapObjects mapObjects = const MapObjects(), + }) { + return _buildView( + creationId, + onPlatformViewCreated, + widgetConfiguration: widgetConfiguration, + mapObjects: mapObjects, + mapOptions: _jsonForMapConfiguration(mapConfiguration), + ); + } + + @override + Widget buildViewWithTextDirection( + int creationId, + PlatformViewCreatedCallback onPlatformViewCreated, { + required CameraPosition initialCameraPosition, + required TextDirection textDirection, + Set markers = const {}, + Set polygons = const {}, + Set polylines = const {}, + Set circles = const {}, + Set tileOverlays = const {}, + Set>? gestureRecognizers, + Map mapOptions = const {}, + }) { + return _buildView( + creationId, + onPlatformViewCreated, + widgetConfiguration: MapWidgetConfiguration( + initialCameraPosition: initialCameraPosition, + textDirection: textDirection), + mapObjects: MapObjects( + markers: markers, + polygons: polygons, + polylines: polylines, + circles: circles, + tileOverlays: tileOverlays), + mapOptions: mapOptions, + ); + } + + @override + Widget buildView( + int creationId, + PlatformViewCreatedCallback onPlatformViewCreated, { + required CameraPosition initialCameraPosition, + Set markers = const {}, + Set polygons = const {}, + Set polylines = const {}, + Set circles = const {}, + Set tileOverlays = const {}, + Set>? gestureRecognizers, + Map mapOptions = const {}, + }) { + return buildViewWithTextDirection( + creationId, + onPlatformViewCreated, + initialCameraPosition: initialCameraPosition, + textDirection: TextDirection.ltr, + markers: markers, + polygons: polygons, + polylines: polylines, + circles: circles, + tileOverlays: tileOverlays, + gestureRecognizers: gestureRecognizers, + mapOptions: mapOptions, + ); + } + + @override + @visibleForTesting + void enableDebugInspection() { + GoogleMapsInspectorPlatform.instance = + GoogleMapsInspectorIOS((int mapId) => _channel(mapId)); + } +} + +Map _jsonForMapConfiguration(MapConfiguration config) { + final EdgeInsets? padding = config.padding; + return { + if (config.compassEnabled != null) 'compassEnabled': config.compassEnabled!, + if (config.mapToolbarEnabled != null) + 'mapToolbarEnabled': config.mapToolbarEnabled!, + if (config.cameraTargetBounds != null) + 'cameraTargetBounds': config.cameraTargetBounds!.toJson(), + if (config.mapType != null) 'mapType': config.mapType!.index, + if (config.minMaxZoomPreference != null) + 'minMaxZoomPreference': config.minMaxZoomPreference!.toJson(), + if (config.rotateGesturesEnabled != null) + 'rotateGesturesEnabled': config.rotateGesturesEnabled!, + if (config.scrollGesturesEnabled != null) + 'scrollGesturesEnabled': config.scrollGesturesEnabled!, + if (config.tiltGesturesEnabled != null) + 'tiltGesturesEnabled': config.tiltGesturesEnabled!, + if (config.zoomControlsEnabled != null) + 'zoomControlsEnabled': config.zoomControlsEnabled!, + if (config.zoomGesturesEnabled != null) + 'zoomGesturesEnabled': config.zoomGesturesEnabled!, + if (config.liteModeEnabled != null) + 'liteModeEnabled': config.liteModeEnabled!, + if (config.trackCameraPosition != null) + 'trackCameraPosition': config.trackCameraPosition!, + if (config.myLocationEnabled != null) + 'myLocationEnabled': config.myLocationEnabled!, + if (config.myLocationButtonEnabled != null) + 'myLocationButtonEnabled': config.myLocationButtonEnabled!, + if (padding != null) + 'padding': [ + padding.top, + padding.left, + padding.bottom, + padding.right, + ], + if (config.indoorViewEnabled != null) + 'indoorEnabled': config.indoorViewEnabled!, + if (config.trafficEnabled != null) 'trafficEnabled': config.trafficEnabled!, + if (config.buildingsEnabled != null) + 'buildingsEnabled': config.buildingsEnabled!, + }; +} + +/// Update specification for a set of [TileOverlay]s. +// TODO(stuartmorgan): Fix the missing export of this class in the platform +// interface, and remove this copy. +class _TileOverlayUpdates extends MapsObjectUpdates { + /// Computes [TileOverlayUpdates] given previous and current [TileOverlay]s. + _TileOverlayUpdates.from(Set previous, Set current) + : super.from(previous, current, objectName: 'tileOverlay'); + + /// Set of TileOverlays to be added in this update. + Set get tileOverlaysToAdd => objectsToAdd; + + /// Set of TileOverlayIds to be removed in this update. + Set get tileOverlayIdsToRemove => + objectIdsToRemove.cast(); + + /// Set of TileOverlays to be changed in this update. + Set get tileOverlaysToChange => objectsToChange; +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml new file mode 100644 index 000000000000..f55da6e32cb2 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml @@ -0,0 +1,29 @@ +name: google_maps_flutter_ios +description: iOS implementation of the google_maps_flutter plugin. +repository: https://github.com/flutter/plugins/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.1.10 + +environment: + sdk: ">=2.14.0 <3.0.0" + flutter: ">=2.8.0" + +flutter: + plugin: + implements: google_maps_flutter + platforms: + ios: + pluginClass: FLTGoogleMapsPlugin + dartPluginClass: GoogleMapsFlutterIOS + +dependencies: + flutter: + sdk: flutter + google_maps_flutter_platform_interface: ^2.2.1 + stream_transform: ^2.0.0 + +dev_dependencies: + async: ^2.5.0 + flutter_test: + sdk: flutter + plugin_platform_interface: ^2.0.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart new file mode 100644 index 000000000000..136481cf3abb --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart @@ -0,0 +1,124 @@ +// 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:async/async.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:google_maps_flutter_ios/google_maps_flutter_ios.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + late List log; + + setUp(() async { + log = []; + }); + + /// Initializes a map with the given ID and canned responses, logging all + /// calls to [log]. + void configureMockMap( + GoogleMapsFlutterIOS maps, { + required int mapId, + required Future? Function(MethodCall call) handler, + }) { + maps + .ensureChannelInitialized(mapId) + .setMockMethodCallHandler((MethodCall methodCall) { + log.add(methodCall.method); + return handler(methodCall); + }); + } + + Future sendPlatformMessage( + int mapId, String method, Map data) async { + final ByteData byteData = + const StandardMethodCodec().encodeMethodCall(MethodCall(method, data)); + await TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger + .handlePlatformMessage('plugins.flutter.dev/google_maps_ios_$mapId', + byteData, (ByteData? data) {}); + } + + test('registers instance', () async { + GoogleMapsFlutterIOS.registerWith(); + expect(GoogleMapsFlutterPlatform.instance, isA()); + }); + + // Calls each method that uses invokeMethod with a return type other than + // void to ensure that the casting/nullability handling succeeds. + // + // TODO(stuartmorgan): Remove this once there is real test coverage of + // each method, since that would cover this issue. + test('non-void invokeMethods handle types correctly', () async { + const int mapId = 0; + final GoogleMapsFlutterIOS maps = GoogleMapsFlutterIOS(); + configureMockMap(maps, mapId: mapId, + handler: (MethodCall methodCall) async { + switch (methodCall.method) { + case 'map#getLatLng': + return [1.0, 2.0]; + case 'markers#isInfoWindowShown': + return true; + case 'map#getZoomLevel': + return 2.5; + case 'map#takeSnapshot': + return null; + } + }); + + await maps.getLatLng(const ScreenCoordinate(x: 0, y: 0), mapId: mapId); + await maps.isMarkerInfoWindowShown(const MarkerId(''), mapId: mapId); + await maps.getZoomLevel(mapId: mapId); + await maps.takeSnapshot(mapId: mapId); + // Check that all the invokeMethod calls happened. + expect(log, [ + 'map#getLatLng', + 'markers#isInfoWindowShown', + 'map#getZoomLevel', + 'map#takeSnapshot', + ]); + }); + + test('markers send drag event to correct streams', () async { + const int mapId = 1; + final Map jsonMarkerDragStartEvent = { + 'mapId': mapId, + 'markerId': 'drag-start-marker', + 'position': [1.0, 1.0] + }; + final Map jsonMarkerDragEvent = { + 'mapId': mapId, + 'markerId': 'drag-marker', + 'position': [1.0, 1.0] + }; + final Map jsonMarkerDragEndEvent = { + 'mapId': mapId, + 'markerId': 'drag-end-marker', + 'position': [1.0, 1.0] + }; + + final GoogleMapsFlutterIOS maps = GoogleMapsFlutterIOS(); + maps.ensureChannelInitialized(mapId); + + final StreamQueue markerDragStartStream = + StreamQueue(maps.onMarkerDragStart(mapId: mapId)); + final StreamQueue markerDragStream = + StreamQueue(maps.onMarkerDrag(mapId: mapId)); + final StreamQueue markerDragEndStream = + StreamQueue(maps.onMarkerDragEnd(mapId: mapId)); + + await sendPlatformMessage( + mapId, 'marker#onDragStart', jsonMarkerDragStartEvent); + await sendPlatformMessage(mapId, 'marker#onDrag', jsonMarkerDragEvent); + await sendPlatformMessage( + mapId, 'marker#onDragEnd', jsonMarkerDragEndEvent); + + expect((await markerDragStartStream.next).value.value, + equals('drag-start-marker')); + expect((await markerDragStream.next).value.value, equals('drag-marker')); + expect((await markerDragEndStream.next).value.value, + equals('drag-end-marker')); + }); +} From 66dfd2ad0937ef1e1fe9a779fcd04fb8f3393ee9 Mon Sep 17 00:00:00 2001 From: CicadaCinema <52425971+CicadaCinema@users.noreply.github.com> Date: Wed, 3 Aug 2022 23:09:06 +0100 Subject: [PATCH 574/844] [path_provider_windows] bump dependency version of package:win32 in path_provider_windows (#6176) --- packages/path_provider/path_provider_windows/CHANGELOG.md | 4 ++++ packages/path_provider/path_provider_windows/pubspec.yaml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/path_provider/path_provider_windows/CHANGELOG.md b/packages/path_provider/path_provider_windows/CHANGELOG.md index 3967c38be411..dfae10708432 100644 --- a/packages/path_provider/path_provider_windows/CHANGELOG.md +++ b/packages/path_provider/path_provider_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.1 + +* Updates dependency version of `package:win32` to 2.1.0. + ## 2.1.0 * Upgrades `package:ffi` dependency to 2.0.0. diff --git a/packages/path_provider/path_provider_windows/pubspec.yaml b/packages/path_provider/path_provider_windows/pubspec.yaml index 4e99be71da2f..7ed01177ffb7 100644 --- a/packages/path_provider/path_provider_windows/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_windows description: Windows implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.1.0 +version: 2.1.1 environment: sdk: ">=2.17.0 <3.0.0" @@ -21,7 +21,7 @@ dependencies: sdk: flutter path: ^1.8.0 path_provider_platform_interface: ^2.0.0 - win32: ^2.0.0 + win32: ^2.1.0 dev_dependencies: flutter_test: From cd009e412abda5f76c6e8eef4ff93d9ccdb0441d Mon Sep 17 00:00:00 2001 From: Phil Quitslund Date: Wed, 3 Aug 2022 15:16:04 -0700 Subject: [PATCH 575/844] [script] fix script noop interpolated toString()s (#6175) --- script/tool/lib/src/common/package_looping_command.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/script/tool/lib/src/common/package_looping_command.dart b/script/tool/lib/src/common/package_looping_command.dart index 1a194bd45b9e..9e696e956192 100644 --- a/script/tool/lib/src/common/package_looping_command.dart +++ b/script/tool/lib/src/common/package_looping_command.dart @@ -357,7 +357,7 @@ abstract class PackageLoopingCommand extends PluginCommand { if (flutterConstraint != null && !flutterConstraint.allows(minFlutterVersion)) { return PackageResult.skip( - 'Does not support Flutter ${minFlutterVersion.toString()}'); + 'Does not support Flutter $minFlutterVersion'); } } @@ -365,8 +365,7 @@ abstract class PackageLoopingCommand extends PluginCommand { final Pubspec pubspec = package.parsePubspec(); final VersionConstraint? dartConstraint = pubspec.environment?['sdk']; if (dartConstraint != null && !dartConstraint.allows(minDartVersion)) { - return PackageResult.skip( - 'Does not support Dart ${minDartVersion.toString()}'); + return PackageResult.skip('Does not support Dart $minDartVersion'); } } From e8a7abcd654f53d9111cb273dd24a7a7c5974b7a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Aug 2022 01:00:08 +0000 Subject: [PATCH 576/844] [gh_actions]: Bump github/codeql-action from 2.1.12 to 2.1.18 (#6201) --- .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 7f2a4d8ec44e..8ca9fe9e823e 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -48,6 +48,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@27ea8f8fe5977c00f5b37e076ab846c5bd783b96 + uses: github/codeql-action/upload-sarif@2ca79b6fa8d3ec278944088b4aa5f46912db5d63 with: sarif_file: results.sarif From 9c9784e4f41a6aaf1c09166f999595e2f3554ea7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 4 Aug 2022 11:53:05 -0400 Subject: [PATCH 577/844] Roll Flutter from 183d3e410102 to 74aef9ff8786 (27 revisions) (#6203) --- .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 e677ec80ecb8..4423bbf4d78d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -183d3e410102f4347eedce60950379d86991323a +74aef9ff8786951fe0c4bd4c96dcb7b80caec219 From 5f2e02cf63b49774202ad809ce63a6dd0bc64837 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Aug 2022 17:02:06 +0000 Subject: [PATCH 578/844] [quick_actions]: Bump mockito-core from 3.2.4 to 4.6.1 in /packages/quick_actions/quick_actions_android/android (#5899) --- packages/quick_actions/quick_actions_android/CHANGELOG.md | 7 ++++++- .../quick_actions_android/android/build.gradle | 2 +- .../io/flutter/plugins/quickactions/QuickActionsTest.java | 8 +++----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/quick_actions/quick_actions_android/CHANGELOG.md b/packages/quick_actions/quick_actions_android/CHANGELOG.md index 5e70799e12cf..fff0b8d38149 100644 --- a/packages/quick_actions/quick_actions_android/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_android/CHANGELOG.md @@ -1,6 +1,11 @@ +## NEXT + +* Updates mockito-core to 4.6.1. +* Removes deprecated FieldSetter from QuickActionsTest. + ## 0.6.2 -* Update gradle version to 7.2.1 on Android. +* Updates gradle version to 7.2.1. ## 0.6.1 diff --git a/packages/quick_actions/quick_actions_android/android/build.gradle b/packages/quick_actions/quick_actions_android/android/build.gradle index 3ca840238567..ca7218c94d39 100644 --- a/packages/quick_actions/quick_actions_android/android/build.gradle +++ b/packages/quick_actions/quick_actions_android/android/build.gradle @@ -35,7 +35,7 @@ android { dependencies { testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-core:3.2.4' + testImplementation 'org.mockito:mockito-core:4.6.1' } compileOptions { diff --git a/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java b/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java index dc4b36e168db..911b789190ab 100644 --- a/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java +++ b/packages/quick_actions/quick_actions_android/android/src/test/java/io/flutter/plugins/quickactions/QuickActionsTest.java @@ -29,7 +29,6 @@ import java.nio.ByteBuffer; import org.junit.After; import org.junit.Test; -import org.mockito.internal.util.reflection.FieldSetter; public class QuickActionsTest { private static class TestBinaryMessenger implements BinaryMessenger { @@ -79,10 +78,9 @@ public void onAttachedToActivity_buildVersionSupported_invokesLaunchMethod() final QuickActionsPlugin plugin = new QuickActionsPlugin(); setUpMessengerAndFlutterPluginBinding(testBinaryMessenger, plugin); setBuildVersion(SUPPORTED_BUILD); - FieldSetter.setField( - plugin, - QuickActionsPlugin.class.getDeclaredField("handler"), - mock(MethodCallHandlerImpl.class)); + Field handler = plugin.getClass().getDeclaredField("handler"); + handler.setAccessible(true); + handler.set(plugin, mock(MethodCallHandlerImpl.class)); final Intent mockIntent = createMockIntentWithQuickActionExtra(); final Activity mockMainActivity = mock(Activity.class); when(mockMainActivity.getIntent()).thenReturn(mockIntent); From 01464063c6b63aa44a8e3d732fbfb49969e2b652 Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Thu, 4 Aug 2022 13:11:06 -0700 Subject: [PATCH 579/844] [quick_actions] add custom modulemap with test submodule (#6169) --- .../quick_actions_ios/CHANGELOG.md | 4 ++ .../quick_actions_ios/example/ios/Podfile | 1 + .../ios/Runner.xcodeproj/project.pbxproj | 54 +++++++++++++------ .../example/ios/Runner/Info.plist | 4 ++ .../example/ios/RunnerTests/RunnerTests.m | 6 ++- .../ios/Classes/FLTQuickActionsPlugin.h | 8 ++- .../ios/Classes/FLTQuickActionsPlugin.m | 11 +++- .../FLTQuickActionsPlugin_Test.h | 20 +++++++ .../ios/Classes/QuickActionsPlugin.modulemap | 10 ++++ .../ios/Classes/quick_actions_ios-umbrella.h | 10 ++++ .../ios/quick_actions_ios.podspec | 6 ++- .../quick_actions_ios/pubspec.yaml | 2 +- 12 files changed, 114 insertions(+), 22 deletions(-) create mode 100644 packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h create mode 100644 packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.modulemap create mode 100644 packages/quick_actions/quick_actions_ios/ios/Classes/quick_actions_ios-umbrella.h diff --git a/packages/quick_actions/quick_actions_ios/CHANGELOG.md b/packages/quick_actions/quick_actions_ios/CHANGELOG.md index 35f9c9fd51d9..9235afafb92b 100644 --- a/packages/quick_actions/quick_actions_ios/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.0+12 + +* Adds a custom module map with a Test submodule for unit tests on iOS platform. + ## 0.6.0+11 * Updates references to the obsolete master branch. diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Podfile b/packages/quick_actions/quick_actions_ios/example/ios/Podfile index 3924e59aa0f9..b52805241c18 100644 --- a/packages/quick_actions/quick_actions_ios/example/ios/Podfile +++ b/packages/quick_actions/quick_actions_ios/example/ios/Podfile @@ -31,6 +31,7 @@ target 'Runner' do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) target 'RunnerTests' do inherit! :search_paths + pod 'OCMock', '~> 3.9.1' end end diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj index 36dc0d81923b..a517b0942ae7 100644 --- a/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,16 +3,16 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 2632072169FF635893D8EB4D /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 436668746754BEEA28B76E55 /* libPods-RunnerTests.a */; }; 33E20B3526EFCDFC00A4A191 /* RunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33E20B3426EFCDFC00A4A191 /* RunnerTests.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 50EB54C1FE43DB743F5DEC7C /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D1A69703A518C37D73BF8B91 /* libPods-RunnerTests.a */; }; 686BE83025E58CCF00862533 /* RunnerUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 686BE82F25E58CCF00862533 /* RunnerUITests.m */; }; - 83C36CAF23D629E5ABE75B2A /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CCC799F2B0AB50A9C34344F0 /* libPods-Runner.a */; }; + 6A841C2B6AED5CF8DB2A1894 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C35AD3650AB6BF850E016715 /* libPods-Runner.a */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -57,6 +57,7 @@ 33E20B3426EFCDFC00A4A191 /* RunnerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerTests.m; sourceTree = ""; }; 33E20B3626EFCDFC00A4A191 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 436668746754BEEA28B76E55 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 5278439583922091276A37C9 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 686BE82D25E58CCF00862533 /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 686BE82F25E58CCF00862533 /* RunnerUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerUITests.m; sourceTree = ""; }; @@ -74,8 +75,7 @@ 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 = ""; }; 9D27FE1F0F21D4D47DDA16DE /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; - CCC799F2B0AB50A9C34344F0 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - D1A69703A518C37D73BF8B91 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + C35AD3650AB6BF850E016715 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; F0609304FBCAEC2289164BD5 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -84,7 +84,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 50EB54C1FE43DB743F5DEC7C /* libPods-RunnerTests.a in Frameworks */, + 2632072169FF635893D8EB4D /* libPods-RunnerTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -99,7 +99,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 83C36CAF23D629E5ABE75B2A /* libPods-Runner.a in Frameworks */, + 6A841C2B6AED5CF8DB2A1894 /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -185,8 +185,8 @@ A44AD0D63DEF785A2A2DEE28 /* Frameworks */ = { isa = PBXGroup; children = ( - CCC799F2B0AB50A9C34344F0 /* libPods-Runner.a */, - D1A69703A518C37D73BF8B91 /* libPods-RunnerTests.a */, + C35AD3650AB6BF850E016715 /* libPods-Runner.a */, + 436668746754BEEA28B76E55 /* libPods-RunnerTests.a */, ); name = Frameworks; sourceTree = ""; @@ -337,6 +337,7 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -373,6 +374,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -473,7 +475,11 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; INFOPLIST_FILE = RunnerTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; @@ -486,7 +492,11 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; INFOPLIST_FILE = RunnerTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; @@ -506,7 +516,11 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = RunnerUITests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerUITests; @@ -529,7 +543,11 @@ GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = RunnerUITests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerUITests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -655,7 +673,10 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -676,7 +697,10 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner/Info.plist b/packages/quick_actions/quick_actions_ios/example/ios/Runner/Info.plist index d6bca84ca23d..2128c14bb939 100644 --- a/packages/quick_actions/quick_actions_ios/example/ios/Runner/Info.plist +++ b/packages/quick_actions/quick_actions_ios/example/ios/Runner/Info.plist @@ -45,5 +45,9 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + diff --git a/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/RunnerTests.m b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/RunnerTests.m index 4a96d05acb58..3e35e2d6876b 100644 --- a/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/RunnerTests.m +++ b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/RunnerTests.m @@ -2,8 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +@import Flutter; @import quick_actions_ios; +@import quick_actions_ios.Test; @import XCTest; +#import @interface QuickActionsTests : XCTestCase @end @@ -11,7 +14,8 @@ @interface QuickActionsTests : XCTestCase @implementation QuickActionsTests - (void)testPlugin { - FLTQuickActionsPlugin *plugin = [[FLTQuickActionsPlugin alloc] init]; + FLTQuickActionsPlugin *plugin = + [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])]; XCTAssertNotNil(plugin); } diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.h b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.h index 8f98cc35e8ba..6eb6e2b782ea 100644 --- a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.h +++ b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.h @@ -2,7 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import +@import Flutter; @interface FLTQuickActionsPlugin : NSObject + +/// Unavailable. Please use `initWithChannel:` instead. +- (instancetype)init NS_UNAVAILABLE; + +/// Unavailable. Please use `initWithChannel:` instead. ++ (instancetype)new NS_UNAVAILABLE; @end diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m index 883352c2ac1d..dc1c866565b7 100644 --- a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m +++ b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m @@ -3,6 +3,7 @@ // found in the LICENSE file. #import "FLTQuickActionsPlugin.h" +#import "FLTQuickActionsPlugin_Test.h" static NSString *const kChannelName = @"plugins.flutter.io/quick_actions_ios"; @@ -17,12 +18,18 @@ + (void)registerWithRegistrar:(NSObject *)registrar { FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:kChannelName binaryMessenger:[registrar messenger]]; - FLTQuickActionsPlugin *instance = [[FLTQuickActionsPlugin alloc] init]; - instance.channel = channel; + FLTQuickActionsPlugin *instance = [[FLTQuickActionsPlugin alloc] initWithChannel:channel]; [registrar addMethodCallDelegate:instance channel:channel]; [registrar addApplicationDelegate:instance]; } +- (instancetype)initWithChannel:(FlutterMethodChannel *)channel { + if ((self = [super init])) { + _channel = channel; + } + return self; +} + - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { if ([call.method isEqualToString:@"setShortcutItems"]) { _setShortcutItems(call.arguments); diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h b/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h new file mode 100644 index 000000000000..c5fa43df727c --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import Flutter; + +NS_ASSUME_NONNULL_BEGIN + +/// APIs exposed for unit tests. +@interface FLTQuickActionsPlugin (Test) + +/// Initializes a FLTQuickActionsPlugin with the given method channel. +/// API exposed for unit tests. +/// @param channel A method channel. +/// @return The initialized FLTQuickActionsPlugin. +- (instancetype)initWithChannel:(FlutterMethodChannel *)channel; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.modulemap b/packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.modulemap new file mode 100644 index 000000000000..e8371cbb7c40 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.modulemap @@ -0,0 +1,10 @@ +framework module quick_actions_ios { + umbrella header "quick_actions_ios-umbrella.h" + + export * + module * { export * } + + explicit module Test { + header "FLTQuickActionsPlugin_Test.h" + } +} diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/quick_actions_ios-umbrella.h b/packages/quick_actions/quick_actions_ios/ios/Classes/quick_actions_ios-umbrella.h new file mode 100644 index 000000000000..d099a0411cf0 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/ios/Classes/quick_actions_ios-umbrella.h @@ -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 + +FOUNDATION_EXPORT double quickActionsIOSVersionNumber; +FOUNDATION_EXPORT const unsigned char quickActionsIOSVersionString[]; + +#import diff --git a/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios.podspec b/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios.podspec index e8485f9b4436..68eaa6ff7dc9 100644 --- a/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios.podspec +++ b/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios.podspec @@ -14,9 +14,11 @@ Downloaded by pub (not CocoaPods). s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/quick_actions' } s.documentation_url = 'https://pub.dev/packages/quick_actions' - s.source_files = 'Classes/**/*' - s.public_header_files = 'Classes/**/*.h' + s.source_files = 'Classes/**/*.{h,m}' + s.public_header_files = 'Classes/*.h' + s.private_header_files = 'Classes/PrivateHeaders/*.h' s.dependency 'Flutter' s.platform = :ios, '9.0' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } + s.module_map = 'Classes/QuickActionsPlugin.modulemap' end diff --git a/packages/quick_actions/quick_actions_ios/pubspec.yaml b/packages/quick_actions/quick_actions_ios/pubspec.yaml index 4dc91f4a5fe7..08ea406e9b9b 100644 --- a/packages/quick_actions/quick_actions_ios/pubspec.yaml +++ b/packages/quick_actions/quick_actions_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_ios description: An implementation for the iOS platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.6.0+11 +version: 0.6.0+12 environment: sdk: ">=2.15.0 <3.0.0" From f0c0c9d89be6761e8c057f4fb64f7af532a7ed86 Mon Sep 17 00:00:00 2001 From: Tomasz Gucio <72562119+tgucio@users.noreply.github.com> Date: Fri, 5 Aug 2022 01:17:42 +0200 Subject: [PATCH 580/844] [camera_windows] Check string size before Win32 MultiByte <-> WideChar conversions (#6170) This PR adds a check for string max length in Utf8FromUtf16 and Utf16FromUtf8, similar to flutter/flutter#99729 / flutter/flutter#94608. --- packages/camera/camera_windows/CHANGELOG.md | 4 ++++ packages/camera/camera_windows/pubspec.yaml | 2 +- .../camera/camera_windows/windows/string_utils.cpp | 12 ++++++------ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index ef0fad497f45..16ce26a5d306 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.1 + +* Adds a check for string size before Win32 MultiByte <-> WideChar conversions + ## 0.2.0 **BREAKING CHANGES**: diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index 9cf1793ad1a8..540a445ceebc 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_windows description: A Flutter plugin for getting information about and controlling the camera on Windows. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.2.0 +version: 0.2.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/camera/camera_windows/windows/string_utils.cpp b/packages/camera/camera_windows/windows/string_utils.cpp index 2e60e1bb01a7..34b13361e71f 100644 --- a/packages/camera/camera_windows/windows/string_utils.cpp +++ b/packages/camera/camera_windows/windows/string_utils.cpp @@ -19,10 +19,10 @@ std::string Utf8FromUtf16(const std::wstring& utf16_string) { int target_length = ::WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string.data(), static_cast(utf16_string.length()), nullptr, 0, nullptr, nullptr); - if (target_length == 0) { - return std::string(); - } std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } utf8_string.resize(target_length); int converted_length = ::WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string.data(), @@ -42,10 +42,10 @@ std::wstring Utf16FromUtf8(const std::string& utf8_string) { int target_length = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(), static_cast(utf8_string.length()), nullptr, 0); - if (target_length == 0) { - return std::wstring(); - } std::wstring utf16_string; + if (target_length == 0 || target_length > utf16_string.max_size()) { + return utf16_string; + } utf16_string.resize(target_length); int converted_length = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(), From c3da2d1dfc3e4bf4647091d3c785b1d9b3b16385 Mon Sep 17 00:00:00 2001 From: Ricardo Amador <32242716+ricardoamador@users.noreply.github.com> Date: Thu, 4 Aug 2022 16:25:42 -0700 Subject: [PATCH 581/844] Move Cirrus task logs to flutter-infra-staging project (#6180) * Changed cirrus logging to flutter-firebase-testlab-staging * Fix test data --- .../tool/lib/src/firebase_test_lab_command.dart | 2 +- .../test/firebase_test_lab_command_test.dart | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/script/tool/lib/src/firebase_test_lab_command.dart b/script/tool/lib/src/firebase_test_lab_command.dart index 4505259b731f..832870e0e4c4 100644 --- a/script/tool/lib/src/firebase_test_lab_command.dart +++ b/script/tool/lib/src/firebase_test_lab_command.dart @@ -59,7 +59,7 @@ class FirebaseTestLabCommand extends PackageLoopingCommand { help: 'Device model(s) to test. See https://cloud.google.com/sdk/gcloud/reference/firebase/test/android/run for more info'); argParser.addOption('results-bucket', - defaultsTo: 'gs://flutter_firebase_testlab'); + defaultsTo: 'gs://flutter_firebase_testlab_staging'); argParser.addOption( kEnableExperiment, defaultsTo: '', diff --git a/script/tool/test/firebase_test_lab_command_test.dart b/script/tool/test/firebase_test_lab_command_test.dart index a41409394728..1ab7055d2878 100644 --- a/script/tool/test/firebase_test_lab_command_test.dart +++ b/script/tool/test/firebase_test_lab_command_test.dart @@ -174,7 +174,7 @@ public class MainActivityTest { '/packages/plugin1/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin1/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab_staging --results-dir=plugins_android_test/plugin1/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin1/example'), ProcessCall( @@ -188,7 +188,7 @@ public class MainActivityTest { '/packages/plugin2/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin2/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab_staging --results-dir=plugins_android_test/plugin2/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin2/example'), ]), @@ -255,7 +255,7 @@ public class MainActivityTest { '/packages/plugin/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab_staging --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin/example'), ProcessCall( @@ -265,7 +265,7 @@ public class MainActivityTest { '/packages/plugin/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example/1/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab_staging --results-dir=plugins_android_test/plugin/buildId/testRunId/example/1/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin/example'), ]), @@ -320,7 +320,7 @@ public class MainActivityTest { '/packages/plugin/example/example1/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example1/0/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab_staging --results-dir=plugins_android_test/plugin/buildId/testRunId/example1/0/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin/example/example1'), ProcessCall( @@ -330,7 +330,7 @@ public class MainActivityTest { '/packages/plugin/example/example2/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example2/0/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab_staging --results-dir=plugins_android_test/plugin/buildId/testRunId/example2/0/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin/example/example2'), ]), @@ -613,7 +613,7 @@ public class MainActivityTest { '/packages/plugin/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab_staging --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30' .split(' '), '/packages/plugin/example'), ]), @@ -785,7 +785,7 @@ public class MainActivityTest { '/packages/plugin/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab_staging --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30' .split(' '), '/packages/plugin/example'), ]), From f07efc23410bdfb015c6aecf28a7568c5cb56bdb Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Fri, 5 Aug 2022 03:39:05 -0700 Subject: [PATCH 582/844] [quick_actions] backfill some unit tests for `FLTQuickActionsPlugin` class (#6206) --- .../quick_actions_ios/CHANGELOG.md | 4 + .../ios/Runner.xcodeproj/project.pbxproj | 4 + .../RunnerTests/FLTQuickActionsPluginTests.m | 156 ++++++++++++++++++ .../ios/Classes/FLTQuickActionsPlugin.m | 9 +- .../FLTQuickActionsPlugin_Test.h | 6 +- .../quick_actions_ios/pubspec.yaml | 2 +- 6 files changed, 174 insertions(+), 7 deletions(-) create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTQuickActionsPluginTests.m diff --git a/packages/quick_actions/quick_actions_ios/CHANGELOG.md b/packages/quick_actions/quick_actions_ios/CHANGELOG.md index 9235afafb92b..53817b804c57 100644 --- a/packages/quick_actions/quick_actions_ios/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.0+13 + +* Adds some unit tests for `FLTQuickActionsPlugin` class. + ## 0.6.0+12 * Adds a custom module map with a Test submodule for unit tests on iOS platform. diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj index a517b0942ae7..b70dc29a58f9 100644 --- a/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -18,6 +18,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 */; }; + E0C09C29289C729D00E6977E /* FLTQuickActionsPluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C09C28289C729D00E6977E /* FLTQuickActionsPluginTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -76,6 +77,7 @@ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9D27FE1F0F21D4D47DDA16DE /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; C35AD3650AB6BF850E016715 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + E0C09C28289C729D00E6977E /* FLTQuickActionsPluginTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTQuickActionsPluginTests.m; sourceTree = ""; }; F0609304FBCAEC2289164BD5 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -111,6 +113,7 @@ children = ( 33E20B3426EFCDFC00A4A191 /* RunnerTests.m */, 33E20B3626EFCDFC00A4A191 /* Info.plist */, + E0C09C28289C729D00E6977E /* FLTQuickActionsPluginTests.m */, ); path = RunnerTests; sourceTree = ""; @@ -413,6 +416,7 @@ buildActionMask = 2147483647; files = ( 33E20B3526EFCDFC00A4A191 /* RunnerTests.m in Sources */, + E0C09C29289C729D00E6977E /* FLTQuickActionsPluginTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTQuickActionsPluginTests.m b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTQuickActionsPluginTests.m new file mode 100644 index 000000000000..0d4070b6dcf6 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTQuickActionsPluginTests.m @@ -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 Flutter; +@import quick_actions_ios; +@import quick_actions_ios.Test; +@import XCTest; +#import + +@interface FLTQuickActionsPluginTests : XCTestCase + +@end + +@implementation FLTQuickActionsPluginTests + +// A dummy `UIApplicationShortcutItem`. +- (UIApplicationShortcutItem *)searchTheThingShortcutItem { + return [[UIApplicationShortcutItem alloc] + initWithType:@"SearchTheThing" + localizedTitle:@"Search the thing" + localizedSubtitle:nil + icon:[UIApplicationShortcutIcon + iconWithTemplateImageName:@"search_the_thing.png"] + userInfo:nil]; +} + +// A dummy raw shortcut item. +- (NSDictionary *)searchTheThingRawItem { + return @{ + @"type" : @"SearchTheThing", + @"localizedTitle" : @"Search the thing", + @"icon" : @"search_the_thing.png", + }; +} + +- (void)testHandleMethodCall_setShortcutItems { + FlutterMethodCall *call = + [FlutterMethodCall methodCallWithMethodName:@"setShortcutItems" + arguments:@[ [self searchTheThingRawItem] ]]; + + FLTQuickActionsPlugin *plugin = + [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])]; + XCTestExpectation *resultExpectation = + [self expectationWithDescription:@"result block must be called."]; + [plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertNil(result, @"result block must be called with nil."); + XCTAssertEqualObjects(UIApplication.sharedApplication.shortcutItems, + @[ [self searchTheThingShortcutItem] ], + @"shortcut items should be set correctly."); + [resultExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testHandleMethodCall_clearShortcutItems { + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"clearShortcutItems" + arguments:nil]; + + FLTQuickActionsPlugin *plugin = + [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])]; + XCTestExpectation *resultExpectation = + [self expectationWithDescription:@"result block must be called."]; + [plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertNil(result, @"result block must be called with nil."); + XCTAssertEqual(UIApplication.sharedApplication.shortcutItems.count, 0, + @"shortcut items should be cleared"); + [resultExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testHandleMethodCall_getLaunchAction { + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"getLaunchAction" + arguments:nil]; + + FLTQuickActionsPlugin *plugin = + [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])]; + XCTestExpectation *resultExpectation = + [self expectationWithDescription:@"result block must be called."]; + [plugin handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertNil(result, @"result block must be called with nil."); + [resultExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testHandleMethodCall_nonExistMethods { + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"nonExist" arguments:nil]; + + FLTQuickActionsPlugin *plugin = + [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])]; + XCTestExpectation *resultExpectation = + [self expectationWithDescription:@"result must be called."]; + [plugin + handleMethodCall:call + result:^(id _Nullable result) { + XCTAssertEqual(result, FlutterMethodNotImplemented, + @"result block must be called with FlutterMethodNotImplemented"); + [resultExpectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testApplicationPerformActionForShortcutItem { + id mockChannel = OCMClassMock([FlutterMethodChannel class]); + FLTQuickActionsPlugin *plugin = [[FLTQuickActionsPlugin alloc] initWithChannel:mockChannel]; + + UIApplicationShortcutItem *item = [self searchTheThingShortcutItem]; + + BOOL actionResult = [plugin application:[UIApplication sharedApplication] + performActionForShortcutItem:item + completionHandler:^(BOOL succeeded){/* no-op */}]; + XCTAssert(actionResult, @"performActionForShortcutItem must return true."); + OCMVerify([mockChannel invokeMethod:@"launch" arguments:item.type]); +} + +- (void)testApplicationDidFinishLaunchingWithOptions_launchWithShortcut { + FLTQuickActionsPlugin *plugin = + [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])]; + + UIApplicationShortcutItem *item = [self searchTheThingShortcutItem]; + + BOOL launchResult = [plugin application:[UIApplication sharedApplication] + didFinishLaunchingWithOptions:@{UIApplicationLaunchOptionsShortcutItemKey : item}]; + + XCTAssertFalse(launchResult, + @"didFinishLaunchingWithOptions must return false if launched from shortcut."); +} + +- (void)testApplicationDidFinishLaunchingWithOptions_launchWithoutShortcut { + FLTQuickActionsPlugin *plugin = + [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])]; + BOOL launchResult = [plugin application:[UIApplication sharedApplication] + didFinishLaunchingWithOptions:@{}]; + XCTAssertTrue(launchResult, + @"didFinishLaunchingWithOptions must return true if not launched from shortcut."); +} + +- (void)testApplicationDidBecomeActive { + UIApplicationShortcutItem *item = [self searchTheThingShortcutItem]; + id mockChannel = OCMClassMock([FlutterMethodChannel class]); + FLTQuickActionsPlugin *plugin = [[FLTQuickActionsPlugin alloc] initWithChannel:mockChannel]; + plugin.launchingShortcutType = item.type; + [plugin applicationDidBecomeActive:[UIApplication sharedApplication]]; + + OCMVerify([mockChannel invokeMethod:@"launch" arguments:item.type]); + XCTAssertNil(plugin.launchingShortcutType, + @"Must reset launchingShortcutType to nil after being used."); +} + +@end diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m index dc1c866565b7..798af0accb1e 100644 --- a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m +++ b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m @@ -9,7 +9,6 @@ @interface FLTQuickActionsPlugin () @property(nonatomic, retain) FlutterMethodChannel *channel; -@property(nonatomic, retain) NSString *shortcutType; @end @implementation FLTQuickActionsPlugin @@ -65,7 +64,7 @@ - (BOOL)application:(UIApplication *)application // Keep hold of the shortcut type and handle it in the // `applicationDidBecomeActure:` method once the Dart MethodChannel // is initialized. - self.shortcutType = shortcutItem.type; + self.launchingShortcutType = shortcutItem.type; // Return NO to indicate we handled the quick action to ensure // the `application:performActionFor:` method is not called (as @@ -77,9 +76,9 @@ - (BOOL)application:(UIApplication *)application } - (void)applicationDidBecomeActive:(UIApplication *)application { - if (self.shortcutType) { - [self handleShortcut:self.shortcutType]; - self.shortcutType = nil; + if (self.launchingShortcutType) { + [self handleShortcut:self.launchingShortcutType]; + self.launchingShortcutType = nil; } } diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h b/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h index c5fa43df727c..bd2d756e6767 100644 --- a/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h +++ b/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h @@ -7,7 +7,11 @@ NS_ASSUME_NONNULL_BEGIN /// APIs exposed for unit tests. -@interface FLTQuickActionsPlugin (Test) +@interface FLTQuickActionsPlugin () + +/// The type of the shortcut item selected when launching the app. +/// API exposed for unit tests. +@property(nonatomic, strong, nullable) NSString *launchingShortcutType; /// Initializes a FLTQuickActionsPlugin with the given method channel. /// API exposed for unit tests. diff --git a/packages/quick_actions/quick_actions_ios/pubspec.yaml b/packages/quick_actions/quick_actions_ios/pubspec.yaml index 08ea406e9b9b..42dbde372fb9 100644 --- a/packages/quick_actions/quick_actions_ios/pubspec.yaml +++ b/packages/quick_actions/quick_actions_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_ios description: An implementation for the iOS platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.6.0+12 +version: 0.6.0+13 environment: sdk: ">=2.15.0 <3.0.0" From 4219332cf3ec011adda4c9cb79d0e25791c517c2 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 5 Aug 2022 11:45:06 -0400 Subject: [PATCH 583/844] Roll Flutter from 74aef9ff8786 to b02f68a66c51 (21 revisions) (#6210) --- .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 4423bbf4d78d..f6fc3e26b01e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -74aef9ff8786951fe0c4bd4c96dcb7b80caec219 +b02f68a66c51df1430dd320f0dfe61c63a76703c From 004af7fadc34d6ab09d3e60aff5c5cbd7eecf86b Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 5 Aug 2022 16:33:05 -0400 Subject: [PATCH 584/844] [ci] Run analyze with minimum resolved packages (#6207) --- .cirrus.yml | 8 +++ .../image_picker_android/CHANGELOG.md | 5 ++ .../image_picker_android/pubspec.yaml | 4 +- .../local_auth_android/CHANGELOG.md | 5 ++ .../local_auth_android/pubspec.yaml | 4 +- .../local_auth/local_auth_ios/CHANGELOG.md | 5 ++ .../local_auth/local_auth_ios/pubspec.yaml | 4 +- .../local_auth_windows/CHANGELOG.md | 5 ++ .../local_auth_windows/pubspec.yaml | 4 +- .../url_launcher_web/CHANGELOG.md | 5 ++ .../url_launcher_web/pubspec.yaml | 4 +- .../webview_flutter_wkwebview/CHANGELOG.md | 5 ++ .../webview_flutter_wkwebview/pubspec.yaml | 4 +- script/tool/CHANGELOG.md | 7 ++- script/tool/lib/src/analyze_command.dart | 22 +++++++-- script/tool/pubspec.yaml | 2 +- script/tool/test/analyze_command_test.dart | 49 +++++++++++++++++++ 17 files changed, 124 insertions(+), 18 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 84961507f89c..3d0c525a5c42 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -171,6 +171,14 @@ task: # Only analyze lib/; non-client code doesn't need to work on # all supported legacy version. - ./script/tool_runner.sh analyze --lib-only --skip-if-not-supporting-flutter-version="$CHANNEL" --custom-analysis=script/configs/custom_analysis.yaml + # Does a sanity check that plugins pass analysis with the lowest possible + # versions of all dependencies. This is to catch cases where we add use of + # new APIs but forget to update minimum versions of dependencies to when + # those APIs are introduced. + - name: downgraded_analyze + depends_on: analyze + analyze_script: + - ./script/tool_runner.sh analyze --downgrade - name: readme_excerpts env: CIRRUS_CLONE_SUBMODULES: true diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index 664be36f880f..193828041ac9 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.8.5+2 + +* Updates `image_picker_platform_interface` constraint to the correct minimum + version. + ## 0.8.5+1 * Switches to an internal method channel implementation. diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index 8cbaaac71daf..6ae76878cb6a 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/plugins/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.5+1 +version: 0.8.5+2 environment: sdk: ">=2.14.0 <3.0.0" @@ -21,7 +21,7 @@ dependencies: flutter: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 - image_picker_platform_interface: ^2.3.0 + image_picker_platform_interface: ^2.5.0 dev_dependencies: flutter_test: diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 6a308ee95285..97f4f7f82904 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.0.10 + +* Updates `local_auth_platform_interface` constraint to the correct minimum + version. + ## 1.0.9 * Updates androidx.fragment version to 1.5.1. diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index b36b92ed3c23..af1ec5104b87 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.9 +version: 1.0.10 environment: sdk: ">=2.14.0 <3.0.0" @@ -22,7 +22,7 @@ dependencies: sdk: flutter flutter_plugin_android_lifecycle: ^2.0.1 intl: ^0.17.0 - local_auth_platform_interface: ^1.0.0 + local_auth_platform_interface: ^1.0.1 dev_dependencies: flutter_test: diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index 4b8e0653a7ad..7892bbcf00ba 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.0.8 + +* Updates `local_auth_platform_interface` constraint to the correct minimum + version. + ## 1.0.7 * Updates references to the obsolete master branch. diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index 043d84eb4a2c..e38e3036b03f 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.7 +version: 1.0.8 environment: sdk: ">=2.14.0 <3.0.0" @@ -20,7 +20,7 @@ dependencies: flutter: sdk: flutter intl: ^0.17.0 - local_auth_platform_interface: ^1.0.0 + local_auth_platform_interface: ^1.0.1 dev_dependencies: flutter_test: diff --git a/packages/local_auth/local_auth_windows/CHANGELOG.md b/packages/local_auth/local_auth_windows/CHANGELOG.md index f6c5e909b31e..14ac67d26a34 100644 --- a/packages/local_auth/local_auth_windows/CHANGELOG.md +++ b/packages/local_auth/local_auth_windows/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.0.2 + +* Updates `local_auth_platform_interface` constraint to the correct minimum + version. + ## 1.0.1 * Updates references to the obsolete master branch. diff --git a/packages/local_auth/local_auth_windows/pubspec.yaml b/packages/local_auth/local_auth_windows/pubspec.yaml index b42a4f846cc3..99af0ccd882b 100644 --- a/packages/local_auth/local_auth_windows/pubspec.yaml +++ b/packages/local_auth/local_auth_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_windows description: Windows implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.1 +version: 1.0.2 environment: sdk: ">=2.14.0 <3.0.0" @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - local_auth_platform_interface: ^1.0.0 + local_auth_platform_interface: ^1.0.1 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 75c0819cefdc..01d739238d40 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.13 + +* Updates `url_launcher_platform_interface` constraint to the correct minimum + version. + ## 2.0.12 * Fixes call to `setState` after dispose on the `Link` widget. diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index d0e0fa905d57..6d4c80689427 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_web description: Web platform implementation of url_launcher repository: https://github.com/flutter/plugins/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.0.12 +version: 2.0.13 environment: sdk: ">=2.12.0 <3.0.0" @@ -21,7 +21,7 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter - url_launcher_platform_interface: ^2.0.0 + url_launcher_platform_interface: ^2.0.3 dev_dependencies: flutter_test: diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 5d445028f4e1..a533ce6a6808 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.9.3 + +* Updates `webview_flutter_platform_interface` constraint to the correct minimum + version. + ## 2.9.2 * Fixes crash when an Objective-C object in `FWFInstanceManager` is released, but the dealloc diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 9837519810b1..a4847d5db5ea 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/plugins/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: 2.9.2 +version: 2.9.3 environment: sdk: ">=2.17.0 <3.0.0" @@ -19,7 +19,7 @@ dependencies: flutter: sdk: flutter path: ^1.8.0 - webview_flutter_platform_interface: ^1.8.0 + webview_flutter_platform_interface: ^1.9.0 dev_dependencies: build_runner: ^2.1.5 diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 667f1a188d19..50e389fd3192 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,10 +1,15 @@ +## 0.9.1 + +* Adds a `--downgrade` flag to `analyze` for analyzing with the oldest possible + versions of packages. + ## 0.9.0 * Replaces PR-description-based version/changelog/breaking change check overrides in `version-check` with label-based overrides using a new `pr-labels` flag, since we don't actually have reliable access to the PR description in checks. - + ## 0.8.10 - Adds a new `remove-dev-dependencies` command to remove `dev_dependencies` diff --git a/script/tool/lib/src/analyze_command.dart b/script/tool/lib/src/analyze_command.dart index 54b4f33a2342..c7a953c50cac 100644 --- a/script/tool/lib/src/analyze_command.dart +++ b/script/tool/lib/src/analyze_command.dart @@ -32,12 +32,16 @@ class AnalyzeCommand extends PackageLoopingCommand { valueHelp: 'dart-sdk', help: 'An optional path to a Dart SDK; this is used to override the ' 'SDK used to provide analysis.'); + argParser.addFlag(_downgradeFlag, + help: 'Runs "flutter pub downgrade" before analysis to verify that ' + 'the minimum constraints are sufficiently new for APIs used.'); argParser.addFlag(_libOnlyFlag, help: 'Only analyze the lib/ directory of the main package, not the ' 'entire package.'); } static const String _customAnalysisFlag = 'custom-analysis'; + static const String _downgradeFlag = 'downgrade'; static const String _libOnlyFlag = 'lib-only'; static const String _analysisSdk = 'analysis-sdk'; @@ -113,6 +117,12 @@ class AnalyzeCommand extends PackageLoopingCommand { return PackageResult.skip('No lib/ directory.'); } + if (getBoolArg(_downgradeFlag)) { + if (!await _runPubCommand(package, 'downgrade')) { + return PackageResult.fail(['Unable to downgrade dependencies']); + } + } + // Analysis runs over the package and all subpackages (unless only lib/ is // being analyzed), so all of them need `flutter pub get` run before // analyzing. `example` packages can be skipped since 'flutter packages get' @@ -127,10 +137,7 @@ class AnalyzeCommand extends PackageLoopingCommand { !RepositoryPackage(packageToGet.directory.parent) .pubspecFile .existsSync()) { - final int exitCode = await processRunner.runAndStream( - flutterCommand, ['pub', 'get'], - workingDir: packageToGet.directory); - if (exitCode != 0) { + if (!await _runPubCommand(packageToGet, 'get')) { return PackageResult.fail(['Unable to get dependencies']); } } @@ -147,4 +154,11 @@ class AnalyzeCommand extends PackageLoopingCommand { } return PackageResult.success(); } + + Future _runPubCommand(RepositoryPackage package, String command) async { + final int exitCode = await processRunner.runAndStream( + flutterCommand, ['pub', command], + workingDir: package.directory); + return exitCode == 0; + } } diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index a99be7098197..e80afa229362 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.9.0 +version: 0.9.1 dependencies: args: ^2.1.0 diff --git a/script/tool/test/analyze_command_test.dart b/script/tool/test/analyze_command_test.dart index d3abf0b6b8e0..e10780fa9ecb 100644 --- a/script/tool/test/analyze_command_test.dart +++ b/script/tool/test/analyze_command_test.dart @@ -187,6 +187,33 @@ void main() { ); }); + test('downgrades first when requested', () async { + final RepositoryPackage plugin = createFakePlugin('a', packagesDir); + + await runCapturingPrint(runner, ['analyze', '--downgrade']); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall( + 'flutter', + const ['pub', 'downgrade'], + plugin.path, + ), + ProcessCall( + 'flutter', + const ['pub', 'get'], + plugin.path, + ), + ProcessCall( + 'dart', + const ['analyze', '--fatal-infos'], + plugin.path, + ), + ]), + ); + }); + group('verifies analysis settings', () { test('fails analysis_options.yaml', () async { createFakePlugin('foo', packagesDir, @@ -312,6 +339,28 @@ void main() { ); }); + test('fails if "pub downgrade" fails', () async { + createFakePlugin('foo', packagesDir); + + processRunner.mockProcessesForExecutable['flutter'] = [ + MockProcess(exitCode: 1) // flutter pub downgrade + ]; + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['analyze', '--downgrade'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Unable to downgrade dependencies'), + ]), + ); + }); + test('fails if "analyze" fails', () async { createFakePlugin('foo', packagesDir); From 85b11c23f3a6dd95b19a20e0f0c51403c5d1030d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 6 Aug 2022 11:57:04 -0400 Subject: [PATCH 585/844] Roll Flutter from b02f68a66c51 to fec5f77cd2f7 (19 revisions) (#6214) --- .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 f6fc3e26b01e..13161a91a00c 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b02f68a66c51df1430dd320f0dfe61c63a76703c +fec5f77cd2f777fb4ac36c5b636e8ad954d1dfaa From 7af13b3bffdc067e1072a1c9868fece0a0a5d880 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 7 Aug 2022 13:20:05 -0400 Subject: [PATCH 586/844] Roll Flutter from fec5f77cd2f7 to 5820b5f03927 (8 revisions) (#6215) --- .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 13161a91a00c..dc8edd877546 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -fec5f77cd2f777fb4ac36c5b636e8ad954d1dfaa +5820b5f03927491b81dcdbd4bbf00f64012e0478 From 8ab74527126925cd8c4001a4815390821e4e7bab Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 8 Aug 2022 13:04:12 -0400 Subject: [PATCH 587/844] Roll Flutter from 5820b5f03927 to a4947768b729 (9 revisions) (#6217) --- .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 dc8edd877546..136cf7d3925f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -5820b5f03927491b81dcdbd4bbf00f64012e0478 +a4947768b72956c353bf950b3febe36a77cb106b From ece8788513a4f720411eaf61bdb91d79207d8fea Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Mon, 8 Aug 2022 11:58:05 -0700 Subject: [PATCH 588/844] [google_maps_ios] Cache `+[GMSServices sharedServices]` when first map is created (#6211) --- .../google_maps_flutter_ios/CHANGELOG.md | 4 ++++ .../example/ios/RunnerTests/GoogleMapsTests.m | 17 +++++++++++++++++ .../ios/Classes/GoogleMapController.m | 18 ++++++++++++++++++ .../google_maps_flutter_ios/pubspec.yaml | 2 +- 4 files changed, 40 insertions(+), 1 deletion(-) 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 e4b243a063a8..33d96e963446 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.1.11 + +* Precaches Google Maps services initialization and syncing. + ## 2.1.10 * Splits iOS implementation out of `google_maps_flutter` as a federated diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/GoogleMapsTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/GoogleMapsTests.m index a8768e18d73b..71f1162890b4 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/GoogleMapsTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios/RunnerTests/GoogleMapsTests.m @@ -5,10 +5,15 @@ @import google_maps_flutter_ios; @import google_maps_flutter_ios.Test; @import XCTest; +@import GoogleMaps; #import #import "PartiallyMockedMapView.h" +@interface FLTGoogleMapFactory (Test) +@property(strong, nonatomic, readonly) id sharedMapServices; +@end + @interface GoogleMapsTests : XCTestCase @end @@ -39,4 +44,16 @@ - (void)testFrameObserver { XCTAssertEqual(mapView.frameObserverCount, 0); } +- (void)testMapsServiceSync { + id registrar = OCMProtocolMock(@protocol(FlutterPluginRegistrar)); + FLTGoogleMapFactory *factory1 = [[FLTGoogleMapFactory alloc] initWithRegistrar:registrar]; + XCTAssertNotNil(factory1.sharedMapServices); + FLTGoogleMapFactory *factory2 = [[FLTGoogleMapFactory alloc] initWithRegistrar:registrar]; + // Test pointer equality, should be same retained singleton +[GMSServices sharedServices] object. + // Retaining the opaque object should be enough to avoid multiple internal initializations, + // but don't test the internals of the GoogleMaps API. Assume that it does what is documented. + // https://developers.google.com/maps/documentation/ios-sdk/reference/interface_g_m_s_services#a436e03c32b1c0be74e072310a7158831 + XCTAssertEqual(factory1.sharedMapServices, factory2.sharedMapServices); +} + @end 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 41c5d77f9cf1..bd50c2d7a6de 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 @@ -11,11 +11,14 @@ @interface FLTGoogleMapFactory () @property(weak, nonatomic) NSObject *registrar; +@property(strong, nonatomic, readonly) id sharedMapServices; @end @implementation FLTGoogleMapFactory +@synthesize sharedMapServices = _sharedMapServices; + - (instancetype)initWithRegistrar:(NSObject *)registrar { self = [super init]; if (self) { @@ -31,11 +34,26 @@ - (instancetype)initWithRegistrar:(NSObject *)registrar - (NSObject *)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args { + // Precache shared map services, if needed. + // Retain the shared map services singleton, don't use the result for anything. + (void)[self sharedMapServices]; + return [[FLTGoogleMapController alloc] initWithFrame:frame viewIdentifier:viewId arguments:args registrar:self.registrar]; } + +- (id)sharedMapServices { + if (_sharedMapServices == nil) { + // Calling this prepares GMSServices on a background thread controlled + // by the GoogleMaps framework. + // Retain the singleton to cache the initialization work across all map views. + _sharedMapServices = [GMSServices sharedServices]; + } + return _sharedMapServices; +} + @end @interface FLTGoogleMapController () 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 f55da6e32cb2..a87096286c7d 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/plugins/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.1.10 +version: 2.1.11 environment: sdk: ">=2.14.0 <3.0.0" From cda37d5640f9fe1d4b1bac9a6e610d5ca4e867ee Mon Sep 17 00:00:00 2001 From: Rodrigues Date: Tue, 9 Aug 2022 02:52:06 +0300 Subject: [PATCH 589/844] Uncomments avoid_redundant_argument_values in analysis_options.yaml (#6163) --- analysis_options.yaml | 2 +- packages/camera/camera/CHANGELOG.md | 4 + packages/camera/camera/example/lib/main.dart | 7 -- packages/camera/camera/pubspec.yaml | 2 +- .../camera/test/camera_preview_test.dart | 1 - packages/camera/camera/test/camera_test.dart | 7 -- .../camera/camera/test/camera_value_test.dart | 3 - packages/camera/camera_android/CHANGELOG.md | 4 + .../camera_android/example/lib/main.dart | 7 -- packages/camera/camera_android/pubspec.yaml | 2 +- .../test/android_camera_test.dart | 1 - .../camera/camera_avfoundation/CHANGELOG.md | 3 +- .../camera_avfoundation/example/lib/main.dart | 7 -- .../camera/camera_avfoundation/pubspec.yaml | 2 +- .../test/avfoundation_camera_test.dart | 1 - .../camera_platform_interface/CHANGELOG.md | 1 + .../method_channel_camera_test.dart | 1 - packages/camera/camera_web/CHANGELOG.md | 1 + .../integration_test/camera_options_test.dart | 4 +- .../integration_test/camera_service_test.dart | 2 +- .../integration_test/camera_web_test.dart | 4 +- packages/camera/camera_windows/CHANGELOG.md | 4 + .../camera_windows/example/lib/main.dart | 2 - packages/camera/camera_windows/pubspec.yaml | 2 +- .../file_selector_web/CHANGELOG.md | 4 + .../file_selector_web_test.dart | 3 +- .../google_maps_flutter/CHANGELOG.md | 1 + .../integration_test/google_maps_test.dart | 24 ----- .../example/lib/map_click.dart | 1 - .../example/lib/map_ui.dart | 1 - .../example/lib/padding.dart | 1 - .../example/lib/place_marker.dart | 1 - .../example/lib/tile_overlay.dart | 1 - .../test/fake_maps_controllers.dart | 3 +- .../test/google_map_test.dart | 12 --- .../test/polyline_updates_test.dart | 3 +- .../google_maps_flutter_android/CHANGELOG.md | 4 + .../integration_test/google_maps_test.dart | 24 ----- .../example/lib/map_click.dart | 1 - .../example/lib/map_ui.dart | 1 - .../example/lib/padding.dart | 1 - .../example/lib/place_marker.dart | 1 - .../example/lib/tile_overlay.dart | 1 - .../google_maps_flutter_ios/CHANGELOG.md | 4 + .../integration_test/google_maps_test.dart | 23 ----- .../example/lib/map_click.dart | 1 - .../example/lib/map_ui.dart | 1 - .../example/lib/padding.dart | 1 - .../example/lib/place_marker.dart | 1 - .../example/lib/tile_overlay.dart | 1 - .../CHANGELOG.md | 4 + .../test/types/tile_overlay_test.dart | 2 - .../google_maps_flutter_web/CHANGELOG.md | 4 + .../google_maps_controller_test.dart | 2 - .../integration_test/projection_test.dart | 4 +- .../google_sign_in/CHANGELOG.md | 4 + .../google_sign_in/lib/google_sign_in.dart | 5 +- .../google_sign_in/pubspec.yaml | 2 +- .../image_picker/image_picker/CHANGELOG.md | 4 + .../test/image_picker_deprecated_test.dart | 93 +++++++------------ .../image_picker/test/image_picker_test.dart | 65 +++---------- .../CHANGELOG.md | 4 + .../new_method_channel_image_picker_test.dart | 4 +- .../in_app_purchase/CHANGELOG.md | 4 + .../in_app_purchase/example/lib/main.dart | 9 +- .../in_app_purchase/pubspec.yaml | 2 +- .../test/in_app_purchase_test.dart | 4 +- .../in_app_purchase_android/CHANGELOG.md | 4 + .../example/lib/main.dart | 6 +- .../billing_client_wrapper.dart | 2 +- .../in_app_purchase_android/pubspec.yaml | 2 +- .../billing_client_wrapper_test.dart | 14 +-- ...rchase_android_platform_addition_test.dart | 2 +- ...in_app_purchase_android_platform_test.dart | 2 +- .../in_app_purchase_storekit/CHANGELOG.md | 4 + .../example/lib/main.dart | 8 +- .../in_app_purchase_storekit_platform.dart | 3 +- .../in_app_purchase_storekit/pubspec.yaml | 2 +- .../test/fakes/fake_storekit_platform.dart | 16 +--- .../sk_methodchannel_apis_test.dart | 4 +- .../sk_test_stub_objects.dart | 1 - packages/local_auth/local_auth/CHANGELOG.md | 4 + .../local_auth/example/lib/main.dart | 2 - packages/local_auth/local_auth/pubspec.yaml | 2 +- .../local_auth/test/local_auth_test.dart | 1 - .../local_auth_android/CHANGELOG.md | 4 + .../local_auth_android/example/lib/main.dart | 2 - .../local_auth_android/pubspec.yaml | 2 +- .../local_auth/local_auth_ios/CHANGELOG.md | 4 + .../local_auth_ios/example/lib/main.dart | 2 - .../local_auth/local_auth_ios/pubspec.yaml | 2 +- .../local_auth_windows/CHANGELOG.md | 4 + .../local_auth_windows/example/lib/main.dart | 2 - .../local_auth_windows/pubspec.yaml | 2 +- .../path_provider/path_provider/CHANGELOG.md | 4 + .../integration_test/path_provider_test.dart | 3 +- .../path_provider_windows/CHANGELOG.md | 4 + .../lib/src/path_provider_windows_real.dart | 2 +- .../path_provider_windows/pubspec.yaml | 2 +- .../test/path_provider_windows_test.dart | 4 +- .../url_launcher/url_launcher/CHANGELOG.md | 4 + .../test/src/url_launcher_string_test.dart | 8 +- .../test/src/url_launcher_uri_test.dart | 4 +- .../video_player/video_player/CHANGELOG.md | 4 + .../controller_swap_test.dart | 3 +- .../integration_test/video_player_test.dart | 13 ++- .../video_player/example/lib/main.dart | 3 +- .../video_player/lib/video_player.dart | 3 +- .../video_player/video_player/pubspec.yaml | 2 +- .../video_player/test/video_player_test.dart | 29 +++--- .../video_player/test/web_vtt_test.dart | 5 +- .../video_player_android/CHANGELOG.md | 4 + .../integration_test/video_player_test.dart | 11 +-- .../example/lib/mini_controller.dart | 1 - .../test/android_video_player_test.dart | 2 +- .../video_player_avfoundation/CHANGELOG.md | 1 + .../integration_test/video_player_test.dart | 11 +-- .../example/lib/mini_controller.dart | 1 - .../test/avfoundation_video_player_test.dart | 2 +- .../CHANGELOG.md | 3 +- .../lib/{messages.dart => messages.g.dart} | 0 .../lib/method_channel_video_player.dart | 2 +- .../pigeons/messages.dart | 2 +- .../pubspec.yaml | 2 +- .../method_channel_video_player_test.dart | 4 +- .../test/test.dart | 2 +- .../webview_flutter/CHANGELOG.md | 1 + .../webview_flutter_test.dart | 13 +-- .../test/webview_flutter_test.dart | 16 +--- .../webview_flutter_android/CHANGELOG.md | 4 + .../webview_flutter_test.dart | 10 +- .../test/android_webview_test.dart | 2 +- .../test/webview_android_widget_test.dart | 2 - .../CHANGELOG.md | 1 + .../webview_method_channel_test.dart | 7 +- .../webview_flutter_wkwebview/CHANGELOG.md | 4 + .../webview_flutter_test.dart | 11 +-- .../test/src/web_kit_webview_widget_test.dart | 6 +- .../v4/webkit_webview_controller_test.dart | 2 +- .../tool/lib/src/common/plugin_command.dart | 1 - .../lib/src/firebase_test_lab_command.dart | 2 +- script/tool/lib/src/lint_android_command.dart | 2 +- .../tool/lib/src/publish_check_command.dart | 5 +- .../tool/lib/src/publish_plugin_command.dart | 7 +- .../tool/lib/src/version_check_command.dart | 2 - .../test/build_examples_command_test.dart | 2 +- script/tool/test/common/file_utils_test.dart | 2 +- .../common/package_looping_command_test.dart | 16 ++-- .../tool/test/common/plugin_utils_test.dart | 9 +- .../test/common/repository_package_test.dart | 2 +- .../tool/test/custom_test_command_test.dart | 4 +- .../tool/test/license_check_command_test.dart | 2 +- .../tool/test/lint_android_command_test.dart | 6 +- .../tool/test/lint_podspecs_command_test.dart | 2 +- .../test/update_excerpts_command_test.dart | 6 +- script/tool/test/util.dart | 5 +- 156 files changed, 300 insertions(+), 512 deletions(-) rename packages/video_player/video_player_platform_interface/lib/{messages.dart => messages.g.dart} (100%) diff --git a/analysis_options.yaml b/analysis_options.yaml index 87515a471050..224ac68cf543 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -83,7 +83,7 @@ linter: # - avoid_positional_boolean_parameters # would have been nice to enable this but by now there's too many places that break it # - avoid_print # LOCAL CHANGE - Needs to be enabled and violations fixed. # - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356) - # - avoid_redundant_argument_values # LOCAL CHANGE - Needs to be enabled and violations fixed. + - avoid_redundant_argument_values - avoid_relative_lib_imports - avoid_renaming_method_parameters - avoid_return_types_on_setters diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 1dc9270824d8..d112a4345695 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.0+1 + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 0.10.0 * **Breaking Change** Bumps default camera_web package version, which updates permission exception code from `cameraPermission` to `CameraAccessDenied`. diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index b90b0033563a..c5804e7d0624 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -159,7 +159,6 @@ class _CameraExampleHomeState extends State Padding( padding: const EdgeInsets.all(5.0), child: Row( - mainAxisAlignment: MainAxisAlignment.start, children: [ _cameraTogglesRowWidget(), _thumbnailWidget(), @@ -271,7 +270,6 @@ class _CameraExampleHomeState extends State children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, children: [ IconButton( icon: const Icon(Icons.flash_on), @@ -325,7 +323,6 @@ class _CameraExampleHomeState extends State child: ClipRect( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, children: [ IconButton( icon: const Icon(Icons.flash_off), @@ -397,7 +394,6 @@ class _CameraExampleHomeState extends State ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, children: [ TextButton( style: styleAuto, @@ -435,7 +431,6 @@ class _CameraExampleHomeState extends State ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, children: [ Text(_minAvailableExposureOffset.toString()), Slider( @@ -486,7 +481,6 @@ class _CameraExampleHomeState extends State ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, children: [ TextButton( style: styleAuto, @@ -523,7 +517,6 @@ class _CameraExampleHomeState extends State return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, children: [ IconButton( icon: const Icon(Icons.camera_alt), diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index e9a07e6b806c..5af8640493c6 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.10.0 +version: 0.10.0+1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera/test/camera_preview_test.dart b/packages/camera/camera/test/camera_preview_test.dart index fe2f4f4e35c7..bedb0ea8e01f 100644 --- a/packages/camera/camera/test/camera_preview_test.dart +++ b/packages/camera/camera/test/camera_preview_test.dart @@ -201,7 +201,6 @@ void main() { controller.value = controller.value.copyWith( isInitialized: true, deviceOrientation: DeviceOrientation.portraitUp, - lockedCaptureOrientation: null, recordingOrientation: const Optional.fromNullable( DeviceOrientation.landscapeLeft), previewSize: const Size(480, 640), diff --git a/packages/camera/camera/test/camera_test.dart b/packages/camera/camera/test/camera_test.dart index 0d3195ba4b4b..3c12648f13b9 100644 --- a/packages/camera/camera/test/camera_test.dart +++ b/packages/camera/camera/test/camera_test.dart @@ -697,7 +697,6 @@ void main() { PlatformException( code: 'TEST_ERROR', message: 'This is a test error message', - details: null, ), ); @@ -742,7 +741,6 @@ void main() { PlatformException( code: 'TEST_ERROR', message: 'This is a test error message', - details: null, ), ); @@ -787,7 +785,6 @@ void main() { PlatformException( code: 'TEST_ERROR', message: 'This is a test error message', - details: null, ), ); @@ -1219,7 +1216,6 @@ void main() { PlatformException( code: 'TEST_ERROR', message: 'This is a test error message', - details: null, ), ); @@ -1285,7 +1281,6 @@ void main() { PlatformException( code: 'TEST_ERROR', message: 'This is a test error message', - details: null, ), ); @@ -1339,7 +1334,6 @@ void main() { PlatformException( code: 'TEST_ERROR', message: 'This is a test error message', - details: null, ), ); @@ -1385,7 +1379,6 @@ void main() { PlatformException( code: 'TEST_ERROR', message: 'This is a test error message', - details: null, ), ); diff --git a/packages/camera/camera/test/camera_value_test.dart b/packages/camera/camera/test/camera_value_test.dart index e70f2ce69868..37168dbd48d7 100644 --- a/packages/camera/camera/test/camera_value_test.dart +++ b/packages/camera/camera/test/camera_value_test.dart @@ -18,7 +18,6 @@ void main() { test('Can be created', () { const CameraValue cameraValue = CameraValue( isInitialized: false, - errorDescription: null, previewSize: Size(10, 10), isRecordingPaused: false, isRecordingVideo: false, @@ -32,7 +31,6 @@ void main() { lockedCaptureOrientation: DeviceOrientation.portraitUp, recordingOrientation: DeviceOrientation.portraitUp, focusPointSupported: true, - isPreviewPaused: false, previewPauseOrientation: DeviceOrientation.portraitUp, ); @@ -129,7 +127,6 @@ void main() { test('toString() works as expected', () { const CameraValue cameraValue = CameraValue( isInitialized: false, - errorDescription: null, previewSize: Size(10, 10), isRecordingPaused: false, isRecordingVideo: false, diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index 3a743340dc6e..2ae809f9782f 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.0+1 + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 0.10.0 * **Breaking Change** Updates Android camera access permission error codes to be consistent with other platforms. If your app still handles the legacy `cameraPermission` exception, please update it to handle the new permission exception codes that are noted in the README. diff --git a/packages/camera/camera_android/example/lib/main.dart b/packages/camera/camera_android/example/lib/main.dart index 1dbdabb7d11c..9ebc27e4be5b 100644 --- a/packages/camera/camera_android/example/lib/main.dart +++ b/packages/camera/camera_android/example/lib/main.dart @@ -161,7 +161,6 @@ class _CameraExampleHomeState extends State Padding( padding: const EdgeInsets.all(5.0), child: Row( - mainAxisAlignment: MainAxisAlignment.start, children: [ _cameraTogglesRowWidget(), _thumbnailWidget(), @@ -274,7 +273,6 @@ class _CameraExampleHomeState extends State children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, children: [ IconButton( icon: const Icon(Icons.flash_on), @@ -328,7 +326,6 @@ class _CameraExampleHomeState extends State child: ClipRect( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, children: [ IconButton( icon: const Icon(Icons.flash_off), @@ -400,7 +397,6 @@ class _CameraExampleHomeState extends State ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, children: [ TextButton( style: styleAuto, @@ -439,7 +435,6 @@ class _CameraExampleHomeState extends State ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, children: [ Text(_minAvailableExposureOffset.toString()), Slider( @@ -490,7 +485,6 @@ class _CameraExampleHomeState extends State ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, children: [ TextButton( style: styleAuto, @@ -528,7 +522,6 @@ class _CameraExampleHomeState extends State return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, children: [ IconButton( icon: const Icon(Icons.camera_alt), diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 581780f0d87b..e7d6aeb51e24 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android description: Android implementation of the camera plugin. repository: https://github.com/flutter/plugins/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.0 +version: 0.10.0+1 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_android/test/android_camera_test.dart b/packages/camera/camera_android/test/android_camera_test.dart index ca1f245019a8..3e50e6918648 100644 --- a/packages/camera/camera_android/test/android_camera_test.dart +++ b/packages/camera/camera_android/test/android_camera_test.dart @@ -961,7 +961,6 @@ void main() { 'setZoomLevel': PlatformException( code: 'ZOOM_ERROR', message: 'Illegal zoom error', - details: null, ) }, ); diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index 9bab2ec375c1..2726309e497f 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.9.8+3 +* Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores missing return warnings in preparation for [upcoming analysis changes](https://github.com/flutter/flutter/issues/105750). ## 0.9.8+2 diff --git a/packages/camera/camera_avfoundation/example/lib/main.dart b/packages/camera/camera_avfoundation/example/lib/main.dart index 1dbdabb7d11c..9ebc27e4be5b 100644 --- a/packages/camera/camera_avfoundation/example/lib/main.dart +++ b/packages/camera/camera_avfoundation/example/lib/main.dart @@ -161,7 +161,6 @@ class _CameraExampleHomeState extends State Padding( padding: const EdgeInsets.all(5.0), child: Row( - mainAxisAlignment: MainAxisAlignment.start, children: [ _cameraTogglesRowWidget(), _thumbnailWidget(), @@ -274,7 +273,6 @@ class _CameraExampleHomeState extends State children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, children: [ IconButton( icon: const Icon(Icons.flash_on), @@ -328,7 +326,6 @@ class _CameraExampleHomeState extends State child: ClipRect( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, children: [ IconButton( icon: const Icon(Icons.flash_off), @@ -400,7 +397,6 @@ class _CameraExampleHomeState extends State ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, children: [ TextButton( style: styleAuto, @@ -439,7 +435,6 @@ class _CameraExampleHomeState extends State ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, children: [ Text(_minAvailableExposureOffset.toString()), Slider( @@ -490,7 +485,6 @@ class _CameraExampleHomeState extends State ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, children: [ TextButton( style: styleAuto, @@ -528,7 +522,6 @@ class _CameraExampleHomeState extends State return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, - mainAxisSize: MainAxisSize.max, children: [ IconButton( icon: const Icon(Icons.camera_alt), diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index adb0bf8dbafb..c2b9dcb46729 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_avfoundation description: iOS implementation of the camera plugin. repository: https://github.com/flutter/plugins/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.8+2 +version: 0.9.8+3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart index 67adcfab81f2..60109a4172b7 100644 --- a/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart +++ b/packages/camera/camera_avfoundation/test/avfoundation_camera_test.dart @@ -961,7 +961,6 @@ void main() { 'setZoomLevel': PlatformException( code: 'ZOOM_ERROR', message: 'Illegal zoom error', - details: null, ) }, ); diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index 9cc0b14dafbd..383fc73e8549 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). * Ignores missing return warnings in preparation for [upcoming analysis changes](https://github.com/flutter/flutter/issues/105750). diff --git a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart index d096f0012c86..60f42fd4af4a 100644 --- a/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart +++ b/packages/camera/camera_platform_interface/test/method_channel/method_channel_camera_test.dart @@ -954,7 +954,6 @@ void main() { 'setZoomLevel': PlatformException( code: 'ZOOM_ERROR', message: 'Illegal zoom error', - details: null, ) }, ); diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index a3522cfee52d..f867ae62c800 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). ## 0.3.0 diff --git a/packages/camera/camera_web/example/integration_test/camera_options_test.dart b/packages/camera/camera_web/example/integration_test/camera_options_test.dart index ee63d87e9f07..6619ff41e03c 100644 --- a/packages/camera/camera_web/example/integration_test/camera_options_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_options_test.dart @@ -30,7 +30,7 @@ void main() { testWidgets('supports value equality', (WidgetTester tester) async { expect( CameraOptions( - audio: const AudioConstraints(enabled: false), + audio: const AudioConstraints(), video: VideoConstraints( facingMode: FacingModeConstraint(CameraType.environment), width: @@ -42,7 +42,7 @@ void main() { ), equals( CameraOptions( - audio: const AudioConstraints(enabled: false), + audio: const AudioConstraints(), video: VideoConstraints( facingMode: FacingModeConstraint(CameraType.environment), width: const VideoSizeConstraint( diff --git a/packages/camera/camera_web/example/integration_test/camera_service_test.dart b/packages/camera/camera_web/example/integration_test/camera_service_test.dart index 96debbf0f491..27199320fc56 100644 --- a/packages/camera/camera_web/example/integration_test/camera_service_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_service_test.dart @@ -24,7 +24,7 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('CameraService', () { - const int cameraId = 0; + const int cameraId = 1; late Window window; late Navigator navigator; 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 7fe6266587ae..e3f11383469c 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 @@ -26,7 +26,7 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('CameraPlugin', () { - const int cameraId = 0; + const int cameraId = 1; late Window window; late Navigator navigator; @@ -593,7 +593,7 @@ void main() { (Camera camera) => camera.options, 'options', CameraOptions( - audio: const AudioConstraints(enabled: false), + audio: const AudioConstraints(), video: VideoConstraints( facingMode: FacingModeConstraint(CameraType.user), width: VideoSizeConstraint( diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index 16ce26a5d306..2b27fb433505 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.1+1 + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 0.2.1 * Adds a check for string size before Win32 MultiByte <-> WideChar conversions diff --git a/packages/camera/camera_windows/example/lib/main.dart b/packages/camera/camera_windows/example/lib/main.dart index 5758b0f1e397..310630493111 100644 --- a/packages/camera/camera_windows/example/lib/main.dart +++ b/packages/camera/camera_windows/example/lib/main.dart @@ -114,7 +114,6 @@ class _MyAppState extends State { await CameraPlatform.instance.initializeCamera( cameraId, - imageFormatGroup: ImageFormatGroup.unknown, ); final CameraInitializedEvent event = await initialized; @@ -429,7 +428,6 @@ class _MyAppState extends State { vertical: 10, ), child: Align( - alignment: Alignment.center, child: Container( constraints: const BoxConstraints( maxHeight: 500, diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index 540a445ceebc..513ca2d61102 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_windows description: A Flutter plugin for getting information about and controlling the camera on Windows. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.2.1 +version: 0.2.1+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md index 8a8a53afe661..c4a6ca2f770e 100644 --- a/packages/file_selector/file_selector_web/CHANGELOG.md +++ b/packages/file_selector/file_selector_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 0.9.0 * **BREAKING CHANGE**: Methods that take `XTypeGroup`s now throw an 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 43c88a2a4241..d3d058122aa6 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 @@ -21,8 +21,7 @@ void main() { final MockDomHelper mockDomHelper = MockDomHelper( files: [mockFile], - expectAccept: '.jpg,.jpeg,image/png,image/*', - expectMultiple: false); + expectAccept: '.jpg,.jpeg,image/png,image/*'); final FileSelectorWeb plugin = FileSelectorWeb(domHelper: mockDomHelper); diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 86cefdd2f594..91eb91bcd0ff 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Fixes avoid_redundant_argument_values lint warnings and minor typos. * Moves Android and iOS implementations to federated packages. ## 2.1.10 diff --git a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart index 1d7494c5fe68..7db14fc1c6de 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart @@ -48,7 +48,6 @@ void main() { child: GoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - compassEnabled: true, onMapCreated: (GoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -86,7 +85,6 @@ void main() { child: GoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - mapToolbarEnabled: true, onMapCreated: (GoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -204,7 +202,6 @@ void main() { child: GoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - zoomGesturesEnabled: true, onMapCreated: (GoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -266,7 +263,6 @@ void main() { child: GoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - liteModeEnabled: false, onMapCreated: (GoogleMapController controller) { mapIdCompleter.complete(controller.mapId); }, @@ -323,7 +319,6 @@ void main() { child: GoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - rotateGesturesEnabled: true, onMapCreated: (GoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -363,7 +358,6 @@ void main() { child: GoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - tiltGesturesEnabled: true, onMapCreated: (GoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -402,7 +396,6 @@ void main() { child: GoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - scrollGesturesEnabled: true, onMapCreated: (GoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -560,7 +553,6 @@ void main() { child: GoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - trafficEnabled: false, onMapCreated: (GoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -580,7 +572,6 @@ void main() { child: GoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - buildingsEnabled: true, onMapCreated: (GoogleMapController controller) { mapIdCompleter.complete(controller.mapId); }, @@ -605,8 +596,6 @@ void main() { child: GoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - myLocationButtonEnabled: true, - myLocationEnabled: false, onMapCreated: (GoogleMapController controller) { mapIdCompleter.complete(controller.mapId); }, @@ -626,7 +615,6 @@ void main() { key: key, initialCameraPosition: _kInitialCameraPosition, myLocationButtonEnabled: false, - myLocationEnabled: false, onMapCreated: (GoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -649,7 +637,6 @@ void main() { key: key, initialCameraPosition: _kInitialCameraPosition, myLocationButtonEnabled: false, - myLocationEnabled: false, onMapCreated: (GoogleMapController controller) { mapIdCompleter.complete(controller.mapId); }, @@ -674,8 +661,6 @@ void main() { child: GoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - myLocationButtonEnabled: true, - myLocationEnabled: false, onMapCreated: (GoogleMapController controller) { mapIdCompleter.complete(controller.mapId); }, @@ -976,9 +961,7 @@ void main() { tileOverlayId: const TileOverlayId('tile_overlay_1'), tileProvider: _DebugTileProvider(), zIndex: 2, - visible: true, transparency: 0.2, - fadeIn: true, ); final TileOverlay tileOverlay2 = TileOverlay( @@ -1035,18 +1018,14 @@ void main() { tileOverlayId: const TileOverlayId('tile_overlay_1'), tileProvider: _DebugTileProvider(), zIndex: 2, - visible: true, transparency: 0.2, - fadeIn: true, ); final TileOverlay tileOverlay2 = TileOverlay( tileOverlayId: const TileOverlayId('tile_overlay_2'), tileProvider: _DebugTileProvider(), zIndex: 3, - visible: true, transparency: 0.5, - fadeIn: true, ); await tester.pumpWidget( Directionality( @@ -1115,9 +1094,7 @@ void main() { tileOverlayId: const TileOverlayId('tile_overlay_1'), tileProvider: _DebugTileProvider(), zIndex: 2, - visible: true, transparency: 0.2, - fadeIn: true, ); await tester.pumpWidget( @@ -1189,7 +1166,6 @@ class _DebugTileProvider implements TileProvider { textDirection: TextDirection.ltr, ); textPainter.layout( - minWidth: 0.0, maxWidth: width.toDouble(), ); const Offset offset = Offset(0, 0); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart index 9c96f25d5fa7..ed25d475ebd6 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/map_click.dart @@ -90,7 +90,6 @@ class _MapClickBodyState extends State<_MapClickBody> { ))); } return Column( - mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: columnChildren, ); 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 fbfeda56a968..3f56f40799af 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 @@ -336,7 +336,6 @@ class MapUiBodyState extends State { ); } return Column( - mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: columnChildren, ); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart index a4bfa88d559f..07359e90294f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart @@ -68,7 +68,6 @@ class MarkerIconsBodyState extends State { columnChildren.addAll([_paddingInput(), _buttons()]); return Column( - mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: columnChildren, ); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart index b8efc4e52562..fa49917e715f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/place_marker.dart @@ -402,7 +402,6 @@ class PlaceMarkerBodyState extends State { padding: const EdgeInsets.only(left: 12, right: 12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, - mainAxisSize: MainAxisSize.max, children: [ if (markerPosition == null) Container() diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart index d88e09988dc7..e73ca25a56b6 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart @@ -137,7 +137,6 @@ class _DebugTileProvider implements TileProvider { textDirection: TextDirection.ltr, ); textPainter.layout( - minWidth: 0.0, maxWidth: width.toDouble(), ); const Offset offset = Offset(0, 0); diff --git a/packages/google_maps_flutter/google_maps_flutter/test/fake_maps_controllers.dart b/packages/google_maps_flutter/google_maps_flutter/test/fake_maps_controllers.dart index bac3ceabc4de..9fe6923204ab 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/fake_maps_controllers.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/fake_maps_controllers.dart @@ -12,8 +12,7 @@ class FakePlatformGoogleMap { FakePlatformGoogleMap(int id, Map params) : cameraPosition = CameraPosition.fromMap(params['initialCameraPosition']), - channel = MethodChannel( - 'plugins.flutter.io/google_maps_$id', const StandardMethodCodec()) { + channel = MethodChannel('plugins.flutter.io/google_maps_$id') { channel.setMockMethodCallHandler(onMethodCall); updateOptions(params['options'] as Map); updateMarkers(params); 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 2b754afbd359..0fc5e7723df5 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 @@ -89,7 +89,6 @@ void main() { textDirection: TextDirection.ltr, child: GoogleMap( initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)), - compassEnabled: true, ), ), ); @@ -118,7 +117,6 @@ void main() { textDirection: TextDirection.ltr, child: GoogleMap( initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)), - mapToolbarEnabled: true, ), ), ); @@ -232,7 +230,6 @@ void main() { textDirection: TextDirection.ltr, child: GoogleMap( initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)), - minMaxZoomPreference: MinMaxZoomPreference.unbounded, ), ), ); @@ -262,7 +259,6 @@ void main() { textDirection: TextDirection.ltr, child: GoogleMap( initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)), - rotateGesturesEnabled: true, ), ), ); @@ -291,7 +287,6 @@ void main() { textDirection: TextDirection.ltr, child: GoogleMap( initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)), - scrollGesturesEnabled: true, ), ), ); @@ -320,7 +315,6 @@ void main() { textDirection: TextDirection.ltr, child: GoogleMap( initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)), - tiltGesturesEnabled: true, ), ), ); @@ -378,7 +372,6 @@ void main() { textDirection: TextDirection.ltr, child: GoogleMap( initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)), - zoomGesturesEnabled: true, ), ), ); @@ -407,7 +400,6 @@ void main() { textDirection: TextDirection.ltr, child: GoogleMap( initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)), - zoomControlsEnabled: true, ), ), ); @@ -421,7 +413,6 @@ void main() { textDirection: TextDirection.ltr, child: GoogleMap( initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)), - myLocationEnabled: false, ), ), ); @@ -451,7 +442,6 @@ void main() { textDirection: TextDirection.ltr, child: GoogleMap( initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)), - myLocationEnabled: false, ), ), ); @@ -536,7 +526,6 @@ void main() { textDirection: TextDirection.ltr, child: GoogleMap( initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)), - trafficEnabled: false, ), ), ); @@ -580,7 +569,6 @@ void main() { textDirection: TextDirection.ltr, child: GoogleMap( initialCameraPosition: CameraPosition(target: LatLng(10.0, 15.0)), - buildingsEnabled: true, ), ), ); diff --git a/packages/google_maps_flutter/google_maps_flutter/test/polyline_updates_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/polyline_updates_test.dart index 9cbba3a510ca..a2dad92f8eb4 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/polyline_updates_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/polyline_updates_test.dart @@ -200,8 +200,7 @@ void main() { }); testWidgets('Update non platform related attr', (WidgetTester tester) async { - Polyline p1 = - const Polyline(polylineId: PolylineId('polyline_1'), onTap: null); + Polyline p1 = const Polyline(polylineId: PolylineId('polyline_1')); final Set prev = {p1}; p1 = Polyline( polylineId: const PolylineId('polyline_1'), onTap: () => print(2 + 2)); 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 bd2f99c9446c..1ce9082b2389 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 2.1.10 * Splits Android implementation out of `google_maps_flutter` as a federated diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart index 00c51eb30b5a..ce79a06780cd 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart @@ -48,7 +48,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - compassEnabled: true, onMapCreated: (ExampleGoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -86,7 +85,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - mapToolbarEnabled: true, onMapCreated: (ExampleGoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -182,7 +180,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - zoomGesturesEnabled: true, onMapCreated: (ExampleGoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -240,7 +237,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - liteModeEnabled: false, onMapCreated: (ExampleGoogleMapController controller) { mapIdCompleter.complete(controller.mapId); }, @@ -297,7 +293,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - rotateGesturesEnabled: true, onMapCreated: (ExampleGoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -337,7 +332,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - tiltGesturesEnabled: true, onMapCreated: (ExampleGoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -376,7 +370,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - scrollGesturesEnabled: true, onMapCreated: (ExampleGoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -528,7 +521,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - trafficEnabled: false, onMapCreated: (ExampleGoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -548,7 +540,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - buildingsEnabled: true, onMapCreated: (ExampleGoogleMapController controller) { mapIdCompleter.complete(controller.mapId); }, @@ -572,8 +563,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - myLocationButtonEnabled: true, - myLocationEnabled: false, onMapCreated: (ExampleGoogleMapController controller) { mapIdCompleter.complete(controller.mapId); }, @@ -593,7 +582,6 @@ void main() { key: key, initialCameraPosition: _kInitialCameraPosition, myLocationButtonEnabled: false, - myLocationEnabled: false, onMapCreated: (ExampleGoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -618,7 +606,6 @@ void main() { key: key, initialCameraPosition: _kInitialCameraPosition, myLocationButtonEnabled: false, - myLocationEnabled: false, onMapCreated: (ExampleGoogleMapController controller) { mapIdCompleter.complete(controller.mapId); }, @@ -645,8 +632,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - myLocationButtonEnabled: true, - myLocationEnabled: false, onMapCreated: (ExampleGoogleMapController controller) { mapIdCompleter.complete(controller.mapId); }, @@ -958,9 +943,7 @@ void main() { tileOverlayId: const TileOverlayId('tile_overlay_1'), tileProvider: _DebugTileProvider(), zIndex: 2, - visible: true, transparency: 0.2, - fadeIn: true, ); final TileOverlay tileOverlay2 = TileOverlay( @@ -1017,18 +1000,14 @@ void main() { tileOverlayId: const TileOverlayId('tile_overlay_1'), tileProvider: _DebugTileProvider(), zIndex: 2, - visible: true, transparency: 0.2, - fadeIn: true, ); final TileOverlay tileOverlay2 = TileOverlay( tileOverlayId: const TileOverlayId('tile_overlay_2'), tileProvider: _DebugTileProvider(), zIndex: 3, - visible: true, transparency: 0.5, - fadeIn: true, ); await tester.pumpWidget( Directionality( @@ -1097,9 +1076,7 @@ void main() { tileOverlayId: const TileOverlayId('tile_overlay_1'), tileProvider: _DebugTileProvider(), zIndex: 2, - visible: true, transparency: 0.2, - fadeIn: true, ); await tester.pumpWidget( @@ -1171,7 +1148,6 @@ class _DebugTileProvider implements TileProvider { textDirection: TextDirection.ltr, ); textPainter.layout( - minWidth: 0.0, maxWidth: width.toDouble(), ); const Offset offset = Offset(0, 0); diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_click.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_click.dart index 20718d9751ab..4017a9fccce2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_click.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/map_click.dart @@ -92,7 +92,6 @@ class _MapClickBodyState extends State<_MapClickBody> { ))); } return Column( - mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: columnChildren, ); 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 6c38e14d86a5..009ee71d8400 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 @@ -337,7 +337,6 @@ class MapUiBodyState extends State { ); } return Column( - mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: columnChildren, ); diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/padding.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/padding.dart index d346549b9d40..67e72442fc0c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/padding.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/padding.dart @@ -70,7 +70,6 @@ class MarkerIconsBodyState extends State { columnChildren.addAll([_paddingInput(), _buttons()]); return Column( - mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: columnChildren, ); diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_marker.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_marker.dart index b7d2a690c3e8..7d12f4c81684 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/place_marker.dart @@ -403,7 +403,6 @@ class PlaceMarkerBodyState extends State { padding: const EdgeInsets.only(left: 12, right: 12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, - mainAxisSize: MainAxisSize.max, children: [ if (markerPosition == null) Container() diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/tile_overlay.dart index 555f037b474a..c912870d6583 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/tile_overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/tile_overlay.dart @@ -138,7 +138,6 @@ class _DebugTileProvider implements TileProvider { textDirection: TextDirection.ltr, ); textPainter.layout( - minWidth: 0.0, maxWidth: width.toDouble(), ); const Offset offset = Offset(0, 0); 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 33d96e963446..935cfd4527ea 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 @@ +## NEXT + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 2.1.11 * Precaches Google Maps services initialization and syncing. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/integration_test/google_maps_test.dart index a2299e6989d4..c9ad0cec2c98 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/integration_test/google_maps_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/integration_test/google_maps_test.dart @@ -47,7 +47,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - compassEnabled: true, onMapCreated: (ExampleGoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -67,7 +66,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - mapToolbarEnabled: true, onMapCreated: (ExampleGoogleMapController controller) { mapIdCompleter.complete(controller.mapId); }, @@ -156,7 +154,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - zoomGesturesEnabled: true, onMapCreated: (ExampleGoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -220,7 +217,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - rotateGesturesEnabled: true, onMapCreated: (ExampleGoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -260,7 +256,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - tiltGesturesEnabled: true, onMapCreated: (ExampleGoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -299,7 +294,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - scrollGesturesEnabled: true, onMapCreated: (ExampleGoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -443,7 +437,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - trafficEnabled: false, onMapCreated: (ExampleGoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -463,7 +456,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - buildingsEnabled: true, onMapCreated: (ExampleGoogleMapController controller) { mapIdCompleter.complete(controller.mapId); }, @@ -487,8 +479,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - myLocationButtonEnabled: true, - myLocationEnabled: false, onMapCreated: (ExampleGoogleMapController controller) { mapIdCompleter.complete(controller.mapId); }, @@ -508,7 +498,6 @@ void main() { key: key, initialCameraPosition: _kInitialCameraPosition, myLocationButtonEnabled: false, - myLocationEnabled: false, onMapCreated: (ExampleGoogleMapController controller) { fail('OnMapCreated should get called only once.'); }, @@ -531,7 +520,6 @@ void main() { key: key, initialCameraPosition: _kInitialCameraPosition, myLocationButtonEnabled: false, - myLocationEnabled: false, onMapCreated: (ExampleGoogleMapController controller) { mapIdCompleter.complete(controller.mapId); }, @@ -556,8 +544,6 @@ void main() { child: ExampleGoogleMap( key: key, initialCameraPosition: _kInitialCameraPosition, - myLocationButtonEnabled: true, - myLocationEnabled: false, onMapCreated: (ExampleGoogleMapController controller) { mapIdCompleter.complete(controller.mapId); }, @@ -864,9 +850,7 @@ void main() { tileOverlayId: const TileOverlayId('tile_overlay_1'), tileProvider: _DebugTileProvider(), zIndex: 2, - visible: true, transparency: 0.2, - fadeIn: true, ); final TileOverlay tileOverlay2 = TileOverlay( @@ -923,18 +907,14 @@ void main() { tileOverlayId: const TileOverlayId('tile_overlay_1'), tileProvider: _DebugTileProvider(), zIndex: 2, - visible: true, transparency: 0.2, - fadeIn: true, ); final TileOverlay tileOverlay2 = TileOverlay( tileOverlayId: const TileOverlayId('tile_overlay_2'), tileProvider: _DebugTileProvider(), zIndex: 3, - visible: true, transparency: 0.5, - fadeIn: true, ); await tester.pumpWidget( Directionality( @@ -1003,9 +983,7 @@ void main() { tileOverlayId: const TileOverlayId('tile_overlay_1'), tileProvider: _DebugTileProvider(), zIndex: 2, - visible: true, transparency: 0.2, - fadeIn: true, ); await tester.pumpWidget( @@ -1077,7 +1055,6 @@ class _DebugTileProvider implements TileProvider { textDirection: TextDirection.ltr, ); textPainter.layout( - minWidth: 0.0, maxWidth: width.toDouble(), ); const Offset offset = Offset(0, 0); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_click.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_click.dart index 20718d9751ab..4017a9fccce2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_click.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_click.dart @@ -92,7 +92,6 @@ class _MapClickBodyState extends State<_MapClickBody> { ))); } return Column( - mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: columnChildren, ); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_ui.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_ui.dart index 6c38e14d86a5..009ee71d8400 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_ui.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/map_ui.dart @@ -337,7 +337,6 @@ class MapUiBodyState extends State { ); } return Column( - mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: columnChildren, ); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/padding.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/padding.dart index d346549b9d40..67e72442fc0c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/padding.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/padding.dart @@ -70,7 +70,6 @@ class MarkerIconsBodyState extends State { columnChildren.addAll([_paddingInput(), _buttons()]); return Column( - mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: columnChildren, ); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_marker.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_marker.dart index b7d2a690c3e8..7d12f4c81684 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_marker.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/place_marker.dart @@ -403,7 +403,6 @@ class PlaceMarkerBodyState extends State { padding: const EdgeInsets.only(left: 12, right: 12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, - mainAxisSize: MainAxisSize.max, children: [ if (markerPosition == null) Container() diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/tile_overlay.dart index 555f037b474a..c912870d6583 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/tile_overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/tile_overlay.dart @@ -138,7 +138,6 @@ class _DebugTileProvider implements TileProvider { textDirection: TextDirection.ltr, ); textPainter.layout( - minWidth: 0.0, maxWidth: width.toDouble(), ); const Offset offset = Offset(0, 0); 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 e8da5f3aba99..695a1b89ee42 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 @@ +## NEXT + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 2.2.1 * Adds a new interface for inspecting the platform map state in tests. diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_test.dart index 1a9a9d480f1a..fe5d86335af3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/tile_overlay_test.dart @@ -35,7 +35,6 @@ void main() { const TileOverlay tileOverlay = TileOverlay( tileOverlayId: TileOverlayId('id'), fadeIn: false, - tileProvider: null, transparency: 0.1, zIndex: 1, visible: false, @@ -91,7 +90,6 @@ void main() { const TileOverlay tileOverlayDifferentProvider = TileOverlay( tileOverlayId: TileOverlayId('id1'), fadeIn: false, - tileProvider: null, transparency: 0.1, zIndex: 1, visible: false, 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 d86642934b77..d766e0d57968 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 @@ +## NEXT + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 0.4.0+1 * Updates `README.md` to describe a hit-testing issue when Flutter widgets are overlaid on top of the Map widget. 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 b44853aacb54..4294f8715524 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 @@ -450,8 +450,6 @@ void main() { initialCameraPosition: const CameraPosition( target: LatLng(43.308, -5.6910), zoom: 12, - bearing: 0, - tilt: 0, ), ); 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 14e4156b87ec..a595a94655de 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 @@ -211,13 +211,13 @@ void main() { Future pumpCenteredMap( WidgetTester tester, { required CameraPosition initialCamera, - Size size = const Size(320, 240), + Size? size, void Function(GoogleMapController)? onMapCreated, }) async { await tester.pumpWidget( CenteredMap( initialCamera: initialCamera, - size: size, + size: size ?? const Size(320, 240), onMapCreated: onMapCreated, ), ); diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 7e433a71368f..d63f0a160961 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.4.1 + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 5.4.0 * Adds support for configuring `serverClientId` through `GoogleSignIn` constructor. diff --git a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart index a1f8f7bc49ca..135c422adfb5 100644 --- a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart +++ b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart @@ -192,10 +192,7 @@ class GoogleSignIn { List scopes = const [], String? hostedDomain, }) { - return GoogleSignIn( - signInOption: SignInOption.standard, - scopes: scopes, - hostedDomain: hostedDomain); + return GoogleSignIn(scopes: scopes, hostedDomain: hostedDomain); } /// Factory for creating sign in suitable for games. This option is only diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index c7724adcebad..8286f6bd9b0e 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/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 and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.4.0 +version: 5.4.1 environment: diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 36a47b1a3d42..ececf68e883f 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 0.8.5+3 * Adds argument error assertions to the app-facing package, to ensure diff --git a/packages/image_picker/image_picker/test/image_picker_deprecated_test.dart b/packages/image_picker/image_picker/test/image_picker_deprecated_test.dart index b3db08020d7e..9ca4d9c977e8 100644 --- a/packages/image_picker/image_picker/test/image_picker_deprecated_test.dart +++ b/packages/image_picker/image_picker/test/image_picker_deprecated_test.dart @@ -79,41 +79,30 @@ void main() { imageQuality: 70); verifyInOrder([ + mockPlatform.pickImage(source: ImageSource.camera), + mockPlatform.pickImage(source: ImageSource.camera, maxWidth: 10.0), + mockPlatform.pickImage(source: ImageSource.camera, maxHeight: 10.0), mockPlatform.pickImage( - source: ImageSource.camera, - maxWidth: null, - maxHeight: null, - imageQuality: null), - mockPlatform.pickImage( - source: ImageSource.camera, - maxWidth: 10.0, - maxHeight: null, - imageQuality: null), - mockPlatform.pickImage( - source: ImageSource.camera, - maxWidth: null, - maxHeight: 10.0, - imageQuality: null), - mockPlatform.pickImage( - source: ImageSource.camera, - maxWidth: 10.0, - maxHeight: 20.0, - imageQuality: null), + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + ), mockPlatform.pickImage( - source: ImageSource.camera, - maxWidth: 10.0, - maxHeight: null, - imageQuality: 70), + source: ImageSource.camera, + maxWidth: 10.0, + imageQuality: 70, + ), mockPlatform.pickImage( - source: ImageSource.camera, - maxWidth: null, - maxHeight: 10.0, - imageQuality: 70), + source: ImageSource.camera, + maxHeight: 10.0, + imageQuality: 70, + ), mockPlatform.pickImage( - source: ImageSource.camera, - maxWidth: 10.0, - maxHeight: 20.0, - imageQuality: 70), + source: ImageSource.camera, + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ), ]); }); @@ -128,9 +117,7 @@ void main() { final ImagePicker picker = ImagePicker(); await picker.getImage(source: ImageSource.camera); - verify(mockPlatform.pickImage( - source: ImageSource.camera, - preferredCameraDevice: CameraDevice.rear)); + verify(mockPlatform.pickImage(source: ImageSource.camera)); }); test('camera position can set to front', () async { @@ -173,14 +160,11 @@ void main() { maxDuration: const Duration(seconds: 10)); verifyInOrder([ + mockPlatform.pickVideo(source: ImageSource.camera), mockPlatform.pickVideo( - source: ImageSource.camera, - preferredCameraDevice: CameraDevice.rear, - maxDuration: null), - mockPlatform.pickVideo( - source: ImageSource.camera, - preferredCameraDevice: CameraDevice.rear, - maxDuration: const Duration(seconds: 10)), + source: ImageSource.camera, + maxDuration: const Duration(seconds: 10), + ), ]); }); @@ -195,9 +179,7 @@ void main() { final ImagePicker picker = ImagePicker(); await picker.getVideo(source: ImageSource.camera); - verify(mockPlatform.pickVideo( - source: ImageSource.camera, - preferredCameraDevice: CameraDevice.rear)); + verify(mockPlatform.pickVideo(source: ImageSource.camera)); }); test('camera position can set to front', () async { @@ -277,20 +259,17 @@ void main() { maxWidth: 10.0, maxHeight: 20.0, imageQuality: 70); verifyInOrder([ + mockPlatform.pickMultiImage(), + mockPlatform.pickMultiImage(maxWidth: 10.0), + mockPlatform.pickMultiImage(maxHeight: 10.0), + mockPlatform.pickMultiImage(maxWidth: 10.0, maxHeight: 20.0), + mockPlatform.pickMultiImage(maxWidth: 10.0, imageQuality: 70), + mockPlatform.pickMultiImage(maxHeight: 10.0, imageQuality: 70), mockPlatform.pickMultiImage( - maxWidth: null, maxHeight: null, imageQuality: null), - mockPlatform.pickMultiImage( - maxWidth: 10.0, maxHeight: null, imageQuality: null), - mockPlatform.pickMultiImage( - maxWidth: null, maxHeight: 10.0, imageQuality: null), - mockPlatform.pickMultiImage( - maxWidth: 10.0, maxHeight: 20.0, imageQuality: null), - mockPlatform.pickMultiImage( - maxWidth: 10.0, maxHeight: null, imageQuality: 70), - mockPlatform.pickMultiImage( - maxWidth: null, maxHeight: 10.0, imageQuality: 70), - mockPlatform.pickMultiImage( - maxWidth: 10.0, maxHeight: 20.0, imageQuality: 70), + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ), ]); }); diff --git a/packages/image_picker/image_picker/test/image_picker_test.dart b/packages/image_picker/image_picker/test/image_picker_test.dart index f981195fe1b3..10b5ac16e570 100644 --- a/packages/image_picker/image_picker/test/image_picker_test.dart +++ b/packages/image_picker/image_picker/test/image_picker_test.dart @@ -76,36 +76,15 @@ void main() { imageQuality: 70); verifyInOrder([ + mockPlatform.getImage(source: ImageSource.camera), + mockPlatform.getImage(source: ImageSource.camera, maxWidth: 10.0), + mockPlatform.getImage(source: ImageSource.camera, maxHeight: 10.0), mockPlatform.getImage( - source: ImageSource.camera, - maxWidth: null, - maxHeight: null, - imageQuality: null), + source: ImageSource.camera, maxWidth: 10.0, maxHeight: 20.0), mockPlatform.getImage( - source: ImageSource.camera, - maxWidth: 10.0, - maxHeight: null, - imageQuality: null), + source: ImageSource.camera, maxWidth: 10.0, imageQuality: 70), mockPlatform.getImage( - source: ImageSource.camera, - maxWidth: null, - maxHeight: 10.0, - imageQuality: null), - mockPlatform.getImage( - source: ImageSource.camera, - maxWidth: 10.0, - maxHeight: 20.0, - imageQuality: null), - mockPlatform.getImage( - source: ImageSource.camera, - maxWidth: 10.0, - maxHeight: null, - imageQuality: 70), - mockPlatform.getImage( - source: ImageSource.camera, - maxWidth: null, - maxHeight: 10.0, - imageQuality: 70), + source: ImageSource.camera, maxHeight: 10.0, imageQuality: 70), mockPlatform.getImage( source: ImageSource.camera, maxWidth: 10.0, @@ -138,9 +117,7 @@ void main() { final ImagePicker picker = ImagePicker(); await picker.pickImage(source: ImageSource.camera); - verify(mockPlatform.getImage( - source: ImageSource.camera, - preferredCameraDevice: CameraDevice.rear)); + verify(mockPlatform.getImage(source: ImageSource.camera)); }); test('camera position can set to front', () async { @@ -183,13 +160,9 @@ void main() { maxDuration: const Duration(seconds: 10)); verifyInOrder([ + mockPlatform.getVideo(source: ImageSource.camera), mockPlatform.getVideo( source: ImageSource.camera, - preferredCameraDevice: CameraDevice.rear, - maxDuration: null), - mockPlatform.getVideo( - source: ImageSource.camera, - preferredCameraDevice: CameraDevice.rear, maxDuration: const Duration(seconds: 10)), ]); }); @@ -205,9 +178,7 @@ void main() { final ImagePicker picker = ImagePicker(); await picker.pickVideo(source: ImageSource.camera); - verify(mockPlatform.getVideo( - source: ImageSource.camera, - preferredCameraDevice: CameraDevice.rear)); + verify(mockPlatform.getVideo(source: ImageSource.camera)); }); test('camera position can set to front', () async { @@ -312,18 +283,12 @@ void main() { maxWidth: 10.0, maxHeight: 20.0, imageQuality: 70); verifyInOrder([ - mockPlatform.getMultiImage( - maxWidth: null, maxHeight: null, imageQuality: null), - mockPlatform.getMultiImage( - maxWidth: 10.0, maxHeight: null, imageQuality: null), - mockPlatform.getMultiImage( - maxWidth: null, maxHeight: 10.0, imageQuality: null), - mockPlatform.getMultiImage( - maxWidth: 10.0, maxHeight: 20.0, imageQuality: null), - mockPlatform.getMultiImage( - maxWidth: 10.0, maxHeight: null, imageQuality: 70), - mockPlatform.getMultiImage( - maxWidth: null, maxHeight: 10.0, imageQuality: 70), + mockPlatform.getMultiImage(), + mockPlatform.getMultiImage(maxWidth: 10.0), + mockPlatform.getMultiImage(maxHeight: 10.0), + mockPlatform.getMultiImage(maxWidth: 10.0, maxHeight: 20.0), + mockPlatform.getMultiImage(maxWidth: 10.0, imageQuality: 70), + mockPlatform.getMultiImage(maxHeight: 10.0, imageQuality: 70), mockPlatform.getMultiImage( maxWidth: 10.0, maxHeight: 20.0, imageQuality: 70), ]); diff --git a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md index fda959a21579..ae4a0a371d6d 100644 --- a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md +++ b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 2.6.1 * Exports new types added for `getMultiImageWithOptions` in 2.6.0. diff --git a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart index 1ddddee079b7..44980100742a 100644 --- a/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart +++ b/packages/image_picker/image_picker_platform_interface/test/new_method_channel_image_picker_test.dart @@ -1440,9 +1440,7 @@ void main() { test('Request full metadata argument defaults to true', () async { returnValue = ['0', '1']; - await picker.getMultiImageWithOptions( - options: const MultiImagePickerOptions(), - ); + await picker.getMultiImageWithOptions(); expect( log, diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 08ba6dd206c4..1d4407ea95a2 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.0.7 + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 3.0.6 * Ignores deprecation warnings for upcoming styleFrom button API changes. diff --git a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart index 017b72e976d5..98a24d1de963 100644 --- a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart @@ -18,7 +18,9 @@ void main() { runApp(_MyApp()); } -const bool _kAutoConsume = true; +// Auto-consume must be true on iOS. +// To try without auto-consume on another platform, change `true` to `false` here. +final bool _kAutoConsume = Platform.isIOS || true; const String _kConsumableId = 'consumable'; const String _kUpgradeId = 'upgrade'; @@ -277,7 +279,6 @@ class _MyAppState extends State<_MyApp> { purchaseParam = GooglePlayPurchaseParam( productDetails: productDetails, - applicationUserName: null, changeSubscriptionParam: (oldSubscription != null) ? ChangeSubscriptionParam( oldPurchaseDetails: oldSubscription, @@ -288,14 +289,13 @@ class _MyAppState extends State<_MyApp> { } else { purchaseParam = PurchaseParam( productDetails: productDetails, - applicationUserName: null, ); } if (productDetails.id == _kConsumableId) { _inAppPurchase.buyConsumable( purchaseParam: purchaseParam, - autoConsume: _kAutoConsume || Platform.isIOS); + autoConsume: _kAutoConsume); } else { _inAppPurchase.buyNonConsumable( purchaseParam: purchaseParam); @@ -358,7 +358,6 @@ class _MyAppState extends State<_MyApp> { return Padding( padding: const EdgeInsets.all(4.0), child: Row( - mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index 8fb69206a95d..887910e1cd99 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 3.0.6 +version: 3.0.7 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase/test/in_app_purchase_test.dart b/packages/in_app_purchase/in_app_purchase/test/in_app_purchase_test.dart index 644d26ed50ad..58f7398add16 100644 --- a/packages/in_app_purchase/in_app_purchase/test/in_app_purchase_test.dart +++ b/packages/in_app_purchase/in_app_purchase/test/in_app_purchase_test.dart @@ -184,12 +184,12 @@ class MockInAppPurchasePlatform extends Fake @override Future completePurchase(PurchaseDetails purchase) { log.add(const MethodCall('completePurchase')); - return Future.value(null); + return Future.value(); } @override Future restorePurchases({String? applicationUserName}) { log.add(const MethodCall('restorePurchases')); - return Future.value(null); + return Future.value(); } } 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 c369b8f4d199..daa487497e25 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.3+3 + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 0.2.3+2 * Fixes incorrect json key in `queryPurchasesAsync` that fixes restore purchases functionality. 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 337811a9acfd..6c0d967196d0 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 @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:io'; import 'package:flutter/material.dart'; import 'package:in_app_purchase_android/billing_client_wrappers.dart'; @@ -22,6 +21,7 @@ void main() { runApp(_MyApp()); } +// To try without auto-consume, change `true` to `false` here. const bool _kAutoConsume = true; const String _kConsumableId = 'consumable'; @@ -282,7 +282,6 @@ class _MyAppState extends State<_MyApp> { final GooglePlayPurchaseParam purchaseParam = GooglePlayPurchaseParam( productDetails: productDetails, - applicationUserName: null, changeSubscriptionParam: oldSubscription != null ? ChangeSubscriptionParam( oldPurchaseDetails: oldSubscription, @@ -292,7 +291,8 @@ class _MyAppState extends State<_MyApp> { if (productDetails.id == _kConsumableId) { _inAppPurchasePlatform.buyConsumable( purchaseParam: purchaseParam, - autoConsume: _kAutoConsume || Platform.isIOS); + // ignore: avoid_redundant_argument_values + autoConsume: _kAutoConsume); } else { _inAppPurchasePlatform.buyNonConsumable( purchaseParam: purchaseParam); 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 70343fcfff0b..4c64a99e7a22 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 @@ -124,7 +124,7 @@ class BillingClient { /// /// This triggers the destruction of the `BillingClient` instance in Java. Future endConnection() async { - return channel.invokeMethod('BillingClient#endConnection()', null); + return channel.invokeMethod('BillingClient#endConnection()'); } /// Returns a list of [SkuDetailsWrapper]s that have [SkuDetailsWrapper.sku] 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 3b334a6848ed..8116de3c283b 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/plugins/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.2.3+2 +version: 0.2.3+3 environment: sdk: ">=2.14.0 <3.0.0" 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 1e30ce41beda..e803e81f7803 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 @@ -95,7 +95,6 @@ void main() { test('handles method channel returning null', () async { stubPlatform.addResponse( name: methodName, - value: null, ); expect( @@ -110,7 +109,7 @@ void main() { test('endConnection', () async { const String endConnectionName = 'BillingClient#endConnection()'; expect(stubPlatform.countPreviousCalls(endConnectionName), equals(0)); - stubPlatform.addResponse(name: endConnectionName, value: null); + stubPlatform.addResponse(name: endConnectionName); await billingClient.endConnection(); expect(stubPlatform.countPreviousCalls(endConnectionName), equals(1)); }); @@ -162,7 +161,7 @@ void main() { }); test('handles null method channel response', () async { - stubPlatform.addResponse(name: queryMethodName, value: null); + stubPlatform.addResponse(name: queryMethodName); final SkuDetailsResponseWrapper response = await billingClient .querySkuDetails( @@ -227,8 +226,7 @@ void main() { sku: skuDetails.sku, accountId: accountId, obfuscatedProfileId: profileId, - oldSku: dummyOldPurchase.sku, - purchaseToken: null), + oldSku: dummyOldPurchase.sku), throwsAssertionError); expect( @@ -236,7 +234,6 @@ void main() { sku: skuDetails.sku, accountId: accountId, obfuscatedProfileId: profileId, - oldSku: null, purchaseToken: dummyOldPurchase.purchaseToken), throwsAssertionError); }); @@ -337,7 +334,6 @@ void main() { test('handles method channel returning null', () async { stubPlatform.addResponse( name: launchMethodName, - value: null, ); const SkuDetailsWrapper skuDetails = dummySkuDetails; expect( @@ -400,7 +396,6 @@ void main() { test('handles method channel returning null', () async { stubPlatform.addResponse( name: queryPurchasesMethodName, - value: null, ); final PurchasesResultWrapper response = await billingClient.queryPurchases(SkuType.inapp); @@ -466,7 +461,6 @@ void main() { test('handles method channel returning null', () async { stubPlatform.addResponse( name: queryPurchaseHistoryMethodName, - value: null, ); final PurchasesHistoryResult response = await billingClient.queryPurchaseHistory(SkuType.inapp); @@ -501,7 +495,6 @@ void main() { test('handles method channel returning null', () async { stubPlatform.addResponse( name: consumeMethodName, - value: null, ); final BillingResultWrapper billingResult = await billingClient.consumeAsync('dummy token'); @@ -534,7 +527,6 @@ void main() { test('handles method channel returning null', () async { stubPlatform.addResponse( name: acknowledgeMethodName, - value: null, ); final BillingResultWrapper billingResult = await billingClient.acknowledgePurchase('dummy token'); 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 c87d0e39f0c2..9737282e27b7 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 @@ -35,7 +35,7 @@ void main() { stubPlatform.addResponse( name: startConnectionCall, value: buildBillingResultMap(expectedBillingResult)); - stubPlatform.addResponse(name: endConnectionCall, value: null); + stubPlatform.addResponse(name: endConnectionCall); iapAndroidPlatformAddition = InAppPurchaseAndroidPlatformAddition(BillingClient((_) {})); }); diff --git a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart index 4f90dccf94f4..b6055cc9a8bb 100644 --- a/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart +++ b/packages/in_app_purchase/in_app_purchase_android/test/in_app_purchase_android_platform_test.dart @@ -39,7 +39,7 @@ void main() { stubPlatform.addResponse( name: startConnectionCall, value: buildBillingResultMap(expectedBillingResult)); - stubPlatform.addResponse(name: endConnectionCall, value: null); + stubPlatform.addResponse(name: endConnectionCall); InAppPurchaseAndroidPlatform.registerPlatform(); iapAndroidPlatform = 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 445fdb6aa491..5aa64191e2a5 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.1+1 + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 0.3.1 * Adds ability to purchase more than one of a product. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart index 5849b17d59d6..a4f9b2603808 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:io'; import 'package:flutter/material.dart'; import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; @@ -22,8 +21,6 @@ void main() { runApp(_MyApp()); } -const bool _kAutoConsume = true; - const String _kConsumableId = 'consumable'; const String _kUpgradeId = 'upgrade'; const String _kSilverSubscriptionId = 'subscription_silver'; @@ -268,12 +265,10 @@ class _MyAppState extends State<_MyApp> { onPressed: () { final PurchaseParam purchaseParam = PurchaseParam( productDetails: productDetails, - applicationUserName: null, ); if (productDetails.id == _kConsumableId) { _iapStoreKitPlatform.buyConsumable( - purchaseParam: purchaseParam, - autoConsume: _kAutoConsume || Platform.isIOS); + purchaseParam: purchaseParam); } else { _iapStoreKitPlatform.buyNonConsumable( purchaseParam: purchaseParam); @@ -335,7 +330,6 @@ class _MyAppState extends State<_MyApp> { return Padding( padding: const EdgeInsets.all(4.0), child: Row( - mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart index f81b36699834..c03f15f8ce48 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart @@ -75,8 +75,7 @@ class InAppPurchaseStoreKitPlatform extends InAppPurchasePlatform { purchaseParam is AppStorePurchaseParam ? purchaseParam.quantity : 1, applicationUsername: purchaseParam.applicationUserName, simulatesAskToBuyInSandbox: purchaseParam is AppStorePurchaseParam && - purchaseParam.simulatesAskToBuyInSandbox, - requestData: null)); + purchaseParam.simulatesAskToBuyInSandbox)); return true; // There's no error feedback from iOS here to return. } 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 dd65b259a283..c037cddf147d 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 platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/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.1 +version: 0.3.1+1 environment: sdk: ">=2.14.0 <3.0.0" 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 e987a5ceac8c..08b9c85961a3 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 @@ -63,8 +63,6 @@ class FakeStoreKitPlatform { payment: SKPaymentWrapper(productIdentifier: id, quantity: quantity), transactionState: SKPaymentTransactionStateWrapper.purchasing, transactionTimeStamp: 123123.121, - error: null, - originalTransaction: null, ); } @@ -76,9 +74,7 @@ class FakeStoreKitPlatform { SKPaymentWrapper(productIdentifier: productId, quantity: quantity), transactionState: SKPaymentTransactionStateWrapper.purchased, transactionTimeStamp: 123123.121, - transactionIdentifier: transactionId, - error: null, - originalTransaction: null); + transactionIdentifier: transactionId); } SKPaymentTransactionWrapper createFailedTransaction(String productId, @@ -92,8 +88,7 @@ class FakeStoreKitPlatform { error: const SKError( code: 0, domain: 'ios_domain', - userInfo: {'message': 'an error message'}), - originalTransaction: null); + userInfo: {'message': 'an error message'})); } SKPaymentTransactionWrapper createCanceledTransaction( @@ -108,8 +103,7 @@ class FakeStoreKitPlatform { error: SKError( code: errorCode, domain: 'ios_domain', - userInfo: const {'message': 'an error message'}), - originalTransaction: null); + userInfo: const {'message': 'an error message'})); } SKPaymentTransactionWrapper createRestoredTransaction( @@ -120,9 +114,7 @@ class FakeStoreKitPlatform { SKPaymentWrapper(productIdentifier: productId, quantity: quantity), transactionState: SKPaymentTransactionStateWrapper.restored, transactionTimeStamp: 123123.121, - transactionIdentifier: transactionId, - error: null, - originalTransaction: null); + transactionIdentifier: transactionId); } Future onMethodCall(MethodCall call) { 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 2baf20892ab6..ff059ffbb1fc 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 @@ -222,7 +222,7 @@ class FakeStoreKitPlatform { case '-[InAppPurchasePlugin startProductRequest:result:]': startProductRequestParam = call.arguments as List; if (getProductRequestFailTest) { - return Future.value(null); + return Future.value(); } return Future>.value( buildProductResponseMap(dummyProductResponseWrapper)); @@ -240,7 +240,7 @@ class FakeStoreKitPlatform { // payment queue case '-[SKPaymentQueue canMakePayments:]': if (testReturnNull) { - return Future.value(null); + return Future.value(); } return Future.value(true); case '-[SKPaymentQueue transactions]': diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart index 51d851cb79b5..e4ef2e3ef432 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart @@ -19,7 +19,6 @@ final SKPaymentTransactionWrapper dummyOriginalTransaction = SKPaymentTransactionWrapper( transactionState: SKPaymentTransactionStateWrapper.purchased, payment: dummyPayment, - originalTransaction: null, transactionTimeStamp: 1231231231.00, transactionIdentifier: '123123', error: dummyError, diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 975d171889cc..5e637d1e8763 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.2 + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 2.1.1 * Replaces `USE_FINGERPRINT` permission with `USE_BIOMETRIC` in README and example project. diff --git a/packages/local_auth/local_auth/example/lib/main.dart b/packages/local_auth/local_auth/example/lib/main.dart index cc687f562402..1330012421ca 100644 --- a/packages/local_auth/local_auth/example/lib/main.dart +++ b/packages/local_auth/local_auth/example/lib/main.dart @@ -83,7 +83,6 @@ class _MyAppState extends State { authenticated = await auth.authenticate( localizedReason: 'Let OS determine authentication method', options: const AuthenticationOptions( - useErrorDialogs: true, stickyAuth: true, ), ); @@ -117,7 +116,6 @@ class _MyAppState extends State { localizedReason: 'Scan your fingerprint (or face or whatever) to authenticate', options: const AuthenticationOptions( - useErrorDialogs: true, stickyAuth: true, biometricOnly: true, ), diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index c6315c50984a..f2598bfdde81 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -3,7 +3,7 @@ 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/plugins/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.1 +version: 2.1.2 environment: sdk: ">=2.14.0 <3.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 844c981e8120..00196a8b875e 100644 --- a/packages/local_auth/local_auth/test/local_auth_test.dart +++ b/packages/local_auth/local_auth/test/local_auth_test.dart @@ -37,7 +37,6 @@ void main() { const AndroidAuthMessages(), const WindowsAuthMessages(), ], - options: const AuthenticationOptions(), )).called(1); }); diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 97f4f7f82904..2d9e277c608f 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.11 + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 1.0.10 * Updates `local_auth_platform_interface` constraint to the correct minimum diff --git a/packages/local_auth/local_auth_android/example/lib/main.dart b/packages/local_auth/local_auth_android/example/lib/main.dart index 016d955f0a3f..9909853a62af 100644 --- a/packages/local_auth/local_auth_android/example/lib/main.dart +++ b/packages/local_auth/local_auth_android/example/lib/main.dart @@ -86,7 +86,6 @@ class _MyAppState extends State { localizedReason: 'Let OS determine authentication method', authMessages: [const AndroidAuthMessages()], options: const AuthenticationOptions( - useErrorDialogs: true, stickyAuth: true, ), ); @@ -121,7 +120,6 @@ class _MyAppState extends State { 'Scan your fingerprint (or face or whatever) to authenticate', authMessages: [const AndroidAuthMessages()], options: const AuthenticationOptions( - useErrorDialogs: true, stickyAuth: true, biometricOnly: true, ), diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index af1ec5104b87..81ee7b4bef07 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.10 +version: 1.0.11 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index 7892bbcf00ba..5972772d6a47 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.9 + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 1.0.8 * Updates `local_auth_platform_interface` constraint to the correct minimum diff --git a/packages/local_auth/local_auth_ios/example/lib/main.dart b/packages/local_auth/local_auth_ios/example/lib/main.dart index 479a96ba809c..fdbf11ffbcaa 100644 --- a/packages/local_auth/local_auth_ios/example/lib/main.dart +++ b/packages/local_auth/local_auth_ios/example/lib/main.dart @@ -86,7 +86,6 @@ class _MyAppState extends State { localizedReason: 'Let OS determine authentication method', authMessages: [const IOSAuthMessages()], options: const AuthenticationOptions( - useErrorDialogs: true, stickyAuth: true, ), ); @@ -121,7 +120,6 @@ class _MyAppState extends State { 'Scan your fingerprint (or face or whatever) to authenticate', authMessages: [const IOSAuthMessages()], options: const AuthenticationOptions( - useErrorDialogs: true, stickyAuth: true, biometricOnly: true, ), diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index e38e3036b03f..0d1b9aef6007 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.8 +version: 1.0.9 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_windows/CHANGELOG.md b/packages/local_auth/local_auth_windows/CHANGELOG.md index 14ac67d26a34..63ce49dd46b7 100644 --- a/packages/local_auth/local_auth_windows/CHANGELOG.md +++ b/packages/local_auth/local_auth_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.3 + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 1.0.2 * Updates `local_auth_platform_interface` constraint to the correct minimum diff --git a/packages/local_auth/local_auth_windows/example/lib/main.dart b/packages/local_auth/local_auth_windows/example/lib/main.dart index ef26ec5545c5..c7b3fd923891 100644 --- a/packages/local_auth/local_auth_windows/example/lib/main.dart +++ b/packages/local_auth/local_auth_windows/example/lib/main.dart @@ -86,7 +86,6 @@ class _MyAppState extends State { localizedReason: 'Let OS determine authentication method', authMessages: [const WindowsAuthMessages()], options: const AuthenticationOptions( - useErrorDialogs: true, stickyAuth: true, ), ); @@ -121,7 +120,6 @@ class _MyAppState extends State { 'Scan your fingerprint (or face or whatever) to authenticate', authMessages: [const WindowsAuthMessages()], options: const AuthenticationOptions( - useErrorDialogs: true, stickyAuth: true, biometricOnly: true, ), diff --git a/packages/local_auth/local_auth_windows/pubspec.yaml b/packages/local_auth/local_auth_windows/pubspec.yaml index 99af0ccd882b..3fec7348b4ef 100644 --- a/packages/local_auth/local_auth_windows/pubspec.yaml +++ b/packages/local_auth/local_auth_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_windows description: Windows implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.2 +version: 1.0.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index 6f345f8c3755..08776093de4d 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 2.0.11 * Updates references to the obsolete master branch. diff --git a/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart b/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart index 6b3dd65fcb14..1fe31df1fe31 100644 --- a/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart +++ b/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart @@ -73,8 +73,7 @@ void main() { testWidgets('getExternalStorageDirectories (type: $type)', (WidgetTester tester) async { if (Platform.isIOS) { - final Future?> result = - getExternalStorageDirectories(type: null); + final Future?> result = getExternalStorageDirectories(); expect(result, throwsA(isInstanceOf())); } else if (Platform.isAndroid) { final List? directories = diff --git a/packages/path_provider/path_provider_windows/CHANGELOG.md b/packages/path_provider/path_provider_windows/CHANGELOG.md index dfae10708432..ebf42b73fb04 100644 --- a/packages/path_provider/path_provider_windows/CHANGELOG.md +++ b/packages/path_provider/path_provider_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.2 + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 2.1.1 * Updates dependency version of `package:win32` to 2.1.0. diff --git a/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart b/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart index 6a9c138f5346..691d7a2da84b 100644 --- a/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart +++ b/packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart @@ -161,7 +161,7 @@ class PathProviderWindows extends PathProviderPlatform { if (hr == E_INVALIDARG || hr == E_FAIL) { throw WindowsException(hr); } - return Future.value(null); + return Future.value(); } final String path = pathPtrPtr.value.toDartString(); diff --git a/packages/path_provider/path_provider_windows/pubspec.yaml b/packages/path_provider/path_provider_windows/pubspec.yaml index 7ed01177ffb7..421a0957dcb2 100644 --- a/packages/path_provider/path_provider_windows/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_windows description: Windows implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.1.1 +version: 2.1.2 environment: sdk: ">=2.17.0 <3.0.0" diff --git a/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart b/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart index 571c31473a0b..48e56406c14f 100644 --- a/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart +++ b/packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart @@ -63,7 +63,7 @@ void main() { pathProvider.versionInfoQuerier = FakeVersionInfoQuerier({ 'CompanyName': 'A Company', 'ProductName': 'Amazing App', - }, language: languageEn, encoding: encodingCP1252); + }, encoding: encodingCP1252); final String? path = await pathProvider.getApplicationSupportPath(); expect(path, isNotNull); if (path != null) { @@ -77,7 +77,7 @@ void main() { pathProvider.versionInfoQuerier = FakeVersionInfoQuerier({ 'CompanyName': 'A Company', 'ProductName': 'Amazing App', - }, language: languageEn, encoding: encodingUnicode); + }); final String? path = await pathProvider.getApplicationSupportPath(); expect(path, isNotNull); if (path != null) { diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index c6e4b9aed8b3..39b2e663f6b1 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 6.1.5 * Migrates `README.md` examples to the [`code-excerpt` system](https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#readme-code). diff --git a/packages/url_launcher/url_launcher/test/src/url_launcher_string_test.dart b/packages/url_launcher/url_launcher/test/src/url_launcher_string_test.dart index 02c0b22903e0..0dcbc34b7dd6 100644 --- a/packages/url_launcher/url_launcher/test/src/url_launcher_string_test.dart +++ b/packages/url_launcher/url_launcher/test/src/url_launcher_string_test.dart @@ -83,8 +83,7 @@ void main() { webOnlyWindowName: null, ) ..setResponse(true); - expect(await launchUrlString(urlString, mode: LaunchMode.platformDefault), - isTrue); + expect(await launchUrlString(urlString), isTrue); }); test('explicit default launch mode with non-web URL', () async { @@ -100,8 +99,7 @@ void main() { webOnlyWindowName: null, ) ..setResponse(true); - expect(await launchUrlString(urlString, mode: LaunchMode.platformDefault), - isTrue); + expect(await launchUrlString(urlString), isTrue); }); test('in-app webview', () async { @@ -246,7 +244,7 @@ void main() { expect(await launchUrlString(emailLaunchUrlString), isTrue); }); - test('allows non-parseable url', () async { + test('allows non-parsable url', () async { // Not a valid Dart [Uri], but a valid URL on at least some platforms. const String urlString = 'rdp://full%20address=s:mypc:3389&audiomode=i:2&disable%20themes=i:1'; diff --git a/packages/url_launcher/url_launcher/test/src/url_launcher_uri_test.dart b/packages/url_launcher/url_launcher/test/src/url_launcher_uri_test.dart index e226e591a4ae..7685aefdd4ee 100644 --- a/packages/url_launcher/url_launcher/test/src/url_launcher_uri_test.dart +++ b/packages/url_launcher/url_launcher/test/src/url_launcher_uri_test.dart @@ -88,7 +88,7 @@ void main() { webOnlyWindowName: null, ) ..setResponse(true); - expect(await launchUrl(url, mode: LaunchMode.platformDefault), isTrue); + expect(await launchUrl(url), isTrue); }); test('explicit default launch mode with non-web URL', () async { @@ -104,7 +104,7 @@ void main() { webOnlyWindowName: null, ) ..setResponse(true); - expect(await launchUrl(url, mode: LaunchMode.platformDefault), isTrue); + expect(await launchUrl(url), isTrue); }); test('in-app webview', () async { diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 2b0ffadaecdf..7a667dafa4da 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.4.6 + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 2.4.5 * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). diff --git a/packages/video_player/video_player/example/integration_test/controller_swap_test.dart b/packages/video_player/video_player/example/integration_test/controller_swap_test.dart index f80e600bcff5..50c81dac3e44 100644 --- a/packages/video_player/video_player/example/integration_test/controller_swap_test.dart +++ b/packages/video_player/video_player/example/integration_test/controller_swap_test.dart @@ -65,7 +65,7 @@ void main() { // Expect that `another` played. expect(another.value.position, - (Duration position) => position > const Duration(seconds: 0)); + (Duration position) => position > const Duration()); await expectLater(started.future, completes); await expectLater(ended.future, completes); @@ -76,7 +76,6 @@ void main() { Widget renderVideoWidget(VideoPlayerController controller) { return Material( - elevation: 0, child: Directionality( textDirection: TextDirection.ltr, child: Center( diff --git a/packages/video_player/video_player/example/integration_test/video_player_test.dart b/packages/video_player/video_player/example/integration_test/video_player_test.dart index d20f47fd69ed..c5e0f3b8c1f1 100644 --- a/packages/video_player/video_player/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player/example/integration_test/video_player_test.dart @@ -49,7 +49,7 @@ void main() { await _controller.initialize(); expect(_controller.value.isInitialized, true); - expect(_controller.value.position, const Duration(seconds: 0)); + expect(_controller.value.position, const Duration()); expect(_controller.value.isPlaying, false); // The WebM version has a slightly different duration than the MP4. expect(_controller.value.duration, @@ -87,7 +87,7 @@ void main() { expect(_controller.value.isPlaying, true); expect(_controller.value.position, - (Duration position) => position > const Duration(seconds: 0)); + (Duration position) => position > const Duration()); }, ); @@ -177,7 +177,6 @@ void main() { } await tester.pumpWidget(Material( - elevation: 0, child: Directionality( textDirection: TextDirection.ltr, child: Center( @@ -265,7 +264,7 @@ void main() { expect(_controller.value.isPlaying, false); expect(_controller.value.position, - (Duration position) => position > const Duration(seconds: 0)); + (Duration position) => position > const Duration()); await expectLater(started.future, completes); await expectLater(ended.future, completes); @@ -285,9 +284,9 @@ void main() { await _controller.initialize(); expect(_controller.value.isInitialized, true); - expect(_controller.value.position, const Duration(seconds: 0)); + expect(_controller.value.position, const Duration()); expect(_controller.value.isPlaying, false); - // Due to the duration calculation accurancy between platforms, + // Due to the duration calculation accuracy between platforms, // the milliseconds on Web will be a slightly different from natives. // The audio was made with 44100 Hz, 192 Kbps CBR, and 32 bits. expect( @@ -308,7 +307,7 @@ void main() { expect(_controller.value.isPlaying, true); expect( _controller.value.position, - (Duration position) => position > const Duration(milliseconds: 0), + (Duration position) => position > const Duration(), ); }); diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index 63afc4a28bc8..c3bed945d007 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -273,7 +273,7 @@ class _ControlsOverlay extends StatelessWidget { Duration(seconds: -3), Duration(seconds: -1, milliseconds: -500), Duration(milliseconds: -250), - Duration(milliseconds: 0), + Duration(), Duration(milliseconds: 250), Duration(seconds: 1, milliseconds: 500), Duration(seconds: 3), @@ -419,7 +419,6 @@ class _PlayerVideoAndPopPageState extends State<_PlayerVideoAndPopPage> { @override Widget build(BuildContext context) { return Material( - elevation: 0, child: Center( child: FutureBuilder( future: started(), diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index 96aa881aba39..fdda3dcc7b56 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -799,7 +799,7 @@ class _VideoPlayerWithRotation extends StatelessWidget { /// Used to configure the [VideoProgressIndicator] widget's colors for how it /// describes the video's status. /// -/// The widget uses default colors that are customizeable through this class. +/// The widget uses default colors that are customizable through this class. class VideoProgressColors { /// Any property can be set to any color. They each have defaults. /// @@ -1005,7 +1005,6 @@ class _VideoProgressIndicatorState extends State { ); } else { progressIndicator = LinearProgressIndicator( - value: null, valueColor: AlwaysStoppedAnimation(colors.playedColor), backgroundColor: colors.backgroundColor, ); diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index bb0e8a8ec581..c9d5fc5ec572 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/plugins/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.4.5 +version: 2.4.6 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index 7837a0bec328..97f39afd5df3 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -175,8 +175,8 @@ void main() { testWidgets('no transform when rotationCorrection is zero', (WidgetTester tester) async { - final FakeController controller = FakeController.value( - VideoPlayerValue(duration: Duration.zero, rotationCorrection: 0)); + final FakeController controller = + FakeController.value(VideoPlayerValue(duration: Duration.zero)); controller.textureId = 1; await tester.pumpWidget(VideoPlayer(controller)); expect(find.byType(Transform), findsNothing); @@ -209,8 +209,7 @@ void main() { }); testWidgets('handles null text', (WidgetTester tester) async { - await tester - .pumpWidget(const MaterialApp(home: ClosedCaption(text: null))); + await tester.pumpWidget(const MaterialApp(home: ClosedCaption())); expect(find.byType(Text), findsNothing); }); @@ -373,7 +372,7 @@ void main() { ); expect( controller.textureId, VideoPlayerController.kUninitializedTextureId); - expect(await controller.position, const Duration(seconds: 0)); + expect(await controller.position, const Duration()); await controller.initialize(); await controller.dispose(); @@ -471,7 +470,7 @@ void main() { 'https://127.0.0.1', ); await controller.initialize(); - expect(await controller.position, const Duration(seconds: 0)); + expect(await controller.position, const Duration()); await controller.seekTo(const Duration(milliseconds: 500)); @@ -494,13 +493,13 @@ void main() { 'https://127.0.0.1', ); await controller.initialize(); - expect(await controller.position, const Duration(seconds: 0)); + expect(await controller.position, const Duration()); await controller.seekTo(const Duration(seconds: 100)); expect(await controller.position, const Duration(seconds: 1)); await controller.seekTo(const Duration(seconds: -100)); - expect(await controller.position, const Duration(seconds: 0)); + expect(await controller.position, const Duration()); }); }); @@ -720,7 +719,7 @@ void main() { expect(controller.value.caption.text, 'one'); }); - test('setClosedCapitonFile loads caption file', () async { + test('setClosedCaptionFile loads caption file', () async { final VideoPlayerController controller = VideoPlayerController.network( 'https://127.0.0.1', ); @@ -735,7 +734,7 @@ void main() { ); }); - test('setClosedCapitonFile removes/changes caption file', () async { + test('setClosedCaptionFile removes/changes caption file', () async { final VideoPlayerController controller = VideoPlayerController.network( 'https://127.0.0.1', closedCaptionFile: _loadClosedCaption(), @@ -789,7 +788,7 @@ void main() { await tester.pumpAndSettle(); expect(controller.value.isBuffering, isTrue); - const Duration bufferStart = Duration(seconds: 0); + const Duration bufferStart = Duration(); const Duration bufferEnd = Duration(milliseconds: 500); fakeVideoEventStream.add(VideoEvent( eventType: VideoEventType.bufferingUpdate, @@ -884,7 +883,7 @@ void main() { text: 'foo', number: 0, start: Duration.zero, end: Duration.zero); const Duration captionOffset = Duration(milliseconds: 250); final List buffered = [ - DurationRange(const Duration(seconds: 0), const Duration(seconds: 4)) + DurationRange(const Duration(), const Duration(seconds: 4)) ]; const bool isInitialized = true; const bool isPlaying = true; @@ -1035,9 +1034,7 @@ void main() { test('false allowBackgroundPlayback pauses playback', () async { final VideoPlayerController controller = VideoPlayerController.file( File(''), - videoPlayerOptions: VideoPlayerOptions( - allowBackgroundPlayback: false, - ), + videoPlayerOptions: VideoPlayerOptions(), ); await controller.initialize(); await controller.play(); @@ -1121,7 +1118,7 @@ class FakeVideoPlayerPlatform extends VideoPlayerPlatform { @override Future getPosition(int textureId) async { calls.add('position'); - return _positions[textureId] ?? const Duration(seconds: 0); + return _positions[textureId] ?? const Duration(); } @override diff --git a/packages/video_player/video_player/test/web_vtt_test.dart b/packages/video_player/video_player/test/web_vtt_test.dart index bde629219484..b7a7bb51ce2b 100644 --- a/packages/video_player/video_player/test/web_vtt_test.dart +++ b/packages/video_player/video_player/test/web_vtt_test.dart @@ -38,8 +38,7 @@ void main() { expect(parsedFile.captions[0].start, const Duration(seconds: 5, milliseconds: 200)); - expect(parsedFile.captions[0].end, - const Duration(seconds: 6, milliseconds: 000)); + expect(parsedFile.captions[0].end, const Duration(seconds: 6)); expect(parsedFile.captions[0].text, "You know I'm so excited my glasses are falling off here."); }); @@ -106,7 +105,7 @@ void main() { final Caption firstCaption = parsedFile.captions.single; expect(firstCaption.number, 1); expect(firstCaption.start, const Duration(seconds: 13)); - expect(firstCaption.end, const Duration(seconds: 16, milliseconds: 0)); + expect(firstCaption.end, const Duration(seconds: 16)); expect(firstCaption.text, 'Valid'); }); } diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index fb34655e50dc..8c26cf7ab149 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 2.3.8 * Updates ExoPlayer to 2.18.0. diff --git a/packages/video_player/video_player_android/example/integration_test/video_player_test.dart b/packages/video_player/video_player_android/example/integration_test/video_player_test.dart index 77a618bbbdfc..5cdab247244f 100644 --- a/packages/video_player/video_player_android/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player_android/example/integration_test/video_player_test.dart @@ -57,7 +57,7 @@ void main() { await _controller.initialize(); expect(_controller.value.isInitialized, true); - expect(await _controller.position, const Duration(seconds: 0)); + expect(await _controller.position, const Duration()); expect(_controller.value.duration, const Duration(seconds: 7, milliseconds: 540)); }); @@ -68,8 +68,7 @@ void main() { await _controller.play(); await tester.pumpAndSettle(_playDuration); - expect( - await _controller.position, greaterThan(const Duration(seconds: 0))); + expect(await _controller.position, greaterThan(const Duration())); }); testWidgets('can seek', (WidgetTester tester) async { @@ -117,8 +116,7 @@ void main() { await _controller.play(); await tester.pumpAndSettle(_playDuration); - expect( - await _controller.position, greaterThan(const Duration(seconds: 0))); + expect(await _controller.position, greaterThan(const Duration())); }); }); @@ -149,8 +147,7 @@ void main() { await tester.pumpAndSettle(_playDuration); await _controller.pause(); - expect( - await _controller.position, greaterThan(const Duration(seconds: 0))); + expect(await _controller.position, greaterThan(const Duration())); await expectLater(started.future, completes); await expectLater(ended.future, completes); diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart index 5bce3117d0d6..bba3fada0307 100644 --- a/packages/video_player/video_player_android/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart @@ -521,7 +521,6 @@ class _VideoProgressIndicatorState extends State { ); } else { progressIndicator = const LinearProgressIndicator( - value: null, valueColor: AlwaysStoppedAnimation(playedColor), backgroundColor: backgroundColor, ); diff --git a/packages/video_player/video_player_android/test/android_video_player_test.dart b/packages/video_player/video_player_android/test/android_video_player_test.dart index a0512a1d1d88..e81c3e824580 100644 --- a/packages/video_player/video_player_android/test/android_video_player_test.dart +++ b/packages/video_player/video_player_android/test/android_video_player_test.dart @@ -341,7 +341,7 @@ void main() { eventType: VideoEventType.bufferingUpdate, buffered: [ DurationRange( - const Duration(milliseconds: 0), + const Duration(), const Duration(milliseconds: 1234), ), DurationRange( diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index bb5c940d3fad..034591348d0a 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). ## 2.3.5 diff --git a/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart b/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart index 528723d092b4..1de77a3b613d 100644 --- a/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart @@ -57,7 +57,7 @@ void main() { await _controller.initialize(); expect(_controller.value.isInitialized, true); - expect(await _controller.position, const Duration(seconds: 0)); + expect(await _controller.position, const Duration()); expect(_controller.value.duration, const Duration(seconds: 7, milliseconds: 540)); }); @@ -68,8 +68,7 @@ void main() { await _controller.play(); await tester.pumpAndSettle(_playDuration); - expect( - await _controller.position, greaterThan(const Duration(seconds: 0))); + expect(await _controller.position, greaterThan(const Duration())); }); testWidgets('can seek', (WidgetTester tester) async { @@ -123,8 +122,7 @@ void main() { await _controller.play(); await tester.pumpAndSettle(_playDuration); - expect( - await _controller.position, greaterThan(const Duration(seconds: 0))); + expect(await _controller.position, greaterThan(const Duration())); }); }); @@ -158,8 +156,7 @@ void main() { // TODO(stuartmorgan): Switch to _controller.position once seekTo is // fixed on the native side to wait for completion, so this is testing // the native code rather than the MiniController position cache. - expect( - _controller.value.position, greaterThan(const Duration(seconds: 0))); + expect(_controller.value.position, greaterThan(const Duration())); await expectLater(started.future, completes); await expectLater(ended.future, completes); diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart index 5bce3117d0d6..bba3fada0307 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart @@ -521,7 +521,6 @@ class _VideoProgressIndicatorState extends State { ); } else { progressIndicator = const LinearProgressIndicator( - value: null, valueColor: AlwaysStoppedAnimation(playedColor), backgroundColor: backgroundColor, ); diff --git a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart index 1e6b024d9a5c..e424aa719f1f 100644 --- a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart +++ b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart @@ -320,7 +320,7 @@ void main() { eventType: VideoEventType.bufferingUpdate, buffered: [ DurationRange( - const Duration(milliseconds: 0), + const Duration(), const Duration(milliseconds: 1234), ), DurationRange( diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index cf5d0168efb3..f7a670ae20e1 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 5.1.4 +* Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). ## 5.1.3 diff --git a/packages/video_player/video_player_platform_interface/lib/messages.dart b/packages/video_player/video_player_platform_interface/lib/messages.g.dart similarity index 100% rename from packages/video_player/video_player_platform_interface/lib/messages.dart rename to packages/video_player/video_player_platform_interface/lib/messages.g.dart diff --git a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart b/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart index be264ca25061..0c2b4b8d6b14 100644 --- a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart +++ b/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart @@ -7,7 +7,7 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; -import 'messages.dart'; +import 'messages.g.dart'; import 'video_player_platform_interface.dart'; /// An implementation of [VideoPlayerPlatform] that uses method channels. diff --git a/packages/video_player/video_player_platform_interface/pigeons/messages.dart b/packages/video_player/video_player_platform_interface/pigeons/messages.dart index 144edb6133b0..7a3490a955d4 100644 --- a/packages/video_player/video_player_platform_interface/pigeons/messages.dart +++ b/packages/video_player/video_player_platform_interface/pigeons/messages.dart @@ -58,6 +58,6 @@ abstract class VideoPlayerApi { } void configurePigeon(PigeonOptions opts) { - opts.dartOut = 'lib/messages.dart'; + opts.dartOut = 'lib/messages.g.dart'; opts.dartTestOut = 'test/test.dart'; } diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml index 8644c45b9e93..365709a85985 100644 --- a/packages/video_player/video_player_platform_interface/pubspec.yaml +++ b/packages/video_player/video_player_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/video_player/v issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%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: 5.1.3 +version: 5.1.4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart b/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart index 924dd08e464d..b894144706b2 100644 --- a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart +++ b/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart @@ -8,7 +8,7 @@ import 'dart:ui'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:video_player_platform_interface/messages.dart'; +import 'package:video_player_platform_interface/messages.g.dart'; import 'package:video_player_platform_interface/method_channel_video_player.dart'; import 'package:video_player_platform_interface/video_player_platform_interface.dart'; @@ -345,7 +345,7 @@ void main() { eventType: VideoEventType.bufferingUpdate, buffered: [ DurationRange( - const Duration(milliseconds: 0), + const Duration(), const Duration(milliseconds: 1234), ), DurationRange( diff --git a/packages/video_player/video_player_platform_interface/test/test.dart b/packages/video_player/video_player_platform_interface/test/test.dart index a12ae45e59db..c696434826a3 100644 --- a/packages/video_player/video_player_platform_interface/test/test.dart +++ b/packages/video_player/video_player_platform_interface/test/test.dart @@ -10,7 +10,7 @@ import 'dart:async'; import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:video_player_platform_interface/messages.dart'; +import 'package:video_player_platform_interface/messages.g.dart'; abstract class TestHostVideoPlayerApi { void initialize(); diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 98ce9d263415..50e3a05006ca 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). * Updates references to the obsolete master branch. diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index 17548901bcb8..f080dae6f1bc 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -401,8 +401,6 @@ Future main() async { onPageFinished: (String url) { pageLoaded.complete(null); }, - initialMediaPlaybackPolicy: - AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, ), ), ); @@ -460,8 +458,6 @@ Future main() async { onPageFinished: (String url) { pageLoaded.complete(null); }, - initialMediaPlaybackPolicy: - AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, ), ), ); @@ -558,7 +554,6 @@ Future main() async { pageLoaded.complete(null); }, initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, - allowsInlineMediaPlayback: false, ), ), ); @@ -663,8 +658,6 @@ Future main() async { onPageFinished: (String url) { pageLoaded.complete(null); }, - initialMediaPlaybackPolicy: - AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, ), ), ); @@ -677,7 +670,7 @@ Future main() async { expect(isPaused, _webviewBool(true)); }); - testWidgets('Changes to initialMediaPlaybackPolocy are ignored', + testWidgets('Changes to initialMediaPlaybackPolicy are ignored', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); @@ -732,8 +725,6 @@ Future main() async { onPageFinished: (String url) { pageLoaded.complete(null); }, - initialMediaPlaybackPolicy: - AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, ), ), ); @@ -876,7 +867,7 @@ Future main() async { }, skip: Platform.isAndroid && _skipDueToIssue86757); }); - // Minimial end-to-end testing of the legacy Android implementation. + // Minimal end-to-end testing of the legacy Android implementation. group('AndroidWebView (virtual display)', () { setUpAll(() { WebView.platform = AndroidWebView(); diff --git a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart index d7189917c221..b10366c82024 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart @@ -85,9 +85,7 @@ void main() { JavascriptMode.unrestricted, ); - await tester.pumpWidget(const WebView( - javascriptMode: JavascriptMode.disabled, - )); + await tester.pumpWidget(const WebView()); final CreationParams disabledparams = captureBuildArgs( mockWebViewPlatform, @@ -455,7 +453,6 @@ void main() { await tester.pumpWidget( WebView( initialUrl: 'https://flutter.io', - javascriptMode: JavascriptMode.disabled, onWebViewCreated: (WebViewController webViewController) { controller = webViewController; }, @@ -489,7 +486,6 @@ void main() { await tester.pumpWidget( WebView( initialUrl: 'https://flutter.io', - javascriptMode: JavascriptMode.disabled, onWebViewCreated: (WebViewController webViewController) { controller = webViewController; }, @@ -527,7 +523,6 @@ void main() { await tester.pumpWidget( WebView( initialUrl: 'https://flutter.io', - javascriptMode: JavascriptMode.disabled, onWebViewCreated: (WebViewController webViewController) { controller = webViewController; }, @@ -757,7 +752,6 @@ void main() { testWidgets('onPageStarted is null', (WidgetTester tester) async { await tester.pumpWidget(const WebView( initialUrl: 'https://youtube.com', - onPageStarted: null, )); final WebViewPlatformCallbacksHandler handler = captureBuildArgs( @@ -818,7 +812,6 @@ void main() { testWidgets('onPageFinished is null', (WidgetTester tester) async { await tester.pumpWidget(const WebView( initialUrl: 'https://youtube.com', - onPageFinished: null, )); final WebViewPlatformCallbacksHandler handler = captureBuildArgs( @@ -878,7 +871,6 @@ void main() { testWidgets('onLoadingProgress is null', (WidgetTester tester) async { await tester.pumpWidget(const WebView( initialUrl: 'https://youtube.com', - onProgress: null, )); final WebViewPlatformCallbacksHandler handler = captureBuildArgs( @@ -1033,7 +1025,6 @@ void main() { await tester.pumpWidget(WebView( key: key, - debuggingEnabled: false, )); final WebSettings disabledSettings = @@ -1046,9 +1037,7 @@ void main() { group('zoomEnabled', () { testWidgets('Enable zoom', (WidgetTester tester) async { - await tester.pumpWidget(const WebView( - zoomEnabled: true, - )); + await tester.pumpWidget(const WebView()); final CreationParams params = captureBuildArgs( mockWebViewPlatform, @@ -1075,7 +1064,6 @@ void main() { await tester.pumpWidget(WebView( key: key, - zoomEnabled: true, )); final WebSettings enabledSettings = diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index dc9850e195dd..79231b02b622 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 2.9.3 * Updates the Dart InstanceManager to take a listener for when an object is garbage collected. 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 c5bf76d2c6cb..8cad944e5455 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 @@ -406,8 +406,6 @@ Future main() async { onPageFinished: (String url) { pageLoaded.complete(null); }, - initialMediaPlaybackPolicy: - AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, ), ), ); @@ -465,8 +463,6 @@ Future main() async { onPageFinished: (String url) { pageLoaded.complete(null); }, - initialMediaPlaybackPolicy: - AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, ), ), ); @@ -616,8 +612,6 @@ Future main() async { onPageFinished: (String url) { pageLoaded.complete(null); }, - initialMediaPlaybackPolicy: - AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, ), ), ); @@ -630,7 +624,7 @@ Future main() async { expect(isPaused, _webviewBool(true)); }); - testWidgets('Changes to initialMediaPlaybackPolocy are ignored', + testWidgets('Changes to initialMediaPlaybackPolicy are ignored', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); @@ -685,8 +679,6 @@ Future main() async { onPageFinished: (String url) { pageLoaded.complete(null); }, - initialMediaPlaybackPolicy: - AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, ), ), ); 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 9d479e62161a..fdb98fc43dbe 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 @@ -81,7 +81,7 @@ void main() { }); test('loadData with null values', () { - webView.loadData(data: 'hello', mimeType: null, encoding: null); + webView.loadData(data: 'hello'); verify(mockPlatformHostApi.loadData( webViewInstanceId, 'hello', diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart index 2fa75af72d69..73076d9141a7 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart @@ -165,8 +165,6 @@ void main() { await buildWidget( tester, creationParams: CreationParams( - autoMediaPlaybackPolicy: - AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, webSettings: WebSettings( userAgent: const WebSetting.absent(), hasNavigationDelegate: false, diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index 565bfee21186..353e679f6467 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). ## 1.9.1 diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart index 8b9a4ceebc91..ea9eb92452ba 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart @@ -39,14 +39,11 @@ void main() { case 'loadFile': if (methodCall.arguments == 'invalid file') { throw PlatformException( - code: 'loadFile_failed', - message: 'Failed loading file.', - details: null); + code: 'loadFile_failed', message: 'Failed loading file.'); } else if (methodCall.arguments == 'some error') { throw PlatformException( code: 'some_error', message: 'Some error occurred.', - details: null, ); } return null; @@ -55,13 +52,11 @@ void main() { throw PlatformException( code: 'loadFlutterAsset_invalidKey', message: 'Failed loading asset.', - details: null, ); } else if (methodCall.arguments == 'some error') { throw PlatformException( code: 'some_error', message: 'Some error occurred.', - details: null, ); } return null; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index a533ce6a6808..fff0110dd4ea 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes avoid_redundant_argument_values lint warnings and minor typos. + ## 2.9.3 * Updates `webview_flutter_platform_interface` constraint to the correct minimum 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 1119f7457bc9..363d046da684 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 @@ -411,8 +411,6 @@ Future main() async { onPageFinished: (String url) { pageLoaded.complete(null); }, - initialMediaPlaybackPolicy: - AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, ), ), ); @@ -470,8 +468,6 @@ Future main() async { onPageFinished: (String url) { pageLoaded.complete(null); }, - initialMediaPlaybackPolicy: - AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, ), ), ); @@ -567,7 +563,6 @@ Future main() async { pageLoaded.complete(null); }, initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow, - allowsInlineMediaPlayback: false, ), ), ); @@ -672,8 +667,6 @@ Future main() async { onPageFinished: (String url) { pageLoaded.complete(null); }, - initialMediaPlaybackPolicy: - AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, ), ), ); @@ -686,7 +679,7 @@ Future main() async { expect(isPaused, _webviewBool(true)); }); - testWidgets('Changes to initialMediaPlaybackPolocy are ignored', + testWidgets('Changes to initialMediaPlaybackPolicy are ignored', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); @@ -741,8 +734,6 @@ Future main() async { onPageFinished: (String url) { pageLoaded.complete(null); }, - initialMediaPlaybackPolicy: - AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, ), ), ); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart index c6d90d04e35e..5e6186ccb4b9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.dart @@ -212,8 +212,6 @@ void main() { await buildWidget( tester, creationParams: CreationParams( - autoMediaPlaybackPolicy: - AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, webSettings: WebSettings( userAgent: const WebSetting.absent(), hasNavigationDelegate: false, @@ -737,7 +735,7 @@ void main() { await buildWidget(tester); when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( - (_) => Future.value(null), + (_) => Future.value(), ); expect( () => testController.runJavascriptReturningResult('runJavaScript'), @@ -1217,7 +1215,7 @@ void main() { testWidgets('progress observer is not removed without being set first', (WidgetTester tester) async { - await buildWidget(tester, hasProgressTracking: false); + await buildWidget(tester); verifyNever(mockWebView.removeObserver( mockWebView, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart index cf5da17dd6e8..71bda575aa52 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart @@ -346,7 +346,7 @@ void main() { ); when(mockWebView.evaluateJavaScript('runJavaScript')).thenAnswer( - (_) => Future.value(null), + (_) => Future.value(), ); expect( () => controller.runJavaScriptReturningResult('runJavaScript'), diff --git a/script/tool/lib/src/common/plugin_command.dart b/script/tool/lib/src/common/plugin_command.dart index be9fb23e57a5..843768fd4c61 100644 --- a/script/tool/lib/src/common/plugin_command.dart +++ b/script/tool/lib/src/common/plugin_command.dart @@ -44,7 +44,6 @@ abstract class PluginCommand extends Command { }) : _gitDir = gitDir { argParser.addMultiOption( _packagesArg, - splitCommas: true, help: 'Specifies which packages the command should run on (before sharding).\n', valueHelp: 'package1,package2,...', diff --git a/script/tool/lib/src/firebase_test_lab_command.dart b/script/tool/lib/src/firebase_test_lab_command.dart index 832870e0e4c4..4fc19e30014c 100644 --- a/script/tool/lib/src/firebase_test_lab_command.dart +++ b/script/tool/lib/src/firebase_test_lab_command.dart @@ -335,7 +335,7 @@ class FirebaseTestLabCommand extends PackageLoopingCommand { } yield* integrationTestDir - .listSync(recursive: true, followLinks: true) + .listSync(recursive: true) .where((FileSystemEntity file) => file is File && file.basename.endsWith('_test.dart')) .cast(); diff --git a/script/tool/lib/src/lint_android_command.dart b/script/tool/lib/src/lint_android_command.dart index 8ba1d643a89b..607674c80d38 100644 --- a/script/tool/lib/src/lint_android_command.dart +++ b/script/tool/lib/src/lint_android_command.dart @@ -35,7 +35,7 @@ class LintAndroidCommand extends PackageLoopingCommand { if (!pluginSupportsPlatform(platformAndroid, package, requiredMode: PlatformSupport.inline)) { return PackageResult.skip( - 'Plugin does not have an Android implemenatation.'); + 'Plugin does not have an Android implementation.'); } bool failed = false; diff --git a/script/tool/lib/src/publish_check_command.dart b/script/tool/lib/src/publish_check_command.dart index af8eac2257eb..d98a286ff582 100644 --- a/script/tool/lib/src/publish_check_command.dart +++ b/script/tool/lib/src/publish_check_command.dart @@ -33,16 +33,13 @@ class PublishCheckCommand extends PackageLoopingCommand { help: 'Allows the pre-release SDK warning to pass.\n' 'When enabled, a pub warning, which asks to publish the package as a pre-release version when ' 'the SDK constraint is a pre-release version, is ignored.', - defaultsTo: false, ); argParser.addFlag(_machineFlag, help: 'Switch outputs to a machine readable JSON. \n' 'The JSON contains a "status" field indicating the final status of the command, the possible values are:\n' ' $_statusNeedsPublish: There is at least one package need to be published. They also passed all publish checks.\n' ' $_statusMessageNoPublish: There are no packages needs to be published. Either no pubspec change detected or all versions have already been published.\n' - ' $_statusMessageError: Some error has occurred.', - defaultsTo: false, - negatable: true); + ' $_statusMessageError: Some error has occurred.'); } static const String _allowPrereleaseFlag = 'allow-pre-release'; diff --git a/script/tool/lib/src/publish_plugin_command.dart b/script/tool/lib/src/publish_plugin_command.dart index 7aa70bd4fd1c..cae8edac71ca 100644 --- a/script/tool/lib/src/publish_plugin_command.dart +++ b/script/tool/lib/src/publish_plugin_command.dart @@ -74,7 +74,6 @@ class PublishPluginCommand extends PackageLoopingCommand { help: 'Release all packages that contains pubspec changes at the current commit compares to the base-sha.\n' 'The --packages option is ignored if this is on.', - defaultsTo: false, ); argParser.addFlag( _dryRunFlag, @@ -82,14 +81,10 @@ class PublishPluginCommand extends PackageLoopingCommand { 'Skips the real `pub publish` and `git tag` commands and assumes both commands are successful.\n' 'This does not run `pub publish --dry-run`.\n' 'If you want to run the command with `pub publish --dry-run`, use `pub-publish-flags=--dry-run`', - defaultsTo: false, - negatable: true, ); 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', - defaultsTo: false, - negatable: true); + 'This command will add a `--force` flag to the `pub publish` command if it is not added with $_pubFlagsOption\n'); } static const String _pubFlagsOption = 'pub-publish-flags'; diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart index d5e9675adcb1..2e5f1efd7934 100644 --- a/script/tool/lib/src/version_check_command.dart +++ b/script/tool/lib/src/version_check_command.dart @@ -115,8 +115,6 @@ class VersionCheckCommand extends PackageLoopingCommand { help: 'Whether the version check should run against the version on pub.\n' 'Defaults to false, which means the version check only run against ' 'the previous version in code.', - defaultsTo: false, - negatable: true, ); argParser.addOption(_changeDescriptionFile, help: 'The path to a file containing the description of the change ' diff --git a/script/tool/test/build_examples_command_test.dart b/script/tool/test/build_examples_command_test.dart index 420b3b1161db..a819e7a12674 100644 --- a/script/tool/test/build_examples_command_test.dart +++ b/script/tool/test/build_examples_command_test.dart @@ -504,7 +504,7 @@ void main() { }); test('skips non-Flutter examples', () async { - createFakePackage('package', packagesDir, isFlutter: false); + createFakePackage('package', packagesDir); final List output = await runCapturingPrint( runner, ['build-examples', '--ios']); diff --git a/script/tool/test/common/file_utils_test.dart b/script/tool/test/common/file_utils_test.dart index e3986842a969..79b804e31ea5 100644 --- a/script/tool/test/common/file_utils_test.dart +++ b/script/tool/test/common/file_utils_test.dart @@ -10,7 +10,7 @@ import 'package:test/test.dart'; void main() { test('works on Posix', () async { final FileSystem fileSystem = - MemoryFileSystem(style: FileSystemStyle.posix); + MemoryFileSystem(); final Directory base = fileSystem.directory('/').childDirectory('base'); final File file = diff --git a/script/tool/test/common/package_looping_command_test.dart b/script/tool/test/common/package_looping_command_test.dart index ec2b9b9be232..7e9f63cb0344 100644 --- a/script/tool/test/common/package_looping_command_test.dart +++ b/script/tool/test/common/package_looping_command_test.dart @@ -373,11 +373,9 @@ void main() { test('skips unsupported Dart versions when requested', () async { final RepositoryPackage excluded = createFakePackage( - 'excluded_package', packagesDir, - isFlutter: false, dartConstraint: '>=2.17.0 <3.0.0'); + 'excluded_package', packagesDir, dartConstraint: '>=2.17.0 <3.0.0'); final RepositoryPackage included = createFakePackage( - 'a_package', packagesDir, - isFlutter: false, dartConstraint: '>=2.14.0 <3.0.0'); + 'a_package', packagesDir); final TestPackageLoopingCommand command = createTestCommand( packageLoopingType: PackageLoopingType.includeAllSubpackages, @@ -409,7 +407,7 @@ void main() { createFakePackage('package_b', packagesDir); final TestPackageLoopingCommand command = - createTestCommand(hasLongOutput: true); + createTestCommand(); final List output = await runCommand(command); const String separator = @@ -443,7 +441,7 @@ void main() { createFakePackage('package_b', packagesDir); final TestPackageLoopingCommand command = - createTestCommand(hasLongOutput: true); + createTestCommand(); final List output = await runCommand(command, arguments: ['--log-timing']); @@ -595,7 +593,7 @@ void main() { createFakePackage('package_b', packagesDir); final TestPackageLoopingCommand command = - createTestCommand(hasLongOutput: true, captureOutput: true); + createTestCommand(captureOutput: true); final List output = await runCommand(command); expect(output, isEmpty); @@ -786,7 +784,7 @@ void main() { createFakePackage('package_f', packagesDir); final TestPackageLoopingCommand command = - createTestCommand(hasLongOutput: true); + createTestCommand(); final List output = await runCommand(command); expect( @@ -812,7 +810,7 @@ void main() { createFakePackage('package_a', packagesDir); final TestPackageLoopingCommand command = - createTestCommand(hasLongOutput: true); + createTestCommand(); final List output = await runCommand(command, arguments: ['--exclude=package_a']); diff --git a/script/tool/test/common/plugin_utils_test.dart b/script/tool/test/common/plugin_utils_test.dart index 9c5ddc3f85b9..415b1db8932a 100644 --- a/script/tool/test/common/plugin_utils_test.dart +++ b/script/tool/test/common/plugin_utils_test.dart @@ -223,12 +223,9 @@ void main() { 'plugin', packagesDir, platformSupport: { - platformLinux: const PlatformDetails(PlatformSupport.inline, - hasNativeCode: true, hasDartCode: true), - platformMacOS: const PlatformDetails(PlatformSupport.inline, - hasNativeCode: true, hasDartCode: true), - platformWindows: const PlatformDetails(PlatformSupport.inline, - hasNativeCode: true, hasDartCode: true), + platformLinux: const PlatformDetails(PlatformSupport.inline, hasDartCode: true), + platformMacOS: const PlatformDetails(PlatformSupport.inline, hasDartCode: true), + platformWindows: const PlatformDetails(PlatformSupport.inline, hasDartCode: true), }, ); diff --git a/script/tool/test/common/repository_package_test.dart b/script/tool/test/common/repository_package_test.dart index dadfc8832997..db519c008233 100644 --- a/script/tool/test/common/repository_package_test.dart +++ b/script/tool/test/common/repository_package_test.dart @@ -213,7 +213,7 @@ void main() { test('returns false for non-Flutter package', () async { final RepositoryPackage package = - createFakePackage('a_package', packagesDir, isFlutter: false); + createFakePackage('a_package', packagesDir); expect(package.requiresFlutter(), false); }); }); diff --git a/script/tool/test/custom_test_command_test.dart b/script/tool/test/custom_test_command_test.dart index 54a1acf8b82b..a28b47505e9d 100644 --- a/script/tool/test/custom_test_command_test.dart +++ b/script/tool/test/custom_test_command_test.dart @@ -146,7 +146,7 @@ void main() { ]); processRunner.mockProcessesForExecutable['dart'] = [ - MockProcess(exitCode: 0), // pub get + MockProcess(), // pub get MockProcess(exitCode: 1), // test script ]; @@ -306,7 +306,7 @@ void main() { ]); processRunner.mockProcessesForExecutable['dart'] = [ - MockProcess(exitCode: 0), // pub get + MockProcess(), // pub get MockProcess(exitCode: 1), // test script ]; diff --git a/script/tool/test/license_check_command_test.dart b/script/tool/test/license_check_command_test.dart index efaf969c83fb..43fbd6b5eca8 100644 --- a/script/tool/test/license_check_command_test.dart +++ b/script/tool/test/license_check_command_test.dart @@ -200,7 +200,7 @@ void main() { test('handles the comment styles for all supported languages', () async { final File fileA = root.childFile('file_a.cc'); fileA.createSync(); - _writeLicense(fileA, comment: '// '); + _writeLicense(fileA); final File fileB = root.childFile('file_b.sh'); fileB.createSync(); _writeLicense(fileB, comment: '# '); diff --git a/script/tool/test/lint_android_command_test.dart b/script/tool/test/lint_android_command_test.dart index b072946ff959..e4a6c5c859e4 100644 --- a/script/tool/test/lint_android_command_test.dart +++ b/script/tool/test/lint_android_command_test.dart @@ -24,7 +24,7 @@ void main() { late RecordingProcessRunner processRunner; setUp(() { - fileSystem = MemoryFileSystem(style: FileSystemStyle.posix); + fileSystem = MemoryFileSystem(); packagesDir = createPackagesDirectory(fileSystem: fileSystem); mockPlatform = MockPlatform(); processRunner = RecordingProcessRunner(); @@ -178,7 +178,7 @@ void main() { containsAllInOrder( [ contains( - 'SKIPPING: Plugin does not have an Android implemenatation.') + 'SKIPPING: Plugin does not have an Android implementation.') ], )); }); @@ -197,7 +197,7 @@ void main() { containsAllInOrder( [ contains( - 'SKIPPING: Plugin does not have an Android implemenatation.') + 'SKIPPING: Plugin does not have an Android implementation.') ], )); }); diff --git a/script/tool/test/lint_podspecs_command_test.dart b/script/tool/test/lint_podspecs_command_test.dart index 516a32fa6925..097bcff338a5 100644 --- a/script/tool/test/lint_podspecs_command_test.dart +++ b/script/tool/test/lint_podspecs_command_test.dart @@ -23,7 +23,7 @@ void main() { late RecordingProcessRunner processRunner; setUp(() { - fileSystem = MemoryFileSystem(style: FileSystemStyle.posix); + fileSystem = MemoryFileSystem(); packagesDir = createPackagesDirectory(fileSystem: fileSystem); mockPlatform = MockPlatform(isMacOS: true); diff --git a/script/tool/test/update_excerpts_command_test.dart b/script/tool/test/update_excerpts_command_test.dart index 5c1d74444eea..dd4b64a98cfc 100644 --- a/script/tool/test/update_excerpts_command_test.dart +++ b/script/tool/test/update_excerpts_command_test.dart @@ -186,7 +186,7 @@ void main() { extraFiles: ['example/build.excerpt.yaml']); processRunner.mockProcessesForExecutable['dart'] = [ - MockProcess(exitCode: 0), // dart pub get + MockProcess(), // dart pub get MockProcess(exitCode: 1), // dart run build_runner ... ]; @@ -211,8 +211,8 @@ void main() { extraFiles: ['example/build.excerpt.yaml']); processRunner.mockProcessesForExecutable['dart'] = [ - MockProcess(exitCode: 0), // dart pub get - MockProcess(exitCode: 0), // dart run build_runner ... + MockProcess(), // dart pub get + MockProcess(), // dart run build_runner ... MockProcess(exitCode: 1), // dart run code_excerpt_updater ... ]; diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index 041d93367f9f..2754f64dde19 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -117,7 +117,6 @@ RepositoryPackage createFakePlugin( createFakePubspec( package, name: name, - isFlutter: true, isPlugin: true, platformSupport: platformSupport, version: version, @@ -399,10 +398,10 @@ class RecordingProcessRunner extends ProcessRunner { final io.Process? process = _getProcessToReturn(executable); final List? processStdout = await process?.stdout.transform(stdoutEncoding.decoder).toList(); - final String stdout = processStdout?.join('') ?? ''; + final String stdout = processStdout?.join() ?? ''; final List? processStderr = await process?.stderr.transform(stderrEncoding.decoder).toList(); - final String stderr = processStderr?.join('') ?? ''; + final String stderr = processStderr?.join() ?? ''; final io.ProcessResult result = process == null ? io.ProcessResult(1, 0, '', '') From a124dcb33b620f2908aae4f28af6487163b7d8ea Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 9 Aug 2022 12:51:05 -0400 Subject: [PATCH 590/844] Roll Flutter from a4947768b729 to 19eed7c870ca (26 revisions) (#6219) --- .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 136cf7d3925f..2a509db17101 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a4947768b72956c353bf950b3febe36a77cb106b +19eed7c870ca2e9047beae8f93b3681d7c6c3a6f From e9d8479a4d03b640300001b5e79af7d8833780c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Aug 2022 18:09:07 +0000 Subject: [PATCH 591/844] [path_provider]: Bump gradle from 3.3.0 to 7.2.2 in /packages/path_provider/path_provider_android/android (#6189) --- packages/path_provider/path_provider_android/CHANGELOG.md | 5 +++++ .../path_provider/path_provider_android/android/build.gradle | 4 ++-- packages/path_provider/path_provider_android/pubspec.yaml | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index 799c95a3bfe6..e7e6dc20595a 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.18 + +* Bumps `androidx.annotation:annotation` version to 1.4.0. +* Bumps gradle version to 7.2.2. + ## 2.0.17 * Lower minimim version back to 2.8.1. diff --git a/packages/path_provider/path_provider_android/android/build.gradle b/packages/path_provider/path_provider_android/android/build.gradle index 4bfa738ac44c..2fdc53b1f11b 100644 --- a/packages/path_provider/path_provider_android/android/build.gradle +++ b/packages/path_provider/path_provider_android/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:7.2.2' } } @@ -52,7 +52,7 @@ android { } dependencies { - implementation 'androidx.annotation:annotation:1.1.0' + implementation 'androidx.annotation:annotation:1.4.0' implementation 'com.google.guava:guava:28.1-android' testImplementation 'junit:junit:4.13.2' } diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index ae132b06f0bc..232e63cf714f 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.17 +version: 2.0.18 environment: sdk: ">=2.14.0 <3.0.0" From 00e73a48f9ed3dc0d2a252d4e5ba64e0e38b33d2 Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Wed, 10 Aug 2022 11:11:05 -0700 Subject: [PATCH 592/844] [quick_actions]refactor into smaller components and 100% unit test coverage (#6212) --- .../quick_actions_ios/CHANGELOG.md | 5 + .../ios/Runner.xcodeproj/project.pbxproj | 8 +- .../RunnerTests/FLTQuickActionsPluginTests.m | 130 ++++++++++++------ .../FLTShortcutStateManagerTests.m | 63 +++++++++ .../example/ios/RunnerTests/RunnerTests.m | 22 --- .../ios/Classes/FLTQuickActionsPlugin.h | 4 +- .../ios/Classes/FLTQuickActionsPlugin.m | 43 ++---- .../ios/Classes/FLTShortcutStateManager.m | 32 +++++ .../FLTQuickActionsPlugin_Test.h | 9 +- .../PrivateHeaders/FLTShortcutStateManager.h | 18 +++ .../ios/Classes/QuickActionsPlugin.modulemap | 1 + .../quick_actions_ios/pubspec.yaml | 2 +- 12 files changed, 233 insertions(+), 104 deletions(-) create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTShortcutStateManagerTests.m delete mode 100644 packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/RunnerTests.m create mode 100644 packages/quick_actions/quick_actions_ios/ios/Classes/FLTShortcutStateManager.m create mode 100644 packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTShortcutStateManager.h diff --git a/packages/quick_actions/quick_actions_ios/CHANGELOG.md b/packages/quick_actions/quick_actions_ios/CHANGELOG.md index 53817b804c57..8f4d4d99c2c3 100644 --- a/packages/quick_actions/quick_actions_ios/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.6.0+14 + +* Refactors `FLTQuickActionsPlugin` class into multiple components. +* Increases unit tests coverage to 100%. + ## 0.6.0+13 * Adds some unit tests for `FLTQuickActionsPlugin` class. diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj index b70dc29a58f9..47360fe51a70 100644 --- a/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,7 +9,6 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 2632072169FF635893D8EB4D /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 436668746754BEEA28B76E55 /* libPods-RunnerTests.a */; }; - 33E20B3526EFCDFC00A4A191 /* RunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 33E20B3426EFCDFC00A4A191 /* RunnerTests.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 686BE83025E58CCF00862533 /* RunnerUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 686BE82F25E58CCF00862533 /* RunnerUITests.m */; }; 6A841C2B6AED5CF8DB2A1894 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C35AD3650AB6BF850E016715 /* libPods-Runner.a */; }; @@ -19,6 +18,7 @@ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; E0C09C29289C729D00E6977E /* FLTQuickActionsPluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C09C28289C729D00E6977E /* FLTQuickActionsPluginTests.m */; }; + E0C09C32289DBFCA00E6977E /* FLTShortcutStateManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C09C31289DBFCA00E6977E /* FLTShortcutStateManagerTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -55,7 +55,6 @@ 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 = ""; }; 33E20B3226EFCDFC00A4A191 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 33E20B3426EFCDFC00A4A191 /* RunnerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerTests.m; sourceTree = ""; }; 33E20B3626EFCDFC00A4A191 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 436668746754BEEA28B76E55 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -78,6 +77,7 @@ 9D27FE1F0F21D4D47DDA16DE /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; C35AD3650AB6BF850E016715 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; E0C09C28289C729D00E6977E /* FLTQuickActionsPluginTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTQuickActionsPluginTests.m; sourceTree = ""; }; + E0C09C31289DBFCA00E6977E /* FLTShortcutStateManagerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTShortcutStateManagerTests.m; sourceTree = ""; }; F0609304FBCAEC2289164BD5 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -111,8 +111,8 @@ 33E20B3326EFCDFC00A4A191 /* RunnerTests */ = { isa = PBXGroup; children = ( - 33E20B3426EFCDFC00A4A191 /* RunnerTests.m */, 33E20B3626EFCDFC00A4A191 /* Info.plist */, + E0C09C31289DBFCA00E6977E /* FLTShortcutStateManagerTests.m */, E0C09C28289C729D00E6977E /* FLTQuickActionsPluginTests.m */, ); path = RunnerTests; @@ -415,7 +415,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 33E20B3526EFCDFC00A4A191 /* RunnerTests.m in Sources */, + E0C09C32289DBFCA00E6977E /* FLTShortcutStateManagerTests.m in Sources */, E0C09C29289C729D00E6977E /* FLTQuickActionsPluginTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTQuickActionsPluginTests.m b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTQuickActionsPluginTests.m index 0d4070b6dcf6..b47f89848bbc 100644 --- a/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTQuickActionsPluginTests.m +++ b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTQuickActionsPluginTests.m @@ -14,62 +14,49 @@ @interface FLTQuickActionsPluginTests : XCTestCase @implementation FLTQuickActionsPluginTests -// A dummy `UIApplicationShortcutItem`. -- (UIApplicationShortcutItem *)searchTheThingShortcutItem { - return [[UIApplicationShortcutItem alloc] - initWithType:@"SearchTheThing" - localizedTitle:@"Search the thing" - localizedSubtitle:nil - icon:[UIApplicationShortcutIcon - iconWithTemplateImageName:@"search_the_thing.png"] - userInfo:nil]; -} - -// A dummy raw shortcut item. -- (NSDictionary *)searchTheThingRawItem { - return @{ +- (void)testHandleMethodCall_setShortcutItems { + NSDictionary *rawItem = @{ @"type" : @"SearchTheThing", @"localizedTitle" : @"Search the thing", @"icon" : @"search_the_thing.png", }; -} -- (void)testHandleMethodCall_setShortcutItems { - FlutterMethodCall *call = - [FlutterMethodCall methodCallWithMethodName:@"setShortcutItems" - arguments:@[ [self searchTheThingRawItem] ]]; + FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"setShortcutItems" + arguments:@[ rawItem ]]; + + FLTShortcutStateManager *mockShortcutStateManager = OCMClassMock([FLTShortcutStateManager class]); FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])]; + [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) + shortcutStateManager:mockShortcutStateManager]; XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result block must be called."]; [plugin handleMethodCall:call result:^(id _Nullable result) { XCTAssertNil(result, @"result block must be called with nil."); - XCTAssertEqualObjects(UIApplication.sharedApplication.shortcutItems, - @[ [self searchTheThingShortcutItem] ], - @"shortcut items should be set correctly."); [resultExpectation fulfill]; }]; [self waitForExpectationsWithTimeout:1 handler:nil]; + + OCMVerify([mockShortcutStateManager setShortcutItems:@[ rawItem ]]); } - (void)testHandleMethodCall_clearShortcutItems { FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"clearShortcutItems" arguments:nil]; - + FLTShortcutStateManager *mockShortcutStateManager = OCMClassMock([FLTShortcutStateManager class]); FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])]; + [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) + shortcutStateManager:mockShortcutStateManager]; XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result block must be called."]; [plugin handleMethodCall:call result:^(id _Nullable result) { XCTAssertNil(result, @"result block must be called with nil."); - XCTAssertEqual(UIApplication.sharedApplication.shortcutItems.count, 0, - @"shortcut items should be cleared"); [resultExpectation fulfill]; }]; [self waitForExpectationsWithTimeout:1 handler:nil]; + OCMVerify([mockShortcutStateManager setShortcutItems:@[]]); } - (void)testHandleMethodCall_getLaunchAction { @@ -77,7 +64,8 @@ - (void)testHandleMethodCall_getLaunchAction { arguments:nil]; FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])]; + [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) + shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result block must be called."]; [plugin handleMethodCall:call @@ -92,7 +80,8 @@ - (void)testHandleMethodCall_nonExistMethods { FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"nonExist" arguments:nil]; FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])]; + [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) + shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result must be called."]; [plugin @@ -108,9 +97,17 @@ - (void)testHandleMethodCall_nonExistMethods { - (void)testApplicationPerformActionForShortcutItem { id mockChannel = OCMClassMock([FlutterMethodChannel class]); - FLTQuickActionsPlugin *plugin = [[FLTQuickActionsPlugin alloc] initWithChannel:mockChannel]; + FLTQuickActionsPlugin *plugin = + [[FLTQuickActionsPlugin alloc] initWithChannel:mockChannel + shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; - UIApplicationShortcutItem *item = [self searchTheThingShortcutItem]; + UIApplicationShortcutItem *item = [[UIApplicationShortcutItem alloc] + initWithType:@"SearchTheThing" + localizedTitle:@"Search the thing" + localizedSubtitle:nil + icon:[UIApplicationShortcutIcon + iconWithTemplateImageName:@"search_the_thing.png"] + userInfo:nil]; BOOL actionResult = [plugin application:[UIApplication sharedApplication] performActionForShortcutItem:item @@ -120,10 +117,18 @@ - (void)testApplicationPerformActionForShortcutItem { } - (void)testApplicationDidFinishLaunchingWithOptions_launchWithShortcut { + id mockShortcutStateManager = OCMClassMock([FLTShortcutStateManager class]); FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])]; + [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) + shortcutStateManager:mockShortcutStateManager]; - UIApplicationShortcutItem *item = [self searchTheThingShortcutItem]; + UIApplicationShortcutItem *item = [[UIApplicationShortcutItem alloc] + initWithType:@"SearchTheThing" + localizedTitle:@"Search the thing" + localizedSubtitle:nil + icon:[UIApplicationShortcutIcon + iconWithTemplateImageName:@"search_the_thing.png"] + userInfo:nil]; BOOL launchResult = [plugin application:[UIApplication sharedApplication] didFinishLaunchingWithOptions:@{UIApplicationLaunchOptionsShortcutItemKey : item}]; @@ -134,23 +139,68 @@ - (void)testApplicationDidFinishLaunchingWithOptions_launchWithShortcut { - (void)testApplicationDidFinishLaunchingWithOptions_launchWithoutShortcut { FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])]; + [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) + shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; BOOL launchResult = [plugin application:[UIApplication sharedApplication] didFinishLaunchingWithOptions:@{}]; XCTAssertTrue(launchResult, @"didFinishLaunchingWithOptions must return true if not launched from shortcut."); } -- (void)testApplicationDidBecomeActive { - UIApplicationShortcutItem *item = [self searchTheThingShortcutItem]; +- (void)testApplicationDidBecomeActive_launchWithoutShortcut { id mockChannel = OCMClassMock([FlutterMethodChannel class]); - FLTQuickActionsPlugin *plugin = [[FLTQuickActionsPlugin alloc] initWithChannel:mockChannel]; - plugin.launchingShortcutType = item.type; + id mockShortcutStateManager = OCMClassMock([FLTShortcutStateManager class]); + FLTQuickActionsPlugin *plugin = + [[FLTQuickActionsPlugin alloc] initWithChannel:mockChannel + shortcutStateManager:mockShortcutStateManager]; + + [plugin application:[UIApplication sharedApplication] didFinishLaunchingWithOptions:@{}]; [plugin applicationDidBecomeActive:[UIApplication sharedApplication]]; + OCMVerify(never(), [mockChannel invokeMethod:OCMOCK_ANY arguments:OCMOCK_ANY]); +} + +- (void)testApplicationDidBecomeActive_launchWithShortcut { + id mockChannel = OCMClassMock([FlutterMethodChannel class]); + id mockShortcutStateManager = OCMClassMock([FLTShortcutStateManager class]); + FLTQuickActionsPlugin *plugin = + [[FLTQuickActionsPlugin alloc] initWithChannel:mockChannel + shortcutStateManager:mockShortcutStateManager]; + + UIApplicationShortcutItem *item = [[UIApplicationShortcutItem alloc] + initWithType:@"SearchTheThing" + localizedTitle:@"Search the thing" + localizedSubtitle:nil + icon:[UIApplicationShortcutIcon + iconWithTemplateImageName:@"search_the_thing.png"] + userInfo:nil]; + [plugin application:[UIApplication sharedApplication] + didFinishLaunchingWithOptions:@{UIApplicationLaunchOptionsShortcutItemKey : item}]; + [plugin applicationDidBecomeActive:[UIApplication sharedApplication]]; OCMVerify([mockChannel invokeMethod:@"launch" arguments:item.type]); - XCTAssertNil(plugin.launchingShortcutType, - @"Must reset launchingShortcutType to nil after being used."); +} + +- (void)testApplicationDidBecomeActive_launchWithShortcut_becomeActiveTwice { + id mockChannel = OCMClassMock([FlutterMethodChannel class]); + id mockShortcutStateManager = OCMClassMock([FLTShortcutStateManager class]); + FLTQuickActionsPlugin *plugin = + [[FLTQuickActionsPlugin alloc] initWithChannel:mockChannel + shortcutStateManager:mockShortcutStateManager]; + + UIApplicationShortcutItem *item = [[UIApplicationShortcutItem alloc] + initWithType:@"SearchTheThing" + localizedTitle:@"Search the thing" + localizedSubtitle:nil + icon:[UIApplicationShortcutIcon + iconWithTemplateImageName:@"search_the_thing.png"] + userInfo:nil]; + [plugin application:[UIApplication sharedApplication] + didFinishLaunchingWithOptions:@{UIApplicationLaunchOptionsShortcutItemKey : item}]; + + [plugin applicationDidBecomeActive:[UIApplication sharedApplication]]; + [plugin applicationDidBecomeActive:[UIApplication sharedApplication]]; + // shortcut should only be handled once per launch. + OCMVerify(times(1), [mockChannel invokeMethod:@"launch" arguments:item.type]); } @end diff --git a/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTShortcutStateManagerTests.m b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTShortcutStateManagerTests.m new file mode 100644 index 000000000000..f5b8b3405fc8 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTShortcutStateManagerTests.m @@ -0,0 +1,63 @@ +// 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 quick_actions_ios; +@import quick_actions_ios.Test; +@import XCTest; +#import + +@interface FLTShortcutStateManagerTests : XCTestCase +@end + +@implementation FLTShortcutStateManagerTests + +- (void)testSetShortcutItems_shouldSetItem { + id mockApplication = OCMPartialMock([UIApplication sharedApplication]); + OCMStub([mockApplication sharedApplication]).andReturn(mockApplication); + + FLTShortcutStateManager *shortcutStateManager = [[FLTShortcutStateManager alloc] init]; + + NSDictionary *rawItem = @{ + @"type" : @"SearchTheThing", + @"localizedTitle" : @"Search the thing", + @"icon" : @"search_the_thing.png", + }; + + [shortcutStateManager setShortcutItems:@[ rawItem ]]; + + UIApplicationShortcutItem *expectedItem = [[UIApplicationShortcutItem alloc] + initWithType:@"SearchTheThing" + localizedTitle:@"Search the thing" + localizedSubtitle:nil + icon:[UIApplicationShortcutIcon + iconWithTemplateImageName:@"search_the_thing.png"] + userInfo:nil]; + + OCMVerify([mockApplication setShortcutItems:@[ expectedItem ]]); +} + +- (void)testSetShortcutItems_shouldSetItemWithoutIcon { + id mockApplication = OCMPartialMock([UIApplication sharedApplication]); + OCMStub([mockApplication sharedApplication]).andReturn(mockApplication); + + NSDictionary *rawItem = @{ + @"type" : @"SearchTheThing", + @"localizedTitle" : @"Search the thing", + // Dart's null value is passed to iOS as `NSNull`. + // The key value pair is still present in the dictionary. + @"icon" : [NSNull null], + }; + FLTShortcutStateManager *shortcutStateManager = [[FLTShortcutStateManager alloc] init]; + [shortcutStateManager setShortcutItems:@[ rawItem ]]; + + UIApplicationShortcutItem *expectedItem = + [[UIApplicationShortcutItem alloc] initWithType:@"SearchTheThing" + localizedTitle:@"Search the thing" + localizedSubtitle:nil + icon:nil + userInfo:nil]; + OCMVerify([mockApplication setShortcutItems:@[ expectedItem ]]); +} + +@end diff --git a/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/RunnerTests.m b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/RunnerTests.m deleted file mode 100644 index 3e35e2d6876b..000000000000 --- a/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/RunnerTests.m +++ /dev/null @@ -1,22 +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 quick_actions_ios; -@import quick_actions_ios.Test; -@import XCTest; -#import - -@interface QuickActionsTests : XCTestCase -@end - -@implementation QuickActionsTests - -- (void)testPlugin { - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class])]; - XCTAssertNotNil(plugin); -} - -@end diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.h b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.h index 6eb6e2b782ea..63f615440dea 100644 --- a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.h +++ b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.h @@ -6,9 +6,9 @@ @interface FLTQuickActionsPlugin : NSObject -/// Unavailable. Please use `initWithChannel:` instead. +/// Unavailable. - (instancetype)init NS_UNAVAILABLE; -/// Unavailable. Please use `initWithChannel:` instead. +/// Unavailable. + (instancetype)new NS_UNAVAILABLE; @end diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m index 798af0accb1e..fb8994322a9b 100644 --- a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m +++ b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m @@ -4,11 +4,15 @@ #import "FLTQuickActionsPlugin.h" #import "FLTQuickActionsPlugin_Test.h" +#import "FLTShortcutStateManager.h" static NSString *const kChannelName = @"plugins.flutter.io/quick_actions_ios"; @interface FLTQuickActionsPlugin () -@property(nonatomic, retain) FlutterMethodChannel *channel; +@property(nonatomic, strong) FlutterMethodChannel *channel; +/// The type of the shortcut item selected when launching the app. +@property(nonatomic, strong, nullable) NSString *launchingShortcutType; +@property(nonatomic, strong) FLTShortcutStateManager *shortcutStateManager; @end @implementation FLTQuickActionsPlugin @@ -17,24 +21,28 @@ + (void)registerWithRegistrar:(NSObject *)registrar { FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:kChannelName binaryMessenger:[registrar messenger]]; - FLTQuickActionsPlugin *instance = [[FLTQuickActionsPlugin alloc] initWithChannel:channel]; + FLTQuickActionsPlugin *instance = + [[FLTQuickActionsPlugin alloc] initWithChannel:channel + shortcutStateManager:[[FLTShortcutStateManager alloc] init]]; [registrar addMethodCallDelegate:instance channel:channel]; [registrar addApplicationDelegate:instance]; } -- (instancetype)initWithChannel:(FlutterMethodChannel *)channel { +- (instancetype)initWithChannel:(FlutterMethodChannel *)channel + shortcutStateManager:(FLTShortcutStateManager *)shortcutStateManager { if ((self = [super init])) { _channel = channel; + _shortcutStateManager = shortcutStateManager; } return self; } - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { if ([call.method isEqualToString:@"setShortcutItems"]) { - _setShortcutItems(call.arguments); + [self.shortcutStateManager setShortcutItems:call.arguments]; result(nil); } else if ([call.method isEqualToString:@"clearShortcutItems"]) { - [UIApplication sharedApplication].shortcutItems = @[]; + [self.shortcutStateManager setShortcutItems:@[]]; result(nil); } else if ([call.method isEqualToString:@"getLaunchAction"]) { result(nil); @@ -88,29 +96,4 @@ - (void)handleShortcut:(NSString *)shortcut { [self.channel invokeMethod:@"launch" arguments:shortcut]; } -NS_INLINE void _setShortcutItems(NSArray *items) API_AVAILABLE(ios(9.0)) { - NSMutableArray *newShortcuts = [[NSMutableArray alloc] init]; - - for (id item in items) { - UIApplicationShortcutItem *shortcut = _deserializeShortcutItem(item); - [newShortcuts addObject:shortcut]; - } - - [UIApplication sharedApplication].shortcutItems = newShortcuts; -} - -NS_INLINE UIApplicationShortcutItem *_deserializeShortcutItem(NSDictionary *serialized) - API_AVAILABLE(ios(9.0)) { - UIApplicationShortcutIcon *icon = - [serialized[@"icon"] isKindOfClass:[NSNull class]] - ? nil - : [UIApplicationShortcutIcon iconWithTemplateImageName:serialized[@"icon"]]; - - return [[UIApplicationShortcutItem alloc] initWithType:serialized[@"type"] - localizedTitle:serialized[@"localizedTitle"] - localizedSubtitle:nil - icon:icon - userInfo:nil]; -} - @end diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTShortcutStateManager.m b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTShortcutStateManager.m new file mode 100644 index 000000000000..e39edd2b0f37 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTShortcutStateManager.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 "FLTShortcutStateManager.h" + +@implementation FLTShortcutStateManager + +- (void)setShortcutItems:(NSArray *)items { + NSMutableArray *newShortcuts = [[NSMutableArray alloc] init]; + + for (id item in items) { + UIApplicationShortcutItem *shortcut = [self deserializeShortcutItem:item]; + [newShortcuts addObject:shortcut]; + } + + [UIApplication sharedApplication].shortcutItems = newShortcuts; +} + +- (UIApplicationShortcutItem *)deserializeShortcutItem:(NSDictionary *)serialized { + UIApplicationShortcutIcon *icon = + [serialized[@"icon"] isKindOfClass:[NSNull class]] + ? nil + : [UIApplicationShortcutIcon iconWithTemplateImageName:serialized[@"icon"]]; + return [[UIApplicationShortcutItem alloc] initWithType:serialized[@"type"] + localizedTitle:serialized[@"localizedTitle"] + localizedSubtitle:nil + icon:icon + userInfo:nil]; +} + +@end diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h b/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h index bd2d756e6767..514d0633f198 100644 --- a/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h +++ b/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h @@ -3,21 +3,20 @@ // found in the LICENSE file. @import Flutter; +#import "FLTShortcutStateManager.h" NS_ASSUME_NONNULL_BEGIN /// APIs exposed for unit tests. @interface FLTQuickActionsPlugin () -/// The type of the shortcut item selected when launching the app. -/// API exposed for unit tests. -@property(nonatomic, strong, nullable) NSString *launchingShortcutType; - /// Initializes a FLTQuickActionsPlugin with the given method channel. /// API exposed for unit tests. /// @param channel A method channel. +/// @param shortcutStateManager An FLTShortcutStateManager that manages shortcut related states. /// @return The initialized FLTQuickActionsPlugin. -- (instancetype)initWithChannel:(FlutterMethodChannel *)channel; +- (instancetype)initWithChannel:(FlutterMethodChannel *)channel + shortcutStateManager:(FLTShortcutStateManager *)shortcutStateManager; @end diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTShortcutStateManager.h b/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTShortcutStateManager.h new file mode 100644 index 000000000000..05d0433db6e0 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTShortcutStateManager.h @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// Manages the shortcut related states. +@interface FLTShortcutStateManager : NSObject + +/// Sets the list of shortcut items. +/// +/// @param items the list of shortcut items to be parsed and set. +- (void)setShortcutItems:(NSArray *)items API_AVAILABLE(ios(9.0)); +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.modulemap b/packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.modulemap index e8371cbb7c40..3f7d7ce08203 100644 --- a/packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.modulemap +++ b/packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.modulemap @@ -6,5 +6,6 @@ framework module quick_actions_ios { explicit module Test { header "FLTQuickActionsPlugin_Test.h" + header "FLTShortcutStateManager.h" } } diff --git a/packages/quick_actions/quick_actions_ios/pubspec.yaml b/packages/quick_actions/quick_actions_ios/pubspec.yaml index 42dbde372fb9..77c2a20414a7 100644 --- a/packages/quick_actions/quick_actions_ios/pubspec.yaml +++ b/packages/quick_actions/quick_actions_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_ios description: An implementation for the iOS platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.6.0+13 +version: 0.6.0+14 environment: sdk: ">=2.15.0 <3.0.0" From 59ee250310a27208d4a3fb03929386db78ab4a29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Aug 2022 18:11:23 +0000 Subject: [PATCH 593/844] [webview]: Bump gradle from 7.2.1 to 7.2.2 in /packages/webview_flutter/webview_flutter_android/android (#6196) --- packages/webview_flutter/webview_flutter_android/CHANGELOG.md | 3 ++- .../webview_flutter_android/android/build.gradle | 2 +- packages/webview_flutter/webview_flutter_android/pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 79231b02b622..bc64a6f1c0ed 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.9.4 * Fixes avoid_redundant_argument_values lint warnings and minor typos. +* Bumps gradle from 7.2.1 to 7.2.2. ## 2.9.3 diff --git a/packages/webview_flutter/webview_flutter_android/android/build.gradle b/packages/webview_flutter/webview_flutter_android/android/build.gradle index 6ba3f65dbedc..0f0eb67e760e 100644 --- a/packages/webview_flutter/webview_flutter_android/android/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.2.1' + classpath 'com.android.tools.build:gradle:7.2.2' } } diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 9a7caabb3fe3..70ca71de50db 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/plugins/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: 2.9.3 +version: 2.9.4 environment: sdk: ">=2.14.0 <3.0.0" From 64ce54df54c005c6c97b129380ab24cf6b1e0db6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 10 Aug 2022 14:16:35 -0400 Subject: [PATCH 594/844] Roll Flutter from 19eed7c870ca to ac2f6ed23784 (28 revisions) (#6222) --- .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 2a509db17101..83cfebd06d69 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -19eed7c870ca2e9047beae8f93b3681d7c6c3a6f +ac2f6ed23784b0be04e2ad734334b2d993c27a28 From 6c9738ea56f99c74bac52d790662ffa5de82ce0d Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Wed, 10 Aug 2022 14:06:06 -0700 Subject: [PATCH 595/844] [in_app_purchase_storekit] Add identifier, type fields to SKProductDiscountWrapper (#6205) --- .../in_app_purchase_storekit/CHANGELOG.md | 4 ++ .../example/ios/RunnerTests/Stubs.m | 4 ++ .../example/ios/RunnerTests/TranslatorTests.m | 32 +++++++++++++-- .../ios/Classes/FIAObjectTranslator.m | 6 ++- .../store_kit_wrappers/enum_converters.dart | 24 +++++++++++ .../store_kit_wrappers/enum_converters.g.dart | 5 +++ .../sk_product_wrapper.dart | 40 +++++++++++++++++-- .../sk_product_wrapper.g.dart | 3 ++ .../in_app_purchase_storekit/pubspec.yaml | 2 +- .../store_kit_wrappers/sk_product_test.dart | 10 +++++ .../sk_test_stub_objects.dart | 28 +++++++++++++ 11 files changed, 148 insertions(+), 10 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 5aa64191e2a5..f0ad4921df94 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.2 + +* Adds the `identifier` and `type` fields to the `SKProductDiscountWrapper` to reflect the changes in the [SKProductDiscount](https://developer.apple.com/documentation/storekit/skproductdiscount?language=objc) in iOS 12.2. + ## 0.3.1+1 * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Stubs.m b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Stubs.m index e4277d3edd59..f5e44d78b157 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Stubs.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Stubs.m @@ -31,6 +31,10 @@ - (instancetype)initWithMap:(NSDictionary *)map { [[SKProductSubscriptionPeriodStub alloc] initWithMap:map[@"subscriptionPeriod"]]; [self setValue:subscriptionPeriodSub forKey:@"subscriptionPeriod"]; [self setValue:map[@"paymentMode"] ?: @(0) forKey:@"paymentMode"]; + if (@available(iOS 12.2, *)) { + [self setValue:map[@"identifier"] ?: [NSNull null] forKey:@"identifier"]; + [self setValue:map[@"type"] ?: @(0) forKey:@"type"]; + } } return self; } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m index c4e1ac1d059d..ed302d61d9b0 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m @@ -10,7 +10,8 @@ @interface TranslatorTest : XCTestCase @property(strong, nonatomic) NSDictionary *periodMap; -@property(strong, nonatomic) NSDictionary *discountMap; +@property(strong, nonatomic) NSMutableDictionary *discountMap; +@property(strong, nonatomic) NSMutableDictionary *discountMissingIdentifierMap; @property(strong, nonatomic) NSMutableDictionary *productMap; @property(strong, nonatomic) NSDictionary *productResponseMap; @property(strong, nonatomic) NSDictionary *paymentMap; @@ -27,13 +28,27 @@ @implementation TranslatorTest - (void)setUp { self.periodMap = @{@"numberOfUnits" : @(0), @"unit" : @(0)}; - self.discountMap = @{ + + self.discountMap = [[NSMutableDictionary alloc] initWithDictionary:@{ @"price" : @"1", @"priceLocale" : [FIAObjectTranslator getMapFromNSLocale:NSLocale.systemLocale], @"numberOfPeriods" : @1, @"subscriptionPeriod" : self.periodMap, - @"paymentMode" : @1 - }; + @"paymentMode" : @1, + }]; + if (@available(iOS 12.2, *)) { + self.discountMap[@"identifier"] = @"test offer id"; + self.discountMap[@"type"] = @(SKProductDiscountTypeIntroductory); + } + self.discountMissingIdentifierMap = [[NSMutableDictionary alloc] initWithDictionary:@{ + @"price" : @"1", + @"priceLocale" : [FIAObjectTranslator getMapFromNSLocale:NSLocale.systemLocale], + @"numberOfPeriods" : @1, + @"subscriptionPeriod" : self.periodMap, + @"paymentMode" : @1, + @"identifier" : [NSNull null], + @"type" : @0, + }]; self.productMap = [[NSMutableDictionary alloc] initWithDictionary:@{ @"price" : @"1", @@ -274,6 +289,15 @@ - (void)testSKPaymentDiscountFromMapMissingIdentifier { } } +- (void)testGetMapFromSKProductDiscountMissingIdentifier { + if (@available(iOS 12.2, *)) { + SKProductDiscountStub *discount = + [[SKProductDiscountStub alloc] initWithMap:self.discountMissingIdentifierMap]; + NSDictionary *map = [FIAObjectTranslator getMapFromSKProductDiscount:discount]; + XCTAssertEqualObjects(map, self.discountMissingIdentifierMap); + } +} + - (void)testSKPaymentDiscountFromMapMissingKeyIdentifier { if (@available(iOS 12.2, *)) { NSArray *invalidValues = @[ [NSNull null], @(1), @"" ]; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAObjectTranslator.m b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAObjectTranslator.m index 5d87a68de67c..d01eb9becf3d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAObjectTranslator.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAObjectTranslator.m @@ -74,8 +74,12 @@ + (NSDictionary *)getMapFromSKProductDiscount:(SKProductDiscount *)discount { @"subscriptionPeriod" : [FIAObjectTranslator getMapFromSKProductSubscriptionPeriod:discount.subscriptionPeriod] ?: [NSNull null], - @"paymentMode" : @(discount.paymentMode) + @"paymentMode" : @(discount.paymentMode), }]; + if (@available(iOS 12.2, *)) { + [map setObject:discount.identifier ?: [NSNull null] forKey:@"identifier"]; + [map setObject:@(discount.type) forKey:@"type"]; + } // TODO(cyanglaz): NSLocale is a complex object, want to see the actual need of getting this // expanded to a map. Matching android to only get the currencySymbol for now. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/enum_converters.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/enum_converters.dart index 1c2bee5a069a..1fdbfebd6ec5 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/enum_converters.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/enum_converters.dart @@ -117,3 +117,27 @@ class _SerializedEnums { late SKSubscriptionPeriodUnit unit; late SKProductDiscountPaymentMode discountPaymentMode; } + +/// Serializer for [SKProductDiscountType]. +/// +/// Use these in `@JsonSerializable()` classes by annotating them with +/// `@SKProductDiscountTypeConverter()`. +class SKProductDiscountTypeConverter + implements JsonConverter { + /// Default const constructor. + const SKProductDiscountTypeConverter(); + + @override + SKProductDiscountType fromJson(int? json) { + if (json == null) { + return SKProductDiscountType.introductory; + } + return $enumDecode( + _$SKProductDiscountTypeEnumMap.cast(), + json); + } + + @override + int toJson(SKProductDiscountType object) => + _$SKProductDiscountTypeEnumMap[object]!; +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/enum_converters.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/enum_converters.g.dart index 0d05720dc7ae..dc6c17276c1c 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/enum_converters.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/enum_converters.g.dart @@ -35,3 +35,8 @@ const _$SKProductDiscountPaymentModeEnumMap = { SKProductDiscountPaymentMode.freeTrail: 2, SKProductDiscountPaymentMode.unspecified: -1, }; + +const _$SKProductDiscountTypeEnumMap = { + SKProductDiscountType.introductory: 0, + SKProductDiscountType.subscription: 1, +}; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart index 2354563261fc..5eace6fda69e 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart @@ -167,6 +167,24 @@ enum SKProductDiscountPaymentMode { unspecified, } +/// Dart wrapper around StoreKit's [SKProductDiscountType] +/// (https://developer.apple.com/documentation/storekit/skproductdiscounttype?language=objc) +/// +/// This is used as a property in the [SKProductDiscountWrapper]. +/// The values of the enum options are matching the [SKProductDiscountType]'s +/// values. +/// +/// Values representing the types of discount offers an app can present. +enum SKProductDiscountType { + /// A constant indicating the discount type is an introductory offer. + @JsonValue(0) + introductory, + + /// A constant indicating the discount type is a promotional offer. + @JsonValue(1) + subscription, +} + /// Dart wrapper around StoreKit's [SKProductDiscount](https://developer.apple.com/documentation/storekit/skproductdiscount?language=objc). /// /// It is used as a property in [SKProductWrapper]. @@ -182,7 +200,9 @@ class SKProductDiscountWrapper { required this.priceLocale, required this.numberOfPeriods, required this.paymentMode, - required this.subscriptionPeriod}); + required this.subscriptionPeriod, + required this.identifier, + required this.type}); /// Constructing an instance from a map from the Objective-C layer. /// @@ -214,6 +234,16 @@ class SKProductDiscountWrapper { /// and their units and duration do not have to be matched. final SKProductSubscriptionPeriodWrapper subscriptionPeriod; + /// A string used to uniquely identify a discount offer for a product. + /// + /// You set up offers and their identifiers in App Store Connect. + @JsonKey(defaultValue: null) + final String? identifier; + + /// Values representing the types of discount offers an app can present. + @SKProductDiscountTypeConverter() + final SKProductDiscountType type; + @override bool operator ==(Object other) { if (identical(other, this)) { @@ -227,12 +257,14 @@ class SKProductDiscountWrapper { other.priceLocale == priceLocale && other.numberOfPeriods == numberOfPeriods && other.paymentMode == paymentMode && - other.subscriptionPeriod == subscriptionPeriod; + other.subscriptionPeriod == subscriptionPeriod && + other.identifier == identifier && + other.type == type; } @override - int get hashCode => Object.hash( - price, priceLocale, numberOfPeriods, paymentMode, subscriptionPeriod); + int get hashCode => Object.hash(price, priceLocale, numberOfPeriods, + paymentMode, subscriptionPeriod, identifier, type); } /// Dart wrapper around StoreKit's [SKProduct](https://developer.apple.com/documentation/storekit/skproduct?language=objc). diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.g.dart index 6eea3ff34da0..9e891e75b497 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.g.dart @@ -42,6 +42,9 @@ SKProductDiscountWrapper _$SKProductDiscountWrapperFromJson(Map json) => (json['subscriptionPeriod'] as Map?)?.map( (k, e) => MapEntry(k as String, e), )), + identifier: json['identifier'] as String? ?? null, + type: + const SKProductDiscountTypeConverter().fromJson(json['type'] as int?), ); SKProductWrapper _$SKProductWrapperFromJson(Map json) => SKProductWrapper( 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 c037cddf147d..f131a411baed 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 platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/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.1+1 +version: 0.3.2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart index 12fb21436ace..de61268e4009 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart @@ -38,6 +38,16 @@ void main() { expect(wrapper, equals(dummyDiscount)); }); + test( + 'SKProductDiscountWrapper missing identifier and type should have ' + 'property values consistent with map', () { + final SKProductDiscountWrapper wrapper = + SKProductDiscountWrapper.fromJson( + buildDiscountMapMissingIdentifierAndType( + dummyDiscountMissingIdentifierAndType)); + expect(wrapper, equals(dummyDiscountMissingIdentifierAndType)); + }); + test( 'SKProductDiscountWrapper should have properties to be default if map is empty', () { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart index e4ef2e3ef432..946fbc81b74c 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart @@ -58,6 +58,19 @@ final SKProductDiscountWrapper dummyDiscount = SKProductDiscountWrapper( numberOfPeriods: 1, paymentMode: SKProductDiscountPaymentMode.payUpFront, subscriptionPeriod: dummySubscription, + identifier: 'id', + type: SKProductDiscountType.subscription, +); + +final SKProductDiscountWrapper dummyDiscountMissingIdentifierAndType = + SKProductDiscountWrapper( + price: '1.0', + priceLocale: dollarLocale, + numberOfPeriods: 1, + paymentMode: SKProductDiscountPaymentMode.payUpFront, + subscriptionPeriod: dummySubscription, + identifier: null, + type: SKProductDiscountType.introductory, ); final SKProductWrapper dummyProductWrapper = SKProductWrapper( @@ -106,6 +119,21 @@ Map buildDiscountMap(SKProductDiscountWrapper discount) { SKProductDiscountPaymentMode.values.indexOf(discount.paymentMode), 'subscriptionPeriod': buildSubscriptionPeriodMap(discount.subscriptionPeriod), + 'identifier': discount.identifier, + 'type': SKProductDiscountType.values.indexOf(discount.type) + }; +} + +Map buildDiscountMapMissingIdentifierAndType( + SKProductDiscountWrapper discount) { + return { + 'price': discount.price, + 'priceLocale': buildLocaleMap(discount.priceLocale), + 'numberOfPeriods': discount.numberOfPeriods, + 'paymentMode': + SKProductDiscountPaymentMode.values.indexOf(discount.paymentMode), + 'subscriptionPeriod': + buildSubscriptionPeriodMap(discount.subscriptionPeriod) }; } From 411d1eda033b7c3c2dc6092199d4b36a1ad63d3e Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Thu, 11 Aug 2022 11:06:52 -0700 Subject: [PATCH 596/844] [path_provider] Bump kotlin to 1.7.10 (#6228) --- packages/path_provider/path_provider_android/CHANGELOG.md | 4 ++++ .../path_provider/path_provider_android/android/build.gradle | 1 + packages/path_provider/path_provider_android/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index e7e6dc20595a..0ea517ed9feb 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.19 + +* Bumps kotlin to 1.7.10 + ## 2.0.18 * Bumps `androidx.annotation:annotation` version to 1.4.0. diff --git a/packages/path_provider/path_provider_android/android/build.gradle b/packages/path_provider/path_provider_android/android/build.gradle index 2fdc53b1f11b..53e39b17ceff 100644 --- a/packages/path_provider/path_provider_android/android/build.gradle +++ b/packages/path_provider/path_provider_android/android/build.gradle @@ -2,6 +2,7 @@ group 'io.flutter.plugins.pathprovider' version '1.0-SNAPSHOT' buildscript { + ext.kotlin_version = '1.7.10' repositories { google() mavenCentral() diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index 232e63cf714f..94ff726c2d46 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.18 +version: 2.0.19 environment: sdk: ">=2.14.0 <3.0.0" From 860112a738bb1bd38d32e6a61620d2bd401ef179 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 11 Aug 2022 14:56:12 -0400 Subject: [PATCH 597/844] [webview_flutter] Maybe fix flakiness of integration tests (#6223) --- .../webview_flutter_test.dart | 105 ++++++++++-------- .../webview_flutter_test.dart | 100 ++++++++--------- .../webview_flutter_test.dart | 32 ++++-- 3 files changed, 129 insertions(+), 108 deletions(-) diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index f080dae6f1bc..8e326fef182f 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -23,8 +23,6 @@ import 'package:webview_flutter/webview_flutter.dart'; Future main() async { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - const bool _skipDueToIssue86757 = true; - final HttpServer server = await HttpServer.bind(InternetAddress.anyIPv4, 0); server.forEach((HttpRequest request) { if (request.uri.path == '/hello.txt') { @@ -45,10 +43,10 @@ Future main() async { final String secondaryUrl = '$prefixUrl/secondary.txt'; final String headersUrl = '$prefixUrl/headers'; - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('initialUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); + final Completer pageFinishedCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -58,18 +56,22 @@ Future main() async { onWebViewCreated: (WebViewController controller) { controllerCompleter.complete(controller); }, + onPageFinished: pageFinishedCompleter.complete, ), ), ); + final WebViewController controller = await controllerCompleter.future; + await pageFinishedCompleter.future; + final String? currentUrl = await controller.currentUrl(); expect(currentUrl, primaryUrl); - }, skip: _skipDueToIssue86757); + }); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('loadUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); + final StreamController pageLoads = StreamController(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -79,14 +81,20 @@ Future main() async { onWebViewCreated: (WebViewController controller) { controllerCompleter.complete(controller); }, + onPageFinished: (String url) { + pageLoads.add(url); + }, ), ), ); final WebViewController controller = await controllerCompleter.future; + await controller.loadUrl(secondaryUrl); - final String? currentUrl = await controller.currentUrl(); - expect(currentUrl, secondaryUrl); - }, skip: _skipDueToIssue86757); + await expectLater( + pageLoads.stream.firstWhere((String url) => url == secondaryUrl), + completion(secondaryUrl), + ); + }); testWidgets('evaluateJavascript', (WidgetTester tester) async { final Completer controllerCompleter = @@ -110,7 +118,6 @@ Future main() async { expect(result, equals('2')); }); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('loadUrl with headers', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); @@ -149,15 +156,14 @@ Future main() async { final String content = await controller .runJavascriptReturningResult('document.documentElement.innerText'); expect(content.contains('flutter_test_header'), isTrue); - }, skip: Platform.isAndroid && _skipDueToIssue86757); + }); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('JavascriptChannel', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageStarted = Completer(); final Completer pageLoaded = Completer(); - final List messagesReceived = []; + final Completer channelCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -174,7 +180,7 @@ Future main() async { JavascriptChannel( name: 'Echo', onMessageReceived: (JavascriptMessage message) { - messagesReceived.add(message.message); + channelCompleter.complete(message.message); }, ), }, @@ -191,10 +197,11 @@ Future main() async { await pageStarted.future; await pageLoaded.future; - expect(messagesReceived, isEmpty); + expect(channelCompleter.isCompleted, isFalse); await controller.runJavascript('Echo.postMessage("hello");'); - expect(messagesReceived, equals(['hello'])); - }, skip: Platform.isAndroid && _skipDueToIssue86757); + + await expectLater(channelCompleter.future, completion('hello')); + }); testWidgets('resize webview', (WidgetTester tester) async { final Completer initialResizeCompleter = Completer(); @@ -263,7 +270,6 @@ Future main() async { expect(customUserAgent2, 'Custom_User_Agent2'); }); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('use default platform userAgent after webView is rebuilt', (WidgetTester tester) async { final Completer controllerCompleter = @@ -313,7 +319,7 @@ Future main() async { final String customUserAgent2 = await _getUserAgent(controller); expect(customUserAgent2, defaultPlatformUserAgent); - }, skip: Platform.isAndroid && _skipDueToIssue86757); + }); group('Video playback policy', () { late String videoTestBase64; @@ -789,7 +795,6 @@ Future main() async { }); group('Programmatic Scroll', () { - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('setAndGetScrollPosition', (WidgetTester tester) async { const String scrollTestPage = ''' @@ -864,7 +869,7 @@ Future main() async { scrollPosY = await controller.getScrollY(); expect(scrollPosX, X_SCROLL * 2); expect(scrollPosY, Y_SCROLL * 2); - }, skip: Platform.isAndroid && _skipDueToIssue86757); + }); }); // Minimal end-to-end testing of the legacy Android implementation. @@ -880,7 +885,7 @@ Future main() async { testWidgets('initialUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); - final Completer loadCompleter = Completer(); + final Completer pageFinishedCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -890,18 +895,18 @@ Future main() async { onWebViewCreated: (WebViewController controller) { controllerCompleter.complete(controller); }, - onPageFinished: (String url) { - loadCompleter.complete(); - }, + onPageFinished: pageFinishedCompleter.complete, ), ), ); + final WebViewController controller = await controllerCompleter.future; - await loadCompleter.future; + await pageFinishedCompleter.future; + final String? currentUrl = await controller.currentUrl(); expect(currentUrl, primaryUrl); }); - }, skip: !Platform.isAndroid || _skipDueToIssue86757); + }, skip: !Platform.isAndroid); group('NavigationDelegate', () { const String blankPage = ''; @@ -1140,7 +1145,6 @@ Future main() async { expect(currentUrl, primaryUrl); }); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('target _blank opens in same window', (WidgetTester tester) async { final Completer controllerCompleter = @@ -1166,9 +1170,8 @@ Future main() async { await pageLoaded.future; final String? currentUrl = await controller.currentUrl(); expect(currentUrl, primaryUrl); - }, skip: Platform.isAndroid && _skipDueToIssue86757); + }); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets( 'can open new window and go back', (WidgetTester tester) async { @@ -1204,9 +1207,8 @@ Future main() async { expect(controller.canGoBack(), completion(true)); await controller.goBack(); await pageLoaded.future; - expect(controller.currentUrl(), completion(primaryUrl)); + await expectLater(controller.currentUrl(), completion(primaryUrl)); }, - skip: _skipDueToIssue86757, ); testWidgets( @@ -1214,7 +1216,9 @@ Future main() async { (WidgetTester tester) async { final Completer controllerCompleter = Completer(); - final Completer onPageFinished = Completer(); + + Completer pageLoadCompleter = Completer(); + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1222,7 +1226,7 @@ Future main() async { key: GlobalKey(), initialUrl: primaryUrl, javascriptMode: JavascriptMode.unrestricted, - onPageFinished: (_) => onPageFinished.complete(), + onPageFinished: (_) => pageLoadCompleter.complete(), onWebViewCreated: (WebViewController controller) { controllerCompleter.complete(controller); }, @@ -1230,29 +1234,34 @@ Future main() async { ), ); - final WebViewController controller = await controllerCompleter.future; - await onPageFinished.future; + await pageLoadCompleter.future; + pageLoadCompleter = Completer(); + final WebViewController controller = await controllerCompleter.future; await controller.runJavascript('localStorage.setItem("myCat", "Tom");'); - - expect( - controller.runJavascriptReturningResult( - 'localStorage.getItem("myCat");', - ), - completion(_webviewString('Tom')), + final String myCatItem = await controller.runJavascriptReturningResult( + 'localStorage.getItem("myCat");', ); + expect(myCatItem, _webviewString('Tom')); await controller.clearCache(); + await pageLoadCompleter.future; - expect( - controller.runJavascriptReturningResult( + late final String? nullItem; + try { + nullItem = await controller.runJavascriptReturningResult( 'localStorage.getItem("myCat");', - ), - completion(_webviewNull()), - ); + ); + } catch (exception) { + if (defaultTargetPlatform == TargetPlatform.iOS && + exception is ArgumentError && + (exception.message as String).contains( + 'Result of JavaScript execution returned a `null` value.')) { + nullItem = ''; + } + } + expect(nullItem, _webviewNull()); }, - // TODO(bparrishMines): Unskip once https://github.com/flutter/plugins/pull/5086 lands and is published. - skip: Platform.isAndroid, ); } 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 8cad944e5455..b7c32f5d77d3 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 @@ -28,8 +28,6 @@ import 'package:webview_flutter_platform_interface/webview_flutter_platform_inte Future main() async { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - const bool _skipDueToIssue86757 = true; - final HttpServer server = await HttpServer.bind(InternetAddress.anyIPv4, 0); server.forEach((HttpRequest request) { if (request.uri.path == '/hello.txt') { @@ -53,6 +51,7 @@ Future main() async { testWidgets('initialUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); + final Completer pageFinishedCompleter = Completer(); await tester.pumpWidget( MaterialApp( home: Directionality( @@ -63,19 +62,23 @@ Future main() async { onWebViewCreated: (WebViewController controller) { controllerCompleter.complete(controller); }, + onPageFinished: pageFinishedCompleter.complete, ), ), ), ); + final WebViewController controller = await controllerCompleter.future; + await pageFinishedCompleter.future; + final String? currentUrl = await controller.currentUrl(); expect(currentUrl, primaryUrl); - }, skip: _skipDueToIssue86757); + }); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('loadUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); + final StreamController pageLoads = StreamController(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -85,14 +88,20 @@ Future main() async { onWebViewCreated: (WebViewController controller) { controllerCompleter.complete(controller); }, + onPageFinished: (String url) { + pageLoads.add(url); + }, ), ), ); final WebViewController controller = await controllerCompleter.future; + await controller.loadUrl(secondaryUrl); - final String? currentUrl = await controller.currentUrl(); - expect(currentUrl, secondaryUrl); - }, skip: _skipDueToIssue86757); + await expectLater( + pageLoads.stream.firstWhere((String url) => url == secondaryUrl), + completion(secondaryUrl), + ); + }); testWidgets('evaluateJavascript', (WidgetTester tester) async { final Completer controllerCompleter = @@ -115,7 +124,6 @@ Future main() async { expect(result, equals('2')); }); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('loadUrl with headers', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); @@ -154,15 +162,14 @@ Future main() async { final String content = await controller .runJavascriptReturningResult('document.documentElement.innerText'); expect(content.contains('flutter_test_header'), isTrue); - }, skip: _skipDueToIssue86757); + }); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('JavascriptChannel', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageStarted = Completer(); final Completer pageLoaded = Completer(); - final List messagesReceived = []; + final Completer channelCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -179,7 +186,7 @@ Future main() async { JavascriptChannel( name: 'Echo', onMessageReceived: (JavascriptMessage message) { - messagesReceived.add(message.message); + channelCompleter.complete(message.message); }, ), }, @@ -196,10 +203,11 @@ Future main() async { await pageStarted.future; await pageLoaded.future; - expect(messagesReceived, isEmpty); + expect(channelCompleter.isCompleted, isFalse); await controller.runJavascript('Echo.postMessage("hello");'); - expect(messagesReceived, equals(['hello'])); - }, skip: _skipDueToIssue86757); + + await expectLater(channelCompleter.future, completion('hello')); + }); testWidgets('resize webview', (WidgetTester tester) async { final Completer initialResizeCompleter = Completer(); @@ -268,7 +276,6 @@ Future main() async { expect(customUserAgent2, 'Custom_User_Agent2'); }); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('use default platform userAgent after webView is rebuilt', (WidgetTester tester) async { final Completer controllerCompleter = @@ -318,7 +325,7 @@ Future main() async { final String customUserAgent2 = await _getUserAgent(controller); expect(customUserAgent2, defaultPlatformUserAgent); - }, skip: _skipDueToIssue86757); + }); group('Video playback policy', () { late String videoTestBase64; @@ -736,7 +743,6 @@ Future main() async { }); group('Programmatic Scroll', () { - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('setAndGetScrollPosition', (WidgetTester tester) async { const String scrollTestPage = ''' @@ -811,7 +817,7 @@ Future main() async { scrollPosY = await controller.getScrollY(); expect(scrollPosX, X_SCROLL * 2); expect(scrollPosY, Y_SCROLL * 2); - }, skip: _skipDueToIssue86757); + }); }); group('SurfaceAndroidWebView', () { @@ -823,7 +829,6 @@ Future main() async { WebView.platform = AndroidWebView(); }); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('setAndGetScrollPosition', (WidgetTester tester) async { const String scrollTestPage = ''' @@ -890,9 +895,8 @@ Future main() async { scrollPosY = await controller.getScrollY(); expect(X_SCROLL * 2, scrollPosX); expect(Y_SCROLL * 2, scrollPosY); - }, skip: _skipDueToIssue86757); + }); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets('inputs are scrolled into view when focused', (WidgetTester tester) async { const String scrollTestPage = ''' @@ -998,7 +1002,7 @@ Future main() async { lastInputClientRectRelativeToViewport['right'] <= viewportRectRelativeToViewport['right'], isTrue); - }, skip: _skipDueToIssue86757); + }); }); group('NavigationDelegate', () { @@ -1258,11 +1262,8 @@ Future main() async { await pageLoaded.future; final String? currentUrl = await controller.currentUrl(); expect(currentUrl, primaryUrl); - }, - // Flaky on Android: https://github.com/flutter/flutter/issues/86757 - skip: _skipDueToIssue86757); + }); - // TODO(bparrishMines): skipped due to https://github.com/flutter/flutter/issues/86757 testWidgets( 'can open new window and go back', (WidgetTester tester) async { @@ -1298,9 +1299,8 @@ Future main() async { expect(controller.canGoBack(), completion(true)); await controller.goBack(); await pageLoaded.future; - expect(controller.currentUrl(), completion(primaryUrl)); + await expectLater(controller.currentUrl(), completion(primaryUrl)); }, - skip: _skipDueToIssue86757, ); testWidgets( @@ -1361,13 +1361,14 @@ Future main() async { final WebViewController controller = await controllerCompleter.future; await pageLoadCompleter.future; - expect(controller.runJavascriptReturningResult('iframeLoaded'), - completion('true')); - expect( - controller.runJavascriptReturningResult( - 'document.querySelector("p") && document.querySelector("p").textContent'), - completion('null'), + final String iframeLoaded = + await controller.runJavascriptReturningResult('iframeLoaded'); + expect(iframeLoaded, 'true'); + + final String elementText = await controller.runJavascriptReturningResult( + 'document.querySelector("p") && document.querySelector("p").textContent', ); + expect(elementText, 'null'); }, ); @@ -1376,7 +1377,9 @@ Future main() async { (WidgetTester tester) async { final Completer controllerCompleter = Completer(); - final Completer onPageFinished = Completer(); + + Completer pageLoadCompleter = Completer(); + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -1384,7 +1387,7 @@ Future main() async { key: GlobalKey(), initialUrl: primaryUrl, javascriptMode: JavascriptMode.unrestricted, - onPageFinished: (_) => onPageFinished.complete(), + onPageFinished: (_) => pageLoadCompleter.complete(), onWebViewCreated: (WebViewController controller) { controllerCompleter.complete(controller); }, @@ -1392,26 +1395,23 @@ Future main() async { ), ); - final WebViewController controller = await controllerCompleter.future; - await onPageFinished.future; + await pageLoadCompleter.future; + pageLoadCompleter = Completer(); + final WebViewController controller = await controllerCompleter.future; await controller.runJavascript('localStorage.setItem("myCat", "Tom");'); - - expect( - controller.runJavascriptReturningResult( - 'localStorage.getItem("myCat");', - ), - completion('"Tom"'), + final String myCatItem = await controller.runJavascriptReturningResult( + 'localStorage.getItem("myCat");', ); + expect(myCatItem, '"Tom"'); await controller.clearCache(); + await pageLoadCompleter.future; - expect( - controller.runJavascriptReturningResult( - 'localStorage.getItem("myCat");', - ), - completion('null'), + final String nullItem = await controller.runJavascriptReturningResult( + 'localStorage.getItem("myCat");', ); + expect(nullItem, 'null'); }, ); } 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 363d046da684..e259bc72a67b 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 @@ -51,6 +51,7 @@ Future main() async { testWidgets('initialUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); + final Completer pageFinishedCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -60,10 +61,14 @@ Future main() async { onWebViewCreated: (WebViewController controller) { controllerCompleter.complete(controller); }, + onPageFinished: pageFinishedCompleter.complete, ), ), ); + final WebViewController controller = await controllerCompleter.future; + await pageFinishedCompleter.future; + final String? currentUrl = await controller.currentUrl(); expect(currentUrl, primaryUrl); }); @@ -86,12 +91,14 @@ Future main() async { await tester.pumpAndSettle(); }); - expect(gcCompleter.future, completion(0)); + final int gcIdentifier = await gcCompleter.future; + expect(gcIdentifier, 0); }, timeout: const Timeout(Duration(seconds: 10))); testWidgets('loadUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); + final StreamController pageLoads = StreamController(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -101,13 +108,19 @@ Future main() async { onWebViewCreated: (WebViewController controller) { controllerCompleter.complete(controller); }, + onPageFinished: (String url) { + pageLoads.add(url); + }, ), ), ); final WebViewController controller = await controllerCompleter.future; + await controller.loadUrl(secondaryUrl); - final String? currentUrl = await controller.currentUrl(); - expect(currentUrl, secondaryUrl); + await expectLater( + pageLoads.stream.firstWhere((String url) => url == secondaryUrl), + completion(secondaryUrl), + ); }); testWidgets('evaluateJavascript', (WidgetTester tester) async { @@ -176,7 +189,7 @@ Future main() async { Completer(); final Completer pageStarted = Completer(); final Completer pageLoaded = Completer(); - final List messagesReceived = []; + final Completer channelCompleter = Completer(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, @@ -193,7 +206,7 @@ Future main() async { JavascriptChannel( name: 'Echo', onMessageReceived: (JavascriptMessage message) { - messagesReceived.add(message.message); + channelCompleter.complete(message.message); }, ), }, @@ -210,9 +223,10 @@ Future main() async { await pageStarted.future; await pageLoaded.future; - expect(messagesReceived, isEmpty); + expect(channelCompleter.isCompleted, isFalse); await controller.runJavascript('Echo.postMessage("hello");'); - expect(messagesReceived, equals(['hello'])); + + await expectLater(channelCompleter.future, completion('hello')); }); testWidgets('resize webview', (WidgetTester tester) async { @@ -1174,10 +1188,8 @@ Future main() async { expect(controller.canGoBack(), completion(true)); await controller.goBack(); await pageLoaded.future; - expect(controller.currentUrl(), completion(primaryUrl)); + await expectLater(controller.currentUrl(), completion(primaryUrl)); }, - // Flaky; see https://github.com/flutter/flutter/issues/90976 - skip: true, ); } From c36100999c02f0bd86077e2becfc151e448ae747 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 11 Aug 2022 14:56:36 -0400 Subject: [PATCH 598/844] [webview_flutter_wkwebview] Remove flaky UI tests (#6227) --- .../ios/RunnerUITests/FLTWebViewUITests.m | 81 ------------------- 1 file changed, 81 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerUITests/FLTWebViewUITests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerUITests/FLTWebViewUITests.m index 689a601d7bdc..a9ffc287a2b5 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerUITests/FLTWebViewUITests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerUITests/FLTWebViewUITests.m @@ -84,85 +84,4 @@ - (void)testTransparentBackground { XCTAssertTrue(CGColorEqualToColor(flutterGreenColor, centerLeftColor.CGColor)); XCTAssertTrue(CGColorEqualToColor(redColor, centerColor.CGColor)); } - -- (void)testUserAgent { - XCUIApplication *app = self.app; - XCUIElement *menu = app.buttons[@"Show menu"]; - if (![menu waitForExistenceWithTimeout:30.0]) { - os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); - XCTFail(@"Failed due to not able to find menu"); - } - [menu tap]; - - XCUIElement *userAgent = app.buttons[@"Show user agent"]; - if (![userAgent waitForExistenceWithTimeout:30.0]) { - os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); - XCTFail(@"Failed due to not able to find Show user agent"); - } - NSPredicate *userAgentPredicate = - [NSPredicate predicateWithFormat:@"label BEGINSWITH 'User Agent: Mozilla/5.0 (iPhone; '"]; - XCUIElement *userAgentPopUp = [app.otherElements elementMatchingPredicate:userAgentPredicate]; - XCTAssertFalse(userAgentPopUp.exists); - [userAgent tap]; - if (![userAgentPopUp waitForExistenceWithTimeout:30.0]) { - os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); - XCTFail(@"Failed due to not able to find user agent pop up"); - } -} - -- (void)testCache { - XCUIApplication *app = self.app; - XCUIElement *menu = app.buttons[@"Show menu"]; - if (![menu waitForExistenceWithTimeout:30.0]) { - os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); - XCTFail(@"Failed due to not able to find menu"); - } - [menu tap]; - - XCUIElement *clearCache = app.buttons[@"Clear cache"]; - if (![clearCache waitForExistenceWithTimeout:30.0]) { - os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); - XCTFail(@"Failed due to not able to find Clear cache"); - } - [clearCache tap]; - - [menu tap]; - - XCUIElement *listCache = app.buttons[@"List cache"]; - if (![listCache waitForExistenceWithTimeout:30.0]) { - os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); - XCTFail(@"Failed due to not able to find List cache"); - } - [listCache tap]; - - XCUIElement *emptyCachePopup = app.otherElements[@"{\"cacheKeys\":[],\"localStorage\":{}}"]; - if (![emptyCachePopup waitForExistenceWithTimeout:30.0]) { - os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); - XCTFail(@"Failed due to not able to find empty cache pop up"); - } - - [menu tap]; - XCUIElement *addCache = app.buttons[@"Add to cache"]; - if (![addCache waitForExistenceWithTimeout:30.0]) { - os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); - XCTFail(@"Failed due to not able to find Add to cache"); - } - [addCache tap]; - [menu tap]; - - if (![listCache waitForExistenceWithTimeout:30.0]) { - os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); - XCTFail(@"Failed due to not able to find List cache"); - } - [listCache tap]; - - XCUIElement *cachePopup = - app.otherElements[@"{\"cacheKeys\":[\"test_caches_entry\"],\"localStorage\":{\"test_" - @"localStorage\":\"dummy_entry\"}}"]; - if (![cachePopup waitForExistenceWithTimeout:30.0]) { - os_log_error(OS_LOG_DEFAULT, "%@", app.debugDescription); - XCTFail(@"Failed due to not able to find cache pop up"); - } -} - @end From 9fb7654f3aa2cb061ece8067050e135500837657 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 11 Aug 2022 15:47:45 -0400 Subject: [PATCH 599/844] Roll Flutter from ac2f6ed23784 to 9ad052a57e0f (21 revisions) (#6226) --- .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 83cfebd06d69..9c00d6d8d192 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ac2f6ed23784b0be04e2ad734334b2d993c27a28 +9ad052a57e0f08f05c8f126f80b59dc06e2b81af From 2b1ace0cc5d35707ab6cb9d23e5d8d320a9243b0 Mon Sep 17 00:00:00 2001 From: yusuf-goog <91688203+yusuf-goog@users.noreply.github.com> Date: Fri, 12 Aug 2022 12:14:31 -0700 Subject: [PATCH 600/844] Updating expiring cirrus key. (#6237) --- .cirrus.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 3d0c525a5c42..1ac69d6484f0 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,4 +1,4 @@ -gcp_credentials: ENCRYPTED[!ebad0a1f4f7a446b77944c33651460a7ab010b4617273cb016cf354eb8fc22aa92e37a3c58bfa4a0c40a799351e027a6!] +gcp_credentials: ENCRYPTED[!f1177d1ddb5330ffaa9ea11c9c9e8e0c542185e895c36071f18cec923dd31c50ece6d18da89c2f6f1cd2d1a98d0c2eea!] # Run on PRs and main branch post submit only. Don't run tests when tagging. only_if: $CIRRUS_TAG == '' && ($CIRRUS_PR != '' || $CIRRUS_BRANCH == 'main') @@ -246,7 +246,7 @@ task: CHANNEL: "master" CHANNEL: "stable" MAPS_API_KEY: ENCRYPTED[596a9f6bca436694625ac50851dc5da6b4d34cba8025f7db5bc9465142e8cd44e15f69e3507787753accebfc4910d550] - GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[c84a06b85f9c906705732aea6142ef6f63ff1a6f07372dc36880a9d0c2c4b9cb35b2e35cd19edc6285167c2a5cc075ec] + GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[30e6cf7189e3ff3868edc25d2e638ef2aec70546456427064bbc74b297d36145364f49f9d26b327787a59df149d69262] build_script: # Unsetting CIRRUS_CHANGE_MESSAGE and CIRRUS_COMMIT_MESSAGE as they # might include non-ASCII characters which makes Gradle crash. From 16297e34293f37baa04ad425e8b2e201e1456788 Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Fri, 12 Aug 2022 14:02:36 -0700 Subject: [PATCH 601/844] [path_provider] Revert Gradle and androidx.annotation bump in path_provider_android (#6231) --- packages/path_provider/path_provider_android/CHANGELOG.md | 4 ++++ .../path_provider/path_provider_android/android/build.gradle | 5 ++--- packages/path_provider/path_provider_android/pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index 0ea517ed9feb..63a731acbd91 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.20 + +* Reverts changes in versions 2.0.18 and 2.0.19. + ## 2.0.19 * Bumps kotlin to 1.7.10 diff --git a/packages/path_provider/path_provider_android/android/build.gradle b/packages/path_provider/path_provider_android/android/build.gradle index 53e39b17ceff..4bfa738ac44c 100644 --- a/packages/path_provider/path_provider_android/android/build.gradle +++ b/packages/path_provider/path_provider_android/android/build.gradle @@ -2,14 +2,13 @@ group 'io.flutter.plugins.pathprovider' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.7.10' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.2.2' + classpath 'com.android.tools.build:gradle:3.3.0' } } @@ -53,7 +52,7 @@ android { } dependencies { - implementation 'androidx.annotation:annotation:1.4.0' + implementation 'androidx.annotation:annotation:1.1.0' implementation 'com.google.guava:guava:28.1-android' testImplementation 'junit:junit:4.13.2' } diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index 94ff726c2d46..c442d16a6889 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_android description: Android implementation of the path_provider plugin. repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.0.19 +version: 2.0.20 environment: sdk: ">=2.14.0 <3.0.0" From 23677a25ca9dfcebb6a0b5eb59cfc4bbe420c76a Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Fri, 12 Aug 2022 17:53:33 -0400 Subject: [PATCH 602/844] [webview_flutter_android] Adds dispose methods for HostApi and FlutterApi of JavaObject (#6172) --- .../webview_flutter_android/CHANGELOG.md | 4 + .../GeneratedAndroidWebView.java | 83 ++++++++++++++++++- .../webviewflutter/JavaObjectHostApiImpl.java | 32 +++++++ .../webviewflutter/JavaObjectHostApiTest.java | 32 +++++++ .../lib/src/android_webview.dart | 24 +++++- .../lib/src/android_webview.pigeon.dart | 74 ++++++++++++++++- .../lib/src/android_webview_api_impls.dart | 41 +++++++++ .../pigeons/android_webview.dart | 18 ++++ .../webview_flutter_android/pubspec.yaml | 2 +- .../test/android_webview_test.dart | 53 ++++++++++++ .../test/android_webview_test.mocks.dart | 15 ++++ .../test/test_android_webview.pigeon.dart | 34 +++++++- 12 files changed, 406 insertions(+), 6 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaObjectHostApiImpl.java create mode 100644 packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaObjectHostApiTest.java diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index bc64a6f1c0ed..3d3731979866 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.9.5 + +* Adds dispose methods for HostApi and FlutterApi of JavaObject. + ## 2.9.4 * Fixes avoid_redundant_argument_values lint warnings and minor typos. 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 2e163311d6d4..34fd8b79b315 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 @@ -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 (v3.0.3), do not edit directly. +// Autogenerated from Pigeon (v3.2.0), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.webviewflutter; @@ -271,6 +271,87 @@ public interface Result { void error(Throwable error); } + private static class JavaObjectHostApiCodec extends StandardMessageCodec { + public static final JavaObjectHostApiCodec INSTANCE = new JavaObjectHostApiCodec(); + + private JavaObjectHostApiCodec() {} + } + + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface JavaObjectHostApi { + void dispose(@NonNull Long identifier); + + /** The codec used by JavaObjectHostApi. */ + static MessageCodec getCodec() { + return JavaObjectHostApiCodec.INSTANCE; + } + + /** + * Sets up an instance of `JavaObjectHostApi` to handle messages through the `binaryMessenger`. + */ + static void setup(BinaryMessenger binaryMessenger, JavaObjectHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.JavaObjectHostApi.dispose", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + api.dispose((identifierArg == null) ? null : identifierArg.longValue()); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } + + private static class JavaObjectFlutterApiCodec extends StandardMessageCodec { + public static final JavaObjectFlutterApiCodec INSTANCE = new JavaObjectFlutterApiCodec(); + + private JavaObjectFlutterApiCodec() {} + } + + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ + public static class JavaObjectFlutterApi { + private final BinaryMessenger binaryMessenger; + + public JavaObjectFlutterApi(BinaryMessenger argBinaryMessenger) { + this.binaryMessenger = argBinaryMessenger; + } + + public interface Reply { + void reply(T reply); + } + + static MessageCodec getCodec() { + return JavaObjectFlutterApiCodec.INSTANCE; + } + + public void dispose(@NonNull Long identifierArg, Reply callback) { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.JavaObjectFlutterApi.dispose", getCodec()); + channel.send( + new ArrayList(Arrays.asList(identifierArg)), + channelReply -> { + callback.reply(null); + }); + } + } + private static class CookieManagerHostApiCodec extends StandardMessageCodec { public static final CookieManagerHostApiCodec INSTANCE = new CookieManagerHostApiCodec(); diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaObjectHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaObjectHostApiImpl.java new file mode 100644 index 000000000000..978e5232657d --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaObjectHostApiImpl.java @@ -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. + +package io.flutter.plugins.webviewflutter; + +import androidx.annotation.NonNull; + +/** + * A pigeon Host API implementation that handles creating {@link Object}s and invoking its static + * and instance methods. + * + *

{@link Object} instances created by {@link JavaObjectHostApiImpl} are used to intercommunicate + * with a paired Dart object. + */ +public class JavaObjectHostApiImpl implements GeneratedAndroidWebView.JavaObjectHostApi { + private final InstanceManager instanceManager; + + /** + * Constructs a {@link JavaObjectHostApiImpl}. + * + * @param instanceManager maintains instances stored to communicate with Dart objects + */ + public JavaObjectHostApiImpl(InstanceManager instanceManager) { + this.instanceManager = instanceManager; + } + + @Override + public void dispose(@NonNull Long identifier) { + instanceManager.remove(identifier); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaObjectHostApiTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaObjectHostApiTest.java new file mode 100644 index 000000000000..8ac349e76418 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaObjectHostApiTest.java @@ -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. + +package io.flutter.plugins.webviewflutter; + +import static org.junit.Assert.assertNull; + +import org.junit.Test; + +public class JavaObjectHostApiTest { + @Test + public void dispose() { + final InstanceManager instanceManager = InstanceManager.open(identifier -> {}); + + final JavaObjectHostApiImpl hostApi = new JavaObjectHostApiImpl(instanceManager); + + Object object = new Object(); + instanceManager.addDartCreatedInstance(object, 0); + + // To free object for garbage collection. + //noinspection UnusedAssignment + object = null; + + hostApi.dispose(0L); + Runtime.getRuntime().gc(); + + assertNull(instanceManager.getInstance(0)); + + instanceManager.close(); + } +} 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 f2e305fe85c5..30c59ea43d2c 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 @@ -13,6 +13,7 @@ import 'dart:typed_data'; import 'dart:ui'; import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart' show BinaryMessenger; import 'package:flutter/widgets.dart' show AndroidViewSurface; import 'android_webview.pigeon.dart'; @@ -22,17 +23,36 @@ import 'instance_manager.dart'; /// Root of the Java class hierarchy. /// /// See https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html. -abstract class JavaObject with Copyable { +class JavaObject with Copyable { /// Constructs a [JavaObject] without creating the associated Java object. /// /// This should only be used by subclasses created by this library or to /// create copies. - JavaObject.detached(); + JavaObject.detached({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _api = JavaObjectHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); /// Global instance of [InstanceManager]. static final InstanceManager globalInstanceManager = InstanceManager( onWeakReferenceRemoved: (_) {}, ); + + /// Pigeon Host Api implementation for [JavaObject]. + final JavaObjectHostApiImpl _api; + + /// Release the reference to a native Java instance. + static void dispose(JavaObject instance) { + instance._api.instanceManager.removeWeakReference(instance); + } + + @override + JavaObject copy() { + return JavaObject.detached(); + } } /// An Android View that displays web pages. diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart index 4491e162ce9c..f7c83b79d25a 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.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 (v3.0.3), do not edit directly. +// Autogenerated from Pigeon (v3.2.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 // @dart = 2.12 @@ -78,6 +78,78 @@ class WebResourceErrorData { } } +class _JavaObjectHostApiCodec extends StandardMessageCodec { + const _JavaObjectHostApiCodec(); +} + +class JavaObjectHostApi { + /// Constructor for [JavaObjectHostApi]. 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. + JavaObjectHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _JavaObjectHostApiCodec(); + + Future dispose(int arg_identifier) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.JavaObjectHostApi.dispose', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_identifier]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _JavaObjectFlutterApiCodec extends StandardMessageCodec { + const _JavaObjectFlutterApiCodec(); +} + +abstract class JavaObjectFlutterApi { + static const MessageCodec codec = _JavaObjectFlutterApiCodec(); + + void dispose(int identifier); + static void setup(JavaObjectFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.JavaObjectFlutterApi.dispose', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.JavaObjectFlutterApi.dispose 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.JavaObjectFlutterApi.dispose was null, expected non-null int.'); + api.dispose(arg_identifier!); + return; + }); + } + } + } +} + class _CookieManagerHostApiCodec extends StandardMessageCodec { const _CookieManagerHostApiCodec(); } 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 c3e2096bbe0f..00ededabd977 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 @@ -36,11 +36,14 @@ WebResourceError _toWebResourceError(WebResourceErrorData data) { class AndroidWebViewFlutterApis { /// Creates a [AndroidWebViewFlutterApis]. AndroidWebViewFlutterApis({ + JavaObjectFlutterApiImpl? javaObjectFlutterApi, DownloadListenerFlutterApiImpl? downloadListenerFlutterApi, WebViewClientFlutterApiImpl? webViewClientFlutterApi, WebChromeClientFlutterApiImpl? webChromeClientFlutterApi, JavaScriptChannelFlutterApiImpl? javaScriptChannelFlutterApi, }) { + this.javaObjectFlutterApi = + javaObjectFlutterApi ?? JavaObjectFlutterApiImpl(); this.downloadListenerFlutterApi = downloadListenerFlutterApi ?? DownloadListenerFlutterApiImpl(); this.webViewClientFlutterApi = @@ -58,6 +61,9 @@ class AndroidWebViewFlutterApis { /// This should only be changed for testing purposes. static AndroidWebViewFlutterApis instance = AndroidWebViewFlutterApis(); + /// Handles callbacks methods for the native Java Object class. + late final JavaObjectFlutterApi javaObjectFlutterApi; + /// Flutter Api for [DownloadListener]. late final DownloadListenerFlutterApiImpl downloadListenerFlutterApi; @@ -73,6 +79,7 @@ class AndroidWebViewFlutterApis { /// Ensures all the Flutter APIs have been setup to receive calls from native code. void ensureSetUp() { if (!_haveBeenSetUp) { + JavaObjectFlutterApi.setup(javaObjectFlutterApi); DownloadListenerFlutterApi.setup(downloadListenerFlutterApi); WebViewClientFlutterApi.setup(webViewClientFlutterApi); WebChromeClientFlutterApi.setup(webChromeClientFlutterApi); @@ -82,6 +89,40 @@ class AndroidWebViewFlutterApis { } } +/// Handles methods calls to the native Java Object class. +class JavaObjectHostApiImpl extends JavaObjectHostApi { + /// Constructs a [JavaObjectHostApiImpl]. + JavaObjectHostApiImpl({ + this.binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); + + /// Receives binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; +} + +/// Handles callbacks methods for the native Java Object class. +class JavaObjectFlutterApiImpl implements JavaObjectFlutterApi { + /// Constructs a [JavaObjectFlutterApiImpl]. + JavaObjectFlutterApiImpl({InstanceManager? instanceManager}) + : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; + + @override + void dispose(int identifier) { + instanceManager.remove(identifier); + } +} + /// Host api implementation for [WebView]. class WebViewHostApiImpl extends WebViewHostApi { /// Constructs a [WebViewHostApiImpl]. 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 70ecd99d3638..eb979870a1d3 100644 --- a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart @@ -51,6 +51,24 @@ class WebResourceErrorData { String description; } +/// Handles methods calls to the native Java Object class. +/// +/// Also handles calls to remove the reference to an instance with `dispose`. +/// +/// See https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html. +@HostApi(dartHostTestHandler: 'TestJavaObjectHostApi') +abstract class JavaObjectHostApi { + void dispose(int identifier); +} + +/// Handles callbacks methods for the native Java Object class. +/// +/// See https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html. +@FlutterApi() +abstract class JavaObjectFlutterApi { + void dispose(int identifier); +} + @HostApi() abstract class CookieManagerHostApi { @async diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 70ca71de50db..bbff4fe7dce5 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/plugins/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: 2.9.4 +version: 2.9.5 environment: sdk: ">=2.14.0 <3.0.0" 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 fdb98fc43dbe..d57eadc1d5cc 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 @@ -18,6 +18,7 @@ import 'test_android_webview.pigeon.dart'; DownloadListener, JavaScriptChannel, TestDownloadListenerHostApi, + TestJavaObjectHostApi, TestJavaScriptChannelHostApi, TestWebChromeClientHostApi, TestWebSettingsHostApi, @@ -33,6 +34,58 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('Android WebView', () { + group('JavaObject', () { + late MockTestJavaObjectHostApi mockPlatformHostApi; + + setUp(() { + mockPlatformHostApi = MockTestJavaObjectHostApi(); + TestJavaObjectHostApi.setup(mockPlatformHostApi); + }); + + tearDown(() { + TestJavaObjectHostApi.setup(null); + }); + + test('JavaObject.dispose', () async { + int? callbackIdentifier; + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (int identifier) { + callbackIdentifier = identifier; + }, + ); + + final JavaObject object = JavaObject.detached( + instanceManager: instanceManager, + ); + instanceManager.addHostCreatedInstance(object, 0); + + JavaObject.dispose(object); + + expect(callbackIdentifier, 0); + }); + + test('JavaObjectFlutterApi.dispose', () { + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + + final JavaObject object = JavaObject.detached( + instanceManager: instanceManager, + ); + instanceManager.addHostCreatedInstance(object, 0); + instanceManager.removeWeakReference(object); + + expect(instanceManager.containsIdentifier(0), isTrue); + + final JavaObjectFlutterApiImpl flutterApi = JavaObjectFlutterApiImpl( + instanceManager: instanceManager, + ); + flutterApi.dispose(0); + + expect(instanceManager.containsIdentifier(0), isFalse); + }); + }); + group('WebView', () { late MockTestWebViewHostApi mockPlatformHostApi; 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 db877e5870b6..3a4043c22864 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 @@ -114,6 +114,21 @@ class MockTestDownloadListenerHostApi extends _i1.Mock returnValueForMissingStub: null); } +/// A class which mocks [TestJavaObjectHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestJavaObjectHostApi extends _i1.Mock + implements _i5.TestJavaObjectHostApi { + MockTestJavaObjectHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void dispose(int? identifier) => + super.noSuchMethod(Invocation.method(#dispose, [identifier]), + returnValueForMissingStub: null); +} + /// A class which mocks [TestJavaScriptChannelHostApi]. /// /// See the documentation for Mockito's code generation for more information. diff --git a/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart index e3c5909f3207..88429fdb337e 100644 --- a/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.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 (v3.0.3), do not edit directly. +// Autogenerated from Pigeon (v3.2.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 // ignore_for_file: avoid_relative_lib_imports @@ -14,6 +14,38 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:webview_flutter_android/src/android_webview.pigeon.dart'; +class _TestJavaObjectHostApiCodec extends StandardMessageCodec { + const _TestJavaObjectHostApiCodec(); +} + +abstract class TestJavaObjectHostApi { + static const MessageCodec codec = _TestJavaObjectHostApiCodec(); + + void dispose(int identifier); + static void setup(TestJavaObjectHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.JavaObjectHostApi.dispose', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.JavaObjectHostApi.dispose 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.JavaObjectHostApi.dispose was null, expected non-null int.'); + api.dispose(arg_identifier!); + return {}; + }); + } + } + } +} + class _TestWebViewHostApiCodec extends StandardMessageCodec { const _TestWebViewHostApiCodec(); } From 7c89e384d2ab4eaaa7ba1a6358c8b4b74907490c Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Fri, 12 Aug 2022 18:41:10 -0400 Subject: [PATCH 603/844] [webview_flutter_wkwebview] Implementation of PlatformNavigationDelegate for WebKit (#6151) --- .../v4/src/webkit_navigation_delegate.dart | 161 ++++++++++++++ .../lib/src/v4/src/webkit_proxy.dart | 17 ++ .../src/v4/src/webkit_webview_controller.dart | 33 ++- .../src/v4/src/webkit_webview_platform.dart | 10 +- .../lib/src/v4/webview_flutter_wkwebview.dart | 2 + .../v4/webkit_navigation_delegate_test.dart | 201 ++++++++++++++++++ .../v4/webkit_webview_controller_test.dart | 98 +++++++++ 7 files changed, 520 insertions(+), 2 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_navigation_delegate.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_navigation_delegate_test.dart diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_navigation_delegate.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_navigation_delegate.dart new file mode 100644 index 000000000000..10b1fab52e03 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_navigation_delegate.dart @@ -0,0 +1,161 @@ +// 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:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; + +import '../../foundation/foundation.dart'; +import '../../web_kit/web_kit.dart'; +import 'webkit_proxy.dart'; + +/// An implementation of [WebResourceError] with the WebKit API. +class WebKitWebResourceError extends WebResourceError { + WebKitWebResourceError._(this._nsError) + : super( + errorCode: _nsError.code, + description: _nsError.localizedDescription, + errorType: _toWebResourceErrorType(_nsError.code), + ); + + static WebResourceErrorType? _toWebResourceErrorType(int code) { + switch (code) { + case WKErrorCode.unknown: + return WebResourceErrorType.unknown; + case WKErrorCode.webContentProcessTerminated: + return WebResourceErrorType.webContentProcessTerminated; + case WKErrorCode.webViewInvalidated: + return WebResourceErrorType.webViewInvalidated; + case WKErrorCode.javaScriptExceptionOccurred: + return WebResourceErrorType.javaScriptExceptionOccurred; + case WKErrorCode.javaScriptResultTypeIsUnsupported: + return WebResourceErrorType.javaScriptResultTypeIsUnsupported; + } + + return null; + } + + /// A string representing the domain of the error. + String? get domain => _nsError.domain; + + final NSError _nsError; +} + +/// An implementation of [PlatformNavigationDelegate] with the WebKit API. +class WebKitNavigationDelegate extends PlatformNavigationDelegate { + /// Constructs a [WebKitNavigationDelegate]. + WebKitNavigationDelegate( + super.params, { + @visibleForTesting WebKitProxy webKitProxy = const WebKitProxy(), + }) : super.implementation() { + final WeakReference weakThis = + WeakReference(this); + navigationDelegate = webKitProxy.createNavigationDelegate( + didFinishNavigation: (WKWebView webView, String? url) { + if (weakThis.target?._onPageFinished != null) { + weakThis.target!._onPageFinished!(url ?? ''); + } + }, + didStartProvisionalNavigation: (WKWebView webView, String? url) { + if (weakThis.target?._onPageStarted != null) { + weakThis.target!._onPageStarted!(url ?? ''); + } + }, + decidePolicyForNavigationAction: ( + WKWebView webView, + WKNavigationAction action, + ) async { + if (weakThis.target?._onNavigationRequest != null) { + final bool allow = await weakThis.target!._onNavigationRequest!( + url: action.request.url, + isForMainFrame: action.targetFrame.isMainFrame, + ); + return allow + ? WKNavigationActionPolicy.allow + : WKNavigationActionPolicy.cancel; + } + return WKNavigationActionPolicy.allow; + }, + didFailNavigation: (WKWebView webView, NSError error) { + if (weakThis.target?._onWebResourceError != null) { + weakThis.target!._onWebResourceError!( + WebKitWebResourceError._(error), + ); + } + }, + didFailProvisionalNavigation: (WKWebView webView, NSError error) { + if (weakThis.target?._onWebResourceError != null) { + weakThis.target!._onWebResourceError!( + WebKitWebResourceError._(error), + ); + } + }, + webViewWebContentProcessDidTerminate: (WKWebView webView) { + if (weakThis.target?._onWebResourceError != null) { + weakThis.target!._onWebResourceError!( + WebKitWebResourceError._( + const NSError( + code: WKErrorCode.webContentProcessTerminated, + // Value from https://developer.apple.com/documentation/webkit/wkerrordomain?language=objc. + domain: 'WKErrorDomain', + localizedDescription: '', + ), + ), + ); + } + }, + ); + } + + // Used to set `WKWebView.setNavigationDelegate` in `WebKitWebViewController`. + /// WebKit class that handles navigation changes and tracking navigation + /// requests. + late final WKNavigationDelegate navigationDelegate; + + void Function(String url)? _onPageFinished; + void Function(String url)? _onPageStarted; + void Function(int progress)? _onProgress; + void Function(WebResourceError error)? _onWebResourceError; + FutureOr Function({required String url, required bool isForMainFrame})? + _onNavigationRequest; + + // `WKWebView` in `WebKitWebViewController` uses this to track loading + // progress. This can't be done with `WKNavigationDelegate`. + /// Callback method that receives progress of a loading page. + void Function(int progress)? get onProgress => _onProgress; + + @override + Future setOnPageFinished( + void Function(String url) onPageFinished, + ) async { + _onPageFinished = onPageFinished; + } + + @override + Future setOnPageStarted(void Function(String url) onPageStarted) async { + _onPageStarted = onPageStarted; + } + + @override + Future setOnProgress(void Function(int progress) onProgress) async { + _onProgress = onProgress; + } + + @override + Future setOnWebResourceError( + void Function(WebResourceError error) onWebResourceError, + ) async { + _onWebResourceError = onWebResourceError; + } + + @override + Future setOnNavigationRequest( + FutureOr Function({required String url, required bool isForMainFrame}) + onNavigationRequest, + ) async { + _onNavigationRequest = onNavigationRequest; + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_proxy.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_proxy.dart index 48e6faf9abd7..013eae0516cd 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_proxy.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_proxy.dart @@ -20,6 +20,7 @@ class WebKitProxy { this.createWebView = WKWebView.new, this.createWebViewConfiguration = WKWebViewConfiguration.new, this.createScriptMessageHandler = WKScriptMessageHandler.new, + this.createNavigationDelegate = WKNavigationDelegate.new, }); /// Constructs a [WKWebView]. @@ -44,4 +45,20 @@ class WebKitProxy { ) didReceiveScriptMessage, }) createScriptMessageHandler; + + /// Constructs a [WKNavigationDelegate]. + final WKNavigationDelegate Function({ + void Function(WKWebView webView, String? url)? didFinishNavigation, + void Function(WKWebView webView, String? url)? + didStartProvisionalNavigation, + Future Function( + WKWebView webView, + WKNavigationAction navigationAction, + )? + decidePolicyForNavigationAction, + void Function(WKWebView webView, NSError error)? didFailNavigation, + void Function(WKWebView webView, NSError error)? + didFailProvisionalNavigation, + void Function(WKWebView webView)? webViewWebContentProcessDidTerminate, + }) createNavigationDelegate; } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_controller.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_controller.dart index 9d76b5cda04d..dc0693e5f9a5 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_controller.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_controller.dart @@ -13,6 +13,7 @@ import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_i import '../../common/weak_reference_utils.dart'; import '../../foundation/foundation.dart'; import '../../web_kit/web_kit.dart'; +import 'webkit_navigation_delegate.dart'; import 'webkit_proxy.dart'; /// Object specifying creation parameters for a [WebKitWebViewController]. @@ -46,8 +47,29 @@ class WebKitWebViewController extends PlatformWebViewController { ? params : WebKitWebViewControllerCreationParams .fromPlatformWebViewControllerCreationParams(params)) { + final WeakReference weakThis = + WeakReference(this); _webView = webKitProxy.createWebView( - (params as WebKitWebViewControllerCreationParams)._configuration); + (params as WebKitWebViewControllerCreationParams)._configuration, + observeValue: ( + String keyPath, + NSObject object, + Map change, + ) { + if (weakThis.target?._onProgress != null) { + final double progress = + change[NSKeyValueChangeKey.newValue]! as double; + weakThis.target!._onProgress!((progress * 100).round()); + } + }, + ); + _webView.addObserver( + _webView, + keyPath: 'estimatedProgress', + options: { + NSKeyValueObservingOptions.newValue, + }, + ); } late final WKWebView _webView; @@ -56,6 +78,7 @@ class WebKitWebViewController extends PlatformWebViewController { {}; bool _zoomEnabled = true; + void Function(int progress)? _onProgress; @override Future loadFile(String absoluteFilePath) { @@ -270,6 +293,14 @@ class WebKitWebViewController extends PlatformWebViewController { } } + @override + Future setPlatformNavigationDelegate( + covariant WebKitNavigationDelegate handler, + ) { + _onProgress = handler.onProgress; + return _webView.setNavigationDelegate(handler.navigationDelegate); + } + Future _disableZoom() { const WKUserScript userScript = WKUserScript( "var meta = document.createElement('meta');\n" diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_platform.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_platform.dart index b808086d56f8..6ffe386a5e28 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_platform.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_platform.dart @@ -4,9 +4,10 @@ import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; +import 'webkit_navigation_delegate.dart'; import 'webkit_webview_controller.dart'; -/// Implementation of [WebViewPlatform] using the WebKit Api. +/// Implementation of [WebViewPlatform] using the WebKit API. class WebKitWebViewPlatform extends WebViewPlatform { @override WebKitWebViewController createPlatformWebViewController( @@ -14,4 +15,11 @@ class WebKitWebViewPlatform extends WebViewPlatform { ) { return WebKitWebViewController(params); } + + @override + WebKitNavigationDelegate createPlatformNavigationDelegate( + PlatformNavigationDelegateCreationParams params, + ) { + return WebKitNavigationDelegate(params); + } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/webview_flutter_wkwebview.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/webview_flutter_wkwebview.dart index 2a593fb5a088..6c5cd93f11d7 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/webview_flutter_wkwebview.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/webview_flutter_wkwebview.dart @@ -4,4 +4,6 @@ library webview_flutter_wkwebview; +export 'src/webkit_navigation_delegate.dart'; +export 'src/webkit_webview_controller.dart'; export 'src/webkit_webview_controller.dart'; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_navigation_delegate_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_navigation_delegate_test.dart new file mode 100644 index 000000000000..fb57c988ec32 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_navigation_delegate_test.dart @@ -0,0 +1,201 @@ +// 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:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; +import 'package:webview_flutter_wkwebview/src/v4/src/webkit_proxy.dart'; +import 'package:webview_flutter_wkwebview/src/v4/webview_flutter_wkwebview.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; + +void main() { + WidgetsFlutterBinding.ensureInitialized(); + + group('WebKitNavigationDelegate', () { + test('setOnPageFinished', () { + final WebKitNavigationDelegate webKitDelgate = WebKitNavigationDelegate( + const PlatformNavigationDelegateCreationParams(), + webKitProxy: const WebKitProxy( + createNavigationDelegate: CapturingNavigationDelegate.new, + ), + ); + + late final String callbackUrl; + webKitDelgate.setOnPageFinished((String url) => callbackUrl = url); + + CapturingNavigationDelegate.lastCreatedDelegate.didFinishNavigation!( + WKWebView.detached(), + 'https://www.google.com', + ); + + expect(callbackUrl, 'https://www.google.com'); + }); + + test('setOnPageStarted', () { + final WebKitNavigationDelegate webKitDelgate = WebKitNavigationDelegate( + const PlatformNavigationDelegateCreationParams(), + webKitProxy: const WebKitProxy( + createNavigationDelegate: CapturingNavigationDelegate.new, + ), + ); + + late final String callbackUrl; + webKitDelgate.setOnPageStarted((String url) => callbackUrl = url); + + CapturingNavigationDelegate + .lastCreatedDelegate.didStartProvisionalNavigation!( + WKWebView.detached(), + 'https://www.google.com', + ); + + expect(callbackUrl, 'https://www.google.com'); + }); + + test('onWebResourceError from didFailNavigation', () { + final WebKitNavigationDelegate webKitDelgate = WebKitNavigationDelegate( + const PlatformNavigationDelegateCreationParams(), + webKitProxy: const WebKitProxy( + createNavigationDelegate: CapturingNavigationDelegate.new, + ), + ); + + late final WebKitWebResourceError callbackError; + void onWebResourceError(WebResourceError error) { + callbackError = error as WebKitWebResourceError; + } + + webKitDelgate.setOnWebResourceError(onWebResourceError); + + CapturingNavigationDelegate.lastCreatedDelegate.didFailNavigation!( + WKWebView.detached(), + const NSError( + code: WKErrorCode.webViewInvalidated, + domain: 'domain', + localizedDescription: 'my desc', + ), + ); + + expect(callbackError.description, 'my desc'); + expect(callbackError.errorCode, WKErrorCode.webViewInvalidated); + expect(callbackError.domain, 'domain'); + expect(callbackError.errorType, WebResourceErrorType.webViewInvalidated); + }); + + test('onWebResourceError from didFailProvisionalNavigation', () { + final WebKitNavigationDelegate webKitDelgate = WebKitNavigationDelegate( + const PlatformNavigationDelegateCreationParams(), + webKitProxy: const WebKitProxy( + createNavigationDelegate: CapturingNavigationDelegate.new, + ), + ); + + late final WebKitWebResourceError callbackError; + void onWebResourceError(WebResourceError error) { + callbackError = error as WebKitWebResourceError; + } + + webKitDelgate.setOnWebResourceError(onWebResourceError); + + CapturingNavigationDelegate + .lastCreatedDelegate.didFailProvisionalNavigation!( + WKWebView.detached(), + const NSError( + code: WKErrorCode.webViewInvalidated, + domain: 'domain', + localizedDescription: 'my desc', + ), + ); + + expect(callbackError.description, 'my desc'); + expect(callbackError.errorCode, WKErrorCode.webViewInvalidated); + expect(callbackError.domain, 'domain'); + expect(callbackError.errorType, WebResourceErrorType.webViewInvalidated); + }); + + test('onWebResourceError from webViewWebContentProcessDidTerminate', () { + final WebKitNavigationDelegate webKitDelgate = WebKitNavigationDelegate( + const PlatformNavigationDelegateCreationParams(), + webKitProxy: const WebKitProxy( + createNavigationDelegate: CapturingNavigationDelegate.new, + ), + ); + + late final WebKitWebResourceError callbackError; + void onWebResourceError(WebResourceError error) { + callbackError = error as WebKitWebResourceError; + } + + webKitDelgate.setOnWebResourceError(onWebResourceError); + + CapturingNavigationDelegate + .lastCreatedDelegate.webViewWebContentProcessDidTerminate!( + WKWebView.detached(), + ); + + expect(callbackError.description, ''); + expect(callbackError.errorCode, WKErrorCode.webContentProcessTerminated); + expect(callbackError.domain, 'WKErrorDomain'); + expect( + callbackError.errorType, + WebResourceErrorType.webContentProcessTerminated, + ); + }); + + test('onNavigationRequest from decidePolicyForNavigationAction', () { + final WebKitNavigationDelegate webKitDelgate = WebKitNavigationDelegate( + const PlatformNavigationDelegateCreationParams(), + webKitProxy: const WebKitProxy( + createNavigationDelegate: CapturingNavigationDelegate.new, + ), + ); + + late final String callbackUrl; + late final bool callbackIsMainFrame; + FutureOr onNavigationRequest({ + required String url, + required bool isForMainFrame, + }) { + callbackUrl = url; + callbackIsMainFrame = isForMainFrame; + return true; + } + + webKitDelgate.setOnNavigationRequest(onNavigationRequest); + + expect( + CapturingNavigationDelegate + .lastCreatedDelegate.decidePolicyForNavigationAction!( + WKWebView.detached(), + const WKNavigationAction( + request: NSUrlRequest(url: 'https://www.google.com'), + targetFrame: WKFrameInfo(isMainFrame: false), + ), + ), + completion(WKNavigationActionPolicy.allow), + ); + + expect(callbackUrl, 'https://www.google.com'); + expect(callbackIsMainFrame, isFalse); + }); + }); +} + +// Records the last created instance of itself. +class CapturingNavigationDelegate extends WKNavigationDelegate { + CapturingNavigationDelegate({ + super.didFinishNavigation, + super.didStartProvisionalNavigation, + super.didFailNavigation, + super.didFailProvisionalNavigation, + super.decidePolicyForNavigationAction, + super.webViewWebContentProcessDidTerminate, + }) : super.detached() { + lastCreatedDelegate = this; + } + static CapturingNavigationDelegate lastCreatedDelegate = + CapturingNavigationDelegate(); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart index 71bda575aa52..3f0e7f217fa9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart @@ -705,6 +705,88 @@ void main() { "var head = document.getElementsByTagName('head')[0];head.appendChild(meta);", ); }); + + test('setPlatformNavigationDelegate', () { + final MockWKWebView mockWebView = MockWKWebView(); + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: (_, {dynamic observeValue}) => mockWebView, + ); + + final WebKitNavigationDelegate navigationDelegate = + WebKitNavigationDelegate( + const PlatformNavigationDelegateCreationParams(), + webKitProxy: const WebKitProxy( + createNavigationDelegate: CapturingNavigationDelegate.new, + ), + ); + + controller.setPlatformNavigationDelegate(navigationDelegate); + + verify( + mockWebView.setNavigationDelegate( + CapturingNavigationDelegate.lastCreatedDelegate, + ), + ); + }); + + test('setPlatformNavigationDelegate onProgress', () async { + final MockWKWebView mockWebView = MockWKWebView(); + + late final void Function( + String keyPath, + NSObject object, + Map change, + ) webViewObserveValue; + + final WebKitWebViewController controller = createControllerWithMocks( + createMockWebView: ( + _, { + void Function( + String keyPath, + NSObject object, + Map change, + )? + observeValue, + }) { + webViewObserveValue = observeValue!; + return mockWebView; + }, + ); + + verify( + mockWebView.addObserver( + mockWebView, + keyPath: 'estimatedProgress', + options: { + NSKeyValueObservingOptions.newValue, + }, + ), + ); + + final WebKitNavigationDelegate navigationDelegate = + WebKitNavigationDelegate( + const PlatformNavigationDelegateCreationParams(), + webKitProxy: const WebKitProxy( + createNavigationDelegate: CapturingNavigationDelegate.new, + ), + ); + + late final int callbackProgress; + navigationDelegate.setOnProgress( + (int progress) => callbackProgress = progress, + ); + + await controller.setPlatformNavigationDelegate(navigationDelegate); + + webViewObserveValue( + 'estimatedProgress', + mockWebView, + {NSKeyValueChangeKey.newValue: 0.0}, + ); + + expect(callbackProgress, 0); + }); }); group('WebKitJavaScriptChannelParams', () { @@ -744,3 +826,19 @@ void main() { }); }); } + +// Records the last created instance of itself. +class CapturingNavigationDelegate extends WKNavigationDelegate { + CapturingNavigationDelegate({ + super.didFinishNavigation, + super.didStartProvisionalNavigation, + super.didFailNavigation, + super.didFailProvisionalNavigation, + super.decidePolicyForNavigationAction, + super.webViewWebContentProcessDidTerminate, + }) : super.detached() { + lastCreatedDelegate = this; + } + static CapturingNavigationDelegate lastCreatedDelegate = + CapturingNavigationDelegate(); +} From 351111c6bc25bd8347644270d26d784f9f590c92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:49:06 +0000 Subject: [PATCH 604/844] [google_maps]: Bump mockito-core from 4.6.1 to 4.7.0 in /packages/google_maps_flutter/google_maps_flutter_android/android (#6266) --- .../google_maps_flutter_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle index 2ed0674ebfe0..50428892f555 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle @@ -40,7 +40,7 @@ android { androidTestImplementation 'androidx.test:rules:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-core:4.6.1' + testImplementation 'org.mockito:mockito-core:4.7.0' testImplementation 'androidx.test:core:1.2.0' testImplementation "org.robolectric:robolectric:4.3.1" } From 8cc68e52e65daa6571c445e80bfb71d08459d460 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:49:27 +0000 Subject: [PATCH 605/844] [camera]: Bump mockito-inline from 4.6.1 to 4.7.0 in /packages/camera/camera_android/android (#6265) --- packages/camera/camera_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android/android/build.gradle b/packages/camera/camera_android/android/build.gradle index 1967ebaf0236..b4e7d7a455be 100644 --- a/packages/camera/camera_android/android/build.gradle +++ b/packages/camera/camera_android/android/build.gradle @@ -61,7 +61,7 @@ android { dependencies { compileOnly 'androidx.annotation:annotation:1.1.0' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-inline:4.6.1' + testImplementation 'org.mockito:mockito-inline:4.7.0' testImplementation 'androidx.test:core:1.3.0' testImplementation 'org.robolectric:robolectric:4.5' } From 094f1c3ad94aa22a142564451933a6ef65116577 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:54:06 +0000 Subject: [PATCH 606/844] [quick_actions]: Bump mockito-android from 4.6.1 to 4.7.0 in /packages/quick_actions/quick_actions_android/example/android/app (#6261) --- .../quick_actions_android/example/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/quick_actions/quick_actions_android/example/android/app/build.gradle b/packages/quick_actions/quick_actions_android/example/android/app/build.gradle index 7cf87cdda485..b043dec31b3d 100644 --- a/packages/quick_actions/quick_actions_android/example/android/app/build.gradle +++ b/packages/quick_actions/quick_actions_android/example/android/app/build.gradle @@ -63,5 +63,5 @@ dependencies { androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' androidTestImplementation 'androidx.test.ext:junit:1.0.0' androidTestImplementation 'org.mockito:mockito-core:4.6.1' - androidTestImplementation 'org.mockito:mockito-android:4.6.1' + androidTestImplementation 'org.mockito:mockito-android:4.7.0' } From 0134dff6cd5744461e0c5f08f32300c9e648a073 Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Mon, 15 Aug 2022 10:09:06 -0700 Subject: [PATCH 607/844] [camera] Add CameraX plugin for development (#6178) --- .github/dependabot.yml | 16 ++++ .../camera/camera_android_camerax/.metadata | 30 +++++++ .../camera/camera_android_camerax/AUTHORS | 4 + .../camera_android_camerax/CHANGELOG.md | 3 + .../camera/camera_android_camerax/LICENSE | 25 ++++++ .../camera/camera_android_camerax/README.md | 3 + .../android/build.gradle | 35 ++++++++ .../android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 3 + .../camerax/CameraAndroidCameraxPlugin.java | 42 +++++++++ .../plugins/camerax/CameraxPluginTest.java | 7 ++ .../camera_android_camerax/example/README.md | 3 + .../example/android/app/build.gradle | 66 ++++++++++++++ .../flutter/plugins/DartIntegrationTest.java | 14 +++ .../cameraxexample/MainActivityTest.java | 17 ++++ .../android/app/src/debug/AndroidManifest.xml | 8 ++ .../android/app/src/main/AndroidManifest.xml | 33 +++++++ .../plugins/cameraexample/MainActivity.java | 9 ++ .../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 ++++ .../app/src/profile/AndroidManifest.xml | 8 ++ .../example/android/build.gradle | 31 +++++++ .../example/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 ++ .../example/android/settings.gradle | 11 +++ .../integration_test/integration_test.dart | 12 +++ .../example/lib/main.dart | 34 +++++++ .../example/pubspec.yaml | 85 ++++++++++++++++++ .../example/test/widget_test.dart | 18 ++++ .../example/test_driver/integration_test.dart | 7 ++ .../lib/camera_android_camerax.dart | 13 +++ ...camera_android_camerax_method_channel.dart | 23 +++++ ...ra_android_camerax_platform_interface.dart | 36 ++++++++ .../camera_android_camerax/pubspec.yaml | 73 +++++++++++++++ ...a_android_camerax_method_channel_test.dart | 29 ++++++ .../test/camera_android_camerax_test.dart | 35 ++++++++ 43 files changed, 802 insertions(+) create mode 100644 packages/camera/camera_android_camerax/.metadata create mode 100644 packages/camera/camera_android_camerax/AUTHORS create mode 100644 packages/camera/camera_android_camerax/CHANGELOG.md create mode 100644 packages/camera/camera_android_camerax/LICENSE create mode 100644 packages/camera/camera_android_camerax/README.md create mode 100644 packages/camera/camera_android_camerax/android/build.gradle create mode 100644 packages/camera/camera_android_camerax/android/settings.gradle create mode 100644 packages/camera/camera_android_camerax/android/src/main/AndroidManifest.xml create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java create mode 100644 packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraxPluginTest.java create mode 100644 packages/camera/camera_android_camerax/example/README.md create mode 100644 packages/camera/camera_android_camerax/example/android/app/build.gradle create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/cameraxexample/MainActivityTest.java create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/debug/AndroidManifest.xml create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/AndroidManifest.xml create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/java/io/flutter/plugins/cameraexample/MainActivity.java create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/res/drawable/launch_background.xml create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/res/values-night/styles.xml create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/main/res/values/styles.xml create mode 100644 packages/camera/camera_android_camerax/example/android/app/src/profile/AndroidManifest.xml create mode 100644 packages/camera/camera_android_camerax/example/android/build.gradle create mode 100644 packages/camera/camera_android_camerax/example/android/gradle.properties create mode 100644 packages/camera/camera_android_camerax/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/camera/camera_android_camerax/example/android/settings.gradle create mode 100644 packages/camera/camera_android_camerax/example/integration_test/integration_test.dart create mode 100644 packages/camera/camera_android_camerax/example/lib/main.dart create mode 100644 packages/camera/camera_android_camerax/example/pubspec.yaml create mode 100644 packages/camera/camera_android_camerax/example/test/widget_test.dart create mode 100644 packages/camera/camera_android_camerax/example/test_driver/integration_test.dart create mode 100644 packages/camera/camera_android_camerax/lib/camera_android_camerax.dart create mode 100644 packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart create mode 100644 packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart create mode 100644 packages/camera/camera_android_camerax/pubspec.yaml create mode 100644 packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart create mode 100644 packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d1b3525c8ed0..9a1011efaaba 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -16,6 +16,22 @@ updates: interval: "weekly" open-pull-requests-limit: 10 + - package-ecosystem: "gradle" + directory: "/packages/camera/camera_android_camerax/android" + commit-message: + prefix: "[camera]" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + + - package-ecosystem: "gradle" + directory: "/packages/camera/camera_android_camerax/example/android/app" + commit-message: + prefix: "[camera]" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + - package-ecosystem: "gradle" directory: "/packages/camera/camera/example/android/app" commit-message: diff --git a/packages/camera/camera_android_camerax/.metadata b/packages/camera/camera_android_camerax/.metadata new file mode 100644 index 000000000000..1667b9356657 --- /dev/null +++ b/packages/camera/camera_android_camerax/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: 6b04999e4aaa9dfafdcb5ca09e812df7379d9ee5 + channel: spellcheck_1_1 + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 6b04999e4aaa9dfafdcb5ca09e812df7379d9ee5 + base_revision: 6b04999e4aaa9dfafdcb5ca09e812df7379d9ee5 + - platform: android + create_revision: 6b04999e4aaa9dfafdcb5ca09e812df7379d9ee5 + base_revision: 6b04999e4aaa9dfafdcb5ca09e812df7379d9ee5 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/packages/camera/camera_android_camerax/AUTHORS b/packages/camera/camera_android_camerax/AUTHORS new file mode 100644 index 000000000000..513c0b0e2f86 --- /dev/null +++ b/packages/camera/camera_android_camerax/AUTHORS @@ -0,0 +1,4 @@ +# 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 diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md new file mode 100644 index 000000000000..044fecbb5272 --- /dev/null +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -0,0 +1,3 @@ +## NEXT + +* Creates camera_android_camerax plugin for development. diff --git a/packages/camera/camera_android_camerax/LICENSE b/packages/camera/camera_android_camerax/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/camera/camera_android_camerax/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/camera/camera_android_camerax/README.md b/packages/camera/camera_android_camerax/README.md new file mode 100644 index 000000000000..06d837ac7214 --- /dev/null +++ b/packages/camera/camera_android_camerax/README.md @@ -0,0 +1,3 @@ +# camera_android_camerax + +An implementation of the camera plugin on Android using CameraX. diff --git a/packages/camera/camera_android_camerax/android/build.gradle b/packages/camera/camera_android_camerax/android/build.gradle new file mode 100644 index 000000000000..15932c52c527 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/build.gradle @@ -0,0 +1,35 @@ +group 'io.flutter.plugins.camerax' +version '1.0' + +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.2.0' + } +} + +rootProject.allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 31 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + defaultConfig { + minSdkVersion 16 + } +} diff --git a/packages/camera/camera_android_camerax/android/settings.gradle b/packages/camera/camera_android_camerax/android/settings.gradle new file mode 100644 index 000000000000..613f994165a0 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'camera_android_camerax' diff --git a/packages/camera/camera_android_camerax/android/src/main/AndroidManifest.xml b/packages/camera/camera_android_camerax/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..ea4275c757cf --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + 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 new file mode 100644 index 000000000000..0e60b13c2cfa --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java @@ -0,0 +1,42 @@ +// 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 androidx.annotation.NonNull; +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; + +/** CameraAndroidCameraxPlugin */ +public class CameraAndroidCameraxPlugin implements 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 MethodChannel channel; + + @Override + public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { + channel = + new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "camera_android_camerax"); + channel.setMethodCallHandler(this); + } + + @Override + public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { + if (call.method.equals("getPlatformVersion")) { + result.success("Android " + android.os.Build.VERSION.RELEASE); + } else { + result.notImplemented(); + } + } + + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + channel.setMethodCallHandler(null); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraxPluginTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraxPluginTest.java new file mode 100644 index 000000000000..69b0e34f6bb6 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraxPluginTest.java @@ -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. + +package io.flutter.plugins.camerax; + +public class CameraxPluginTest {} diff --git a/packages/camera/camera_android_camerax/example/README.md b/packages/camera/camera_android_camerax/example/README.md new file mode 100644 index 000000000000..438e446d1921 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/README.md @@ -0,0 +1,3 @@ +# camera_android_camerax_example + +Demonstrates how to use the camera_android_camerax plugin. diff --git a/packages/camera/camera_android_camerax/example/android/app/build.gradle b/packages/camera/camera_android_camerax/example/android/app/build.gradle new file mode 100644 index 000000000000..1dbeb6653915 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/build.gradle @@ -0,0 +1,66 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "io.flutter.plugins.cameraxexample" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. + minSdkVersion flutter.minSdkVersion + targetSdkVersion 30 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test:runner:1.2.0' + api 'androidx.test:core:1.2.0' +} diff --git a/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java new file mode 100644 index 000000000000..0f4298dca155 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java @@ -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. + +package io.flutter.plugins; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DartIntegrationTest {} diff --git a/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/cameraxexample/MainActivityTest.java b/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/cameraxexample/MainActivityTest.java new file mode 100644 index 000000000000..8bcb398abb87 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/androidTest/java/io/flutter/plugins/cameraxexample/MainActivityTest.java @@ -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 io.flutter.plugins.cameraxexample; + +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) +public class MainActivityTest { + @Rule public ActivityTestRule rule = new ActivityTestRule<>(MainActivity.class); +} diff --git a/packages/camera/camera_android_camerax/example/android/app/src/debug/AndroidManifest.xml b/packages/camera/camera_android_camerax/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000000..093e904635f7 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/AndroidManifest.xml b/packages/camera/camera_android_camerax/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000000..82b92e25bdfe --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/java/io/flutter/plugins/cameraexample/MainActivity.java b/packages/camera/camera_android_camerax/example/android/app/src/main/java/io/flutter/plugins/cameraexample/MainActivity.java new file mode 100644 index 000000000000..5e2a10f1555a --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/main/java/io/flutter/plugins/cameraexample/MainActivity.java @@ -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 io.flutter.plugins.cameraxexample; + +import io.flutter.embedding.android.FlutterActivity; + +public class MainActivity extends FlutterActivity {} diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/camera/camera_android_camerax/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 000000000000..f74085f3f6a2 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/res/drawable/launch_background.xml b/packages/camera/camera_android_camerax/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 000000000000..304732f88420 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/camera/camera_android_camerax/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/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/camera/camera_android_camerax/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/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/camera/camera_android_camerax/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/camera/camera_android_camerax/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/camera/camera_android_camerax/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/camera/camera_android_camerax/example/android/app/src/main/res/values-night/styles.xml b/packages/camera/camera_android_camerax/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000000..06952be745f9 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/packages/camera/camera_android_camerax/example/android/app/src/main/res/values/styles.xml b/packages/camera/camera_android_camerax/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000000..cb1ef88056ed --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/packages/camera/camera_android_camerax/example/android/app/src/profile/AndroidManifest.xml b/packages/camera/camera_android_camerax/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 000000000000..093e904635f7 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/packages/camera/camera_android_camerax/example/android/build.gradle b/packages/camera/camera_android_camerax/example/android/build.gradle new file mode 100644 index 000000000000..58a8c74b1474 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.7.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.2.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/packages/camera/camera_android_camerax/example/android/gradle.properties b/packages/camera/camera_android_camerax/example/android/gradle.properties new file mode 100644 index 000000000000..94adc3a3f97a --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/packages/camera/camera_android_camerax/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/camera/camera_android_camerax/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..3c472b99c6f3 --- /dev/null +++ b/packages/camera/camera_android_camerax/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.5-all.zip diff --git a/packages/camera/camera_android_camerax/example/android/settings.gradle b/packages/camera/camera_android_camerax/example/android/settings.gradle new file mode 100644 index 000000000000..44e62bcf06ae --- /dev/null +++ b/packages/camera/camera_android_camerax/example/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/packages/camera/camera_android_camerax/example/integration_test/integration_test.dart b/packages/camera/camera_android_camerax/example/integration_test/integration_test.dart new file mode 100644 index 000000000000..2b82b4bda5e4 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/integration_test/integration_test.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. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('placeholder test', (WidgetTester tester) async {}); +} diff --git a/packages/camera/camera_android_camerax/example/lib/main.dart b/packages/camera/camera_android_camerax/example/lib/main.dart new file mode 100644 index 000000000000..c5391096374e --- /dev/null +++ b/packages/camera/camera_android_camerax/example/lib/main.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/material.dart'; + +void main() { + runApp(const MyApp()); +} + +/// Example app +class MyApp extends StatefulWidget { + /// App instantiation + const MyApp({Key? key}) : super(key: key); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: const Center( + child: Text('Hello, world!'), + ), + ), + ); + } +} diff --git a/packages/camera/camera_android_camerax/example/pubspec.yaml b/packages/camera/camera_android_camerax/example/pubspec.yaml new file mode 100644 index 000000000000..891e9486e846 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/pubspec.yaml @@ -0,0 +1,85 @@ +name: camera_android_camerax_example +description: Demonstrates how to use the camera_android_camerax plugin. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: '>=2.14.0 <3.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + camera_android_camerax: + # When depending on this package from a real application you should use: + # camera_android_camerax: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # 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: ../ + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + flutter: + sdk: flutter + +dev_dependencies: + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + + flutter_test: + sdk: flutter + + integration_test: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/camera/camera_android_camerax/example/test/widget_test.dart b/packages/camera/camera_android_camerax/example/test/widget_test.dart new file mode 100644 index 000000000000..bfe91af3eae6 --- /dev/null +++ b/packages/camera/camera_android_camerax/example/test/widget_test.dart @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('Fake test', (WidgetTester tester) async { + expect(true, isTrue); + }); +} diff --git a/packages/camera/camera_android_camerax/example/test_driver/integration_test.dart b/packages/camera/camera_android_camerax/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/camera/camera_android_camerax/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/camera/camera_android_camerax/lib/camera_android_camerax.dart b/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart new file mode 100644 index 000000000000..40f8c703fa9f --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/camera_android_camerax.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 'camera_android_camerax_platform_interface.dart'; + +/// Android Camera implented with the CameraX library. +class CameraAndroidCamerax { + /// Returns platform version of this instance. + Future getPlatformVersion() { + return CameraAndroidCameraxPlatform.instance.getPlatformVersion(); + } +} diff --git a/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart b/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart new file mode 100644 index 000000000000..699a3667d14d --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.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/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'camera_android_camerax_platform_interface.dart'; + +/// An implementation of [CameraAndroidCameraxPlatform] that uses method channels. +class MethodChannelCameraAndroidCamerax extends CameraAndroidCameraxPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + final MethodChannel methodChannel = + const MethodChannel('camera_android_camerax'); + + @override + Future getPlatformVersion() async { + final String? version = + await methodChannel.invokeMethod('getPlatformVersion'); + return version; + } +} diff --git a/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart b/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart new file mode 100644 index 000000000000..03554caf3245 --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart @@ -0,0 +1,36 @@ +// 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:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'camera_android_camerax_method_channel.dart'; + +/// The platform interface for the Android Camera implemented with CameraX. +abstract class CameraAndroidCameraxPlatform extends PlatformInterface { + /// Constructs a CameraAndroidCameraxPlatform. + CameraAndroidCameraxPlatform() : super(token: _token); + + static final Object _token = Object(); + + static CameraAndroidCameraxPlatform _instance = + MethodChannelCameraAndroidCamerax(); + + /// The default instance of [CameraAndroidCameraxPlatform] to use. + /// + /// Defaults to [MethodChannelCameraAndroidCamerax]. + static CameraAndroidCameraxPlatform get instance => _instance; + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [CameraAndroidCameraxPlatform] when + /// they register themselves. + static set instance(CameraAndroidCameraxPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + /// Returns the platform version of this isntance. + Future getPlatformVersion() { + throw UnimplementedError('platformVersion() has not been implemented.'); + } +} diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml new file mode 100644 index 000000000000..bb5b15b6baf4 --- /dev/null +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -0,0 +1,73 @@ +name: camera_android_camerax +description: A new Flutter plugin project. +repository: https://github.com/flutter/plugins/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 +homepage: +publish_to: 'none' + +environment: + sdk: '>=2.14.0 <3.0.0' + flutter: ">=2.5.0" + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + implements: camera + platforms: + android: + package: io.flutter.plugins.camerax + pluginClass: CameraAndroidCameraxPlugin + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.0.2 + +dev_dependencies: + flutter_lints: ^2.0.0 + flutter_test: + sdk: flutter diff --git a/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart b/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart new file mode 100644 index 000000000000..0a1ea847e1a5 --- /dev/null +++ b/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart @@ -0,0 +1,29 @@ +// 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:camera_android_camerax/camera_android_camerax_method_channel.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + final MethodChannelCameraAndroidCamerax platform = + MethodChannelCameraAndroidCamerax(); + const MethodChannel channel = MethodChannel('camera_android_camerax'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + return '42'; + }); + }); + + tearDown(() { + channel.setMockMethodCallHandler(null); + }); + + test('getPlatformVersion', () async { + expect(await platform.getPlatformVersion(), '42'); + }); +} diff --git a/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart b/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart new file mode 100644 index 000000000000..05ff46c1c55d --- /dev/null +++ b/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart @@ -0,0 +1,35 @@ +// 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:camera_android_camerax/camera_android_camerax.dart'; +import 'package:camera_android_camerax/camera_android_camerax_method_channel.dart'; +import 'package:camera_android_camerax/camera_android_camerax_platform_interface.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +class MockCameraAndroidCameraxPlatform + with MockPlatformInterfaceMixin + implements CameraAndroidCameraxPlatform { + @override + Future getPlatformVersion() => Future.value('42'); +} + +void main() { + final CameraAndroidCameraxPlatform initialPlatform = + CameraAndroidCameraxPlatform.instance; + + test('$MethodChannelCameraAndroidCamerax is the default instance', () { + expect(initialPlatform, isInstanceOf()); + }); + + test('getPlatformVersion', () async { + final CameraAndroidCamerax cameraAndroidCameraxPlugin = + CameraAndroidCamerax(); + final MockCameraAndroidCameraxPlatform fakePlatform = + MockCameraAndroidCameraxPlatform(); + CameraAndroidCameraxPlatform.instance = fakePlatform; + + expect(await cameraAndroidCameraxPlugin.getPlatformVersion(), '42'); + }); +} From 06558c0d09218e3e65130d0eb8db9f351ff92f8f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 17:10:01 +0000 Subject: [PATCH 608/844] [lifecycle]: Bump mockito-core from 1.10.19 to 4.7.0 in /packages/flutter_plugin_android_lifecycle/android (#6246) --- packages/flutter_plugin_android_lifecycle/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_plugin_android_lifecycle/android/build.gradle b/packages/flutter_plugin_android_lifecycle/android/build.gradle index 9702604009f3..a86e070bc693 100644 --- a/packages/flutter_plugin_android_lifecycle/android/build.gradle +++ b/packages/flutter_plugin_android_lifecycle/android/build.gradle @@ -54,6 +54,6 @@ android { dependencies { testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-core:1.10.19' + testImplementation 'org.mockito:mockito-core:4.7.0' } From 4adf7137bf2703c9d66f6bad979283fe6601c97b Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Mon, 15 Aug 2022 11:10:06 -0700 Subject: [PATCH 609/844] [multiple] format code (#6236) Apply new format rules to some dart files. This fixes the flutter -> plugin roll --- .ci/flutter_master.version | 2 +- .../integration_test/google_maps_plugin_test.mocks.dart | 4 ++-- .../image_picker/test/image_picker_test.mocks.dart | 4 ++-- .../test/android_webview_test.mocks.dart | 4 ++-- .../test/webview_android_widget_test.mocks.dart | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 9c00d6d8d192..0e1912635f02 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -9ad052a57e0f08f05c8f126f80b59dc06e2b81af +a0f1c670ed90ee1dbb656ad2b9c469bfbd529f19 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 744552f45d4d..18012f471a53 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 @@ -25,8 +25,8 @@ class _FakeStreamController_0 extends _i1.Fake class _FakeLatLngBounds_1 extends _i1.Fake implements _i3.LatLngBounds {} -class _FakeScreenCoordinate_2 extends _i1.Fake implements _i3.ScreenCoordinate { -} +class _FakeScreenCoordinate_2 extends _i1.Fake + implements _i3.ScreenCoordinate {} class _FakeLatLng_3 extends _i1.Fake implements _i3.LatLng {} diff --git a/packages/image_picker/image_picker/test/image_picker_test.mocks.dart b/packages/image_picker/image_picker/test/image_picker_test.mocks.dart index 641a104a33c5..a79d076dd42c 100644 --- a/packages/image_picker/image_picker/test/image_picker_test.mocks.dart +++ b/packages/image_picker/image_picker/test/image_picker_test.mocks.dart @@ -22,8 +22,8 @@ import 'package:mockito/mockito.dart' as _i1; class _FakeLostData_0 extends _i1.Fake implements _i2.LostData {} -class _FakeLostDataResponse_1 extends _i1.Fake implements _i2.LostDataResponse { -} +class _FakeLostDataResponse_1 extends _i1.Fake + implements _i2.LostDataResponse {} /// A class which mocks [ImagePickerPlatform]. /// 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 3a4043c22864..6afda598d93b 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 @@ -22,8 +22,8 @@ import 'test_android_webview.pigeon.dart' as _i5; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types -class _FakeDownloadListener_0 extends _i1.Fake implements _i2.DownloadListener { -} +class _FakeDownloadListener_0 extends _i1.Fake + implements _i2.DownloadListener {} class _FakeJavaScriptChannel_1 extends _i1.Fake implements _i2.JavaScriptChannel {} diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart index 04c2d778ed19..e17b31b3d857 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart @@ -28,8 +28,8 @@ class _FakeWebStorage_1 extends _i1.Fake implements _i2.WebStorage {} class _FakeWebView_2 extends _i1.Fake implements _i2.WebView {} -class _FakeDownloadListener_3 extends _i1.Fake implements _i2.DownloadListener { -} +class _FakeDownloadListener_3 extends _i1.Fake + implements _i2.DownloadListener {} class _FakeJavascriptChannelRegistry_4 extends _i1.Fake implements _i3.JavascriptChannelRegistry {} From fbeffa44e2b00480113d2488b78e9ae53db74416 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 18:10:29 +0000 Subject: [PATCH 610/844] [in_app_pur]: Bump mockito-core from 4.6.1 to 4.7.0 in /packages/in_app_purchase/in_app_purchase_android/android (#6259) --- .../in_app_purchase_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle index 0d9a03121d8a..d58d1cc7c052 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle +++ b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle @@ -56,7 +56,7 @@ dependencies { implementation 'com.android.billingclient:billing:5.0.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.json:json:20220320' - testImplementation 'org.mockito:mockito-core:4.6.1' + testImplementation 'org.mockito:mockito-core:4.7.0' androidTestImplementation 'androidx.test:runner:1.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' } From 85776e73fe087ec7001c82c2e0f9baab89b70f31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 19:15:51 +0000 Subject: [PATCH 611/844] [video_player]: Bump mockito-core from 4.6.1 to 4.7.0 in /packages/video_player/video_player_android/example/android/app (#6250) --- .../video_player_android/example/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player_android/example/android/app/build.gradle b/packages/video_player/video_player_android/example/android/app/build.gradle index 215deca1e2b0..84389f89e5e6 100644 --- a/packages/video_player/video_player_android/example/android/app/build.gradle +++ b/packages/video_player/video_player_android/example/android/app/build.gradle @@ -60,7 +60,7 @@ flutter { dependencies { testImplementation 'junit:junit:4.13' testImplementation 'org.robolectric:robolectric:4.4' - testImplementation 'org.mockito:mockito-core:4.6.1' + testImplementation 'org.mockito:mockito-core:4.7.0' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } From 5e4c28638ab6bef5eec9b3389d41c8d250f24ff7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 19:17:14 +0000 Subject: [PATCH 612/844] [quick_actions]: Bump mockito-core from 4.6.1 to 4.7.0 in /packages/quick_actions/quick_actions_android/android (#6248) --- .../quick_actions/quick_actions_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/quick_actions/quick_actions_android/android/build.gradle b/packages/quick_actions/quick_actions_android/android/build.gradle index ca7218c94d39..8cb1ca327251 100644 --- a/packages/quick_actions/quick_actions_android/android/build.gradle +++ b/packages/quick_actions/quick_actions_android/android/build.gradle @@ -35,7 +35,7 @@ android { dependencies { testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-core:4.6.1' + testImplementation 'org.mockito:mockito-core:4.7.0' } compileOptions { From edfcbea597634fbecedb3664071cc2775ce9f7ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 19:17:22 +0000 Subject: [PATCH 613/844] [quick_actions]: Bump mockito-core from 4.6.1 to 4.7.0 in /packages/quick_actions/quick_actions_android/example/android/app (#6258) --- .../quick_actions_android/example/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/quick_actions/quick_actions_android/example/android/app/build.gradle b/packages/quick_actions/quick_actions_android/example/android/app/build.gradle index b043dec31b3d..d01dce6dab26 100644 --- a/packages/quick_actions/quick_actions_android/example/android/app/build.gradle +++ b/packages/quick_actions/quick_actions_android/example/android/app/build.gradle @@ -62,6 +62,6 @@ dependencies { androidTestImplementation "androidx.test:runner:$androidXTestVersion" androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' androidTestImplementation 'androidx.test.ext:junit:1.0.0' - androidTestImplementation 'org.mockito:mockito-core:4.6.1' + androidTestImplementation 'org.mockito:mockito-core:4.7.0' androidTestImplementation 'org.mockito:mockito-android:4.7.0' } From d5d615fcd891b98455c89014c27ab7964af26a9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 03:00:33 +0000 Subject: [PATCH 614/844] [in_app_pur]: Bump mockito-core from 4.6.1 to 4.7.0 in /packages/in_app_purchase/in_app_purchase_android/example/android/app (#6254) --- .../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 f24b1a19c944..e2ec6af97c7a 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 @@ -108,7 +108,7 @@ flutter { dependencies { implementation 'com.android.billingclient:billing:5.0.0' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-core:4.6.1' + testImplementation 'org.mockito:mockito-core:4.7.0' testImplementation 'org.json:json:20180813' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' From d18079b41616b969de2448b8eac79aeb1d283fd5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 03:56:39 +0000 Subject: [PATCH 615/844] [in_app_pur]: Bump mockito-core from 4.6.1 to 4.7.0 in /packages/in_app_purchase/in_app_purchase/example/android/app (#6257) --- .../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 441b49cf373b..d0c62753faa1 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 @@ -108,7 +108,7 @@ flutter { dependencies { implementation 'com.android.billingclient:billing:3.0.2' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-core:4.6.1' + testImplementation 'org.mockito:mockito-core:4.7.0' testImplementation 'org.json:json:20180813' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' From ba27a65fc103904a1c5cf6bf71d7487dbcee9cb9 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 16 Aug 2022 11:03:39 -0400 Subject: [PATCH 616/844] Roll Flutter from a0f1c670ed90 to c873c2100ad7 (27 revisions) (#6275) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 7d1323c3a Updating expiring cirrus key. (flutter/flutter#109461) * a85902aa3 Roll Flutter Engine from de118a55ade0 to c354e0e04cea (2 revisions) (flutter/flutter#109462) * 28de3d49f Roll Flutter Engine from c354e0e04cea to d1b18b5d6811 (1 revision) (flutter/flutter#109466) * a624cb71b Keep `dirty` manipulations private to `Element` base class (flutter/flutter#109401) * dce82f7e2 Remove deprecated Ruby File.exists? in helper script (flutter/flutter#109428) * 7887ca5bb Revert "Keep `dirty` manipulations private to `Element` base class (#109401)" (flutter/flutter#109481) * 237a29835 Roll Flutter Engine from d1b18b5d6811 to a2f3bd58ce73 (1 revision) (flutter/flutter#109472) * fe2fc8daa Single tap on the previous selection should toggle the toolbar on iOS… (flutter/flutter#108913) * 696c6fb35 Roll Flutter Engine from a2f3bd58ce73 to fb19700742c3 (6 revisions) (flutter/flutter#109482) * 17bc0ceb2 Roll Flutter Engine from fb19700742c3 to 2180e62f6c7e (1 revision) (flutter/flutter#109483) * 4265015fd Roll Flutter Engine from 2180e62f6c7e to cd1a1cb757f5 (1 revision) (flutter/flutter#109488) * c142dd3df Roll Flutter Engine from cd1a1cb757f5 to 5cc5c5f04f0a (1 revision) (flutter/flutter#109490) * 8a55c366e Roll Flutter Engine from 5cc5c5f04f0a to 18dca3de7496 (2 revisions) (flutter/flutter#109493) * d50c5b1b6 Roll Flutter Engine from 18dca3de7496 to 83771593cb55 (1 revision) (flutter/flutter#109505) * 97901da14 Cleaner test.dart output. (flutter/flutter#109206) * fb3c8ea2b Roll Flutter Engine from 83771593cb55 to f49a617535af (1 revision) (flutter/flutter#109509) * 5fcd9c255 Update README.md (flutter/flutter#109506) * 4dd706524 Add onOpened callback to PopupMenuButton (flutter/flutter#103753) * ed3238e8e Roll Flutter Engine from f49a617535af to cc9f4c63b7e5 (1 revision) (flutter/flutter#109518) * 8b1ca3f79 Roll Flutter Engine from cc9f4c63b7e5 to 4656c2f46cad (1 revision) (flutter/flutter#109524) * 20ffb32fa Roll Flutter Engine from 4656c2f46cad to 16fb19e72f88 (1 revision) (flutter/flutter#109526) * 087750f2f Roll Flutter Engine from 16fb19e72f88 to 9c3c233e2639 (1 revision) (flutter/flutter#109532) * 4aba1248c Roll Flutter Engine from 9c3c233e2639 to f0c3829e90ce (1 revision) (flutter/flutter#109536) * 2bce10866 Roll Plugins from 9fb7654f3aa2 to 094f1c3ad94a (7 revisions) (flutter/flutter#109543) * 97d9a2f0d Roll Flutter Engine from f0c3829e90ce to 8c019cdd446f (2 revisions) (flutter/flutter#109548) * 458d618ad Roll Flutter Engine from 8c019cdd446f to 198b0051a5a2 (1 revision) (flutter/flutter#109552) * c873c2100 Roll Flutter Engine from 198b0051a5a2 to 33fccf564973 (1 revision) (flutter/flutter#109557) --- .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 0e1912635f02..9e57683efe64 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a0f1c670ed90ee1dbb656ad2b9c469bfbd529f19 +c873c2100ad79872cef92ab97dc96cc222dcdbf2 From 9c0049d9d17815508399b09c346fe94fee2385ad Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 15:23:58 +0000 Subject: [PATCH 617/844] [video_player]: Bump mockito-inline from 4.6.1 to 4.7.0 in /packages/video_player/video_player_android/android (#6247) --- packages/video_player/video_player_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player_android/android/build.gradle b/packages/video_player/video_player_android/android/build.gradle index 1165feaf5048..c5d1d35aacc5 100644 --- a/packages/video_player/video_player_android/android/build.gradle +++ b/packages/video_player/video_player_android/android/build.gradle @@ -49,7 +49,7 @@ android { implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.18.0' testImplementation 'junit:junit:4.13.2' testImplementation 'androidx.test:core:1.3.0' - testImplementation 'org.mockito:mockito-inline:4.6.1' + testImplementation 'org.mockito:mockito-inline:4.7.0' testImplementation 'org.robolectric:robolectric:4.8.1' } From 28350641d3faa86573205265652e53a458a65856 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 15:25:21 +0000 Subject: [PATCH 618/844] [local_auth]: Bump mockito-inline from 4.6.1 to 4.7.0 in /packages/local_auth/local_auth_android/android (#6256) --- packages/local_auth/local_auth_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/local_auth/local_auth_android/android/build.gradle b/packages/local_auth/local_auth_android/android/build.gradle index 8d2f40245f2d..b3b258683797 100644 --- a/packages/local_auth/local_auth_android/android/build.gradle +++ b/packages/local_auth/local_auth_android/android/build.gradle @@ -53,7 +53,7 @@ dependencies { api "androidx.biometric:biometric:1.1.0" api "androidx.fragment:fragment:1.5.1" testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-inline:4.6.1' + testImplementation 'org.mockito:mockito-inline:4.7.0' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:rules:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' From 79a7a90164268e042bdc931854df12bf0e5a9b7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 16:27:32 +0000 Subject: [PATCH 619/844] [video_player]: Bump exoplayerfrom 2.18.0 to 2.18.1 in /packages/video_player/video_player_android/android (#6142) --- packages/video_player/video_player_android/CHANGELOG.md | 3 ++- .../video_player_android/android/build.gradle | 9 ++++----- packages/video_player/video_player_android/pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 8c26cf7ab149..2c8c56b8861d 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.3.9 +* Updates ExoPlayer to 2.18.1. * Fixes avoid_redundant_argument_values lint warnings and minor typos. ## 2.3.8 diff --git a/packages/video_player/video_player_android/android/build.gradle b/packages/video_player/video_player_android/android/build.gradle index c5d1d35aacc5..2376d53a0ec1 100644 --- a/packages/video_player/video_player_android/android/build.gradle +++ b/packages/video_player/video_player_android/android/build.gradle @@ -43,17 +43,16 @@ android { } dependencies { - implementation 'com.google.android.exoplayer:exoplayer-core:2.18.0' - implementation 'com.google.android.exoplayer:exoplayer-hls:2.18.0' - implementation 'com.google.android.exoplayer:exoplayer-dash:2.18.0' - implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.18.0' + implementation 'com.google.android.exoplayer:exoplayer-core:2.18.1' + implementation 'com.google.android.exoplayer:exoplayer-hls:2.18.1' + implementation 'com.google.android.exoplayer:exoplayer-dash:2.18.1' + implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.18.1' testImplementation 'junit:junit:4.13.2' testImplementation 'androidx.test:core:1.3.0' testImplementation 'org.mockito:mockito-inline:4.7.0' testImplementation 'org.robolectric:robolectric:4.8.1' } - testOptions { unitTests.includeAndroidResources = true unitTests.returnDefaultValues = true diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index 056221a006d3..9039a2d8e90f 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_android description: Android implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.8 +version: 2.3.9 environment: sdk: ">=2.14.0 <3.0.0" From 87495400e08411eb55a48b2247f7bf2df5907374 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Tue, 16 Aug 2022 11:13:18 -0700 Subject: [PATCH 620/844] [google_maps_flutter_platform_interface] Adds size to BitmapDescriptor (1/2) (#6208) Adds a size parameter to the BitmapDescriptor.fromBytes constructor, so web applications can specify the actual physical size of the bitmap. The parameter is not needed (and ignored) in other platforms. Other platforms seem to be doing actual size / dpi to figure out the correct physical size. Fixes (part 1) flutter/flutter#73789 --- .../CHANGELOG.md | 5 +- .../lib/src/types/bitmap.dart | 18 ++++++- .../pubspec.yaml | 2 +- .../test/types/bitmap_test.dart | 53 +++++++++++++++++++ 4 files changed, 74 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 695a1b89ee42..dd32d787bddc 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,8 @@ -## NEXT +## 2.2.2 +* Adds a `size` parameter to `BitmapDescriptor.fromBytes`, so **web** applications + can specify the actual *physical size* of the bitmap. The parameter is not needed + (and ignored) in other platforms. Issue [#73789](https://github.com/flutter/flutter/issues/73789). * Fixes avoid_redundant_argument_values lint warnings and minor typos. ## 2.2.1 diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/bitmap.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/bitmap.dart index 0ccc3e624abe..7dda43a7abf4 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/bitmap.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/bitmap.dart @@ -157,8 +157,22 @@ class BitmapDescriptor { /// Creates a BitmapDescriptor using an array of bytes that must be encoded /// as PNG. - static BitmapDescriptor fromBytes(Uint8List byteData) { - return BitmapDescriptor._([_fromBytes, byteData]); + /// On the web, the [size] parameter represents the *physical size* of the + /// bitmap, regardless of the actual resolution of the encoded PNG. + /// This helps the browser to render High-DPI images at the correct size. + /// `size` is not required (and ignored, if passed) in other platforms. + static BitmapDescriptor fromBytes(Uint8List byteData, {Size? size}) { + assert(byteData.isNotEmpty, + 'Cannot create BitmapDescriptor with empty byteData'); + return BitmapDescriptor._([ + _fromBytes, + byteData, + if (kIsWeb && size != null) + [ + size.width, + size.height, + ] + ]); } final Object _json; 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 f1463c71230a..9f4b7c03fdb2 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/plugins/tree/main/packages/google_maps_fl issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.2.1 +version: 2.2.2 environment: sdk: '>=2.12.0 <3.0.0' diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/bitmap_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/bitmap_test.dart index 7fbaf4998355..2499e87bb649 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/bitmap_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/bitmap_test.dart @@ -2,8 +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 import 'dart:typed_data'; +import 'dart:ui'; +import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; @@ -26,6 +29,56 @@ void main() { expect(identical(descriptorFromJson.toJson(), json), isTrue); // Same JSON }); + group('fromBytes constructor', () { + test('with empty byte array, throws assertion error', () { + expect(() { + BitmapDescriptor.fromBytes(Uint8List.fromList([])); + }, throwsAssertionError); + }); + + test('with bytes', () { + final BitmapDescriptor descriptor = BitmapDescriptor.fromBytes( + Uint8List.fromList([1, 2, 3]), + ); + expect(descriptor, isA()); + expect( + descriptor.toJson(), + equals([ + 'fromBytes', + [1, 2, 3], + ])); + }); + + test('with size, not on the web, size is ignored', () { + final BitmapDescriptor descriptor = BitmapDescriptor.fromBytes( + Uint8List.fromList([1, 2, 3]), + size: const Size(40, 20), + ); + + expect( + descriptor.toJson(), + equals([ + 'fromBytes', + [1, 2, 3], + ])); + }, skip: kIsWeb); + + test('with size, on the web, size is preserved', () { + final BitmapDescriptor descriptor = BitmapDescriptor.fromBytes( + Uint8List.fromList([1, 2, 3]), + size: const Size(40, 20), + ); + + expect( + descriptor.toJson(), + equals([ + 'fromBytes', + [1, 2, 3], + [40, 20], + ])); + }, skip: !kIsWeb); + }); + group('fromJson validation', () { group('type validation', () { test('correct type', () { From 436685537737cd41ebc4e6c347c8b501658c08da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 18:13:24 +0000 Subject: [PATCH 621/844] [camera]: Bump gradle from 7.2.0 to 7.2.2 in /packages/camera/camera_android_camerax/android (#6272) --- packages/camera/camera_android_camerax/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android_camerax/android/build.gradle b/packages/camera/camera_android_camerax/android/build.gradle index 15932c52c527..4cb85eed3ba4 100644 --- a/packages/camera/camera_android_camerax/android/build.gradle +++ b/packages/camera/camera_android_camerax/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.2.0' + classpath 'com.android.tools.build:gradle:7.2.2' } } From 00f464f4e1673698de939762a8a88cd138130ee3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 18:15:07 +0000 Subject: [PATCH 622/844] [shared_pref]: Bump mockito-inline from 4.6.1 to 4.7.0 in /packages/shared_preferences/shared_preferences_android/android (#6264) --- .../shared_preferences_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index 770de0cfbd5c..89ca75c40d75 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -43,7 +43,7 @@ android { } dependencies { testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-inline:4.6.1' + testImplementation 'org.mockito:mockito-inline:4.7.0' } From c74873aac9b31c100ac66db0c566d28759e5b58f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 19:24:49 +0000 Subject: [PATCH 623/844] [webview]: Bump mockito-inline from 4.6.1 to 4.7.0 in /packages/webview_flutter/webview_flutter_android/android (#6260) --- .../webview_flutter_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webview_flutter/webview_flutter_android/android/build.gradle b/packages/webview_flutter/webview_flutter_android/android/build.gradle index 0f0eb67e760e..7c55559cb832 100644 --- a/packages/webview_flutter/webview_flutter_android/android/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/android/build.gradle @@ -38,7 +38,7 @@ android { implementation 'androidx.annotation:annotation:1.4.0' implementation 'androidx.webkit:webkit:1.0.0' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-inline:4.6.1' + testImplementation 'org.mockito:mockito-inline:4.7.0' testImplementation 'androidx.test:core:1.3.0' } From 9bb6771ea0f8d9867b299c70c1512d175e30e626 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Wed, 17 Aug 2022 02:39:33 -0700 Subject: [PATCH 624/844] [google_maps_flutter_web] Pass BitmapDescriptor.fromBytes size to JS SDK (2/2) (#6209) --- .../google_maps_flutter_web/CHANGELOG.md | 4 +- .../google_maps_controller_test.mocks.dart | 67 ++++++++------ .../google_maps_plugin_test.mocks.dart | 88 +++++++++++++------ .../integration_test/markers_test.dart | 40 +++++++-- .../lib/src/convert.dart | 57 ++++++++---- .../google_maps_flutter_web/pubspec.yaml | 4 +- 6 files changed, 179 insertions(+), 81 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 d766e0d57968..68e6a3f5b0b9 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,7 @@ -## NEXT +## 0.4.0+2 +* Updates conversion of `BitmapDescriptor.fromBytes` marker icons to support the + new `size` parameter. Issue [#73789](https://github.com/flutter/flutter/issues/73789). * Fixes avoid_redundant_argument_values lint warnings and minor typos. ## 0.4.0+1 diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart index 9565935bd8ed..a7fe6bae347b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart @@ -1,7 +1,8 @@ -// Mocks generated by Mockito 5.2.0 from annotations +// Mocks generated by Mockito 5.3.0 from annotations // in google_maps_flutter_web_integration_tests/integration_test/google_maps_controller_test.dart. // Do not manually edit this file. +// ignore_for_file: no_leading_underscores_for_library_prefixes import 'package:google_maps/google_maps.dart' as _i2; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart' as _i4; @@ -17,8 +18,12 @@ import 'package:mockito/mockito.dart' as _i1; // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class -class _FakeGMap_0 extends _i1.Fake implements _i2.GMap {} +class _FakeGMap_0 extends _i1.SmartFake implements _i2.GMap { + _FakeGMap_0(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} /// A class which mocks [CirclesController]. /// @@ -27,18 +32,21 @@ class MockCirclesController extends _i1.Mock implements _i3.CirclesController { @override Map<_i4.CircleId, _i3.CircleController> get circles => (super.noSuchMethod(Invocation.getter(#circles), - returnValue: <_i4.CircleId, _i3.CircleController>{}) + returnValue: <_i4.CircleId, _i3.CircleController>{}, + returnValueForMissingStub: <_i4.CircleId, _i3.CircleController>{}) as Map<_i4.CircleId, _i3.CircleController>); @override _i2.GMap get googleMap => (super.noSuchMethod(Invocation.getter(#googleMap), - returnValue: _FakeGMap_0()) as _i2.GMap); + returnValue: _FakeGMap_0(this, Invocation.getter(#googleMap)), + returnValueForMissingStub: + _FakeGMap_0(this, Invocation.getter(#googleMap))) as _i2.GMap); @override set googleMap(_i2.GMap? _googleMap) => super.noSuchMethod(Invocation.setter(#googleMap, _googleMap), returnValueForMissingStub: null); @override - int get mapId => - (super.noSuchMethod(Invocation.getter(#mapId), returnValue: 0) as int); + int get mapId => (super.noSuchMethod(Invocation.getter(#mapId), + returnValue: 0, returnValueForMissingStub: 0) as int); @override set mapId(int? _mapId) => super.noSuchMethod(Invocation.setter(#mapId, _mapId), @@ -67,20 +75,23 @@ class MockCirclesController extends _i1.Mock implements _i3.CirclesController { class MockPolygonsController extends _i1.Mock implements _i3.PolygonsController { @override - Map<_i4.PolygonId, _i3.PolygonController> get polygons => - (super.noSuchMethod(Invocation.getter(#polygons), - returnValue: <_i4.PolygonId, _i3.PolygonController>{}) - as Map<_i4.PolygonId, _i3.PolygonController>); + Map<_i4.PolygonId, _i3.PolygonController> get polygons => (super.noSuchMethod( + Invocation.getter(#polygons), + returnValue: <_i4.PolygonId, _i3.PolygonController>{}, + returnValueForMissingStub: <_i4.PolygonId, _i3.PolygonController>{}) + as Map<_i4.PolygonId, _i3.PolygonController>); @override _i2.GMap get googleMap => (super.noSuchMethod(Invocation.getter(#googleMap), - returnValue: _FakeGMap_0()) as _i2.GMap); + returnValue: _FakeGMap_0(this, Invocation.getter(#googleMap)), + returnValueForMissingStub: + _FakeGMap_0(this, Invocation.getter(#googleMap))) as _i2.GMap); @override set googleMap(_i2.GMap? _googleMap) => super.noSuchMethod(Invocation.setter(#googleMap, _googleMap), returnValueForMissingStub: null); @override - int get mapId => - (super.noSuchMethod(Invocation.getter(#mapId), returnValue: 0) as int); + int get mapId => (super.noSuchMethod(Invocation.getter(#mapId), + returnValue: 0, returnValueForMissingStub: 0) as int); @override set mapId(int? _mapId) => super.noSuchMethod(Invocation.setter(#mapId, _mapId), @@ -109,20 +120,23 @@ class MockPolygonsController extends _i1.Mock class MockPolylinesController extends _i1.Mock implements _i3.PolylinesController { @override - Map<_i4.PolylineId, _i3.PolylineController> get lines => - (super.noSuchMethod(Invocation.getter(#lines), - returnValue: <_i4.PolylineId, _i3.PolylineController>{}) - as Map<_i4.PolylineId, _i3.PolylineController>); + Map<_i4.PolylineId, _i3.PolylineController> get lines => (super.noSuchMethod( + Invocation.getter(#lines), + returnValue: <_i4.PolylineId, _i3.PolylineController>{}, + returnValueForMissingStub: <_i4.PolylineId, _i3.PolylineController>{}) + as Map<_i4.PolylineId, _i3.PolylineController>); @override _i2.GMap get googleMap => (super.noSuchMethod(Invocation.getter(#googleMap), - returnValue: _FakeGMap_0()) as _i2.GMap); + returnValue: _FakeGMap_0(this, Invocation.getter(#googleMap)), + returnValueForMissingStub: + _FakeGMap_0(this, Invocation.getter(#googleMap))) as _i2.GMap); @override set googleMap(_i2.GMap? _googleMap) => super.noSuchMethod(Invocation.setter(#googleMap, _googleMap), returnValueForMissingStub: null); @override - int get mapId => - (super.noSuchMethod(Invocation.getter(#mapId), returnValue: 0) as int); + int get mapId => (super.noSuchMethod(Invocation.getter(#mapId), + returnValue: 0, returnValueForMissingStub: 0) as int); @override set mapId(int? _mapId) => super.noSuchMethod(Invocation.setter(#mapId, _mapId), @@ -152,18 +166,21 @@ class MockMarkersController extends _i1.Mock implements _i3.MarkersController { @override Map<_i4.MarkerId, _i3.MarkerController> get markers => (super.noSuchMethod(Invocation.getter(#markers), - returnValue: <_i4.MarkerId, _i3.MarkerController>{}) + returnValue: <_i4.MarkerId, _i3.MarkerController>{}, + returnValueForMissingStub: <_i4.MarkerId, _i3.MarkerController>{}) as Map<_i4.MarkerId, _i3.MarkerController>); @override _i2.GMap get googleMap => (super.noSuchMethod(Invocation.getter(#googleMap), - returnValue: _FakeGMap_0()) as _i2.GMap); + returnValue: _FakeGMap_0(this, Invocation.getter(#googleMap)), + returnValueForMissingStub: + _FakeGMap_0(this, Invocation.getter(#googleMap))) as _i2.GMap); @override set googleMap(_i2.GMap? _googleMap) => super.noSuchMethod(Invocation.setter(#googleMap, _googleMap), returnValueForMissingStub: null); @override - int get mapId => - (super.noSuchMethod(Invocation.getter(#mapId), returnValue: 0) as int); + int get mapId => (super.noSuchMethod(Invocation.getter(#mapId), + returnValue: 0, returnValueForMissingStub: 0) as int); @override set mapId(int? _mapId) => super.noSuchMethod(Invocation.setter(#mapId, _mapId), @@ -191,7 +208,7 @@ class MockMarkersController extends _i1.Mock implements _i3.MarkersController { @override bool isInfoWindowShown(_i4.MarkerId? markerId) => (super.noSuchMethod(Invocation.method(#isInfoWindowShown, [markerId]), - returnValue: false) as bool); + returnValue: false, returnValueForMissingStub: false) as bool); @override void bindToMap(int? mapId, _i2.GMap? googleMap) => super.noSuchMethod(Invocation.method(#bindToMap, [mapId, googleMap]), 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 18012f471a53..04cbc4a3416d 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 @@ -1,7 +1,8 @@ -// Mocks generated by Mockito 5.2.0 from annotations +// Mocks generated by Mockito 5.3.0 from annotations // in google_maps_flutter_web_integration_tests/integration_test/google_maps_plugin_test.dart. // Do not manually edit this file. +// ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i2; import 'package:google_maps/google_maps.dart' as _i5; @@ -19,16 +20,29 @@ import 'package:mockito/mockito.dart' as _i1; // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class -class _FakeStreamController_0 extends _i1.Fake - implements _i2.StreamController {} +class _FakeStreamController_0 extends _i1.SmartFake + implements _i2.StreamController { + _FakeStreamController_0(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} -class _FakeLatLngBounds_1 extends _i1.Fake implements _i3.LatLngBounds {} +class _FakeLatLngBounds_1 extends _i1.SmartFake implements _i3.LatLngBounds { + _FakeLatLngBounds_1(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} -class _FakeScreenCoordinate_2 extends _i1.Fake - implements _i3.ScreenCoordinate {} +class _FakeScreenCoordinate_2 extends _i1.SmartFake + implements _i3.ScreenCoordinate { + _FakeScreenCoordinate_2(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} -class _FakeLatLng_3 extends _i1.Fake implements _i3.LatLng {} +class _FakeLatLng_3 extends _i1.SmartFake implements _i3.LatLng { + _FakeLatLng_3(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} /// A class which mocks [GoogleMapController]. /// @@ -36,19 +50,23 @@ class _FakeLatLng_3 extends _i1.Fake implements _i3.LatLng {} class MockGoogleMapController extends _i1.Mock implements _i4.GoogleMapController { @override - _i2.StreamController<_i3.MapEvent> get stream => - (super.noSuchMethod(Invocation.getter(#stream), - returnValue: _FakeStreamController_0<_i3.MapEvent>()) - as _i2.StreamController<_i3.MapEvent>); + _i2.StreamController<_i3.MapEvent> get stream => (super.noSuchMethod( + Invocation.getter(#stream), + returnValue: _FakeStreamController_0<_i3.MapEvent>( + this, Invocation.getter(#stream)), + returnValueForMissingStub: _FakeStreamController_0<_i3.MapEvent>( + this, Invocation.getter(#stream))) as _i2 + .StreamController<_i3.MapEvent>); @override - _i2.Stream<_i3.MapEvent> get events => - (super.noSuchMethod(Invocation.getter(#events), - returnValue: Stream<_i3.MapEvent>.empty()) - as _i2.Stream<_i3.MapEvent>); + _i2.Stream<_i3.MapEvent> get events => (super.noSuchMethod( + Invocation.getter(#events), + returnValue: _i2.Stream<_i3.MapEvent>.empty(), + returnValueForMissingStub: _i2.Stream<_i3.MapEvent>.empty()) + as _i2.Stream<_i3.MapEvent>); @override bool get isInitialized => - (super.noSuchMethod(Invocation.getter(#isInitialized), returnValue: false) - as bool); + (super.noSuchMethod(Invocation.getter(#isInitialized), + returnValue: false, returnValueForMissingStub: false) as bool); @override void debugSetOverrides( {_i4.DebugCreateMapFunction? createMap, @@ -78,29 +96,43 @@ class MockGoogleMapController extends _i1.Mock returnValueForMissingStub: null); @override _i2.Future<_i3.LatLngBounds> getVisibleRegion() => (super.noSuchMethod( - Invocation.method(#getVisibleRegion, []), - returnValue: Future<_i3.LatLngBounds>.value(_FakeLatLngBounds_1())) - as _i2.Future<_i3.LatLngBounds>); + Invocation.method(#getVisibleRegion, []), + returnValue: _i2.Future<_i3.LatLngBounds>.value( + _FakeLatLngBounds_1(this, Invocation.method(#getVisibleRegion, []))), + returnValueForMissingStub: _i2.Future<_i3.LatLngBounds>.value( + _FakeLatLngBounds_1( + this, Invocation.method(#getVisibleRegion, [])))) as _i2 + .Future<_i3.LatLngBounds>); @override _i2.Future<_i3.ScreenCoordinate> getScreenCoordinate(_i3.LatLng? latLng) => (super.noSuchMethod(Invocation.method(#getScreenCoordinate, [latLng]), - returnValue: - Future<_i3.ScreenCoordinate>.value(_FakeScreenCoordinate_2())) + returnValue: _i2.Future<_i3.ScreenCoordinate>.value( + _FakeScreenCoordinate_2( + this, Invocation.method(#getScreenCoordinate, [latLng]))), + returnValueForMissingStub: _i2.Future<_i3.ScreenCoordinate>.value( + _FakeScreenCoordinate_2( + this, Invocation.method(#getScreenCoordinate, [latLng])))) as _i2.Future<_i3.ScreenCoordinate>); @override _i2.Future<_i3.LatLng> getLatLng(_i3.ScreenCoordinate? screenCoordinate) => (super.noSuchMethod(Invocation.method(#getLatLng, [screenCoordinate]), - returnValue: Future<_i3.LatLng>.value(_FakeLatLng_3())) - as _i2.Future<_i3.LatLng>); + returnValue: _i2.Future<_i3.LatLng>.value(_FakeLatLng_3( + this, Invocation.method(#getLatLng, [screenCoordinate]))), + returnValueForMissingStub: _i2.Future<_i3.LatLng>.value(_FakeLatLng_3( + this, Invocation.method(#getLatLng, [screenCoordinate])))) as _i2 + .Future<_i3.LatLng>); @override _i2.Future moveCamera(_i3.CameraUpdate? cameraUpdate) => (super.noSuchMethod(Invocation.method(#moveCamera, [cameraUpdate]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i2.Future); + returnValue: _i2.Future.value(), + returnValueForMissingStub: _i2.Future.value()) + as _i2.Future); @override _i2.Future getZoomLevel() => (super.noSuchMethod(Invocation.method(#getZoomLevel, []), - returnValue: Future.value(0.0)) as _i2.Future); + returnValue: _i2.Future.value(0.0), + returnValueForMissingStub: _i2.Future.value(0.0)) + as _i2.Future); @override void updateCircles(_i3.CircleUpdates? updates) => super.noSuchMethod(Invocation.method(#updateCircles, [updates]), @@ -128,7 +160,7 @@ class MockGoogleMapController extends _i1.Mock @override bool isInfoWindowShown(_i3.MarkerId? markerId) => (super.noSuchMethod(Invocation.method(#isInfoWindowShown, [markerId]), - returnValue: false) as bool); + returnValue: false, returnValueForMissingStub: false) as bool); @override void dispose() => super.noSuchMethod(Invocation.method(#dispose, []), returnValueForMissingStub: null); 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 90195ec6397b..e4c4dd7c0cfe 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 @@ -5,8 +5,8 @@ import 'dart:async'; import 'dart:convert'; import 'dart:html' as html; -import 'dart:js_util' show getProperty; import 'dart:typed_data'; +import 'dart:ui'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps/google_maps.dart' as gmaps; @@ -155,22 +155,46 @@ void main() { controller.addMarkers(markers); expect(controller.markers.length, 1); - expect(controller.markers[const MarkerId('1')]?.marker?.icon, isNotNull); - - final String blobUrl = getProperty( - controller.markers[const MarkerId('1')]!.marker!.icon!, - 'url', - ); + final gmaps.Icon? icon = + controller.markers[const MarkerId('1')]?.marker?.icon as gmaps.Icon?; + expect(icon, isNotNull); + final String blobUrl = icon!.url!; expect(blobUrl, startsWith('blob:')); final http.Response response = await http.get(Uri.parse(blobUrl)); - expect(response.bodyBytes, bytes, reason: 'Bytes from the Icon blob must match bytes used to create Marker'); }); + // https://github.com/flutter/flutter/issues/73789 + testWidgets('markers with custom bitmap icon pass size to sdk', + (WidgetTester tester) async { + final Uint8List bytes = const Base64Decoder().convert(iconImageBase64); + final Set markers = { + Marker( + markerId: const MarkerId('1'), + icon: BitmapDescriptor.fromBytes(bytes, size: const Size(20, 30)), + ), + }; + + controller.addMarkers(markers); + + expect(controller.markers.length, 1); + final gmaps.Icon? icon = + controller.markers[const MarkerId('1')]?.marker?.icon as gmaps.Icon?; + expect(icon, isNotNull); + + final gmaps.Size size = icon!.size!; + final gmaps.Size scaledSize = icon.scaledSize!; + + expect(size.width, 20); + expect(size.height, 30); + expect(scaledSize.width, 20); + expect(scaledSize.height, 30); + }); + // https://github.com/flutter/flutter/issues/67854 testWidgets('InfoWindow snippet can have links', (WidgetTester tester) async { 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 250bb5468fa7..2b09950cc00d 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 @@ -228,14 +228,25 @@ gmaps.InfoWindowOptions? _infoWindowOptionsFromMarker(Marker marker) { // and the marker.infoWindow.anchor property. } -// Computes the options for a new [gmaps.Marker] from an incoming set of options -// [marker], and the existing marker registered with the map: [currentMarker]. -// Preserves the position from the [currentMarker], if set. -gmaps.MarkerOptions _markerOptionsFromMarker( - Marker marker, - gmaps.Marker? currentMarker, -) { - final List iconConfig = marker.icon.toJson() as List; +// Attempts to extract a [gmaps.Size] from `iconConfig[sizeIndex]`. +gmaps.Size? _gmSizeFromIconConfig(List iconConfig, int sizeIndex) { + gmaps.Size? size; + if (iconConfig.length >= sizeIndex + 1) { + final List? rawIconSize = iconConfig[sizeIndex] as List?; + if (rawIconSize != null) { + size = gmaps.Size( + rawIconSize[0] as num?, + rawIconSize[1] as num?, + ); + } + } + return size; +} + +// Converts a [BitmapDescriptor] into a [gmaps.Icon] that can be used in Markers. +gmaps.Icon? _gmIconFromBitmapDescriptor(BitmapDescriptor bitmapDescriptor) { + final List iconConfig = bitmapDescriptor.toJson() as List; + gmaps.Icon? icon; if (iconConfig != null) { @@ -243,17 +254,11 @@ gmaps.MarkerOptions _markerOptionsFromMarker( assert(iconConfig.length >= 2); // iconConfig[2] contains the DPIs of the screen, but that information is // already encoded in the iconConfig[1] - icon = gmaps.Icon() ..url = ui.webOnlyAssetManager.getAssetUrl(iconConfig[1]! as String); - // iconConfig[3] may contain the [width, height] of the image, if passed! - if (iconConfig.length >= 4 && iconConfig[3] != null) { - final List rawIconSize = iconConfig[3]! as List; - final gmaps.Size size = gmaps.Size( - rawIconSize[0] as num?, - rawIconSize[1] as num?, - ); + final gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 3); + if (size != null) { icon ..size = size ..scaledSize = size; @@ -264,8 +269,26 @@ gmaps.MarkerOptions _markerOptionsFromMarker( // 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 gmaps.Size? size = _gmSizeFromIconConfig(iconConfig, 2); + if (size != null) { + icon + ..size = size + ..scaledSize = size; + } } } + + return icon; +} + +// Computes the options for a new [gmaps.Marker] from an incoming set of options +// [marker], and the existing marker registered with the map: [currentMarker]. +// Preserves the position from the [currentMarker], if set. +gmaps.MarkerOptions _markerOptionsFromMarker( + Marker marker, + gmaps.Marker? currentMarker, +) { return gmaps.MarkerOptions() ..position = currentMarker?.position ?? gmaps.LatLng( @@ -277,7 +300,7 @@ gmaps.MarkerOptions _markerOptionsFromMarker( ..visible = marker.visible ..opacity = marker.alpha ..draggable = marker.draggable - ..icon = icon; + ..icon = _gmIconFromBitmapDescriptor(marker.icon); // TODO(ditman): Compute anchor properly, otherwise infowindows attach to the wrong spot. // Flat and Rotation are not supported directly on the web. } 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 9670e0f28e2e..731b90547638 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/plugins/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.4.0+1 +version: 0.4.0+2 environment: sdk: ">=2.12.0 <3.0.0" @@ -22,7 +22,7 @@ dependencies: flutter_web_plugins: sdk: flutter google_maps: ^6.1.0 - google_maps_flutter_platform_interface: ^2.2.0 + google_maps_flutter_platform_interface: ^2.2.2 sanitize_html: ^2.0.0 stream_transform: ^2.0.0 From 01163e8ab8075e014da389bc2ffe478024c35f4e Mon Sep 17 00:00:00 2001 From: Paul Berry Date: Wed, 17 Aug 2022 06:08:22 -0700 Subject: [PATCH 625/844] Prepare for fix to https://github.com/flutter/flutter/issues/109339. (#6278) Currently, in some circumstances where a subclasses of `PlatformInterface` erroneously uses `implements` rather than `extends`, a `NoSuchMethodError` will be thrown (in spite of the documentation at https://pub.dev/documentation/plugin_platform_interface/latest/plugin_platform_interface/PlatformInterface/verify.html claiming that `AssertionError` will be thrown). After https://github.com/flutter/flutter/issues/109339 is fixed, the correct type of exception will be thrown. To avoid tests breakages when the fix happens, we need to modify these tests so that they don't care what kind of exception is thrown. --- .../test/camera_platform_interface_test.dart | 9 ++++++++- .../google_maps_flutter_platform_test.dart | 9 ++++++++- .../test/google_sign_in_platform_interface_test.dart | 9 ++++++++- .../test/in_app_purchase_platform_test.dart | 11 +++++++++-- .../test/quick_actions_platform_interface_test.dart | 9 ++++++++- .../test/method_channel_url_launcher_test.dart | 9 ++++++++- .../src/v4/platform_navigation_delegate_test.dart | 9 ++++++++- .../test/src/v4/platform_webview_controller_test.dart | 9 ++++++++- .../test/src/v4/platform_webview_widget_test.dart | 9 ++++++++- .../test/src/v4/webview_platform_test.dart | 9 ++++++++- 10 files changed, 81 insertions(+), 11 deletions(-) diff --git a/packages/camera/camera_platform_interface/test/camera_platform_interface_test.dart b/packages/camera/camera_platform_interface/test/camera_platform_interface_test.dart index 3060089bef40..eab518ce3b23 100644 --- a/packages/camera/camera_platform_interface/test/camera_platform_interface_test.dart +++ b/packages/camera/camera_platform_interface/test/camera_platform_interface_test.dart @@ -18,7 +18,14 @@ void main() { test('Cannot be implemented with `implements`', () { expect(() { CameraPlatform.instance = ImplementsCameraPlatform(); - }, throwsNoSuchMethodError); + // In versions of `package:plugin_platform_interface` prior to fixing + // https://github.com/flutter/flutter/issues/109339, an attempt to + // implement a platform interface using `implements` would sometimes + // throw a `NoSuchMethodError` and other times throw an + // `AssertionError`. After the issue is fixed, an `AssertionError` will + // always be thrown. For the purpose of this test, we don't really care + // what exception is thrown, so just allow any exception. + }, throwsA(anything)); }); test('Can be extended', () { 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 d185aabe1a5c..d1dba2b75b55 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 @@ -27,7 +27,14 @@ void main() { expect(() { GoogleMapsFlutterPlatform.instance = ImplementsGoogleMapsFlutterPlatform(); - }, throwsA(isInstanceOf())); + // In versions of `package:plugin_platform_interface` prior to fixing + // https://github.com/flutter/flutter/issues/109339, an attempt to + // implement a platform interface using `implements` would sometimes + // throw a `NoSuchMethodError` and other times throw an + // `AssertionError`. After the issue is fixed, an `AssertionError` will + // always be thrown. For the purpose of this test, we don't really care + // what exception is thrown, so just allow any exception. + }, throwsA(anything)); }); test('Can be mocked with `implements`', () { diff --git a/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart b/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart index bf960abc7375..6ffa85fa1e4b 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart @@ -18,7 +18,14 @@ void main() { test('Cannot be implemented with `implements`', () { expect(() { GoogleSignInPlatform.instance = ImplementsGoogleSignInPlatform(); - }, throwsA(isA())); + // In versions of `package:plugin_platform_interface` prior to fixing + // https://github.com/flutter/flutter/issues/109339, an attempt to + // implement a platform interface using `implements` would sometimes + // throw a `NoSuchMethodError` and other times throw an + // `AssertionError`. After the issue is fixed, an `AssertionError` will + // always be thrown. For the purpose of this test, we don't really care + // what exception is thrown, so just allow any exception. + }, throwsA(anything)); }); test('Can be extended', () { diff --git a/packages/in_app_purchase/in_app_purchase_platform_interface/test/in_app_purchase_platform_test.dart b/packages/in_app_purchase/in_app_purchase_platform_interface/test/in_app_purchase_platform_test.dart index 9c0f2dc00020..879ad9c4c633 100644 --- a/packages/in_app_purchase/in_app_purchase_platform_interface/test/in_app_purchase_platform_test.dart +++ b/packages/in_app_purchase/in_app_purchase_platform_interface/test/in_app_purchase_platform_test.dart @@ -14,7 +14,14 @@ void main() { test('Cannot be implemented with `implements`', () { expect(() { InAppPurchasePlatform.instance = ImplementsInAppPurchasePlatform(); - }, throwsNoSuchMethodError); + // In versions of `package:plugin_platform_interface` prior to fixing + // https://github.com/flutter/flutter/issues/109339, an attempt to + // implement a platform interface using `implements` would sometimes + // throw a `NoSuchMethodError` and other times throw an + // `AssertionError`. After the issue is fixed, an `AssertionError` will + // always be thrown. For the purpose of this test, we don't really care + // what exception is thrown, so just allow any exception. + }, throwsA(anything)); }); test('Can be extended', () { @@ -126,7 +133,7 @@ void main() { InAppPurchasePlatformAddition.instance = null; }); - test('Cannot be implemented with `implements`', () { + test('Default instance is null', () { expect(InAppPurchasePlatformAddition.instance, isNull); }); diff --git a/packages/quick_actions/quick_actions_platform_interface/test/quick_actions_platform_interface_test.dart b/packages/quick_actions/quick_actions_platform_interface/test/quick_actions_platform_interface_test.dart index b9655dc56a3c..ab3299b0cc14 100644 --- a/packages/quick_actions/quick_actions_platform_interface/test/quick_actions_platform_interface_test.dart +++ b/packages/quick_actions/quick_actions_platform_interface/test/quick_actions_platform_interface_test.dart @@ -21,7 +21,14 @@ void main() { test('Cannot be implemented with `implements`', () { expect(() { QuickActionsPlatform.instance = ImplementsQuickActionsPlatform(); - }, throwsNoSuchMethodError); + // In versions of `package:plugin_platform_interface` prior to fixing + // https://github.com/flutter/flutter/issues/109339, an attempt to + // implement a platform interface using `implements` would sometimes + // throw a `NoSuchMethodError` and other times throw an + // `AssertionError`. After the issue is fixed, an `AssertionError` will + // always be thrown. For the purpose of this test, we don't really care + // what exception is thrown, so just allow any exception. + }, throwsA(anything)); }); test('Can be extended', () { diff --git a/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart b/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart index e44e80bab02c..c8ec08c53095 100644 --- a/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart +++ b/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart @@ -24,7 +24,14 @@ void main() { test('Cannot be implemented with `implements`', () { expect(() { UrlLauncherPlatform.instance = ImplementsUrlLauncherPlatform(); - }, throwsA(isInstanceOf())); + // In versions of `package:plugin_platform_interface` prior to fixing + // https://github.com/flutter/flutter/issues/109339, an attempt to + // implement a platform interface using `implements` would sometimes + // throw a `NoSuchMethodError` and other times throw an + // `AssertionError`. After the issue is fixed, an `AssertionError` will + // always be thrown. For the purpose of this test, we don't really care + // what exception is thrown, so just allow any exception. + }, throwsA(anything)); }); test('Can be mocked with `implements`', () { diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_navigation_delegate_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_navigation_delegate_test.dart index 5674c1522408..dd4a26c4faf9 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_navigation_delegate_test.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_navigation_delegate_test.dart @@ -23,7 +23,14 @@ void main() { expect(() { PlatformNavigationDelegate(params); - }, throwsNoSuchMethodError); + // In versions of `package:plugin_platform_interface` prior to fixing + // https://github.com/flutter/flutter/issues/109339, an attempt to + // implement a platform interface using `implements` would sometimes throw + // a `NoSuchMethodError` and other times throw an `AssertionError`. After + // the issue is fixed, an `AssertionError` will always be thrown. For the + // purpose of this test, we don't really care what exception is thrown, so + // just allow any exception. + }, throwsA(anything)); }); test('Can be extended', () { diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.dart index b6d043cac9c8..32374fb04484 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_controller_test.dart @@ -28,7 +28,14 @@ void main() { expect(() { PlatformWebViewController( const PlatformWebViewControllerCreationParams()); - }, throwsNoSuchMethodError); + // In versions of `package:plugin_platform_interface` prior to fixing + // https://github.com/flutter/flutter/issues/109339, an attempt to + // implement a platform interface using `implements` would sometimes throw + // a `NoSuchMethodError` and other times throw an `AssertionError`. After + // the issue is fixed, an `AssertionError` will always be thrown. For the + // purpose of this test, we don't really care what exception is thrown, so + // just allow any exception. + }, throwsA(anything)); }); test('Can be extended', () { diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.dart index 30fa52ece24a..ede16c162413 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.dart @@ -27,7 +27,14 @@ void main() { expect(() { PlatformWebViewWidget(params); - }, throwsNoSuchMethodError); + // In versions of `package:plugin_platform_interface` prior to fixing + // https://github.com/flutter/flutter/issues/109339, an attempt to + // implement a platform interface using `implements` would sometimes throw + // a `NoSuchMethodError` and other times throw an `AssertionError`. After + // the issue is fixed, an `AssertionError` will always be thrown. For the + // purpose of this test, we don't really care what exception is thrown, so + // just allow any exception. + }, throwsA(anything)); }); test('Can be extended', () { diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.dart index f09156919512..4ab6d587b879 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/webview_platform_test.dart @@ -22,7 +22,14 @@ void main() { test('Cannot be implemented with `implements`', () { expect(() { WebViewPlatform.instance = ImplementsWebViewPlatform(); - }, throwsNoSuchMethodError); + // In versions of `package:plugin_platform_interface` prior to fixing + // https://github.com/flutter/flutter/issues/109339, an attempt to + // implement a platform interface using `implements` would sometimes throw + // a `NoSuchMethodError` and other times throw an `AssertionError`. After + // the issue is fixed, an `AssertionError` will always be thrown. For the + // purpose of this test, we don't really care what exception is thrown, so + // just allow any exception. + }, throwsA(anything)); }); test('Can be extended', () { From 0d3d520952c114979b490e7ea9f29cb01d99c610 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 17 Aug 2022 10:08:46 -0400 Subject: [PATCH 626/844] [google_maps_flutter] Publish federation (#6204) Restored the app-facing package to publishable state, using the newly-published federated mobile implementations. Part of https://github.com/flutter/flutter/issues/68498 --- .../google_maps_flutter/CHANGELOG.md | 2 +- .../google_maps_flutter/pubspec.yaml | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 91eb91bcd0ff..ce116ee67c0b 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,4 +1,4 @@ -## NEXT +## 2.1.11 * Fixes avoid_redundant_argument_values lint warnings and minor typos. * Moves Android and iOS implementations to federated packages. diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index 8d4e49f0ab4a..1e692cb1931e 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,10 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/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.1.10 -# Temporarily disable publishing to allow moving Android and iOS -# implementations. -publish_to: none +version: 2.1.11 environment: sdk: ">=2.14.0 <3.0.0" @@ -22,11 +19,8 @@ flutter: dependencies: flutter: sdk: flutter - # Temporary path dependencies to allow moving Android and iOS implementations. - google_maps_flutter_android: - path: ../google_maps_flutter_android - google_maps_flutter_ios: - path: ../google_maps_flutter_ios + google_maps_flutter_android: ^2.1.10 + google_maps_flutter_ios: ^2.1.10 google_maps_flutter_platform_interface: ^2.2.1 dev_dependencies: From 02a003301a8edeb3ed69bdd9200c57e4f383765c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 17 Aug 2022 11:47:05 -0400 Subject: [PATCH 627/844] Roll Flutter from c873c2100ad7 to cb936c51f8b2 (40 revisions) (#6285) --- .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 9e57683efe64..697b03a758e8 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c873c2100ad79872cef92ab97dc96cc222dcdbf2 +cb936c51f8b2abf4a78ef8cf5ebb28c1ce3b9371 From c8167591431eccea9b477f2fa3391335151cbb20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Aug 2022 16:40:01 +0000 Subject: [PATCH 628/844] [camera]: Bump core from 1.3.0 to 1.4.0 in /packages/camera/camera_android/android (#6018) --- packages/camera/camera_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android/android/build.gradle b/packages/camera/camera_android/android/build.gradle index b4e7d7a455be..b060181316c1 100644 --- a/packages/camera/camera_android/android/build.gradle +++ b/packages/camera/camera_android/android/build.gradle @@ -62,6 +62,6 @@ dependencies { compileOnly 'androidx.annotation:annotation:1.1.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:4.7.0' - testImplementation 'androidx.test:core:1.3.0' + testImplementation 'androidx.test:core:1.4.0' testImplementation 'org.robolectric:robolectric:4.5' } From fbdf7e3e9617748166ede491d96ab41322cd8e6d Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Wed, 17 Aug 2022 11:24:25 -0700 Subject: [PATCH 629/844] [camera]use weak self to fix a crash due to orientation change (#6277) --- .../camera/camera_avfoundation/CHANGELOG.md | 4 ++ .../ios/RunnerTests/CameraOrientationTests.m | 33 ++++++++++++ .../ios/Classes/CameraPlugin.m | 51 ++++++++++++------- .../ios/Classes/CameraPlugin_Test.h | 6 ++- .../camera_avfoundation/ios/Classes/FLTCam.m | 36 +++++++++---- .../ios/Classes/FLTSavePhotoDelegate.m | 10 ++-- .../ios/Classes/FLTThreadSafeEventChannel.m | 5 +- .../ios/Classes/FLTThreadSafeFlutterResult.m | 5 ++ .../ios/Classes/FLTThreadSafeMethodChannel.m | 3 +- .../Classes/FLTThreadSafeTextureRegistry.m | 11 ++-- .../camera/camera_avfoundation/pubspec.yaml | 2 +- 11 files changed, 127 insertions(+), 39 deletions(-) diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index 2726309e497f..4df497becba6 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.8+4 + +* Fixes a crash due to sending orientation change events when the engine is torn down. + ## 0.9.8+3 * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.m index 29fc325dffc0..60e88fffee2b 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/CameraOrientationTests.m @@ -92,6 +92,39 @@ - (void)rotate:(UIDeviceOrientation)deviceOrientation [self waitForExpectationsWithTimeout:30.0 handler:nil]; } +- (void)testOrientationChanged_noRetainCycle { + dispatch_queue_t captureSessionQueue = dispatch_queue_create("capture_session_queue", NULL); + FLTCam *mockCam = OCMClassMock([FLTCam class]); + FLTThreadSafeMethodChannel *mockChannel = OCMClassMock([FLTThreadSafeMethodChannel class]); + + __weak CameraPlugin *weakCamera; + + @autoreleasepool { + CameraPlugin *camera = [[CameraPlugin alloc] initWithRegistry:nil messenger:nil]; + weakCamera = camera; + camera.captureSessionQueue = captureSessionQueue; + camera.camera = mockCam; + camera.deviceEventMethodChannel = mockChannel; + + [camera orientationChanged: + [self createMockNotificationForOrientation:UIDeviceOrientationLandscapeLeft]]; + } + + // Sanity check + XCTAssertNil(weakCamera, @"Camera must have been deallocated."); + + // Must check in captureSessionQueue since orientationChanged dispatches to this queue. + XCTestExpectation *expectation = + [self expectationWithDescription:@"Dispatched to capture session queue"]; + dispatch_async(captureSessionQueue, ^{ + OCMVerify(never(), [mockCam setDeviceOrientation:UIDeviceOrientationLandscapeLeft]); + OCMVerify(never(), [mockChannel invokeMethod:@"orientation_changed" arguments:OCMOCK_ANY]); + [expectation fulfill]; + }); + + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + - (NSNotification *)createMockNotificationForOrientation:(UIDeviceOrientation)deviceOrientation { UIDevice *mockDevice = OCMClassMock([UIDevice class]); OCMStub([mockDevice orientation]).andReturn(deviceOrientation); diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m index cb19c0909158..628211ac7f7a 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m +++ b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin.m @@ -19,7 +19,6 @@ @interface CameraPlugin () @property(readonly, nonatomic) FLTThreadSafeTextureRegistry *registry; @property(readonly, nonatomic) NSObject *messenger; -@property(readonly, nonatomic) FLTThreadSafeMethodChannel *deviceEventMethodChannel; @end @implementation CameraPlugin @@ -56,6 +55,10 @@ - (void)initDeviceEventMethodChannel { [[FLTThreadSafeMethodChannel alloc] initWithMethodChannel:methodChannel]; } +- (void)detachFromEngineForRegistrar:(NSObject *)registrar { + [UIDevice.currentDevice endGeneratingDeviceOrientationNotifications]; +} + - (void)startOrientationListener { [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; [[NSNotificationCenter defaultCenter] addObserver:self @@ -73,11 +76,12 @@ - (void)orientationChanged:(NSNotification *)note { return; } + __weak typeof(self) weakSelf = self; dispatch_async(self.captureSessionQueue, ^{ // `FLTCam::setDeviceOrientation` must be called on capture session queue. - [self.camera setDeviceOrientation:orientation]; + [weakSelf.camera setDeviceOrientation:orientation]; // `CameraPlugin::sendDeviceOrientation` can be called on any queue. - [self sendDeviceOrientation:orientation]; + [weakSelf sendDeviceOrientation:orientation]; }); } @@ -89,11 +93,11 @@ - (void)sendDeviceOrientation:(UIDeviceOrientation)orientation { - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { // Invoke the plugin on another dispatch queue to avoid blocking the UI. - dispatch_async(_captureSessionQueue, ^{ + __weak typeof(self) weakSelf = self; + dispatch_async(self.captureSessionQueue, ^{ FLTThreadSafeFlutterResult *threadSafeResult = [[FLTThreadSafeFlutterResult alloc] initWithResult:result]; - - [self handleMethodCallAsync:call result:threadSafeResult]; + [weakSelf handleMethodCallAsync:call result:threadSafeResult]; }); } @@ -261,7 +265,11 @@ - (void)handleMethodCallAsync:(FlutterMethodCall *)call - (void)handleCreateMethodCall:(FlutterMethodCall *)call result:(FLTThreadSafeFlutterResult *)result { // Create FLTCam only if granted camera access (and audio access if audio is enabled) + __weak typeof(self) weakSelf = self; FLTRequestCameraPermissionWithCompletionHandler(^(FlutterError *error) { + typeof(self) strongSelf = weakSelf; + if (!strongSelf) return; + if (error) { [result sendFlutterError:error]; } else { @@ -272,14 +280,17 @@ - (void)handleCreateMethodCall:(FlutterMethodCall *)call if (audioEnabled) { // Setup audio capture session only if granted audio access. FLTRequestAudioPermissionWithCompletionHandler(^(FlutterError *error) { + // cannot use the outter `strongSelf` + typeof(self) strongSelf = weakSelf; + if (!strongSelf) return; if (error) { [result sendFlutterError:error]; } else { - [self createCameraOnSessionQueueWithCreateMethodCall:call result:result]; + [strongSelf createCameraOnSessionQueueWithCreateMethodCall:call result:result]; } }); } else { - [self createCameraOnSessionQueueWithCreateMethodCall:call result:result]; + [strongSelf createCameraOnSessionQueueWithCreateMethodCall:call result:result]; } } }); @@ -287,7 +298,11 @@ - (void)handleCreateMethodCall:(FlutterMethodCall *)call - (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)createMethodCall result:(FLTThreadSafeFlutterResult *)result { + __weak typeof(self) weakSelf = self; dispatch_async(self.captureSessionQueue, ^{ + typeof(self) strongSelf = weakSelf; + if (!strongSelf) return; + NSString *cameraName = createMethodCall.arguments[@"cameraName"]; NSString *resolutionPreset = createMethodCall.arguments[@"resolutionPreset"]; NSNumber *enableAudio = createMethodCall.arguments[@"enableAudio"]; @@ -296,22 +311,22 @@ - (void)createCameraOnSessionQueueWithCreateMethodCall:(FlutterMethodCall *)crea resolutionPreset:resolutionPreset enableAudio:[enableAudio boolValue] orientation:[[UIDevice currentDevice] orientation] - captureSessionQueue:self.captureSessionQueue + captureSessionQueue:strongSelf.captureSessionQueue error:&error]; if (error) { [result sendError:error]; } else { - if (self.camera) { - [self.camera close]; + if (strongSelf.camera) { + [strongSelf.camera close]; } - self.camera = cam; - [self.registry registerTexture:cam - completion:^(int64_t textureId) { - [result sendSuccessWithData:@{ - @"cameraId" : @(textureId), - }]; - }]; + strongSelf.camera = cam; + [strongSelf.registry registerTexture:cam + completion:^(int64_t textureId) { + [result sendSuccessWithData:@{ + @"cameraId" : @(textureId), + }]; + }]; } }); } diff --git a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin_Test.h b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin_Test.h index 77a758d8dea3..f6c97da4ad84 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin_Test.h +++ b/packages/camera/camera_avfoundation/ios/Classes/CameraPlugin_Test.h @@ -8,7 +8,7 @@ #import "FLTCam.h" #import "FLTThreadSafeFlutterResult.h" -/// Methods exposed for unit testing. +/// APIs exposed for unit testing. @interface CameraPlugin () /// All FLTCam's state access and capture session related operations should be on run on this queue. @@ -17,6 +17,10 @@ /// An internal camera object that manages camera's state and performs camera operations. @property(nonatomic, strong) FLTCam *camera; +/// A thread safe wrapper of the method channel used to send device events such as orientation +/// changes. +@property(nonatomic, strong) FLTThreadSafeMethodChannel *deviceEventMethodChannel; + /// Inject @p FlutterTextureRegistry and @p FlutterBinaryMessenger for unit testing. - (instancetype)initWithRegistry:(NSObject *)registry messenger:(NSObject *)messenger diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m index f267604af419..90b81adbd84c 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTCam.m @@ -20,16 +20,18 @@ - (instancetype)initWithCaptureSessionQueue:(dispatch_queue_t)captureSessionQueu } - (FlutterError *_Nullable)onCancelWithArguments:(id _Nullable)arguments { + __weak typeof(self) weakSelf = self; dispatch_async(self.captureSessionQueue, ^{ - self.eventSink = nil; + weakSelf.eventSink = nil; }); return nil; } - (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(nonnull FlutterEventSink)events { + __weak typeof(self) weakSelf = self; dispatch_async(self.captureSessionQueue, ^{ - self.eventSink = events; + weakSelf.eventSink = events; }); return nil; } @@ -247,16 +249,18 @@ - (void)captureToFile:(FLTThreadSafeFlutterResult *)result API_AVAILABLE(ios(10) return; } + __weak typeof(self) weakSelf = self; FLTSavePhotoDelegate *savePhotoDelegate = [[FLTSavePhotoDelegate alloc] initWithPath:path ioQueue:self.photoIOQueue completionHandler:^(NSString *_Nullable path, NSError *_Nullable error) { - dispatch_async(self.captureSessionQueue, ^{ - // Dispatch back to capture session queue to delete reference. - // Retain cycle is broken after the dictionary entry is cleared. - // This is to keep the behavior with the previous `selfReference` approach in the - // FLTSavePhotoDelegate, where delegate is released only after capture completion. - [self.inProgressSavePhotoDelegates removeObjectForKey:@(settings.uniqueID)]; + typeof(self) strongSelf = weakSelf; + if (!strongSelf) return; + dispatch_async(strongSelf.captureSessionQueue, ^{ + // cannot use the outter `strongSelf` + typeof(self) strongSelf = weakSelf; + if (!strongSelf) return; + [strongSelf.inProgressSavePhotoDelegates removeObjectForKey:@(settings.uniqueID)]; }); if (error) { @@ -387,6 +391,7 @@ - (void)captureOutput:(AVCaptureOutput *)output // Under rare contest scenarios, it will not block for too long since the critical section is // quite lightweight. dispatch_sync(self.pixelBufferSynchronizationQueue, ^{ + // No need weak self because it's dispatch_sync. previousPixelBuffer = self.latestPixelBuffer; self.latestPixelBuffer = newBuffer; }); @@ -610,6 +615,7 @@ - (CVPixelBufferRef)copyPixelBuffer { __block CVPixelBufferRef pixelBuffer = nil; // Use `dispatch_sync` because `copyPixelBuffer` API requires synchronous return. dispatch_sync(self.pixelBufferSynchronizationQueue, ^{ + // No need weak self because it's dispatch_sync. pixelBuffer = self.latestPixelBuffer; self.latestPixelBuffer = nil; }); @@ -917,11 +923,19 @@ - (void)startImageStreamWithMessenger:(NSObject *)messen [[FLTThreadSafeEventChannel alloc] initWithEventChannel:eventChannel]; _imageStreamHandler = imageStreamHandler; + __weak typeof(self) weakSelf = self; [threadSafeEventChannel setStreamHandler:_imageStreamHandler completion:^{ - dispatch_async(self->_captureSessionQueue, ^{ - self.isStreamingImages = YES; - self.streamingPendingFramesCount = 0; + typeof(self) strongSelf = weakSelf; + if (!strongSelf) return; + + dispatch_async(strongSelf.captureSessionQueue, ^{ + // cannot use the outter strongSelf + typeof(self) strongSelf = weakSelf; + if (!strongSelf) return; + + strongSelf.isStreamingImages = YES; + strongSelf.streamingPendingFramesCount = 0; }); }]; } else { diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.m b/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.m index 1df1708c54e8..617890c44055 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTSavePhotoDelegate.m @@ -31,13 +31,17 @@ - (void)handlePhotoCaptureResultWithError:(NSError *)error self.completionHandler(nil, error); return; } + __weak typeof(self) weakSelf = self; dispatch_async(self.ioQueue, ^{ + typeof(self) strongSelf = weakSelf; + if (!strongSelf) return; + NSData *data = photoDataProvider(); NSError *ioError; - if ([data writeToFile:self.path options:NSDataWritingAtomic error:&ioError]) { - self.completionHandler(self.path, nil); + if ([data writeToFile:strongSelf.path options:NSDataWritingAtomic error:&ioError]) { + strongSelf.completionHandler(self.path, nil); } else { - self.completionHandler(nil, ioError); + strongSelf.completionHandler(nil, ioError); } }); } diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.m b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.m index 46941bb18dd6..ed749a304cee 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.m @@ -21,8 +21,11 @@ - (instancetype)initWithEventChannel:(FlutterEventChannel *)channel { - (void)setStreamHandler:(NSObject *)handler completion:(void (^)(void))completion { + __weak typeof(self) weakSelf = self; FLTEnsureToRunOnMainQueue(^{ - [self.channel setStreamHandler:handler]; + typeof(self) strongSelf = weakSelf; + if (!strongSelf) return; + [strongSelf.channel setStreamHandler:handler]; completion(); }); } diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.m b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.m index ad125f7f32ed..283a0d6bc164 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeFlutterResult.m @@ -52,6 +52,11 @@ - (void)sendNotImplemented { */ - (void)send:(id _Nullable)result { FLTEnsureToRunOnMainQueue(^{ + // WARNING: Should not use weak self, because `FlutterResult`s are passed as arguments + // (retained within call stack, but not in the heap). FLTEnsureToRunOnMainQueue may trigger a + // context switch (when calling from background thread), in which case using weak self will + // always result in a nil self. Alternative to using strong self, we can also create a local + // strong variable to be captured by this block. self.flutterResult(result); }); } diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeMethodChannel.m b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeMethodChannel.m index 5b29b70ee432..df7c169bd43f 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeMethodChannel.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeMethodChannel.m @@ -20,8 +20,9 @@ - (instancetype)initWithMethodChannel:(FlutterMethodChannel *)channel { } - (void)invokeMethod:(NSString *)method arguments:(id)arguments { + __weak typeof(self) weakSelf = self; FLTEnsureToRunOnMainQueue(^{ - [self.channel invokeMethod:method arguments:arguments]; + [weakSelf.channel invokeMethod:method arguments:arguments]; }); } diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeTextureRegistry.m b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeTextureRegistry.m index 349b89fdc117..b82d566d740b 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeTextureRegistry.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeTextureRegistry.m @@ -21,20 +21,25 @@ - (instancetype)initWithTextureRegistry:(NSObject *)regi - (void)registerTexture:(NSObject *)texture completion:(void (^)(int64_t))completion { + __weak typeof(self) weakSelf = self; FLTEnsureToRunOnMainQueue(^{ - completion([self.registry registerTexture:texture]); + typeof(self) strongSelf = weakSelf; + if (!strongSelf) return; + completion([strongSelf.registry registerTexture:texture]); }); } - (void)textureFrameAvailable:(int64_t)textureId { + __weak typeof(self) weakSelf = self; FLTEnsureToRunOnMainQueue(^{ - [self.registry textureFrameAvailable:textureId]; + [weakSelf.registry textureFrameAvailable:textureId]; }); } - (void)unregisterTexture:(int64_t)textureId { + __weak typeof(self) weakSelf = self; FLTEnsureToRunOnMainQueue(^{ - [self.registry unregisterTexture:textureId]; + [weakSelf.registry unregisterTexture:textureId]; }); } diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index c2b9dcb46729..3d1b7c6c3fa7 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_avfoundation description: iOS implementation of the camera plugin. repository: https://github.com/flutter/plugins/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.8+3 +version: 0.9.8+4 environment: sdk: ">=2.14.0 <3.0.0" From b77f6fcfbf8d02cdf43326a7f9b042bf8916bd36 Mon Sep 17 00:00:00 2001 From: Tarrin Neal Date: Wed, 17 Aug 2022 14:47:45 -0700 Subject: [PATCH 630/844] Add use_named_constants to analysis options and fix errors (#6281) * update to remove errors related to analysis option * update to remove errors related to analysis option * remove errors related to analysis option * remove errors related to analysis option * remove errors related to analysis option * remove errors related to analysis option * add use_named_constants * Fix violations of analysis opt use_named_constants * revert metada changes * Updated changelogs to correct version nums * undo automated changes * Reconsile changes by bots that cause failing tests --- analysis_options.yaml | 2 +- .../google_maps_flutter/CHANGELOG.md | 2 ++ .../integration_test/google_maps_test.dart | 3 +-- .../example/lib/padding.dart | 4 ++-- .../example/lib/tile_overlay.dart | 3 +-- .../lib/src/google_map.dart | 2 +- .../google_maps_flutter_android/CHANGELOG.md | 1 + .../integration_test/google_maps_test.dart | 3 +-- .../example/lib/example_google_map.dart | 2 +- .../example/lib/padding.dart | 4 ++-- .../example/lib/tile_overlay.dart | 3 +-- .../google_maps_flutter_ios/CHANGELOG.md | 1 + .../integration_test/google_maps_test.dart | 3 +-- .../example/lib/example_google_map.dart | 2 +- .../example/lib/padding.dart | 4 ++-- .../example/lib/tile_overlay.dart | 3 +-- .../image_picker_for_web/CHANGELOG.md | 4 ++++ .../integration_test/image_resizer_test.dart | 2 +- .../video_player/video_player/CHANGELOG.md | 4 ++++ .../controller_swap_test.dart | 2 +- .../integration_test/video_player_test.dart | 10 +++++----- .../video_player/example/lib/main.dart | 2 +- .../video_player/lib/video_player.dart | 6 +++--- .../video_player/test/video_player_test.dart | 20 +++++++++---------- .../video_player_android/CHANGELOG.md | 4 ++++ .../integration_test/video_player_test.dart | 8 ++++---- .../example/lib/mini_controller.dart | 4 ++-- .../test/android_video_player_test.dart | 2 +- .../video_player_avfoundation/CHANGELOG.md | 1 + .../integration_test/video_player_test.dart | 8 ++++---- .../example/lib/mini_controller.dart | 4 ++-- .../test/avfoundation_video_player_test.dart | 2 +- .../CHANGELOG.md | 4 ++++ .../method_channel_video_player_test.dart | 2 +- 34 files changed, 73 insertions(+), 58 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 224ac68cf543..891e2ef49180 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -248,7 +248,7 @@ linter: - use_is_even_rather_than_modulo - use_key_in_widget_constructors - use_late_for_private_fields_and_variables - # - use_named_constants # LOCAL CHANGE - Needs to be enabled and violations fixed. + - use_named_constants - use_raw_strings - use_rethrow_when_possible - use_setters_to_change_properties diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index ce116ee67c0b..66f3ef80aae5 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,5 +1,7 @@ + ## 2.1.11 +* Fixes violations of new analysis option use_named_constants. * Fixes avoid_redundant_argument_values lint warnings and minor typos. * Moves Android and iOS implementations to federated packages. diff --git a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart index 7db14fc1c6de..7479917402ee 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart @@ -1168,8 +1168,7 @@ class _DebugTileProvider implements TileProvider { textPainter.layout( maxWidth: width.toDouble(), ); - const Offset offset = Offset(0, 0); - textPainter.paint(canvas, offset); + textPainter.paint(canvas, Offset.zero); canvas.drawRect( Rect.fromLTRB(0, 0, width.toDouble(), width.toDouble()), boxPaint); final ui.Picture picture = recorder.endRecording(); diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart index 07359e90294f..d5d396fa69c1 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/padding.dart @@ -30,7 +30,7 @@ const LatLng _kMapCenter = LatLng(52.4478, -3.5402); class MarkerIconsBodyState extends State { GoogleMapController? controller; - EdgeInsets _padding = const EdgeInsets.all(0); + EdgeInsets _padding = EdgeInsets.zero; @override Widget build(BuildContext context) { @@ -167,7 +167,7 @@ class MarkerIconsBodyState extends State { _bottomController.clear(); _leftController.clear(); _rightController.clear(); - _padding = const EdgeInsets.all(0); + _padding = EdgeInsets.zero; }); }, ) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart index e73ca25a56b6..31f470dd9c25 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/tile_overlay.dart @@ -139,8 +139,7 @@ class _DebugTileProvider implements TileProvider { textPainter.layout( maxWidth: width.toDouble(), ); - const Offset offset = Offset(0, 0); - textPainter.paint(canvas, offset); + textPainter.paint(canvas, Offset.zero); canvas.drawRect( Rect.fromLTRB(0, 0, width.toDouble(), width.toDouble()), boxPaint); final ui.Picture picture = recorder.endRecording(); 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 e037d7e464fd..6bb5e75f91ec 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 @@ -108,7 +108,7 @@ class GoogleMap extends StatefulWidget { this.layoutDirection, /// If no padding is specified default padding will be 0. - this.padding = const EdgeInsets.all(0), + this.padding = EdgeInsets.zero, this.indoorViewEnabled = false, this.trafficEnabled = false, this.buildingsEnabled = true, 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 1ce9082b2389..52fb263fa256 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,6 @@ ## NEXT +* Fixes violations of new analysis option use_named_constants. * Fixes avoid_redundant_argument_values lint warnings and minor typos. ## 2.1.10 diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart index ce79a06780cd..ccda42f2c7dd 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart @@ -1150,8 +1150,7 @@ class _DebugTileProvider implements TileProvider { textPainter.layout( maxWidth: width.toDouble(), ); - const Offset offset = Offset(0, 0); - textPainter.paint(canvas, offset); + textPainter.paint(canvas, Offset.zero); canvas.drawRect( Rect.fromLTRB(0, 0, width.toDouble(), width.toDouble()), boxPaint); final ui.Picture picture = recorder.endRecording(); 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 e2c713163a2a..1c1261cb5b82 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 @@ -233,7 +233,7 @@ class ExampleGoogleMap extends StatefulWidget { this.layoutDirection, /// If no padding is specified default padding will be 0. - this.padding = const EdgeInsets.all(0), + this.padding = EdgeInsets.zero, this.indoorViewEnabled = false, this.trafficEnabled = false, this.buildingsEnabled = true, diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/padding.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/padding.dart index 67e72442fc0c..98be700a2af2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/padding.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/padding.dart @@ -32,7 +32,7 @@ const LatLng _kMapCenter = LatLng(52.4478, -3.5402); class MarkerIconsBodyState extends State { ExampleGoogleMapController? controller; - EdgeInsets _padding = const EdgeInsets.all(0); + EdgeInsets _padding = EdgeInsets.zero; @override Widget build(BuildContext context) { @@ -169,7 +169,7 @@ class MarkerIconsBodyState extends State { _bottomController.clear(); _leftController.clear(); _rightController.clear(); - _padding = const EdgeInsets.all(0); + _padding = EdgeInsets.zero; }); }, ) diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/tile_overlay.dart index c912870d6583..e25ab916d8de 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/tile_overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/tile_overlay.dart @@ -140,8 +140,7 @@ class _DebugTileProvider implements TileProvider { textPainter.layout( maxWidth: width.toDouble(), ); - const Offset offset = Offset(0, 0); - textPainter.paint(canvas, offset); + textPainter.paint(canvas, Offset.zero); canvas.drawRect( Rect.fromLTRB(0, 0, width.toDouble(), width.toDouble()), boxPaint); final ui.Picture picture = recorder.endRecording(); 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 935cfd4527ea..19f2adc82ce6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Fixes violations of new analysis option use_named_constants. * Fixes avoid_redundant_argument_values lint warnings and minor typos. ## 2.1.11 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/integration_test/google_maps_test.dart index c9ad0cec2c98..eb00ccb673f4 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/integration_test/google_maps_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/integration_test/google_maps_test.dart @@ -1057,8 +1057,7 @@ class _DebugTileProvider implements TileProvider { textPainter.layout( maxWidth: width.toDouble(), ); - const Offset offset = Offset(0, 0); - textPainter.paint(canvas, offset); + textPainter.paint(canvas, Offset.zero); canvas.drawRect( Rect.fromLTRB(0, 0, width.toDouble(), width.toDouble()), boxPaint); final ui.Picture picture = recorder.endRecording(); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/example_google_map.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/example_google_map.dart index e2c713163a2a..1c1261cb5b82 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/example_google_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/example_google_map.dart @@ -233,7 +233,7 @@ class ExampleGoogleMap extends StatefulWidget { this.layoutDirection, /// If no padding is specified default padding will be 0. - this.padding = const EdgeInsets.all(0), + this.padding = EdgeInsets.zero, this.indoorViewEnabled = false, this.trafficEnabled = false, this.buildingsEnabled = true, diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/padding.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/padding.dart index 67e72442fc0c..98be700a2af2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/padding.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/padding.dart @@ -32,7 +32,7 @@ const LatLng _kMapCenter = LatLng(52.4478, -3.5402); class MarkerIconsBodyState extends State { ExampleGoogleMapController? controller; - EdgeInsets _padding = const EdgeInsets.all(0); + EdgeInsets _padding = EdgeInsets.zero; @override Widget build(BuildContext context) { @@ -169,7 +169,7 @@ class MarkerIconsBodyState extends State { _bottomController.clear(); _leftController.clear(); _rightController.clear(); - _padding = const EdgeInsets.all(0); + _padding = EdgeInsets.zero; }); }, ) diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/tile_overlay.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/tile_overlay.dart index c912870d6583..e25ab916d8de 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/tile_overlay.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/tile_overlay.dart @@ -140,8 +140,7 @@ class _DebugTileProvider implements TileProvider { textPainter.layout( maxWidth: width.toDouble(), ); - const Offset offset = Offset(0, 0); - textPainter.paint(canvas, offset); + textPainter.paint(canvas, Offset.zero); canvas.drawRect( Rect.fromLTRB(0, 0, width.toDouble(), width.toDouble()), boxPaint); final ui.Picture picture = recorder.endRecording(); diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index b69ba597aca3..e9f88a9ccd7a 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes violations of new analysis option use_named_constants. + ## 2.1.8 * Minor fixes for new analysis options. 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 1efd7b29a810..0ff6d2380004 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 @@ -117,7 +117,7 @@ Future _getImageSize(XFile file) async { completer.complete(Size(image.width!.toDouble(), image.height!.toDouble())); }); image.onError.listen((html.Event event) { - completer.complete(const Size(0, 0)); + completer.complete(Size.zero); }); return completer.future; } diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 7a667dafa4da..d593dfcbc751 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes violations of new analysis option use_named_constants. + ## 2.4.6 * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/video_player/video_player/example/integration_test/controller_swap_test.dart b/packages/video_player/video_player/example/integration_test/controller_swap_test.dart index 50c81dac3e44..bdae599ebc8b 100644 --- a/packages/video_player/video_player/example/integration_test/controller_swap_test.dart +++ b/packages/video_player/video_player/example/integration_test/controller_swap_test.dart @@ -65,7 +65,7 @@ void main() { // Expect that `another` played. expect(another.value.position, - (Duration position) => position > const Duration()); + (Duration position) => position > Duration.zero); await expectLater(started.future, completes); await expectLater(ended.future, completes); diff --git a/packages/video_player/video_player/example/integration_test/video_player_test.dart b/packages/video_player/video_player/example/integration_test/video_player_test.dart index c5e0f3b8c1f1..023c4b3fcb43 100644 --- a/packages/video_player/video_player/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player/example/integration_test/video_player_test.dart @@ -49,7 +49,7 @@ void main() { await _controller.initialize(); expect(_controller.value.isInitialized, true); - expect(_controller.value.position, const Duration()); + expect(_controller.value.position, Duration.zero); expect(_controller.value.isPlaying, false); // The WebM version has a slightly different duration than the MP4. expect(_controller.value.duration, @@ -87,7 +87,7 @@ void main() { expect(_controller.value.isPlaying, true); expect(_controller.value.position, - (Duration position) => position > const Duration()); + (Duration position) => position > Duration.zero); }, ); @@ -264,7 +264,7 @@ void main() { expect(_controller.value.isPlaying, false); expect(_controller.value.position, - (Duration position) => position > const Duration()); + (Duration position) => position > Duration.zero); await expectLater(started.future, completes); await expectLater(ended.future, completes); @@ -284,7 +284,7 @@ void main() { await _controller.initialize(); expect(_controller.value.isInitialized, true); - expect(_controller.value.position, const Duration()); + expect(_controller.value.position, Duration.zero); expect(_controller.value.isPlaying, false); // Due to the duration calculation accuracy between platforms, // the milliseconds on Web will be a slightly different from natives. @@ -307,7 +307,7 @@ void main() { expect(_controller.value.isPlaying, true); expect( _controller.value.position, - (Duration position) => position > const Duration(), + (Duration position) => position > Duration.zero, ); }); diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index c3bed945d007..208cd2fc6c39 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -273,7 +273,7 @@ class _ControlsOverlay extends StatelessWidget { Duration(seconds: -3), Duration(seconds: -1, milliseconds: -500), Duration(milliseconds: -250), - Duration(), + Duration.zero, Duration(milliseconds: 250), Duration(seconds: 1, milliseconds: 500), Duration(seconds: 3), diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index fdda3dcc7b56..c1f4886282f8 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -453,7 +453,7 @@ class VideoPlayerController extends ValueNotifier { /// finished. Future play() async { if (value.position == value.duration) { - await seekTo(const Duration()); + await seekTo(Duration.zero); } value = value.copyWith(isPlaying: true); await _applyPlayPause(); @@ -556,8 +556,8 @@ class VideoPlayerController extends ValueNotifier { } if (position > value.duration) { position = value.duration; - } else if (position < const Duration()) { - position = const Duration(); + } else if (position < Duration.zero) { + position = Duration.zero; } await _videoPlayerPlatform.seekTo(_textureId, position); _updatePosition(position); diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index 97f39afd5df3..e7b3e1de7e63 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -372,7 +372,7 @@ void main() { ); expect( controller.textureId, VideoPlayerController.kUninitializedTextureId); - expect(await controller.position, const Duration()); + expect(await controller.position, Duration.zero); await controller.initialize(); await controller.dispose(); @@ -470,7 +470,7 @@ void main() { 'https://127.0.0.1', ); await controller.initialize(); - expect(await controller.position, const Duration()); + expect(await controller.position, Duration.zero); await controller.seekTo(const Duration(milliseconds: 500)); @@ -493,13 +493,13 @@ void main() { 'https://127.0.0.1', ); await controller.initialize(); - expect(await controller.position, const Duration()); + expect(await controller.position, Duration.zero); await controller.seekTo(const Duration(seconds: 100)); expect(await controller.position, const Duration(seconds: 1)); await controller.seekTo(const Duration(seconds: -100)); - expect(await controller.position, const Duration()); + expect(await controller.position, Duration.zero); }); }); @@ -619,7 +619,7 @@ void main() { ); await controller.initialize(); - expect(controller.value.position, const Duration()); + expect(controller.value.position, Duration.zero); expect(controller.value.caption.text, ''); await controller.seekTo(const Duration(milliseconds: 100)); @@ -652,7 +652,7 @@ void main() { await controller.initialize(); controller.setCaptionOffset(const Duration(milliseconds: 100)); - expect(controller.value.position, const Duration()); + expect(controller.value.position, Duration.zero); expect(controller.value.caption.text, ''); await controller.seekTo(const Duration(milliseconds: 100)); @@ -688,7 +688,7 @@ void main() { await controller.initialize(); controller.setCaptionOffset(const Duration(milliseconds: -100)); - expect(controller.value.position, const Duration()); + expect(controller.value.position, Duration.zero); expect(controller.value.caption.text, ''); await controller.seekTo(const Duration(milliseconds: 100)); @@ -788,7 +788,7 @@ void main() { await tester.pumpAndSettle(); expect(controller.value.isBuffering, isTrue); - const Duration bufferStart = Duration(); + const Duration bufferStart = Duration.zero; const Duration bufferEnd = Duration(milliseconds: 500); fakeVideoEventStream.add(VideoEvent( eventType: VideoEventType.bufferingUpdate, @@ -883,7 +883,7 @@ void main() { text: 'foo', number: 0, start: Duration.zero, end: Duration.zero); const Duration captionOffset = Duration(milliseconds: 250); final List buffered = [ - DurationRange(const Duration(), const Duration(seconds: 4)) + DurationRange(Duration.zero, const Duration(seconds: 4)) ]; const bool isInitialized = true; const bool isPlaying = true; @@ -1118,7 +1118,7 @@ class FakeVideoPlayerPlatform extends VideoPlayerPlatform { @override Future getPosition(int textureId) async { calls.add('position'); - return _positions[textureId] ?? const Duration(); + return _positions[textureId] ?? Duration.zero; } @override diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 2c8c56b8861d..db8aa564b2d2 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes violations of new analysis option use_named_constants. + ## 2.3.9 * Updates ExoPlayer to 2.18.1. diff --git a/packages/video_player/video_player_android/example/integration_test/video_player_test.dart b/packages/video_player/video_player_android/example/integration_test/video_player_test.dart index 5cdab247244f..ef7bdeda3503 100644 --- a/packages/video_player/video_player_android/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player_android/example/integration_test/video_player_test.dart @@ -57,7 +57,7 @@ void main() { await _controller.initialize(); expect(_controller.value.isInitialized, true); - expect(await _controller.position, const Duration()); + expect(await _controller.position, Duration.zero); expect(_controller.value.duration, const Duration(seconds: 7, milliseconds: 540)); }); @@ -68,7 +68,7 @@ void main() { await _controller.play(); await tester.pumpAndSettle(_playDuration); - expect(await _controller.position, greaterThan(const Duration())); + expect(await _controller.position, greaterThan(Duration.zero)); }); testWidgets('can seek', (WidgetTester tester) async { @@ -116,7 +116,7 @@ void main() { await _controller.play(); await tester.pumpAndSettle(_playDuration); - expect(await _controller.position, greaterThan(const Duration())); + expect(await _controller.position, greaterThan(Duration.zero)); }); }); @@ -147,7 +147,7 @@ void main() { await tester.pumpAndSettle(_playDuration); await _controller.pause(); - expect(await _controller.position, greaterThan(const Duration())); + expect(await _controller.position, greaterThan(Duration.zero)); await expectLater(started.future, completes); await expectLater(ended.future, completes); diff --git a/packages/video_player/video_player_android/example/lib/mini_controller.dart b/packages/video_player/video_player_android/example/lib/mini_controller.dart index bba3fada0307..24a9e0297df2 100644 --- a/packages/video_player/video_player_android/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_android/example/lib/mini_controller.dart @@ -325,8 +325,8 @@ class MiniController extends ValueNotifier { Future seekTo(Duration position) async { if (position > value.duration) { position = value.duration; - } else if (position < const Duration()) { - position = const Duration(); + } else if (position < Duration.zero) { + position = Duration.zero; } await _platform.seekTo(_textureId, position); _updatePosition(position); diff --git a/packages/video_player/video_player_android/test/android_video_player_test.dart b/packages/video_player/video_player_android/test/android_video_player_test.dart index e81c3e824580..fad9617ddad9 100644 --- a/packages/video_player/video_player_android/test/android_video_player_test.dart +++ b/packages/video_player/video_player_android/test/android_video_player_test.dart @@ -341,7 +341,7 @@ void main() { eventType: VideoEventType.bufferingUpdate, buffered: [ DurationRange( - const Duration(), + Duration.zero, const Duration(milliseconds: 1234), ), DurationRange( diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 034591348d0a..19088a884fae 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Fixes violations of new analysis option use_named_constants. * Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). diff --git a/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart b/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart index 1de77a3b613d..15108aa0713a 100644 --- a/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart @@ -57,7 +57,7 @@ void main() { await _controller.initialize(); expect(_controller.value.isInitialized, true); - expect(await _controller.position, const Duration()); + expect(await _controller.position, Duration.zero); expect(_controller.value.duration, const Duration(seconds: 7, milliseconds: 540)); }); @@ -68,7 +68,7 @@ void main() { await _controller.play(); await tester.pumpAndSettle(_playDuration); - expect(await _controller.position, greaterThan(const Duration())); + expect(await _controller.position, greaterThan(Duration.zero)); }); testWidgets('can seek', (WidgetTester tester) async { @@ -122,7 +122,7 @@ void main() { await _controller.play(); await tester.pumpAndSettle(_playDuration); - expect(await _controller.position, greaterThan(const Duration())); + expect(await _controller.position, greaterThan(Duration.zero)); }); }); @@ -156,7 +156,7 @@ void main() { // TODO(stuartmorgan): Switch to _controller.position once seekTo is // fixed on the native side to wait for completion, so this is testing // the native code rather than the MiniController position cache. - expect(_controller.value.position, greaterThan(const Duration())); + expect(_controller.value.position, greaterThan(Duration.zero)); await expectLater(started.future, completes); await expectLater(ended.future, completes); diff --git a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart index bba3fada0307..24a9e0297df2 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/mini_controller.dart @@ -325,8 +325,8 @@ class MiniController extends ValueNotifier { Future seekTo(Duration position) async { if (position > value.duration) { position = value.duration; - } else if (position < const Duration()) { - position = const Duration(); + } else if (position < Duration.zero) { + position = Duration.zero; } await _platform.seekTo(_textureId, position); _updatePosition(position); diff --git a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart index e424aa719f1f..ea81d438ad75 100644 --- a/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart +++ b/packages/video_player/video_player_avfoundation/test/avfoundation_video_player_test.dart @@ -320,7 +320,7 @@ void main() { eventType: VideoEventType.bufferingUpdate, buffered: [ DurationRange( - const Duration(), + Duration.zero, const Duration(milliseconds: 1234), ), DurationRange( diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index f7a670ae20e1..c14f73ff8bd4 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes violations of new analysis option use_named_constants. + ## 5.1.4 * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart b/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart index b894144706b2..7d64ac8ab7d7 100644 --- a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart +++ b/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart @@ -345,7 +345,7 @@ void main() { eventType: VideoEventType.bufferingUpdate, buffered: [ DurationRange( - const Duration(), + Duration.zero, const Duration(milliseconds: 1234), ), DurationRange( From 4395e3195be7cefc404b4fa9851b6cdbe90e3b2e Mon Sep 17 00:00:00 2001 From: Tarrin Neal Date: Wed, 17 Aug 2022 18:10:04 -0700 Subject: [PATCH 631/844] [google_maps_flutter] update to correct version (#6289) * update to correct version * removed accidental keystroke --- .../google_maps_flutter/google_maps_flutter/CHANGELOG.md | 5 ++++- .../google_maps_flutter/google_maps_flutter/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 66f3ef80aae5..e1f963b8e732 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,7 +1,10 @@ -## 2.1.11 +## 2.1.12 * Fixes violations of new analysis option use_named_constants. + +## 2.1.11 + * Fixes avoid_redundant_argument_values lint warnings and minor typos. * Moves Android and iOS implementations to federated packages. diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index 1e692cb1931e..f632e374351b 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/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.1.11 +version: 2.1.12 environment: sdk: ">=2.14.0 <3.0.0" From 3f287afaec9bf449f6ab2989e6036656f2ecf367 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 18 Aug 2022 11:52:09 -0400 Subject: [PATCH 632/844] Roll Flutter from cb936c51f8b2 to 9eca99b8df79 (31 revisions) (#6291) --- .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 697b03a758e8..681c8453de74 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -cb936c51f8b2abf4a78ef8cf5ebb28c1ce3b9371 +9eca99b8df79585e98103f577342a2314ba26656 From 31ffdb576cb2f0166e7d1b0e5faad0a0cc51e413 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 18 Aug 2022 14:15:33 -0400 Subject: [PATCH 633/844] [file_selector] Import Linux implementation from FDE (#6292) --- .cirrus.yml | 4 +- .../file_selector_linux/.gitignore | 5 + .../file_selector/file_selector_linux/AUTHORS | 6 + .../file_selector_linux/CHANGELOG.md | 19 + .../file_selector/file_selector_linux/LICENSE | 25 ++ .../file_selector_linux/README.md | 11 + .../file_selector_linux/example/.gitignore | 48 +++ .../file_selector_linux/example/.metadata | 10 + .../file_selector_linux/example/README.md | 4 + .../example/lib/get_directory_page.dart | 83 ++++ .../example/lib/home_page.dart | 63 +++ .../file_selector_linux/example/lib/main.dart | 42 ++ .../example/lib/open_image_page.dart | 94 +++++ .../lib/open_multiple_images_page.dart | 105 +++++ .../example/lib/open_text_page.dart | 91 ++++ .../example/lib/save_text_page.dart | 84 ++++ .../example/linux/.gitignore | 1 + .../example/linux/CMakeLists.txt | 111 +++++ .../example/linux/flutter/CMakeLists.txt | 87 ++++ .../linux/flutter/generated_plugins.cmake | 24 ++ .../file_selector_linux/example/linux/main.cc | 10 + .../example/linux/my_application.cc | 110 +++++ .../example/linux/my_application.h | 22 + .../file_selector_linux/example/pubspec.yaml | 21 + .../lib/file_selector_linux.dart | 144 +++++++ .../file_selector_linux/linux/CMakeLists.txt | 67 +++ .../linux/file_selector_plugin.cc | 246 +++++++++++ .../linux/file_selector_plugin_private.h | 12 + .../file_selector_plugin.h | 31 ++ .../linux/test/file_selector_plugin_test.cc | 171 ++++++++ .../linux/test/test_main.cc | 15 + .../file_selector_linux/pubspec.yaml | 27 ++ .../test/file_selector_linux_test.dart | 389 ++++++++++++++++++ .../file_selector_macos/CHANGELOG.md | 5 + .../file_selector_macos/README.md | 18 +- .../file_selector_macos/pubspec.yaml | 4 +- .../file_selector_windows/CHANGELOG.md | 5 + .../file_selector_windows/README.md | 14 +- .../file_selector_windows/pubspec.yaml | 6 +- script/configs/exclude_integration_linux.yaml | 3 + 40 files changed, 2207 insertions(+), 30 deletions(-) create mode 100644 packages/file_selector/file_selector_linux/.gitignore create mode 100644 packages/file_selector/file_selector_linux/AUTHORS create mode 100644 packages/file_selector/file_selector_linux/CHANGELOG.md create mode 100644 packages/file_selector/file_selector_linux/LICENSE create mode 100644 packages/file_selector/file_selector_linux/README.md create mode 100644 packages/file_selector/file_selector_linux/example/.gitignore create mode 100644 packages/file_selector/file_selector_linux/example/.metadata create mode 100644 packages/file_selector/file_selector_linux/example/README.md create mode 100644 packages/file_selector/file_selector_linux/example/lib/get_directory_page.dart create mode 100644 packages/file_selector/file_selector_linux/example/lib/home_page.dart create mode 100644 packages/file_selector/file_selector_linux/example/lib/main.dart create mode 100644 packages/file_selector/file_selector_linux/example/lib/open_image_page.dart create mode 100644 packages/file_selector/file_selector_linux/example/lib/open_multiple_images_page.dart create mode 100644 packages/file_selector/file_selector_linux/example/lib/open_text_page.dart create mode 100644 packages/file_selector/file_selector_linux/example/lib/save_text_page.dart create mode 100644 packages/file_selector/file_selector_linux/example/linux/.gitignore create mode 100644 packages/file_selector/file_selector_linux/example/linux/CMakeLists.txt create mode 100644 packages/file_selector/file_selector_linux/example/linux/flutter/CMakeLists.txt create mode 100644 packages/file_selector/file_selector_linux/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/file_selector/file_selector_linux/example/linux/main.cc create mode 100644 packages/file_selector/file_selector_linux/example/linux/my_application.cc create mode 100644 packages/file_selector/file_selector_linux/example/linux/my_application.h create mode 100644 packages/file_selector/file_selector_linux/example/pubspec.yaml create mode 100644 packages/file_selector/file_selector_linux/lib/file_selector_linux.dart create mode 100644 packages/file_selector/file_selector_linux/linux/CMakeLists.txt create mode 100644 packages/file_selector/file_selector_linux/linux/file_selector_plugin.cc create mode 100644 packages/file_selector/file_selector_linux/linux/file_selector_plugin_private.h create mode 100644 packages/file_selector/file_selector_linux/linux/include/file_selector_linux/file_selector_plugin.h create mode 100644 packages/file_selector/file_selector_linux/linux/test/file_selector_plugin_test.cc create mode 100644 packages/file_selector/file_selector_linux/linux/test/test_main.cc create mode 100644 packages/file_selector/file_selector_linux/pubspec.yaml create mode 100644 packages/file_selector/file_selector_linux/test/file_selector_linux_test.dart create mode 100644 script/configs/exclude_integration_linux.yaml diff --git a/.cirrus.yml b/.cirrus.yml index 1ac69d6484f0..059f5fe7e5d8 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -212,9 +212,9 @@ task: - flutter config --enable-linux-desktop - ./script/tool_runner.sh build-examples --linux native_test_script: - - ./script/tool_runner.sh native-test --linux --no-integration + - xvfb-run ./script/tool_runner.sh native-test --linux --no-integration drive_script: - - xvfb-run ./script/tool_runner.sh drive-examples --linux + - xvfb-run ./script/tool_runner.sh drive-examples --linux --exclude=script/configs/exclude_integration_linux.yaml # Heavy-workload Linux tasks. # These use machines with more CPUs and memory, so will reduce parallelization diff --git a/packages/file_selector/file_selector_linux/.gitignore b/packages/file_selector/file_selector_linux/.gitignore new file mode 100644 index 000000000000..0393a47ff732 --- /dev/null +++ b/packages/file_selector/file_selector_linux/.gitignore @@ -0,0 +1,5 @@ +.dart_tool +.packages +.flutter-plugins +.flutter-plugins-dependencies +pubspec.lock diff --git a/packages/file_selector/file_selector_linux/AUTHORS b/packages/file_selector/file_selector_linux/AUTHORS new file mode 100644 index 000000000000..557dff97933b --- /dev/null +++ b/packages/file_selector/file_selector_linux/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/file_selector/file_selector_linux/CHANGELOG.md b/packages/file_selector/file_selector_linux/CHANGELOG.md new file mode 100644 index 000000000000..a63a52d5dbe7 --- /dev/null +++ b/packages/file_selector/file_selector_linux/CHANGELOG.md @@ -0,0 +1,19 @@ +## 0.9.0 + +* Moves source to flutter/plugins. + +## 0.0.3 + +* Adds Dart implementation for in-package method channel. + +## 0.0.2+1 + +* Updates README + +## 0.0.2 + +* Updates SDK constraint to signal compatibility with null safety. + +## 0.0.1 + +* Initial Linux implementation of `file_selector`. diff --git a/packages/file_selector/file_selector_linux/LICENSE b/packages/file_selector/file_selector_linux/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/file_selector/file_selector_linux/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/file_selector/file_selector_linux/README.md b/packages/file_selector/file_selector_linux/README.md new file mode 100644 index 000000000000..55a0529364b2 --- /dev/null +++ b/packages/file_selector/file_selector_linux/README.md @@ -0,0 +1,11 @@ +# file\_selector\_linux + +The Linux implementation of [`file_selector`][1]. + +## Usage + +This package is [endorsed][2], which means you can simply use `file_selector` +normally. This package will be automatically included in your app when you do. + +[1]: https://pub.dev/packages/file_selector +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/file_selector/file_selector_linux/example/.gitignore b/packages/file_selector/file_selector_linux/example/.gitignore new file mode 100644 index 000000000000..7abd0753cfc3 --- /dev/null +++ b/packages/file_selector/file_selector_linux/example/.gitignore @@ -0,0 +1,48 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Currently only web supported +android/ +ios/ + +# Exceptions to above rules. +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/packages/file_selector/file_selector_linux/example/.metadata b/packages/file_selector/file_selector_linux/example/.metadata new file mode 100644 index 000000000000..897381f2373f --- /dev/null +++ b/packages/file_selector/file_selector_linux/example/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 7736f3bc90270dcb0480db2ccffbf1d13c28db85 + channel: dev + +project_type: app diff --git a/packages/file_selector/file_selector_linux/example/README.md b/packages/file_selector/file_selector_linux/example/README.md new file mode 100644 index 000000000000..2f9f8c0f824b --- /dev/null +++ b/packages/file_selector/file_selector_linux/example/README.md @@ -0,0 +1,4 @@ +# `file_selector_linux` example + +Demonstrates Linux implementation of the +[`file_selector` plugin](https://pub.dev/packages/file_selector). diff --git a/packages/file_selector/file_selector_linux/example/lib/get_directory_page.dart b/packages/file_selector/file_selector_linux/example/lib/get_directory_page.dart new file mode 100644 index 000000000000..0699dd121541 --- /dev/null +++ b/packages/file_selector/file_selector_linux/example/lib/get_directory_page.dart @@ -0,0 +1,83 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select a directory using `getDirectoryPath`, +/// then displays the selected directory in a dialog. +class GetDirectoryPage extends StatelessWidget { + /// Default Constructor + const GetDirectoryPage({Key? key}) : super(key: key); + + Future _getDirectoryPath(BuildContext context) async { + const String confirmButtonText = 'Choose'; + final String? directoryPath = + await FileSelectorPlatform.instance.getDirectoryPath( + confirmButtonText: confirmButtonText, + ); + if (directoryPath == null) { + // Operation was canceled by the user. + return; + } + await showDialog( + context: context, + builder: (BuildContext context) => TextDisplay(directoryPath), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open a text file'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use + primary: Colors.blue, + // ignore: deprecated_member_use + onPrimary: Colors.white, + ), + child: const Text('Press to ask user to choose a directory'), + onPressed: () => _getDirectoryPath(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class TextDisplay extends StatelessWidget { + /// Creates a `TextDisplay`. + const TextDisplay(this.directoryPath, {Key? key}) : super(key: key); + + /// The path selected in the dialog. + final String directoryPath; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Selected Directory'), + content: Scrollbar( + child: SingleChildScrollView( + child: Text(directoryPath), + ), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () => Navigator.pop(context), + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_linux/example/lib/home_page.dart b/packages/file_selector/file_selector_linux/example/lib/home_page.dart new file mode 100644 index 000000000000..a4b2ae1f63ea --- /dev/null +++ b/packages/file_selector/file_selector_linux/example/lib/home_page.dart @@ -0,0 +1,63 @@ +// 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'; + +/// Home Page of the application. +class HomePage extends StatelessWidget { + /// Default Constructor + const HomePage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final ButtonStyle style = ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use + primary: Colors.blue, + // ignore: deprecated_member_use + onPrimary: Colors.white, + ); + return Scaffold( + appBar: AppBar( + title: const Text('File Selector Demo Home Page'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: style, + child: const Text('Open a text file'), + onPressed: () => Navigator.pushNamed(context, '/open/text'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open an image'), + onPressed: () => Navigator.pushNamed(context, '/open/image'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open multiple images'), + onPressed: () => Navigator.pushNamed(context, '/open/images'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Save a file'), + onPressed: () => Navigator.pushNamed(context, '/save/text'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open a get directory dialog'), + onPressed: () => Navigator.pushNamed(context, '/directory'), + ), + ], + ), + ), + ); + } +} diff --git a/packages/file_selector/file_selector_linux/example/lib/main.dart b/packages/file_selector/file_selector_linux/example/lib/main.dart new file mode 100644 index 000000000000..3e447104ef9f --- /dev/null +++ b/packages/file_selector/file_selector_linux/example/lib/main.dart @@ -0,0 +1,42 @@ +// 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 'get_directory_page.dart'; +import 'home_page.dart'; +import 'open_image_page.dart'; +import 'open_multiple_images_page.dart'; +import 'open_text_page.dart'; +import 'save_text_page.dart'; + +void main() { + runApp(const MyApp()); +} + +/// MyApp is the Main Application. +class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'File Selector Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: const HomePage(), + routes: { + '/open/image': (BuildContext context) => const OpenImagePage(), + '/open/images': (BuildContext context) => + const OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => const OpenTextPage(), + '/save/text': (BuildContext context) => SaveTextPage(), + '/directory': (BuildContext context) => const GetDirectoryPage(), + }, + ); + } +} diff --git a/packages/file_selector/file_selector_linux/example/lib/open_image_page.dart b/packages/file_selector/file_selector_linux/example/lib/open_image_page.dart new file mode 100644 index 000000000000..9e1d2074d5f2 --- /dev/null +++ b/packages/file_selector/file_selector_linux/example/lib/open_image_page.dart @@ -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. + +import 'dart:io'; + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select an image file using +/// `openFiles`, then displays the selected images in a gallery dialog. +class OpenImagePage extends StatelessWidget { + /// Default Constructor + const OpenImagePage({Key? key}) : super(key: key); + + Future _openImageFile(BuildContext context) async { + final XTypeGroup typeGroup = XTypeGroup( + label: 'images', + extensions: ['jpg', 'png'], + ); + final XFile? file = await FileSelectorPlatform.instance + .openFile(acceptedTypeGroups: [typeGroup]); + if (file == null) { + // Operation was canceled by the user. + return; + } + final String fileName = file.name; + final String filePath = file.path; + + await showDialog( + context: context, + builder: (BuildContext context) => ImageDisplay(fileName, filePath), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open an image'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use + primary: Colors.blue, + // ignore: deprecated_member_use + onPrimary: Colors.white, + ), + child: const Text('Press to open an image file(png, jpg)'), + onPressed: () => _openImageFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays an image in a dialog. +class ImageDisplay extends StatelessWidget { + /// Default Constructor. + const ImageDisplay(this.fileName, this.filePath, {Key? key}) + : super(key: key); + + /// The name of the selected file. + final String fileName; + + /// The path to the selected file. + final String filePath; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(fileName), + // On web the filePath is a blob url + // while on other platforms it is a system path. + content: kIsWeb ? Image.network(filePath) : Image.file(File(filePath)), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_linux/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_linux/example/lib/open_multiple_images_page.dart new file mode 100644 index 000000000000..21da8c22fa4d --- /dev/null +++ b/packages/file_selector/file_selector_linux/example/lib/open_multiple_images_page.dart @@ -0,0 +1,105 @@ +// 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:io'; + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select multiple image files using +/// `openFiles`, then displays the selected images in a gallery dialog. +class OpenMultipleImagesPage extends StatelessWidget { + /// Default Constructor + const OpenMultipleImagesPage({Key? key}) : super(key: key); + + Future _openImageFile(BuildContext context) async { + final XTypeGroup jpgsTypeGroup = XTypeGroup( + label: 'JPEGs', + extensions: ['jpg', 'jpeg'], + ); + final XTypeGroup pngTypeGroup = XTypeGroup( + label: 'PNGs', + extensions: ['png'], + ); + final List files = await FileSelectorPlatform.instance + .openFiles(acceptedTypeGroups: [ + jpgsTypeGroup, + pngTypeGroup, + ]); + if (files.isEmpty) { + // Operation was canceled by the user. + return; + } + await showDialog( + context: context, + builder: (BuildContext context) => MultipleImagesDisplay(files), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open multiple images'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use + primary: Colors.blue, + // ignore: deprecated_member_use + onPrimary: Colors.white, + ), + child: const Text('Press to open multiple images (png, jpg)'), + onPressed: () => _openImageFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class MultipleImagesDisplay extends StatelessWidget { + /// Default Constructor. + const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); + + /// The files containing the images. + final List files; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Gallery'), + // On web the filePath is a blob url + // while on other platforms it is a system path. + content: Center( + child: Row( + children: [ + ...files.map( + (XFile file) => Flexible( + child: kIsWeb + ? Image.network(file.path) + : Image.file(File(file.path))), + ) + ], + ), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_linux/example/lib/open_text_page.dart b/packages/file_selector/file_selector_linux/example/lib/open_text_page.dart new file mode 100644 index 000000000000..05c6d166fb6f --- /dev/null +++ b/packages/file_selector/file_selector_linux/example/lib/open_text_page.dart @@ -0,0 +1,91 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select a text file using `openFile`, then +/// displays its contents in a dialog. +class OpenTextPage extends StatelessWidget { + /// Default Constructor + const OpenTextPage({Key? key}) : super(key: key); + + Future _openTextFile(BuildContext context) async { + final XTypeGroup typeGroup = XTypeGroup( + label: 'text', + extensions: ['txt', 'json'], + ); + final XFile? file = await FileSelectorPlatform.instance + .openFile(acceptedTypeGroups: [typeGroup]); + if (file == null) { + // Operation was canceled by the user. + return; + } + final String fileName = file.name; + final String fileContent = await file.readAsString(); + + await showDialog( + context: context, + builder: (BuildContext context) => TextDisplay(fileName, fileContent), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open a text file'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use + primary: Colors.blue, + // ignore: deprecated_member_use + onPrimary: Colors.white, + ), + child: const Text('Press to open a text file (json, txt)'), + onPressed: () => _openTextFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class TextDisplay extends StatelessWidget { + /// Default Constructor. + const TextDisplay(this.fileName, this.fileContent, {Key? key}) + : super(key: key); + + /// The name of the selected file. + final String fileName; + + /// The contents of the text file. + final String fileContent; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(fileName), + content: Scrollbar( + child: SingleChildScrollView( + child: Text(fileContent), + ), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () => Navigator.pop(context), + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_linux/example/lib/save_text_page.dart b/packages/file_selector/file_selector_linux/example/lib/save_text_page.dart new file mode 100644 index 000000000000..9803f285a536 --- /dev/null +++ b/packages/file_selector/file_selector_linux/example/lib/save_text_page.dart @@ -0,0 +1,84 @@ +// 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:typed_data'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select a save location using `getSavePath`, +/// then writes text to a file at that location. +class SaveTextPage extends StatelessWidget { + /// Default Constructor + SaveTextPage({Key? key}) : super(key: key); + + final TextEditingController _nameController = TextEditingController(); + final TextEditingController _contentController = TextEditingController(); + + Future _saveFile() async { + final String fileName = _nameController.text; + final String? path = await FileSelectorPlatform.instance.getSavePath( + // Operation was canceled by the user. + suggestedName: fileName, + ); + if (path == null) { + return; + } + final String text = _contentController.text; + final Uint8List fileData = Uint8List.fromList(text.codeUnits); + const String fileMimeType = 'text/plain'; + final XFile textFile = + XFile.fromData(fileData, mimeType: fileMimeType, name: fileName); + await textFile.saveTo(path); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Save text into a file'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 300, + child: TextField( + minLines: 1, + maxLines: 12, + controller: _nameController, + decoration: const InputDecoration( + hintText: '(Optional) Suggest File Name', + ), + ), + ), + Container( + width: 300, + child: TextField( + minLines: 1, + maxLines: 12, + controller: _contentController, + decoration: const InputDecoration( + hintText: 'Enter File Contents', + ), + ), + ), + const SizedBox(height: 10), + ElevatedButton( + style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use + primary: Colors.blue, + // ignore: deprecated_member_use + onPrimary: Colors.white, + ), + onPressed: _saveFile, + child: const Text('Press to save a text file'), + ), + ], + ), + ), + ); + } +} diff --git a/packages/file_selector/file_selector_linux/example/linux/.gitignore b/packages/file_selector/file_selector_linux/example/linux/.gitignore new file mode 100644 index 000000000000..d3896c98444f --- /dev/null +++ b/packages/file_selector/file_selector_linux/example/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/packages/file_selector/file_selector_linux/example/linux/CMakeLists.txt b/packages/file_selector/file_selector_linux/example/linux/CMakeLists.txt new file mode 100644 index 000000000000..9d7224cc9280 --- /dev/null +++ b/packages/file_selector/file_selector_linux/example/linux/CMakeLists.txt @@ -0,0 +1,111 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "example") +set(APPLICATION_ID "com.example.example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Enable the test target. +set(include_file_selector_linux_tests TRUE) +# Provide an alias for the test target using the name expected by repo tooling. +add_custom_target(unit_tests DEPENDS file_selector_linux_test) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/packages/file_selector/file_selector_linux/example/linux/flutter/CMakeLists.txt b/packages/file_selector/file_selector_linux/example/linux/flutter/CMakeLists.txt new file mode 100644 index 000000000000..33fd5801e713 --- /dev/null +++ b/packages/file_selector/file_selector_linux/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,87 @@ +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/packages/file_selector/file_selector_linux/example/linux/flutter/generated_plugins.cmake b/packages/file_selector/file_selector_linux/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000000..2db3c22ae228 --- /dev/null +++ b/packages/file_selector/file_selector_linux/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + file_selector_linux +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/file_selector/file_selector_linux/example/linux/main.cc b/packages/file_selector/file_selector_linux/example/linux/main.cc new file mode 100644 index 000000000000..1507d02825e7 --- /dev/null +++ b/packages/file_selector/file_selector_linux/example/linux/main.cc @@ -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. + +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/packages/file_selector/file_selector_linux/example/linux/my_application.cc b/packages/file_selector/file_selector_linux/example/linux/my_application.cc new file mode 100644 index 000000000000..e970be04c827 --- /dev/null +++ b/packages/file_selector/file_selector_linux/example/linux/my_application.cc @@ -0,0 +1,110 @@ +// 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. + +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "example"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments( + project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, + gchar*** arguments, + int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = + my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new( + my_application_get_type(), "application-id", APPLICATION_ID, nullptr)); +} diff --git a/packages/file_selector/file_selector_linux/example/linux/my_application.h b/packages/file_selector/file_selector_linux/example/linux/my_application.h new file mode 100644 index 000000000000..6e9f0c3ff665 --- /dev/null +++ b/packages/file_selector/file_selector_linux/example/linux/my_application.h @@ -0,0 +1,22 @@ +// 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. + +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/packages/file_selector/file_selector_linux/example/pubspec.yaml b/packages/file_selector/file_selector_linux/example/pubspec.yaml new file mode 100644 index 000000000000..857c950ecc95 --- /dev/null +++ b/packages/file_selector/file_selector_linux/example/pubspec.yaml @@ -0,0 +1,21 @@ +name: file_selector_linux_example +description: Local testbed for Linux file_selector implementation. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev +version: 1.0.0+1 + +environment: + sdk: ">=2.12.0 <3.0.0" + +dependencies: + file_selector_linux: + path: ../ + file_selector_platform_interface: ^2.0.0 + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + +flutter: + uses-material-design: true diff --git a/packages/file_selector/file_selector_linux/lib/file_selector_linux.dart b/packages/file_selector/file_selector_linux/lib/file_selector_linux.dart new file mode 100644 index 000000000000..430b41c398db --- /dev/null +++ b/packages/file_selector/file_selector_linux/lib/file_selector_linux.dart @@ -0,0 +1,144 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart' show visibleForTesting; +import 'package:flutter/services.dart'; + +const MethodChannel _channel = + MethodChannel('plugins.flutter.dev/file_selector_linux'); + +const String _typeGroupLabelKey = 'label'; +const String _typeGroupExtensionsKey = 'extensions'; +const String _typeGroupMimeTypesKey = 'mimeTypes'; + +const String _openFileMethod = 'openFile'; +const String _getSavePathMethod = 'getSavePath'; +const String _getDirectoryPathMethod = 'getDirectoryPath'; + +const String _acceptedTypeGroupsKey = 'acceptedTypeGroups'; +const String _confirmButtonTextKey = 'confirmButtonText'; +const String _initialDirectoryKey = 'initialDirectory'; +const String _multipleKey = 'multiple'; +const String _suggestedNameKey = 'suggestedName'; + +/// An implementation of [FileSelectorPlatform] for Linux. +class FileSelectorLinux extends FileSelectorPlatform { + /// The MethodChannel that is being used by this implementation of the plugin. + @visibleForTesting + MethodChannel get channel => _channel; + + /// Registers the Linux implementation. + static void registerWith() { + FileSelectorPlatform.instance = FileSelectorLinux(); + } + + @override + Future openFile({ + List? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText, + }) async { + final List> serializedTypeGroups = + _serializeTypeGroups(acceptedTypeGroups); + final List? path = await _channel.invokeListMethod( + _openFileMethod, + { + if (serializedTypeGroups.isNotEmpty) + _acceptedTypeGroupsKey: serializedTypeGroups, + 'initialDirectory': initialDirectory, + _confirmButtonTextKey: confirmButtonText, + _multipleKey: false, + }, + ); + return path == null ? null : XFile(path.first); + } + + @override + Future> openFiles({ + List? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText, + }) async { + final List> serializedTypeGroups = + _serializeTypeGroups(acceptedTypeGroups); + final List? pathList = await _channel.invokeListMethod( + _openFileMethod, + { + if (serializedTypeGroups.isNotEmpty) + _acceptedTypeGroupsKey: serializedTypeGroups, + _initialDirectoryKey: initialDirectory, + _confirmButtonTextKey: confirmButtonText, + _multipleKey: true, + }, + ); + return pathList?.map((String path) => XFile(path)).toList() ?? []; + } + + @override + Future getSavePath({ + List? acceptedTypeGroups, + String? initialDirectory, + String? suggestedName, + String? confirmButtonText, + }) async { + final List> serializedTypeGroups = + _serializeTypeGroups(acceptedTypeGroups); + return _channel.invokeMethod( + _getSavePathMethod, + { + if (serializedTypeGroups.isNotEmpty) + _acceptedTypeGroupsKey: serializedTypeGroups, + _initialDirectoryKey: initialDirectory, + _suggestedNameKey: suggestedName, + _confirmButtonTextKey: confirmButtonText, + }, + ); + } + + @override + Future getDirectoryPath({ + String? initialDirectory, + String? confirmButtonText, + }) async { + return _channel.invokeMethod( + _getDirectoryPathMethod, + { + _initialDirectoryKey: initialDirectory, + _confirmButtonTextKey: confirmButtonText, + }, + ); + } +} + +List> _serializeTypeGroups(List? groups) { + return (groups ?? []).map(_serializeTypeGroup).toList(); +} + +Map _serializeTypeGroup(XTypeGroup group) { + final Map serialization = { + _typeGroupLabelKey: group.label ?? '', + }; + if (group.allowsAny) { + serialization[_typeGroupExtensionsKey] = ['*']; + } else { + if ((group.extensions?.isEmpty ?? true) && + (group.mimeTypes?.isEmpty ?? true)) { + throw ArgumentError('Provided type group $group does not allow ' + 'all files, but does not set any of the Linux-supported filter ' + 'categories. "extensions" or "mimeTypes" must be non-empty for Linux ' + 'if anything is non-empty.'); + } + if (group.extensions?.isNotEmpty ?? false) { + serialization[_typeGroupExtensionsKey] = group.extensions + ?.map((String extension) => '*.$extension') + .toList() ?? + []; + } + if (group.mimeTypes?.isNotEmpty ?? false) { + serialization[_typeGroupMimeTypesKey] = group.mimeTypes ?? []; + } + } + return serialization; +} diff --git a/packages/file_selector/file_selector_linux/linux/CMakeLists.txt b/packages/file_selector/file_selector_linux/linux/CMakeLists.txt new file mode 100644 index 000000000000..d0316d94e4ac --- /dev/null +++ b/packages/file_selector/file_selector_linux/linux/CMakeLists.txt @@ -0,0 +1,67 @@ +cmake_minimum_required(VERSION 3.10) +set(PROJECT_NAME "file_selector_linux") +project(${PROJECT_NAME} LANGUAGES CXX) + +set(PLUGIN_NAME "${PROJECT_NAME}_plugin") + +list(APPEND PLUGIN_SOURCES + "file_selector_plugin.cc" +) + +add_library(${PLUGIN_NAME} SHARED + "file_selector_plugin.cc" +) +apply_standard_settings(${PLUGIN_NAME}) +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter) +target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK) + + +# === Tests === + +if(${include_${PROJECT_NAME}_tests}) +if(${CMAKE_VERSION} VERSION_LESS "3.11.0") +message("Unit tests require CMake 3.11.0 or later") +else() +set(TEST_RUNNER "${PROJECT_NAME}_test") +enable_testing() +# TODO(stuartmorgan): Consider using a single shared, pre-checked-in googletest +# instance rather than downloading for each plugin. This approach makes sense +# for a template, but not for a monorepo with many plugins. +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/release-1.11.0.zip +) +# Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +# Disable install commands for gtest so it doesn't end up in the bundle. +set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE) + +FetchContent_MakeAvailable(googletest) + +# The plugin's exported API is not very useful for unit testing, so build the +# sources directly into the test binary rather than using the shared library. +add_executable(${TEST_RUNNER} + test/file_selector_plugin_test.cc + test/test_main.cc + ${PLUGIN_SOURCES} +) +apply_standard_settings(${TEST_RUNNER}) +target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(${TEST_RUNNER} PRIVATE flutter) +target_link_libraries(${TEST_RUNNER} PRIVATE PkgConfig::GTK) +target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock) + +include(GoogleTest) +# TODO(stuartmorgan): Switch back to gtest_discover_tests when moving to +# flutter/plugins; it doesn't work in the FDE CI because it requires actually +# running a GTK app, which it hasn't been set up for. +gtest_add_tests(TARGET ${TEST_RUNNER}) +#gtest_discover_tests(${TEST_RUNNER}) +endif() # CMake version check +endif() # include_${PROJECT_NAME}_tests diff --git a/packages/file_selector/file_selector_linux/linux/file_selector_plugin.cc b/packages/file_selector/file_selector_linux/linux/file_selector_plugin.cc new file mode 100644 index 000000000000..833771955120 --- /dev/null +++ b/packages/file_selector/file_selector_linux/linux/file_selector_plugin.cc @@ -0,0 +1,246 @@ +// 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. + +#include "include/file_selector_linux/file_selector_plugin.h" + +#include +#include + +#include "file_selector_plugin_private.h" + +// From file_selector_linux.dart +const char kChannelName[] = "plugins.flutter.dev/file_selector_linux"; + +const char kOpenFileMethod[] = "openFile"; +const char kGetSavePathMethod[] = "getSavePath"; +const char kGetDirectoryPathMethod[] = "getDirectoryPath"; + +const char kAcceptedTypeGroupsKey[] = "acceptedTypeGroups"; +const char kConfirmButtonTextKey[] = "confirmButtonText"; +const char kInitialDirectoryKey[] = "initialDirectory"; +const char kMultipleKey[] = "multiple"; +const char kSuggestedNameKey[] = "suggestedName"; + +const char kTypeGroupLabelKey[] = "label"; +const char kTypeGroupExtensionsKey[] = "extensions"; +const char kTypeGroupMimeTypesKey[] = "mimeTypes"; + +// Errors +const char kBadArgumentsError[] = "Bad Arguments"; +const char kNoScreenError[] = "No Screen"; + +struct _FlFileSelectorPlugin { + GObject parent_instance; + + FlPluginRegistrar* registrar; + + // Connection to Flutter engine. + FlMethodChannel* channel; +}; + +G_DEFINE_TYPE(FlFileSelectorPlugin, fl_file_selector_plugin, G_TYPE_OBJECT) + +// Converts a type group received from Flutter into a GTK file filter. +static GtkFileFilter* type_group_to_filter(FlValue* value) { + g_autoptr(GtkFileFilter) filter = gtk_file_filter_new(); + + FlValue* label = fl_value_lookup_string(value, kTypeGroupLabelKey); + if (label != nullptr && fl_value_get_type(label) == FL_VALUE_TYPE_STRING) { + gtk_file_filter_set_name(filter, fl_value_get_string(label)); + } + + FlValue* extensions = fl_value_lookup_string(value, kTypeGroupExtensionsKey); + if (extensions != nullptr && + fl_value_get_type(extensions) == FL_VALUE_TYPE_LIST) { + for (size_t i = 0; i < fl_value_get_length(extensions); i++) { + FlValue* v = fl_value_get_list_value(extensions, i); + const gchar* pattern = fl_value_get_string(v); + gtk_file_filter_add_pattern(filter, pattern); + } + } + FlValue* mime_types = fl_value_lookup_string(value, kTypeGroupMimeTypesKey); + if (mime_types != nullptr && + fl_value_get_type(mime_types) == FL_VALUE_TYPE_LIST) { + for (size_t i = 0; i < fl_value_get_length(mime_types); i++) { + FlValue* v = fl_value_get_list_value(mime_types, i); + const gchar* pattern = fl_value_get_string(v); + gtk_file_filter_add_mime_type(filter, pattern); + } + } + + return GTK_FILE_FILTER(g_object_ref(filter)); +} + +// Creates a GtkFileChooserNative for the given method call details. +static GtkFileChooserNative* create_dialog( + GtkWindow* window, GtkFileChooserAction action, const gchar* title, + const gchar* default_confirm_button_text, FlValue* properties) { + const gchar* confirm_button_text = default_confirm_button_text; + FlValue* value = fl_value_lookup_string(properties, kConfirmButtonTextKey); + if (value != nullptr && fl_value_get_type(value) == FL_VALUE_TYPE_STRING) + confirm_button_text = fl_value_get_string(value); + + g_autoptr(GtkFileChooserNative) dialog = + GTK_FILE_CHOOSER_NATIVE(gtk_file_chooser_native_new( + title, window, action, confirm_button_text, "_Cancel")); + + value = fl_value_lookup_string(properties, kMultipleKey); + if (value != nullptr && fl_value_get_type(value) == FL_VALUE_TYPE_BOOL) { + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), + fl_value_get_bool(value)); + } + + value = fl_value_lookup_string(properties, kInitialDirectoryKey); + if (value != nullptr && fl_value_get_type(value) == FL_VALUE_TYPE_STRING) { + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), + fl_value_get_string(value)); + } + + value = fl_value_lookup_string(properties, kSuggestedNameKey); + if (value != nullptr && fl_value_get_type(value) == FL_VALUE_TYPE_STRING) { + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), + fl_value_get_string(value)); + } + + value = fl_value_lookup_string(properties, kAcceptedTypeGroupsKey); + if (value != nullptr && fl_value_get_type(value) == FL_VALUE_TYPE_LIST) { + for (size_t i = 0; i < fl_value_get_length(value); i++) { + FlValue* type_group = fl_value_get_list_value(value, i); + GtkFileFilter* filter = type_group_to_filter(type_group); + if (filter == nullptr) { + return nullptr; + } + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); + } + } + + return GTK_FILE_CHOOSER_NATIVE(g_object_ref(dialog)); +} + +// TODO(stuartmorgan): Move this logic back into method_call_cb once +// https://github.com/flutter/flutter/issues/88724 is fixed, and test +// through the public API instead. This only exists to move as much +// logic as possible behind the private entry point used by unit tests. +GtkFileChooserNative* create_dialog_for_method(GtkWindow* window, + const gchar* method, + FlValue* properties) { + if (strcmp(method, kOpenFileMethod) == 0) { + return create_dialog(window, GTK_FILE_CHOOSER_ACTION_OPEN, "Open File", + "_Open", properties); + } else if (strcmp(method, kGetDirectoryPathMethod) == 0) { + return create_dialog(window, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + "Choose Directory", "_Open", properties); + } else if (strcmp(method, kGetSavePathMethod) == 0) { + return create_dialog(window, GTK_FILE_CHOOSER_ACTION_SAVE, "Save File", + "_Save", properties); + } + return nullptr; +} + +// Shows the requested dialog type. +static FlMethodResponse* show_dialog(FlFileSelectorPlugin* self, + const gchar* method, FlValue* properties, + bool return_list) { + if (fl_value_get_type(properties) != FL_VALUE_TYPE_MAP) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Argument map missing or malformed", nullptr)); + } + + FlView* view = fl_plugin_registrar_get_view(self->registrar); + if (view == nullptr) { + return FL_METHOD_RESPONSE( + fl_method_error_response_new(kNoScreenError, nullptr, nullptr)); + } + GtkWindow* window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view))); + + g_autoptr(GtkFileChooserNative) dialog = + create_dialog_for_method(window, method, properties); + + if (dialog == nullptr) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, "Unable to create dialog from arguments", nullptr)); + } + + gint response = gtk_native_dialog_run(GTK_NATIVE_DIALOG(dialog)); + g_autoptr(FlValue) result = nullptr; + if (response == GTK_RESPONSE_ACCEPT) { + if (return_list) { + result = fl_value_new_list(); + g_autoptr(GSList) filenames = + gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); + for (GSList* link = filenames; link != nullptr; link = link->next) { + g_autofree gchar* filename = static_cast(link->data); + fl_value_append_take(result, fl_value_new_string(filename)); + } + } else { + g_autofree gchar* filename = + gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + result = fl_value_new_string(filename); + } + } + + return FL_METHOD_RESPONSE(fl_method_success_response_new(result)); +} + +// Called when a method call is received from Flutter. +static void method_call_cb(FlMethodChannel* channel, FlMethodCall* method_call, + gpointer user_data) { + FlFileSelectorPlugin* self = FL_FILE_SELECTOR_PLUGIN(user_data); + + const gchar* method = fl_method_call_get_name(method_call); + FlValue* args = fl_method_call_get_args(method_call); + + g_autoptr(FlMethodResponse) response = nullptr; + if (strcmp(method, kOpenFileMethod) == 0) { + response = show_dialog(self, method, args, true); + } else if (strcmp(method, kGetDirectoryPathMethod) == 0 || + strcmp(method, kGetSavePathMethod) == 0) { + response = show_dialog(self, method, args, false); + } else { + response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new()); + } + + g_autoptr(GError) error = nullptr; + if (!fl_method_call_respond(method_call, response, &error)) + g_warning("Failed to send method call response: %s", error->message); +} + +static void fl_file_selector_plugin_dispose(GObject* object) { + FlFileSelectorPlugin* self = FL_FILE_SELECTOR_PLUGIN(object); + + g_clear_object(&self->registrar); + g_clear_object(&self->channel); + + G_OBJECT_CLASS(fl_file_selector_plugin_parent_class)->dispose(object); +} + +static void fl_file_selector_plugin_class_init( + FlFileSelectorPluginClass* klass) { + G_OBJECT_CLASS(klass)->dispose = fl_file_selector_plugin_dispose; +} + +static void fl_file_selector_plugin_init(FlFileSelectorPlugin* self) {} + +FlFileSelectorPlugin* fl_file_selector_plugin_new( + FlPluginRegistrar* registrar) { + FlFileSelectorPlugin* self = FL_FILE_SELECTOR_PLUGIN( + g_object_new(fl_file_selector_plugin_get_type(), nullptr)); + + self->registrar = FL_PLUGIN_REGISTRAR(g_object_ref(registrar)); + + g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); + self->channel = + fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar), + kChannelName, FL_METHOD_CODEC(codec)); + fl_method_channel_set_method_call_handler(self->channel, method_call_cb, + g_object_ref(self), g_object_unref); + + return self; +} + +void file_selector_plugin_register_with_registrar( + FlPluginRegistrar* registrar) { + FlFileSelectorPlugin* plugin = fl_file_selector_plugin_new(registrar); + g_object_unref(plugin); +} diff --git a/packages/file_selector/file_selector_linux/linux/file_selector_plugin_private.h b/packages/file_selector/file_selector_linux/linux/file_selector_plugin_private.h new file mode 100644 index 000000000000..e58a78ccda37 --- /dev/null +++ b/packages/file_selector/file_selector_linux/linux/file_selector_plugin_private.h @@ -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. + +#include + +#include "include/file_selector_linux/file_selector_plugin.h" + +// Creates a GtkFileChooserNative for the given method call. +GtkFileChooserNative* create_dialog_for_method(GtkWindow* window, + const gchar* method, + FlValue* properties); diff --git a/packages/file_selector/file_selector_linux/linux/include/file_selector_linux/file_selector_plugin.h b/packages/file_selector/file_selector_linux/linux/include/file_selector_linux/file_selector_plugin.h new file mode 100644 index 000000000000..98e90e5d68ab --- /dev/null +++ b/packages/file_selector/file_selector_linux/linux/include/file_selector_linux/file_selector_plugin.h @@ -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. + +#ifndef PLUGINS_FILE_SELECTOR_LINUX_FILE_SELECTOR_PLUGIN_H_ +#define PLUGINS_FILE_SELECTOR_LINUX_FILE_SELECTOR_PLUGIN_H_ + +// A plugin to show native save/open file choosers. + +#include + +G_BEGIN_DECLS + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default"))) +#else +#define FLUTTER_PLUGIN_EXPORT +#endif + +G_DECLARE_FINAL_TYPE(FlFileSelectorPlugin, fl_file_selector_plugin, FL, + FILE_SELECTOR_PLUGIN, GObject) + +FLUTTER_PLUGIN_EXPORT FlFileSelectorPlugin* fl_file_selector_plugin_new( + FlPluginRegistrar* registrar); + +FLUTTER_PLUGIN_EXPORT void file_selector_plugin_register_with_registrar( + FlPluginRegistrar* registrar); + +G_END_DECLS + +#endif // PLUGINS_FILE_SELECTOR_LINUX_FILE_SELECTOR_PLUGIN_H_ diff --git a/packages/file_selector/file_selector_linux/linux/test/file_selector_plugin_test.cc b/packages/file_selector/file_selector_linux/linux/test/file_selector_plugin_test.cc new file mode 100644 index 000000000000..84c55ac91900 --- /dev/null +++ b/packages/file_selector/file_selector_linux/linux/test/file_selector_plugin_test.cc @@ -0,0 +1,171 @@ +// 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. + +#include "include/file_selector_linux/file_selector_plugin.h" + +#include +#include +#include + +#include "file_selector_plugin_private.h" + +// TODO(stuartmorgan): Restructure the helper to take a callback for showing +// the dialog, so that the tests can mock out that callback with something +// that changes the selection so that the return value path can be tested +// as well. +// TODO(stuartmorgan): Add an injectable wrapper around +// gtk_file_chooser_native_new to allow for testing values that are given as +// construction paramaters and can't be queried later. + +TEST(FileSelectorPlugin, TestOpenSimple) { + g_autoptr(FlValue) args = fl_value_new_map(); + + g_autoptr(GtkFileChooserNative) dialog = + create_dialog_for_method(nullptr, "openFile", args); + + ASSERT_NE(dialog, nullptr); + EXPECT_EQ(gtk_file_chooser_get_action(GTK_FILE_CHOOSER(dialog)), + GTK_FILE_CHOOSER_ACTION_OPEN); + EXPECT_EQ(gtk_file_chooser_get_select_multiple(GTK_FILE_CHOOSER(dialog)), + false); +} + +TEST(FileSelectorPlugin, TestOpenMultiple) { + g_autoptr(FlValue) args = fl_value_new_map(); + fl_value_set_string_take(args, "multiple", fl_value_new_bool(true)); + + g_autoptr(GtkFileChooserNative) dialog = + create_dialog_for_method(nullptr, "openFile", args); + + ASSERT_NE(dialog, nullptr); + EXPECT_EQ(gtk_file_chooser_get_action(GTK_FILE_CHOOSER(dialog)), + GTK_FILE_CHOOSER_ACTION_OPEN); + EXPECT_EQ(gtk_file_chooser_get_select_multiple(GTK_FILE_CHOOSER(dialog)), + true); +} + +TEST(FileSelectorPlugin, TestOpenWithFilter) { + g_autoptr(FlValue) type_groups = fl_value_new_list(); + + { + g_autoptr(FlValue) text_group_mime_types = fl_value_new_list(); + fl_value_append_take(text_group_mime_types, + fl_value_new_string("text/plain")); + g_autoptr(FlValue) text_group = fl_value_new_map(); + fl_value_set_string_take(text_group, "label", fl_value_new_string("Text")); + fl_value_set_string(text_group, "mimeTypes", text_group_mime_types); + fl_value_append(type_groups, text_group); + } + + { + g_autoptr(FlValue) image_group_extensions = fl_value_new_list(); + fl_value_append_take(image_group_extensions, fl_value_new_string("*.png")); + fl_value_append_take(image_group_extensions, fl_value_new_string("*.gif")); + fl_value_append_take(image_group_extensions, + fl_value_new_string("*.jgpeg")); + g_autoptr(FlValue) image_group = fl_value_new_map(); + fl_value_set_string_take(image_group, "label", + fl_value_new_string("Images")); + fl_value_set_string(image_group, "extensions", image_group_extensions); + fl_value_append(type_groups, image_group); + } + + { + g_autoptr(FlValue) any_group_extensions = fl_value_new_list(); + fl_value_append_take(any_group_extensions, fl_value_new_string("*")); + g_autoptr(FlValue) any_group = fl_value_new_map(); + fl_value_set_string_take(any_group, "label", fl_value_new_string("Any")); + fl_value_set_string(any_group, "extensions", any_group_extensions); + fl_value_append(type_groups, any_group); + } + + g_autoptr(FlValue) args = fl_value_new_map(); + fl_value_set_string(args, "acceptedTypeGroups", type_groups); + + g_autoptr(GtkFileChooserNative) dialog = + create_dialog_for_method(nullptr, "openFile", args); + + ASSERT_NE(dialog, nullptr); + EXPECT_EQ(gtk_file_chooser_get_action(GTK_FILE_CHOOSER(dialog)), + GTK_FILE_CHOOSER_ACTION_OPEN); + EXPECT_EQ(gtk_file_chooser_get_select_multiple(GTK_FILE_CHOOSER(dialog)), + false); + // Validate filters. + g_autoptr(GSList) type_group_list = + gtk_file_chooser_list_filters(GTK_FILE_CHOOSER(dialog)); + EXPECT_EQ(g_slist_length(type_group_list), 3); + GtkFileFilter* text_filter = + GTK_FILE_FILTER(g_slist_nth_data(type_group_list, 0)); + GtkFileFilter* image_filter = + GTK_FILE_FILTER(g_slist_nth_data(type_group_list, 1)); + GtkFileFilter* any_filter = + GTK_FILE_FILTER(g_slist_nth_data(type_group_list, 2)); + // Filters can't be inspected, so query them to see that they match expected + // filter behavior. + GtkFileFilterInfo text_file_info = {}; + text_file_info.contains = static_cast( + GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE); + text_file_info.display_name = "foo.txt"; + text_file_info.mime_type = "text/plain"; + GtkFileFilterInfo image_file_info = {}; + image_file_info.contains = static_cast( + GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE); + image_file_info.display_name = "foo.png"; + image_file_info.mime_type = "image/png"; + EXPECT_TRUE(gtk_file_filter_filter(text_filter, &text_file_info)); + EXPECT_FALSE(gtk_file_filter_filter(text_filter, &image_file_info)); + EXPECT_FALSE(gtk_file_filter_filter(image_filter, &text_file_info)); + EXPECT_TRUE(gtk_file_filter_filter(image_filter, &image_file_info)); + EXPECT_TRUE(gtk_file_filter_filter(any_filter, &image_file_info)); + EXPECT_TRUE(gtk_file_filter_filter(any_filter, &text_file_info)); +} + +TEST(FileSelectorPlugin, TestSaveSimple) { + g_autoptr(FlValue) args = fl_value_new_map(); + + g_autoptr(GtkFileChooserNative) dialog = + create_dialog_for_method(nullptr, "getSavePath", args); + + ASSERT_NE(dialog, nullptr); + EXPECT_EQ(gtk_file_chooser_get_action(GTK_FILE_CHOOSER(dialog)), + GTK_FILE_CHOOSER_ACTION_SAVE); + EXPECT_EQ(gtk_file_chooser_get_select_multiple(GTK_FILE_CHOOSER(dialog)), + false); +} + +TEST(FileSelectorPlugin, TestSaveWithArguments) { + g_autoptr(FlValue) args = fl_value_new_map(); + fl_value_set_string_take(args, "initialDirectory", + fl_value_new_string("/tmp")); + fl_value_set_string_take(args, "suggestedName", + fl_value_new_string("foo.txt")); + + g_autoptr(GtkFileChooserNative) dialog = + create_dialog_for_method(nullptr, "getSavePath", args); + + ASSERT_NE(dialog, nullptr); + EXPECT_EQ(gtk_file_chooser_get_action(GTK_FILE_CHOOSER(dialog)), + GTK_FILE_CHOOSER_ACTION_SAVE); + EXPECT_EQ(gtk_file_chooser_get_select_multiple(GTK_FILE_CHOOSER(dialog)), + false); + g_autofree gchar* current_name = + gtk_file_chooser_get_current_name(GTK_FILE_CHOOSER(dialog)); + EXPECT_STREQ(current_name, "foo.txt"); + // TODO(stuartmorgan): gtk_file_chooser_get_current_folder doesn't seem to + // return a value set by gtk_file_chooser_set_current_folder, or at least + // doesn't in a test context, so that's not currently validated. +} + +TEST(FileSelectorPlugin, TestGetDirectory) { + g_autoptr(FlValue) args = fl_value_new_map(); + + g_autoptr(GtkFileChooserNative) dialog = + create_dialog_for_method(nullptr, "getDirectoryPath", args); + + ASSERT_NE(dialog, nullptr); + EXPECT_EQ(gtk_file_chooser_get_action(GTK_FILE_CHOOSER(dialog)), + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER); + EXPECT_EQ(gtk_file_chooser_get_select_multiple(GTK_FILE_CHOOSER(dialog)), + false); +} diff --git a/packages/file_selector/file_selector_linux/linux/test/test_main.cc b/packages/file_selector/file_selector_linux/linux/test/test_main.cc new file mode 100644 index 000000000000..7e33b21fd419 --- /dev/null +++ b/packages/file_selector/file_selector_linux/linux/test/test_main.cc @@ -0,0 +1,15 @@ +// 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. + +#include +#include + +int main(int argc, char** argv) { + gtk_init(0, nullptr); + + testing::InitGoogleTest(&argc, argv); + int exit_code = RUN_ALL_TESTS(); + + return exit_code; +} diff --git a/packages/file_selector/file_selector_linux/pubspec.yaml b/packages/file_selector/file_selector_linux/pubspec.yaml new file mode 100644 index 000000000000..369fd4445e65 --- /dev/null +++ b/packages/file_selector/file_selector_linux/pubspec.yaml @@ -0,0 +1,27 @@ +name: file_selector_linux +description: Liunx implementation of the file_selector plugin. +repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_linux +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 +version: 0.9.0 + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=2.8.0" + +flutter: + plugin: + implements: file_selector + platforms: + linux: + pluginClass: FileSelectorPlugin + dartPluginClass: FileSelectorLinux + +dependencies: + cross_file: ^0.3.1 + file_selector_platform_interface: ^2.1.0 + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter diff --git a/packages/file_selector/file_selector_linux/test/file_selector_linux_test.dart b/packages/file_selector/file_selector_linux/test/file_selector_linux_test.dart new file mode 100644 index 000000000000..67b99298adc3 --- /dev/null +++ b/packages/file_selector/file_selector_linux/test/file_selector_linux_test.dart @@ -0,0 +1,389 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_linux/file_selector_linux.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + late FileSelectorLinux plugin; + late List log; + + setUp(() { + plugin = FileSelectorLinux(); + log = []; + plugin.channel.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + return null; + }); + }); + + test('registers instance', () { + FileSelectorLinux.registerWith(); + expect(FileSelectorPlatform.instance, isA()); + }); + + group('#openFile', () { + test('passes the accepted type groups correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + extensions: ['txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); + + final XTypeGroup groupTwo = XTypeGroup( + label: 'image', + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image'], + webWildCards: ['image/*'], + ); + + await plugin.openFile(acceptedTypeGroups: [group, groupTwo]); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypeGroups': >[ + { + 'label': 'text', + 'extensions': ['*.txt'], + 'mimeTypes': ['text/plain'], + }, + { + 'label': 'image', + 'extensions': ['*.jpg'], + 'mimeTypes': ['image/jpg'], + }, + ], + 'initialDirectory': null, + 'confirmButtonText': null, + 'multiple': false, + }), + ], + ); + }); + + test('passes initialDirectory correctly', () async { + await plugin.openFile(initialDirectory: '/example/directory'); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'initialDirectory': '/example/directory', + 'confirmButtonText': null, + 'multiple': false, + }), + ], + ); + }); + + test('passes confirmButtonText correctly', () async { + await plugin.openFile(confirmButtonText: 'Open File'); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'initialDirectory': null, + 'confirmButtonText': 'Open File', + 'multiple': false, + }), + ], + ); + }); + + test('throws for a type group that does not support Linux', () async { + final XTypeGroup group = XTypeGroup( + label: 'images', + webWildCards: ['images/*'], + ); + + await expectLater( + plugin.openFile(acceptedTypeGroups: [group]), + throwsArgumentError); + }); + + test('passes a wildcard group correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'any', + ); + + await plugin.openFile(acceptedTypeGroups: [group]); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypeGroups': >[ + { + 'label': 'any', + 'extensions': ['*'], + }, + ], + 'initialDirectory': null, + 'confirmButtonText': null, + 'multiple': false, + }), + ], + ); + }); + }); + + group('#openFiles', () { + test('passes the accepted type groups correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + extensions: ['txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); + + final XTypeGroup groupTwo = XTypeGroup( + label: 'image', + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image'], + webWildCards: ['image/*'], + ); + + await plugin.openFiles(acceptedTypeGroups: [group, groupTwo]); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypeGroups': >[ + { + 'label': 'text', + 'extensions': ['*.txt'], + 'mimeTypes': ['text/plain'], + }, + { + 'label': 'image', + 'extensions': ['*.jpg'], + 'mimeTypes': ['image/jpg'], + }, + ], + 'initialDirectory': null, + 'confirmButtonText': null, + 'multiple': true, + }), + ], + ); + }); + + test('passes initialDirectory correctly', () async { + await plugin.openFiles(initialDirectory: '/example/directory'); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'initialDirectory': '/example/directory', + 'confirmButtonText': null, + 'multiple': true, + }), + ], + ); + }); + + test('passes confirmButtonText correctly', () async { + await plugin.openFiles(confirmButtonText: 'Open File'); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'initialDirectory': null, + 'confirmButtonText': 'Open File', + 'multiple': true, + }), + ], + ); + }); + + test('throws for a type group that does not support Linux', () async { + final XTypeGroup group = XTypeGroup( + label: 'images', + webWildCards: ['images/*'], + ); + + await expectLater( + plugin.openFile(acceptedTypeGroups: [group]), + throwsArgumentError); + }); + + test('passes a wildcard group correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'any', + ); + + await plugin.openFile(acceptedTypeGroups: [group]); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypeGroups': >[ + { + 'label': 'any', + 'extensions': ['*'], + }, + ], + 'initialDirectory': null, + 'confirmButtonText': null, + 'multiple': false, + }), + ], + ); + }); + }); + + group('#getSavePath', () { + test('passes the accepted type groups correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + extensions: ['txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); + + final XTypeGroup groupTwo = XTypeGroup( + label: 'image', + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image'], + webWildCards: ['image/*'], + ); + + await plugin + .getSavePath(acceptedTypeGroups: [group, groupTwo]); + + expect( + log, + [ + isMethodCall('getSavePath', arguments: { + 'acceptedTypeGroups': >[ + { + 'label': 'text', + 'extensions': ['*.txt'], + 'mimeTypes': ['text/plain'], + }, + { + 'label': 'image', + 'extensions': ['*.jpg'], + 'mimeTypes': ['image/jpg'], + }, + ], + 'initialDirectory': null, + 'suggestedName': null, + 'confirmButtonText': null, + }), + ], + ); + }); + + test('passes initialDirectory correctly', () async { + await plugin.getSavePath(initialDirectory: '/example/directory'); + + expect( + log, + [ + isMethodCall('getSavePath', arguments: { + 'initialDirectory': '/example/directory', + 'suggestedName': null, + 'confirmButtonText': null, + }), + ], + ); + }); + + test('passes confirmButtonText correctly', () async { + await plugin.getSavePath(confirmButtonText: 'Open File'); + + expect( + log, + [ + isMethodCall('getSavePath', arguments: { + 'initialDirectory': null, + 'suggestedName': null, + 'confirmButtonText': 'Open File', + }), + ], + ); + }); + + test('throws for a type group that does not support Linux', () async { + final XTypeGroup group = XTypeGroup( + label: 'images', + webWildCards: ['images/*'], + ); + + await expectLater( + plugin.openFile(acceptedTypeGroups: [group]), + throwsArgumentError); + }); + + test('passes a wildcard group correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'any', + ); + + await plugin.openFile(acceptedTypeGroups: [group]); + + expect( + log, + [ + isMethodCall('openFile', arguments: { + 'acceptedTypeGroups': >[ + { + 'label': 'any', + 'extensions': ['*'], + }, + ], + 'initialDirectory': null, + 'confirmButtonText': null, + 'multiple': false, + }), + ], + ); + }); + }); + + group('#getDirectoryPath', () { + test('passes initialDirectory correctly', () async { + await plugin.getDirectoryPath(initialDirectory: '/example/directory'); + + expect( + log, + [ + isMethodCall('getDirectoryPath', arguments: { + 'initialDirectory': '/example/directory', + 'confirmButtonText': null, + }), + ], + ); + }); + test('passes confirmButtonText correctly', () async { + await plugin.getDirectoryPath(confirmButtonText: 'Open File'); + + expect( + log, + [ + isMethodCall('getDirectoryPath', arguments: { + 'initialDirectory': null, + 'confirmButtonText': 'Open File', + }), + ], + ); + }); + }); +} diff --git a/packages/file_selector/file_selector_macos/CHANGELOG.md b/packages/file_selector/file_selector_macos/CHANGELOG.md index 0adf1ab891ad..499667d79d59 100644 --- a/packages/file_selector/file_selector_macos/CHANGELOG.md +++ b/packages/file_selector/file_selector_macos/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.9.0+1 + +* Updates README for endorsement. +* Updates `flutter_test` to be a `dev_dependencies` entry. + ## 0.9.0 * **BREAKING CHANGE**: Methods that take `XTypeGroup`s now throw an diff --git a/packages/file_selector/file_selector_macos/README.md b/packages/file_selector/file_selector_macos/README.md index 3241b21d1e18..10a5636ef4b1 100644 --- a/packages/file_selector/file_selector_macos/README.md +++ b/packages/file_selector/file_selector_macos/README.md @@ -4,19 +4,12 @@ The macOS implementation of [`file_selector`][1]. ## Usage -### Importing the package - -This implementation has not yet been endorsed, meaning that you need to -[depend on `file_selector_macos`][2] in addition to -[depending on `file_selector`][3]. - -Once your pubspec includes the macOS implementation, you can use the -`file_selector` APIs normally. You should not use the `file_selector_macos` -APIs directly. +This package is [endorsed][2], which means you can simply use `file_selector` +normally. This package will be automatically included in your app when you do. ### Entitlements -You will need to [add an entitlement][4] for either read-only access: +You will need to [add an entitlement][3] for either read-only access: ```xml com.apple.security.files.user-selected.read-only @@ -29,6 +22,5 @@ or read/write access: depending on your use case. [1]: https://pub.dev/packages/file_selector -[2]: https://pub.dev/packages/file_selector_macos/install -[3]: https://pub.dev/packages/file_selector/install -[4]: https://flutter.dev/desktop#entitlements-and-the-app-sandbox +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin +[3]: https://flutter.dev/desktop#entitlements-and-the-app-sandbox diff --git a/packages/file_selector/file_selector_macos/pubspec.yaml b/packages/file_selector/file_selector_macos/pubspec.yaml index fc9ca4d62275..44b842ed4536 100644 --- a/packages/file_selector/file_selector_macos/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_macos description: macOS implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.9.0 +version: 0.9.0+1 environment: sdk: ">=2.12.0 <3.0.0" @@ -21,5 +21,7 @@ dependencies: file_selector_platform_interface: ^2.1.0 flutter: sdk: flutter + +dev_dependencies: flutter_test: sdk: flutter diff --git a/packages/file_selector/file_selector_windows/CHANGELOG.md b/packages/file_selector/file_selector_windows/CHANGELOG.md index b0e6f0da6242..6db4fb3c4263 100644 --- a/packages/file_selector/file_selector_windows/CHANGELOG.md +++ b/packages/file_selector/file_selector_windows/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.9.1+1 + +* Updates README for endorsement. +* Updates `flutter_test` to be a `dev_dependencies` entry. + ## 0.9.1 * Converts the method channel to Pigeon. diff --git a/packages/file_selector/file_selector_windows/README.md b/packages/file_selector/file_selector_windows/README.md index 69fb088d599e..c597d704cadb 100644 --- a/packages/file_selector/file_selector_windows/README.md +++ b/packages/file_selector/file_selector_windows/README.md @@ -4,16 +4,8 @@ The Windows implementation of [`file_selector`][1]. ## Usage -### Importing the package - -This implementation has not yet been endorsed, meaning that you need to -[depend on `file_selector_windows`][2] in addition to -[depending on `file_selector`][3]. - -Once your pubspec includes the Windows implementation, you can use the -`file_selector` APIs normally. You should not use the `file_selector_windows` -APIs directly. +This package is [endorsed][2], which means you can simply use `file_selector` +normally. This package will be automatically included in your app when you do. [1]: https://pub.dev/packages/file_selector -[2]: https://pub.dev/packages/file_selector_windows/install -[3]: https://pub.dev/packages/file_selector/install +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/file_selector/file_selector_windows/pubspec.yaml b/packages/file_selector/file_selector_windows/pubspec.yaml index 6e398381b27a..e4f38d8d6b2f 100644 --- a/packages/file_selector/file_selector_windows/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_windows description: Windows implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.9.1 +version: 0.9.1+1 environment: sdk: ">=2.12.0 <3.0.0" @@ -21,10 +21,10 @@ dependencies: file_selector_platform_interface: ^2.1.0 flutter: sdk: flutter - flutter_test: - sdk: flutter dev_dependencies: build_runner: 2.1.11 + flutter_test: + sdk: flutter mockito: ^5.1.0 pigeon: ^3.2.5 diff --git a/script/configs/exclude_integration_linux.yaml b/script/configs/exclude_integration_linux.yaml new file mode 100644 index 000000000000..a83550e6808f --- /dev/null +++ b/script/configs/exclude_integration_linux.yaml @@ -0,0 +1,3 @@ +# Can't use Flutter integration tests due to native modal UI. +- file_selector +- file_selector_linux From 7ad8ae5a9240751b4a10cbf2e6b4c9625b0cb0a7 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 18 Aug 2022 19:18:06 -0400 Subject: [PATCH 634/844] [webview_flutter_platform_interface] Adds missing build params for WebViewWidget (#6279) --- .../CHANGELOG.md | 3 +- .../lib/v4/src/platform_webview_widget.dart | 75 ++++++++++++++++++- .../pubspec.yaml | 2 +- .../src/v4/platform_webview_widget_test.dart | 2 +- 4 files changed, 78 insertions(+), 4 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index 353e679f6467..12792ace964f 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md @@ -1,7 +1,8 @@ -## NEXT +## 1.9.2 * Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). +* Adds missing build params for v4 WebViewWidget interface. ## 1.9.1 diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_widget.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_widget.dart index 40334c650b3a..6d73162e6c4a 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_widget.dart @@ -2,8 +2,11 @@ // 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/widgets.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_controller.dart'; import 'webview_platform.dart'; @@ -33,5 +36,75 @@ abstract class PlatformWebViewWidget extends PlatformInterface { /// Builds a new WebView. /// /// Returns a Widget tree that embeds the created web view. - Widget build(BuildContext context); + Widget build(BuildParams params); +} + +/// Describes the parameters necessary for displaying the platform WebView. +/// +/// Platform specific implementations can add additional fields by extending +/// this class. +/// +/// {@tool sample} +/// This example demonstrates how to extend the [BuildParams] to provide +/// additional platform specific parameters. +/// +/// When extending [BuildParams], additional parameters should always accept +/// `null` or have a default value to prevent breaking changes. +/// +/// ```dart +/// @immutable +/// class WebKitBuildParams extends BuildParams { +/// WebKitBuildParams( +/// super.context, { +/// required super.controller, +/// super.layoutDirection, +/// super.gestureRecognizers, +/// this.platformSpecificFieldExample, +/// }); +/// +/// WebKitBuildParams.fromBuildParams( +/// BuildParams params, { +/// Object? platformSpecificFieldExample, +/// }) : this( +/// params.context, +/// controller: params.controller, +/// layoutDirection: params.layoutDirection, +/// gestureRecognizers: params.gestureRecognizers, +/// platformSpecificFieldExample: platformSpecificFieldExample, +/// ); +/// +/// final Object? platformSpecificFieldExample; +/// } +/// ``` +/// {@end-tool} +@immutable +class BuildParams { + /// Constructs a [BuildParams]. + const BuildParams( + this.context, { + required this.controller, + this.layoutDirection = TextDirection.ltr, + this.gestureRecognizers = const >{}, + }); + + /// Describes the part of the user interface represented by the returned + /// widget. + final BuildContext context; + + /// Controls the embedded WebView for the current platform. + final PlatformWebViewController controller; + + /// The layout direction to use for the embedded WebView. + final TextDirection layoutDirection; + + /// Specifies which gestures should be consumed by the web view. + /// + /// It is possible for other gesture recognizers to be competing with the web + /// view on pointer events, e.g if the web view is inside a [ListView] the + /// [ListView] will want to handle vertical drags. The web view will claim + /// gestures that are recognized by any of the recognizers on this list. + /// + /// When this is empty, the web view will only handle pointer events for + /// gestures that were not claimed by any other gesture recognizer. + final Set> gestureRecognizers; } diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml index 2c1fb0975866..d0fffe830798 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutte issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview_flutter%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.9.1 +version: 1.9.2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.dart index ede16c162413..ffa759072e5a 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.dart @@ -82,7 +82,7 @@ class ExtendsWebViewWidgetDelegate extends PlatformWebViewWidget { : super.implementation(params); @override - Widget build(BuildContext context) { + Widget build(BuildParams params) { throw UnimplementedError( 'build is not implemented for ExtendedWebViewWidgetDelegate.'); } From c9403f1cfa3809844b1c7a8613456968a750f419 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 19 Aug 2022 09:34:15 -0400 Subject: [PATCH 635/844] [file_selector] Endorse Linux implementation (#6293) --- CODEOWNERS | 1 + .../file_selector/file_selector/CHANGELOG.md | 4 + .../file_selector/file_selector/README.md | 18 +-- .../file_selector/example/linux/.gitignore | 1 + .../example/linux/CMakeLists.txt | 138 ++++++++++++++++++ .../example/linux/flutter/CMakeLists.txt | 88 +++++++++++ .../linux/flutter/generated_plugins.cmake | 24 +++ .../file_selector/example/linux/main.cc | 10 ++ .../example/linux/my_application.cc | 111 ++++++++++++++ .../example/linux/my_application.h | 22 +++ .../file_selector/file_selector/pubspec.yaml | 5 +- 11 files changed, 412 insertions(+), 10 deletions(-) create mode 100644 packages/file_selector/file_selector/example/linux/.gitignore create mode 100644 packages/file_selector/file_selector/example/linux/CMakeLists.txt create mode 100644 packages/file_selector/file_selector/example/linux/flutter/CMakeLists.txt create mode 100644 packages/file_selector/file_selector/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/file_selector/file_selector/example/linux/main.cc create mode 100644 packages/file_selector/file_selector/example/linux/my_application.cc create mode 100644 packages/file_selector/file_selector/example/linux/my_application.h diff --git a/CODEOWNERS b/CODEOWNERS index db8187d7d3f3..63bcf564e9a0 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -56,6 +56,7 @@ packages/video_player/video_player_avfoundation/** @hellohuanlin packages/webview_flutter/webview_flutter_wkwebview/** @cyanglaz # - Linux +packages/file_selector/file_selector_linux/** @cbracken packages/path_provider/path_provider_linux/** @cbracken packages/shared_preferences/shared_preferences_linux/** @cbracken packages/url_launcher/url_launcher_linux/** @cbracken diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 356e9ffe5550..024f0a03e796 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.1 + +* Adds an endorsed Linux implementation. + ## 0.9.0 * **BREAKING CHANGE**: The following methods: diff --git a/packages/file_selector/file_selector/README.md b/packages/file_selector/file_selector/README.md index a45153357ba2..c9727ef18d39 100644 --- a/packages/file_selector/file_selector/README.md +++ b/packages/file_selector/file_selector/README.md @@ -6,9 +6,9 @@ A Flutter plugin that manages files and interactions with file dialogs. -| | macOS | Web | Windows | -|-------------|--------|-----|-------------| -| **Support** | 10.11+ | Any | Windows 10+ | +| | Linux | macOS | Web | Windows | +|-------------|-------|--------|-----|-------------| +| **Support** | Any | 10.11+ | Any | Windows 10+ | ## Usage To use this plugin, add `file_selector` as a [dependency in your pubspec.yaml file](https://flutter.dev/platform-plugins/). @@ -83,12 +83,12 @@ Different platforms support different type group filter options. To avoid filters that cover all platforms you are targeting, or that you conditionally pass different `XTypeGroup`s based on `Platform`. -| | macOS | Web | Windows | -|----------------|--------|-----|-------------| -| `extensions` | ✔️ | ✔️ | ✔️ | -| `mimeTypes` | ✔️† | ✔️ | | -| `macUTIs` | ✔️ | | | -| `webWildCards` | | ✔️ | | +| | Linux | macOS | Web | Windows | +|----------------|-------|--------|-----|-------------| +| `extensions` | ✔️ | ✔️ | ✔️ | ✔️ | +| `mimeTypes` | ✔️ | ✔️† | ✔️ | | +| `macUTIs` | | ✔️ | | | +| `webWildCards` | | | ✔️ | | † `mimeTypes` are not supported on version of macOS earlier than 11 (Big Sur). diff --git a/packages/file_selector/file_selector/example/linux/.gitignore b/packages/file_selector/file_selector/example/linux/.gitignore new file mode 100644 index 000000000000..d3896c98444f --- /dev/null +++ b/packages/file_selector/file_selector/example/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/packages/file_selector/file_selector/example/linux/CMakeLists.txt b/packages/file_selector/file_selector/example/linux/CMakeLists.txt new file mode 100644 index 000000000000..39bed64e6674 --- /dev/null +++ b/packages/file_selector/file_selector/example/linux/CMakeLists.txt @@ -0,0 +1,138 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "example") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "dev.flutter.plugins.file_selector_linux_example") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/packages/file_selector/file_selector/example/linux/flutter/CMakeLists.txt b/packages/file_selector/file_selector/example/linux/flutter/CMakeLists.txt new file mode 100644 index 000000000000..d5bd01648a96 --- /dev/null +++ b/packages/file_selector/file_selector/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/packages/file_selector/file_selector/example/linux/flutter/generated_plugins.cmake b/packages/file_selector/file_selector/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000000..2db3c22ae228 --- /dev/null +++ b/packages/file_selector/file_selector/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,24 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + file_selector_linux +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/file_selector/file_selector/example/linux/main.cc b/packages/file_selector/file_selector/example/linux/main.cc new file mode 100644 index 000000000000..1507d02825e7 --- /dev/null +++ b/packages/file_selector/file_selector/example/linux/main.cc @@ -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. + +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/packages/file_selector/file_selector/example/linux/my_application.cc b/packages/file_selector/file_selector/example/linux/my_application.cc new file mode 100644 index 000000000000..3a67810f5612 --- /dev/null +++ b/packages/file_selector/file_selector/example/linux/my_application.cc @@ -0,0 +1,111 @@ +// 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. + +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "example"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments( + project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, + gchar*** arguments, + int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = + my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, "flags", + G_APPLICATION_NON_UNIQUE, nullptr)); +} diff --git a/packages/file_selector/file_selector/example/linux/my_application.h b/packages/file_selector/file_selector/example/linux/my_application.h new file mode 100644 index 000000000000..6e9f0c3ff665 --- /dev/null +++ b/packages/file_selector/file_selector/example/linux/my_application.h @@ -0,0 +1,22 @@ +// 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. + +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index 627a5921b163..56b82101fe8a 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.9.0 +version: 0.9.1 environment: sdk: ">=2.12.0 <3.0.0" @@ -12,6 +12,8 @@ environment: flutter: plugin: platforms: + linux: + default_package: file_selector_linux macos: default_package: file_selector_macos web: @@ -20,6 +22,7 @@ flutter: default_package: file_selector_windows dependencies: + file_selector_linux: ^0.9.0 file_selector_macos: ^0.9.0 file_selector_platform_interface: ^2.0.0 file_selector_web: ^0.9.0 From cd586d7d6a4082bf5bad6ab49450670844c542f6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 19 Aug 2022 12:58:03 -0400 Subject: [PATCH 636/844] Roll Flutter from 9eca99b8df79 to 663bb66bfd90 (12 revisions) (#6294) --- .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 681c8453de74..81085ba8a07e 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -9eca99b8df79585e98103f577342a2314ba26656 +663bb66bfd90514a241a101a1c9b260dddbe7321 From a42835a3f57d5c1d9a3dd30ce8b1cc45dd3afec7 Mon Sep 17 00:00:00 2001 From: tgalpha <61353126+tgalpha@users.noreply.github.com> Date: Sat, 20 Aug 2022 02:10:04 +0800 Subject: [PATCH 637/844] [file_selector_windows] Fix the problem that the initial directory does not work after completing a file selection on windows (#5416) --- .../file_selector_windows/CHANGELOG.md | 4 ++++ .../file_selector_windows/pubspec.yaml | 2 +- .../windows/file_dialog_controller.cpp | 4 ++-- .../windows/file_dialog_controller.h | 2 +- .../windows/file_selector_plugin.cpp | 6 +++--- .../windows/test/file_selector_plugin_test.cpp | 9 +++++++-- .../test/test_file_dialog_controller.cpp | 17 ++++++++++++++++- .../windows/test/test_file_dialog_controller.h | 11 ++++++++++- 8 files changed, 44 insertions(+), 11 deletions(-) diff --git a/packages/file_selector/file_selector_windows/CHANGELOG.md b/packages/file_selector/file_selector_windows/CHANGELOG.md index 6db4fb3c4263..1c53df0caae2 100644 --- a/packages/file_selector/file_selector_windows/CHANGELOG.md +++ b/packages/file_selector/file_selector_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.1+2 + +* Fixes the problem that the initial directory does not work after completing a file selection. + ## 0.9.1+1 * Updates README for endorsement. diff --git a/packages/file_selector/file_selector_windows/pubspec.yaml b/packages/file_selector/file_selector_windows/pubspec.yaml index e4f38d8d6b2f..90751df15664 100644 --- a/packages/file_selector/file_selector_windows/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_windows description: Windows implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.9.1+1 +version: 0.9.1+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_windows/windows/file_dialog_controller.cpp b/packages/file_selector/file_selector_windows/windows/file_dialog_controller.cpp index e4b1a2afcdde..5820c4a5da40 100644 --- a/packages/file_selector/file_selector_windows/windows/file_dialog_controller.cpp +++ b/packages/file_selector/file_selector_windows/windows/file_dialog_controller.cpp @@ -17,8 +17,8 @@ FileDialogController::FileDialogController(IFileDialog* dialog) FileDialogController::~FileDialogController() {} -HRESULT FileDialogController::SetDefaultFolder(IShellItem* folder) { - return dialog_->SetDefaultFolder(folder); +HRESULT FileDialogController::SetFolder(IShellItem* folder) { + return dialog_->SetFolder(folder); } HRESULT FileDialogController::SetFileName(const wchar_t* name) { diff --git a/packages/file_selector/file_selector_windows/windows/file_dialog_controller.h b/packages/file_selector/file_selector_windows/windows/file_dialog_controller.h index e7357338243e..f5c93974cbe9 100644 --- a/packages/file_selector/file_selector_windows/windows/file_dialog_controller.h +++ b/packages/file_selector/file_selector_windows/windows/file_dialog_controller.h @@ -30,7 +30,7 @@ class FileDialogController { FileDialogController& operator=(const FileDialogController&) = delete; // IFileDialog wrappers: - virtual HRESULT SetDefaultFolder(IShellItem* folder); + virtual HRESULT SetFolder(IShellItem* folder); virtual HRESULT SetFileName(const wchar_t* name); virtual HRESULT SetFileTypes(UINT count, COMDLG_FILTERSPEC* filters); virtual HRESULT SetOkButtonLabel(const wchar_t* text); diff --git a/packages/file_selector/file_selector_windows/windows/file_selector_plugin.cpp b/packages/file_selector/file_selector_windows/windows/file_selector_plugin.cpp index 7f37a1a50d50..b9e6d211b2d1 100644 --- a/packages/file_selector/file_selector_windows/windows/file_selector_plugin.cpp +++ b/packages/file_selector/file_selector_windows/windows/file_selector_plugin.cpp @@ -86,7 +86,7 @@ class DialogWrapper { // Attempts to set the default folder for the dialog to |path|, // if it exists. - void SetDefaultFolder(std::string_view path) { + void SetFolder(std::string_view path) { std::wstring wide_path = Utf16FromUtf8(path); IShellItemPtr item; last_result_ = SHCreateItemFromParsingName(wide_path.c_str(), nullptr, @@ -94,7 +94,7 @@ class DialogWrapper { if (!SUCCEEDED(last_result_)) { return; } - dialog_controller_->SetDefaultFolder(item); + dialog_controller_->SetFolder(item); } // Sets the file name that is initially shown in the dialog. @@ -230,7 +230,7 @@ ErrorOr ShowDialog( } if (initial_directory) { - dialog.SetDefaultFolder(*initial_directory); + dialog.SetFolder(*initial_directory); } if (suggested_name) { dialog.SetFileName(*suggested_name); diff --git a/packages/file_selector/file_selector_windows/windows/test/file_selector_plugin_test.cpp b/packages/file_selector/file_selector_windows/windows/test/file_selector_plugin_test.cpp index f3d130bb1787..2325a271b777 100644 --- a/packages/file_selector/file_selector_windows/windows/test/file_selector_plugin_test.cpp +++ b/packages/file_selector/file_selector_windows/windows/test/file_selector_plugin_test.cpp @@ -115,7 +115,9 @@ TEST(FileSelectorPlugin, TestOpenWithArguments) { EXPECT_EQ(parent, fake_window); // Validate arguments. - EXPECT_EQ(dialog.GetDefaultFolderPath(), L"C:\\Program Files"); + EXPECT_EQ(dialog.GetDialogFolderPath(), L"C:\\Program Files"); + // Make sure that the folder was called via SetFolder, not SetDefaultFolder. + EXPECT_EQ(dialog.GetSetFolderPath(), L"C:\\Program Files"); EXPECT_EQ(dialog.GetOkButtonLabel(), L"Open it!"); return MockShowResult(fake_result_array); @@ -322,7 +324,10 @@ TEST(FileSelectorPlugin, TestSaveWithArguments) { EXPECT_EQ(parent, fake_window); // Validate arguments. - EXPECT_EQ(dialog.GetDefaultFolderPath(), L"C:\\Program Files"); + EXPECT_EQ(dialog.GetDialogFolderPath(), L"C:\\Program Files"); + // Make sure that the folder was called via SetFolder, not + // SetDefaultFolder. + EXPECT_EQ(dialog.GetSetFolderPath(), L"C:\\Program Files"); EXPECT_EQ(dialog.GetFileName(), L"a name"); EXPECT_EQ(dialog.GetOkButtonLabel(), L"Save it!"); diff --git a/packages/file_selector/file_selector_windows/windows/test/test_file_dialog_controller.cpp b/packages/file_selector/file_selector_windows/windows/test/test_file_dialog_controller.cpp index a98b686ddd6e..15065f916c8b 100644 --- a/packages/file_selector/file_selector_windows/windows/test/test_file_dialog_controller.cpp +++ b/packages/file_selector/file_selector_windows/windows/test/test_file_dialog_controller.cpp @@ -20,6 +20,17 @@ TestFileDialogController::TestFileDialogController(IFileDialog* dialog, TestFileDialogController::~TestFileDialogController() {} +HRESULT TestFileDialogController::SetFolder(IShellItem* folder) { + wchar_t* path_chars = nullptr; + if (SUCCEEDED(folder->GetDisplayName(SIGDN_FILESYSPATH, &path_chars))) { + set_folder_path_ = path_chars; + } else { + set_folder_path_ = L""; + } + + return FileDialogController::SetFolder(folder); +} + HRESULT TestFileDialogController::SetFileTypes(UINT count, COMDLG_FILTERSPEC* filters) { filter_groups_.clear(); @@ -56,7 +67,11 @@ HRESULT TestFileDialogController::GetResults( return S_OK; } -std::wstring TestFileDialogController::GetDefaultFolderPath() const { +std::wstring TestFileDialogController::GetSetFolderPath() const { + return set_folder_path_; +} + +std::wstring TestFileDialogController::GetDialogFolderPath() const { IShellItemPtr item; if (!SUCCEEDED(dialog_->GetFolder(&item))) { return L""; diff --git a/packages/file_selector/file_selector_windows/windows/test/test_file_dialog_controller.h b/packages/file_selector/file_selector_windows/windows/test/test_file_dialog_controller.h index 2e7292bad4b2..1c221fc219f9 100644 --- a/packages/file_selector/file_selector_windows/windows/test/test_file_dialog_controller.h +++ b/packages/file_selector/file_selector_windows/windows/test/test_file_dialog_controller.h @@ -51,6 +51,7 @@ class TestFileDialogController : public FileDialogController { ~TestFileDialogController(); // FileDialogController: + HRESULT SetFolder(IShellItem* folder) override; HRESULT SetFileTypes(UINT count, COMDLG_FILTERSPEC* filters) override; HRESULT SetOkButtonLabel(const wchar_t* text) override; HRESULT Show(HWND parent) override; @@ -58,7 +59,14 @@ class TestFileDialogController : public FileDialogController { HRESULT GetResults(IShellItemArray** out_items) const override; // Accessors for validating IFileDialogController setter calls. - std::wstring GetDefaultFolderPath() const; + // Gets the folder path set by FileDialogController::SetFolder. + // + // This exists because there are multiple ways that the value returned by + // GetDialogFolderPath can be changed, so this allows specifically validating + // calls to SetFolder. + std::wstring GetSetFolderPath() const; + // Gets dialog folder path by calling IFileDialog::GetFolder. + std::wstring GetDialogFolderPath() const; std::wstring GetFileName() const; const std::vector& GetFileTypes() const; std::wstring GetOkButtonLabel() const; @@ -70,6 +78,7 @@ class TestFileDialogController : public FileDialogController { // The last set values, for IFileDialog properties that have setters but no // corresponding getters. + std::wstring set_folder_path_; std::wstring ok_button_label_; std::vector filter_groups_; }; From 6fd6fc5f7c43922b2094ff22c27f4a58035fce6e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 20 Aug 2022 11:44:28 -0400 Subject: [PATCH 638/844] Roll Flutter from 663bb66bfd90 to 723609bff1f7 (29 revisions) (#6297) --- .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 81085ba8a07e..95b3655700f9 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -663bb66bfd90514a241a101a1c9b260dddbe7321 +723609bff1f7eae8031079ae503b3b70432fc903 From baab165b95f3463e1ee3cf0c72ab4eeae6af5008 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 21 Aug 2022 11:50:36 -0400 Subject: [PATCH 639/844] Roll Flutter from 723609bff1f7 to 9d78b2fe2a03 (6 revisions) (#6298) --- .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 95b3655700f9..953eb1a57686 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -723609bff1f7eae8031079ae503b3b70432fc903 +9d78b2fe2a03fa359faf11f8db7fccb668b31a0e From 89fe2a2c0b09672032b67f83e8226b3b44eb31c3 Mon Sep 17 00:00:00 2001 From: Tarrin Neal Date: Sun, 21 Aug 2022 21:24:57 -0700 Subject: [PATCH 640/844] [video_player] code excerpts in README (#6296) * update README to pull code excerpts from basic * update version to 2.4.6 * new line * fix dart issues * update to match snippet * re-remove video_player from exclusion list * add . * sync readme to snippet * Roll Flutter from 663bb66bfd90 to 723609bff1f7 (29 revisions) (#6297) * Roll Flutter from 723609bff1f7 to 9d78b2fe2a03 (6 revisions) (#6298) Co-authored-by: engine-flutter-autoroll --- .../video_player/video_player/CHANGELOG.md | 3 +- packages/video_player/video_player/README.md | 12 ++- .../video_player/example/build.excerpt.yaml | 20 +++++ .../video_player/example/lib/basic.dart | 73 +++++++++++++++++++ .../video_player/example/pubspec.yaml | 1 + .../video_player/video_player/pubspec.yaml | 2 +- script/configs/temp_exclude_excerpt.yaml | 1 - 7 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 packages/video_player/video_player/example/build.excerpt.yaml create mode 100644 packages/video_player/video_player/example/lib/basic.dart diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index d593dfcbc751..7a47cce54a6e 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.4.7 +* Updates README via code excerpts. * Fixes violations of new analysis option use_named_constants. ## 2.4.6 diff --git a/packages/video_player/video_player/README.md b/packages/video_player/video_player/README.md index c870688a4fee..de10f1e2f1dd 100644 --- a/packages/video_player/video_player/README.md +++ b/packages/video_player/video_player/README.md @@ -1,3 +1,5 @@ + + # Video Player plugin for Flutter [![pub package](https://img.shields.io/pub/v/video_player.svg)](https://pub.dev/packages/video_player) @@ -50,19 +52,23 @@ The `VideoPlayerOptions.mixWithOthers` option can't be implemented in web, at le ## Example + ```dart -import 'package:video_player/video_player.dart'; import 'package:flutter/material.dart'; +import 'package:video_player/video_player.dart'; -void main() => runApp(VideoApp()); +void main() => runApp(const VideoApp()); +/// Stateful widget to fetch and then display video content. class VideoApp extends StatefulWidget { + const VideoApp({Key? key}) : super(key: key); + @override _VideoAppState createState() => _VideoAppState(); } class _VideoAppState extends State { - VideoPlayerController _controller; + late VideoPlayerController _controller; @override void initState() { diff --git a/packages/video_player/video_player/example/build.excerpt.yaml b/packages/video_player/video_player/example/build.excerpt.yaml new file mode 100644 index 000000000000..c9a9c71ba14f --- /dev/null +++ b/packages/video_player/video_player/example/build.excerpt.yaml @@ -0,0 +1,20 @@ +targets: + $default: + sources: + include: + - lib/** + - android/app/src/main/** + # Some default includes that aren't really used here but will prevent + # false-negative warnings: + - $package$ + - lib/$lib$ + exclude: + - '**/.*/**' + - '**/build/**' + - 'android/app/src/main/res/**' + builders: + code_excerpter|code_excerpter: + enabled: true + generate_for: + - '**/*.dart' + - android/**/*.xml diff --git a/packages/video_player/video_player/example/lib/basic.dart b/packages/video_player/video_player/example/lib/basic.dart new file mode 100644 index 000000000000..169f1cdd00a8 --- /dev/null +++ b/packages/video_player/video_player/example/lib/basic.dart @@ -0,0 +1,73 @@ +// 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 is used to extract code samples for the README.md file. +// Run update-excerpts if you modify this file. + +// ignore_for_file: library_private_types_in_public_api, public_member_api_docs + +// #docregion basic-example +import 'package:flutter/material.dart'; +import 'package:video_player/video_player.dart'; + +void main() => runApp(const VideoApp()); + +/// Stateful widget to fetch and then display video content. +class VideoApp extends StatefulWidget { + const VideoApp({Key? key}) : super(key: key); + + @override + _VideoAppState createState() => _VideoAppState(); +} + +class _VideoAppState extends State { + late VideoPlayerController _controller; + + @override + void initState() { + super.initState(); + _controller = VideoPlayerController.network( + 'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4') + ..initialize().then((_) { + // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed. + setState(() {}); + }); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Video Demo', + home: Scaffold( + body: Center( + child: _controller.value.isInitialized + ? AspectRatio( + aspectRatio: _controller.value.aspectRatio, + child: VideoPlayer(_controller), + ) + : Container(), + ), + floatingActionButton: FloatingActionButton( + onPressed: () { + setState(() { + _controller.value.isPlaying + ? _controller.pause() + : _controller.play(); + }); + }, + child: Icon( + _controller.value.isPlaying ? Icons.pause : Icons.play_arrow, + ), + ), + ), + ); + } + + @override + void dispose() { + super.dispose(); + _controller.dispose(); + } +} +// #enddocregion basic-example diff --git a/packages/video_player/video_player/example/pubspec.yaml b/packages/video_player/video_player/example/pubspec.yaml index e6b51f99e115..7b6aa09329fa 100644 --- a/packages/video_player/video_player/example/pubspec.yaml +++ b/packages/video_player/video_player/example/pubspec.yaml @@ -18,6 +18,7 @@ dependencies: path: ../ dev_dependencies: + build_runner: ^2.1.10 flutter_driver: sdk: flutter flutter_test: diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index c9d5fc5ec572..7e2df60d3cdd 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/plugins/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.4.6 +version: 2.4.7 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/script/configs/temp_exclude_excerpt.yaml b/script/configs/temp_exclude_excerpt.yaml index 9f14bf2315aa..c59983efd058 100644 --- a/script/configs/temp_exclude_excerpt.yaml +++ b/script/configs/temp_exclude_excerpt.yaml @@ -18,7 +18,6 @@ - plugin_platform_interface - quick_actions/quick_actions - shared_preferences/shared_preferences -- video_player/video_player - webview_flutter/webview_flutter - webview_flutter_android - webview_flutter_web From 927d50680a10a7103e44705621a0d58c0e2dc210 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 22 Aug 2022 11:54:51 -0400 Subject: [PATCH 641/844] Roll Flutter from 9d78b2fe2a03 to e04c8becf5ef (10 revisions) (#6301) --- .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 953eb1a57686..fe4b1db3d5a0 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -9d78b2fe2a03fa359faf11f8db7fccb668b31a0e +e04c8becf5efd147c9b174355a6faeb4022a53ec From 8f3749c3634269d5489acf243c9ef824788e1246 Mon Sep 17 00:00:00 2001 From: Rollin Su Date: Mon, 22 Aug 2022 12:02:54 -0700 Subject: [PATCH 642/844] [file_selector] Basic iOS implementation (#5448) --- .../file_selector_ios/.gitignore | 29 + .../file_selector/file_selector_ios/.metadata | 30 + .../file_selector/file_selector_ios/AUTHORS | 6 + .../file_selector_ios/CHANGELOG.md | 3 + .../file_selector/file_selector_ios/LICENSE | 25 + .../file_selector/file_selector_ios/README.md | 19 + .../file_selector_ios/example/.gitignore | 46 ++ .../file_selector_ios/example/.metadata | 10 + .../file_selector_ios/example/README.md | 4 + .../file_selector_ios/example/ios/.gitignore | 34 + .../ios/Flutter/AppFrameworkInfo.plist | 26 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + .../file_selector_ios/example/ios/Podfile | 46 ++ .../ios/Runner.xcodeproj/project.pbxproj | 771 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 97 +++ .../contents.xcworkspacedata | 10 + .../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 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 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 .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../example/ios/Runner/Info.plist | 51 ++ .../ios/Runner/Runner-Bridging-Header.h | 5 + .../ios/RunnerTests/FileSelectorTests.m | 98 +++ .../example/lib/home_page.dart | 63 ++ .../file_selector_ios/example/lib/main.dart | 38 + .../example/lib/open_image_page.dart | 95 +++ .../lib/open_multiple_images_page.dart | 107 +++ .../example/lib/open_text_page.dart | 92 +++ .../file_selector_ios/example/pubspec.yaml | 31 + .../example/test_driver/integration_test.dart | 7 + .../file_selector_ios/ios/.gitignore | 38 + .../file_selector_ios/ios/Assets/.gitkeep | 0 .../ios/Classes/FFSFileSelectorPlugin.h | 8 + .../ios/Classes/FFSFileSelectorPlugin.m | 88 ++ .../ios/Classes/FFSFileSelectorPlugin_Test.h | 22 + .../ios/Classes/FileSelectorPlugin.modulemap | 10 + .../ios/Classes/file_selector_ios-umbrella.h | 6 + .../ios/Classes/messages.g.h | 37 + .../ios/Classes/messages.g.m | 143 ++++ .../ios/file_selector_ios.podspec | 24 + .../lib/file_selector_ios.dart | 64 ++ .../file_selector_ios/lib/src/messages.g.dart | 101 +++ .../file_selector_ios/pigeons/copyright.txt | 3 + .../file_selector_ios/pigeons/messages.dart | 29 + .../file_selector_ios/pubspec.yaml | 30 + .../test/file_selector_ios_test.dart | 136 +++ .../test/file_selector_ios_test.mocks.dart | 38 + .../file_selector_ios/test/test_api.dart | 70 ++ script/configs/exclude_integration_ios.yaml | 2 + 75 files changed, 2865 insertions(+) create mode 100644 packages/file_selector/file_selector_ios/.gitignore create mode 100644 packages/file_selector/file_selector_ios/.metadata create mode 100644 packages/file_selector/file_selector_ios/AUTHORS create mode 100644 packages/file_selector/file_selector_ios/CHANGELOG.md create mode 100644 packages/file_selector/file_selector_ios/LICENSE create mode 100644 packages/file_selector/file_selector_ios/README.md create mode 100644 packages/file_selector/file_selector_ios/example/.gitignore create mode 100644 packages/file_selector/file_selector_ios/example/.metadata create mode 100644 packages/file_selector/file_selector_ios/example/README.md create mode 100644 packages/file_selector/file_selector_ios/example/ios/.gitignore create mode 100644 packages/file_selector/file_selector_ios/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 packages/file_selector/file_selector_ios/example/ios/Flutter/Debug.xcconfig create mode 100644 packages/file_selector/file_selector_ios/example/ios/Flutter/Release.xcconfig create mode 100644 packages/file_selector/file_selector_ios/example/ios/Podfile create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/AppDelegate.swift create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Info.plist create mode 100644 packages/file_selector/file_selector_ios/example/ios/Runner/Runner-Bridging-Header.h create mode 100644 packages/file_selector/file_selector_ios/example/ios/RunnerTests/FileSelectorTests.m create mode 100644 packages/file_selector/file_selector_ios/example/lib/home_page.dart create mode 100644 packages/file_selector/file_selector_ios/example/lib/main.dart create mode 100644 packages/file_selector/file_selector_ios/example/lib/open_image_page.dart create mode 100644 packages/file_selector/file_selector_ios/example/lib/open_multiple_images_page.dart create mode 100644 packages/file_selector/file_selector_ios/example/lib/open_text_page.dart create mode 100644 packages/file_selector/file_selector_ios/example/pubspec.yaml create mode 100644 packages/file_selector/file_selector_ios/example/test_driver/integration_test.dart create mode 100644 packages/file_selector/file_selector_ios/ios/.gitignore create mode 100644 packages/file_selector/file_selector_ios/ios/Assets/.gitkeep create mode 100644 packages/file_selector/file_selector_ios/ios/Classes/FFSFileSelectorPlugin.h create mode 100644 packages/file_selector/file_selector_ios/ios/Classes/FFSFileSelectorPlugin.m create mode 100644 packages/file_selector/file_selector_ios/ios/Classes/FFSFileSelectorPlugin_Test.h create mode 100644 packages/file_selector/file_selector_ios/ios/Classes/FileSelectorPlugin.modulemap create mode 100644 packages/file_selector/file_selector_ios/ios/Classes/file_selector_ios-umbrella.h create mode 100644 packages/file_selector/file_selector_ios/ios/Classes/messages.g.h create mode 100644 packages/file_selector/file_selector_ios/ios/Classes/messages.g.m create mode 100644 packages/file_selector/file_selector_ios/ios/file_selector_ios.podspec create mode 100644 packages/file_selector/file_selector_ios/lib/file_selector_ios.dart create mode 100644 packages/file_selector/file_selector_ios/lib/src/messages.g.dart create mode 100644 packages/file_selector/file_selector_ios/pigeons/copyright.txt create mode 100644 packages/file_selector/file_selector_ios/pigeons/messages.dart create mode 100644 packages/file_selector/file_selector_ios/pubspec.yaml create mode 100644 packages/file_selector/file_selector_ios/test/file_selector_ios_test.dart create mode 100644 packages/file_selector/file_selector_ios/test/file_selector_ios_test.mocks.dart create mode 100644 packages/file_selector/file_selector_ios/test/test_api.dart diff --git a/packages/file_selector/file_selector_ios/.gitignore b/packages/file_selector/file_selector_ios/.gitignore new file mode 100644 index 000000000000..9be145fde98d --- /dev/null +++ b/packages/file_selector/file_selector_ios/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/packages/file_selector/file_selector_ios/.metadata b/packages/file_selector/file_selector_ios/.metadata new file mode 100644 index 000000000000..295d2f7a8803 --- /dev/null +++ b/packages/file_selector/file_selector_ios/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: ac1aa511ca94f46c7e80b94dafd521de35e808e5 + channel: master + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: ac1aa511ca94f46c7e80b94dafd521de35e808e5 + base_revision: ac1aa511ca94f46c7e80b94dafd521de35e808e5 + - platform: ios + create_revision: ac1aa511ca94f46c7e80b94dafd521de35e808e5 + base_revision: ac1aa511ca94f46c7e80b94dafd521de35e808e5 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/packages/file_selector/file_selector_ios/AUTHORS b/packages/file_selector/file_selector_ios/AUTHORS new file mode 100644 index 000000000000..557dff97933b --- /dev/null +++ b/packages/file_selector/file_selector_ios/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/file_selector/file_selector_ios/CHANGELOG.md b/packages/file_selector/file_selector_ios/CHANGELOG.md new file mode 100644 index 000000000000..8c5d8857aec2 --- /dev/null +++ b/packages/file_selector/file_selector_ios/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.5.0 + +* Initial iOS implementation of `file_selector`. diff --git a/packages/file_selector/file_selector_ios/LICENSE b/packages/file_selector/file_selector_ios/LICENSE new file mode 100644 index 000000000000..c6823b81eb84 --- /dev/null +++ b/packages/file_selector/file_selector_ios/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/file_selector/file_selector_ios/README.md b/packages/file_selector/file_selector_ios/README.md new file mode 100644 index 000000000000..23b234d2b846 --- /dev/null +++ b/packages/file_selector/file_selector_ios/README.md @@ -0,0 +1,19 @@ +# file\_selector\_ios + +The iOS implementation of [`file_selector`][1]. + +## Usage + +### Importing the package + +This implementation has not yet been endorsed, meaning that you need to +[depend on `file_selector_ios`][2] in addition to +[depending on `file_selector`][3]. + +Once your pubspec includes the ios implementation, you can use the +`file_selector` APIs normally. You should not use the `file_selector_ios` +APIs directly. + +[1]: https://pub.dev/packages/file_selector +[2]: https://pub.dev/packages/file_selector_ios/install +[3]: https://pub.dev/packages/file_selector/install \ No newline at end of file diff --git a/packages/file_selector/file_selector_ios/example/.gitignore b/packages/file_selector/file_selector_ios/example/.gitignore new file mode 100644 index 000000000000..0fa6b675c0a5 --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/.gitignore @@ -0,0 +1,46 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/packages/file_selector/file_selector_ios/example/.metadata b/packages/file_selector/file_selector_ios/example/.metadata new file mode 100644 index 000000000000..3c3e4b52f734 --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 5464c5bac742001448fe4fc0597be939379f88ea + channel: stable + +project_type: app diff --git a/packages/file_selector/file_selector_ios/example/README.md b/packages/file_selector/file_selector_ios/example/README.md new file mode 100644 index 000000000000..9ed63fdb669f --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/README.md @@ -0,0 +1,4 @@ +# `file_selector_ios` example + +Demonstrates iOS implementation of the +[`file_selector` plugin](https://pub.dev/packages/file_selector). \ No newline at end of file diff --git a/packages/file_selector/file_selector_ios/example/ios/.gitignore b/packages/file_selector/file_selector_ios/example/ios/.gitignore new file mode 100644 index 000000000000..7a7f9873ad7d --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/packages/file_selector/file_selector_ios/example/ios/Flutter/AppFrameworkInfo.plist b/packages/file_selector/file_selector_ios/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000000..9625e105df39 --- /dev/null +++ b/packages/file_selector/file_selector_ios/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 + 11.0 + + diff --git a/packages/file_selector/file_selector_ios/example/ios/Flutter/Debug.xcconfig b/packages/file_selector/file_selector_ios/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000000..ec97fc6f3021 --- /dev/null +++ b/packages/file_selector/file_selector_ios/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/file_selector/file_selector_ios/example/ios/Flutter/Release.xcconfig b/packages/file_selector/file_selector_ios/example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000000..c4855bfe2000 --- /dev/null +++ b/packages/file_selector/file_selector_ios/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/file_selector/file_selector_ios/example/ios/Podfile b/packages/file_selector/file_selector_ios/example/ios/Podfile new file mode 100644 index 000000000000..3c0b3140c95a --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/ios/Podfile @@ -0,0 +1,46 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '11.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 + # Pods for testing + pod 'OCMock', '~> 3.8.1' + 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/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..e21f78a55c1b --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,771 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 21160A929DC757957DE39F1E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 000792269CB6B9FE88AC567C /* Pods_Runner.framework */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 6165A2F80DFA224EAF50A1D5 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AC3841659BF3693FAC5A2F8F /* Pods_RunnerTests.framework */; }; + 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 */; }; + C71AE4C8281C6B6B0086307A /* FileSelectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C71AE4C5281C6B530086307A /* FileSelectorTests.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + C71AE4BA281C6A090086307A /* 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 */ + 000792269CB6B9FE88AC567C /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 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 = ""; }; + 4A27CC0DB4EF6669B637A1E8 /* 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 = ""; }; + 5667547C6832727A744371E2 /* 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 = ""; }; + 5C0E87EDCB9350EC4916E293 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; 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 = ""; }; + 786CCB880423FD6D1019F59B /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 79C120FEED85F112A72B5D35 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; 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 = ""; }; + AC3841659BF3693FAC5A2F8F /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C71AE4B6281C6A090086307A /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + C71AE4C5281C6B530086307A /* FileSelectorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FileSelectorTests.m; sourceTree = ""; }; + F818CE2D7CDF8AFF94707327 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 21160A929DC757957DE39F1E /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C71AE4B3281C6A090086307A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 6165A2F80DFA224EAF50A1D5 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 2E44EE3EE3BCCAB6933171F8 /* Pods */ = { + isa = PBXGroup; + children = ( + 79C120FEED85F112A72B5D35 /* Pods-Runner.debug.xcconfig */, + F818CE2D7CDF8AFF94707327 /* Pods-Runner.release.xcconfig */, + 5C0E87EDCB9350EC4916E293 /* Pods-Runner.profile.xcconfig */, + 4A27CC0DB4EF6669B637A1E8 /* Pods-RunnerTests.debug.xcconfig */, + 5667547C6832727A744371E2 /* Pods-RunnerTests.release.xcconfig */, + 786CCB880423FD6D1019F59B /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + 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 = ( + C71AE4C4281C6B370086307A /* RunnerTests */, + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 2E44EE3EE3BCCAB6933171F8 /* Pods */, + C832A34FD3BC866442874ED0 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + C71AE4B6281C6A090086307A /* 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 = ""; + }; + C71AE4C4281C6B370086307A /* RunnerTests */ = { + isa = PBXGroup; + children = ( + C71AE4C5281C6B530086307A /* FileSelectorTests.m */, + ); + path = RunnerTests; + sourceTree = ""; + }; + C832A34FD3BC866442874ED0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 000792269CB6B9FE88AC567C /* Pods_Runner.framework */, + AC3841659BF3693FAC5A2F8F /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + AC24910767ED5F17F5245292 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + BE6D85B8F242B768015B938B /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; + C71AE4B5281C6A090086307A /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = C71AE4BF281C6A090086307A /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + A68B14611B411E4F96A5C80D /* [CP] Check Pods Manifest.lock */, + C71AE4B2281C6A090086307A /* Sources */, + C71AE4B3281C6A090086307A /* Frameworks */, + C71AE4B4281C6A090086307A /* Resources */, + 5BE5886DAAA885227DE0796D /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + C71AE4BB281C6A090086307A /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = FileSelectorTests; + productReference = C71AE4B6281C6A090086307A /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + C71AE4B5281C6A090086307A = { + CreatedOnToolsVersion = 13.1; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + }; + }; + 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 */, + C71AE4B5281C6A090086307A /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 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; + }; + C71AE4B4281C6A090086307A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 5BE5886DAAA885227DE0796D /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 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"; + }; + A68B14611B411E4F96A5C80D /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + AC24910767ED5F17F5245292 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + BE6D85B8F242B768015B938B /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C71AE4B2281C6A090086307A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C71AE4C8281C6B6B0086307A /* FileSelectorTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + C71AE4BB281C6A090086307A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = C71AE4BA281C6A090086307A /* 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; + 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; + 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 = 11.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)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.fileSelectorIosExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + 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; + 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 = 11.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; + 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; + 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 = 11.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)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.fileSelectorIosExample; + 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)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.fileSelectorIosExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + C71AE4BC281C6A090086307A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4A27CC0DB4EF6669B637A1E8 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.FileSelectorTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Debug; + }; + C71AE4BD281C6A090086307A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5667547C6832727A744371E2 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.FileSelectorTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Release; + }; + C71AE4BE281C6A090086307A /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 786CCB880423FD6D1019F59B /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GCC_C_LANGUAGE_STANDARD = gnu11; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.FileSelectorTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Profile; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 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; + }; + C71AE4BF281C6A090086307A /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C71AE4BC281C6A090086307A /* Debug */, + C71AE4BD281C6A090086307A /* Release */, + C71AE4BE281C6A090086307A /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..919434a6254f --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000000..f9b0d7c5ea15 --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..c842c6b3214b --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/file_selector/file_selector_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/file_selector/file_selector_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/file_selector/file_selector_ios/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000000..f9b0d7c5ea15 --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner/AppDelegate.swift b/packages/file_selector/file_selector_ios/example/ios/Runner/AppDelegate.swift new file mode 100644 index 000000000000..caf998393333 --- /dev/null +++ b/packages/file_selector/file_selector_ios/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 UIKit +import Flutter + +@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/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..d36b1fab2d9d --- /dev/null +++ b/packages/file_selector/file_selector_ios/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/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/file_selector/file_selector_ios/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<_Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV7ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 zcmds$`#;kQ7{|XelZftyR5~xW7?MLxS4^|Hw3&P7^y)@A9Fj{Xm1~_CIV^XZ%SLBn zA;!r`GqGHg=7>xrB{?psZQs88ZaedDoagm^KF{a*>G|dJWRSe^I$DNW008I^+;Kjt z>9p3GNR^I;v>5_`+91i(*G;u5|L+Bu6M=(afLjtkya#yZ175|z$pU~>2#^Z_pCZ7o z1c6UNcv2B3?; zX%qdxCXQpdKRz=#b*q0P%b&o)5ZrNZt7$fiETSK_VaY=mb4GK`#~0K#~9^ zcY!`#Af+4h?UMR-gMKOmpuYeN5P*RKF!(tb`)oe0j2BH1l?=>y#S5pMqkx6i{*=V9JF%>N8`ewGhRE(|WohnD59R^$_36{4>S zDFlPC5|k?;SPsDo87!B{6*7eqmMdU|QZ84>6)Kd9wNfh90=y=TFQay-0__>=<4pk& zYDjgIhL-jQ9o>z32K)BgAH+HxamL{ZL~ozu)Qqe@a`FpH=oQRA8=L-m-1dam(Ix2V z?du;LdMO+ooBelr^_y4{|44tmgH^2hSzPFd;U^!1p>6d|o)(-01z{i&Kj@)z-yfWQ)V#3Uo!_U}q3u`(fOs`_f^ueFii1xBNUB z6MecwJN$CqV&vhc+)b(p4NzGGEgwWNs z@*lUV6LaduZH)4_g!cE<2G6#+hJrWd5(|p1Z;YJ7ifVHv+n49btR}dq?HHDjl{m$T z!jLZcGkb&XS2OG~u%&R$(X+Z`CWec%QKt>NGYvd5g20)PU(dOn^7%@6kQb}C(%=vr z{?RP(z~C9DPnL{q^@pVw@|Vx~@3v!9dCaBtbh2EdtoNHm4kGxp>i#ct)7p|$QJs+U z-a3qtcPvhihub?wnJqEt>zC@)2suY?%-96cYCm$Q8R%-8$PZYsx3~QOLMDf(piXMm zB=<63yQk1AdOz#-qsEDX>>c)EES%$owHKue;?B3)8aRd}m~_)>SL3h2(9X;|+2#7X z+#2)NpD%qJvCQ0a-uzZLmz*ms+l*N}w)3LRQ*6>|Ub-fyptY(keUxw+)jfwF5K{L9 z|Cl_w=`!l_o><384d&?)$6Nh(GAm=4p_;{qVn#hI8lqewW7~wUlyBM-4Z|)cZr?Rh z=xZ&Ol>4(CU85ea(CZ^aO@2N18K>ftl8>2MqetAR53_JA>Fal`^)1Y--Am~UDa4th zKfCYpcXky$XSFDWBMIl(q=Mxj$iMBX=|j9P)^fDmF(5(5$|?Cx}DKEJa&XZP%OyE`*GvvYQ4PV&!g2|L^Q z?YG}tx;sY@GzMmsY`7r$P+F_YLz)(e}% zyakqFB<6|x9R#TdoP{R$>o7y(-`$$p0NxJ6?2B8tH)4^yF(WhqGZlM3=9Ibs$%U1w zWzcss*_c0=v_+^bfb`kBFsI`d;ElwiU%frgRB%qBjn@!0U2zZehBn|{%uNIKBA7n= zzE`nnwTP85{g;8AkYxA68>#muXa!G>xH22D1I*SiD~7C?7Za+9y7j1SHiuSkKK*^O zsZ==KO(Ua#?YUpXl{ViynyT#Hzk=}5X$e04O@fsMQjb}EMuPWFO0e&8(2N(29$@Vd zn1h8Yd>6z(*p^E{c(L0Lg=wVdupg!z@WG;E0k|4a%s7Up5C0c)55XVK*|x9RQeZ1J@1v9MX;>n34(i>=YE@Iur`0Vah(inE3VUFZNqf~tSz{1fz3Fsn_x4F>o(Yo;kpqvBe-sbwH(*Y zu$JOl0b83zu$JMvy<#oH^Wl>aWL*?aDwnS0iEAwC?DK@aT)GHRLhnz2WCvf3Ba;o=aY7 z2{Asu5MEjGOY4O#Ggz@@J;q*0`kd2n8I3BeNuMmYZf{}pg=jTdTCrIIYuW~luKecn z+E-pHY%ohj@uS0%^ z&(OxwPFPD$+#~`H?fMvi9geVLci(`K?Kj|w{rZ9JgthFHV+=6vMbK~0)Ea<&WY-NC zy-PnZft_k2tfeQ*SuC=nUj4H%SQ&Y$gbH4#2sT0cU0SdFs=*W*4hKGpuR1{)mV;Qf5pw4? zfiQgy0w3fC*w&Bj#{&=7033qFR*<*61B4f9K%CQvxEn&bsWJ{&winp;FP!KBj=(P6 z4Z_n4L7cS;ao2)ax?Tm|I1pH|uLpDSRVghkA_UtFFuZ0b2#>!8;>-_0ELjQSD-DRd z4im;599VHDZYtnWZGAB25W-e(2VrzEh|etsv2YoP#VbIZ{aFkwPrzJ#JvCvA*mXS& z`}Q^v9(W4GiSs}#s7BaN!WA2bniM$0J(#;MR>uIJ^uvgD3GS^%*ikdW6-!VFUU?JV zZc2)4cMsX@j z5HQ^e3BUzOdm}yC-xA%SY``k$rbfk z;CHqifhU*jfGM@DkYCecD9vl*qr58l6x<8URB=&%{!Cu3RO*MrKZ4VO}V6R0a zZw3Eg^0iKWM1dcTYZ0>N899=r6?+adUiBKPciJw}L$=1f4cs^bio&cr9baLF>6#BM z(F}EXe-`F=f_@`A7+Q&|QaZ??Txp_dB#lg!NH=t3$G8&06MFhwR=Iu*Im0s_b2B@| znW>X}sy~m#EW)&6E&!*0%}8UAS)wjt+A(io#wGI@Z2S+Ms1Cxl%YVE800007ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 zcmVjJBgitF5mAp-i>4+KS_oR{|13AP->1TD4=w)g|)JHOx|a2Wk1Va z!k)vP$UcQ#mdj%wNQoaJ!w>jv_6&JPyutpQps?s5dmDQ>`%?Bvj>o<%kYG!YW6H-z zu`g$@mp`;qDR!51QaS}|ZToSuAGcJ7$2HF0z`ln4t!#Yg46>;vGG9N9{V@9z#}6v* zfP?}r6b{*-C*)(S>NECI_E~{QYzN5SXRmVnP<=gzP+_Sp(Aza_hKlZ{C1D&l*(7IKXxQC1Z9#6wx}YrGcn~g%;icdw>T0Rf^w0{ z$_wn1J+C0@!jCV<%Go5LA45e{5gY9PvZp8uM$=1}XDI+9m7!A95L>q>>oe0$nC->i zeexUIvq%Uk<-$>DiDb?!In)lAmtuMWxvWlk`2>4lNuhSsjAf2*2tjT`y;@d}($o)S zn(+W&hJ1p0xy@oxP%AM15->wPLp{H!k)BdBD$toBpJh+crWdsNV)qsHaqLg2_s|Ih z`8E9z{E3sA!}5aKu?T!#enD(wLw?IT?k-yWVHZ8Akz4k5(TZJN^zZgm&zM28sfTD2BYJ|Fde3Xzh;;S` z=GXTnY4Xc)8nYoz6&vF;P7{xRF-{|2Xs5>a5)@BrnQ}I(_x7Cgpx#5&Td^4Q9_FnQ zX5so*;#8-J8#c$OlA&JyPp$LKUhC~-e~Ij!L%uSMu!-VZG7Hx-L{m2DVR2i=GR(_% zCVD!4N`I)&Q5S`?P&fQZ=4#Dgt_v2-DzkT}K(9gF0L(owe-Id$Rc2qZVLqI_M_DyO z9@LC#U28_LU{;wGZ&))}0R2P4MhajKCd^K#D+JJ&JIXZ_p#@+7J9A&P<0kdRujtQ_ zOy>3=C$kgi6$0pW06KaLz!21oOryKM3ZUOWqppndxfH}QpgjEJ`j7Tzn5bk6K&@RA?vl##y z$?V~1E(!wB5rH`>3nc&@)|#<1dN2cMzzm=PGhQ|Yppne(C-Vlt450IXc`J4R0W@I7 zd1e5uW6juvO%ni(WX7BsKx3MLngO7rHO;^R5I~0^nE^9^E_eYLgiR9&KnJ)pBbfno zSVnW$0R+&6jOOsZ82}nJ126+c|%svPo;TeUku<2G7%?$oft zyaO;tVo}(W)VsTUhq^XmFi#2z%-W9a{7mXn{uzivYQ_d6b7VJG{77naW(vHt-uhnY zVN#d!JTqVh(7r-lhtXVU6o})aZbDt_;&wJVGl2FKYFBFpU-#9U)z#(A%=IVnqytR$SY-sO( z($oNE09{D^@OuYPz&w~?9>Fl5`g9u&ecFGhqX=^#fmR=we0CJw+5xna*@oHnkahk+ z9aWeE3v|An+O5%?4fA&$Fgu~H_YmqR!yIU!bFCk4!#pAj%(lI(A5n)n@Id#M)O9Yx zJU9oKy{sRAIV3=5>(s8n{8ryJ!;ho}%pn6hZKTKbqk=&m=f*UnK$zW3YQP*)pw$O* zIfLA^!-bmBl6%d_n$#tP8Zd_(XdA*z*WH|E_yILwjtI~;jK#v-6jMl^?<%Y%`gvpwv&cFb$||^v4D&V=aNy?NGo620jL3VZnA%s zH~I|qPzB~e(;p;b^gJr7Ure#7?8%F0m4vzzPy^^(q4q1OdthF}Fi*RmVZN1OwTsAP zn9CZP`FazX3^kG(KodIZ=Kty8DLTy--UKfa1$6XugS zk%6v$Kmxt6U!YMx0JQ)0qX*{CXwZZk$vEROidEc7=J-1;peNat!vS<3P-FT5po>iE z!l3R+<`#x|+_hw!HjQGV=8!q|76y8L7N8gP3$%0kfush|u0uU^?dKBaeRSBUpOZ0c z62;D&Mdn2}N}xHRFTRI?zRv=>=AjHgH}`2k4WK=#AHB)UFrR-J87GgX*x5fL^W2#d z=(%K8-oZfMO=i{aWRDg=FX}UubM4eotRDcn;OR#{3q=*?3mE3_oJ-~prjhxh%PgQT zyn)Qozaq0@o&|LEgS{Ind4Swsr;b`u185hZPOBLL<`d2%^Yp1?oL)=jnLi;Zo0ZDliTtQ^b5SmfIMe{T==zZkbvn$KTQGlbG8w}s@M3TZnde;1Am46P3juKb zl9GU&3F=q`>j!`?SyH#r@O59%@aMX^rx}Nxe<>NqpUp5=lX1ojGDIR*-D^SDuvCKF z?3$xG(gVUsBERef_YjPFl^rU9EtD{pt z0CXwpN7BN3!8>hajGaTVk-wl=9rxmfWtIhC{mheHgStLi^+Nz12a?4r(fz)?3A%at zMlvQmL<2-R)-@G1wJ0^zQK%mR=r4d{Y3fHp){nWXUL#|CqXl(+v+qDh>FkF9`eWrW zfr^D%LNfOcTNvtx0JXR35J0~Jpi2#P3Q&80w+nqNfc}&G0A~*)lGHKv=^FE+b(37|)zL;KLF>oiGfb(?&1 zV3XRu!Sw>@quKiab%g6jun#oZ%!>V#A%+lNc?q>6+VvyAn=kf_6z^(TZUa4Eelh{{ zqFX-#dY(EV@7l$NE&kv9u9BR8&Ojd#ZGJ6l8_BW}^r?DIS_rU2(XaGOK z225E@kH5Opf+CgD^{y29jD4gHbGf{1MD6ggQ&%>UG4WyPh5q_tb`{@_34B?xfSO*| zZv8!)q;^o-bz`MuxXk*G^}(6)ACb@=Lfs`Hxoh>`Y0NE8QRQ!*p|SH@{r8=%RKd4p z+#Ty^-0kb=-H-O`nAA3_6>2z(D=~Tbs(n8LHxD0`R0_ATFqp-SdY3(bZ3;VUM?J=O zKCNsxsgt@|&nKMC=*+ZqmLHhX1KHbAJs{nGVMs6~TiF%Q)P@>!koa$%oS zjXa=!5>P`vC-a}ln!uH1ooeI&v?=?v7?1n~P(wZ~0>xWxd_Aw;+}9#eULM7M8&E?Y zC-ZLhi3RoM92SXUb-5i-Lmt5_rfjE{6y^+24`y$1lywLyHO!)Boa7438K4#iLe?rh z2O~YGSgFUBH?og*6=r9rme=peP~ah`(8Zt7V)j5!V0KPFf_mebo3z95U8(up$-+EA^9dTRLq>Yl)YMBuch9%=e5B`Vnb>o zt03=kq;k2TgGe4|lGne&zJa~h(UGutjP_zr?a7~#b)@15XNA>Dj(m=gg2Q5V4-$)D|Q9}R#002ovPDHLkV1o7DH3k3x literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 zcmV-m2cP(fP)x~L`~4d)Rspd&<9kFh{hn*KP1LP0~$;u(LfAu zp%fx&qLBcRHx$G|3q(bv@+b;o0*D|jwD-Q9uQR(l*ST}s+uPgQ-MeFwZ#GS?b332? z&Tk$&_miXn3IGq)AmQ)3sisq{raD4(k*bHvpCe-TdWq^NRTEVM)i9xbgQ&ccnUVx* zEY%vS%gDcSg=!tuIK8$Th2_((_h^+7;R|G{n06&O2#6%LK`a}n?h_fL18btz<@lFG za}xS}u?#DBMB> zw^b($1Z)`9G?eP95EKi&$eOy@K%h;ryrR3la%;>|o*>CgB(s>dDcNOXg}CK9SPmD? zmr-s{0wRmxUnbDrYfRvnZ@d z6johZ2sMX{YkGSKWd}m|@V7`Degt-43=2M?+jR%8{(H$&MLLmS;-|JxnX2pnz;el1jsvqQz}pGSF<`mqEXRQ5sC4#BbwnB_4` zc5bFE-Gb#JV3tox9fp-vVEN{(tOCpRse`S+@)?%pz+zVJXSooTrNCUg`R6`hxwb{) zC@{O6MKY8tfZ5@!yy=p5Y|#+myRL=^{tc(6YgAnkg3I(Cd!r5l;|;l-MQ8B`;*SCE z{u)uP^C$lOPM z5d~UhKhRRmvv{LIa^|oavk1$QiEApSrP@~Jjbg`<*dW4TO?4qG%a%sTPUFz(QtW5( zM)lA+5)0TvH~aBaOAs|}?u2FO;yc-CZ1gNM1dAxJ?%m?YsGR`}-xk2*dxC}r5j$d* zE!#Vtbo69h>V4V`BL%_&$} z+oJAo@jQ^Tk`;%xw-4G>hhb&)B?##U+(6Fi7nno`C<|#PVA%$Y{}N-?(Gc$1%tr4Pc}}hm~yY#fTOe!@v9s-ik$dX~|ygArPhByaXn8 zpI^FUjNWMsTFKTP3X7m?UK)3m zp6rI^_zxRYrx6_QmhoWoDR`fp4R7gu6;gdO)!KexaoO2D88F9x#TM1(9Bn7g;|?|o z)~$n&Lh#hCP6_LOPD>a)NmhW})LADx2kq=X7}7wYRj-0?dXr&bHaRWCfSqvzFa=sn z-8^gSyn-RmH=BZ{AJZ~!8n5621GbUJV7Qvs%JNv&$%Q17s_X%s-41vAPfIR>;x0Wlqr5?09S>x#%Qkt>?(&XjFRY}*L6BeQ3 z<6XEBh^S7>AbwGm@XP{RkeEKj6@_o%oV?hDuUpUJ+r#JZO?!IUc;r0R?>mi)*ZpQ) z#((dn=A#i_&EQn|hd)N$#A*fjBFuiHcYvo?@y1 z5|fV=a^a~d!c-%ZbMNqkMKiSzM{Yq=7_c&1H!mXk60Uv32dV;vMg&-kQ)Q{+PFtwc zj|-uQ;b^gts??J*9VxxOro}W~Q9j4Em|zSRv)(WSO9$F$s=Ydu%Q+5DOid~lwk&we zY%W(Z@ofdwPHncEZzZgmqS|!gTj3wQq9rxQy+^eNYKr1mj&?tm@wkO*9@UtnRMG>c aR{jt9+;fr}hV%pg00001^@s67{VYS000c7NklQEG_j zup^)eW&WUIApqy$=APz8jE@awGp)!bsTjDbrJO`$x^ZR^dr;>)LW>{ zs70vpsD38v)19rI=GNk1b(0?Js9~rjsQsu*K;@SD40RB-3^gKU-MYC7G!Bw{fZsqp zih4iIi;Hr_xZ033Iu{sQxLS=}yBXgLMn40d++>aQ0#%8D1EbGZp7+ z5=mK?t31BkVYbGOxE9`i748x`YgCMwL$qMsChbSGSE1`p{nSmadR zcQ#R)(?!~dmtD0+D2!K zR9%!Xp1oOJzm(vbLvT^$IKp@+W2=-}qTzTgVtQ!#Y7Gxz}stUIm<1;oBQ^Sh2X{F4ibaOOx;5ZGSNK z0maF^@(UtV$=p6DXLgRURwF95C=|U8?osGhgOED*b z7woJ_PWXBD>V-NjQAm{~T%sjyJ{5tn2f{G%?J!KRSrrGvQ1(^`YLA5B!~eycY(e5_ z*%aa{at13SxC(=7JT7$IQF~R3sy`Nn%EMv!$-8ZEAryB*yB1k&stni)=)8-ODo41g zkJu~roIgAih94tb=YsL%iH5@^b~kU9M-=aqgXIrbtxMpFy5mekFm#edF9z7RQ6V}R zBIhbXs~pMzt0VWy1Fi$^fh+1xxLDoK09&5&MJl(q#THjPm(0=z2H2Yfm^a&E)V+a5 zbi>08u;bJsDRUKR9(INSc7XyuWv(JsD+BB*0hS)FO&l&7MdViuur@-<-EHw>kHRGY zqoT}3fDv2-m{NhBG8X}+rgOEZ;amh*DqN?jEfQdqxdj08`Sr=C-KmT)qU1 z+9Cl)a1mgXxhQiHVB}l`m;-RpmKy?0*|yl?FXvJkFxuu!fKlcmz$kN(a}i*saM3nr z0!;a~_%Xqy24IxA2rz<+08=B-Q|2PT)O4;EaxP^6qixOv7-cRh?*T?zZU`{nIM-at zTKYWr9rJ=tppQ9I#Z#mLgINVB!pO-^FOcvFw6NhV0gztuO?g ztoA*C-52Q-Z-P#xB4HAY3KQVd%dz1S4PA3vHp0aa=zAO?FCt zC_GaTyVBg2F!bBr3U@Zy2iJgIAt>1sf$JWA9kh{;L+P*HfUBX1Zy{4MgNbDfBV_ly z!y#+753arsZUt@366jIC0klaC@ckuk!qu=pAyf7&QmiBUT^L1&tOHzsK)4n|pmrVT zs2($4=?s~VejTFHbFdDOwG;_58LkIj1Fh@{glkO#F1>a==ymJS$z;gdedT1zPx4Kj ztjS`y_C}%af-RtpehdQDt3a<=W5C4$)9W@QAse;WUry$WYmr51ml9lkeunUrE`-3e zmq1SgSOPNEE-Mf+AGJ$g0M;3@w!$Ej;hMh=v=I+Lpz^n%Pg^MgwyqOkNyu2c^of)C z1~ALor3}}+RiF*K4+4{(1%1j3pif1>sv0r^mTZ?5Jd-It!tfPfiG_p$AY*Vfak%FG z4z#;wLtw&E&?}w+eKG^=#jF7HQzr8rV0mY<1YAJ_uGz~$E13p?F^fPSzXSn$8UcI$ z8er9{5w5iv0qf8%70zV71T1IBB1N}R5Kp%NO0=5wJalZt8;xYp;b{1K) zHY>2wW-`Sl{=NpR%iu3(u6l&)rc%%cSA#aV7WCowfbFR4wcc{LQZv~o1u_`}EJA3>ki`?9CKYTA!rhO)if*zRdd}Kn zEPfYbhoVE~!FI_2YbC5qAj1kq;xP6%J8+?2PAs?`V3}nyFVD#sV3+uP`pi}{$l9U^ zSz}_M9f7RgnnRhaoIJgT8us!1aB&4!*vYF07Hp&}L zCRlop0oK4DL@ISz{2_BPlezc;xj2|I z23RlDNpi9LgTG_#(w%cMaS)%N`e>~1&a3<{Xy}>?WbF>OOLuO+j&hc^YohQ$4F&ze z+hwnro1puQjnKm;vFG~o>`kCeUIlkA-2tI?WBKCFLMBY=J{hpSsQ=PDtU$=duS_hq zHpymHt^uuV1q@uc4bFb{MdG*|VoW@15Osrqt2@8ll0qO=j*uOXn{M0UJX#SUztui9FN4)K3{9!y8PC-AHHvpVTU;x|-7P+taAtyglk#rjlH2 z5Gq8ik}BPaGiM{#Woyg;*&N9R2{J0V+WGB69cEtH7F?U~Kbi6ksi*`CFXsi931q7Y zGO82?whBhN%w1iDetv%~wM*Y;E^)@Vl?VDj-f*RX>{;o_=$fU!&KAXbuadYZ46Zbg z&6jMF=49$uL^73y;;N5jaHYv)BTyfh&`qVLYn?`o6BCA_z-0niZz=qPG!vonK3MW_ zo$V96zM!+kJRs{P-5-rQVse0VBH*n6A58)4uc&gfHMa{gIhV2fGf{st>E8sKyP-$8zp~wJX^A*@DI&-;8>gANXZj zU)R+Y)PB?=)a|Kj>8NXEu^S_h^7R`~Q&7*Kn!xyvzVv&^>?^iu;S~R2e-2fJx-oUb cX)(b1KSk$MOV07*qoM6N<$f&6$jw%VRuvdN2+38CZWny1cRtlsl+0_KtW)EU14Ei(F!UtWuj4IK+3{sK@>rh zs1Z;=(DD&U6+tlyL?UnHVN^&g6QhFi2#HS+*qz;(>63G(`|jRtW|nz$Pv7qTovP!^ zP_jES{mr@O-02w%!^a?^1ZP!_KmQiz0L~jZ=W@Qt`8wzOoclQsAS<5YdH;a(4bGLE zk8s}1If(PSIgVi!XE!5kA?~z*sobvNyohr;=Q_@h2@$6Flyej3J)D-6YfheRGl`HEcPk|~huT_2-U?PfL=4BPV)f1o!%rQ!NMt_MYw-5bUSwQ9Z&zC>u zOrl~UJglJNa%f50Ok}?WB{on`Ci`p^Y!xBA?m@rcJXLxtrE0FhRF3d*ir>yzO|BD$ z3V}HpFcCh6bTzY}Nt_(W%QYd3NG)jJ4<`F<1Od) zfQblTdC&h2lCz`>y?>|9o2CdvC8qZeIZt%jN;B7Hdn2l*k4M4MFEtq`q_#5?}c$b$pf_3y{Y!cRDafZBEj-*OD|gz#PBDeu3QoueOesLzB+O zxjf2wvf6Wwz>@AiOo2mO4=TkAV+g~%_n&R;)l#!cBxjuoD$aS-`IIJv7cdX%2{WT7 zOm%5rs(wqyPE^k5SIpUZ!&Lq4<~%{*>_Hu$2|~Xa;iX*tz8~G6O3uFOS?+)tWtdi| zV2b#;zRN!m@H&jd=!$7YY6_}|=!IU@=SjvGDFtL;aCtw06U;-v^0%k0FOyESt z1Wv$={b_H&8FiRV?MrzoHWd>%v6KTRU;-v^Miiz+@q`(BoT!+<37CKhoKb)|8!+RG z6BQFU^@fRW;s8!mOf2QViKQGk0TVER6EG1`#;Nm39Do^PoT!+<37AD!%oJe86(=et zZ~|sLzU>V-qYiU6V8$0GmU7_K8|Fd0B?+9Un1BhKAz#V~Fk^`mJtlCX#{^8^M8!me z8Yg;8-~>!e<-iG;h*0B1kBKm}hItVGY6WnjVpgnTTAC$rqQ^v)4KvOtpY|sIj@WYg zyw##ZZ5AC2IKNC;^hwg9BPk0wLStlmBr;E|$5GoAo$&Ui_;S9WY62n3)i49|T%C#i017z3J=$RF|KyZWnci*@lW4 z=AKhNN6+m`Q!V3Ye68|8y@%=am>YD0nG99M)NWc20%)gwO!96j7muR}Fr&54SxKP2 zP30S~lt=a*qDlbu3+Av57=9v&vr<6g0&`!8E2fq>I|EJGKs}t|{h7+KT@)LfIV-3K zK)r_fr2?}FFyn*MYoLC>oV-J~eavL2ho4a4^r{E-8m2hi>~hA?_vIG4a*KT;2eyl1 zh_hUvUJpNCFwBvRq5BI*srSle>c6%n`#VNsyC|MGa{(P&08p=C9+WUw9Hl<1o9T4M zdD=_C0F7#o8A_bRR?sFNmU0R6tW`ElnF8p53IdHo#S9(JoZCz}fHwJ6F<&?qrpVqE zte|m%89JQD+XwaPU#%#lVs-@-OL);|MdfINd6!XwP2h(eyafTUsoRkA%&@fe?9m@jw-v(yTTiV2(*fthQH9}SqmsRPVnwwbV$1E(_lkmo&S zF-truCU914_$jpqjr(>Ha4HkM4YMT>m~NosUu&UZ>zirfHo%N6PPs9^_o$WqPA0#5 z%tG>qFCL+b*0s?sZ;Sht0nE7Kl>OVXy=gjWxxK;OJ3yGd7-pZf7JYNcZo2*1SF`u6 zHJyRRxGw9mDlOiXqVMsNe#WX`fC`vrtjSQ%KmLcl(lC>ZOQzG^%iql2w-f_K@r?OE zwCICifM#L-HJyc7Gm>Ern?+Sk3&|Khmu4(~3qa$(m6Ub^U0E5RHq49za|XklN#?kP zl;EstdW?(_4D>kwjWy2f!LM)y?F94kyU3`W!6+AyId-89v}sXJpuic^NLL7GJItl~ zsiuB98AI-(#Mnm|=A-R6&2fwJ0JVSY#Q>&3$zFh|@;#%0qeF=j5Ajq@4i0tIIW z&}sk$&fGwoJpe&u-JeGLi^r?dO`m=y(QO{@h zQqAC7$rvz&5+mo3IqE?h=a~6m>%r5Quapvzq;{y~p zJpyXOBgD9VrW7@#p6l7O?o3feml(DtSL>D^R) zZUY%T2b0-vBAFN7VB;M88!~HuOXi4KcI6aRQ&h|XQ0A?m%j2=l1f0cGP}h(oVfJ`N zz#PpmFC*ieab)zJK<4?^k=g%OjPnkANzbAbmGZHoVRk*mTfm75s_cWVa`l*f$B@xu z5E*?&@seIo#*Y~1rBm!7sF9~~u6Wrj5oICUOuz}CS)jdNIznfzCA(stJ(7$c^e5wN z?lt>eYgbA!kvAR7zYSD&*r1$b|(@;9dcZ^67R0 zXAXJKa|5Sdmj!g578Nwt6d$sXuc&MWezA0Whd`94$h{{?1IwXP4)Tx4obDK%xoFZ_Z zjjHJ_P@R_e5blG@yEjnaJb`l;s%Lb2&=8$&Ct-fV`E^4CUs)=jTk!I}2d&n!f@)bm z@ z_4Dc86+3l2*p|~;o-Sb~oXb_RuLmoifDU^&Te$*FevycC0*nE3Xws8gsWp|Rj2>SM zns)qcYj?^2sd8?N!_w~4v+f-HCF|a$TNZDoNl$I1Uq87euoNgKb6&r26TNrfkUa@o zfdiFA@p{K&mH3b8i!lcoz)V{n8Q@g(vR4ns4r6w;K z>1~ecQR0-<^J|Ndg5fvVUM9g;lbu-){#ghGw(fg>L zh)T5Ljb%lWE;V9L!;Cqk>AV1(rULYF07ZBJbGb9qbSoLAd;in9{)95YqX$J43-dY7YU*k~vrM25 zxh5_IqO0LYZW%oxQ5HOzmk4x{atE*vipUk}sh88$b2tn?!ujEHn`tQLe&vo}nMb&{ zio`xzZ&GG6&ZyN3jnaQy#iVqXE9VT(3tWY$n-)uWDQ|tc{`?fq2F`oQ{;d3aWPg4Hp-(iE{ry>MIPWL> iW8Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/file_selector/file_selector_ios/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/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/file_selector/file_selector_ios/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/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 000000000000..89c2725b70f1 --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/file_selector/file_selector_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000000..f2e259c7c939 --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner/Base.lproj/Main.storyboard b/packages/file_selector/file_selector_ios/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner/Info.plist b/packages/file_selector/file_selector_ios/example/ios/Runner/Info.plist new file mode 100644 index 000000000000..2bf6e923d3b6 --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/ios/Runner/Info.plist @@ -0,0 +1,51 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + File Selector Ios + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + file_selector_ios_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 + + UIViewControllerBasedStatusBarAppearance + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/packages/file_selector/file_selector_ios/example/ios/Runner/Runner-Bridging-Header.h b/packages/file_selector/file_selector_ios/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 000000000000..eb7e8ba8052f --- /dev/null +++ b/packages/file_selector/file_selector_ios/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/file_selector/file_selector_ios/example/ios/RunnerTests/FileSelectorTests.m b/packages/file_selector/file_selector_ios/example/ios/RunnerTests/FileSelectorTests.m new file mode 100644 index 000000000000..a32622a6afef --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/ios/RunnerTests/FileSelectorTests.m @@ -0,0 +1,98 @@ +// 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 file_selector_ios; +@import file_selector_ios.Test; +@import XCTest; + +#import + +@interface FileSelectorTests : XCTestCase + +@end + +@implementation FileSelectorTests + +- (void)testPickerPresents { + FFSFileSelectorPlugin *plugin = [[FFSFileSelectorPlugin alloc] init]; + UIDocumentPickerViewController *picker = + [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[] + inMode:UIDocumentPickerModeImport]; + id mockPresentingVC = OCMClassMock([UIViewController class]); + plugin.documentPickerViewControllerOverride = picker; + plugin.presentingViewControllerOverride = mockPresentingVC; + + [plugin openFileSelectorWithConfig:[FFSFileSelectorConfig makeWithUtis:@[] + allowMultiSelection:@NO] + completion:^(NSArray *paths, FlutterError *error){ + }]; + + XCTAssertEqualObjects(picker.delegate, plugin); + OCMVerify(times(1), [mockPresentingVC presentViewController:picker + animated:[OCMArg any] + completion:[OCMArg any]]); +} + +- (void)testReturnsPickedFiles { + FFSFileSelectorPlugin *plugin = [[FFSFileSelectorPlugin alloc] init]; + XCTestExpectation *completionWasCalled = [self expectationWithDescription:@"completion"]; + UIDocumentPickerViewController *picker = + [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[] + inMode:UIDocumentPickerModeImport]; + plugin.documentPickerViewControllerOverride = picker; + [plugin openFileSelectorWithConfig:[FFSFileSelectorConfig makeWithUtis:@[] + allowMultiSelection:@YES] + completion:^(NSArray *paths, FlutterError *error) { + NSArray *expectedPaths = @[ @"/file1.txt", @"/file2.txt" ]; + XCTAssertEqualObjects(paths, expectedPaths); + [completionWasCalled fulfill]; + }]; + [plugin documentPicker:picker + didPickDocumentsAtURLs:@[ + [NSURL URLWithString:@"file:///file1.txt"], [NSURL URLWithString:@"file:///file2.txt"] + ]]; + [self waitForExpectationsWithTimeout:1.0 handler:nil]; +} + +- (void)testReturnsPickedFileLegacy { + // Tests that it handles the pre iOS 11 UIDocumentPickerDelegate method. + FFSFileSelectorPlugin *plugin = [[FFSFileSelectorPlugin alloc] init]; + XCTestExpectation *completionWasCalled = [self expectationWithDescription:@"completion"]; + UIDocumentPickerViewController *picker = + [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[] + inMode:UIDocumentPickerModeImport]; + plugin.documentPickerViewControllerOverride = picker; + [plugin openFileSelectorWithConfig:[FFSFileSelectorConfig makeWithUtis:@[] + allowMultiSelection:@NO] + completion:^(NSArray *paths, FlutterError *error) { + NSArray *expectedPaths = @[ @"/file1.txt" ]; + XCTAssertEqualObjects(paths, expectedPaths); + [completionWasCalled fulfill]; + }]; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + [plugin documentPicker:picker didPickDocumentAtURL:[NSURL URLWithString:@"file:///file1.txt"]]; +#pragma GCC diagnostic pop + [self waitForExpectationsWithTimeout:1.0 handler:nil]; +} + +- (void)testCancellingPickerReturnsNil { + FFSFileSelectorPlugin *plugin = [[FFSFileSelectorPlugin alloc] init]; + UIDocumentPickerViewController *picker = + [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[] + inMode:UIDocumentPickerModeImport]; + plugin.documentPickerViewControllerOverride = picker; + + XCTestExpectation *completionWasCalled = [self expectationWithDescription:@"completion"]; + [plugin openFileSelectorWithConfig:[FFSFileSelectorConfig makeWithUtis:@[] + allowMultiSelection:@NO] + completion:^(NSArray *paths, FlutterError *error) { + XCTAssertEqual(paths.count, 0); + [completionWasCalled fulfill]; + }]; + [plugin documentPickerWasCancelled:picker]; + [self waitForExpectationsWithTimeout:1.0 handler:nil]; +} + +@end diff --git a/packages/file_selector/file_selector_ios/example/lib/home_page.dart b/packages/file_selector/file_selector_ios/example/lib/home_page.dart new file mode 100644 index 000000000000..a4b2ae1f63ea --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/lib/home_page.dart @@ -0,0 +1,63 @@ +// 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'; + +/// Home Page of the application. +class HomePage extends StatelessWidget { + /// Default Constructor + const HomePage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final ButtonStyle style = ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use + primary: Colors.blue, + // ignore: deprecated_member_use + onPrimary: Colors.white, + ); + return Scaffold( + appBar: AppBar( + title: const Text('File Selector Demo Home Page'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: style, + child: const Text('Open a text file'), + onPressed: () => Navigator.pushNamed(context, '/open/text'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open an image'), + onPressed: () => Navigator.pushNamed(context, '/open/image'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open multiple images'), + onPressed: () => Navigator.pushNamed(context, '/open/images'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Save a file'), + onPressed: () => Navigator.pushNamed(context, '/save/text'), + ), + const SizedBox(height: 10), + ElevatedButton( + style: style, + child: const Text('Open a get directory dialog'), + onPressed: () => Navigator.pushNamed(context, '/directory'), + ), + ], + ), + ), + ); + } +} diff --git a/packages/file_selector/file_selector_ios/example/lib/main.dart b/packages/file_selector/file_selector_ios/example/lib/main.dart new file mode 100644 index 000000000000..929c48fb9037 --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/lib/main.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/material.dart'; + +import 'home_page.dart'; +import 'open_image_page.dart'; +import 'open_multiple_images_page.dart'; +import 'open_text_page.dart'; + +void main() { + runApp(const MyApp()); +} + +/// MyApp is the Main Application. +class MyApp extends StatelessWidget { + /// Default Constructor + const MyApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'File Selector Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: const HomePage(), + routes: { + '/open/image': (BuildContext context) => const OpenImagePage(), + '/open/images': (BuildContext context) => + const OpenMultipleImagesPage(), + '/open/text': (BuildContext context) => const OpenTextPage(), + }, + ); + } +} diff --git a/packages/file_selector/file_selector_ios/example/lib/open_image_page.dart b/packages/file_selector/file_selector_ios/example/lib/open_image_page.dart new file mode 100644 index 000000000000..fd0f4e711d7f --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/lib/open_image_page.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 'dart:io'; + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select an image file using +/// `openFiles`, then displays the selected images in a gallery dialog. +class OpenImagePage extends StatelessWidget { + /// Default Constructor + const OpenImagePage({Key? key}) : super(key: key); + + Future _openImageFile(BuildContext context) async { + final XTypeGroup typeGroup = XTypeGroup( + label: 'images', + extensions: ['jpg', 'png'], + macUTIs: ['public.image'], + ); + final XFile? file = await FileSelectorPlatform.instance + .openFile(acceptedTypeGroups: [typeGroup]); + if (file == null) { + // Operation was canceled by the user. + return; + } + final String fileName = file.name; + final String filePath = file.path; + + await showDialog( + context: context, + builder: (BuildContext context) => ImageDisplay(fileName, filePath), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open an image'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use + primary: Colors.blue, + // ignore: deprecated_member_use + onPrimary: Colors.white, + ), + child: const Text('Press to open an image file(png, jpg)'), + onPressed: () => _openImageFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays an image in a dialog. +class ImageDisplay extends StatelessWidget { + /// Default Constructor. + const ImageDisplay(this.fileName, this.filePath, {Key? key}) + : super(key: key); + + /// The name of the selected file. + final String fileName; + + /// The path to the selected file. + final String filePath; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(fileName), + // On web the filePath is a blob url + // while on other platforms it is a system path. + content: kIsWeb ? Image.network(filePath) : Image.file(File(filePath)), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_ios/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_ios/example/lib/open_multiple_images_page.dart new file mode 100644 index 000000000000..29b27c1d637c --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/lib/open_multiple_images_page.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 'dart:io'; + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select multiple image files using +/// `openFiles`, then displays the selected images in a gallery dialog. +class OpenMultipleImagesPage extends StatelessWidget { + /// Default Constructor + const OpenMultipleImagesPage({Key? key}) : super(key: key); + + Future _openImageFile(BuildContext context) async { + final XTypeGroup jpgsTypeGroup = XTypeGroup( + label: 'JPEGs', + extensions: ['jpg', 'jpeg'], + macUTIs: ['public.jpeg'], + ); + final XTypeGroup pngTypeGroup = XTypeGroup( + label: 'PNGs', + extensions: ['png'], + macUTIs: ['public.png'], + ); + final List files = await FileSelectorPlatform.instance + .openFiles(acceptedTypeGroups: [ + jpgsTypeGroup, + pngTypeGroup, + ]); + if (files.isEmpty) { + // Operation was canceled by the user. + return; + } + await showDialog( + context: context, + builder: (BuildContext context) => MultipleImagesDisplay(files), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open multiple images'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use + primary: Colors.blue, + // ignore: deprecated_member_use + onPrimary: Colors.white, + ), + child: const Text('Press to open multiple images (png, jpg)'), + onPressed: () => _openImageFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class MultipleImagesDisplay extends StatelessWidget { + /// Default Constructor. + const MultipleImagesDisplay(this.files, {Key? key}) : super(key: key); + + /// The files containing the images. + final List files; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Gallery'), + // On web the filePath is a blob url + // while on other platforms it is a system path. + content: Center( + child: Row( + children: [ + ...files.map( + (XFile file) => Flexible( + child: kIsWeb + ? Image.network(file.path) + : Image.file(File(file.path))), + ) + ], + ), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_ios/example/lib/open_text_page.dart b/packages/file_selector/file_selector_ios/example/lib/open_text_page.dart new file mode 100644 index 000000000000..b747aa89611c --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/lib/open_text_page.dart @@ -0,0 +1,92 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/material.dart'; + +/// Screen that allows the user to select a text file using `openFile`, then +/// displays its contents in a dialog. +class OpenTextPage extends StatelessWidget { + /// Default Constructor + const OpenTextPage({Key? key}) : super(key: key); + + Future _openTextFile(BuildContext context) async { + final XTypeGroup typeGroup = XTypeGroup( + label: 'text', + extensions: ['txt', 'json'], + macUTIs: ['public.text'], + ); + final XFile? file = await FileSelectorPlatform.instance + .openFile(acceptedTypeGroups: [typeGroup]); + if (file == null) { + // Operation was canceled by the user. + return; + } + final String fileName = file.name; + final String fileContent = await file.readAsString(); + + await showDialog( + context: context, + builder: (BuildContext context) => TextDisplay(fileName, fileContent), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Open a text file'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 + // ignore: deprecated_member_use + primary: Colors.blue, + // ignore: deprecated_member_use + onPrimary: Colors.white, + ), + child: const Text('Press to open a text file (json, txt)'), + onPressed: () => _openTextFile(context), + ), + ], + ), + ), + ); + } +} + +/// Widget that displays a text file in a dialog. +class TextDisplay extends StatelessWidget { + /// Default Constructor. + const TextDisplay(this.fileName, this.fileContent, {Key? key}) + : super(key: key); + + /// The name of the selected file. + final String fileName; + + /// The contents of the text file. + final String fileContent; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(fileName), + content: Scrollbar( + child: SingleChildScrollView( + child: Text(fileContent), + ), + ), + actions: [ + TextButton( + child: const Text('Close'), + onPressed: () => Navigator.pop(context), + ), + ], + ); + } +} diff --git a/packages/file_selector/file_selector_ios/example/pubspec.yaml b/packages/file_selector/file_selector_ios/example/pubspec.yaml new file mode 100644 index 000000000000..eae4a2b45492 --- /dev/null +++ b/packages/file_selector/file_selector_ios/example/pubspec.yaml @@ -0,0 +1,31 @@ +name: example +description: Example for file_selector_ios implementation. +publish_to: 'none' +version: 1.0.0 + +environment: + sdk: ">=2.14.4 <3.0.0" + +dependencies: + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + file_selector_ios: + # When depending on this package from a real application you should use: + # file_selector_ios: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # 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: .. + file_selector_platform_interface: ^2.0.0 + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + +flutter: + uses-material-design: true \ No newline at end of file diff --git a/packages/file_selector/file_selector_ios/example/test_driver/integration_test.dart b/packages/file_selector/file_selector_ios/example/test_driver/integration_test.dart new file mode 100644 index 000000000000..4f10f2a522f3 --- /dev/null +++ b/packages/file_selector/file_selector_ios/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/file_selector/file_selector_ios/ios/.gitignore b/packages/file_selector/file_selector_ios/ios/.gitignore new file mode 100644 index 000000000000..0c885071e36b --- /dev/null +++ b/packages/file_selector/file_selector_ios/ios/.gitignore @@ -0,0 +1,38 @@ +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +.DS_Store +*.swp +profile + +DerivedData/ +build/ +GeneratedPluginRegistrant.h +GeneratedPluginRegistrant.m + +.generated/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + +/Flutter/Generated.xcconfig +/Flutter/ephemeral/ +/Flutter/flutter_export_environment.sh \ No newline at end of file diff --git a/packages/file_selector/file_selector_ios/ios/Assets/.gitkeep b/packages/file_selector/file_selector_ios/ios/Assets/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/file_selector/file_selector_ios/ios/Classes/FFSFileSelectorPlugin.h b/packages/file_selector/file_selector_ios/ios/Classes/FFSFileSelectorPlugin.h new file mode 100644 index 000000000000..ca7ca56f3bd4 --- /dev/null +++ b/packages/file_selector/file_selector_ios/ios/Classes/FFSFileSelectorPlugin.h @@ -0,0 +1,8 @@ +// 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 + +@interface FFSFileSelectorPlugin : NSObject +@end diff --git a/packages/file_selector/file_selector_ios/ios/Classes/FFSFileSelectorPlugin.m b/packages/file_selector/file_selector_ios/ios/Classes/FFSFileSelectorPlugin.m new file mode 100644 index 000000000000..e77585ad3a17 --- /dev/null +++ b/packages/file_selector/file_selector_ios/ios/Classes/FFSFileSelectorPlugin.m @@ -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 "FFSFileSelectorPlugin.h" +#import "FFSFileSelectorPlugin_Test.h" +#import "messages.g.h" + +#import + +@implementation FFSFileSelectorPlugin + +#pragma mark - FFSFileSelectorApi + +- (void)openFileSelectorWithConfig:(FFSFileSelectorConfig *)config + completion:(void (^)(NSArray *_Nullable, + FlutterError *_Nullable))completion { + UIDocumentPickerViewController *documentPicker = + self.documentPickerViewControllerOverride + ?: [[UIDocumentPickerViewController alloc] + initWithDocumentTypes:config.utis + inMode:UIDocumentPickerModeImport]; + documentPicker.delegate = self; + if (@available(iOS 11.0, *)) { + documentPicker.allowsMultipleSelection = config.allowMultiSelection.boolValue; + } + + UIViewController *presentingVC = + self.presentingViewControllerOverride + ?: UIApplication.sharedApplication.delegate.window.rootViewController; + if (presentingVC) { + objc_setAssociatedObject(documentPicker, @selector(openFileSelectorWithConfig:completion:), + completion, OBJC_ASSOCIATION_COPY_NONATOMIC); + [presentingVC presentViewController:documentPicker animated:YES completion:nil]; + } else { + completion(nil, [FlutterError errorWithCode:@"error" + message:@"Missing root view controller." + details:nil]); + } +} + +#pragma mark - FlutterPlugin + ++ (void)registerWithRegistrar:(NSObject *)registrar { + FFSFileSelectorPlugin *plugin = [[FFSFileSelectorPlugin alloc] init]; + FFSFileSelectorApiSetup(registrar.messenger, plugin); +} + +#pragma mark - UIDocumentPickerDelegate + +// This method is only called in iOS < 11.0. The new codepath is +// documentPicker:didPickDocumentsAtURLs:, implemented below. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" +- (void)documentPicker:(UIDocumentPickerViewController *)controller + didPickDocumentAtURL:(NSURL *)url { + [self sendBackResults:@[ url.path ] error:nil forPicker:controller]; +} +#pragma clang diagnostic pop + +- (void)documentPicker:(UIDocumentPickerViewController *)controller + didPickDocumentsAtURLs:(NSArray *)urls { + NSMutableArray *paths = [NSMutableArray arrayWithCapacity:urls.count]; + for (NSURL *url in urls) { + [paths addObject:url.path]; + }; + [self sendBackResults:paths error:nil forPicker:controller]; +} + +- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller { + [self sendBackResults:@[] error:nil forPicker:controller]; +} + +#pragma mark - Helper Methods + +- (void)sendBackResults:(NSArray *)results + error:(FlutterError *)error + forPicker:(UIDocumentPickerViewController *)picker { + void (^completionBlock)(NSArray *, FlutterError *) = + objc_getAssociatedObject(picker, @selector(openFileSelectorWithConfig:completion:)); + if (completionBlock) { + completionBlock(results, error); + objc_setAssociatedObject(picker, @selector(openFileSelectorWithConfig:completion:), nil, + OBJC_ASSOCIATION_ASSIGN); + } +} + +@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 new file mode 100644 index 000000000000..f71a8ae109e6 --- /dev/null +++ b/packages/file_selector/file_selector_ios/ios/Classes/FFSFileSelectorPlugin_Test.h @@ -0,0 +1,22 @@ +// 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 "FFSFileSelectorPlugin.h" + +#import "messages.g.h" + +// 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. + */ +@property(nonatomic) UIViewController *_Nullable presentingViewControllerOverride; + +/** + * Overrides the UIDocumentPickerViewController used for file picking. + */ +@property(nonatomic) UIDocumentPickerViewController *_Nullable documentPickerViewControllerOverride; + +@end diff --git a/packages/file_selector/file_selector_ios/ios/Classes/FileSelectorPlugin.modulemap b/packages/file_selector/file_selector_ios/ios/Classes/FileSelectorPlugin.modulemap new file mode 100644 index 000000000000..4ff40260ffb3 --- /dev/null +++ b/packages/file_selector/file_selector_ios/ios/Classes/FileSelectorPlugin.modulemap @@ -0,0 +1,10 @@ +framework module file_selector_ios { + umbrella header "file_selector_ios-umbrella.h" + + export * + module * { export * } + + explicit module Test { + header "FFSFileSelectorPlugin_Test.h" + } +} diff --git a/packages/file_selector/file_selector_ios/ios/Classes/file_selector_ios-umbrella.h b/packages/file_selector/file_selector_ios/ios/Classes/file_selector_ios-umbrella.h new file mode 100644 index 000000000000..d79d3642b3e8 --- /dev/null +++ b/packages/file_selector/file_selector_ios/ios/Classes/file_selector_ios-umbrella.h @@ -0,0 +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. + +#import +#import diff --git a/packages/file_selector/file_selector_ios/ios/Classes/messages.g.h b/packages/file_selector/file_selector_ios/ios/Classes/messages.g.h new file mode 100644 index 000000000000..a04b41129a73 --- /dev/null +++ b/packages/file_selector/file_selector_ios/ios/Classes/messages.g.h @@ -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. +// Autogenerated from Pigeon (v3.2.5), do not edit directly. +// See also: https://pub.dev/packages/pigeon +#import +@protocol FlutterBinaryMessenger; +@protocol FlutterMessageCodec; +@class FlutterError; +@class FlutterStandardTypedData; + +NS_ASSUME_NONNULL_BEGIN + +@class FFSFileSelectorConfig; + +@interface FFSFileSelectorConfig : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithUtis:(NSArray *)utis + allowMultiSelection:(NSNumber *)allowMultiSelection; +@property(nonatomic, strong) NSArray *utis; +@property(nonatomic, strong) NSNumber *allowMultiSelection; +@end + +/// The codec used by FFSFileSelectorApi. +NSObject *FFSFileSelectorApiGetCodec(void); + +@protocol FFSFileSelectorApi +- (void)openFileSelectorWithConfig:(FFSFileSelectorConfig *)config + completion:(void (^)(NSArray *_Nullable, + FlutterError *_Nullable))completion; +@end + +extern void FFSFileSelectorApiSetup(id binaryMessenger, + NSObject *_Nullable api); + +NS_ASSUME_NONNULL_END diff --git a/packages/file_selector/file_selector_ios/ios/Classes/messages.g.m b/packages/file_selector/file_selector_ios/ios/Classes/messages.g.m new file mode 100644 index 000000000000..d4046d281293 --- /dev/null +++ b/packages/file_selector/file_selector_ios/ios/Classes/messages.g.m @@ -0,0 +1,143 @@ +// 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 (v3.2.5), do not edit directly. +// See also: https://pub.dev/packages/pigeon +#import "messages.g.h" +#import + +#if !__has_feature(objc_arc) +#error File requires ARC to be enabled. +#endif + +static NSDictionary *wrapResult(id result, FlutterError *error) { + NSDictionary *errorDict = (NSDictionary *)[NSNull null]; + if (error) { + errorDict = @{ + @"code" : (error.code ?: [NSNull null]), + @"message" : (error.message ?: [NSNull null]), + @"details" : (error.details ?: [NSNull null]), + }; + } + return @{ + @"result" : (result ?: [NSNull null]), + @"error" : errorDict, + }; +} +static id GetNullableObject(NSDictionary *dict, id key) { + id result = dict[key]; + return (result == [NSNull null]) ? nil : result; +} +static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { + id result = array[key]; + return (result == [NSNull null]) ? nil : result; +} + +@interface FFSFileSelectorConfig () ++ (FFSFileSelectorConfig *)fromMap:(NSDictionary *)dict; ++ (nullable FFSFileSelectorConfig *)nullableFromMap:(NSDictionary *)dict; +- (NSDictionary *)toMap; +@end + +@implementation FFSFileSelectorConfig ++ (instancetype)makeWithUtis:(NSArray *)utis + allowMultiSelection:(NSNumber *)allowMultiSelection { + FFSFileSelectorConfig *pigeonResult = [[FFSFileSelectorConfig alloc] init]; + pigeonResult.utis = utis; + pigeonResult.allowMultiSelection = allowMultiSelection; + return pigeonResult; +} ++ (FFSFileSelectorConfig *)fromMap:(NSDictionary *)dict { + FFSFileSelectorConfig *pigeonResult = [[FFSFileSelectorConfig alloc] init]; + pigeonResult.utis = GetNullableObject(dict, @"utis"); + NSAssert(pigeonResult.utis != nil, @""); + pigeonResult.allowMultiSelection = GetNullableObject(dict, @"allowMultiSelection"); + NSAssert(pigeonResult.allowMultiSelection != nil, @""); + return pigeonResult; +} ++ (nullable FFSFileSelectorConfig *)nullableFromMap:(NSDictionary *)dict { + return (dict) ? [FFSFileSelectorConfig fromMap:dict] : nil; +} +- (NSDictionary *)toMap { + return @{ + @"utis" : (self.utis ?: [NSNull null]), + @"allowMultiSelection" : (self.allowMultiSelection ?: [NSNull null]), + }; +} +@end + +@interface FFSFileSelectorApiCodecReader : FlutterStandardReader +@end +@implementation FFSFileSelectorApiCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [FFSFileSelectorConfig fromMap:[self readValue]]; + + default: + return [super readValueOfType:type]; + } +} +@end + +@interface FFSFileSelectorApiCodecWriter : FlutterStandardWriter +@end +@implementation FFSFileSelectorApiCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[FFSFileSelectorConfig class]]) { + [self writeByte:128]; + [self writeValue:[value toMap]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface FFSFileSelectorApiCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation FFSFileSelectorApiCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[FFSFileSelectorApiCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[FFSFileSelectorApiCodecReader alloc] initWithData:data]; +} +@end + +NSObject *FFSFileSelectorApiGetCodec() { + static dispatch_once_t sPred = 0; + static FlutterStandardMessageCodec *sSharedObject = nil; + dispatch_once(&sPred, ^{ + FFSFileSelectorApiCodecReaderWriter *readerWriter = + [[FFSFileSelectorApiCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void FFSFileSelectorApiSetup(id binaryMessenger, + NSObject *api) { + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.FileSelectorApi.openFile" + binaryMessenger:binaryMessenger + codec:FFSFileSelectorApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(openFileSelectorWithConfig:completion:)], + @"FFSFileSelectorApi api (%@) doesn't respond to " + @"@selector(openFileSelectorWithConfig:completion:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FFSFileSelectorConfig *arg_config = GetNullableObjectAtIndex(args, 0); + [api openFileSelectorWithConfig:arg_config + completion:^(NSArray *_Nullable output, + FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} diff --git a/packages/file_selector/file_selector_ios/ios/file_selector_ios.podspec b/packages/file_selector/file_selector_ios/ios/file_selector_ios.podspec new file mode 100644 index 000000000000..bb96b3c72917 --- /dev/null +++ b/packages/file_selector/file_selector_ios/ios/file_selector_ios.podspec @@ -0,0 +1,24 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint file_selector_ios.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'file_selector_ios' + s.version = '0.0.1' + s.summary = 'iOS implementation of file_selector.' + s.description = <<-DESC +Displays the native iOS document picker. + DESC + s.homepage = 'https://github.com/flutter/plugins/tree/main/packages/file_selector' + s.license = { :type => 'BSD', :file => '../LICENSE' } + s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_ios' } + s.source_files = 'Classes/**/*.{h,m}' + s.module_map = 'Classes/FileSelectorPlugin.modulemap' + s.dependency 'Flutter' + s.platform = :ios, '9.0' + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } + s.swift_version = '5.0' +end diff --git a/packages/file_selector/file_selector_ios/lib/file_selector_ios.dart b/packages/file_selector/file_selector_ios/lib/file_selector_ios.dart new file mode 100644 index 000000000000..e75f67e4f1bd --- /dev/null +++ b/packages/file_selector/file_selector_ios/lib/file_selector_ios.dart @@ -0,0 +1,64 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; + +import 'src/messages.g.dart'; + +/// An implementation of [FileSelectorPlatform] for iOS. +class FileSelectorIOS extends FileSelectorPlatform { + final FileSelectorApi _hostApi = FileSelectorApi(); + + /// Registers the iOS implementation. + static void registerWith() { + FileSelectorPlatform.instance = FileSelectorIOS(); + } + + @override + Future openFile({ + List? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText, + }) async { + final List path = (await _hostApi.openFile(FileSelectorConfig( + utis: _allowedUtiListFromTypeGroups(acceptedTypeGroups), + allowMultiSelection: false))) + .cast(); + return path.isEmpty ? null : XFile(path.first); + } + + @override + Future> openFiles({ + List? acceptedTypeGroups, + String? initialDirectory, + String? confirmButtonText, + }) async { + final List pathList = (await _hostApi.openFile(FileSelectorConfig( + utis: _allowedUtiListFromTypeGroups(acceptedTypeGroups), + allowMultiSelection: true))) + .cast(); + return pathList.map((String path) => XFile(path)).toList(); + } + + // Converts the type group list into a list of all allowed UTIs, since + // iOS doesn't support filter groups. + List _allowedUtiListFromTypeGroups(List? typeGroups) { + if (typeGroups == null || typeGroups.isEmpty) { + return []; + } + final List allowedUTIs = []; + for (final XTypeGroup typeGroup in typeGroups) { + // If any group allows everything, no filtering should be done. + if (typeGroup.allowsAny) { + return []; + } + if (typeGroup.macUTIs?.isEmpty ?? true) { + throw ArgumentError('The provided type group $typeGroup should either ' + 'allow all files, or have a non-empty "macUTIs"'); + } + allowedUTIs.addAll(typeGroup.macUTIs!); + } + return allowedUTIs; + } +} diff --git a/packages/file_selector/file_selector_ios/lib/src/messages.g.dart b/packages/file_selector/file_selector_ios/lib/src/messages.g.dart new file mode 100644 index 000000000000..42184740358e --- /dev/null +++ b/packages/file_selector/file_selector_ios/lib/src/messages.g.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. +// Autogenerated from Pigeon (v3.2.5), 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 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; + +class FileSelectorConfig { + FileSelectorConfig({ + required this.utis, + required this.allowMultiSelection, + }); + + List utis; + bool allowMultiSelection; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['utis'] = utis; + pigeonMap['allowMultiSelection'] = allowMultiSelection; + return pigeonMap; + } + + static FileSelectorConfig decode(Object message) { + final Map pigeonMap = message as Map; + return FileSelectorConfig( + utis: (pigeonMap['utis'] as List?)!.cast(), + allowMultiSelection: pigeonMap['allowMultiSelection']! as bool, + ); + } +} + +class _FileSelectorApiCodec extends StandardMessageCodec { + const _FileSelectorApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is FileSelectorConfig) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return FileSelectorConfig.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +class FileSelectorApi { + /// Constructor for [FileSelectorApi]. 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. + FileSelectorApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _FileSelectorApiCodec(); + + Future> openFile(FileSelectorConfig arg_config) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FileSelectorApi.openFile', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_config]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as List?)!.cast(); + } + } +} diff --git a/packages/file_selector/file_selector_ios/pigeons/copyright.txt b/packages/file_selector/file_selector_ios/pigeons/copyright.txt new file mode 100644 index 000000000000..fb682b1ab965 --- /dev/null +++ b/packages/file_selector/file_selector_ios/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/file_selector/file_selector_ios/pigeons/messages.dart b/packages/file_selector/file_selector_ios/pigeons/messages.dart new file mode 100644 index 000000000000..d0ea73cde111 --- /dev/null +++ b/packages/file_selector/file_selector_ios/pigeons/messages.dart @@ -0,0 +1,29 @@ +// 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'; + +@ConfigurePigeon(PigeonOptions( + dartOut: 'lib/src/messages.g.dart', + dartTestOut: 'test/test_api.dart', + objcHeaderOut: 'ios/Classes/messages.g.h', + objcSourceOut: 'ios/Classes/messages.g.m', + objcOptions: ObjcOptions( + prefix: 'FFS', + ), + copyrightHeader: 'pigeons/copyright.txt', +)) +class FileSelectorConfig { + FileSelectorConfig( + {this.utis = const [], this.allowMultiSelection = false}); + List utis; + bool allowMultiSelection; +} + +@HostApi(dartHostTestHandler: 'TestFileSelectorApi') +abstract class FileSelectorApi { + @async + @ObjCSelector('openFileSelectorWithConfig:') + List openFile(FileSelectorConfig config); +} diff --git a/packages/file_selector/file_selector_ios/pubspec.yaml b/packages/file_selector/file_selector_ios/pubspec.yaml new file mode 100644 index 000000000000..1e4056dbcadb --- /dev/null +++ b/packages/file_selector/file_selector_ios/pubspec.yaml @@ -0,0 +1,30 @@ +name: file_selector_ios +description: iOS implementation of the file_selector plugin. +repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_ios +issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 +version: 0.5.0 + +environment: + sdk: ">=2.14.4 <3.0.0" + flutter: ">=2.8.0" + +flutter: + plugin: + implements: file_selector + platforms: + ios: + dartPluginClass: FileSelectorIOS + pluginClass: FFSFileSelectorPlugin + +dependencies: + file_selector_platform_interface: ^2.1.0 + flutter: + sdk: flutter + +dev_dependencies: + build_runner: 2.1.11 + flutter_test: + sdk: flutter + mockito: ^5.1.0 + pigeon: ^3.2.5 + diff --git a/packages/file_selector/file_selector_ios/test/file_selector_ios_test.dart b/packages/file_selector/file_selector_ios/test/file_selector_ios_test.dart new file mode 100644 index 000000000000..f9ef40628991 --- /dev/null +++ b/packages/file_selector/file_selector_ios/test/file_selector_ios_test.dart @@ -0,0 +1,136 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file_selector_ios/file_selector_ios.dart'; +import 'package:file_selector_ios/src/messages.g.dart'; +import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'file_selector_ios_test.mocks.dart'; +import 'test_api.dart'; + +@GenerateMocks([TestFileSelectorApi]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + final FileSelectorIOS plugin = FileSelectorIOS(); + late MockTestFileSelectorApi mockApi; + + setUp(() { + mockApi = MockTestFileSelectorApi(); + TestFileSelectorApi.setup(mockApi); + }); + + test('registered instance', () { + FileSelectorIOS.registerWith(); + expect(FileSelectorPlatform.instance, isA()); + }); + + group('openFile', () { + setUp(() { + when(mockApi.openFile(any)).thenAnswer((_) async => ['foo']); + }); + + test('passes the accepted type groups correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + extensions: ['txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); + + final XTypeGroup groupTwo = XTypeGroup( + label: 'image', + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image'], + webWildCards: ['image/*']); + + await plugin.openFile(acceptedTypeGroups: [group, groupTwo]); + + final VerificationResult result = verify(mockApi.openFile(captureAny)); + final FileSelectorConfig config = + result.captured[0] as FileSelectorConfig; + + // iOS only accepts macUTIs. + expect(listEquals(config.utis, ['public.text', 'public.image']), + isTrue); + expect(config.allowMultiSelection, isFalse); + }); + test('throws for a type group that does not support iOS', () async { + final XTypeGroup group = XTypeGroup( + label: 'images', + webWildCards: ['images/*'], + ); + + await expectLater( + plugin.openFile(acceptedTypeGroups: [group]), + throwsArgumentError); + }); + + test('allows a wildcard group', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + ); + + await expectLater( + plugin.openFile(acceptedTypeGroups: [group]), completes); + }); + }); + + group('openFiles', () { + setUp(() { + when(mockApi.openFile(any)).thenAnswer((_) async => ['foo']); + }); + + test('passes the accepted type groups correctly', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + extensions: ['txt'], + mimeTypes: ['text/plain'], + macUTIs: ['public.text'], + ); + + final XTypeGroup groupTwo = XTypeGroup( + label: 'image', + extensions: ['jpg'], + mimeTypes: ['image/jpg'], + macUTIs: ['public.image'], + webWildCards: ['image/*']); + + await plugin.openFiles(acceptedTypeGroups: [group, groupTwo]); + + final VerificationResult result = verify(mockApi.openFile(captureAny)); + final FileSelectorConfig config = + result.captured[0] as FileSelectorConfig; + + // iOS only accepts macUTIs. + expect(listEquals(config.utis, ['public.text', 'public.image']), + isTrue); + expect(config.allowMultiSelection, isTrue); + }); + test('throws for a type group that does not support iOS', () async { + final XTypeGroup group = XTypeGroup( + label: 'images', + webWildCards: ['images/*'], + ); + + await expectLater( + plugin.openFiles(acceptedTypeGroups: [group]), + throwsArgumentError); + }); + + test('allows a wildcard group', () async { + final XTypeGroup group = XTypeGroup( + label: 'text', + ); + + await expectLater( + plugin.openFiles(acceptedTypeGroups: [group]), completes); + }); + }); +} diff --git a/packages/file_selector/file_selector_ios/test/file_selector_ios_test.mocks.dart b/packages/file_selector/file_selector_ios/test/file_selector_ios_test.mocks.dart new file mode 100644 index 000000000000..38c91b46f65e --- /dev/null +++ b/packages/file_selector/file_selector_ios/test/file_selector_ios_test.mocks.dart @@ -0,0 +1,38 @@ +// Mocks generated by Mockito 5.3.0 from annotations +// in file_selector_ios/example/ios/.symlinks/plugins/file_selector_ios/test/file_selector_ios_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i3; + +import 'package:file_selector_ios/src/messages.g.dart' as _i4; +import 'package:mockito/mockito.dart' as _i1; + +import 'test_api.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: 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 [TestFileSelectorApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestFileSelectorApi extends _i1.Mock + implements _i2.TestFileSelectorApi { + MockTestFileSelectorApi() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future> openFile(_i4.FileSelectorConfig? config) => + (super.noSuchMethod(Invocation.method(#openFile, [config]), + returnValue: _i3.Future>.value([])) + as _i3.Future>); +} diff --git a/packages/file_selector/file_selector_ios/test/test_api.dart b/packages/file_selector/file_selector_ios/test/test_api.dart new file mode 100644 index 000000000000..69f6c19d5a23 --- /dev/null +++ b/packages/file_selector/file_selector_ios/test/test_api.dart @@ -0,0 +1,70 @@ +// 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 (v3.2.5), 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 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +// This line has been hand-edited due to +// https://github.com/flutter/flutter/issues/97744 +// ignore: directives_ordering +import 'package:file_selector_ios/src/messages.g.dart'; + +class _TestFileSelectorApiCodec extends StandardMessageCodec { + const _TestFileSelectorApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is FileSelectorConfig) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return FileSelectorConfig.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestFileSelectorApi { + static const MessageCodec codec = _TestFileSelectorApiCodec(); + + Future> openFile(FileSelectorConfig config); + static void setup(TestFileSelectorApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.FileSelectorApi.openFile', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.FileSelectorApi.openFile was null.'); + final List args = (message as List?)!; + final FileSelectorConfig? arg_config = + (args[0] as FileSelectorConfig?); + assert(arg_config != null, + 'Argument for dev.flutter.pigeon.FileSelectorApi.openFile was null, expected non-null FileSelectorConfig.'); + final List output = await api.openFile(arg_config!); + return {'result': output}; + }); + } + } + } +} diff --git a/script/configs/exclude_integration_ios.yaml b/script/configs/exclude_integration_ios.yaml index 06283ae59c16..3df22b9bb2de 100644 --- a/script/configs/exclude_integration_ios.yaml +++ b/script/configs/exclude_integration_ios.yaml @@ -2,3 +2,5 @@ - in_app_purchase_storekit # Currently missing: https://github.com/flutter/flutter/issues/82208 - ios_platform_images +# Can't use Flutter integration tests due to native modal UI. +- file_selector_ios \ No newline at end of file From dfdc38482e70fe8afc0bba2217648f15f67173d4 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 22 Aug 2022 15:50:49 -0400 Subject: [PATCH 643/844] [various] Remove stale NNBD opt-out tags (#6302) --- .../example/integration_test/google_sign_in_test.dart | 2 -- .../google_sign_in/example/test_driver/integration_test.dart | 2 -- .../example/integration_test/quick_actions_test.dart | 3 +-- .../quick_actions/example/test_driver/integration_test.dart | 2 -- 4 files changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/google_sign_in/google_sign_in/example/integration_test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in/example/integration_test/google_sign_in_test.dart index be3cc89674a3..54e454c28f4a 100644 --- a/packages/google_sign_in/google_sign_in/example/integration_test/google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in/example/integration_test/google_sign_in_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.9 - import 'package:flutter_test/flutter_test.dart'; import 'package:google_sign_in/google_sign_in.dart'; import 'package:integration_test/integration_test.dart'; diff --git a/packages/google_sign_in/google_sign_in/example/test_driver/integration_test.dart b/packages/google_sign_in/google_sign_in/example/test_driver/integration_test.dart index 6a0e6fa82dbe..4f10f2a522f3 100644 --- a/packages/google_sign_in/google_sign_in/example/test_driver/integration_test.dart +++ b/packages/google_sign_in/google_sign_in/example/test_driver/integration_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart=2.9 - import 'package:integration_test/integration_test_driver.dart'; Future main() => integrationDriver(); diff --git a/packages/quick_actions/quick_actions/example/integration_test/quick_actions_test.dart b/packages/quick_actions/quick_actions/example/integration_test/quick_actions_test.dart index bfefef3b298b..37846c323591 100644 --- a/packages/quick_actions/quick_actions/example/integration_test/quick_actions_test.dart +++ b/packages/quick_actions/quick_actions/example/integration_test/quick_actions_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. -// @dart = 2.9 import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:quick_actions/quick_actions.dart'; @@ -12,7 +11,7 @@ void main() { testWidgets('Can set shortcuts', (WidgetTester tester) async { const QuickActions quickActions = QuickActions(); - await quickActions.initialize(null); + await quickActions.initialize((String _) {}); const ShortcutItem shortCutItem = ShortcutItem( type: 'action_one', diff --git a/packages/quick_actions/quick_actions/example/test_driver/integration_test.dart b/packages/quick_actions/quick_actions/example/test_driver/integration_test.dart index 6a0e6fa82dbe..4f10f2a522f3 100644 --- a/packages/quick_actions/quick_actions/example/test_driver/integration_test.dart +++ b/packages/quick_actions/quick_actions/example/test_driver/integration_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart=2.9 - import 'package:integration_test/integration_test_driver.dart'; Future main() => integrationDriver(); From 3a5e6f3acdd386983899734a1918e962947f8646 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 23 Aug 2022 09:44:09 -0400 Subject: [PATCH 644/844] [webview_flutter_wkwebview] Implementation of PlatformWebViewCookieManager for WebKit (#6240) --- .../lib/src/v4/src/webkit_proxy.dart | 9 ++ .../v4/src/webkit_webview_cookie_manager.dart | 89 ++++++++++++++ .../src/v4/src/webkit_webview_platform.dart | 8 ++ .../lib/src/v4/webview_flutter_wkwebview.dart | 1 + .../webkit_webview_controller_test.mocks.dart | 2 +- .../webkit_webview_cookie_manager_test.dart | 112 ++++++++++++++++++ ...kit_webview_cookie_manager_test.mocks.dart | 100 ++++++++++++++++ 7 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_cookie_manager.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_cookie_manager_test.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_cookie_manager_test.mocks.dart diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_proxy.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_proxy.dart index 013eae0516cd..10f922efd0d5 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_proxy.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_proxy.dart @@ -5,6 +5,11 @@ import '../../foundation/foundation.dart'; import '../../web_kit/web_kit.dart'; +// This convenience method was added because Dart doesn't support constant +// function literals: https://github.com/dart-lang/language/issues/1048. +WKWebsiteDataStore _defaultWebsiteDataStore() => + WKWebsiteDataStore.defaultDataStore; + /// Handles constructing objects and calling static methods for the WebKit /// native library. /// @@ -20,6 +25,7 @@ class WebKitProxy { this.createWebView = WKWebView.new, this.createWebViewConfiguration = WKWebViewConfiguration.new, this.createScriptMessageHandler = WKScriptMessageHandler.new, + this.defaultWebsiteDataStore = _defaultWebsiteDataStore, this.createNavigationDelegate = WKNavigationDelegate.new, }); @@ -46,6 +52,9 @@ class WebKitProxy { didReceiveScriptMessage, }) createScriptMessageHandler; + /// The default [WKWebsiteDataStore]. + final WKWebsiteDataStore Function() defaultWebsiteDataStore; + /// Constructs a [WKNavigationDelegate]. final WKNavigationDelegate Function({ void Function(WKWebView webView, String? url)? didFinishNavigation, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_cookie_manager.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_cookie_manager.dart new file mode 100644 index 000000000000..423b3bdb7f4e --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_cookie_manager.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 'package:flutter/foundation.dart'; +import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; + +import '../../foundation/foundation.dart'; +import '../../web_kit/web_kit.dart'; +import 'webkit_proxy.dart'; + +/// Object specifying creation parameters for a [WebKitWebViewCookieManager]. +class WebKitWebViewCookieManagerCreationParams + extends PlatformWebViewCookieManagerCreationParams { + /// Constructs a [WebKitWebViewCookieManagerCreationParams]. + WebKitWebViewCookieManagerCreationParams({ + WebKitProxy? webKitProxy, + }) : webKitProxy = webKitProxy ?? const WebKitProxy(); + + /// Constructs a [WebKitWebViewCookieManagerCreationParams] using a + /// [PlatformWebViewCookieManagerCreationParams]. + WebKitWebViewCookieManagerCreationParams.fromPlatformWebViewCookieManagerCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformWebViewCookieManagerCreationParams params, { + @visibleForTesting WebKitProxy? webKitProxy, + }) : this(webKitProxy: webKitProxy); + + /// Handles constructing objects and calling static methods for the WebKit + /// native library. + @visibleForTesting + final WebKitProxy webKitProxy; + + /// Manages stored data for [WKWebView]s. + late final WKWebsiteDataStore _websiteDataStore = + webKitProxy.defaultWebsiteDataStore(); +} + +/// An implementation of [PlatformWebViewCookieManager] with the WebKit api. +class WebKitWebViewCookieManager extends PlatformWebViewCookieManager { + /// Constructs a [WebKitWebViewCookieManager]. + WebKitWebViewCookieManager(PlatformWebViewCookieManagerCreationParams params) + : super.implementation( + params is WebKitWebViewCookieManagerCreationParams + ? params + : WebKitWebViewCookieManagerCreationParams + .fromPlatformWebViewCookieManagerCreationParams(params), + ); + + WebKitWebViewCookieManagerCreationParams get _webkitParams => + params as WebKitWebViewCookieManagerCreationParams; + + @override + Future clearCookies() { + return _webkitParams._websiteDataStore.removeDataOfTypes( + {WKWebsiteDataType.cookies}, + DateTime.fromMillisecondsSinceEpoch(0), + ); + } + + @override + Future setCookie(WebViewCookie cookie) { + if (!_isValidPath(cookie.path)) { + throw ArgumentError( + 'The path property for the provided cookie was not given a legal value.', + ); + } + + return _webkitParams._websiteDataStore.httpCookieStore.setCookie( + NSHttpCookie.withProperties( + { + NSHttpCookiePropertyKey.name: cookie.name, + NSHttpCookiePropertyKey.value: cookie.value, + NSHttpCookiePropertyKey.domain: cookie.domain, + NSHttpCookiePropertyKey.path: cookie.path, + }, + ), + ); + } + + bool _isValidPath(String path) { + // Permitted ranges based on RFC6265bis: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1 + return !path.codeUnits.any( + (int char) { + return (char < 0x20 || char > 0x3A) && (char < 0x3C || char > 0x7E); + }, + ); + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_platform.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_platform.dart index 6ffe386a5e28..aec446921d92 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_platform.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_platform.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; +import 'package:webview_flutter_wkwebview/src/v4/src/webkit_webview_cookie_manager.dart'; import 'webkit_navigation_delegate.dart'; import 'webkit_webview_controller.dart'; @@ -22,4 +23,11 @@ class WebKitWebViewPlatform extends WebViewPlatform { ) { return WebKitNavigationDelegate(params); } + + @override + WebKitWebViewCookieManager createPlatformCookieManager( + PlatformWebViewCookieManagerCreationParams params, + ) { + return WebKitWebViewCookieManager(params); + } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/webview_flutter_wkwebview.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/webview_flutter_wkwebview.dart index 6c5cd93f11d7..91180c9234ab 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/webview_flutter_wkwebview.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/webview_flutter_wkwebview.dart @@ -7,3 +7,4 @@ library webview_flutter_wkwebview; export 'src/webkit_navigation_delegate.dart'; export 'src/webkit_webview_controller.dart'; export 'src/webkit_webview_controller.dart'; +export 'src/webkit_webview_cookie_manager.dart'; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.mocks.dart index 6138fdd2f4a4..17d47917b2ee 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.mocks.dart @@ -1,5 +1,5 @@ // Mocks generated by Mockito 5.2.0 from annotations -// in webview_flutter_wkwebview/example/ios/.symlinks/plugins/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart. +// in webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart. // Do not manually edit this file. import 'dart:async' as _i5; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_cookie_manager_test.dart new file mode 100644 index 000000000000..71107cb563a6 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_cookie_manager_test.dart @@ -0,0 +1,112 @@ +// 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:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; +import 'package:webview_flutter_wkwebview/src/v4/src/webkit_proxy.dart'; +import 'package:webview_flutter_wkwebview/src/v4/webview_flutter_wkwebview.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; + +import 'webkit_webview_cookie_manager_test.mocks.dart'; + +@GenerateMocks([WKWebsiteDataStore, WKHttpCookieStore]) +void main() { + WidgetsFlutterBinding.ensureInitialized(); + + group('WebKitWebViewCookieManager', () { + test('clearCookies', () { + final MockWKWebsiteDataStore mockWKWebsiteDataStore = + MockWKWebsiteDataStore(); + + final WebKitWebViewCookieManager manager = WebKitWebViewCookieManager( + WebKitWebViewCookieManagerCreationParams( + webKitProxy: WebKitProxy( + defaultWebsiteDataStore: () => mockWKWebsiteDataStore, + ), + ), + ); + + when( + mockWKWebsiteDataStore.removeDataOfTypes( + {WKWebsiteDataType.cookies}, + any, + ), + ).thenAnswer((_) => Future.value(true)); + expect(manager.clearCookies(), completion(true)); + + when( + mockWKWebsiteDataStore.removeDataOfTypes( + {WKWebsiteDataType.cookies}, + any, + ), + ).thenAnswer((_) => Future.value(false)); + expect(manager.clearCookies(), completion(false)); + }); + + test('setCookie', () async { + final MockWKWebsiteDataStore mockWKWebsiteDataStore = + MockWKWebsiteDataStore(); + + final MockWKHttpCookieStore mockCookieStore = MockWKHttpCookieStore(); + when(mockWKWebsiteDataStore.httpCookieStore).thenReturn(mockCookieStore); + + final WebKitWebViewCookieManager manager = WebKitWebViewCookieManager( + WebKitWebViewCookieManagerCreationParams( + webKitProxy: WebKitProxy( + defaultWebsiteDataStore: () => mockWKWebsiteDataStore, + ), + ), + ); + + await manager.setCookie( + const WebViewCookie(name: 'a', value: 'b', domain: 'c', path: 'd'), + ); + + final NSHttpCookie cookie = verify(mockCookieStore.setCookie(captureAny)) + .captured + .single as NSHttpCookie; + expect( + cookie.properties, + { + NSHttpCookiePropertyKey.name: 'a', + NSHttpCookiePropertyKey.value: 'b', + NSHttpCookiePropertyKey.domain: 'c', + NSHttpCookiePropertyKey.path: 'd', + }, + ); + }); + + test('setCookie throws argument error with invalid path', () async { + final MockWKWebsiteDataStore mockWKWebsiteDataStore = + MockWKWebsiteDataStore(); + + final MockWKHttpCookieStore mockCookieStore = MockWKHttpCookieStore(); + when(mockWKWebsiteDataStore.httpCookieStore).thenReturn(mockCookieStore); + + final WebKitWebViewCookieManager manager = WebKitWebViewCookieManager( + WebKitWebViewCookieManagerCreationParams( + webKitProxy: WebKitProxy( + defaultWebsiteDataStore: () => mockWKWebsiteDataStore, + ), + ), + ); + + expect( + () => manager.setCookie( + WebViewCookie( + name: 'a', + value: 'b', + domain: 'c', + path: String.fromCharCode(0x1F), + ), + ), + throwsArgumentError, + ); + }); + }); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_cookie_manager_test.mocks.dart new file mode 100644 index 000000000000..90bf2522ab77 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_cookie_manager_test.mocks.dart @@ -0,0 +1,100 @@ +// Mocks generated by Mockito 5.2.0 from annotations +// in webview_flutter_wkwebview/test/v4/webkit_webview_cookie_manager_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i3; + +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart' + as _i4; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.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: 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 + +class _FakeWKHttpCookieStore_0 extends _i1.Fake + implements _i2.WKHttpCookieStore {} + +class _FakeWKWebsiteDataStore_1 extends _i1.Fake + implements _i2.WKWebsiteDataStore {} + +/// A class which mocks [WKWebsiteDataStore]. +/// +/// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable +class MockWKWebsiteDataStore extends _i1.Mock + implements _i2.WKWebsiteDataStore { + MockWKWebsiteDataStore() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.WKHttpCookieStore get httpCookieStore => + (super.noSuchMethod(Invocation.getter(#httpCookieStore), + returnValue: _FakeWKHttpCookieStore_0()) as _i2.WKHttpCookieStore); + @override + _i3.Future removeDataOfTypes( + Set<_i2.WKWebsiteDataType>? dataTypes, DateTime? since) => + (super.noSuchMethod( + Invocation.method(#removeDataOfTypes, [dataTypes, since]), + returnValue: Future.value(false)) as _i3.Future); + @override + _i2.WKWebsiteDataStore copy() => + (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWKWebsiteDataStore_1()) as _i2.WKWebsiteDataStore); + @override + _i3.Future addObserver(_i4.NSObject? observer, + {String? keyPath, Set<_i4.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); + @override + _i3.Future removeObserver(_i4.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); +} + +/// A class which mocks [WKHttpCookieStore]. +/// +/// See the documentation for Mockito's code generation for more information. +// ignore: must_be_immutable +class MockWKHttpCookieStore extends _i1.Mock implements _i2.WKHttpCookieStore { + MockWKHttpCookieStore() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future setCookie(_i4.NSHttpCookie? cookie) => + (super.noSuchMethod(Invocation.method(#setCookie, [cookie]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); + @override + _i2.WKHttpCookieStore copy() => + (super.noSuchMethod(Invocation.method(#copy, []), + returnValue: _FakeWKHttpCookieStore_0()) as _i2.WKHttpCookieStore); + @override + _i3.Future addObserver(_i4.NSObject? observer, + {String? keyPath, Set<_i4.NSKeyValueObservingOptions>? options}) => + (super.noSuchMethod( + Invocation.method( + #addObserver, [observer], {#keyPath: keyPath, #options: options}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); + @override + _i3.Future removeObserver(_i4.NSObject? observer, {String? keyPath}) => + (super.noSuchMethod( + Invocation.method(#removeObserver, [observer], {#keyPath: keyPath}), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i3.Future); +} From 51d0d913c124f5431bd58e793de5a820cabf4370 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 23 Aug 2022 11:47:37 -0400 Subject: [PATCH 645/844] Roll Flutter from e04c8becf5ef to abfba69f8776 (22 revisions) (#6305) --- .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 fe4b1db3d5a0..09679562aac1 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -e04c8becf5efd147c9b174355a6faeb4022a53ec +abfba69f87762414e69c72ad92006f6cc56055c9 From 38cd9af26223a43ea6633b7f09b9ff60c3e9e131 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Tue, 23 Aug 2022 12:49:59 -0700 Subject: [PATCH 646/844] [ci] Add file_selector_ios CODEOWNER (#6306) --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index 63bcf564e9a0..6562217907f0 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -42,6 +42,7 @@ packages/video_player/video_player_android/** @camsim99 # - iOS packages/camera/camera_avfoundation/** @hellohuanlin +packages/file_selector/file_selector_ios/** @jmagman packages/google_maps_flutter/google_maps_flutter_ios/** @cyanglaz packages/google_sign_in/google_sign_in_ios/** @jmagman packages/image_picker/image_picker_ios/** @cyanglaz From e4cabdf56d0d781e353fdbbb00b1b871fefdd1e1 Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Tue, 23 Aug 2022 13:38:26 -0700 Subject: [PATCH 647/844] [camera]revert the weakSelf in FLTThreadSafeEventChannel (#6303) --- packages/camera/camera_avfoundation/CHANGELOG.md | 4 ++++ .../RunnerTests/ThreadSafeEventChannelTests.m | 16 ++++++++++++++++ .../ios/Classes/FLTThreadSafeEventChannel.m | 10 ++++++---- packages/camera/camera_avfoundation/pubspec.yaml | 2 +- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index 4df497becba6..897a1759643f 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.8+5 + +* Fixes a regression introduced in 0.9.8+4 where the stream handler is not set. + ## 0.9.8+4 * Fixes a crash due to sending orientation change events when the engine is torn down. diff --git a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.m b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.m index e7460de6977e..2aad7e3de9dd 100644 --- a/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.m +++ b/packages/camera/camera_avfoundation/example/ios/RunnerTests/ThreadSafeEventChannelTests.m @@ -63,4 +63,20 @@ - (void)testSetStreamHandler_shouldDispatchToMainThreadIfCalledFromBackgroundThr [self waitForExpectationsWithTimeout:1 handler:nil]; } +- (void)testEventChannel_shouldBeKeptAliveWhenDispatchingBackToMainThread { + XCTestExpectation *expectation = + [self expectationWithDescription:@"Completion should be called."]; + dispatch_async(dispatch_queue_create("test", NULL), ^{ + FLTThreadSafeEventChannel *channel = [[FLTThreadSafeEventChannel alloc] + initWithEventChannel:OCMClassMock([FlutterEventChannel class])]; + + [channel setStreamHandler:OCMOCK_ANY + completion:^{ + [expectation fulfill]; + }]; + }); + + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + @end diff --git a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.m b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.m index ed749a304cee..57d154c595ec 100644 --- a/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.m +++ b/packages/camera/camera_avfoundation/ios/Classes/FLTThreadSafeEventChannel.m @@ -21,11 +21,13 @@ - (instancetype)initWithEventChannel:(FlutterEventChannel *)channel { - (void)setStreamHandler:(NSObject *)handler completion:(void (^)(void))completion { - __weak typeof(self) weakSelf = self; + // WARNING: Should not use weak self, because FLTThreadSafeEventChannel is a local variable + // (retained within call stack, but not in the heap). FLTEnsureToRunOnMainQueue may trigger a + // context switch (when calling from background thread), in which case using weak self will always + // result in a nil self. Alternative to using strong self, we can also create a local strong + // variable to be captured by this block. FLTEnsureToRunOnMainQueue(^{ - typeof(self) strongSelf = weakSelf; - if (!strongSelf) return; - [strongSelf.channel setStreamHandler:handler]; + [self.channel setStreamHandler:handler]; completion(); }); } diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index 3d1b7c6c3fa7..c25fe66e4cc7 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_avfoundation description: iOS implementation of the camera plugin. repository: https://github.com/flutter/plugins/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.8+4 +version: 0.9.8+5 environment: sdk: ">=2.14.0 <3.0.0" From cedd66cbba5d13aa3b39a39aef9167e8efde80da Mon Sep 17 00:00:00 2001 From: Pierre-Louis <6655696+guidezpl@users.noreply.github.com> Date: Thu, 25 Aug 2022 10:23:17 +0200 Subject: [PATCH 648/844] Commit generated boilerplate (#6311) --- .../example/windows/flutter/generated_plugins.cmake | 8 ++++++++ .../example/linux/flutter/generated_plugins.cmake | 8 ++++++++ .../example/windows/flutter/generated_plugins.cmake | 8 ++++++++ .../example/linux/flutter/generated_plugins.cmake | 8 ++++++++ .../example/linux/flutter/generated_plugins.cmake | 8 ++++++++ .../example/windows/flutter/generated_plugins.cmake | 8 ++++++++ .../example/linux/flutter/generated_plugins.cmake | 8 ++++++++ .../example/windows/flutter/generated_plugins.cmake | 8 ++++++++ .../example/linux/flutter/generated_plugins.cmake | 8 ++++++++ .../example/windows/flutter/generated_plugins.cmake | 8 ++++++++ 10 files changed, 80 insertions(+) diff --git a/packages/image_picker/image_picker_windows/example/windows/flutter/generated_plugins.cmake b/packages/image_picker/image_picker_windows/example/windows/flutter/generated_plugins.cmake index 63eda9b7b59f..a423a02476a2 100644 --- a/packages/image_picker/image_picker_windows/example/windows/flutter/generated_plugins.cmake +++ b/packages/image_picker/image_picker_windows/example/windows/flutter/generated_plugins.cmake @@ -6,6 +6,9 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_windows ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -14,3 +17,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/path_provider/path_provider/example/linux/flutter/generated_plugins.cmake b/packages/path_provider/path_provider/example/linux/flutter/generated_plugins.cmake index 51436ae8c982..2e1de87a7eb6 100644 --- a/packages/path_provider/path_provider/example/linux/flutter/generated_plugins.cmake +++ b/packages/path_provider/path_provider/example/linux/flutter/generated_plugins.cmake @@ -5,6 +5,9 @@ list(APPEND FLUTTER_PLUGIN_LIST ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -13,3 +16,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/path_provider/path_provider/example/windows/flutter/generated_plugins.cmake b/packages/path_provider/path_provider/example/windows/flutter/generated_plugins.cmake index 4d10c2518654..b93c4c30c167 100644 --- a/packages/path_provider/path_provider/example/windows/flutter/generated_plugins.cmake +++ b/packages/path_provider/path_provider/example/windows/flutter/generated_plugins.cmake @@ -5,6 +5,9 @@ list(APPEND FLUTTER_PLUGIN_LIST ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -13,3 +16,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/path_provider/path_provider_linux/example/linux/flutter/generated_plugins.cmake b/packages/path_provider/path_provider_linux/example/linux/flutter/generated_plugins.cmake index 51436ae8c982..2e1de87a7eb6 100644 --- a/packages/path_provider/path_provider_linux/example/linux/flutter/generated_plugins.cmake +++ b/packages/path_provider/path_provider_linux/example/linux/flutter/generated_plugins.cmake @@ -5,6 +5,9 @@ list(APPEND FLUTTER_PLUGIN_LIST ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -13,3 +16,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/shared_preferences/shared_preferences/example/linux/flutter/generated_plugins.cmake b/packages/shared_preferences/shared_preferences/example/linux/flutter/generated_plugins.cmake index 51436ae8c982..2e1de87a7eb6 100644 --- a/packages/shared_preferences/shared_preferences/example/linux/flutter/generated_plugins.cmake +++ b/packages/shared_preferences/shared_preferences/example/linux/flutter/generated_plugins.cmake @@ -5,6 +5,9 @@ list(APPEND FLUTTER_PLUGIN_LIST ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -13,3 +16,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/shared_preferences/shared_preferences/example/windows/flutter/generated_plugins.cmake b/packages/shared_preferences/shared_preferences/example/windows/flutter/generated_plugins.cmake index 4d10c2518654..b93c4c30c167 100644 --- a/packages/shared_preferences/shared_preferences/example/windows/flutter/generated_plugins.cmake +++ b/packages/shared_preferences/shared_preferences/example/windows/flutter/generated_plugins.cmake @@ -5,6 +5,9 @@ list(APPEND FLUTTER_PLUGIN_LIST ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -13,3 +16,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugins.cmake b/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugins.cmake index 51436ae8c982..2e1de87a7eb6 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugins.cmake +++ b/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugins.cmake @@ -5,6 +5,9 @@ list(APPEND FLUTTER_PLUGIN_LIST ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -13,3 +16,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/shared_preferences/shared_preferences_windows/example/windows/flutter/generated_plugins.cmake b/packages/shared_preferences/shared_preferences_windows/example/windows/flutter/generated_plugins.cmake index 4d10c2518654..b93c4c30c167 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/windows/flutter/generated_plugins.cmake +++ b/packages/shared_preferences/shared_preferences_windows/example/windows/flutter/generated_plugins.cmake @@ -5,6 +5,9 @@ list(APPEND FLUTTER_PLUGIN_LIST ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -13,3 +16,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugins.cmake b/packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugins.cmake index 1fc8ed344297..f16b4c34213a 100644 --- a/packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugins.cmake +++ b/packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugins.cmake @@ -6,6 +6,9 @@ list(APPEND FLUTTER_PLUGIN_LIST url_launcher_linux ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -14,3 +17,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/packages/url_launcher/url_launcher_windows/example/windows/flutter/generated_plugins.cmake b/packages/url_launcher/url_launcher_windows/example/windows/flutter/generated_plugins.cmake index 411af46dd721..88b22e5c775e 100644 --- a/packages/url_launcher/url_launcher_windows/example/windows/flutter/generated_plugins.cmake +++ b/packages/url_launcher/url_launcher_windows/example/windows/flutter/generated_plugins.cmake @@ -6,6 +6,9 @@ list(APPEND FLUTTER_PLUGIN_LIST url_launcher_windows ) +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + set(PLUGIN_BUNDLED_LIBRARIES) foreach(plugin ${FLUTTER_PLUGIN_LIST}) @@ -14,3 +17,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) From 0c7b4e742bb50937e2693d537859b42c91d52684 Mon Sep 17 00:00:00 2001 From: Pierre-Louis <6655696+guidezpl@users.noreply.github.com> Date: Thu, 25 Aug 2022 19:23:59 +0200 Subject: [PATCH 649/844] [in_app_purchase] Migrate `ThemeData.errorColor` (#6310) * Migrate ThemeData.errorColor * x --- .../in_app_purchase/example/lib/main.dart | 10 +++++++--- .../in_app_purchase_android/example/lib/main.dart | 8 +++++--- .../in_app_purchase_storekit/example/lib/main.dart | 8 +++++--- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart index 98a24d1de963..9e53b4bf8b8e 100644 --- a/packages/in_app_purchase/in_app_purchase/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase/example/lib/main.dart @@ -4,12 +4,14 @@ import 'dart:async'; import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:in_app_purchase/in_app_purchase.dart'; 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_storekit/in_app_purchase_storekit.dart'; import 'package:in_app_purchase_storekit/store_kit_wrappers.dart'; + import 'consumable_store.dart'; void main() { @@ -194,7 +196,9 @@ class _MyAppState extends State<_MyApp> { } final Widget storeHeader = ListTile( leading: Icon(_isAvailable ? Icons.check : Icons.block, - color: _isAvailable ? Colors.green : ThemeData.light().errorColor), + color: _isAvailable + ? Colors.green + : ThemeData.light().colorScheme.error), title: Text('The store is ${_isAvailable ? 'available' : 'unavailable'}.'), ); @@ -205,7 +209,7 @@ class _MyAppState extends State<_MyApp> { const Divider(), ListTile( title: Text('Not connected', - style: TextStyle(color: ThemeData.light().errorColor)), + style: TextStyle(color: ThemeData.light().colorScheme.error)), subtitle: const Text( 'Unable to connect to the payments processor. Has this app been configured correctly? See the example README for instructions.'), ), @@ -229,7 +233,7 @@ class _MyAppState extends State<_MyApp> { if (_notFoundIds.isNotEmpty) { productList.add(ListTile( title: Text('[${_notFoundIds.join(", ")}] not found', - style: TextStyle(color: ThemeData.light().errorColor)), + style: TextStyle(color: ThemeData.light().colorScheme.error)), subtitle: const Text( 'This app needs special configuration to run. Please see example/README.md for instructions.'))); } 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 6c0d967196d0..4eea725b089f 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 @@ -186,7 +186,9 @@ class _MyAppState extends State<_MyApp> { } final Widget storeHeader = ListTile( leading: Icon(_isAvailable ? Icons.check : Icons.block, - color: _isAvailable ? Colors.green : ThemeData.light().errorColor), + color: _isAvailable + ? Colors.green + : ThemeData.light().colorScheme.error), title: Text('The store is ${_isAvailable ? 'available' : 'unavailable'}.'), ); @@ -197,7 +199,7 @@ class _MyAppState extends State<_MyApp> { const Divider(), ListTile( title: Text('Not connected', - style: TextStyle(color: ThemeData.light().errorColor)), + style: TextStyle(color: ThemeData.light().colorScheme.error)), subtitle: const Text( 'Unable to connect to the payments processor. Has this app been configured correctly? See the example README for instructions.'), ), @@ -221,7 +223,7 @@ class _MyAppState extends State<_MyApp> { if (_notFoundIds.isNotEmpty) { productList.add(ListTile( title: Text('[${_notFoundIds.join(", ")}] not found', - style: TextStyle(color: ThemeData.light().errorColor)), + style: TextStyle(color: ThemeData.light().colorScheme.error)), subtitle: const Text( 'This app needs special configuration to run. Please see example/README.md for instructions.'))); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart index a4f9b2603808..72086eeb65a5 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart @@ -188,7 +188,9 @@ class _MyAppState extends State<_MyApp> { } final Widget storeHeader = ListTile( leading: Icon(_isAvailable ? Icons.check : Icons.block, - color: _isAvailable ? Colors.green : ThemeData.light().errorColor), + color: _isAvailable + ? Colors.green + : ThemeData.light().colorScheme.error), title: Text('The store is ${_isAvailable ? 'available' : 'unavailable'}.'), ); @@ -199,7 +201,7 @@ class _MyAppState extends State<_MyApp> { const Divider(), ListTile( title: Text('Not connected', - style: TextStyle(color: ThemeData.light().errorColor)), + style: TextStyle(color: ThemeData.light().colorScheme.error)), subtitle: const Text( 'Unable to connect to the payments processor. Has this app been configured correctly? See the example README for instructions.'), ), @@ -223,7 +225,7 @@ class _MyAppState extends State<_MyApp> { if (_notFoundIds.isNotEmpty) { productList.add(ListTile( title: Text('[${_notFoundIds.join(", ")}] not found', - style: TextStyle(color: ThemeData.light().errorColor)), + style: TextStyle(color: ThemeData.light().colorScheme.error)), subtitle: const Text( 'This app needs special configuration to run. Please see example/README.md for instructions.'))); } From 9da395c1ed8cee40febe5b0f6d5e3dd9f61d84b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Aug 2022 00:03:11 +0000 Subject: [PATCH 650/844] [gh_actions]: Bump lewagon/wait-on-check-action from 1.1.1 to 1.1.2 (#6300) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d4e2b11ec5f1..06154081619b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,7 +36,7 @@ jobs: # This workflow should be the last to run. So wait for all the other tests to succeed. - name: Wait on all tests - uses: lewagon/wait-on-check-action@752bfae19aef55dab12a00bc36d48acc46b77e9d + uses: lewagon/wait-on-check-action@e2558238c09778af25867eb5de5a3ce4bbae3dcd with: ref: ${{ github.sha }} running-workflow-name: 'release' From 3f73f7fa17ababafc68a436dbed34d97963ef04e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Aug 2022 00:11:16 +0000 Subject: [PATCH 651/844] [gh_actions]: Bump github/codeql-action from 2.1.18 to 2.1.19 (#6299) --- .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 8ca9fe9e823e..1f6ab01fe390 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -48,6 +48,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@2ca79b6fa8d3ec278944088b4aa5f46912db5d63 + uses: github/codeql-action/upload-sarif@f5d217be74900c6ac8fbbe53f3c10376ba4e64da with: sarif_file: results.sarif From 46821dcd160a984f895acc6a0856505779dea969 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Aug 2022 01:00:11 +0000 Subject: [PATCH 652/844] [gh_actions]: Bump ossf/scorecard-action from 1.1.1 to 1.1.2 (#6071) --- .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 1f6ab01fe390..a299a8dc9d0d 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -25,7 +25,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@3e15ea8318eee9b333819ec77a36aca8d39df13e + uses: ossf/scorecard-action@ce330fde6b1a5c9c75b417e7efc510b822a35564 with: results_file: results.sarif results_format: sarif From 20bb8653be9ed5e95c6a559e4a466dcded409500 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 26 Aug 2022 02:50:23 -0400 Subject: [PATCH 653/844] Roll Flutter from abfba69f8776 to 6d3f782fb004 (74 revisions) (#6314) --- .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 09679562aac1..956a117e14a0 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -abfba69f87762414e69c72ad92006f6cc56055c9 +6d3f782fb004ad6fd3f08f7dd4f2e8364b08e74a From f1178db970f77c8ee304d5418a80b36216627c12 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Aug 2022 15:02:34 +0000 Subject: [PATCH 654/844] [gh_actions]: Bump actions/labeler from 4.0.0 to 4.0.1 (#6262) --- .github/workflows/pull_request_label.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull_request_label.yml b/.github/workflows/pull_request_label.yml index dff3fbd411d6..baec3deb4893 100644 --- a/.github/workflows/pull_request_label.yml +++ b/.github/workflows/pull_request_label.yml @@ -21,7 +21,7 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@9fd24f1f9d6ceb64ba34d181b329ee72f99978a0 + - uses: actions/labeler@e54e5b338fbd6e6cdb5d60f51c22335fc57c401e with: repo-token: "${{ secrets.GITHUB_TOKEN }}" sync-labels: true From 0e8d8310c573449874302a2a1c440cbece0954ed Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 26 Aug 2022 13:42:41 -0400 Subject: [PATCH 655/844] Roll Flutter from 6d3f782fb004 to 000b96cfdf84 (13 revisions) (#6317) --- .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 956a117e14a0..df8a1ed1841f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -6d3f782fb004ad6fd3f08f7dd4f2e8364b08e74a +000b96cfdf848d5accb2da8fe455fb19d01e3b9c From 64c5b8ff5b55daf52f0010cf143e508a0259f061 Mon Sep 17 00:00:00 2001 From: Tarrin Neal Date: Fri, 26 Aug 2022 11:40:55 -0700 Subject: [PATCH 656/844] Adds info about commands requiring Flutter-bundled dart (#6312) --- script/tool/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/tool/README.md b/script/tool/README.md index 225cda2ef265..9b91b03eadaf 100644 --- a/script/tool/README.md +++ b/script/tool/README.md @@ -15,6 +15,8 @@ instead. (It is marked as Discontinued since it is no longer maintained as a general-purpose tool, but updates are still published for use in flutter/packages.) +The commands in tools require the Flutter-bundled version of Dart to be the first `dart` loaded in the path. + ### From Source (flutter/plugins only) Set up: From c5126a86f13597c2d519cfa63f4924cb8b785b3b Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 26 Aug 2022 16:51:20 -0400 Subject: [PATCH 657/844] [tools] Validate code excerpt configuration (#6286) --- script/tool/CHANGELOG.md | 6 +++ script/tool/lib/src/readme_check_command.dart | 30 +++++++++++-- script/tool/pubspec.yaml | 2 +- .../tool/test/readme_check_command_test.dart | 44 ++++++++++++++++++- .../test/update_excerpts_command_test.dart | 16 +++---- script/tool/test/util.dart | 7 +++ 6 files changed, 90 insertions(+), 15 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 50e389fd3192..595943777c68 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.9.2 + +* Adds checking of `code-excerpt` configuration to `readme-check`, to validate + that if the excerpting tags are added to a README they are actually being + used. + ## 0.9.1 * Adds a `--downgrade` flag to `analyze` for analyzing with the oldest possible diff --git a/script/tool/lib/src/readme_check_command.dart b/script/tool/lib/src/readme_check_command.dart index 6e79b736781f..4acf997a0085 100644 --- a/script/tool/lib/src/readme_check_command.dart +++ b/script/tool/lib/src/readme_check_command.dart @@ -12,6 +12,9 @@ import 'common/package_looping_command.dart'; import 'common/process_runner.dart'; import 'common/repository_package.dart'; +const String _instructionWikiUrl = + 'https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages'; + /// A command to enforce README conventions across the repository. class ReadmeCheckCommand extends PackageLoopingCommand { /// Creates an instance of the README check command. @@ -95,7 +98,8 @@ class ReadmeCheckCommand extends PackageLoopingCommand { final List readmeLines = readme.readAsLinesSync(); final List errors = []; - final String? blockValidationError = _validateCodeBlocks(readmeLines); + final String? blockValidationError = + _validateCodeBlocks(readmeLines, mainPackage: mainPackage); if (blockValidationError != null) { errors.add(blockValidationError); } @@ -123,8 +127,12 @@ class ReadmeCheckCommand extends PackageLoopingCommand { } /// Validates that code blocks (``` ... ```) follow repository standards. - String? _validateCodeBlocks(List readmeLines) { + String? _validateCodeBlocks( + List readmeLines, { + required RepositoryPackage mainPackage, + }) { final RegExp codeBlockDelimiterPattern = RegExp(r'^\s*```\s*([^ ]*)\s*'); + const String excerptTagStart = ' missingLanguageLines = []; final List missingExcerptLines = []; bool inBlock = false; @@ -151,7 +159,6 @@ class ReadmeCheckCommand extends PackageLoopingCommand { // Check for code-excerpt usage if requested. if (getBoolArg(_requireExcerptsArg) && infoString == 'dart') { - const String excerptTagStart = ' line.startsWith(excerptTagStart))) { + const String buildRunnerConfigFile = 'build.excerpt.yaml'; + if (!mainPackage.getExamples().any((RepositoryPackage example) => + example.directory.childFile(buildRunnerConfigFile).existsSync())) { + printError('code-excerpt tag found, but the package is not configured ' + 'for excerpting. Follow the instructions at\n' + '$_instructionWikiUrl\n' + 'for setting up a build.excerpt.yaml file.'); + errorSummary ??= 'Missing code-excerpt configuration'; + } + } + if (missingExcerptLines.isNotEmpty) { for (final int lineNumber in missingExcerptLines) { printError('${indentation}Dart code block at line $lineNumber is not ' @@ -180,7 +201,8 @@ class ReadmeCheckCommand extends PackageLoopingCommand { printError( '\n${indentation}For each block listed above, add ' 'tag on the previous line, and ensure that a build.excerpt.yaml is ' - 'configured for the source example.\n'); + 'configured for the source example as explained at\n' + '$_instructionWikiUrl'); errorSummary ??= 'Missing code-excerpt management for code block'; } diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index e80afa229362..3c25ef62b205 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.9.1 +version: 0.9.2 dependencies: args: ^2.1.0 diff --git a/script/tool/test/readme_check_command_test.dart b/script/tool/test/readme_check_command_test.dart index fa4fc604dd73..37224fddc56b 100644 --- a/script/tool/test/readme_check_command_test.dart +++ b/script/tool/test/readme_check_command_test.dart @@ -504,8 +504,11 @@ A B C }); test('passes when excerpt requirement is met', () async { - final RepositoryPackage package = - createFakePackage('a_package', packagesDir); + final RepositoryPackage package = createFakePackage( + 'a_package', + packagesDir, + extraFiles: [kReadmeExcerptConfigPath], + ); package.readmeFile.writeAsStringSync(''' Example: @@ -528,6 +531,40 @@ A B C ); }); + test('fails when excerpts are used but the package is not configured', + () async { + final RepositoryPackage package = + createFakePackage('a_package', packagesDir); + + package.readmeFile.writeAsStringSync(''' +Example: + + +```dart +A B C +``` +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check', '--require-excerpts'], + errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('code-excerpt tag found, but the package is not configured ' + 'for excerpting. Follow the instructions at\n' + 'https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages\n' + 'for setting up a build.excerpt.yaml file.'), + contains('Missing code-excerpt configuration'), + ]), + ); + }); + test('fails on missing excerpt tag when requested', () async { final RepositoryPackage package = createFakePackage('a_package', packagesDir); @@ -552,6 +589,9 @@ A B C output, containsAllInOrder([ contains('Dart code block at line 3 is not managed by code-excerpt.'), + // Ensure that the failure message links to instructions. + contains( + 'https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages'), contains('Missing code-excerpt management for code block'), ]), ); diff --git a/script/tool/test/update_excerpts_command_test.dart b/script/tool/test/update_excerpts_command_test.dart index dd4b64a98cfc..34c85cc172fe 100644 --- a/script/tool/test/update_excerpts_command_test.dart +++ b/script/tool/test/update_excerpts_command_test.dart @@ -42,7 +42,7 @@ void main() { test('runs pub get before running scripts', () async { final RepositoryPackage package = createFakePlugin('a_package', packagesDir, - extraFiles: ['example/build.excerpt.yaml']); + extraFiles: [kReadmeExcerptConfigPath]); final Directory example = getExampleDir(package); await runCapturingPrint(runner, ['update-excerpts']); @@ -69,7 +69,7 @@ void main() { test('runs when config is present', () async { final RepositoryPackage package = createFakePlugin('a_package', packagesDir, - extraFiles: ['example/build.excerpt.yaml']); + extraFiles: [kReadmeExcerptConfigPath]); final Directory example = getExampleDir(package); final List output = @@ -128,7 +128,7 @@ void main() { test('restores pubspec even if running the script fails', () async { final RepositoryPackage package = createFakePlugin('a_package', packagesDir, - extraFiles: ['example/build.excerpt.yaml']); + extraFiles: [kReadmeExcerptConfigPath]); processRunner.mockProcessesForExecutable['dart'] = [ MockProcess(exitCode: 1), // dart pub get @@ -159,7 +159,7 @@ void main() { test('fails if pub get fails', () async { createFakePlugin('a_package', packagesDir, - extraFiles: ['example/build.excerpt.yaml']); + extraFiles: [kReadmeExcerptConfigPath]); processRunner.mockProcessesForExecutable['dart'] = [ MockProcess(exitCode: 1), // dart pub get @@ -183,7 +183,7 @@ void main() { test('fails if extraction fails', () async { createFakePlugin('a_package', packagesDir, - extraFiles: ['example/build.excerpt.yaml']); + extraFiles: [kReadmeExcerptConfigPath]); processRunner.mockProcessesForExecutable['dart'] = [ MockProcess(), // dart pub get @@ -208,7 +208,7 @@ void main() { test('fails if injection fails', () async { createFakePlugin('a_package', packagesDir, - extraFiles: ['example/build.excerpt.yaml']); + extraFiles: [kReadmeExcerptConfigPath]); processRunner.mockProcessesForExecutable['dart'] = [ MockProcess(), // dart pub get @@ -234,7 +234,7 @@ void main() { test('fails if files are changed with --fail-on-change', () async { createFakePlugin('a_plugin', packagesDir, - extraFiles: ['example/build.excerpt.yaml']); + extraFiles: [kReadmeExcerptConfigPath]); const String changedFilePath = 'packages/a_plugin/linux/foo_plugin.cc'; processRunner.mockProcessesForExecutable['git'] = [ @@ -258,7 +258,7 @@ void main() { test('fails if git ls-files fails', () async { createFakePlugin('a_plugin', packagesDir, - extraFiles: ['example/build.excerpt.yaml']); + extraFiles: [kReadmeExcerptConfigPath]); processRunner.mockProcessesForExecutable['git'] = [ MockProcess(exitCode: 1) diff --git a/script/tool/test/util.dart b/script/tool/test/util.dart index 2754f64dde19..a8cb527d9238 100644 --- a/script/tool/test/util.dart +++ b/script/tool/test/util.dart @@ -23,6 +23,13 @@ import 'mocks.dart'; export 'package:flutter_plugin_tools/src/common/repository_package.dart'; +/// The relative path from a package to the file that is used to enable +/// README excerpting for a package. +// This is a shared constant to ensure that both readme-check and +// update-excerpt are looking for the same file, so that readme-check can't +// get out of sync with what actually drives excerpting. +const String kReadmeExcerptConfigPath = 'example/build.excerpt.yaml'; + const String _defaultDartConstraint = '>=2.14.0 <3.0.0'; const String _defaultFlutterConstraint = '>=2.5.0'; From a8aa5111e6c6525c3b3a0ef0f05c5813eed03e57 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 27 Aug 2022 11:51:35 -0400 Subject: [PATCH 658/844] Roll Flutter from 000b96cfdf84 to 10b321f22cc7 (23 revisions) (#6321) --- .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 df8a1ed1841f..a993587be743 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -000b96cfdf848d5accb2da8fe455fb19d01e3b9c +10b321f22cc7df0baf157be86e3df5e1a0ce3f1e From 5f54809783f4f235f16098293a8e169aab12f76d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 28 Aug 2022 11:43:20 -0400 Subject: [PATCH 659/844] Roll Flutter from 10b321f22cc7 to 041c3af53209 (6 revisions) (#6322) --- .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 a993587be743..fb73e7e50b7c 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -10b321f22cc7df0baf157be86e3df5e1a0ce3f1e +041c3af53209cae4d7da2348fcaeb1a5035dbe0b From 630a200144df5cdcb551e64a83c58b7dd4edff7a Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 29 Aug 2022 08:14:25 -0400 Subject: [PATCH 660/844] [google_maps_flutter] Update Android view selection (#6319) --- .../google_maps_flutter_android/CHANGELOG.md | 5 ++- .../integration_test/google_maps_test.dart | 35 +++++++++++++++++++ .../lib/src/google_maps_flutter_android.dart | 4 +-- .../google_maps_flutter_android/pubspec.yaml | 4 +-- 4 files changed, 43 insertions(+), 5 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 52fb263fa256..38dff3d82470 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,8 @@ -## NEXT +## 2.2.0 +* Updates `useAndroidViewSurface` to require Hybrid Composition, making the + selection work again in Flutter 3.0+. Earlier versions of Flutter are + no longer supported. * Fixes violations of new analysis option use_named_constants. * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart index ccda42f2c7dd..8fc1ede0eb0f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart @@ -9,6 +9,7 @@ import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:google_maps_flutter_android/google_maps_flutter_android.dart'; import 'package:google_maps_flutter_example/example_google_map.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:integration_test/integration_test.dart'; @@ -22,6 +23,40 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); GoogleMapsFlutterPlatform.instance.enableDebugInspection(); + testWidgets('uses surface view', (WidgetTester tester) async { + final GoogleMapsFlutterAndroid instance = + GoogleMapsFlutterPlatform.instance as GoogleMapsFlutterAndroid; + final bool previousUseAndroidViewSurfaceValue = + instance.useAndroidViewSurface; + instance.useAndroidViewSurface = true; + + final Key key = GlobalKey(); + final Completer mapIdCompleter = Completer(); + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + compassEnabled: false, + onMapCreated: (ExampleGoogleMapController controller) { + mapIdCompleter.complete(controller.mapId); + }, + ), + )); + + await mapIdCompleter.future; + + // Wait for the placeholder to be replaced by the actual view. + while (!tester.any(find.byType(AndroidViewSurface)) && + !tester.any(find.byType(AndroidView))) { + await tester.pump(); + } + + instance.useAndroidViewSurface = previousUseAndroidViewSurfaceValue; + + expect(tester.any(find.byType(AndroidViewSurface)), true); + }); + testWidgets('testCompassToggle', (WidgetTester tester) async { final Key key = GlobalKey(); final Completer mapIdCompleter = Completer(); 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 2c11053baa68..95dea4c5d938 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 @@ -518,8 +518,8 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { ); }, onCreatePlatformView: (PlatformViewCreationParams params) { - final SurfaceAndroidViewController controller = - PlatformViewsService.initSurfaceAndroidView( + final AndroidViewController controller = + PlatformViewsService.initExpensiveAndroidView( id: params.id, viewType: viewType, layoutDirection: widgetConfiguration.textDirection, 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 a0417d5dafbc..2ee7c820db43 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_android/pubspec.yaml @@ -2,11 +2,11 @@ name: google_maps_flutter_android description: Android implementation of the google_maps_flutter plugin. repository: https://github.com/flutter/plugins/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.1.10 +version: 2.2.0 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=3.0.0" flutter: plugin: From 0f0ef20501a096951e336e444c7a35dfd5fe595d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 29 Aug 2022 11:50:40 -0400 Subject: [PATCH 661/844] Roll Flutter from 041c3af53209 to 5848306e3784 (8 revisions) (#6333) --- .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 fb73e7e50b7c..2200c3c14f7c 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -041c3af53209cae4d7da2348fcaeb1a5035dbe0b +5848306e3784d0a8fe88f29d63f8749151a2677d From c8fcad4ac418dc9dc95eff3dd35f55942ce640d4 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 29 Aug 2022 15:37:35 -0400 Subject: [PATCH 662/844] [various] Adopt `plugin_platform_interface` (#6332) --- .../CHANGELOG.md | 5 ++ .../google_sign_in_platform_interface.dart | 25 +++--- .../pubspec.yaml | 3 +- ...oogle_sign_in_platform_interface_test.dart | 16 +++- .../CHANGELOG.md | 5 +- ...shared_preferences_platform_interface.dart | 30 +++---- .../pubspec.yaml | 3 +- ...d_preferences_platform_interface_test.dart | 79 +++++++++++++++++-- 8 files changed, 120 insertions(+), 46 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md index 591f36eb1ae8..cc88c07eb00d 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.3.0 + +* Adopts `plugin_platform_interface`. As a result, `isMock` is deprecated in + favor of the now-standard `MockPlatformInterfaceMixin`. + ## 2.2.0 * Adds support for the `serverClientId` parameter. diff --git a/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart b/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart index 69d8455b6bd2..64fc88d4866f 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart' show visibleForTesting; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'src/method_channel_google_sign_in.dart'; import 'src/types.dart'; @@ -20,13 +21,19 @@ export 'src/types.dart'; /// ensures that the subclass will get the default implementation, while /// platform implementations that `implements` this interface will be broken by /// newly added [GoogleSignInPlatform] methods. -abstract class GoogleSignInPlatform { +abstract class GoogleSignInPlatform extends PlatformInterface { + /// Constructs a GoogleSignInPlatform. + GoogleSignInPlatform() : super(token: _token); + + static final Object _token = Object(); + /// Only mock implementations should set this to `true`. /// /// Mockito mocks implement this class with `implements` which is forbidden /// (see class docs). This property provides a backdoor for mocks to skip the /// verification that the class isn't implemented with `implements`. @visibleForTesting + @Deprecated('Use MockPlatformInterfaceMixin instead') bool get isMock => false; /// The default instance of [GoogleSignInPlatform] to use. @@ -44,25 +51,11 @@ abstract class GoogleSignInPlatform { // https://github.com/flutter/flutter/issues/43368 static set instance(GoogleSignInPlatform instance) { if (!instance.isMock) { - try { - instance._verifyProvidesDefaultImplementations(); - } on NoSuchMethodError catch (_) { - throw AssertionError( - 'Platform interfaces must not be implemented with `implements`'); - } + PlatformInterface.verify(instance, _token); } _instance = instance; } - /// This method ensures that [GoogleSignInPlatform] isn't implemented with `implements`. - /// - /// See class docs for more details on why using `implements` to implement - /// [GoogleSignInPlatform] is forbidden. - /// - /// This private method is called by the [instance] setter, which should fail - /// if the provided instance is a class implemented with `implements`. - void _verifyProvidesDefaultImplementations() {} - /// Initializes the plugin. Deprecated: call [initWithParams] instead. /// /// The [hostedDomain] argument specifies a hosted domain restriction. By diff --git a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml index 9ad3e1cf005b..aa9c8f6ff520 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.2.0 +version: 2.3.0 environment: sdk: ">=2.12.0 <3.0.0" @@ -13,6 +13,7 @@ environment: dependencies: flutter: sdk: flutter + plugin_platform_interface: ^2.1.0 quiver: ^3.0.0 dev_dependencies: diff --git a/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart b/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart index 6ffa85fa1e4b..057f13cb26f5 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart @@ -5,6 +5,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; import 'package:mockito/mockito.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; void main() { // Store the initial instance before any tests change it. @@ -33,7 +34,11 @@ void main() { }); test('Can be mocked with `implements`', () { - GoogleSignInPlatform.instance = ImplementsWithIsMock(); + GoogleSignInPlatform.instance = ModernMockImplementation(); + }); + + test('still supports legacy isMock', () { + GoogleSignInPlatform.instance = LegacyIsMockImplementation(); }); }); @@ -76,11 +81,18 @@ void main() { }); } -class ImplementsWithIsMock extends Mock implements GoogleSignInPlatform { +class LegacyIsMockImplementation extends Mock implements GoogleSignInPlatform { @override bool get isMock => true; } +class ModernMockImplementation extends Mock + with MockPlatformInterfaceMixin + implements GoogleSignInPlatform { + @override + bool get isMock => false; +} + class ImplementsGoogleSignInPlatform extends Mock implements GoogleSignInPlatform {} diff --git a/packages/shared_preferences/shared_preferences_platform_interface/CHANGELOG.md b/packages/shared_preferences/shared_preferences_platform_interface/CHANGELOG.md index 51b59651b49a..12d61329171f 100644 --- a/packages/shared_preferences/shared_preferences_platform_interface/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_platform_interface/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.1.0 -* Fixes newly enabled analyzer options. +* Adopts `plugin_platform_interface`. As a result, `isMock` is deprecated in + favor of the now-standard `MockPlatformInterfaceMixin`. ## 2.0.0 diff --git a/packages/shared_preferences/shared_preferences_platform_interface/lib/shared_preferences_platform_interface.dart b/packages/shared_preferences/shared_preferences_platform_interface/lib/shared_preferences_platform_interface.dart index 8023c864a399..ced6aa5c389f 100644 --- a/packages/shared_preferences/shared_preferences_platform_interface/lib/shared_preferences_platform_interface.dart +++ b/packages/shared_preferences/shared_preferences_platform_interface/lib/shared_preferences_platform_interface.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'method_channel_shared_preferences.dart'; @@ -15,7 +16,12 @@ import 'method_channel_shared_preferences.dart'; /// (using `extends`) ensures that the subclass will get the default implementation, while /// platform implementations that `implements` this interface will be broken by newly added /// [SharedPreferencesStorePlatform] methods. -abstract class SharedPreferencesStorePlatform { +abstract class SharedPreferencesStorePlatform extends PlatformInterface { + /// Constructs a SharedPreferencesStorePlatform. + SharedPreferencesStorePlatform() : super(token: _token); + + static final Object _token = Object(); + /// The default instance of [SharedPreferencesStorePlatform] to use. /// /// Defaults to [MethodChannelSharedPreferencesStore]. @@ -23,16 +29,11 @@ abstract class SharedPreferencesStorePlatform { /// Platform-specific plugins should set this with their own platform-specific /// class that extends [SharedPreferencesStorePlatform] when they register themselves. - static set instance(SharedPreferencesStorePlatform value) { - if (!value.isMock) { - try { - value._verifyProvidesDefaultImplementations(); - } on NoSuchMethodError catch (_) { - throw AssertionError( - 'Platform interfaces must not be implemented with `implements`'); - } + static set instance(SharedPreferencesStorePlatform instance) { + if (!instance.isMock) { + PlatformInterface.verify(instance, _token); } - _instance = value; + _instance = instance; } static SharedPreferencesStorePlatform _instance = @@ -44,6 +45,7 @@ abstract class SharedPreferencesStorePlatform { /// other than mocks (see class docs). This property provides a backdoor for mockito mocks to /// skip the verification that the class isn't implemented with `implements`. @visibleForTesting + @Deprecated('Use MockPlatformInterfaceMixin instead') bool get isMock => false; /// Removes the value associated with the [key]. @@ -65,14 +67,6 @@ abstract class SharedPreferencesStorePlatform { /// Returns all key/value pairs persisted in this store. Future> getAll(); - - // This method makes sure that SharedPreferencesStorePlatform isn't implemented with `implements`. - // - // See class doc for more details on why implementing this class is forbidden. - // - // This private method is called by the instance setter, which fails if the class is - // implemented with `implements`. - void _verifyProvidesDefaultImplementations() {} } /// Stores data in memory. diff --git a/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml b/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml index 9dce94c616c4..7e4b9f0699fc 100644 --- a/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_platform_interface description: A common platform interface for the shared_preferences plugin. repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_platform_interface issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.0 +version: 2.1.0 environment: sdk: ">=2.12.0 <3.0.0" @@ -11,6 +11,7 @@ environment: dependencies: flutter: sdk: flutter + plugin_platform_interface: ^2.1.0 dev_dependencies: flutter_test: diff --git a/packages/shared_preferences/shared_preferences_platform_interface/test/shared_preferences_platform_interface_test.dart b/packages/shared_preferences/shared_preferences_platform_interface/test/shared_preferences_platform_interface_test.dart index 8efe885c122c..ed078e87909e 100644 --- a/packages/shared_preferences/shared_preferences_platform_interface/test/shared_preferences_platform_interface_test.dart +++ b/packages/shared_preferences/shared_preferences_platform_interface/test/shared_preferences_platform_interface_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'package:flutter_test/flutter_test.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { @@ -10,16 +11,30 @@ void main() { group(SharedPreferencesStorePlatform, () { test('disallows implementing interface', () { - expect( - () { - SharedPreferencesStorePlatform.instance = IllegalImplementation(); - }, - throwsAssertionError, - ); + expect(() { + SharedPreferencesStorePlatform.instance = IllegalImplementation(); + }, + // In versions of `package:plugin_platform_interface` prior to fixing + // https://github.com/flutter/flutter/issues/109339, an attempt to + // implement a platform interface using `implements` would sometimes + // throw a `NoSuchMethodError` and other times throw an + // `AssertionError`. After the issue is fixed, an `AssertionError` will + // always be thrown. For the purpose of this test, we don't really care + // what exception is thrown, so just allow any exception. + throwsA(anything)); + }); + + test('supports MockPlatformInterfaceMixin', () { + SharedPreferencesStorePlatform.instance = ModernMockImplementation(); + }); + + test('still supports legacy isMock', () { + SharedPreferencesStorePlatform.instance = LegacyIsMockImplementation(); }); }); } +/// An implementation using `implements` that isn't a mock, which isn't allowed. class IllegalImplementation implements SharedPreferencesStorePlatform { // Intentionally declare self as not a mock to trigger the // compliance check. @@ -46,3 +61,55 @@ class IllegalImplementation implements SharedPreferencesStorePlatform { throw UnimplementedError(); } } + +class LegacyIsMockImplementation implements SharedPreferencesStorePlatform { + @override + bool get isMock => true; + + @override + Future clear() { + throw UnimplementedError(); + } + + @override + Future> getAll() { + throw UnimplementedError(); + } + + @override + Future remove(String key) { + throw UnimplementedError(); + } + + @override + Future setValue(String valueType, String key, Object value) { + throw UnimplementedError(); + } +} + +class ModernMockImplementation + with MockPlatformInterfaceMixin + implements SharedPreferencesStorePlatform { + @override + bool get isMock => false; + + @override + Future clear() { + throw UnimplementedError(); + } + + @override + Future> getAll() { + throw UnimplementedError(); + } + + @override + Future remove(String key) { + throw UnimplementedError(); + } + + @override + Future setValue(String valueType, String key, Object value) { + throw UnimplementedError(); + } +} From c08e447f989c154c9f111298014e090da6cc4c41 Mon Sep 17 00:00:00 2001 From: Tarrin Neal Date: Mon, 29 Aug 2022 14:18:34 -0700 Subject: [PATCH 663/844] [file_selector] Endorse ios and update examples (#6320) * Endorse ios and update examples * [gh_actions]: Bump lewagon/wait-on-check-action from 1.1.1 to 1.1.2 (#6300) * [gh_actions]: Bump github/codeql-action from 2.1.18 to 2.1.19 (#6299) * [gh_actions]: Bump ossf/scorecard-action from 1.1.1 to 1.1.2 (#6071) * Roll Flutter from abfba69f8776 to 6d3f782fb004 (74 revisions) (#6314) * [gh_actions]: Bump actions/labeler from 4.0.0 to 4.0.1 (#6262) * Roll Flutter from 6d3f782fb004 to 000b96cfdf84 (13 revisions) (#6317) * Adds info about commands requiring Flutter-bundled dart (#6312) * [tools] Validate code excerpt configuration (#6286) * temp lower ios version number * Update README supported table * remove unused routes * lower version num * iOS vs ios * todo * don't ignore ios * add ios files * add license block * fix Uint8 error * relocate import * Exclude ios integration test for file_selector * fix requests Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: engine-flutter-autoroll Co-authored-by: stuartmorgan Co-authored-by: Tarrin Neal --- .../file_selector/file_selector/CHANGELOG.md | 4 + .../file_selector/file_selector/README.md | 6 +- .../file_selector/example/.gitignore | 4 - .../file_selector/example/ios/.gitignore | 34 ++ .../ios/Flutter/AppFrameworkInfo.plist | 26 + .../example/ios/Flutter/Debug.xcconfig | 1 + .../example/ios/Flutter/Release.xcconfig | 1 + .../ios/Runner.xcodeproj/project.pbxproj | 483 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 87 ++++ .../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 .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 ++ .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../example/ios/Runner/Info.plist | 51 ++ .../ios/Runner/Runner-Bridging-Header.h | 5 + .../example/lib/get_directory_page.dart | 11 +- .../file_selector/example/lib/main.dart | 2 +- .../example/lib/save_text_page.dart | 12 +- .../file_selector/file_selector/pubspec.yaml | 5 +- .../file_selector_ios/CHANGELOG.md | 4 + .../file_selector/file_selector_ios/README.md | 14 +- .../example/lib/home_page.dart | 11 - .../file_selector_ios/pubspec.yaml | 2 +- script/configs/exclude_integration_ios.yaml | 3 +- 50 files changed, 1004 insertions(+), 38 deletions(-) create mode 100644 packages/file_selector/file_selector/example/ios/.gitignore create mode 100644 packages/file_selector/file_selector/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 packages/file_selector/file_selector/example/ios/Flutter/Debug.xcconfig create mode 100644 packages/file_selector/file_selector/example/ios/Flutter/Release.xcconfig create mode 100644 packages/file_selector/file_selector/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 packages/file_selector/file_selector/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 packages/file_selector/file_selector/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/file_selector/file_selector/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 packages/file_selector/file_selector/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/file_selector/file_selector/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/file_selector/file_selector/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/file_selector/file_selector/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 packages/file_selector/file_selector/example/ios/Runner/AppDelegate.swift create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Info.plist create mode 100644 packages/file_selector/file_selector/example/ios/Runner/Runner-Bridging-Header.h diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 024f0a03e796..c16bbe51819e 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.2 + +* Adds an endorsed iOS implementation. + ## 0.9.1 * Adds an endorsed Linux implementation. diff --git a/packages/file_selector/file_selector/README.md b/packages/file_selector/file_selector/README.md index c9727ef18d39..d5fb2d36263e 100644 --- a/packages/file_selector/file_selector/README.md +++ b/packages/file_selector/file_selector/README.md @@ -6,9 +6,9 @@ A Flutter plugin that manages files and interactions with file dialogs. -| | Linux | macOS | Web | Windows | -|-------------|-------|--------|-----|-------------| -| **Support** | Any | 10.11+ | Any | Windows 10+ | +| | iOS | Linux | macOS | Web | Windows | +|-------------|--------|-------|--------|-----|-------------| +| **Support** | iOS 9+ | Any | 10.11+ | Any | Windows 10+ | ## Usage To use this plugin, add `file_selector` as a [dependency in your pubspec.yaml file](https://flutter.dev/platform-plugins/). diff --git a/packages/file_selector/file_selector/example/.gitignore b/packages/file_selector/file_selector/example/.gitignore index 7abd0753cfc3..f3c205341e7d 100644 --- a/packages/file_selector/file_selector/example/.gitignore +++ b/packages/file_selector/file_selector/example/.gitignore @@ -40,9 +40,5 @@ app.*.symbols # Obfuscation related app.*.map.json -# Currently only web supported -android/ -ios/ - # Exceptions to above rules. !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/packages/file_selector/file_selector/example/ios/.gitignore b/packages/file_selector/file_selector/example/ios/.gitignore new file mode 100644 index 000000000000..7a7f9873ad7d --- /dev/null +++ b/packages/file_selector/file_selector/example/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/packages/file_selector/file_selector/example/ios/Flutter/AppFrameworkInfo.plist b/packages/file_selector/file_selector/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 000000000000..9625e105df39 --- /dev/null +++ b/packages/file_selector/file_selector/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 + 11.0 + + diff --git a/packages/file_selector/file_selector/example/ios/Flutter/Debug.xcconfig b/packages/file_selector/file_selector/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000000..592ceee85b89 --- /dev/null +++ b/packages/file_selector/file_selector/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/packages/file_selector/file_selector/example/ios/Flutter/Release.xcconfig b/packages/file_selector/file_selector/example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000000..592ceee85b89 --- /dev/null +++ b/packages/file_selector/file_selector/example/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/packages/file_selector/file_selector/example/ios/Runner.xcodeproj/project.pbxproj b/packages/file_selector/file_selector/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..fe3d67b222fe --- /dev/null +++ b/packages/file_selector/file_selector/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,483 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 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 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 = ""; }; + 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 */ + 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 */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + 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 */ + 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 = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 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 */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 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 = ( + ); + 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 */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase 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; + 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; + 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 = 11.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)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + 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; + 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 = 11.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; + 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; + 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 = 11.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)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; + 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)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; + 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 */ + 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/file_selector/file_selector/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/file_selector/file_selector/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..919434a6254f --- /dev/null +++ b/packages/file_selector/file_selector/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/file_selector/file_selector/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/file_selector/file_selector/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/file_selector/file_selector/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/file_selector/file_selector/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/file_selector/file_selector/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000000..f9b0d7c5ea15 --- /dev/null +++ b/packages/file_selector/file_selector/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/file_selector/file_selector/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/file_selector/file_selector/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000000..c87d15a33520 --- /dev/null +++ b/packages/file_selector/file_selector/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/file_selector/file_selector/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/file_selector/file_selector/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000000..1d526a16ed0f --- /dev/null +++ b/packages/file_selector/file_selector/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/file_selector/file_selector/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/file_selector/file_selector/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000000..18d981003d68 --- /dev/null +++ b/packages/file_selector/file_selector/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/file_selector/file_selector/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/file_selector/file_selector/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000000..f9b0d7c5ea15 --- /dev/null +++ b/packages/file_selector/file_selector/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/packages/file_selector/file_selector/example/ios/Runner/AppDelegate.swift b/packages/file_selector/file_selector/example/ios/Runner/AppDelegate.swift new file mode 100644 index 000000000000..caf998393333 --- /dev/null +++ b/packages/file_selector/file_selector/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 UIKit +import Flutter + +@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/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000000..d36b1fab2d9d --- /dev/null +++ b/packages/file_selector/file_selector/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/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/file_selector/file_selector/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/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/file_selector/file_selector/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/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/file_selector/file_selector/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/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/file_selector/file_selector/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/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/file_selector/file_selector/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/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/file_selector/file_selector/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/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/file_selector/file_selector/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/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/file_selector/file_selector/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/file_selector/file_selector/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/file_selector/file_selector/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/file_selector/file_selector/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 000000000000..0bedcf2fd467 --- /dev/null +++ b/packages/file_selector/file_selector/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/file_selector/file_selector/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/file_selector/file_selector/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/file_selector/file_selector/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/file_selector/file_selector/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/file_selector/file_selector/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/file_selector/file_selector/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/file_selector/file_selector/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 000000000000..89c2725b70f1 --- /dev/null +++ b/packages/file_selector/file_selector/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/packages/file_selector/file_selector/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/file_selector/file_selector/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 000000000000..f2e259c7c939 --- /dev/null +++ b/packages/file_selector/file_selector/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/file_selector/file_selector/example/ios/Runner/Base.lproj/Main.storyboard b/packages/file_selector/file_selector/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/file_selector/file_selector/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/file_selector/file_selector/example/ios/Runner/Info.plist b/packages/file_selector/file_selector/example/ios/Runner/Info.plist new file mode 100644 index 000000000000..7f553465b77e --- /dev/null +++ b/packages/file_selector/file_selector/example/ios/Runner/Info.plist @@ -0,0 +1,51 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Example + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + 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 + + UIViewControllerBasedStatusBarAppearance + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/packages/file_selector/file_selector/example/ios/Runner/Runner-Bridging-Header.h b/packages/file_selector/file_selector/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 000000000000..eb7e8ba8052f --- /dev/null +++ b/packages/file_selector/file_selector/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/file_selector/file_selector/example/lib/get_directory_page.dart b/packages/file_selector/file_selector/example/lib/get_directory_page.dart index 1a36be3fe26b..de80aa56be56 100644 --- a/packages/file_selector/file_selector/example/lib/get_directory_page.dart +++ b/packages/file_selector/file_selector/example/lib/get_directory_page.dart @@ -3,12 +3,15 @@ // found in the LICENSE file. import 'package:file_selector/file_selector.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; /// Screen that shows an example of getDirectoryPath class GetDirectoryPage extends StatelessWidget { /// Default Constructor - const GetDirectoryPage({Key? key}) : super(key: key); + GetDirectoryPage({Key? key}) : super(key: key); + + final bool _isIOS = !kIsWeb && defaultTargetPlatform == TargetPlatform.iOS; Future _getDirectoryPath(BuildContext context) async { const String confirmButtonText = 'Choose'; @@ -43,8 +46,10 @@ class GetDirectoryPage extends StatelessWidget { // ignore: deprecated_member_use onPrimary: Colors.white, ), - child: const Text('Press to ask user to choose a directory'), - onPressed: () => _getDirectoryPath(context), + onPressed: _isIOS ? null : () => _getDirectoryPath(context), + child: const Text( + 'Press to ask user to choose a directory (not supported on iOS).', + ), ), ], ), diff --git a/packages/file_selector/file_selector/example/lib/main.dart b/packages/file_selector/file_selector/example/lib/main.dart index d05e80f1b755..a15842a1191c 100644 --- a/packages/file_selector/file_selector/example/lib/main.dart +++ b/packages/file_selector/file_selector/example/lib/main.dart @@ -35,7 +35,7 @@ class MyApp extends StatelessWidget { const OpenMultipleImagesPage(), '/open/text': (BuildContext context) => const OpenTextPage(), '/save/text': (BuildContext context) => SaveTextPage(), - '/directory': (BuildContext context) => const GetDirectoryPage(), + '/directory': (BuildContext context) => GetDirectoryPage(), }, ); } diff --git a/packages/file_selector/file_selector/example/lib/save_text_page.dart b/packages/file_selector/file_selector/example/lib/save_text_page.dart index d2a8f30db06b..6dc765f7accf 100644 --- a/packages/file_selector/file_selector/example/lib/save_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/save_text_page.dart @@ -2,8 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(tarrinneal): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import import 'dart:typed_data'; + import 'package:file_selector/file_selector.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; @@ -12,6 +16,8 @@ class SaveTextPage extends StatelessWidget { /// Default Constructor SaveTextPage({Key? key}) : super(key: key); + final bool _isIOS = !kIsWeb && defaultTargetPlatform == TargetPlatform.iOS; + final TextEditingController _nameController = TextEditingController(); final TextEditingController _contentController = TextEditingController(); @@ -81,8 +87,10 @@ class SaveTextPage extends StatelessWidget { // ignore: deprecated_member_use onPrimary: Colors.white, ), - child: const Text('Press to save a text file'), - onPressed: () => _saveFile(), + onPressed: _isIOS ? null : () => _saveFile(), + child: const Text( + 'Press to save a text file (not supported on iOS).', + ), ), ], ), diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index 56b82101fe8a..cd633e73a3c5 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.9.1 +version: 0.9.2 environment: sdk: ">=2.12.0 <3.0.0" @@ -12,6 +12,8 @@ environment: flutter: plugin: platforms: + ios: + default_package: file_selector_ios linux: default_package: file_selector_linux macos: @@ -22,6 +24,7 @@ flutter: default_package: file_selector_windows dependencies: + file_selector_ios: ^0.5.0 file_selector_linux: ^0.9.0 file_selector_macos: ^0.9.0 file_selector_platform_interface: ^2.0.0 diff --git a/packages/file_selector/file_selector_ios/CHANGELOG.md b/packages/file_selector/file_selector_ios/CHANGELOG.md index 8c5d8857aec2..0d259e2e8c56 100644 --- a/packages/file_selector/file_selector_ios/CHANGELOG.md +++ b/packages/file_selector/file_selector_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.5.0+1 + +* Updates README for endorsement. + ## 0.5.0 * Initial iOS implementation of `file_selector`. diff --git a/packages/file_selector/file_selector_ios/README.md b/packages/file_selector/file_selector_ios/README.md index 23b234d2b846..4564499e6faf 100644 --- a/packages/file_selector/file_selector_ios/README.md +++ b/packages/file_selector/file_selector_ios/README.md @@ -4,16 +4,8 @@ The iOS implementation of [`file_selector`][1]. ## Usage -### Importing the package - -This implementation has not yet been endorsed, meaning that you need to -[depend on `file_selector_ios`][2] in addition to -[depending on `file_selector`][3]. - -Once your pubspec includes the ios implementation, you can use the -`file_selector` APIs normally. You should not use the `file_selector_ios` -APIs directly. +This package is [endorsed][2], which means you can simply use `file_selector` +normally. This package will be automatically included in your app when you do. [1]: https://pub.dev/packages/file_selector -[2]: https://pub.dev/packages/file_selector_ios/install -[3]: https://pub.dev/packages/file_selector/install \ No newline at end of file +[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/packages/file_selector/file_selector_ios/example/lib/home_page.dart b/packages/file_selector/file_selector_ios/example/lib/home_page.dart index a4b2ae1f63ea..7486977556af 100644 --- a/packages/file_selector/file_selector_ios/example/lib/home_page.dart +++ b/packages/file_selector/file_selector_ios/example/lib/home_page.dart @@ -44,17 +44,6 @@ class HomePage extends StatelessWidget { onPressed: () => Navigator.pushNamed(context, '/open/images'), ), const SizedBox(height: 10), - ElevatedButton( - style: style, - child: const Text('Save a file'), - onPressed: () => Navigator.pushNamed(context, '/save/text'), - ), - const SizedBox(height: 10), - ElevatedButton( - style: style, - child: const Text('Open a get directory dialog'), - onPressed: () => Navigator.pushNamed(context, '/directory'), - ), ], ), ), diff --git a/packages/file_selector/file_selector_ios/pubspec.yaml b/packages/file_selector/file_selector_ios/pubspec.yaml index 1e4056dbcadb..911bcaca069d 100644 --- a/packages/file_selector/file_selector_ios/pubspec.yaml +++ b/packages/file_selector/file_selector_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_ios description: iOS implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.5.0 +version: 0.5.0+1 environment: sdk: ">=2.14.4 <3.0.0" diff --git a/script/configs/exclude_integration_ios.yaml b/script/configs/exclude_integration_ios.yaml index 3df22b9bb2de..19dfdb0350dc 100644 --- a/script/configs/exclude_integration_ios.yaml +++ b/script/configs/exclude_integration_ios.yaml @@ -3,4 +3,5 @@ # Currently missing: https://github.com/flutter/flutter/issues/82208 - ios_platform_images # Can't use Flutter integration tests due to native modal UI. -- file_selector_ios \ No newline at end of file +- file_selector_ios +- file_selector \ No newline at end of file From 7e55a7c67303835e6d4fdce0affca8faefe6ff88 Mon Sep 17 00:00:00 2001 From: Jami Couch Date: Tue, 30 Aug 2022 10:00:10 -0500 Subject: [PATCH 664/844] [google_sign_in] Add forceCodeForRefreshToken parameter platform implementations (#6130) --- .../google_sign_in_android/AUTHORS | 1 + .../google_sign_in_android/CHANGELOG.md | 4 + .../googlesignin/GoogleSignInPlugin.java | 17 ++++- .../googlesignin/GoogleSignInTest.java | 73 ++++++++++++++++++- .../lib/google_sign_in_android.dart | 19 ++++- .../google_sign_in_android/pubspec.yaml | 2 +- .../test/google_sign_in_android_test.dart | 15 ++++ .../google_sign_in/google_sign_in_ios/AUTHORS | 1 + .../google_sign_in_ios/CHANGELOG.md | 4 + .../lib/google_sign_in_ios.dart | 18 ++++- .../google_sign_in_ios/pubspec.yaml | 2 +- .../test/google_sign_in_ios_test.dart | 10 +++ 12 files changed, 150 insertions(+), 16 deletions(-) diff --git a/packages/google_sign_in/google_sign_in_android/AUTHORS b/packages/google_sign_in/google_sign_in_android/AUTHORS index 493a0b4ef9c2..35d24a5ae0b5 100644 --- a/packages/google_sign_in/google_sign_in_android/AUTHORS +++ b/packages/google_sign_in/google_sign_in_android/AUTHORS @@ -64,3 +64,4 @@ Aleksandr Yurkovskiy Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> +Twin Sun, LLC 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 9852a1f733ae..5125b22a0963 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.1.0 + +* Adds override for `GoogleSignIn.initWithParams` to handle new `forceCodeForRefreshToken` parameter. + ## 6.0.1 * Updates gradle version to 7.2.1 on Android. diff --git a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java index 21640233f210..e84d196a6a0d 100644 --- a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java +++ b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java @@ -140,8 +140,15 @@ public void onMethodCall(MethodCall call, Result result) { String hostedDomain = call.argument("hostedDomain"); String clientId = call.argument("clientId"); String serverClientId = call.argument("serverClientId"); + boolean forceCodeForRefreshToken = call.argument("forceCodeForRefreshToken"); delegate.init( - result, signInOption, requestedScopes, hostedDomain, clientId, serverClientId); + result, + signInOption, + requestedScopes, + hostedDomain, + clientId, + serverClientId, + forceCodeForRefreshToken); break; case METHOD_SIGN_IN_SILENTLY: @@ -198,7 +205,8 @@ public void init( List requestedScopes, String hostedDomain, String clientId, - String serverClientId); + String serverClientId, + boolean forceCodeForRefreshToken); /** * Returns the account information for the user who is signed in to this app. If no user is @@ -326,7 +334,8 @@ public void init( List requestedScopes, String hostedDomain, String clientId, - String serverClientId) { + String serverClientId, + boolean forceCodeForRefreshToken) { try { GoogleSignInOptions.Builder optionsBuilder; @@ -374,7 +383,7 @@ public void init( } if (!Strings.isNullOrEmpty(serverClientId)) { optionsBuilder.requestIdToken(serverClientId); - optionsBuilder.requestServerAuthCode(serverClientId); + optionsBuilder.requestServerAuthCode(serverClientId, forceCodeForRefreshToken); } for (String scope : requestedScopes) { optionsBuilder.requestScopes(new Scope(scope)); diff --git a/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java b/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java index 11f8cda2e9b1..9692417390a5 100644 --- a/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java +++ b/packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java @@ -239,6 +239,48 @@ public void init_IgnoresClientIdIfServerClientIdIsProvided() { initAndAssertServerClientId(methodCall, serverClientId); } + @Test + public void init_PassesForceCodeForRefreshTokenFalseWithServerClientIdParameter() { + MethodCall methodCall = buildInitMethodCall("fakeClientId", "fakeServerClientId", false); + + initAndAssertForceCodeForRefreshToken(methodCall, false); + } + + @Test + public void init_PassesForceCodeForRefreshTokenTrueWithServerClientIdParameter() { + MethodCall methodCall = buildInitMethodCall("fakeClientId", "fakeServerClientId", true); + + initAndAssertForceCodeForRefreshToken(methodCall, true); + } + + @Test + public void init_PassesForceCodeForRefreshTokenFalseWithServerClientIdFromResources() { + final String packageName = "fakePackageName"; + final String serverClientId = "fakeServerClientId"; + final int resourceId = 1; + MethodCall methodCall = buildInitMethodCall(null, null, false); + when(mockContext.getPackageName()).thenReturn(packageName); + when(mockResources.getIdentifier("default_web_client_id", "string", packageName)) + .thenReturn(resourceId); + when(mockContext.getString(resourceId)).thenReturn(serverClientId); + + initAndAssertForceCodeForRefreshToken(methodCall, false); + } + + @Test + public void init_PassesForceCodeForRefreshTokenTrueWithServerClientIdFromResources() { + final String packageName = "fakePackageName"; + final String serverClientId = "fakeServerClientId"; + final int resourceId = 1; + MethodCall methodCall = buildInitMethodCall(null, null, true); + when(mockContext.getPackageName()).thenReturn(packageName); + when(mockResources.getIdentifier("default_web_client_id", "string", packageName)) + .thenReturn(resourceId); + when(mockContext.getString(resourceId)).thenReturn(serverClientId); + + initAndAssertForceCodeForRefreshToken(methodCall, true); + } + public void initAndAssertServerClientId(MethodCall methodCall, String serverClientId) { ArgumentCaptor optionsCaptor = ArgumentCaptor.forClass(GoogleSignInOptions.class); @@ -249,13 +291,39 @@ public void initAndAssertServerClientId(MethodCall methodCall, String serverClie Assert.assertEquals(serverClientId, optionsCaptor.getValue().getServerClientId()); } + public void initAndAssertForceCodeForRefreshToken( + MethodCall methodCall, boolean forceCodeForRefreshToken) { + ArgumentCaptor optionsCaptor = + ArgumentCaptor.forClass(GoogleSignInOptions.class); + when(mockGoogleSignIn.getClient(any(Context.class), optionsCaptor.capture())) + .thenReturn(mockClient); + plugin.onMethodCall(methodCall, result); + verify(result).success(null); + Assert.assertEquals( + forceCodeForRefreshToken, optionsCaptor.getValue().isForceCodeForRefreshToken()); + } + private static MethodCall buildInitMethodCall(String clientId, String serverClientId) { return buildInitMethodCall( - "SignInOption.standard", Collections.emptyList(), clientId, serverClientId); + "SignInOption.standard", Collections.emptyList(), clientId, serverClientId, false); + } + + private static MethodCall buildInitMethodCall( + String clientId, String serverClientId, boolean forceCodeForRefreshToken) { + return buildInitMethodCall( + "SignInOption.standard", + Collections.emptyList(), + clientId, + serverClientId, + forceCodeForRefreshToken); } private static MethodCall buildInitMethodCall( - String signInOption, List scopes, String clientId, String serverClientId) { + String signInOption, + List scopes, + String clientId, + String serverClientId, + boolean forceCodeForRefreshToken) { HashMap arguments = new HashMap<>(); arguments.put("signInOption", signInOption); arguments.put("scopes", scopes); @@ -265,6 +333,7 @@ private static MethodCall buildInitMethodCall( if (serverClientId != null) { arguments.put("serverClientId", serverClientId); } + arguments.put("forceCodeForRefreshToken", forceCodeForRefreshToken); return new MethodCall("init", arguments); } } diff --git a/packages/google_sign_in/google_sign_in_android/lib/google_sign_in_android.dart b/packages/google_sign_in/google_sign_in_android/lib/google_sign_in_android.dart index d96328de695a..731da3968b3f 100644 --- a/packages/google_sign_in/google_sign_in_android/lib/google_sign_in_android.dart +++ b/packages/google_sign_in/google_sign_in_android/lib/google_sign_in_android.dart @@ -30,11 +30,22 @@ class GoogleSignInAndroid extends GoogleSignInPlatform { String? hostedDomain, String? clientId, }) { + return initWithParams(SignInInitParameters( + signInOption: signInOption, + scopes: scopes, + hostedDomain: hostedDomain, + clientId: clientId, + )); + } + + @override + Future initWithParams(SignInInitParameters params) { return channel.invokeMethod('init', { - 'signInOption': signInOption.toString(), - 'scopes': scopes, - 'hostedDomain': hostedDomain, - 'clientId': clientId, + 'signInOption': params.signInOption.toString(), + 'scopes': params.scopes, + 'hostedDomain': params.hostedDomain, + 'clientId': params.clientId, + 'forceCodeForRefreshToken': params.forceCodeForRefreshToken, }); } 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 0a863dce1714..de5e3f0a8470 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/plugins/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.0.1 +version: 6.1.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/google_sign_in/google_sign_in_android/test/google_sign_in_android_test.dart b/packages/google_sign_in/google_sign_in_android/test/google_sign_in_android_test.dart index 7d39ae5f0232..948ced3f65bc 100644 --- a/packages/google_sign_in/google_sign_in_android/test/google_sign_in_android_test.dart +++ b/packages/google_sign_in/google_sign_in_android/test/google_sign_in_android_test.dart @@ -111,6 +111,21 @@ void main() { 'scopes': ['two', 'scopes'], 'signInOption': 'SignInOption.games', 'clientId': 'fakeClientId', + 'forceCodeForRefreshToken': false, + }), + () { + googleSignIn.initWithParams(const SignInInitParameters( + hostedDomain: 'example.com', + scopes: ['two', 'scopes'], + signInOption: SignInOption.games, + clientId: 'fakeClientId', + forceCodeForRefreshToken: true)); + }: isMethodCall('init', arguments: { + 'hostedDomain': 'example.com', + 'scopes': ['two', 'scopes'], + 'signInOption': 'SignInOption.games', + 'clientId': 'fakeClientId', + 'forceCodeForRefreshToken': true, }), () { googleSignIn.getTokens( diff --git a/packages/google_sign_in/google_sign_in_ios/AUTHORS b/packages/google_sign_in/google_sign_in_ios/AUTHORS index 493a0b4ef9c2..35d24a5ae0b5 100644 --- a/packages/google_sign_in/google_sign_in_ios/AUTHORS +++ b/packages/google_sign_in/google_sign_in_ios/AUTHORS @@ -64,3 +64,4 @@ Aleksandr Yurkovskiy Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> +Twin Sun, LLC 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 5ed38de5cb74..2b62f8afa0b8 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.5.0 + +* Adds override for `GoogleSignInPlatform.initWithParams`. + ## 5.4.0 * Adds support for `serverClientId` configuration option. diff --git a/packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart b/packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart index ce8865664507..07407eaf5236 100644 --- a/packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart +++ b/packages/google_sign_in/google_sign_in_ios/lib/google_sign_in_ios.dart @@ -30,15 +30,25 @@ class GoogleSignInIOS extends GoogleSignInPlatform { String? hostedDomain, String? clientId, }) { - if (signInOption == SignInOption.games) { + return initWithParams(SignInInitParameters( + signInOption: signInOption, + scopes: scopes, + hostedDomain: hostedDomain, + clientId: clientId, + )); + } + + @override + Future initWithParams(SignInInitParameters params) { + if (params.signInOption == SignInOption.games) { throw PlatformException( code: 'unsupported-options', message: 'Games sign in is not supported on iOS'); } return channel.invokeMethod('init', { - 'scopes': scopes, - 'hostedDomain': hostedDomain, - 'clientId': clientId, + 'scopes': params.scopes, + 'hostedDomain': params.hostedDomain, + 'clientId': params.clientId, }); } 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 65c8928c1402..760e3339e32d 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/plugins/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.4.0 +version: 5.5.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart b/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart index 92637e938fd9..ace65092f61d 100644 --- a/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart +++ b/packages/google_sign_in/google_sign_in_ios/test/google_sign_in_ios_test.dart @@ -125,6 +125,16 @@ void main() { 'scopes': ['two', 'scopes'], 'clientId': 'fakeClientId', }), + () { + googleSignIn.initWithParams(const SignInInitParameters( + hostedDomain: 'example.com', + scopes: ['two', 'scopes'], + clientId: 'fakeClientId')); + }: isMethodCall('init', arguments: { + 'hostedDomain': 'example.com', + 'scopes': ['two', 'scopes'], + 'clientId': 'fakeClientId', + }), () { googleSignIn.getTokens( email: 'example@example.com', shouldRecoverAuth: false); From 2ea96a898cfed57d8ca8f6f9b8ca7f636a06b902 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 30 Aug 2022 14:52:47 -0400 Subject: [PATCH 665/844] Roll Flutter from 5848306e3784 to dfcd9650e36b (24 revisions) (#6336) --- .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 2200c3c14f7c..a8bf77908c73 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -5848306e3784d0a8fe88f29d63f8749151a2677d +dfcd9650e36bdc4e3c9d186682b4e574c7012e45 From e4f400fe6ae72c00b730edb3e07915e3e97b2a1d Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 30 Aug 2022 16:10:05 -0400 Subject: [PATCH 666/844] [ci] Update for Flutter 3.3 release (#6340) --- .ci/flutter_stable.version | 2 +- .cirrus.yml | 2 +- packages/camera/camera/CHANGELOG.md | 4 ++++ packages/camera/camera/example/pubspec.yaml | 2 +- packages/camera/camera/pubspec.yaml | 2 +- packages/camera/camera_android/CHANGELOG.md | 4 ++++ packages/camera/camera_android/example/pubspec.yaml | 2 +- packages/camera/camera_android/pubspec.yaml | 2 +- packages/camera/camera_avfoundation/CHANGELOG.md | 4 ++++ packages/camera/camera_avfoundation/example/pubspec.yaml | 2 +- packages/camera/camera_avfoundation/pubspec.yaml | 2 +- packages/camera/camera_platform_interface/CHANGELOG.md | 1 + packages/camera/camera_platform_interface/pubspec.yaml | 2 +- packages/camera/camera_web/CHANGELOG.md | 1 + packages/camera/camera_web/example/pubspec.yaml | 2 +- packages/camera/camera_web/pubspec.yaml | 2 +- packages/camera/camera_windows/CHANGELOG.md | 4 ++++ packages/camera/camera_windows/example/pubspec.yaml | 2 +- packages/camera/camera_windows/pubspec.yaml | 2 +- packages/espresso/CHANGELOG.md | 4 ++++ packages/espresso/example/pubspec.yaml | 2 +- packages/espresso/pubspec.yaml | 2 +- packages/file_selector/file_selector/CHANGELOG.md | 4 ++++ packages/file_selector/file_selector/pubspec.yaml | 2 +- packages/file_selector/file_selector_ios/CHANGELOG.md | 4 ++++ packages/file_selector/file_selector_ios/pubspec.yaml | 2 +- packages/file_selector/file_selector_linux/CHANGELOG.md | 4 ++++ packages/file_selector/file_selector_linux/pubspec.yaml | 2 +- packages/file_selector/file_selector_macos/CHANGELOG.md | 4 ++++ .../file_selector/file_selector_macos/example/pubspec.yaml | 2 +- packages/file_selector/file_selector_macos/pubspec.yaml | 2 +- .../file_selector_platform_interface/CHANGELOG.md | 4 ++++ .../file_selector_platform_interface/pubspec.yaml | 2 +- packages/file_selector/file_selector_web/CHANGELOG.md | 1 + packages/file_selector/file_selector_web/example/pubspec.yaml | 2 +- packages/file_selector/file_selector_web/pubspec.yaml | 2 +- packages/file_selector/file_selector_windows/CHANGELOG.md | 4 ++++ .../file_selector/file_selector_windows/example/pubspec.yaml | 2 +- packages/file_selector/file_selector_windows/pubspec.yaml | 2 +- packages/flutter_plugin_android_lifecycle/CHANGELOG.md | 4 ++++ packages/flutter_plugin_android_lifecycle/pubspec.yaml | 2 +- packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md | 3 +++ .../google_maps_flutter/example/pubspec.yaml | 2 +- packages/google_maps_flutter/google_maps_flutter/pubspec.yaml | 2 +- .../google_maps_flutter_android/CHANGELOG.md | 4 ++++ .../google_maps_flutter_android/example/pubspec.yaml | 2 +- .../google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md | 1 + .../google_maps_flutter_ios/example/pubspec.yaml | 2 +- .../google_maps_flutter/google_maps_flutter_ios/pubspec.yaml | 2 +- .../google_maps_flutter_platform_interface/CHANGELOG.md | 4 ++++ .../google_maps_flutter_platform_interface/pubspec.yaml | 2 +- .../google_maps_flutter/google_maps_flutter_web/CHANGELOG.md | 4 ++++ .../google_maps_flutter_web/example/pubspec.yaml | 2 +- .../google_maps_flutter/google_maps_flutter_web/pubspec.yaml | 2 +- packages/google_sign_in/google_sign_in/CHANGELOG.md | 4 ++++ packages/google_sign_in/google_sign_in/example/pubspec.yaml | 2 +- packages/google_sign_in/google_sign_in/pubspec.yaml | 2 +- packages/google_sign_in/google_sign_in_android/CHANGELOG.md | 4 ++++ .../google_sign_in_android/example/pubspec.yaml | 2 +- packages/google_sign_in/google_sign_in_android/pubspec.yaml | 2 +- packages/google_sign_in/google_sign_in_ios/CHANGELOG.md | 4 ++++ .../google_sign_in/google_sign_in_ios/example/pubspec.yaml | 2 +- packages/google_sign_in/google_sign_in_ios/pubspec.yaml | 2 +- .../google_sign_in_platform_interface/CHANGELOG.md | 4 ++++ .../google_sign_in_platform_interface/pubspec.yaml | 2 +- packages/google_sign_in/google_sign_in_web/CHANGELOG.md | 4 ++++ .../google_sign_in/google_sign_in_web/example/pubspec.yaml | 2 +- packages/google_sign_in/google_sign_in_web/pubspec.yaml | 2 +- packages/image_picker/image_picker/CHANGELOG.md | 1 + packages/image_picker/image_picker/example/pubspec.yaml | 2 +- packages/image_picker/image_picker/pubspec.yaml | 2 +- packages/image_picker/image_picker_android/CHANGELOG.md | 4 ++++ .../image_picker/image_picker_android/example/pubspec.yaml | 2 +- packages/image_picker/image_picker_android/pubspec.yaml | 2 +- packages/image_picker/image_picker_for_web/CHANGELOG.md | 1 + .../image_picker/image_picker_for_web/example/pubspec.yaml | 2 +- packages/image_picker/image_picker_for_web/pubspec.yaml | 2 +- packages/image_picker/image_picker_ios/CHANGELOG.md | 4 ++++ packages/image_picker/image_picker_ios/example/pubspec.yaml | 2 +- packages/image_picker/image_picker_ios/pubspec.yaml | 2 +- .../image_picker/image_picker_platform_interface/CHANGELOG.md | 1 + .../image_picker/image_picker_platform_interface/pubspec.yaml | 2 +- packages/image_picker/image_picker_windows/CHANGELOG.md | 4 ++++ .../image_picker/image_picker_windows/example/pubspec.yaml | 2 +- packages/image_picker/image_picker_windows/pubspec.yaml | 2 +- packages/in_app_purchase/in_app_purchase/CHANGELOG.md | 4 ++++ packages/in_app_purchase/in_app_purchase/example/pubspec.yaml | 2 +- packages/in_app_purchase/in_app_purchase/pubspec.yaml | 2 +- packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md | 4 ++++ .../in_app_purchase_android/example/pubspec.yaml | 2 +- packages/in_app_purchase/in_app_purchase_android/pubspec.yaml | 2 +- .../in_app_purchase_platform_interface/CHANGELOG.md | 1 + .../in_app_purchase_platform_interface/pubspec.yaml | 2 +- .../in_app_purchase/in_app_purchase_storekit/CHANGELOG.md | 4 ++++ .../in_app_purchase_storekit/example/pubspec.yaml | 2 +- .../in_app_purchase/in_app_purchase_storekit/pubspec.yaml | 2 +- packages/ios_platform_images/CHANGELOG.md | 1 + packages/ios_platform_images/example/pubspec.yaml | 2 +- packages/ios_platform_images/pubspec.yaml | 2 +- packages/local_auth/local_auth/CHANGELOG.md | 4 ++++ packages/local_auth/local_auth/example/pubspec.yaml | 2 +- packages/local_auth/local_auth/pubspec.yaml | 2 +- packages/local_auth/local_auth_android/CHANGELOG.md | 4 ++++ packages/local_auth/local_auth_android/example/pubspec.yaml | 2 +- packages/local_auth/local_auth_android/pubspec.yaml | 2 +- packages/local_auth/local_auth_ios/CHANGELOG.md | 4 ++++ packages/local_auth/local_auth_ios/example/pubspec.yaml | 2 +- packages/local_auth/local_auth_ios/pubspec.yaml | 2 +- .../local_auth/local_auth_platform_interface/CHANGELOG.md | 4 ++++ .../local_auth/local_auth_platform_interface/pubspec.yaml | 2 +- packages/local_auth/local_auth_windows/CHANGELOG.md | 4 ++++ packages/local_auth/local_auth_windows/example/pubspec.yaml | 2 +- packages/local_auth/local_auth_windows/pubspec.yaml | 2 +- packages/path_provider/path_provider/CHANGELOG.md | 1 + packages/path_provider/path_provider/example/pubspec.yaml | 2 +- packages/path_provider/path_provider/pubspec.yaml | 2 +- packages/path_provider/path_provider_android/CHANGELOG.md | 4 ++++ .../path_provider/path_provider_android/example/pubspec.yaml | 2 +- packages/path_provider/path_provider_android/pubspec.yaml | 2 +- packages/path_provider/path_provider_ios/CHANGELOG.md | 4 ++++ packages/path_provider/path_provider_ios/example/pubspec.yaml | 2 +- packages/path_provider/path_provider_ios/pubspec.yaml | 2 +- packages/path_provider/path_provider_linux/CHANGELOG.md | 4 ++++ .../path_provider/path_provider_linux/example/pubspec.yaml | 2 +- packages/path_provider/path_provider_linux/pubspec.yaml | 2 +- packages/path_provider/path_provider_macos/CHANGELOG.md | 4 ++++ .../path_provider/path_provider_macos/example/pubspec.yaml | 2 +- packages/path_provider/path_provider_macos/pubspec.yaml | 2 +- .../path_provider_platform_interface/CHANGELOG.md | 4 ++++ .../path_provider_platform_interface/pubspec.yaml | 2 +- packages/path_provider/path_provider_windows/CHANGELOG.md | 4 ++++ .../path_provider/path_provider_windows/example/pubspec.yaml | 2 +- packages/quick_actions/quick_actions/CHANGELOG.md | 1 + packages/quick_actions/quick_actions/example/pubspec.yaml | 2 +- packages/quick_actions/quick_actions/pubspec.yaml | 2 +- packages/quick_actions/quick_actions_android/CHANGELOG.md | 1 + .../quick_actions/quick_actions_android/example/pubspec.yaml | 2 +- packages/quick_actions/quick_actions_android/pubspec.yaml | 2 +- packages/quick_actions/quick_actions_ios/CHANGELOG.md | 4 ++++ packages/quick_actions/quick_actions_ios/example/pubspec.yaml | 2 +- packages/quick_actions/quick_actions_ios/pubspec.yaml | 4 ++-- .../quick_actions_platform_interface/CHANGELOG.md | 4 ++++ .../quick_actions_platform_interface/pubspec.yaml | 2 +- packages/shared_preferences/shared_preferences/CHANGELOG.md | 4 ++++ .../shared_preferences/example/pubspec.yaml | 2 +- packages/shared_preferences/shared_preferences/pubspec.yaml | 2 +- .../shared_preferences_android/CHANGELOG.md | 4 ++++ .../shared_preferences_android/example/pubspec.yaml | 2 +- .../shared_preferences_android/pubspec.yaml | 2 +- .../shared_preferences/shared_preferences_ios/CHANGELOG.md | 4 ++++ .../shared_preferences_ios/example/pubspec.yaml | 2 +- .../shared_preferences/shared_preferences_ios/pubspec.yaml | 2 +- .../shared_preferences/shared_preferences_linux/CHANGELOG.md | 4 ++++ .../shared_preferences_linux/example/pubspec.yaml | 2 +- .../shared_preferences/shared_preferences_linux/pubspec.yaml | 2 +- .../shared_preferences/shared_preferences_macos/CHANGELOG.md | 4 ++++ .../shared_preferences_macos/example/pubspec.yaml | 2 +- .../shared_preferences/shared_preferences_macos/pubspec.yaml | 2 +- .../shared_preferences_platform_interface/CHANGELOG.md | 4 ++++ .../shared_preferences_platform_interface/pubspec.yaml | 2 +- .../shared_preferences/shared_preferences_web/CHANGELOG.md | 4 ++++ .../shared_preferences_web/example/pubspec.yaml | 2 +- .../shared_preferences/shared_preferences_web/pubspec.yaml | 2 +- .../shared_preferences_windows/CHANGELOG.md | 4 ++++ .../shared_preferences_windows/example/pubspec.yaml | 2 +- .../shared_preferences_windows/pubspec.yaml | 2 +- packages/url_launcher/url_launcher/CHANGELOG.md | 1 + packages/url_launcher/url_launcher/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_android/CHANGELOG.md | 4 ++++ .../url_launcher/url_launcher_android/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_android/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_ios/CHANGELOG.md | 4 ++++ packages/url_launcher/url_launcher_ios/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_ios/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_linux/CHANGELOG.md | 4 ++++ packages/url_launcher/url_launcher_linux/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_linux/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_macos/CHANGELOG.md | 4 ++++ packages/url_launcher/url_launcher_macos/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_macos/pubspec.yaml | 2 +- .../url_launcher/url_launcher_platform_interface/CHANGELOG.md | 4 ++++ .../url_launcher/url_launcher_platform_interface/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_web/CHANGELOG.md | 4 ++++ packages/url_launcher/url_launcher_web/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_windows/CHANGELOG.md | 4 ++++ .../url_launcher/url_launcher_windows/example/pubspec.yaml | 2 +- packages/url_launcher/url_launcher_windows/pubspec.yaml | 2 +- packages/video_player/video_player_android/CHANGELOG.md | 1 + .../video_player/video_player_android/example/pubspec.yaml | 2 +- packages/video_player/video_player_android/pubspec.yaml | 2 +- packages/video_player/video_player_avfoundation/CHANGELOG.md | 1 + .../video_player_avfoundation/example/pubspec.yaml | 2 +- packages/video_player/video_player_avfoundation/pubspec.yaml | 2 +- .../video_player/video_player_platform_interface/CHANGELOG.md | 1 + .../video_player/video_player_platform_interface/pubspec.yaml | 2 +- packages/video_player/video_player_web/CHANGELOG.md | 4 ++++ packages/video_player/video_player_web/example/pubspec.yaml | 2 +- packages/video_player/video_player_web/pubspec.yaml | 2 +- packages/webview_flutter/webview_flutter/CHANGELOG.md | 1 + packages/webview_flutter/webview_flutter/example/pubspec.yaml | 2 +- packages/webview_flutter/webview_flutter/pubspec.yaml | 2 +- .../webview_flutter_platform_interface/CHANGELOG.md | 4 ++++ .../webview_flutter_platform_interface/pubspec.yaml | 2 +- packages/webview_flutter/webview_flutter_web/CHANGELOG.md | 4 ++++ packages/webview_flutter/webview_flutter_web/pubspec.yaml | 2 +- 206 files changed, 380 insertions(+), 132 deletions(-) diff --git a/.ci/flutter_stable.version b/.ci/flutter_stable.version index 8c2db2fa8a81..14a210a6a125 100644 --- a/.ci/flutter_stable.version +++ b/.ci/flutter_stable.version @@ -1 +1 @@ -f1875d570e39de09040c8f79aa13cc56baab8db1 +ffccd96b62ee8cec7740dab303538c5fc26ac543 diff --git a/.cirrus.yml b/.cirrus.yml index 059f5fe7e5d8..27d98baf8923 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -161,8 +161,8 @@ task: depends_on: analyze env: matrix: + CHANNEL: "3.0.5" CHANNEL: "2.10.5" - CHANNEL: "2.8.1" package_prep_script: # Allow analyzing plugins that use a Pigeon version with a higher # minimum Flutter/Dart version than the plugin itself. diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index d112a4345695..67b8b7970bbe 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 0.10.0+1 * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/camera/camera/example/pubspec.yaml b/packages/camera/camera/example/pubspec.yaml index e9ae2c74a6be..22e275e633fe 100644 --- a/packages/camera/camera/example/pubspec.yaml +++ b/packages/camera/camera/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: camera: diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 5af8640493c6..5a68f6da8036 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -8,7 +8,7 @@ version: 0.10.0+1 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index 2ae809f9782f..024836d6f43e 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 0.10.0+1 * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/camera/camera_android/example/pubspec.yaml b/packages/camera/camera_android/example/pubspec.yaml index 64e69405041c..5fcc8093d29e 100644 --- a/packages/camera/camera_android/example/pubspec.yaml +++ b/packages/camera/camera_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: camera_android: diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index e7d6aeb51e24..afb40dee33c5 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.10.0+1 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index 897a1759643f..ab33c35dd8da 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 0.9.8+5 * Fixes a regression introduced in 0.9.8+4 where the stream handler is not set. diff --git a/packages/camera/camera_avfoundation/example/pubspec.yaml b/packages/camera/camera_avfoundation/example/pubspec.yaml index 78927fc70d76..55296a6bf4db 100644 --- a/packages/camera/camera_avfoundation/example/pubspec.yaml +++ b/packages/camera/camera_avfoundation/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: camera_avfoundation: diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index c25fe66e4cc7..4b5b67dd8943 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.9.8+5 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index 383fc73e8549..7411db738483 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). * Ignores missing return warnings in preparation for [upcoming analysis changes](https://github.com/flutter/flutter/issues/105750). diff --git a/packages/camera/camera_platform_interface/pubspec.yaml b/packages/camera/camera_platform_interface/pubspec.yaml index 473dcb552c82..1c874725e9ce 100644 --- a/packages/camera/camera_platform_interface/pubspec.yaml +++ b/packages/camera/camera_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 2.2.0 environment: sdk: '>=2.12.0 <3.0.0' - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: cross_file: ^0.3.1 diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index f867ae62c800..c6254ac11d38 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). diff --git a/packages/camera/camera_web/example/pubspec.yaml b/packages/camera/camera_web/example/pubspec.yaml index 441c6eb7988f..78ff61a5f883 100644 --- a/packages/camera/camera_web/example/pubspec.yaml +++ b/packages/camera/camera_web/example/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 68aa79181ce4..527b28367eaf 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.3.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index 2b27fb433505..10b08874092a 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 0.2.1+1 * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/camera/camera_windows/example/pubspec.yaml b/packages/camera/camera_windows/example/pubspec.yaml index aa806a292333..e1d63a3f81e9 100644 --- a/packages/camera/camera_windows/example/pubspec.yaml +++ b/packages/camera/camera_windows/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: 'none' environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: camera_platform_interface: ^2.1.2 diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index 513ca2d61102..403cb459a6f7 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.2.1+1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/espresso/CHANGELOG.md b/packages/espresso/CHANGELOG.md index 8f41a8ddb2ae..1bb1b8e42204 100644 --- a/packages/espresso/CHANGELOG.md +++ b/packages/espresso/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 0.2.0+3 * Bumps okhttp to 4.10.0. diff --git a/packages/espresso/example/pubspec.yaml b/packages/espresso/example/pubspec.yaml index 1e668b518aa0..67f9edcd4644 100644 --- a/packages/espresso/example/pubspec.yaml +++ b/packages/espresso/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/espresso/pubspec.yaml b/packages/espresso/pubspec.yaml index 724d5fed4c33..4c55aafbc063 100644 --- a/packages/espresso/pubspec.yaml +++ b/packages/espresso/pubspec.yaml @@ -7,7 +7,7 @@ version: 0.2.0+3 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index c16bbe51819e..1cecd5605a7e 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 0.9.2 * Adds an endorsed iOS implementation. diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index cd633e73a3c5..556ddf23b3be 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -7,7 +7,7 @@ version: 0.9.2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/file_selector/file_selector_ios/CHANGELOG.md b/packages/file_selector/file_selector_ios/CHANGELOG.md index 0d259e2e8c56..2b2d2f8c2d58 100644 --- a/packages/file_selector/file_selector_ios/CHANGELOG.md +++ b/packages/file_selector/file_selector_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 0.5.0+1 * Updates README for endorsement. diff --git a/packages/file_selector/file_selector_ios/pubspec.yaml b/packages/file_selector/file_selector_ios/pubspec.yaml index 911bcaca069d..b69db86b2950 100644 --- a/packages/file_selector/file_selector_ios/pubspec.yaml +++ b/packages/file_selector/file_selector_ios/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.5.0+1 environment: sdk: ">=2.14.4 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/file_selector/file_selector_linux/CHANGELOG.md b/packages/file_selector/file_selector_linux/CHANGELOG.md index a63a52d5dbe7..d76c635cde8e 100644 --- a/packages/file_selector/file_selector_linux/CHANGELOG.md +++ b/packages/file_selector/file_selector_linux/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 0.9.0 * Moves source to flutter/plugins. diff --git a/packages/file_selector/file_selector_linux/pubspec.yaml b/packages/file_selector/file_selector_linux/pubspec.yaml index 369fd4445e65..e0b26d18e719 100644 --- a/packages/file_selector/file_selector_linux/pubspec.yaml +++ b/packages/file_selector/file_selector_linux/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.9.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/file_selector/file_selector_macos/CHANGELOG.md b/packages/file_selector/file_selector_macos/CHANGELOG.md index 499667d79d59..ec09997a52d2 100644 --- a/packages/file_selector/file_selector_macos/CHANGELOG.md +++ b/packages/file_selector/file_selector_macos/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 0.9.0+1 * Updates README for endorsement. diff --git a/packages/file_selector/file_selector_macos/example/pubspec.yaml b/packages/file_selector/file_selector_macos/example/pubspec.yaml index dbe127282a17..58c5d6e22b49 100644 --- a/packages/file_selector/file_selector_macos/example/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/example/pubspec.yaml @@ -5,7 +5,7 @@ version: 1.0.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: file_selector_macos: diff --git a/packages/file_selector/file_selector_macos/pubspec.yaml b/packages/file_selector/file_selector_macos/pubspec.yaml index 44b842ed4536..34d418490a14 100644 --- a/packages/file_selector/file_selector_macos/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.9.0+1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md index b1fa45b708d5..e6949b09fa3b 100644 --- a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md +++ b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.1.0 * Adds `allowsAny` to `XTypeGroup` as a simple and future-proof way of identifying diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml index 405864414e9a..ae24a4b93b5d 100644 --- a/packages/file_selector/file_selector_platform_interface/pubspec.yaml +++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 2.1.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: cross_file: ^0.3.0 diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md index c4a6ca2f770e..873a76e60a25 100644 --- a/packages/file_selector/file_selector_web/CHANGELOG.md +++ b/packages/file_selector/file_selector_web/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. ## 0.9.0 diff --git a/packages/file_selector/file_selector_web/example/pubspec.yaml b/packages/file_selector/file_selector_web/example/pubspec.yaml index d8b93ee816f3..042f412b816b 100644 --- a/packages/file_selector/file_selector_web/example/pubspec.yaml +++ b/packages/file_selector/file_selector_web/example/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index fc9e9e217b62..c1854aea4a88 100644 --- a/packages/file_selector/file_selector_web/pubspec.yaml +++ b/packages/file_selector/file_selector_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.9.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/file_selector/file_selector_windows/CHANGELOG.md b/packages/file_selector/file_selector_windows/CHANGELOG.md index 1c53df0caae2..e0e6152e9b2f 100644 --- a/packages/file_selector/file_selector_windows/CHANGELOG.md +++ b/packages/file_selector/file_selector_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 0.9.1+2 * Fixes the problem that the initial directory does not work after completing a file selection. diff --git a/packages/file_selector/file_selector_windows/example/pubspec.yaml b/packages/file_selector/file_selector_windows/example/pubspec.yaml index a3e69a6186f8..99c251d0867f 100644 --- a/packages/file_selector/file_selector_windows/example/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/example/pubspec.yaml @@ -5,7 +5,7 @@ version: 1.0.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: file_selector_platform_interface: ^2.0.0 diff --git a/packages/file_selector/file_selector_windows/pubspec.yaml b/packages/file_selector/file_selector_windows/pubspec.yaml index 90751df15664..52b9f97174b4 100644 --- a/packages/file_selector/file_selector_windows/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.9.1+2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md index b86228532d00..81202f8159de 100644 --- a/packages/flutter_plugin_android_lifecycle/CHANGELOG.md +++ b/packages/flutter_plugin_android_lifecycle/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.0.7 * Bumps gradle from 3.5.0 to 7.2.1. diff --git a/packages/flutter_plugin_android_lifecycle/pubspec.yaml b/packages/flutter_plugin_android_lifecycle/pubspec.yaml index 155d6faea5d0..3a6a2e017b53 100644 --- a/packages/flutter_plugin_android_lifecycle/pubspec.yaml +++ b/packages/flutter_plugin_android_lifecycle/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.7 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index e1f963b8e732..be73df702d76 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,3 +1,6 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. ## 2.1.12 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 196f054e1fc0..6736fce066b2 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: cupertino_icons: ^0.1.0 diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index f632e374351b..95c4e98c1dd0 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.1.12 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: 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 38dff3d82470..91f01f8abd15 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.2.0 * Updates `useAndroidViewSurface` to require Hybrid Composition, making the 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 fc1059bbdcd5..778af9a018fa 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 @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: cupertino_icons: ^0.1.0 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 19f2adc82ce6..e3a033f3df07 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates minimum Flutter version to 2.10. * Fixes violations of new analysis option use_named_constants. * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/example/pubspec.yaml index b4fc5db16cfd..d9d8d5899bdd 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: cupertino_icons: ^0.1.0 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 a87096286c7d..f1e613806eb9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.1.11 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: 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 dd32d787bddc..5ef9142beea8 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 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.2.2 * Adds a `size` parameter to `BitmapDescriptor.fromBytes`, so **web** applications 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 9f4b7c03fdb2..160819912a0a 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 @@ -8,7 +8,7 @@ version: 2.2.2 environment: sdk: '>=2.12.0 <3.0.0' - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: collection: ^1.15.0 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 68e6a3f5b0b9..541ac49f0a16 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 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 0.4.0+2 * Updates conversion of `BitmapDescriptor.fromBytes` marker icons to support the 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 82c36e22b15f..8ec25c6bee72 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 @@ -4,7 +4,7 @@ publish_to: none # Tests require flutter beta or greater to run. environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: 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 731b90547638..cb4063e315bc 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.4.0+2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index d63f0a160961..286365ef9601 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 5.4.1 * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/google_sign_in/google_sign_in/example/pubspec.yaml b/packages/google_sign_in/google_sign_in/example/pubspec.yaml index 822f83895cfb..2ade7d324201 100644 --- a/packages/google_sign_in/google_sign_in/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 8286f6bd9b0e..50339902fe75 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/pubspec.yaml @@ -8,7 +8,7 @@ version: 5.4.1 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: 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 5125b22a0963..e3e404fb48b3 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 6.1.0 * Adds override for `GoogleSignIn.initWithParams` to handle new `forceCodeForRefreshToken` parameter. diff --git a/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml index 4d12bbad7987..b7b104c3307e 100644 --- a/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: 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 de5e3f0a8470..941ca2c8d774 100644 --- a/packages/google_sign_in/google_sign_in_android/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_android/pubspec.yaml @@ -6,7 +6,7 @@ version: 6.1.0 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: 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 2b62f8afa0b8..ecb3b6bee039 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 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 5.5.0 * Adds override for `GoogleSignInPlatform.initWithParams`. diff --git a/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml index d17c929a989f..a6c97a5678cb 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: 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 760e3339e32d..24c08cdaa674 100644 --- a/packages/google_sign_in/google_sign_in_ios/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_ios/pubspec.yaml @@ -6,7 +6,7 @@ version: 5.5.0 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md index cc88c07eb00d..01d54b23dae0 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.3.0 * Adopts `plugin_platform_interface`. As a result, `isMock` is deprecated in diff --git a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml index aa9c8f6ff520..0902069364ce 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 2.3.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: 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 12e6d9630f9c..51842e4319a0 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 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 0.10.2 * Migrates to new platform-interface `initWithParams` method. 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 1bdb2f09c465..ea4b1002d79f 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 @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: 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 1dedd6de6666..42413e091e6e 100644 --- a/packages/google_sign_in/google_sign_in_web/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_web/pubspec.yaml @@ -7,7 +7,7 @@ version: 0.10.2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index ececf68e883f..ccf1cb3c5e7d 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. ## 0.8.5+3 diff --git a/packages/image_picker/image_picker/example/pubspec.yaml b/packages/image_picker/image_picker/example/pubspec.yaml index 23c682af3922..b511cde0a554 100755 --- a/packages/image_picker/image_picker/example/pubspec.yaml +++ b/packages/image_picker/image_picker/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index acc085a06bb9..58f9f0658483 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -7,7 +7,7 @@ version: 0.8.5+3 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index 193828041ac9..286efbeb934b 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 0.8.5+2 * Updates `image_picker_platform_interface` constraint to the correct minimum diff --git a/packages/image_picker/image_picker_android/example/pubspec.yaml b/packages/image_picker/image_picker_android/example/pubspec.yaml index b5afb16235db..ac4cb232f02d 100755 --- a/packages/image_picker/image_picker_android/example/pubspec.yaml +++ b/packages/image_picker/image_picker_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index 6ae76878cb6a..341a98ac63db 100755 --- a/packages/image_picker/image_picker_android/pubspec.yaml +++ b/packages/image_picker/image_picker_android/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.8.5+2 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index e9f88a9ccd7a..99d3dbc11121 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates minimum Flutter version to 2.10. * Fixes violations of new analysis option use_named_constants. ## 2.1.8 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 72316ee60988..bb226e0c9c10 100644 --- a/packages/image_picker/image_picker_for_web/example/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/example/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index 508e32aca5bd..e19147b6306e 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.1.8 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index ecaaf773ece5..c1450136b2e6 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 0.8.5+6 * Updates description. diff --git a/packages/image_picker/image_picker_ios/example/pubspec.yaml b/packages/image_picker/image_picker_ios/example/pubspec.yaml index 84fa77e64d70..24b3af031436 100755 --- a/packages/image_picker/image_picker_ios/example/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index 314a52e94510..dc695138dd7f 100755 --- a/packages/image_picker/image_picker_ios/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.8.5+6 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md index ae4a0a371d6d..8defffe95a7a 100644 --- a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md +++ b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. ## 2.6.1 diff --git a/packages/image_picker/image_picker_platform_interface/pubspec.yaml b/packages/image_picker/image_picker_platform_interface/pubspec.yaml index 567914316c1a..f4b745f2c1bf 100644 --- a/packages/image_picker/image_picker_platform_interface/pubspec.yaml +++ b/packages/image_picker/image_picker_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 2.6.1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: cross_file: ^0.3.1+1 diff --git a/packages/image_picker/image_picker_windows/CHANGELOG.md b/packages/image_picker/image_picker_windows/CHANGELOG.md index b8a265568633..2480d6da9ea4 100644 --- a/packages/image_picker/image_picker_windows/CHANGELOG.md +++ b/packages/image_picker/image_picker_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 0.1.0+2 * Minor fixes for new analysis options. diff --git a/packages/image_picker/image_picker_windows/example/pubspec.yaml b/packages/image_picker/image_picker_windows/example/pubspec.yaml index df1dd49327bd..1a48e6ddc14b 100644 --- a/packages/image_picker/image_picker_windows/example/pubspec.yaml +++ b/packages/image_picker/image_picker_windows/example/pubspec.yaml @@ -5,7 +5,7 @@ version: 1.0.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/image_picker/image_picker_windows/pubspec.yaml b/packages/image_picker/image_picker_windows/pubspec.yaml index 3b6fd922cbea..2887b080cef3 100644 --- a/packages/image_picker/image_picker_windows/pubspec.yaml +++ b/packages/image_picker/image_picker_windows/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.1.0+2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 1d4407ea95a2..902c4377e9d6 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 3.0.7 * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml index 9db9d63c3a79..0b4ea28bc7f6 100644 --- a/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index 887910e1cd99..49382c90c01a 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -6,7 +6,7 @@ version: 3.0.7 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: 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 daa487497e25..910c5cdc1fc0 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 0.2.3+3 * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml index 0d37b3df1ee5..69e3ca755993 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: 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 8116de3c283b..320e4b818e83 100644 --- a/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.2.3+3 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md index f7d3268d1cae..f2a76ae55db2 100644 --- a/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates minimum Flutter version to 2.10. * Removes unnecessary imports. ## 1.3.1 diff --git a/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml index ee8d29461d82..e181c8a5ab49 100644 --- a/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 1.3.1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: 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 f0ad4921df94..1982a45e3e99 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 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 0.3.2 * Adds the `identifier` and `type` fields to the `SKProductDiscountWrapper` to reflect the changes in the [SKProductDiscount](https://developer.apple.com/documentation/storekit/skproductdiscount?language=objc) in iOS 12.2. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml index a98e1693aa40..9e4f88b79bc5 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: 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 f131a411baed..69bc6c652c5a 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.3.2 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md index eadf63de9b92..1a28c9b2a550 100644 --- a/packages/ios_platform_images/CHANGELOG.md +++ b/packages/ios_platform_images/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates minimum Flutter version to 2.10. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). ## 0.2.0+9 diff --git a/packages/ios_platform_images/example/pubspec.yaml b/packages/ios_platform_images/example/pubspec.yaml index 10be0d6be998..34bd431e97fc 100644 --- a/packages/ios_platform_images/example/pubspec.yaml +++ b/packages/ios_platform_images/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: cupertino_icons: ^1.0.2 diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml index 64875ecad88b..8b32b39343a7 100644 --- a/packages/ios_platform_images/pubspec.yaml +++ b/packages/ios_platform_images/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.2.0+9 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/local_auth/local_auth/CHANGELOG.md b/packages/local_auth/local_auth/CHANGELOG.md index 5e637d1e8763..34e26efef238 100644 --- a/packages/local_auth/local_auth/CHANGELOG.md +++ b/packages/local_auth/local_auth/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.1.2 * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/local_auth/local_auth/example/pubspec.yaml b/packages/local_auth/local_auth/example/pubspec.yaml index 3819cd1c0f15..14331dab78bf 100644 --- a/packages/local_auth/local_auth/example/pubspec.yaml +++ b/packages/local_auth/local_auth/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/local_auth/local_auth/pubspec.yaml b/packages/local_auth/local_auth/pubspec.yaml index f2598bfdde81..133df06d43b0 100644 --- a/packages/local_auth/local_auth/pubspec.yaml +++ b/packages/local_auth/local_auth/pubspec.yaml @@ -7,7 +7,7 @@ version: 2.1.2 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 2d9e277c608f..942a9948ecc0 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 1.0.11 * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/local_auth/local_auth_android/example/pubspec.yaml b/packages/local_auth/local_auth_android/example/pubspec.yaml index c07a81d2be3b..b1c68d49e555 100644 --- a/packages/local_auth/local_auth_android/example/pubspec.yaml +++ b/packages/local_auth/local_auth_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index 81ee7b4bef07..0bfe680a20a6 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -6,7 +6,7 @@ version: 1.0.11 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index 5972772d6a47..748db7a7e695 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 1.0.9 * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/local_auth/local_auth_ios/example/pubspec.yaml b/packages/local_auth/local_auth_ios/example/pubspec.yaml index f83806b9d08e..52a95010d43b 100644 --- a/packages/local_auth/local_auth_ios/example/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index 0d1b9aef6007..18c39187b411 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -6,7 +6,7 @@ version: 1.0.9 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md index 387a20050ed8..ade26dd62e47 100644 --- a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md +++ b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 1.0.4 * Updates references to the obsolete master branch. diff --git a/packages/local_auth/local_auth_platform_interface/pubspec.yaml b/packages/local_auth/local_auth_platform_interface/pubspec.yaml index a4ad682b363d..27b5cb33c0f5 100644 --- a/packages/local_auth/local_auth_platform_interface/pubspec.yaml +++ b/packages/local_auth/local_auth_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 1.0.4 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/local_auth/local_auth_windows/CHANGELOG.md b/packages/local_auth/local_auth_windows/CHANGELOG.md index 63ce49dd46b7..4e36f75aea1d 100644 --- a/packages/local_auth/local_auth_windows/CHANGELOG.md +++ b/packages/local_auth/local_auth_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 1.0.3 * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/local_auth/local_auth_windows/example/pubspec.yaml b/packages/local_auth/local_auth_windows/example/pubspec.yaml index 266c9fc7140d..664f8623abb7 100644 --- a/packages/local_auth/local_auth_windows/example/pubspec.yaml +++ b/packages/local_auth/local_auth_windows/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/local_auth/local_auth_windows/pubspec.yaml b/packages/local_auth/local_auth_windows/pubspec.yaml index 3fec7348b4ef..7f8d7f631211 100644 --- a/packages/local_auth/local_auth_windows/pubspec.yaml +++ b/packages/local_auth/local_auth_windows/pubspec.yaml @@ -6,7 +6,7 @@ version: 1.0.3 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index 08776093de4d..e19415ff68ac 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. ## 2.0.11 diff --git a/packages/path_provider/path_provider/example/pubspec.yaml b/packages/path_provider/path_provider/example/pubspec.yaml index ea6f499622f9..13be15f0c1a1 100644 --- a/packages/path_provider/path_provider/example/pubspec.yaml +++ b/packages/path_provider/path_provider/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml index 272cb44c5617..8b68e1264fe7 100644 --- a/packages/path_provider/path_provider/pubspec.yaml +++ b/packages/path_provider/path_provider/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.11 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index 63a731acbd91..93f074795649 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.0.20 * Reverts changes in versions 2.0.18 and 2.0.19. diff --git a/packages/path_provider/path_provider_android/example/pubspec.yaml b/packages/path_provider/path_provider_android/example/pubspec.yaml index d546d9f2d729..4ab05835158d 100644 --- a/packages/path_provider/path_provider_android/example/pubspec.yaml +++ b/packages/path_provider/path_provider_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider_android/pubspec.yaml b/packages/path_provider/path_provider_android/pubspec.yaml index c442d16a6889..fba4c32506fe 100644 --- a/packages/path_provider/path_provider_android/pubspec.yaml +++ b/packages/path_provider/path_provider_android/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.20 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.1" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/path_provider/path_provider_ios/CHANGELOG.md b/packages/path_provider/path_provider_ios/CHANGELOG.md index c9deb3f7112d..ffc158c19156 100644 --- a/packages/path_provider/path_provider_ios/CHANGELOG.md +++ b/packages/path_provider/path_provider_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.0.11 * Lower minimim version back to 2.8. diff --git a/packages/path_provider/path_provider_ios/example/pubspec.yaml b/packages/path_provider/path_provider_ios/example/pubspec.yaml index 00ac1f1af3a7..0df5e6141fdf 100644 --- a/packages/path_provider/path_provider_ios/example/pubspec.yaml +++ b/packages/path_provider/path_provider_ios/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider_ios/pubspec.yaml b/packages/path_provider/path_provider_ios/pubspec.yaml index dfa4a87d91d8..2f6171cd70bd 100644 --- a/packages/path_provider/path_provider_ios/pubspec.yaml +++ b/packages/path_provider/path_provider_ios/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.11 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/path_provider/path_provider_linux/CHANGELOG.md b/packages/path_provider/path_provider_linux/CHANGELOG.md index b69ec900d614..baf3283348de 100644 --- a/packages/path_provider/path_provider_linux/CHANGELOG.md +++ b/packages/path_provider/path_provider_linux/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.1.7 * Bumps ffi dependency to match path_provider_windows. diff --git a/packages/path_provider/path_provider_linux/example/pubspec.yaml b/packages/path_provider/path_provider_linux/example/pubspec.yaml index 47ed4be220a6..8d8940ba2f05 100644 --- a/packages/path_provider/path_provider_linux/example/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: "none" environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider_linux/pubspec.yaml b/packages/path_provider/path_provider_linux/pubspec.yaml index c0b7954087f5..41d587360b5e 100644 --- a/packages/path_provider/path_provider_linux/pubspec.yaml +++ b/packages/path_provider/path_provider_linux/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.1.7 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/path_provider/path_provider_macos/CHANGELOG.md b/packages/path_provider/path_provider_macos/CHANGELOG.md index c59ba971d461..61727e4d364b 100644 --- a/packages/path_provider/path_provider_macos/CHANGELOG.md +++ b/packages/path_provider/path_provider_macos/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.0.6 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/path_provider/path_provider_macos/example/pubspec.yaml b/packages/path_provider/path_provider_macos/example/pubspec.yaml index 65ecf3ab0423..f5bd2ba024b5 100644 --- a/packages/path_provider/path_provider_macos/example/pubspec.yaml +++ b/packages/path_provider/path_provider_macos/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider_macos/pubspec.yaml b/packages/path_provider/path_provider_macos/pubspec.yaml index 4381041079b5..289e13103ddd 100644 --- a/packages/path_provider/path_provider_macos/pubspec.yaml +++ b/packages/path_provider/path_provider_macos/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.6 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/path_provider/path_provider_platform_interface/CHANGELOG.md b/packages/path_provider/path_provider_platform_interface/CHANGELOG.md index 4eea4b36ba8a..6baf950a397a 100644 --- a/packages/path_provider/path_provider_platform_interface/CHANGELOG.md +++ b/packages/path_provider/path_provider_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.0.4 * Minor fixes for new analysis options. diff --git a/packages/path_provider/path_provider_platform_interface/pubspec.yaml b/packages/path_provider/path_provider_platform_interface/pubspec.yaml index 92ec432dc394..ef16037de71f 100644 --- a/packages/path_provider/path_provider_platform_interface/pubspec.yaml +++ b/packages/path_provider/path_provider_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/path_provider/path_provider_windows/CHANGELOG.md b/packages/path_provider/path_provider_windows/CHANGELOG.md index ebf42b73fb04..9f42b3d9471b 100644 --- a/packages/path_provider/path_provider_windows/CHANGELOG.md +++ b/packages/path_provider/path_provider_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.1.2 * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/path_provider/path_provider_windows/example/pubspec.yaml b/packages/path_provider/path_provider_windows/example/pubspec.yaml index d48219648b30..c7a12cae4573 100644 --- a/packages/path_provider/path_provider_windows/example/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index d7703a85a548..170212045b94 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates minimum Flutter version to 2.10. * Minor fixes for new analysis options. ## 0.6.0+11 diff --git a/packages/quick_actions/quick_actions/example/pubspec.yaml b/packages/quick_actions/quick_actions/example/pubspec.yaml index 79ab57cd4fc8..3b197c219109 100644 --- a/packages/quick_actions/quick_actions/example/pubspec.yaml +++ b/packages/quick_actions/quick_actions/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/quick_actions/quick_actions/pubspec.yaml b/packages/quick_actions/quick_actions/pubspec.yaml index 37e8dbe5f3e3..61e0a16957a5 100644 --- a/packages/quick_actions/quick_actions/pubspec.yaml +++ b/packages/quick_actions/quick_actions/pubspec.yaml @@ -7,7 +7,7 @@ version: 0.6.0+11 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/quick_actions/quick_actions_android/CHANGELOG.md b/packages/quick_actions/quick_actions_android/CHANGELOG.md index fff0b8d38149..e4aa75dbf2b3 100644 --- a/packages/quick_actions/quick_actions_android/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_android/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates minimum Flutter version to 2.10. * Updates mockito-core to 4.6.1. * Removes deprecated FieldSetter from QuickActionsTest. diff --git a/packages/quick_actions/quick_actions_android/example/pubspec.yaml b/packages/quick_actions/quick_actions_android/example/pubspec.yaml index 53170971d136..6ff13f063c5f 100644 --- a/packages/quick_actions/quick_actions_android/example/pubspec.yaml +++ b/packages/quick_actions/quick_actions_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.15.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/quick_actions/quick_actions_android/pubspec.yaml b/packages/quick_actions/quick_actions_android/pubspec.yaml index 836baae8e368..6398b0715cf8 100644 --- a/packages/quick_actions/quick_actions_android/pubspec.yaml +++ b/packages/quick_actions/quick_actions_android/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.6.2 environment: sdk: ">=2.15.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/quick_actions/quick_actions_ios/CHANGELOG.md b/packages/quick_actions/quick_actions_ios/CHANGELOG.md index 8f4d4d99c2c3..97ba2cfdd36b 100644 --- a/packages/quick_actions/quick_actions_ios/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 0.6.0+14 * Refactors `FLTQuickActionsPlugin` class into multiple components. diff --git a/packages/quick_actions/quick_actions_ios/example/pubspec.yaml b/packages/quick_actions/quick_actions_ios/example/pubspec.yaml index 49c6b5e89ed4..f93e162e7fa9 100644 --- a/packages/quick_actions/quick_actions_ios/example/pubspec.yaml +++ b/packages/quick_actions/quick_actions_ios/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.15.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/quick_actions/quick_actions_ios/pubspec.yaml b/packages/quick_actions/quick_actions_ios/pubspec.yaml index 77c2a20414a7..a2f415982439 100644 --- a/packages/quick_actions/quick_actions_ios/pubspec.yaml +++ b/packages/quick_actions/quick_actions_ios/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.6.0+14 environment: sdk: ">=2.15.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: @@ -27,4 +27,4 @@ dev_dependencies: integration_test: sdk: flutter plugin_platform_interface: ^2.1.2 - \ No newline at end of file + diff --git a/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md b/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md index ad959de03be8..26c57ca3be27 100644 --- a/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 1.0.2 * Removes dependency on `meta`. diff --git a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml index 5303f8b90958..aa331dc54544 100644 --- a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml +++ b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 1.0.2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index 4bf0f6a6b144..d7c3aa49e22e 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.0.15 * Minor fixes for new analysis options. diff --git a/packages/shared_preferences/shared_preferences/example/pubspec.yaml b/packages/shared_preferences/shared_preferences/example/pubspec.yaml index fd22f13172f8..35eb6f159bcd 100644 --- a/packages/shared_preferences/shared_preferences/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index d5a9109834ea..7a310d9a584a 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -7,7 +7,7 @@ version: 2.0.15 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index 51e99ec6d3d5..57de5e712777 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.0.12 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml index 1ea27cb381be..2838b262885e 100644 --- a/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_android/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/pubspec.yaml index 2d8cc88d3703..8b6fa3af1590 100644 --- a/packages/shared_preferences/shared_preferences_android/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.12 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md index 29ade8d496a1..99dde9c462b7 100644 --- a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.1.1 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml index fb7b1e7316f5..e3db3c348946 100644 --- a/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml index a8bde2e9f87f..45b1aae2c473 100644 --- a/packages/shared_preferences/shared_preferences_ios/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_ios/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.1.1 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md index f0cb8322f385..60f93f31be3c 100644 --- a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.1.1 * Removes unnecessary imports. diff --git a/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml index 1b3772f0eeea..65a61112e588 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml index 922437256748..88889dcde5fd 100644 --- a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.1.1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md index 8ba116a74fe0..bc4e1504be1c 100644 --- a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.0.4 * Removes unnecessary imports. diff --git a/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml index 64d13fe31a5f..96d8b5f0ba2e 100644 --- a/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml index 9259ef5888fa..77f5f11f0525 100644 --- a/packages/shared_preferences/shared_preferences_macos/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_macos/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/shared_preferences/shared_preferences_platform_interface/CHANGELOG.md b/packages/shared_preferences/shared_preferences_platform_interface/CHANGELOG.md index 12d61329171f..3ef89c396222 100644 --- a/packages/shared_preferences/shared_preferences_platform_interface/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.1.0 * Adopts `plugin_platform_interface`. As a result, `isMock` is deprecated in diff --git a/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml b/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml index 7e4b9f0699fc..b55eb1ccceb2 100644 --- a/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.1.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md index 9ea249034105..8c8411da6fff 100644 --- a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.0.4 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml index 656fdeb01876..5e637144a257 100644 --- a/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_web/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/pubspec.yaml index cfe712999910..b64f37d10da6 100644 --- a/packages/shared_preferences/shared_preferences_web/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.4 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md index f79f9e3d5d39..b8351c8795d1 100644 --- a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.1.1 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml index 7bca1fb4ce45..b1f6db4f67c7 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml index 57e086b81ed3..032e0fb213df 100644 --- a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.1.1 environment: sdk: '>=2.12.0 <3.0.0' - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 39b2e663f6b1..0b590d5deaad 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. ## 6.1.5 diff --git a/packages/url_launcher/url_launcher/example/pubspec.yaml b/packages/url_launcher/url_launcher/example/pubspec.yaml index 673785e9e1f6..77af2dc2b65a 100644 --- a/packages/url_launcher/url_launcher/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 03d0846bf173..ff21c6360e10 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -7,7 +7,7 @@ version: 6.1.5 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md index 887178c479e4..d3655fa032bd 100644 --- a/packages/url_launcher/url_launcher_android/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 6.0.17 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/url_launcher/url_launcher_android/example/pubspec.yaml b/packages/url_launcher/url_launcher_android/example/pubspec.yaml index cdb19458ba07..52d761baa120 100644 --- a/packages/url_launcher/url_launcher_android/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index 3c80170f1422..d07005406769 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -6,7 +6,7 @@ version: 6.0.17 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/url_launcher/url_launcher_ios/CHANGELOG.md b/packages/url_launcher/url_launcher_ios/CHANGELOG.md index 5fc00bff486e..cf018da4f59d 100644 --- a/packages/url_launcher/url_launcher_ios/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 6.0.17 * Suppresses warnings for pre-iOS-13 codepaths. diff --git a/packages/url_launcher/url_launcher_ios/example/pubspec.yaml b/packages/url_launcher/url_launcher_ios/example/pubspec.yaml index 2e39e92d5638..84748f17a781 100644 --- a/packages/url_launcher/url_launcher_ios/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_ios/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_ios/pubspec.yaml b/packages/url_launcher/url_launcher_ios/pubspec.yaml index 9bb1616441b3..5e06a80b4cfe 100644 --- a/packages/url_launcher/url_launcher_ios/pubspec.yaml +++ b/packages/url_launcher/url_launcher_ios/pubspec.yaml @@ -6,7 +6,7 @@ version: 6.0.17 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/url_launcher/url_launcher_linux/CHANGELOG.md b/packages/url_launcher/url_launcher_linux/CHANGELOG.md index 27c18a66805b..69d8b24ddf6f 100644 --- a/packages/url_launcher/url_launcher_linux/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_linux/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 3.0.1 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/url_launcher/url_launcher_linux/example/pubspec.yaml b/packages/url_launcher/url_launcher_linux/example/pubspec.yaml index 90ea19dd2a04..0d5357055e96 100644 --- a/packages/url_launcher/url_launcher_linux/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_linux/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_linux/pubspec.yaml b/packages/url_launcher/url_launcher_linux/pubspec.yaml index 0bbd4b590cd2..99b237506c60 100644 --- a/packages/url_launcher/url_launcher_linux/pubspec.yaml +++ b/packages/url_launcher/url_launcher_linux/pubspec.yaml @@ -6,7 +6,7 @@ version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/url_launcher/url_launcher_macos/CHANGELOG.md b/packages/url_launcher/url_launcher_macos/CHANGELOG.md index 2fa5e918eadd..7386ecced865 100644 --- a/packages/url_launcher/url_launcher_macos/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_macos/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 3.0.1 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/url_launcher/url_launcher_macos/example/pubspec.yaml b/packages/url_launcher/url_launcher_macos/example/pubspec.yaml index 2652df03448a..efa4f63bd48f 100644 --- a/packages/url_launcher/url_launcher_macos/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_macos/pubspec.yaml b/packages/url_launcher/url_launcher_macos/pubspec.yaml index 8f93e57c9dc4..eaf210a367b6 100644 --- a/packages/url_launcher/url_launcher_macos/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/pubspec.yaml @@ -6,7 +6,7 @@ version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md b/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md index 78818eff7bbf..fcf362ceb7d9 100644 --- a/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.1.0 * Adds a new `launchUrl` method corresponding to the new app-facing interface. diff --git a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml index 76461ff7b979..94ee1ecb29a4 100644 --- a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml +++ b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 2.1.0 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md index 01d739238d40..5454338bde51 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.0.13 * Updates `url_launcher_platform_interface` constraint to the correct minimum diff --git a/packages/url_launcher/url_launcher_web/example/pubspec.yaml b/packages/url_launcher/url_launcher_web/example/pubspec.yaml index a25f4ce148e9..15bdc70517d8 100644 --- a/packages/url_launcher/url_launcher_web/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/example/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_windows/CHANGELOG.md b/packages/url_launcher/url_launcher_windows/CHANGELOG.md index 3ff14fd2f18a..a5952feb4978 100644 --- a/packages/url_launcher/url_launcher_windows/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 3.0.1 * Fixes library_private_types_in_public_api, sort_child_properties_last and use_key_in_widget_constructors diff --git a/packages/url_launcher/url_launcher_windows/example/pubspec.yaml b/packages/url_launcher/url_launcher_windows/example/pubspec.yaml index 22b524df2488..3bd8492709af 100644 --- a/packages/url_launcher/url_launcher_windows/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml index 2717e3807e21..b35b62e1d82e 100644 --- a/packages/url_launcher/url_launcher_windows/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml @@ -6,7 +6,7 @@ version: 3.0.1 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index db8aa564b2d2..7986d1a28625 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates minimum Flutter version to 2.10. * Fixes violations of new analysis option use_named_constants. ## 2.3.9 diff --git a/packages/video_player/video_player_android/example/pubspec.yaml b/packages/video_player/video_player_android/example/pubspec.yaml index 14c06f4123d4..d935244ed924 100644 --- a/packages/video_player/video_player_android/example/pubspec.yaml +++ b/packages/video_player/video_player_android/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/video_player/video_player_android/pubspec.yaml b/packages/video_player/video_player_android/pubspec.yaml index 9039a2d8e90f..1df1deeebf22 100644 --- a/packages/video_player/video_player_android/pubspec.yaml +++ b/packages/video_player/video_player_android/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.3.9 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 19088a884fae..3c9cc2d371fd 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates minimum Flutter version to 2.10. * Fixes violations of new analysis option use_named_constants. * Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). diff --git a/packages/video_player/video_player_avfoundation/example/pubspec.yaml b/packages/video_player/video_player_avfoundation/example/pubspec.yaml index 40b88d577ce6..9123b15aa721 100644 --- a/packages/video_player/video_player_avfoundation/example/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index c00f4baa1f0a..06042c34bad6 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.3.5 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index c14f73ff8bd4..89e3a05b20e4 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates minimum Flutter version to 2.10. * Fixes violations of new analysis option use_named_constants. ## 5.1.4 diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml index 365709a85985..965641d2ee75 100644 --- a/packages/video_player/video_player_platform_interface/pubspec.yaml +++ b/packages/video_player/video_player_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 5.1.4 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index 3ea526e4d25e..52cfd319349b 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 2.0.12 * Updates the `README` with: diff --git a/packages/video_player/video_player_web/example/pubspec.yaml b/packages/video_player/video_player_web/example/pubspec.yaml index abd299a6c1b3..9c0a9e188daf 100644 --- a/packages/video_player/video_player_web/example/pubspec.yaml +++ b/packages/video_player/video_player_web/example/pubspec.yaml @@ -3,7 +3,7 @@ publish_to: none environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index e3cbbabd482b..ad6c9bfa198f 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 2.0.12 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 50e3a05006ca..73dd7ced0279 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). * Updates references to the obsolete master branch. diff --git a/packages/webview_flutter/webview_flutter/example/pubspec.yaml b/packages/webview_flutter/webview_flutter/example/pubspec.yaml index 7ff55bccf432..7af8b2f9dcc4 100644 --- a/packages/webview_flutter/webview_flutter/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 5c9363e42acc..996f6478b800 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -6,7 +6,7 @@ version: 3.0.4 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index 12792ace964f..b16823197fee 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 1.9.2 * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml index d0fffe830798..f2fc9aa3f450 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml @@ -8,7 +8,7 @@ version: 1.9.2 environment: sdk: ">=2.12.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" dependencies: flutter: diff --git a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md index 631608689a7a..82be36f7d094 100644 --- a/packages/webview_flutter/webview_flutter_web/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates minimum Flutter version to 2.10. + ## 0.1.0+4 * Fixes incorrect escaping of some characters when setting the HTML to the iframe element. diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml index 12bec7242519..8f1b523be555 100644 --- a/packages/webview_flutter/webview_flutter_web/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/pubspec.yaml @@ -6,7 +6,7 @@ version: 0.1.0+4 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.8.0" + flutter: ">=2.10.0" flutter: plugin: From ec479864470fb706beaad3f88ca57a75351cc306 Mon Sep 17 00:00:00 2001 From: godofredoc Date: Tue, 30 Aug 2022 15:54:04 -0700 Subject: [PATCH 667/844] Upload firebase test result to a buket in flutter-cirrus. (#6337) --- .../tool/lib/src/firebase_test_lab_command.dart | 2 +- .../test/firebase_test_lab_command_test.dart | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/script/tool/lib/src/firebase_test_lab_command.dart b/script/tool/lib/src/firebase_test_lab_command.dart index 4fc19e30014c..a11284411908 100644 --- a/script/tool/lib/src/firebase_test_lab_command.dart +++ b/script/tool/lib/src/firebase_test_lab_command.dart @@ -59,7 +59,7 @@ class FirebaseTestLabCommand extends PackageLoopingCommand { help: 'Device model(s) to test. See https://cloud.google.com/sdk/gcloud/reference/firebase/test/android/run for more info'); argParser.addOption('results-bucket', - defaultsTo: 'gs://flutter_firebase_testlab_staging'); + defaultsTo: 'gs://flutter_cirrus_testlab'); argParser.addOption( kEnableExperiment, defaultsTo: '', diff --git a/script/tool/test/firebase_test_lab_command_test.dart b/script/tool/test/firebase_test_lab_command_test.dart index 1ab7055d2878..2d3175e171e0 100644 --- a/script/tool/test/firebase_test_lab_command_test.dart +++ b/script/tool/test/firebase_test_lab_command_test.dart @@ -174,7 +174,7 @@ public class MainActivityTest { '/packages/plugin1/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab_staging --results-dir=plugins_android_test/plugin1/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_cirrus_testlab --results-dir=plugins_android_test/plugin1/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin1/example'), ProcessCall( @@ -188,7 +188,7 @@ public class MainActivityTest { '/packages/plugin2/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab_staging --results-dir=plugins_android_test/plugin2/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_cirrus_testlab --results-dir=plugins_android_test/plugin2/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin2/example'), ]), @@ -255,7 +255,7 @@ public class MainActivityTest { '/packages/plugin/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab_staging --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_cirrus_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin/example'), ProcessCall( @@ -265,7 +265,7 @@ public class MainActivityTest { '/packages/plugin/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab_staging --results-dir=plugins_android_test/plugin/buildId/testRunId/example/1/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_cirrus_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example/1/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin/example'), ]), @@ -320,7 +320,7 @@ public class MainActivityTest { '/packages/plugin/example/example1/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab_staging --results-dir=plugins_android_test/plugin/buildId/testRunId/example1/0/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_cirrus_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example1/0/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin/example/example1'), ProcessCall( @@ -330,7 +330,7 @@ public class MainActivityTest { '/packages/plugin/example/example2/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab_staging --results-dir=plugins_android_test/plugin/buildId/testRunId/example2/0/ --device model=redfin,version=30 --device model=seoul,version=26' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_cirrus_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example2/0/ --device model=redfin,version=30 --device model=seoul,version=26' .split(' '), '/packages/plugin/example/example2'), ]), @@ -613,7 +613,7 @@ public class MainActivityTest { '/packages/plugin/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab_staging --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_cirrus_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30' .split(' '), '/packages/plugin/example'), ]), @@ -785,7 +785,7 @@ public class MainActivityTest { '/packages/plugin/example/android'), ProcessCall( 'gcloud', - 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_firebase_testlab_staging --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30' + 'firebase test android run --type instrumentation --app build/app/outputs/apk/debug/app-debug.apk --test build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk --timeout 7m --results-bucket=gs://flutter_cirrus_testlab --results-dir=plugins_android_test/plugin/buildId/testRunId/example/0/ --device model=redfin,version=30' .split(' '), '/packages/plugin/example'), ]), From 35372527c70ed0ae4cc5392330f53ede9774d88a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Aug 2022 01:43:23 +0000 Subject: [PATCH 668/844] [gh_actions]: Bump github/codeql-action from 2.1.19 to 2.1.21 (#6328) --- .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 a299a8dc9d0d..725eac6d6400 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -48,6 +48,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@f5d217be74900c6ac8fbbe53f3c10376ba4e64da + uses: github/codeql-action/upload-sarif@c7f292ea4f542c473194b33813ccd4c207a6c725 with: sarif_file: results.sarif From 0e3cefcd6988fde039b887c0508417a8b4ff63fd Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Wed, 31 Aug 2022 08:20:22 -0400 Subject: [PATCH 669/844] [webview_flutter_platform_interface] Fix build params (#6304) --- .../CHANGELOG.md | 3 +- .../lib/v4/src/platform_webview_widget.dart | 75 +------------------ ...atform_webview_widget_creation_params.dart | 35 +++++---- .../pubspec.yaml | 2 +- .../src/v4/platform_webview_widget_test.dart | 2 +- 5 files changed, 25 insertions(+), 92 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index b16823197fee..17b86523f29a 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 1.9.3 * Updates minimum Flutter version to 2.10. +* Removes `BuildParams` from v4 interface and adds `layoutDirection` to the creation params. ## 1.9.2 diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_widget.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_widget.dart index 6d73162e6c4a..40334c650b3a 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/platform_webview_widget.dart @@ -2,11 +2,8 @@ // 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/widgets.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:webview_flutter_platform_interface/v4/src/platform_webview_controller.dart'; import 'webview_platform.dart'; @@ -36,75 +33,5 @@ abstract class PlatformWebViewWidget extends PlatformInterface { /// Builds a new WebView. /// /// Returns a Widget tree that embeds the created web view. - Widget build(BuildParams params); -} - -/// Describes the parameters necessary for displaying the platform WebView. -/// -/// Platform specific implementations can add additional fields by extending -/// this class. -/// -/// {@tool sample} -/// This example demonstrates how to extend the [BuildParams] to provide -/// additional platform specific parameters. -/// -/// When extending [BuildParams], additional parameters should always accept -/// `null` or have a default value to prevent breaking changes. -/// -/// ```dart -/// @immutable -/// class WebKitBuildParams extends BuildParams { -/// WebKitBuildParams( -/// super.context, { -/// required super.controller, -/// super.layoutDirection, -/// super.gestureRecognizers, -/// this.platformSpecificFieldExample, -/// }); -/// -/// WebKitBuildParams.fromBuildParams( -/// BuildParams params, { -/// Object? platformSpecificFieldExample, -/// }) : this( -/// params.context, -/// controller: params.controller, -/// layoutDirection: params.layoutDirection, -/// gestureRecognizers: params.gestureRecognizers, -/// platformSpecificFieldExample: platformSpecificFieldExample, -/// ); -/// -/// final Object? platformSpecificFieldExample; -/// } -/// ``` -/// {@end-tool} -@immutable -class BuildParams { - /// Constructs a [BuildParams]. - const BuildParams( - this.context, { - required this.controller, - this.layoutDirection = TextDirection.ltr, - this.gestureRecognizers = const >{}, - }); - - /// Describes the part of the user interface represented by the returned - /// widget. - final BuildContext context; - - /// Controls the embedded WebView for the current platform. - final PlatformWebViewController controller; - - /// The layout direction to use for the embedded WebView. - final TextDirection layoutDirection; - - /// Specifies which gestures should be consumed by the web view. - /// - /// It is possible for other gesture recognizers to be competing with the web - /// view on pointer events, e.g if the web view is inside a [ListView] the - /// [ListView] will want to handle vertical drags. The web view will claim - /// gestures that are recognized by any of the recognizers on this list. - /// - /// When this is empty, the web view will only handle pointer events for - /// gestures that were not claimed by any other gesture recognizer. - final Set> gestureRecognizers; + Widget build(BuildContext context); } diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_widget_creation_params.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_widget_creation_params.dart index 1812d7e39c29..83a73c2a44a3 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_widget_creation_params.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/v4/src/types/platform_webview_widget_creation_params.dart @@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; +import 'package:flutter/painting.dart'; import '../platform_webview_controller.dart'; @@ -21,26 +22,26 @@ import '../platform_webview_controller.dart'; /// changes. /// /// ```dart -/// class WKWebViewWidgetCreationParams extends PlatformWebViewWidgetCreationParams { -/// WKWebViewWidgetCreationParams._( -/// // This parameter prevents breaking changes later. -/// // ignore: avoid_unused_constructor_parameters +/// class AndroidWebViewWidgetCreationParams +/// extends PlatformWebViewWidgetCreationParams { +/// AndroidWebViewWidgetCreationParams({ +/// super.key, +/// super.layoutDirection, +/// super.gestureRecognizers, +/// this.platformSpecificFieldExample, +/// }); +/// +/// WKWebViewWidgetCreationParams.fromPlatformWebViewWidgetCreationParams( /// PlatformWebViewWidgetCreationParams params, { -/// this.domain, -/// }) : super( +/// Object? platformSpecificFieldExample, +/// }) : this( /// key: params.key, -/// controller: params.controller, +/// layoutDirection: params.layoutDirection, /// gestureRecognizers: params.gestureRecognizers, +/// platformSpecificFieldExample: platformSpecificFieldExample, /// ); /// -/// factory WKWebViewWidgetCreationParams.fromPlatformWebViewWidgetCreationParams( -/// PlatformWebViewWidgetCreationParams params, { -/// String? domain, -/// }) { -/// return WKWebViewWidgetCreationParams._(params, domain: domain); -/// } -/// -/// final String? domain; +/// final Object? platformSpecificFieldExample; /// } /// ``` /// {@end-tool} @@ -50,6 +51,7 @@ class PlatformWebViewWidgetCreationParams { const PlatformWebViewWidgetCreationParams({ this.key, required this.controller, + this.layoutDirection = TextDirection.ltr, this.gestureRecognizers = const >{}, }); @@ -64,6 +66,9 @@ class PlatformWebViewWidgetCreationParams { /// view. final PlatformWebViewController controller; + /// The layout direction to use for the embedded WebView. + final TextDirection layoutDirection; + /// The `gestureRecognizers` specifies which gestures should be consumed by the /// web view. /// diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml index f2fc9aa3f450..5b98154998a8 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutte issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview_flutter%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.9.2 +version: 1.9.3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.dart index ffa759072e5a..ede16c162413 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/v4/platform_webview_widget_test.dart @@ -82,7 +82,7 @@ class ExtendsWebViewWidgetDelegate extends PlatformWebViewWidget { : super.implementation(params); @override - Widget build(BuildParams params) { + Widget build(BuildContext context) { throw UnimplementedError( 'build is not implemented for ExtendedWebViewWidgetDelegate.'); } From ef2079182960fc2aebc2a0153b85b29414dd130d Mon Sep 17 00:00:00 2001 From: Piotr Mitkowski Date: Wed, 31 Aug 2022 15:55:05 +0200 Subject: [PATCH 670/844] [image_picker] add requestFullMetadata for iOS (optional permissions) - iOS changes (#5713) --- .../image_picker_ios/CHANGELOG.md | 3 +- .../ios/RunnerTests/ImagePickerPluginTests.m | 59 +- .../image_picker_ios/example/pubspec.yaml | 2 +- .../ios/Classes/FLTImagePickerPlugin.m | 29 +- .../ios/Classes/FLTImagePickerPlugin_Test.h | 3 + .../image_picker_ios/ios/Classes/messages.g.h | 4 +- .../image_picker_ios/ios/Classes/messages.g.m | 16 +- .../lib/image_picker_ios.dart | 90 ++- .../image_picker_ios/lib/src/messages.g.dart | 23 +- .../image_picker_ios/pigeons/messages.dart | 11 +- .../image_picker_ios/pubspec.yaml | 4 +- .../test/image_picker_ios_test.dart | 561 +++++++++++++++++- .../image_picker_ios/test/test_api.dart | 24 +- 13 files changed, 737 insertions(+), 92 deletions(-) diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index c1450136b2e6..33e2b61b0e88 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.8.6 +* Adds `requestFullMetadata` option to `pickImage`, so images on iOS can be picked without `Photo Library Usage` permission. * Updates minimum Flutter version to 2.10. ## 0.8.5+6 diff --git a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m index 04d491131d5b..320582b0f8a3 100644 --- a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m @@ -46,7 +46,7 @@ - (void)testPluginPickImageDeviceBack { .andReturn(AVAuthorizationStatusAuthorized); // Run test - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; @@ -54,6 +54,7 @@ - (void)testPluginPickImageDeviceBack { camera:FLTSourceCameraRear] maxSize:[[FLTMaxSize alloc] init] quality:nil + fullMetadata:@(YES) completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ }]; @@ -78,7 +79,7 @@ - (void)testPluginPickImageDeviceFront { .andReturn(AVAuthorizationStatusAuthorized); // Run test - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; @@ -86,6 +87,7 @@ - (void)testPluginPickImageDeviceFront { camera:FLTSourceCameraFront] maxSize:[[FLTMaxSize alloc] init] quality:nil + fullMetadata:@(YES) completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ }]; @@ -110,7 +112,7 @@ - (void)testPluginPickVideoDeviceBack { .andReturn(AVAuthorizationStatusAuthorized); // Run test - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; @@ -142,7 +144,7 @@ - (void)testPluginPickVideoDeviceFront { .andReturn(AVAuthorizationStatusAuthorized); // Run test - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; @@ -165,11 +167,12 @@ - (void)testPickMultiImageShouldUseUIImagePickerControllerOnPreiOS14 { OCMStub(ClassMethod([photoLibrary authorizationStatus])) .andReturn(PHAuthorizationStatusAuthorized); - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; [plugin setImagePickerControllerOverrides:@[ mockUIImagePicker ]]; [plugin pickMultiImageWithMaxSize:[FLTMaxSize makeWithWidth:@(100) height:@(200)] quality:@(50) + fullMetadata:@(YES) completion:^(NSArray *_Nullable result, FlutterError *_Nullable error){ }]; @@ -177,13 +180,48 @@ - (void)testPickMultiImageShouldUseUIImagePickerControllerOnPreiOS14 { [mockUIImagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary]); } +- (void)testPickImageWithoutFullMetadata API_AVAILABLE(ios(11)) { + id mockUIImagePicker = OCMClassMock([UIImagePickerController class]); + id photoLibrary = OCMClassMock([PHPhotoLibrary class]); + + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; + [plugin setImagePickerControllerOverrides:@[ mockUIImagePicker ]]; + + [plugin pickImageWithSource:[FLTSourceSpecification makeWithType:FLTSourceTypeGallery + camera:FLTSourceCameraFront] + maxSize:[[FLTMaxSize alloc] init] + quality:nil + fullMetadata:@(NO) + completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ + }]; + + OCMVerify(times(0), [photoLibrary authorizationStatus]); +} + +- (void)testPickMultiImageWithoutFullMetadata API_AVAILABLE(ios(11)) { + id mockUIImagePicker = OCMClassMock([UIImagePickerController class]); + id photoLibrary = OCMClassMock([PHPhotoLibrary class]); + + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; + [plugin setImagePickerControllerOverrides:@[ mockUIImagePicker ]]; + + [plugin pickMultiImageWithMaxSize:[[FLTMaxSize alloc] init] + quality:nil + fullMetadata:@(NO) + completion:^(NSArray *_Nullable result, + FlutterError *_Nullable error){ + }]; + + OCMVerify(times(0), [photoLibrary authorizationStatus]); +} + #pragma mark - Test camera devices, no op on simulators - (void)testPluginPickImageDeviceCancelClickMultipleTimes { if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { return; } - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; plugin.imagePickerControllerOverrides = @[ controller ]; @@ -191,6 +229,7 @@ - (void)testPluginPickImageDeviceCancelClickMultipleTimes { camera:FLTSourceCameraRear] maxSize:[[FLTMaxSize alloc] init] quality:nil + fullMetadata:@(YES) completion:^(NSString *_Nullable result, FlutterError *_Nullable error){ }]; @@ -202,7 +241,7 @@ - (void)testPluginPickImageDeviceCancelClickMultipleTimes { #pragma mark - Test video duration - (void)testPickingVideoWithDuration { - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; UIImagePickerController *controller = [[UIImagePickerController alloc] init]; [plugin setImagePickerControllerOverrides:@[ controller ]]; @@ -223,12 +262,12 @@ - (void)testViewController { UIViewController *vc2 = [UIViewController new]; vc1.mockPresented = vc2; - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; XCTAssertEqual([plugin viewControllerWithWindow:window], vc2); } - (void)testPluginMultiImagePathHasNullItem { - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; dispatch_semaphore_t resultSemaphore = dispatch_semaphore_create(0); __block FlutterError *pickImageResult = nil; @@ -245,7 +284,7 @@ - (void)testPluginMultiImagePathHasNullItem { } - (void)testPluginMultiImagePathHasItem { - FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init]; NSArray *pathList = @[ @"test" ]; dispatch_semaphore_t resultSemaphore = dispatch_semaphore_create(0); diff --git a/packages/image_picker/image_picker_ios/example/pubspec.yaml b/packages/image_picker/image_picker_ios/example/pubspec.yaml index 24b3af031436..bca58a553682 100755 --- a/packages/image_picker/image_picker_ios/example/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/example/pubspec.yaml @@ -16,7 +16,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: ../ - image_picker_platform_interface: ^2.3.0 + image_picker_platform_interface: ^2.6.1 video_player: ^2.1.4 dev_dependencies: 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 18d4ad2f054c..fa1bb6650501 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m @@ -56,7 +56,7 @@ @interface FLTImagePickerPlugin () *)registrar { - FLTImagePickerPlugin *instance = [FLTImagePickerPlugin new]; + FLTImagePickerPlugin *instance = [[FLTImagePickerPlugin alloc] init]; FLTImagePickerApiSetup(registrar.messenger, instance); } @@ -119,7 +119,11 @@ - (void)launchPHPickerWithContext:(nonnull FLTImagePickerMethodCallContext *)con _pickerViewController.presentationController.delegate = self; self.callContext = context; - [self checkPhotoAuthorizationForAccessLevel]; + if (context.requestFullMetadata) { + [self checkPhotoAuthorizationForAccessLevel]; + } else { + [self showPhotoLibraryWithPHPicker:_pickerViewController]; + } } - (void)launchUIImagePickerWithSource:(nonnull FLTSourceSpecification *)source @@ -136,7 +140,16 @@ - (void)launchUIImagePickerWithSource:(nonnull FLTSourceSpecification *)source camera:[self cameraDeviceForSource:source]]; break; case FLTSourceTypeGallery: - [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; + if (@available(iOS 11, *)) { + if (context.requestFullMetadata) { + [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; + } else { + [self showPhotoLibraryWithImagePicker:imagePickerController]; + } + } else { + // Prior to iOS 11, accessing gallery requires authorization + [self checkPhotoAuthorizationWithImagePicker:imagePickerController]; + } break; default: [self sendCallResultWithError:[FlutterError errorWithCode:@"invalid_source" @@ -151,6 +164,7 @@ - (void)launchUIImagePickerWithSource:(nonnull FLTSourceSpecification *)source - (void)pickImageWithSource:(nonnull FLTSourceSpecification *)source maxSize:(nonnull FLTMaxSize *)maxSize quality:(nullable NSNumber *)imageQuality + fullMetadata:(NSNumber *)fullMetadata completion: (nonnull void (^)(NSString *_Nullable, FlutterError *_Nullable))completion { [self cancelInProgressCall]; @@ -166,6 +180,7 @@ - (void)pickImageWithSource:(nonnull FLTSourceSpecification *)source context.maxSize = maxSize; context.imageQuality = imageQuality; context.maxImageCount = 1; + context.requestFullMetadata = [fullMetadata boolValue]; if (source.type == FLTSourceTypeGallery) { // Capture is not possible with PHPicker if (@available(iOS 14, *)) { @@ -180,12 +195,14 @@ - (void)pickImageWithSource:(nonnull FLTSourceSpecification *)source - (void)pickMultiImageWithMaxSize:(nonnull FLTMaxSize *)maxSize quality:(nullable NSNumber *)imageQuality + fullMetadata:(NSNumber *)fullMetadata completion:(nonnull void (^)(NSArray *_Nullable, FlutterError *_Nullable))completion { FLTImagePickerMethodCallContext *context = [[FLTImagePickerMethodCallContext alloc] initWithResult:completion]; context.maxSize = maxSize; context.imageQuality = imageQuality; + context.requestFullMetadata = [fullMetadata boolValue]; if (@available(iOS 14, *)) { [self launchPHPickerWithContext:context]; @@ -554,7 +571,11 @@ - (void)imagePickerController:(UIImagePickerController *)picker NSNumber *imageQuality = self.callContext.imageQuality; NSNumber *desiredImageQuality = [self getDesiredImageQuality:imageQuality]; - PHAsset *originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromImagePickerInfo:info]; + PHAsset *originalAsset; + if (_callContext.requestFullMetadata) { + // Full metadata are available only in PHAsset, which requires gallery permission. + originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromImagePickerInfo:info]; + } if (maxWidth != nil || maxHeight != nil) { image = [FLTImagePickerImageUtil scaledImage:image 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 64c20452987d..d73a54d245f6 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 @@ -46,6 +46,9 @@ typedef void (^FlutterResultAdapter)(NSArray *_Nullable, FlutterErro /** 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) */ +@property(nonatomic, assign) BOOL requestFullMetadata; + @end #pragma mark - diff --git a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h index 310165f72f4f..c87bda59d8fb 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.h +++ b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.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 (v3.0.2), do not edit directly. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. // See also: https://pub.dev/packages/pigeon #import @protocol FlutterBinaryMessenger; @@ -45,9 +45,11 @@ NSObject *FLTImagePickerApiGetCodec(void); - (void)pickImageWithSource:(FLTSourceSpecification *)source maxSize:(FLTMaxSize *)maxSize quality:(nullable NSNumber *)imageQuality + fullMetadata:(NSNumber *)requestFullMetadata completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; - (void)pickMultiImageWithMaxSize:(FLTMaxSize *)maxSize quality:(nullable NSNumber *)imageQuality + fullMetadata:(NSNumber *)requestFullMetadata completion:(void (^)(NSArray *_Nullable, FlutterError *_Nullable))completion; - (void)pickVideoWithSource:(FLTSourceSpecification *)source diff --git a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m index 6c91c0ab264f..71a5b5140417 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/messages.g.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/messages.g.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 (v3.0.2), do not edit directly. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "messages.g.h" #import @@ -144,18 +144,21 @@ void FLTImagePickerApiSetup(id binaryMessenger, binaryMessenger:binaryMessenger codec:FLTImagePickerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(pickImageWithSource:maxSize:quality:completion:)], + NSCAssert([api respondsToSelector:@selector + (pickImageWithSource:maxSize:quality:fullMetadata:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to " - @"@selector(pickImageWithSource:maxSize:quality:completion:)", + @"@selector(pickImageWithSource:maxSize:quality:fullMetadata:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FLTSourceSpecification *arg_source = GetNullableObjectAtIndex(args, 0); FLTMaxSize *arg_maxSize = GetNullableObjectAtIndex(args, 1); NSNumber *arg_imageQuality = GetNullableObjectAtIndex(args, 2); + NSNumber *arg_requestFullMetadata = GetNullableObjectAtIndex(args, 3); [api pickImageWithSource:arg_source maxSize:arg_maxSize quality:arg_imageQuality + fullMetadata:arg_requestFullMetadata completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); }]; @@ -170,16 +173,19 @@ void FLTImagePickerApiSetup(id binaryMessenger, binaryMessenger:binaryMessenger codec:FLTImagePickerApiGetCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(pickMultiImageWithMaxSize:quality:completion:)], + NSCAssert([api respondsToSelector:@selector + (pickMultiImageWithMaxSize:quality:fullMetadata:completion:)], @"FLTImagePickerApi api (%@) doesn't respond to " - @"@selector(pickMultiImageWithMaxSize:quality:completion:)", + @"@selector(pickMultiImageWithMaxSize:quality:fullMetadata:completion:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; FLTMaxSize *arg_maxSize = GetNullableObjectAtIndex(args, 0); NSNumber *arg_imageQuality = GetNullableObjectAtIndex(args, 1); + NSNumber *arg_requestFullMetadata = GetNullableObjectAtIndex(args, 2); [api pickMultiImageWithMaxSize:arg_maxSize quality:arg_imageQuality + fullMetadata:arg_requestFullMetadata completion:^(NSArray *_Nullable output, FlutterError *_Nullable error) { callback(wrapResult(output, error)); diff --git a/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart b/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart index 3d1413cf0cce..fbc356f212b8 100644 --- a/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart +++ b/packages/image_picker/image_picker_ios/lib/image_picker_ios.dart @@ -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. - import 'dart:async'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; @@ -51,14 +50,28 @@ class ImagePickerIOS extends ImagePickerPlatform { }) async { final String? path = await _pickImageAsPath( source: source, - maxWidth: maxWidth, - maxHeight: maxHeight, - imageQuality: imageQuality, - preferredCameraDevice: preferredCameraDevice, + options: ImagePickerOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice, + ), ); return path != null ? PickedFile(path) : null; } + @override + Future getImageFromSource({ + required ImageSource source, + ImagePickerOptions options = const ImagePickerOptions(), + }) async { + final String? path = await _pickImageAsPath( + source: source, + options: options, + ); + return path != null ? XFile(path) : null; + } + @override Future?> pickMultiImage({ double? maxWidth, @@ -66,9 +79,13 @@ class ImagePickerIOS extends ImagePickerPlatform { int? imageQuality, }) async { final List? paths = await _pickMultiImageAsPath( - maxWidth: maxWidth, - maxHeight: maxHeight, - imageQuality: imageQuality, + options: MultiImagePickerOptions( + imageOptions: ImageOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + ), + ), ); if (paths == null) { return null; @@ -77,20 +94,33 @@ class ImagePickerIOS extends ImagePickerPlatform { return paths.map((dynamic path) => PickedFile(path as String)).toList(); } + @override + Future> getMultiImageWithOptions({ + MultiImagePickerOptions options = const MultiImagePickerOptions(), + }) async { + final List? paths = await _pickMultiImageAsPath(options: options); + if (paths == null) { + return []; + } + + return paths.map((String path) => XFile(path)).toList(); + } + Future?> _pickMultiImageAsPath({ - double? maxWidth, - double? maxHeight, - int? imageQuality, + MultiImagePickerOptions options = const MultiImagePickerOptions(), }) async { + final int? imageQuality = options.imageOptions.imageQuality; if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { throw ArgumentError.value( imageQuality, 'imageQuality', 'must be between 0 and 100'); } + final double? maxWidth = options.imageOptions.maxWidth; if (maxWidth != null && maxWidth < 0) { throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative'); } + final double? maxHeight = options.imageOptions.maxHeight; if (maxHeight != null && maxHeight < 0) { throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative'); } @@ -98,22 +128,24 @@ class ImagePickerIOS extends ImagePickerPlatform { // TODO(stuartmorgan): Remove the cast once Pigeon supports non-nullable // generics, https://github.com/flutter/flutter/issues/97848 return (await _hostApi.pickMultiImage( - MaxSize(width: maxWidth, height: maxHeight), imageQuality)) + MaxSize(width: maxWidth, height: maxHeight), + imageQuality, + options.imageOptions.requestFullMetadata)) ?.cast(); } Future _pickImageAsPath({ required ImageSource source, - double? maxWidth, - double? maxHeight, - int? imageQuality, - CameraDevice preferredCameraDevice = CameraDevice.rear, + ImagePickerOptions options = const ImagePickerOptions(), }) { + final int? imageQuality = options.imageQuality; if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { throw ArgumentError.value( imageQuality, 'imageQuality', 'must be between 0 and 100'); } + final double? maxHeight = options.maxHeight; + final double? maxWidth = options.maxWidth; if (maxWidth != null && maxWidth < 0) { throw ArgumentError.value(maxWidth, 'maxWidth', 'cannot be negative'); } @@ -124,10 +156,12 @@ class ImagePickerIOS extends ImagePickerPlatform { return _hostApi.pickImage( SourceSpecification( - type: _convertSource(source), - camera: _convertCamera(preferredCameraDevice)), + type: _convertSource(source), + camera: _convertCamera(options.preferredCameraDevice), + ), MaxSize(width: maxWidth, height: maxHeight), imageQuality, + options.requestFullMetadata, ); } @@ -167,10 +201,12 @@ class ImagePickerIOS extends ImagePickerPlatform { }) async { final String? path = await _pickImageAsPath( source: source, - maxWidth: maxWidth, - maxHeight: maxHeight, - imageQuality: imageQuality, - preferredCameraDevice: preferredCameraDevice, + options: ImagePickerOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice, + ), ); return path != null ? XFile(path) : null; } @@ -182,9 +218,13 @@ class ImagePickerIOS extends ImagePickerPlatform { int? imageQuality, }) async { final List? paths = await _pickMultiImageAsPath( - maxWidth: maxWidth, - maxHeight: maxHeight, - imageQuality: imageQuality, + options: MultiImagePickerOptions( + imageOptions: ImageOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + ), + ), ); if (paths == null) { return null; diff --git a/packages/image_picker/image_picker_ios/lib/src/messages.g.dart b/packages/image_picker/image_picker_ios/lib/src/messages.g.dart index 0c5859e80ac9..5f8768ba8cc1 100644 --- a/packages/image_picker/image_picker_ios/lib/src/messages.g.dart +++ b/packages/image_picker/image_picker_ios/lib/src/messages.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v3.0.2), do not edit directly. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name // @dart = 2.12 @@ -115,13 +115,16 @@ class ImagePickerApi { static const MessageCodec codec = _ImagePickerApiCodec(); Future pickImage(SourceSpecification arg_source, MaxSize arg_maxSize, - int? arg_imageQuality) async { + int? arg_imageQuality, bool arg_requestFullMetadata) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.ImagePickerApi.pickImage', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_source, arg_maxSize, arg_imageQuality]) - as Map?; + final Map? replyMap = await channel.send([ + arg_source, + arg_maxSize, + arg_imageQuality, + arg_requestFullMetadata + ]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', @@ -140,14 +143,14 @@ class ImagePickerApi { } } - Future?> pickMultiImage( - MaxSize arg_maxSize, int? arg_imageQuality) async { + Future?> pickMultiImage(MaxSize arg_maxSize, + int? arg_imageQuality, bool arg_requestFullMetadata) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.ImagePickerApi.pickMultiImage', codec, binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_maxSize, arg_imageQuality]) - as Map?; + final Map? replyMap = await channel.send( + [arg_maxSize, arg_imageQuality, arg_requestFullMetadata]) + as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', diff --git a/packages/image_picker/image_picker_ios/pigeons/messages.dart b/packages/image_picker/image_picker_ios/pigeons/messages.dart index 94ac034606e9..dd8a0f0c0834 100644 --- a/packages/image_picker/image_picker_ios/pigeons/messages.dart +++ b/packages/image_picker/image_picker_ios/pigeons/messages.dart @@ -35,12 +35,13 @@ class SourceSpecification { @HostApi(dartHostTestHandler: 'TestHostImagePickerApi') abstract class ImagePickerApi { @async - @ObjCSelector('pickImageWithSource:maxSize:quality:') - String? pickImage( - SourceSpecification source, MaxSize maxSize, int? imageQuality); + @ObjCSelector('pickImageWithSource:maxSize:quality:fullMetadata:') + String? pickImage(SourceSpecification source, MaxSize maxSize, + int? imageQuality, bool requestFullMetadata); @async - @ObjCSelector('pickMultiImageWithMaxSize:quality:') - List? pickMultiImage(MaxSize maxSize, int? imageQuality); + @ObjCSelector('pickMultiImageWithMaxSize:quality:fullMetadata:') + List? pickMultiImage( + MaxSize maxSize, int? imageQuality, bool requestFullMetadata); @async @ObjCSelector('pickVideoWithSource:maxDuration:') String? pickVideo(SourceSpecification source, int? maxDurationSeconds); diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index dc695138dd7f..5c30bf9d8c35 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/plugins/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.5+6 +version: 0.8.6 environment: sdk: ">=2.14.0 <3.0.0" @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - image_picker_platform_interface: ^2.3.0 + image_picker_platform_interface: ^2.6.1 dev_dependencies: flutter_test: diff --git a/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart b/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart index 09517f1ef96b..b20025770ad1 100644 --- a/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart +++ b/packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart @@ -39,7 +39,11 @@ class _ApiLogger implements TestHostImagePickerApi { @override Future pickImage( - SourceSpecification source, MaxSize maxSize, int? imageQuality) async { + SourceSpecification source, + MaxSize maxSize, + int? imageQuality, + bool requestFullMetadata, + ) async { // Flatten arguments for easy comparison. calls.add(_LoggedMethodCall('pickImage', arguments: { 'source': source.type, @@ -47,17 +51,22 @@ class _ApiLogger implements TestHostImagePickerApi { 'maxWidth': maxSize.width, 'maxHeight': maxSize.height, 'imageQuality': imageQuality, + 'requestFullMetadata': requestFullMetadata, })); return returnValue as String?; } @override Future?> pickMultiImage( - MaxSize maxSize, int? imageQuality) async { + MaxSize maxSize, + int? imageQuality, + bool requestFullMetadata, + ) async { calls.add(_LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': maxSize.width, 'maxHeight': maxSize.height, 'imageQuality': imageQuality, + 'requestFullMetadata': requestFullMetadata, })); return returnValue as List?; } @@ -103,14 +112,16 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.gallery, 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), ], ); @@ -156,49 +167,56 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': 70, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': 70, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': 70, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), ], ); @@ -257,6 +275,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), ], ); @@ -276,6 +295,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': SourceCamera.front, + 'requestFullMetadata': true, }), ], ); @@ -295,6 +315,7 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), ], ); @@ -335,42 +356,49 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': null, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': null, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': 70, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': 70, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': 70, + 'requestFullMetadata': true, }), ], ); @@ -524,14 +552,16 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.gallery, 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), ], ); @@ -577,49 +607,56 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': null, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': 70, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': 70, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickImage', arguments: { 'source': SourceType.camera, 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': 70, - 'cameraDevice': SourceCamera.rear + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), ], ); @@ -678,6 +715,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, }), ], ); @@ -697,6 +735,7 @@ void main() { 'maxHeight': null, 'imageQuality': null, 'cameraDevice': SourceCamera.front, + 'requestFullMetadata': true, }), ], ); @@ -716,6 +755,7 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), ], ); @@ -756,42 +796,49 @@ void main() { 'maxWidth': null, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': null, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': null, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': null, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': null, 'imageQuality': 70, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': null, 'maxHeight': 10.0, 'imageQuality': 70, + 'requestFullMetadata': true, }), const _LoggedMethodCall('pickMultiImage', arguments: { 'maxWidth': 10.0, 'maxHeight': 20.0, 'imageQuality': 70, + 'requestFullMetadata': true, }), ], ); @@ -934,4 +981,478 @@ void main() { ); }); }); + + group('#getImageFromSource', () { + test('passes the image source argument correctly', () async { + await picker.getImageFromSource(source: ImageSource.camera); + await picker.getImageFromSource(source: ImageSource.gallery); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.gallery, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + await picker.getImageFromSource(source: ImageSource.camera); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxWidth: 10.0), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxHeight: 10.0), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxWidth: 10.0, + maxHeight: 20.0, + ), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxWidth: 10.0, + imageQuality: 70, + ), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxHeight: 10.0, + imageQuality: 70, + ), + ); + await picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions( + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ), + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('does not accept a invalid imageQuality argument', () { + expect( + () => picker.getImageFromSource( + source: ImageSource.gallery, + options: const ImagePickerOptions(imageQuality: -1), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.gallery, + options: const ImagePickerOptions(imageQuality: 101), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(imageQuality: -1), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(imageQuality: 101), + ), + throwsArgumentError, + ); + }); + + test('does not accept a negative width or height argument', () { + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxWidth: -1.0), + ), + throwsArgumentError, + ); + + expect( + () => picker.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(maxHeight: -1.0), + ), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + log.returnValue = null; + + expect( + await picker.getImageFromSource(source: ImageSource.gallery), isNull); + expect( + await picker.getImageFromSource(source: ImageSource.camera), isNull); + }); + + test('camera position defaults to back', () async { + await picker.getImageFromSource(source: ImageSource.camera); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('camera position can set to front', () async { + await picker.getImageFromSource( + source: ImageSource.camera, + options: + const ImagePickerOptions(preferredCameraDevice: CameraDevice.front), + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.camera, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.front, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('Request full metadata argument defaults to true', () async { + await picker.getImageFromSource(source: ImageSource.gallery); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.gallery, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('passes the request full metadata argument correctly', () async { + await picker.getImageFromSource( + source: ImageSource.gallery, + options: const ImagePickerOptions(requestFullMetadata: false), + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickImage', arguments: { + 'source': SourceType.gallery, + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'cameraDevice': SourceCamera.rear, + 'requestFullMetadata': false, + }), + ], + ); + }); + }); + + group('#getMultiImageWithOptions', () { + test('calls the method correctly', () async { + log.returnValue = ['0', '1']; + await picker.getMultiImageWithOptions(); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('passes the width and height arguments correctly', () async { + log.returnValue = ['0', '1']; + await picker.getMultiImageWithOptions(); + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(maxWidth: 10.0), + ), + ); + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(maxHeight: 10.0), + ), + ); + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(maxWidth: 10.0, maxHeight: 20.0), + ), + ); + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(maxWidth: 10.0, imageQuality: 70), + ), + ); + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(maxHeight: 10.0, imageQuality: 70), + ), + ); + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions( + maxWidth: 10.0, + maxHeight: 20.0, + imageQuality: 70, + ), + ), + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': null, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': null, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': null, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': null, + 'imageQuality': 70, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': 10.0, + 'imageQuality': 70, + 'requestFullMetadata': true, + }), + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': 10.0, + 'maxHeight': 20.0, + 'imageQuality': 70, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('does not accept a negative width or height argument', () { + log.returnValue = ['0', '1']; + expect( + () => picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(maxWidth: -1.0), + ), + ), + throwsArgumentError, + ); + + expect( + () => picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(maxHeight: -1.0), + ), + ), + throwsArgumentError, + ); + }); + + test('does not accept a invalid imageQuality argument', () { + log.returnValue = ['0', '1']; + expect( + () => picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(imageQuality: -1), + ), + ), + throwsArgumentError, + ); + + expect( + () => picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(imageQuality: 101), + ), + ), + throwsArgumentError, + ); + }); + + test('handles a null image path response gracefully', () async { + log.returnValue = null; + + expect(await picker.getMultiImageWithOptions(), isEmpty); + }); + + test('Request full metadata argument defaults to true', () async { + log.returnValue = ['0', '1']; + await picker.getMultiImageWithOptions(); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'requestFullMetadata': true, + }), + ], + ); + }); + + test('Passes the request full metadata argument correctly', () async { + log.returnValue = ['0', '1']; + await picker.getMultiImageWithOptions( + options: const MultiImagePickerOptions( + imageOptions: ImageOptions(requestFullMetadata: false), + ), + ); + + expect( + log.calls, + <_LoggedMethodCall>[ + const _LoggedMethodCall('pickMultiImage', + arguments: { + 'maxWidth': null, + 'maxHeight': null, + 'imageQuality': null, + 'requestFullMetadata': false, + }), + ], + ); + }); + }); } diff --git a/packages/image_picker/image_picker_ios/test/test_api.dart b/packages/image_picker/image_picker_ios/test/test_api.dart index 8b20f2129d89..1e44f600f57d 100644 --- a/packages/image_picker/image_picker_ios/test/test_api.dart +++ b/packages/image_picker/image_picker_ios/test/test_api.dart @@ -1,12 +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. -// Autogenerated from Pigeon (v3.0.2), do not edit directly. +// Autogenerated from Pigeon (v3.0.3), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis // ignore_for_file: avoid_relative_lib_imports // @dart = 2.12 import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + // TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) // ignore: unnecessary_import import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; @@ -49,9 +50,10 @@ class _TestHostImagePickerApiCodec extends StandardMessageCodec { abstract class TestHostImagePickerApi { static const MessageCodec codec = _TestHostImagePickerApiCodec(); - Future pickImage( - SourceSpecification source, MaxSize maxSize, int? imageQuality); - Future?> pickMultiImage(MaxSize maxSize, int? imageQuality); + Future pickImage(SourceSpecification source, MaxSize maxSize, + int? imageQuality, bool requestFullMetadata); + Future?> pickMultiImage( + MaxSize maxSize, int? imageQuality, bool requestFullMetadata); Future pickVideo( SourceSpecification source, int? maxDurationSeconds); static void setup(TestHostImagePickerApi? api, @@ -75,8 +77,11 @@ abstract class TestHostImagePickerApi { assert(arg_maxSize != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null MaxSize.'); final int? arg_imageQuality = (args[2] as int?); - final String? output = - await api.pickImage(arg_source!, arg_maxSize!, arg_imageQuality); + final bool? arg_requestFullMetadata = (args[3] as bool?); + assert(arg_requestFullMetadata != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickImage was null, expected non-null bool.'); + final String? output = await api.pickImage(arg_source!, arg_maxSize!, + arg_imageQuality, arg_requestFullMetadata!); return {'result': output}; }); } @@ -96,8 +101,11 @@ abstract class TestHostImagePickerApi { assert(arg_maxSize != null, 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMultiImage was null, expected non-null MaxSize.'); final int? arg_imageQuality = (args[1] as int?); - final List? output = - await api.pickMultiImage(arg_maxSize!, arg_imageQuality); + final bool? arg_requestFullMetadata = (args[2] as bool?); + assert(arg_requestFullMetadata != null, + 'Argument for dev.flutter.pigeon.ImagePickerApi.pickMultiImage was null, expected non-null bool.'); + final List? output = await api.pickMultiImage( + arg_maxSize!, arg_imageQuality, arg_requestFullMetadata!); return {'result': output}; }); } From c6b2f8a844394f522d634a253c050537ea0252ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Aug 2022 15:44:25 +0000 Subject: [PATCH 671/844] [espresso]: Bump gson from 2.8.6 to 2.9.1 in /packages/espresso/android (#6164) --- packages/espresso/CHANGELOG.md | 3 ++- packages/espresso/android/build.gradle | 2 +- packages/espresso/pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/espresso/CHANGELOG.md b/packages/espresso/CHANGELOG.md index 1bb1b8e42204..bd04ecde59b6 100644 --- a/packages/espresso/CHANGELOG.md +++ b/packages/espresso/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 0.2.0+4 * Updates minimum Flutter version to 2.10. +* Bumps gson to 2.9.1 ## 0.2.0+3 diff --git a/packages/espresso/android/build.gradle b/packages/espresso/android/build.gradle index 7fafa0b309ec..d78609270bf4 100644 --- a/packages/espresso/android/build.gradle +++ b/packages/espresso/android/build.gradle @@ -51,7 +51,7 @@ android { dependencies { implementation 'com.google.guava:guava:31.1-android' implementation 'com.squareup.okhttp3:okhttp:4.10.0' - implementation 'com.google.code.gson:gson:2.8.6' + implementation 'com.google.code.gson:gson:2.9.1' androidTestImplementation 'org.hamcrest:hamcrest:2.2' testImplementation 'junit:junit:4.13.2' diff --git a/packages/espresso/pubspec.yaml b/packages/espresso/pubspec.yaml index 4c55aafbc063..c0068ec9883c 100644 --- a/packages/espresso/pubspec.yaml +++ b/packages/espresso/pubspec.yaml @@ -3,7 +3,7 @@ description: Java classes for testing Flutter apps using Espresso. Allows driving Flutter widgets from a native Espresso test. repository: https://github.com/flutter/plugins/tree/main/packages/espresso issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+espresso%22 -version: 0.2.0+3 +version: 0.2.0+4 environment: sdk: ">=2.12.0 <3.0.0" From caa6a19c6c8fdcce4df3bfad418578bf45165836 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 31 Aug 2022 12:52:05 -0400 Subject: [PATCH 672/844] Roll Flutter from dfcd9650e36b to 069f5042973d (14 revisions) (#6343) --- .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 a8bf77908c73..e262c65abbe9 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -dfcd9650e36bdc4e3c9d186682b4e574c7012e45 +069f5042973dc4aafbf76a44ca14af7c0e2c2ca1 From dfa488307a222f6b1ae83194a66ed083d50bdddb Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 31 Aug 2022 13:41:18 -0400 Subject: [PATCH 673/844] [quick_actions] Mark the plugin as 1.0 (#6346) --- packages/quick_actions/quick_actions/CHANGELOG.md | 5 ++++- packages/quick_actions/quick_actions/README.md | 5 +++++ packages/quick_actions/quick_actions/pubspec.yaml | 2 +- .../quick_actions/quick_actions_android/CHANGELOG.md | 5 +++-- .../quick_actions/quick_actions_android/pubspec.yaml | 2 +- packages/quick_actions/quick_actions_ios/CHANGELOG.md | 9 +++++---- packages/quick_actions/quick_actions_ios/pubspec.yaml | 3 +-- 7 files changed, 20 insertions(+), 11 deletions(-) diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index 170212045b94..b8b431490cae 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,6 +1,9 @@ -## NEXT +## 1.0.0 +* Updates version to 1.0 to reflect current status. * Updates minimum Flutter version to 2.10. +* Updates README to document that on Android, icons may need to be explicitly + marked as used in the Android project for release builds. * Minor fixes for new analysis options. ## 0.6.0+11 diff --git a/packages/quick_actions/quick_actions/README.md b/packages/quick_actions/quick_actions/README.md index 10bae164d534..3b5bcbaa64ef 100644 --- a/packages/quick_actions/quick_actions/README.md +++ b/packages/quick_actions/quick_actions/README.md @@ -47,3 +47,8 @@ quick action. \* The plugin will compile and run on SDK 16+, but will be a no-op below SDK 25 (Android 7.1). + +If the drawables used as icons are not referenced other than in your Dart code, +you may need to +[explicitly mark them to be kept](https://developer.android.com/studio/build/shrink-code#keep-resources) +to ensure that they will be available for use in release builds. diff --git a/packages/quick_actions/quick_actions/pubspec.yaml b/packages/quick_actions/quick_actions/pubspec.yaml index 61e0a16957a5..9ce18456969e 100644 --- a/packages/quick_actions/quick_actions/pubspec.yaml +++ b/packages/quick_actions/quick_actions/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for creating shortcuts on home screen, also known as Quick Actions on iOS and App Shortcuts on Android. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%22 -version: 0.6.0+11 +version: 1.0.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions_android/CHANGELOG.md b/packages/quick_actions/quick_actions_android/CHANGELOG.md index e4aa75dbf2b3..bc809a4dc477 100644 --- a/packages/quick_actions/quick_actions_android/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_android/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 1.0.0 +* Updates version to 1.0 to reflect current status. * Updates minimum Flutter version to 2.10. * Updates mockito-core to 4.6.1. * Removes deprecated FieldSetter from QuickActionsTest. @@ -8,7 +9,7 @@ * Updates gradle version to 7.2.1. -## 0.6.1 +## 0.6.1 * Allows Android to trigger quick actions without restarting the app. diff --git a/packages/quick_actions/quick_actions_android/pubspec.yaml b/packages/quick_actions/quick_actions_android/pubspec.yaml index 6398b0715cf8..e47a1fdc13e9 100644 --- a/packages/quick_actions/quick_actions_android/pubspec.yaml +++ b/packages/quick_actions/quick_actions_android/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_android description: An implementation for the Android platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.6.2 +version: 1.0.0 environment: sdk: ">=2.15.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions_ios/CHANGELOG.md b/packages/quick_actions/quick_actions_ios/CHANGELOG.md index 97ba2cfdd36b..7334bbd6a632 100644 --- a/packages/quick_actions/quick_actions_ios/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_ios/CHANGELOG.md @@ -1,15 +1,16 @@ -## NEXT +## 1.0.0 +* Updates version to 1.0 to reflect current status. * Updates minimum Flutter version to 2.10. ## 0.6.0+14 -* Refactors `FLTQuickActionsPlugin` class into multiple components. -* Increases unit tests coverage to 100%. +* Refactors `FLTQuickActionsPlugin` class into multiple components. +* Increases unit tests coverage to 100%. ## 0.6.0+13 -* Adds some unit tests for `FLTQuickActionsPlugin` class. +* Adds some unit tests for `FLTQuickActionsPlugin` class. ## 0.6.0+12 diff --git a/packages/quick_actions/quick_actions_ios/pubspec.yaml b/packages/quick_actions/quick_actions_ios/pubspec.yaml index a2f415982439..82fd25f06b2c 100644 --- a/packages/quick_actions/quick_actions_ios/pubspec.yaml +++ b/packages/quick_actions/quick_actions_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_ios description: An implementation for the iOS platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 0.6.0+14 +version: 1.0.0 environment: sdk: ">=2.15.0 <3.0.0" @@ -27,4 +27,3 @@ dev_dependencies: integration_test: sdk: flutter plugin_platform_interface: ^2.1.2 - From db0517a4f2d036b307afd67ba4eabb72c2fc388b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Aug 2022 23:18:32 +0000 Subject: [PATCH 674/844] [webview]: Bump webkit from 1.0.0 to 1.5.0 in /packages/webview_flutter/webview_flutter_android/android (#6325) --- .../webview_flutter/webview_flutter_android/CHANGELOG.md | 5 +++++ .../webview_flutter_android/android/build.gradle | 4 ++-- .../webview_flutter_android/example/android/app/build.gradle | 2 +- .../webview_flutter/webview_flutter_android/pubspec.yaml | 2 +- script/tool/CHANGELOG.md | 4 ++++ script/tool/lib/src/create_all_plugins_app_command.dart | 4 ++-- script/tool/pubspec.yaml | 2 +- 7 files changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 3d3731979866..0eb1aac530df 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.10.0 + +* Bumps webkit from 1.0.0 to 1.5.0. +* Raises minimum `compileSdkVersion` to 32. + ## 2.9.5 * Adds dispose methods for HostApi and FlutterApi of JavaObject. diff --git a/packages/webview_flutter/webview_flutter_android/android/build.gradle b/packages/webview_flutter/webview_flutter_android/android/build.gradle index 7c55559cb832..8fd52e396da2 100644 --- a/packages/webview_flutter/webview_flutter_android/android/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 31 + compileSdkVersion 32 defaultConfig { minSdkVersion 19 @@ -36,7 +36,7 @@ android { dependencies { implementation 'androidx.annotation:annotation:1.4.0' - implementation 'androidx.webkit:webkit:1.0.0' + implementation 'androidx.webkit:webkit:1.5.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:4.7.0' testImplementation 'androidx.test:core:1.3.0' diff --git a/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle b/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle index b75bc9df5ebb..0c55b4d594be 100644 --- a/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 31 + compileSdkVersion 32 lintOptions { disable 'InvalidPackage' diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index bbff4fe7dce5..4c1f5766e712 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/plugins/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: 2.9.5 +version: 2.10.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 595943777c68..ebfbe80b0184 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.3 + +* Raises minimum `compileSdkVersion` to 32 for the `all-plugins-app` command. + ## 0.9.2 * Adds checking of `code-excerpt` configuration to `readme-check`, to validate diff --git a/script/tool/lib/src/create_all_plugins_app_command.dart b/script/tool/lib/src/create_all_plugins_app_command.dart index 595779b8be68..7bf007ac72a5 100644 --- a/script/tool/lib/src/create_all_plugins_app_command.dart +++ b/script/tool/lib/src/create_all_plugins_app_command.dart @@ -101,8 +101,8 @@ class CreateAllPluginsAppCommand extends PluginCommand { // minSdkVersion 19 is required by WebView. newGradle.writeln('minSdkVersion 20'); } else if (line.contains('compileSdkVersion')) { - // compileSdkVersion 31 is required by Camera. - newGradle.writeln('compileSdkVersion 31'); + // compileSdkVersion 32 is required by webview_flutter. + newGradle.writeln('compileSdkVersion 32'); } else { newGradle.writeln(line); } diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 3c25ef62b205..b1d55e0fe550 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.9.2 +version: 0.9.3 dependencies: args: ^2.1.0 From 686dcf411dcb967d361923b003828be1770c2c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Sousa?= Date: Thu, 1 Sep 2022 02:55:21 -0300 Subject: [PATCH 675/844] Update badges for pub.dev scores (#6271) --- README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index edcffe208cad..fc9972e1ef72 100644 --- a/README.md +++ b/README.md @@ -42,20 +42,20 @@ These are the available plugins in this repository. | Plugin | Pub | Points | Popularity | Likes | Issues | Pull requests | |--------|-----|--------|------------|-------|--------|---------------| -| [camera](./packages/camera/) | [![pub package](https://img.shields.io/pub/v/camera.svg)](https://pub.dev/packages/camera) | [![pub points](https://badges.bar/camera/pub%20points)](https://pub.dev/packages/camera/score) | [![popularity](https://badges.bar/camera/popularity)](https://pub.dev/packages/camera/score) | [![likes](https://badges.bar/camera/likes)](https://pub.dev/packages/camera/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20camera?label=)](https://github.com/flutter/flutter/labels/p%3A%20camera) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20camera?label=)](https://github.com/flutter/plugins/labels/p%3A%20camera) | -| [espresso](./packages/espresso/) | [![pub package](https://img.shields.io/pub/v/espresso.svg)](https://pub.dev/packages/espresso) | [![pub points](https://badges.bar/espresso/pub%20points)](https://pub.dev/packages/espresso/score) | [![popularity](https://badges.bar/espresso/popularity)](https://pub.dev/packages/espresso/score) | [![likes](https://badges.bar/espresso/likes)](https://pub.dev/packages/espresso/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20espresso?label=)](https://github.com/flutter/flutter/labels/p%3A%20espresso) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20espresso?label=)](https://github.com/flutter/plugins/labels/p%3A%20espresso) | -| [file_selector](./packages/file_selector/) | [![pub package](https://img.shields.io/pub/v/file_selector.svg)](https://pub.dev/packages/file_selector) | [![pub points](https://badges.bar/file_selector/pub%20points)](https://pub.dev/packages/file_selector/score) | [![popularity](https://badges.bar/file_selector/popularity)](https://pub.dev/packages/file_selector/score) | [![likes](https://badges.bar/file_selector/likes)](https://pub.dev/packages/file_selector/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20file_selector?label=)](https://github.com/flutter/flutter/labels/p%3A%20file_selector) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20file_selector?label=)](https://github.com/flutter/plugins/labels/p%3A%20file_selector) | -| [flutter_plugin_android_lifecycle](./packages/flutter_plugin_android_lifecycle/) | [![pub package](https://img.shields.io/pub/v/flutter_plugin_android_lifecycle.svg)](https://pub.dev/packages/flutter_plugin_android_lifecycle) | [![pub points](https://badges.bar/flutter_plugin_android_lifecycle/pub%20points)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![popularity](https://badges.bar/flutter_plugin_android_lifecycle/popularity)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![likes](https://badges.bar/flutter_plugin_android_lifecycle/likes)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20flutter_plugin_android_lifecycle?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_plugin_android_lifecycle) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20flutter_plugin_android_lifecycle?label=)](https://github.com/flutter/plugins/labels/p%3A%20flutter_plugin_android_lifecycle) | -| [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://badges.bar/google_maps_flutter/pub%20points)](https://pub.dev/packages/google_maps_flutter/score) | [![popularity](https://badges.bar/google_maps_flutter/popularity)](https://pub.dev/packages/google_maps_flutter/score) | [![likes](https://badges.bar/google_maps_flutter/likes)](https://pub.dev/packages/google_maps_flutter/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20maps?label=)](https://github.com/flutter/flutter/labels/p%3A%20maps) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20google_maps_flutter?label=)](https://github.com/flutter/plugins/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://badges.bar/google_sign_in/pub%20points)](https://pub.dev/packages/google_sign_in/score) | [![popularity](https://badges.bar/google_sign_in/popularity)](https://pub.dev/packages/google_sign_in/score) | [![likes](https://badges.bar/google_sign_in/likes)](https://pub.dev/packages/google_sign_in/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%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/plugins/p:%20google_sign_in?label=)](https://github.com/flutter/plugins/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://badges.bar/image_picker/pub%20points)](https://pub.dev/packages/image_picker/score) | [![popularity](https://badges.bar/image_picker/popularity)](https://pub.dev/packages/image_picker/score) | [![likes](https://badges.bar/image_picker/likes)](https://pub.dev/packages/image_picker/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%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/plugins/p:%20image_picker?label=)](https://github.com/flutter/plugins/labels/p%3A%20image_picker) | -| [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://badges.bar/in_app_purchase/pub%20points)](https://pub.dev/packages/in_app_purchase/score) | [![popularity](https://badges.bar/in_app_purchase/popularity)](https://pub.dev/packages/in_app_purchase/score) | [![likes](https://badges.bar/in_app_purchase/likes)](https://pub.dev/packages/in_app_purchase/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%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/plugins/p:%20in_app_purchase?label=)](https://github.com/flutter/plugins/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://badges.bar/ios_platform_images/pub%20points)](https://pub.dev/packages/ios_platform_images/score) | [![popularity](https://badges.bar/ios_platform_images/popularity)](https://pub.dev/packages/ios_platform_images/score) | [![likes](https://badges.bar/ios_platform_images/likes)](https://pub.dev/packages/ios_platform_images/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%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/plugins/p:%20ios_platform_images?label=)](https://github.com/flutter/plugins/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://badges.bar/local_auth/pub%20points)](https://pub.dev/packages/local_auth/score) | [![popularity](https://badges.bar/local_auth/popularity)](https://pub.dev/packages/local_auth/score) | [![likes](https://badges.bar/local_auth/likes)](https://pub.dev/packages/local_auth/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%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/plugins/p:%20local_auth?label=)](https://github.com/flutter/plugins/labels/p%3A%20local_auth) | -| [path_provider](./packages/path_provider/) | [![pub package](https://img.shields.io/pub/v/path_provider.svg)](https://pub.dev/packages/path_provider) | [![pub points](https://badges.bar/path_provider/pub%20points)](https://pub.dev/packages/path_provider/score) | [![popularity](https://badges.bar/path_provider/popularity)](https://pub.dev/packages/path_provider/score) | [![likes](https://badges.bar/path_provider/likes)](https://pub.dev/packages/path_provider/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20path_provider?label=)](https://github.com/flutter/flutter/labels/p%3A%20path_provider) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20path_provider?label=)](https://github.com/flutter/plugins/labels/p%3A%20path_provider) | -| [plugin_platform_interface](./packages/plugin_platform_interface/) | [![pub package](https://img.shields.io/pub/v/plugin_platform_interface.svg)](https://pub.dev/packages/plugin_platform_interface) | [![pub points](https://badges.bar/plugin_platform_interface/pub%20points)](https://pub.dev/packages/plugin_platform_interface/score) | [![popularity](https://badges.bar/plugin_platform_interface/popularity)](https://pub.dev/packages/plugin_platform_interface/score) | [![likes](https://badges.bar/plugin_platform_interface/likes)](https://pub.dev/packages/plugin_platform_interface/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20plugin_platform_interface?label=)](https://github.com/flutter/flutter/labels/p%3A%20plugin_platform_interface) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20plugin_platform_interface?label=)](https://github.com/flutter/plugins/labels/p%3A%20plugin_platform_interface) | -| [quick_actions](./packages/quick_actions/) | [![pub package](https://img.shields.io/pub/v/quick_actions.svg)](https://pub.dev/packages/quick_actions) | [![pub points](https://badges.bar/quick_actions/pub%20points)](https://pub.dev/packages/quick_actions/score) | [![popularity](https://badges.bar/quick_actions/popularity)](https://pub.dev/packages/quick_actions/score) | [![likes](https://badges.bar/quick_actions/likes)](https://pub.dev/packages/quick_actions/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20quick_actions?label=)](https://github.com/flutter/flutter/labels/p%3A%20quick_actions) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20quick_actions?label=)](https://github.com/flutter/plugins/labels/p%3A%20quick_actions) | -| [shared_preferences](./packages/shared_preferences/) | [![pub package](https://img.shields.io/pub/v/shared_preferences.svg)](https://pub.dev/packages/shared_preferences) | [![pub points](https://badges.bar/shared_preferences/pub%20points)](https://pub.dev/packages/shared_preferences/score) | [![popularity](https://badges.bar/shared_preferences/popularity)](https://pub.dev/packages/shared_preferences/score) | [![likes](https://badges.bar/shared_preferences/likes)](https://pub.dev/packages/shared_preferences/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20shared_preferences?label=)](https://github.com/flutter/flutter/labels/p%3A%20shared_preferences) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20shared_preferences?label=)](https://github.com/flutter/plugins/labels/p%3A%20shared_preferences) | -| [url_launcher](./packages/url_launcher/) | [![pub package](https://img.shields.io/pub/v/url_launcher.svg)](https://pub.dev/packages/url_launcher) | [![pub points](https://badges.bar/url_launcher/pub%20points)](https://pub.dev/packages/url_launcher/score) | [![popularity](https://badges.bar/url_launcher/popularity)](https://pub.dev/packages/url_launcher/score) | [![likes](https://badges.bar/url_launcher/likes)](https://pub.dev/packages/url_launcher/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20url_launcher?label=)](https://github.com/flutter/flutter/labels/p%3A%20url_launcher) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20url_launcher?label=)](https://github.com/flutter/plugins/labels/p%3A%20url_launcher) | -| [video_player](./packages/video_player/) | [![pub package](https://img.shields.io/pub/v/video_player.svg)](https://pub.dev/packages/video_player) | [![pub points](https://badges.bar/video_player/pub%20points)](https://pub.dev/packages/video_player/score) | [![popularity](https://badges.bar/video_player/popularity)](https://pub.dev/packages/video_player/score) | [![likes](https://badges.bar/video_player/likes)](https://pub.dev/packages/video_player/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20video_player?label=)](https://github.com/flutter/flutter/labels/p%3A%20video_player) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20video_player?label=)](https://github.com/flutter/plugins/labels/p%3A%20video_player) | -| [webview_flutter](./packages/webview_flutter/) | [![pub package](https://img.shields.io/pub/v/webview_flutter.svg)](https://pub.dev/packages/webview_flutter) | [![pub points](https://badges.bar/webview_flutter/pub%20points)](https://pub.dev/packages/webview_flutter/score) | [![popularity](https://badges.bar/webview_flutter/popularity)](https://pub.dev/packages/webview_flutter/score) | [![likes](https://badges.bar/webview_flutter/likes)](https://pub.dev/packages/webview_flutter/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20webview?label=)](https://github.com/flutter/flutter/labels/p%3A%20webview) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20webview_flutter?label=)](https://github.com/flutter/plugins/labels/p%3A%20webview_flutter) | +| [camera](./packages/camera/) | [![pub package](https://img.shields.io/pub/v/camera.svg)](https://pub.dev/packages/camera) | [![pub points](https://img.shields.io/pub/points/camera)](https://pub.dev/packages/camera/score) | [![popularity](https://img.shields.io/pub/popularity/camera)](https://pub.dev/packages/camera/score) | [![likes](https://img.shields.io/pub/likes/camera)](https://pub.dev/packages/camera/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20camera?label=)](https://github.com/flutter/flutter/labels/p%3A%20camera) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20camera?label=)](https://github.com/flutter/plugins/labels/p%3A%20camera) | +| [espresso](./packages/espresso/) | [![pub package](https://img.shields.io/pub/v/espresso.svg)](https://pub.dev/packages/espresso) | [![pub points](https://img.shields.io/pub/points/espresso)](https://pub.dev/packages/espresso/score) | [![popularity](https://img.shields.io/pub/popularity/espresso)](https://pub.dev/packages/espresso/score) | [![likes](https://img.shields.io/pub/likes/espresso)](https://pub.dev/packages/espresso/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20espresso?label=)](https://github.com/flutter/flutter/labels/p%3A%20espresso) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20espresso?label=)](https://github.com/flutter/plugins/labels/p%3A%20espresso) | +| [file_selector](./packages/file_selector/) | [![pub package](https://img.shields.io/pub/v/file_selector.svg)](https://pub.dev/packages/file_selector) | [![pub points](https://img.shields.io/pub/points/file_selector)](https://pub.dev/packages/file_selector/score) | [![popularity](https://img.shields.io/pub/popularity/file_selector)](https://pub.dev/packages/file_selector/score) | [![likes](https://img.shields.io/pub/likes/file_selector)](https://pub.dev/packages/file_selector/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20file_selector?label=)](https://github.com/flutter/flutter/labels/p%3A%20file_selector) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20file_selector?label=)](https://github.com/flutter/plugins/labels/p%3A%20file_selector) | +| [flutter_plugin_android_lifecycle](./packages/flutter_plugin_android_lifecycle/) | [![pub package](https://img.shields.io/pub/v/flutter_plugin_android_lifecycle.svg)](https://pub.dev/packages/flutter_plugin_android_lifecycle) | [![pub points](https://img.shields.io/pub/points/flutter_plugin_android_lifecycle)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![popularity](https://img.shields.io/pub/popularity/flutter_plugin_android_lifecycle)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![likes](https://img.shields.io/pub/likes/flutter_plugin_android_lifecycle)](https://pub.dev/packages/flutter_plugin_android_lifecycle/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20flutter_plugin_android_lifecycle?label=)](https://github.com/flutter/flutter/labels/p%3A%20flutter_plugin_android_lifecycle) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20flutter_plugin_android_lifecycle?label=)](https://github.com/flutter/plugins/labels/p%3A%20flutter_plugin_android_lifecycle) | +| [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) | [![likes](https://img.shields.io/pub/likes/google_maps_flutter)](https://pub.dev/packages/google_maps_flutter/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20maps?label=)](https://github.com/flutter/flutter/labels/p%3A%20maps) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20google_maps_flutter?label=)](https://github.com/flutter/plugins/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) | [![likes](https://img.shields.io/pub/likes/google_sign_in)](https://pub.dev/packages/google_sign_in/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%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/plugins/p:%20google_sign_in?label=)](https://github.com/flutter/plugins/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) | [![likes](https://img.shields.io/pub/likes/image_picker)](https://pub.dev/packages/image_picker/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%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/plugins/p:%20image_picker?label=)](https://github.com/flutter/plugins/labels/p%3A%20image_picker) | +| [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) | [![likes](https://img.shields.io/pub/likes/in_app_purchase)](https://pub.dev/packages/in_app_purchase/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%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/plugins/p:%20in_app_purchase?label=)](https://github.com/flutter/plugins/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) | [![likes](https://img.shields.io/pub/likes/ios_platform_images)](https://pub.dev/packages/ios_platform_images/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%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/plugins/p:%20ios_platform_images?label=)](https://github.com/flutter/plugins/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) | [![likes](https://img.shields.io/pub/likes/local_auth)](https://pub.dev/packages/local_auth/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%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/plugins/p:%20local_auth?label=)](https://github.com/flutter/plugins/labels/p%3A%20local_auth) | +| [path_provider](./packages/path_provider/) | [![pub package](https://img.shields.io/pub/v/path_provider.svg)](https://pub.dev/packages/path_provider) | [![pub points](https://img.shields.io/pub/points/path_provider)](https://pub.dev/packages/path_provider/score) | [![popularity](https://img.shields.io/pub/popularity/path_provider)](https://pub.dev/packages/path_provider/score) | [![likes](https://img.shields.io/pub/likes/path_provider)](https://pub.dev/packages/path_provider/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20path_provider?label=)](https://github.com/flutter/flutter/labels/p%3A%20path_provider) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20path_provider?label=)](https://github.com/flutter/plugins/labels/p%3A%20path_provider) | +| [plugin_platform_interface](./packages/plugin_platform_interface/) | [![pub package](https://img.shields.io/pub/v/plugin_platform_interface.svg)](https://pub.dev/packages/plugin_platform_interface) | [![pub points](https://img.shields.io/pub/points/plugin_platform_interface)](https://pub.dev/packages/plugin_platform_interface/score) | [![popularity](https://img.shields.io/pub/popularity/plugin_platform_interface)](https://pub.dev/packages/plugin_platform_interface/score) | [![likes](https://img.shields.io/pub/likes/plugin_platform_interface)](https://pub.dev/packages/plugin_platform_interface/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20plugin_platform_interface?label=)](https://github.com/flutter/flutter/labels/p%3A%20plugin_platform_interface) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20plugin_platform_interface?label=)](https://github.com/flutter/plugins/labels/p%3A%20plugin_platform_interface) | +| [quick_actions](./packages/quick_actions/) | [![pub package](https://img.shields.io/pub/v/quick_actions.svg)](https://pub.dev/packages/quick_actions) | [![pub points](https://img.shields.io/pub/points/quick_actions)](https://pub.dev/packages/quick_actions/score) | [![popularity](https://img.shields.io/pub/popularity/quick_actions)](https://pub.dev/packages/quick_actions/score) | [![likes](https://img.shields.io/pub/likes/quick_actions)](https://pub.dev/packages/quick_actions/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20quick_actions?label=)](https://github.com/flutter/flutter/labels/p%3A%20quick_actions) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20quick_actions?label=)](https://github.com/flutter/plugins/labels/p%3A%20quick_actions) | +| [shared_preferences](./packages/shared_preferences/) | [![pub package](https://img.shields.io/pub/v/shared_preferences.svg)](https://pub.dev/packages/shared_preferences) | [![pub points](https://img.shields.io/pub/points/shared_preferences)](https://pub.dev/packages/shared_preferences/score) | [![popularity](https://img.shields.io/pub/popularity/shared_preferences)](https://pub.dev/packages/shared_preferences/score) | [![likes](https://img.shields.io/pub/likes/shared_preferences)](https://pub.dev/packages/shared_preferences/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20shared_preferences?label=)](https://github.com/flutter/flutter/labels/p%3A%20shared_preferences) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20shared_preferences?label=)](https://github.com/flutter/plugins/labels/p%3A%20shared_preferences) | +| [url_launcher](./packages/url_launcher/) | [![pub package](https://img.shields.io/pub/v/url_launcher.svg)](https://pub.dev/packages/url_launcher) | [![pub points](https://img.shields.io/pub/points/url_launcher)](https://pub.dev/packages/url_launcher/score) | [![popularity](https://img.shields.io/pub/popularity/url_launcher)](https://pub.dev/packages/url_launcher/score) | [![likes](https://img.shields.io/pub/likes/url_launcher)](https://pub.dev/packages/url_launcher/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20url_launcher?label=)](https://github.com/flutter/flutter/labels/p%3A%20url_launcher) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20url_launcher?label=)](https://github.com/flutter/plugins/labels/p%3A%20url_launcher) | +| [video_player](./packages/video_player/) | [![pub package](https://img.shields.io/pub/v/video_player.svg)](https://pub.dev/packages/video_player) | [![pub points](https://img.shields.io/pub/points/video_player)](https://pub.dev/packages/video_player/score) | [![popularity](https://img.shields.io/pub/popularity/video_player)](https://pub.dev/packages/video_player/score) | [![likes](https://img.shields.io/pub/likes/video_player)](https://pub.dev/packages/video_player/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20video_player?label=)](https://github.com/flutter/flutter/labels/p%3A%20video_player) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20video_player?label=)](https://github.com/flutter/plugins/labels/p%3A%20video_player) | +| [webview_flutter](./packages/webview_flutter/) | [![pub package](https://img.shields.io/pub/v/webview_flutter.svg)](https://pub.dev/packages/webview_flutter) | [![pub points](https://img.shields.io/pub/points/webview_flutter)](https://pub.dev/packages/webview_flutter/score) | [![popularity](https://img.shields.io/pub/popularity/webview_flutter)](https://pub.dev/packages/webview_flutter/score) | [![likes](https://img.shields.io/pub/likes/webview_flutter)](https://pub.dev/packages/webview_flutter/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p:%20webview?label=)](https://github.com/flutter/flutter/labels/p%3A%20webview) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/plugins/p:%20webview_flutter?label=)](https://github.com/flutter/plugins/labels/p%3A%20webview_flutter) | From 50f4cb849f810880d5421dccea8b8c8cd65e6cfc Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 1 Sep 2022 09:37:37 -0400 Subject: [PATCH 676/844] [webview_flutter] Implementation of the app facing WebViewCookieManager for v4 (#6344) --- .../src/v4/src/webview_cookie_manager.dart | 39 +++++++++++++ .../lib/src/v4/webview_flutter.dart | 10 ++++ .../webview_flutter/pubspec.yaml | 2 +- .../test/v4/webview_cookie_manager_test.dart | 54 ++++++++++++++++++ .../v4/webview_cookie_manager_test.mocks.dart | 56 +++++++++++++++++++ 5 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_cookie_manager.dart create mode 100644 packages/webview_flutter/webview_flutter/lib/src/v4/webview_flutter.dart create mode 100644 packages/webview_flutter/webview_flutter/test/v4/webview_cookie_manager_test.dart create mode 100644 packages/webview_flutter/webview_flutter/test/v4/webview_cookie_manager_test.mocks.dart diff --git a/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_cookie_manager.dart b/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_cookie_manager.dart new file mode 100644 index 000000000000..c209e95a1917 --- /dev/null +++ b/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_cookie_manager.dart @@ -0,0 +1,39 @@ +// 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:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; + +/// Manages cookies pertaining to all WebViews. +class WebViewCookieManager { + /// Constructs a [WebViewCookieManager]. + WebViewCookieManager() + : this.fromPlatform( + platform: PlatformWebViewCookieManager( + const PlatformWebViewCookieManagerCreationParams(), + ), + ); + + /// Constructs a [WebViewCookieManager] from creation params for a specific + /// platform. + WebViewCookieManager.fromPlatformCreationParams( + PlatformWebViewCookieManagerCreationParams params, + ) : this.fromPlatform(platform: PlatformWebViewCookieManager(params)); + + /// Constructs a [WebViewCookieManager] from a specific platform + /// implementation. + WebViewCookieManager.fromPlatform({required this.platform}); + + /// Implementation of [PlatformWebViewCookieManager] for the current platform. + final PlatformWebViewCookieManager platform; + + /// Clears all cookies for all WebViews. + /// + /// Returns true if cookies were present before clearing, else false. + Future clearCookies() => platform.clearCookies(); + + /// Sets a cookie for all WebView instances. + /// + /// This is a no op on iOS versions below 11. + Future setCookie(WebViewCookie cookie) => platform.setCookie(cookie); +} diff --git a/packages/webview_flutter/webview_flutter/lib/src/v4/webview_flutter.dart b/packages/webview_flutter/webview_flutter/lib/src/v4/webview_flutter.dart new file mode 100644 index 000000000000..6ab8c3a2f6c8 --- /dev/null +++ b/packages/webview_flutter/webview_flutter/lib/src/v4/webview_flutter.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. + +library webview_flutter; + +export 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart' + show WebViewCookie; + +export 'src/webview_cookie_manager.dart'; diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 996f6478b800..a02b0323e7ab 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -20,7 +20,7 @@ dependencies: flutter: sdk: flutter webview_flutter_android: ^2.8.0 - webview_flutter_platform_interface: ^1.8.0 + webview_flutter_platform_interface: ^1.9.3 webview_flutter_wkwebview: ^2.7.0 dev_dependencies: diff --git a/packages/webview_flutter/webview_flutter/test/v4/webview_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter/test/v4/webview_cookie_manager_test.dart new file mode 100644 index 000000000000..d7941ab60cc9 --- /dev/null +++ b/packages/webview_flutter/webview_flutter/test/v4/webview_cookie_manager_test.dart @@ -0,0 +1,54 @@ +// 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:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:webview_flutter/src/v4/src/webview_cookie_manager.dart'; +import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; + +import 'webview_cookie_manager_test.mocks.dart'; + +@GenerateMocks([PlatformWebViewCookieManager]) +void main() { + group('WebViewCookieManager', () { + test('clearCookies', () async { + final MockPlatformWebViewCookieManager mockPlatformWebViewCookieManager = + MockPlatformWebViewCookieManager(); + when(mockPlatformWebViewCookieManager.clearCookies()).thenAnswer( + (_) => Future.value(false), + ); + + final WebViewCookieManager cookieManager = + WebViewCookieManager.fromPlatform( + platform: mockPlatformWebViewCookieManager, + ); + + await expectLater(cookieManager.clearCookies(), completion(false)); + }); + + test('setCookie', () async { + final MockPlatformWebViewCookieManager mockPlatformWebViewCookieManager = + MockPlatformWebViewCookieManager(); + + final WebViewCookieManager cookieManager = + WebViewCookieManager.fromPlatform( + platform: mockPlatformWebViewCookieManager, + ); + + const WebViewCookie cookie = WebViewCookie( + name: 'name', + value: 'value', + domain: 'domain', + ); + + await cookieManager.setCookie(cookie); + + final WebViewCookie capturedCookie = verify( + mockPlatformWebViewCookieManager.setCookie(captureAny), + ).captured.single as WebViewCookie; + expect(capturedCookie, cookie); + }); + }); +} diff --git a/packages/webview_flutter/webview_flutter/test/v4/webview_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/v4/webview_cookie_manager_test.mocks.dart new file mode 100644 index 000000000000..4bca8b6a1f12 --- /dev/null +++ b/packages/webview_flutter/webview_flutter/test/v4/webview_cookie_manager_test.mocks.dart @@ -0,0 +1,56 @@ +// Mocks generated by Mockito 5.3.0 from annotations +// in webview_flutter/test/v4/webview_cookie_manager_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i4; + +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_cookie_manager.dart' + as _i3; +import 'package:webview_flutter_platform_interface/v4/src/webview_platform.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: 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 + +class _FakePlatformWebViewCookieManagerCreationParams_0 extends _i1.SmartFake + implements _i2.PlatformWebViewCookieManagerCreationParams { + _FakePlatformWebViewCookieManagerCreationParams_0( + Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +/// A class which mocks [PlatformWebViewCookieManager]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockPlatformWebViewCookieManager extends _i1.Mock + implements _i3.PlatformWebViewCookieManager { + MockPlatformWebViewCookieManager() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.PlatformWebViewCookieManagerCreationParams get params => + (super.noSuchMethod(Invocation.getter(#params), + returnValue: _FakePlatformWebViewCookieManagerCreationParams_0( + this, Invocation.getter(#params))) + as _i2.PlatformWebViewCookieManagerCreationParams); + @override + _i4.Future clearCookies() => + (super.noSuchMethod(Invocation.method(#clearCookies, []), + returnValue: _i4.Future.value(false)) as _i4.Future); + @override + _i4.Future setCookie(_i2.WebViewCookie? cookie) => (super.noSuchMethod( + Invocation.method(#setCookie, [cookie]), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value()) as _i4.Future); +} From 6d1d1ce2ab04fb3866c2634ff2423426d551f5ef Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 1 Sep 2022 09:39:04 -0400 Subject: [PATCH 677/844] Update shared_preferences CODEOWNER (#6347) --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 6562217907f0..04cc2b1c4468 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -15,7 +15,7 @@ packages/local_auth/** @stuartmorgan packages/path_provider/** @gaaclarke packages/plugin_platform_interface/** @stuartmorgan packages/quick_actions/** @stuartmorgan -packages/shared_preferences/** @gaaclarke +packages/shared_preferences/** @tarrinneal packages/url_launcher/** @stuartmorgan packages/video_player/** @gaaclarke packages/webview_flutter/** @bparrishMines From 8b70ec886b5237ceb78d2bd97d222aba8fbc7974 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 1 Sep 2022 10:40:24 -0400 Subject: [PATCH 678/844] [google_maps_flutter] Deprecate AndroidGoogleMapsFlutter (#6335) --- .../google_maps_flutter/CHANGELOG.md | 4 +++- .../google_maps_flutter/README.md | 14 ++++---------- .../google_maps_flutter/example/lib/main.dart | 10 ++++++---- .../google_maps_flutter/example/pubspec.yaml | 2 ++ .../google_maps_flutter/lib/src/google_map.dart | 15 ++++++++------- .../google_maps_flutter/pubspec.yaml | 2 +- 6 files changed, 24 insertions(+), 23 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index be73df702d76..291ba6230a60 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,5 +1,7 @@ -## NEXT +## 2.2.0 +* Deprecates `AndroidGoogleMapsFlutter.useAndroidViewSurface` in favor of + [setting the flag directly in the Android implementation](https://pub.dev/packages/google_maps_flutter_android#display-mode). * Updates minimum Flutter version to 2.10. ## 2.1.12 diff --git a/packages/google_maps_flutter/google_maps_flutter/README.md b/packages/google_maps_flutter/google_maps_flutter/README.md index ae9a659c715f..58726a1faaa1 100644 --- a/packages/google_maps_flutter/google_maps_flutter/README.md +++ b/packages/google_maps_flutter/google_maps_flutter/README.md @@ -50,17 +50,11 @@ This means that app will only be available for users that run Android SDK 20 or android:value="YOUR KEY HERE"/> ``` -#### Hybrid Composition +#### Display Mode -To use [Hybrid Composition](https://flutter.dev/docs/development/platform-integration/platform-views) -to render the `GoogleMap` widget on Android, set `AndroidGoogleMapsFlutter.useAndroidViewSurface` to -true. - -```dart -if (defaultTargetPlatform == TargetPlatform.android) { - AndroidGoogleMapsFlutter.useAndroidViewSurface = true; -} -``` +The Android implementation supports multiple +[platform view display modes](https://flutter.dev/docs/development/platform-integration/platform-views). +For details, see [the Android README](https://pub.dev/packages/google_maps_flutter_android#display-mode). ### iOS diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart index 8932705bc6d5..b11f29977f23 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart @@ -2,11 +2,11 @@ // 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:google_maps_flutter/google_maps_flutter.dart'; +import 'package:google_maps_flutter_android/google_maps_flutter_android.dart'; import 'package:google_maps_flutter_example/lite_mode.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'animate_camera.dart'; import 'map_click.dart'; import 'map_coordinates.dart'; @@ -71,8 +71,10 @@ class MapsDemo extends StatelessWidget { } void main() { - if (defaultTargetPlatform == TargetPlatform.android) { - AndroidGoogleMapsFlutter.useAndroidViewSurface = true; + final GoogleMapsFlutterPlatform mapsImplementation = + GoogleMapsFlutterPlatform.instance; + if (mapsImplementation is GoogleMapsFlutterAndroid) { + mapsImplementation.useAndroidViewSurface = true; } runApp(const MaterialApp(home: MapsDemo())); } 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 6736fce066b2..77e9b9497410 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml @@ -18,6 +18,8 @@ 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_android: ^2.1.10 + google_maps_flutter_platform_interface: ^2.2.1 dev_dependencies: espresso: ^0.1.0+2 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 6bb5e75f91ec..1f7871068cab 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 @@ -40,10 +40,11 @@ class UnknownMapObjectIdError extends Error { } /// Android specific settings for [GoogleMap]. -// TODO(stuartmorgan): Deprecate this in favor of pointing people who want to -// change this to using the Android implementation Dart class directly. This -// should be done as part of switching the default to hybrid composition. +@Deprecated( + 'See https://pub.dev/packages/google_maps_flutter_android#display-mode') class AndroidGoogleMapsFlutter { + @Deprecated( + 'See https://pub.dev/packages/google_maps_flutter_android#display-mode') AndroidGoogleMapsFlutter._(); /// Whether to render [GoogleMap] with a [AndroidViewSurface] to build the Google Maps widget. @@ -53,8 +54,8 @@ class AndroidGoogleMapsFlutter { /// versions below 10. See /// https://flutter.dev/docs/development/platform-integration/platform-views#performance for more /// information. - /// - /// Defaults to false. + @Deprecated( + 'See https://pub.dev/packages/google_maps_flutter_android#display-mode') static bool get useAndroidViewSurface { final GoogleMapsFlutterPlatform platform = GoogleMapsFlutterPlatform.instance; @@ -71,8 +72,8 @@ class AndroidGoogleMapsFlutter { /// versions below 10. See /// https://flutter.dev/docs/development/platform-integration/platform-views#performance for more /// information. - /// - /// Defaults to false. + @Deprecated( + 'See https://pub.dev/packages/google_maps_flutter_android#display-mode') static set useAndroidViewSurface(bool useAndroidViewSurface) { final GoogleMapsFlutterPlatform platform = GoogleMapsFlutterPlatform.instance; diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index 95c4e98c1dd0..dd8c82d4d4e5 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/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.1.12 +version: 2.2.0 environment: sdk: ">=2.14.0 <3.0.0" From 23cd9a136356177056aa94848dddef1db52b1e2b Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 1 Sep 2022 11:28:10 -0400 Subject: [PATCH 679/844] [webview_flutter_wkwebview] Implementation of WebViewWidget with WebKit (#6342) --- .../v4/src/webkit_navigation_delegate.dart | 161 ---------- .../lib/src/v4/src/webkit_proxy.dart | 2 +- .../src/v4/src/webkit_webview_controller.dart | 284 ++++++++++++++++-- .../src/v4/src/webkit_webview_platform.dart | 10 +- .../lib/src/v4/webview_flutter_wkwebview.dart | 3 +- .../webview_flutter_wkwebview/pubspec.yaml | 2 +- .../v4/webkit_navigation_delegate_test.dart | 42 +-- .../v4/webkit_webview_controller_test.dart | 24 +- .../test/v4/webkit_webview_widget_test.dart | 59 ++++ 9 files changed, 366 insertions(+), 221 deletions(-) delete mode 100644 packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_navigation_delegate.dart create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_widget_test.dart diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_navigation_delegate.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_navigation_delegate.dart deleted file mode 100644 index 10b1fab52e03..000000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_navigation_delegate.dart +++ /dev/null @@ -1,161 +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 'dart:async'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; - -import '../../foundation/foundation.dart'; -import '../../web_kit/web_kit.dart'; -import 'webkit_proxy.dart'; - -/// An implementation of [WebResourceError] with the WebKit API. -class WebKitWebResourceError extends WebResourceError { - WebKitWebResourceError._(this._nsError) - : super( - errorCode: _nsError.code, - description: _nsError.localizedDescription, - errorType: _toWebResourceErrorType(_nsError.code), - ); - - static WebResourceErrorType? _toWebResourceErrorType(int code) { - switch (code) { - case WKErrorCode.unknown: - return WebResourceErrorType.unknown; - case WKErrorCode.webContentProcessTerminated: - return WebResourceErrorType.webContentProcessTerminated; - case WKErrorCode.webViewInvalidated: - return WebResourceErrorType.webViewInvalidated; - case WKErrorCode.javaScriptExceptionOccurred: - return WebResourceErrorType.javaScriptExceptionOccurred; - case WKErrorCode.javaScriptResultTypeIsUnsupported: - return WebResourceErrorType.javaScriptResultTypeIsUnsupported; - } - - return null; - } - - /// A string representing the domain of the error. - String? get domain => _nsError.domain; - - final NSError _nsError; -} - -/// An implementation of [PlatformNavigationDelegate] with the WebKit API. -class WebKitNavigationDelegate extends PlatformNavigationDelegate { - /// Constructs a [WebKitNavigationDelegate]. - WebKitNavigationDelegate( - super.params, { - @visibleForTesting WebKitProxy webKitProxy = const WebKitProxy(), - }) : super.implementation() { - final WeakReference weakThis = - WeakReference(this); - navigationDelegate = webKitProxy.createNavigationDelegate( - didFinishNavigation: (WKWebView webView, String? url) { - if (weakThis.target?._onPageFinished != null) { - weakThis.target!._onPageFinished!(url ?? ''); - } - }, - didStartProvisionalNavigation: (WKWebView webView, String? url) { - if (weakThis.target?._onPageStarted != null) { - weakThis.target!._onPageStarted!(url ?? ''); - } - }, - decidePolicyForNavigationAction: ( - WKWebView webView, - WKNavigationAction action, - ) async { - if (weakThis.target?._onNavigationRequest != null) { - final bool allow = await weakThis.target!._onNavigationRequest!( - url: action.request.url, - isForMainFrame: action.targetFrame.isMainFrame, - ); - return allow - ? WKNavigationActionPolicy.allow - : WKNavigationActionPolicy.cancel; - } - return WKNavigationActionPolicy.allow; - }, - didFailNavigation: (WKWebView webView, NSError error) { - if (weakThis.target?._onWebResourceError != null) { - weakThis.target!._onWebResourceError!( - WebKitWebResourceError._(error), - ); - } - }, - didFailProvisionalNavigation: (WKWebView webView, NSError error) { - if (weakThis.target?._onWebResourceError != null) { - weakThis.target!._onWebResourceError!( - WebKitWebResourceError._(error), - ); - } - }, - webViewWebContentProcessDidTerminate: (WKWebView webView) { - if (weakThis.target?._onWebResourceError != null) { - weakThis.target!._onWebResourceError!( - WebKitWebResourceError._( - const NSError( - code: WKErrorCode.webContentProcessTerminated, - // Value from https://developer.apple.com/documentation/webkit/wkerrordomain?language=objc. - domain: 'WKErrorDomain', - localizedDescription: '', - ), - ), - ); - } - }, - ); - } - - // Used to set `WKWebView.setNavigationDelegate` in `WebKitWebViewController`. - /// WebKit class that handles navigation changes and tracking navigation - /// requests. - late final WKNavigationDelegate navigationDelegate; - - void Function(String url)? _onPageFinished; - void Function(String url)? _onPageStarted; - void Function(int progress)? _onProgress; - void Function(WebResourceError error)? _onWebResourceError; - FutureOr Function({required String url, required bool isForMainFrame})? - _onNavigationRequest; - - // `WKWebView` in `WebKitWebViewController` uses this to track loading - // progress. This can't be done with `WKNavigationDelegate`. - /// Callback method that receives progress of a loading page. - void Function(int progress)? get onProgress => _onProgress; - - @override - Future setOnPageFinished( - void Function(String url) onPageFinished, - ) async { - _onPageFinished = onPageFinished; - } - - @override - Future setOnPageStarted(void Function(String url) onPageStarted) async { - _onPageStarted = onPageStarted; - } - - @override - Future setOnProgress(void Function(int progress) onProgress) async { - _onProgress = onProgress; - } - - @override - Future setOnWebResourceError( - void Function(WebResourceError error) onWebResourceError, - ) async { - _onWebResourceError = onWebResourceError; - } - - @override - Future setOnNavigationRequest( - FutureOr Function({required String url, required bool isForMainFrame}) - onNavigationRequest, - ) async { - _onNavigationRequest = onNavigationRequest; - } -} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_proxy.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_proxy.dart index 10f922efd0d5..e3d1f609ef9c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_proxy.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_proxy.dart @@ -36,7 +36,7 @@ class WebKitProxy { String keyPath, NSObject object, Map change, - ) + )? observeValue, }) createWebView; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_controller.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_controller.dart index dc0693e5f9a5..117aa784dc3f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_controller.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_controller.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/foundation.dart'; @@ -10,10 +11,10 @@ import 'package:flutter/services.dart'; import 'package:path/path.dart' as path; import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; +import '../../common/instance_manager.dart'; import '../../common/weak_reference_utils.dart'; import '../../foundation/foundation.dart'; import '../../web_kit/web_kit.dart'; -import 'webkit_navigation_delegate.dart'; import 'webkit_proxy.dart'; /// Object specifying creation parameters for a [WebKitWebViewController]. @@ -22,7 +23,7 @@ class WebKitWebViewControllerCreationParams extends PlatformWebViewControllerCreationParams { /// Constructs a [WebKitWebViewControllerCreationParams]. WebKitWebViewControllerCreationParams({ - @visibleForTesting WebKitProxy webKitProxy = const WebKitProxy(), + @visibleForTesting this.webKitProxy = const WebKitProxy(), }) : _configuration = webKitProxy.createWebViewConfiguration(); /// Constructs a [WebKitWebViewControllerCreationParams] using a @@ -35,44 +36,49 @@ class WebKitWebViewControllerCreationParams }) : this(webKitProxy: webKitProxy); final WKWebViewConfiguration _configuration; + + /// Handles constructing objects and calling static methods for the WebKit + /// native library. + @visibleForTesting + final WebKitProxy webKitProxy; } /// An implementation of [PlatformWebViewController] with the WebKit api. class WebKitWebViewController extends PlatformWebViewController { /// Constructs a [WebKitWebViewController]. - WebKitWebViewController( - PlatformWebViewControllerCreationParams params, { - @visibleForTesting WebKitProxy webKitProxy = const WebKitProxy(), - }) : super.implementation(params is WebKitWebViewControllerCreationParams + WebKitWebViewController(PlatformWebViewControllerCreationParams params) + : super.implementation(params is WebKitWebViewControllerCreationParams ? params : WebKitWebViewControllerCreationParams .fromPlatformWebViewControllerCreationParams(params)) { - final WeakReference weakThis = - WeakReference(this); - _webView = webKitProxy.createWebView( - (params as WebKitWebViewControllerCreationParams)._configuration, + _webView.addObserver( + _webView, + keyPath: 'estimatedProgress', + options: { + NSKeyValueObservingOptions.newValue, + }, + ); + } + + /// The WebKit WebView being controlled. + late final WKWebView _webView = withWeakRefenceTo(this, ( + WeakReference weakReference, + ) { + return _webKitParams.webKitProxy.createWebView( + _webKitParams._configuration, observeValue: ( String keyPath, NSObject object, Map change, ) { - if (weakThis.target?._onProgress != null) { + if (weakReference.target?._onProgress != null) { final double progress = change[NSKeyValueChangeKey.newValue]! as double; - weakThis.target!._onProgress!((progress * 100).round()); + weakReference.target!._onProgress!((progress * 100).round()); } }, ); - _webView.addObserver( - _webView, - keyPath: 'estimatedProgress', - options: { - NSKeyValueObservingOptions.newValue, - }, - ); - } - - late final WKWebView _webView; + }); final Map _javaScriptChannelParams = {}; @@ -80,6 +86,9 @@ class WebKitWebViewController extends PlatformWebViewController { bool _zoomEnabled = true; void Function(int progress)? _onProgress; + WebKitWebViewControllerCreationParams get _webKitParams => + params as WebKitWebViewControllerCreationParams; + @override Future loadFile(String absoluteFilePath) { return _webView.loadFileUrl( @@ -297,8 +306,8 @@ class WebKitWebViewController extends PlatformWebViewController { Future setPlatformNavigationDelegate( covariant WebKitNavigationDelegate handler, ) { - _onProgress = handler.onProgress; - return _webView.setNavigationDelegate(handler.navigationDelegate); + _onProgress = handler._onProgress; + return _webView.setNavigationDelegate(handler._navigationDelegate); } Future _disableZoom() { @@ -383,3 +392,230 @@ class WebKitJavaScriptChannelParams extends JavaScriptChannelParams { final WKScriptMessageHandler _messageHandler; } + +/// Object specifying creation parameters for a [WebKitWebViewWidget]. +@immutable +class WebKitWebViewWidgetCreationParams + extends PlatformWebViewWidgetCreationParams { + /// Constructs a [WebKitWebViewWidgetCreationParams]. + WebKitWebViewWidgetCreationParams({ + super.key, + required super.controller, + super.layoutDirection, + super.gestureRecognizers, + @visibleForTesting InstanceManager? instanceManager, + }) : _instanceManager = instanceManager ?? NSObject.globalInstanceManager; + + /// Constructs a [WebKitWebViewWidgetCreationParams] using a + /// [PlatformWebViewWidgetCreationParams]. + WebKitWebViewWidgetCreationParams.fromPlatformWebViewWidgetCreationParams( + PlatformWebViewWidgetCreationParams params, { + InstanceManager? instanceManager, + }) : this( + key: params.key, + controller: params.controller, + layoutDirection: params.layoutDirection, + gestureRecognizers: params.gestureRecognizers, + instanceManager: instanceManager, + ); + + // Maintains instances used to communicate with the native objects they + // represent. + final InstanceManager _instanceManager; +} + +/// An implementation of [PlatformWebViewWidget] with the WebKit api. +class WebKitWebViewWidget extends PlatformWebViewWidget { + /// Constructs a [WebKitWebViewWidget]. + WebKitWebViewWidget(PlatformWebViewWidgetCreationParams params) + : super.implementation( + params is WebKitWebViewWidgetCreationParams + ? params + : WebKitWebViewWidgetCreationParams + .fromPlatformWebViewWidgetCreationParams(params), + ); + + WebKitWebViewWidgetCreationParams get _webKitParams => + params as WebKitWebViewWidgetCreationParams; + + @override + Widget build(BuildContext context) { + return UiKitView( + viewType: 'plugins.flutter.io/webview', + onPlatformViewCreated: (_) {}, + layoutDirection: params.layoutDirection, + gestureRecognizers: params.gestureRecognizers, + creationParams: _webKitParams._instanceManager.getIdentifier( + (params.controller as WebKitWebViewController)._webView), + creationParamsCodec: const StandardMessageCodec(), + ); + } +} + +/// An implementation of [WebResourceError] with the WebKit API. +class WebKitWebResourceError extends WebResourceError { + WebKitWebResourceError._(this._nsError) + : super( + errorCode: _nsError.code, + description: _nsError.localizedDescription, + errorType: _toWebResourceErrorType(_nsError.code), + ); + + static WebResourceErrorType? _toWebResourceErrorType(int code) { + switch (code) { + case WKErrorCode.unknown: + return WebResourceErrorType.unknown; + case WKErrorCode.webContentProcessTerminated: + return WebResourceErrorType.webContentProcessTerminated; + case WKErrorCode.webViewInvalidated: + return WebResourceErrorType.webViewInvalidated; + case WKErrorCode.javaScriptExceptionOccurred: + return WebResourceErrorType.javaScriptExceptionOccurred; + case WKErrorCode.javaScriptResultTypeIsUnsupported: + return WebResourceErrorType.javaScriptResultTypeIsUnsupported; + } + + return null; + } + + /// A string representing the domain of the error. + String? get domain => _nsError.domain; + + final NSError _nsError; +} + +/// Object specifying creation parameters for a [WebKitNavigationDelegate]. +@immutable +class WebKitNavigationDelegateCreationParams + extends PlatformNavigationDelegateCreationParams { + /// Constructs a [WebKitNavigationDelegateCreationParams]. + const WebKitNavigationDelegateCreationParams({ + @visibleForTesting this.webKitProxy = const WebKitProxy(), + }); + + /// Constructs a [WebKitNavigationDelegateCreationParams] using a + /// [PlatformNavigationDelegateCreationParams]. + const WebKitNavigationDelegateCreationParams.fromPlatformNavigationDelegateCreationParams( + // Recommended placeholder to prevent being broken by platform interface. + // ignore: avoid_unused_constructor_parameters + PlatformNavigationDelegateCreationParams params, { + @visibleForTesting WebKitProxy webKitProxy = const WebKitProxy(), + }) : this(webKitProxy: webKitProxy); + + /// Handles constructing objects and calling static methods for the WebKit + /// native library. + @visibleForTesting + final WebKitProxy webKitProxy; +} + +/// An implementation of [PlatformNavigationDelegate] with the WebKit API. +class WebKitNavigationDelegate extends PlatformNavigationDelegate { + /// Constructs a [WebKitNavigationDelegate]. + WebKitNavigationDelegate(PlatformNavigationDelegateCreationParams params) + : super.implementation(params is WebKitNavigationDelegateCreationParams + ? params + : WebKitNavigationDelegateCreationParams + .fromPlatformNavigationDelegateCreationParams(params)) { + final WeakReference weakThis = + WeakReference(this); + _navigationDelegate = (params as WebKitNavigationDelegateCreationParams) + .webKitProxy + .createNavigationDelegate( + didFinishNavigation: (WKWebView webView, String? url) { + if (weakThis.target?._onPageFinished != null) { + weakThis.target!._onPageFinished!(url ?? ''); + } + }, + didStartProvisionalNavigation: (WKWebView webView, String? url) { + if (weakThis.target?._onPageStarted != null) { + weakThis.target!._onPageStarted!(url ?? ''); + } + }, + decidePolicyForNavigationAction: ( + WKWebView webView, + WKNavigationAction action, + ) async { + if (weakThis.target?._onNavigationRequest != null) { + final bool allow = await weakThis.target!._onNavigationRequest!( + url: action.request.url, + isForMainFrame: action.targetFrame.isMainFrame, + ); + return allow + ? WKNavigationActionPolicy.allow + : WKNavigationActionPolicy.cancel; + } + return WKNavigationActionPolicy.allow; + }, + didFailNavigation: (WKWebView webView, NSError error) { + if (weakThis.target?._onWebResourceError != null) { + weakThis.target!._onWebResourceError!( + WebKitWebResourceError._(error), + ); + } + }, + didFailProvisionalNavigation: (WKWebView webView, NSError error) { + if (weakThis.target?._onWebResourceError != null) { + weakThis.target!._onWebResourceError!( + WebKitWebResourceError._(error), + ); + } + }, + webViewWebContentProcessDidTerminate: (WKWebView webView) { + if (weakThis.target?._onWebResourceError != null) { + weakThis.target!._onWebResourceError!( + WebKitWebResourceError._( + const NSError( + code: WKErrorCode.webContentProcessTerminated, + // Value from https://developer.apple.com/documentation/webkit/wkerrordomain?language=objc. + domain: 'WKErrorDomain', + localizedDescription: '', + ), + ), + ); + } + }, + ); + } + + // Used to set `WKWebView.setNavigationDelegate` in `WebKitWebViewController`. + late final WKNavigationDelegate _navigationDelegate; + + void Function(String url)? _onPageFinished; + void Function(String url)? _onPageStarted; + void Function(int progress)? _onProgress; + void Function(WebResourceError error)? _onWebResourceError; + FutureOr Function({required String url, required bool isForMainFrame})? + _onNavigationRequest; + + @override + Future setOnPageFinished( + void Function(String url) onPageFinished, + ) async { + _onPageFinished = onPageFinished; + } + + @override + Future setOnPageStarted(void Function(String url) onPageStarted) async { + _onPageStarted = onPageStarted; + } + + @override + Future setOnProgress(void Function(int progress) onProgress) async { + _onProgress = onProgress; + } + + @override + Future setOnWebResourceError( + void Function(WebResourceError error) onWebResourceError, + ) async { + _onWebResourceError = onWebResourceError; + } + + @override + Future setOnNavigationRequest( + FutureOr Function({required String url, required bool isForMainFrame}) + onNavigationRequest, + ) async { + _onNavigationRequest = onNavigationRequest; + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_platform.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_platform.dart index aec446921d92..13116bb30b5c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_platform.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/src/webkit_webview_platform.dart @@ -3,10 +3,9 @@ // found in the LICENSE file. import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; -import 'package:webview_flutter_wkwebview/src/v4/src/webkit_webview_cookie_manager.dart'; -import 'webkit_navigation_delegate.dart'; import 'webkit_webview_controller.dart'; +import 'webkit_webview_cookie_manager.dart'; /// Implementation of [WebViewPlatform] using the WebKit API. class WebKitWebViewPlatform extends WebViewPlatform { @@ -24,6 +23,13 @@ class WebKitWebViewPlatform extends WebViewPlatform { return WebKitNavigationDelegate(params); } + @override + WebKitWebViewWidget createPlatformWebViewWidget( + PlatformWebViewWidgetCreationParams params, + ) { + return WebKitWebViewWidget(params); + } + @override WebKitWebViewCookieManager createPlatformCookieManager( PlatformWebViewCookieManagerCreationParams params, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/webview_flutter_wkwebview.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/webview_flutter_wkwebview.dart index 91180c9234ab..f54fb73bcda3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/webview_flutter_wkwebview.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/v4/webview_flutter_wkwebview.dart @@ -4,7 +4,6 @@ library webview_flutter_wkwebview; -export 'src/webkit_navigation_delegate.dart'; -export 'src/webkit_webview_controller.dart'; export 'src/webkit_webview_controller.dart'; export 'src/webkit_webview_cookie_manager.dart'; +export 'src/webkit_webview_platform.dart'; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index a4847d5db5ea..804d4f3b12bc 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: flutter: sdk: flutter path: ^1.8.0 - webview_flutter_platform_interface: ^1.9.0 + webview_flutter_platform_interface: ^1.9.3 dev_dependencies: build_runner: ^2.1.5 diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_navigation_delegate_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_navigation_delegate_test.dart index fb57c988ec32..fe57e94364bf 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_navigation_delegate_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_navigation_delegate_test.dart @@ -18,9 +18,10 @@ void main() { group('WebKitNavigationDelegate', () { test('setOnPageFinished', () { final WebKitNavigationDelegate webKitDelgate = WebKitNavigationDelegate( - const PlatformNavigationDelegateCreationParams(), - webKitProxy: const WebKitProxy( - createNavigationDelegate: CapturingNavigationDelegate.new, + const WebKitNavigationDelegateCreationParams( + webKitProxy: WebKitProxy( + createNavigationDelegate: CapturingNavigationDelegate.new, + ), ), ); @@ -37,9 +38,10 @@ void main() { test('setOnPageStarted', () { final WebKitNavigationDelegate webKitDelgate = WebKitNavigationDelegate( - const PlatformNavigationDelegateCreationParams(), - webKitProxy: const WebKitProxy( - createNavigationDelegate: CapturingNavigationDelegate.new, + const WebKitNavigationDelegateCreationParams( + webKitProxy: WebKitProxy( + createNavigationDelegate: CapturingNavigationDelegate.new, + ), ), ); @@ -57,9 +59,10 @@ void main() { test('onWebResourceError from didFailNavigation', () { final WebKitNavigationDelegate webKitDelgate = WebKitNavigationDelegate( - const PlatformNavigationDelegateCreationParams(), - webKitProxy: const WebKitProxy( - createNavigationDelegate: CapturingNavigationDelegate.new, + const WebKitNavigationDelegateCreationParams( + webKitProxy: WebKitProxy( + createNavigationDelegate: CapturingNavigationDelegate.new, + ), ), ); @@ -87,9 +90,10 @@ void main() { test('onWebResourceError from didFailProvisionalNavigation', () { final WebKitNavigationDelegate webKitDelgate = WebKitNavigationDelegate( - const PlatformNavigationDelegateCreationParams(), - webKitProxy: const WebKitProxy( - createNavigationDelegate: CapturingNavigationDelegate.new, + const WebKitNavigationDelegateCreationParams( + webKitProxy: WebKitProxy( + createNavigationDelegate: CapturingNavigationDelegate.new, + ), ), ); @@ -118,9 +122,10 @@ void main() { test('onWebResourceError from webViewWebContentProcessDidTerminate', () { final WebKitNavigationDelegate webKitDelgate = WebKitNavigationDelegate( - const PlatformNavigationDelegateCreationParams(), - webKitProxy: const WebKitProxy( - createNavigationDelegate: CapturingNavigationDelegate.new, + const WebKitNavigationDelegateCreationParams( + webKitProxy: WebKitProxy( + createNavigationDelegate: CapturingNavigationDelegate.new, + ), ), ); @@ -147,9 +152,10 @@ void main() { test('onNavigationRequest from decidePolicyForNavigationAction', () { final WebKitNavigationDelegate webKitDelgate = WebKitNavigationDelegate( - const PlatformNavigationDelegateCreationParams(), - webKitProxy: const WebKitProxy( - createNavigationDelegate: CapturingNavigationDelegate.new, + const WebKitNavigationDelegateCreationParams( + webKitProxy: WebKitProxy( + createNavigationDelegate: CapturingNavigationDelegate.new, + ), ), ); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart index 3f0e7f217fa9..87a90db9a766 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.dart @@ -58,12 +58,6 @@ void main() { WebKitWebViewControllerCreationParams( webKitProxy: WebKitProxy( createWebViewConfiguration: () => nonNullMockWebViewConfiguration, - ), - ); - - final WebKitWebViewController controller = WebKitWebViewController( - controllerCreationParams, - webKitProxy: WebKitProxy( createWebView: ( _, { void Function( @@ -84,6 +78,10 @@ void main() { ), ); + final WebKitWebViewController controller = WebKitWebViewController( + controllerCreationParams, + ); + when(nonNullMockWebView.scrollView) .thenReturn(mockScrollView ?? MockUIScrollView()); when(nonNullMockWebView.configuration) @@ -715,9 +713,10 @@ void main() { final WebKitNavigationDelegate navigationDelegate = WebKitNavigationDelegate( - const PlatformNavigationDelegateCreationParams(), - webKitProxy: const WebKitProxy( - createNavigationDelegate: CapturingNavigationDelegate.new, + const WebKitNavigationDelegateCreationParams( + webKitProxy: WebKitProxy( + createNavigationDelegate: CapturingNavigationDelegate.new, + ), ), ); @@ -766,9 +765,10 @@ void main() { final WebKitNavigationDelegate navigationDelegate = WebKitNavigationDelegate( - const PlatformNavigationDelegateCreationParams(), - webKitProxy: const WebKitProxy( - createNavigationDelegate: CapturingNavigationDelegate.new, + const WebKitNavigationDelegateCreationParams( + webKitProxy: WebKitProxy( + createNavigationDelegate: CapturingNavigationDelegate.new, + ), ), ); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_widget_test.dart new file mode 100644 index 000000000000..36a95d6f1cab --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_widget_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:webview_flutter_wkwebview/src/common/instance_manager.dart'; +import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; +import 'package:webview_flutter_wkwebview/src/v4/src/webkit_proxy.dart'; +import 'package:webview_flutter_wkwebview/src/v4/webview_flutter_wkwebview.dart'; +import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('WebKitWebViewWidget', () { + testWidgets('build', (WidgetTester tester) async { + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + + final WebKitWebViewController controller = WebKitWebViewController( + WebKitWebViewControllerCreationParams( + webKitProxy: WebKitProxy( + createWebView: ( + WKWebViewConfiguration configuration, { + void Function( + String keyPath, + NSObject object, + Map change, + )? + observeValue, + }) { + final WKWebView webView = WKWebView.detached( + instanceManager: instanceManager, + ); + instanceManager.addDartCreatedInstance(webView); + return webView; + }, + createWebViewConfiguration: WKWebViewConfiguration.detached, + ), + ), + ); + + final WebKitWebViewWidget widget = WebKitWebViewWidget( + WebKitWebViewWidgetCreationParams( + controller: controller, + instanceManager: instanceManager, + ), + ); + + await tester.pumpWidget( + Builder(builder: (BuildContext context) => widget.build(context)), + ); + + expect(find.byType(UiKitView), findsOneWidget); + }); + }); +} From c5703277cb74f9218dfe2c76bea61412c05b0434 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 1 Sep 2022 11:35:15 -0400 Subject: [PATCH 680/844] [google_maps_flutter] Default Android to Hybrid Composition (#6334) --- .../google_maps_flutter_android/CHANGELOG.md | 4 +- .../google_maps_flutter_android/README.md | 42 ++++++++++++++++++ .../example/build.excerpt.yaml | 15 +++++++ .../integration_test/google_maps_test.dart | 44 +++++++++++++++---- .../example/lib/readme_excerpts.dart | 21 +++++++++ .../example/pubspec.yaml | 1 + .../lib/src/google_maps_flutter_android.dart | 17 +++---- .../google_maps_flutter_android/pubspec.yaml | 2 +- .../google_maps_flutter_android_test.dart | 15 ++++++- 9 files changed, 138 insertions(+), 23 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/build.excerpt.yaml create mode 100644 packages/google_maps_flutter/google_maps_flutter_android/example/lib/readme_excerpts.dart 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 91f01f8abd15..01c98f388e61 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.3.0 +* Switches the default for `useAndroidViewSurface` to true, and adds + information about the current mode behaviors to the README. * Updates minimum Flutter version to 2.10. ## 2.2.0 diff --git a/packages/google_maps_flutter/google_maps_flutter_android/README.md b/packages/google_maps_flutter/google_maps_flutter_android/README.md index 5ac1df04a4df..877b9bbe9102 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/README.md +++ b/packages/google_maps_flutter/google_maps_flutter_android/README.md @@ -1,5 +1,7 @@ # google\_maps\_flutter\_android + + The Android implementation of [`google_maps_flutter`][1]. ## Usage @@ -8,5 +10,45 @@ This package is [endorsed][2], which means you can simply use `google_maps_flutter` normally. This package will be automatically included in your app when you do. +## Display Mode + +This plugin supports two different [platform view display modes][3]. The default +display mode is subject to change in the future, and will not be considered a +breaking change, so if you want to ensure a specific mode you can set it +explicitly: + + +```dart +import 'package:google_maps_flutter_android/google_maps_flutter_android.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +void main() { + // Require Hybrid Composition mode on Android. + final GoogleMapsFlutterPlatform mapsImplementation = + GoogleMapsFlutterPlatform.instance; + if (mapsImplementation is GoogleMapsFlutterAndroid) { + mapsImplementation.useAndroidViewSurface = true; + } + // ··· +} +``` + +### Hybrid Composition + +This is the current default mode, and corresponds to +`useAndroidViewSurface = true`. It ensures that the map display will work as +expected, at the cost of some performance. + +### Texture Layer Hybrid Composition + +This is a new display mode used by most plugins starting with Flutter 3.0, and +corresponds to `useAndroidViewSurface = false`. This is more performant than +Hybrid Composition, but currently [misses certain map updates][4]. + +This mode will likely become the default in future versions if/when the +missed updates issue can be resolved. + [1]: https://pub.dev/packages/google_maps_flutter [2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin +[3]: https://docs.flutter.dev/development/platform-integration/android/platform-views +[4]: https://github.com/flutter/flutter/issues/103686 diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/build.excerpt.yaml b/packages/google_maps_flutter/google_maps_flutter_android/example/build.excerpt.yaml new file mode 100644 index 000000000000..e317efa11cb3 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/build.excerpt.yaml @@ -0,0 +1,15 @@ +targets: + $default: + sources: + include: + - lib/** + # Some default includes that aren't really used here but will prevent + # false-negative warnings: + - $package$ + - lib/$lib$ + exclude: + - '**/.*/**' + - '**/build/**' + builders: + code_excerpter|code_excerpter: + enabled: true diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart index 8fc1ede0eb0f..0945740b1e45 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/integration_test/google_maps_test.dart @@ -23,6 +23,28 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); GoogleMapsFlutterPlatform.instance.enableDebugInspection(); + // Repeatedly checks an asynchronous value against a test condition, waiting + // on frame between each check, returing the value if it passes the predicate + // before [maxTries] is reached. + // + // Returns null if the predicate is never satisfied. + // + // This is useful for cases where the Maps SDK has some internally + // asynchronous operation that we don't have visibility into (e.g., native UI + // animations). + Future waitForValueMatchingPredicate(WidgetTester tester, + Future Function() getValue, bool Function(T) predicate, + {int maxTries = 100}) async { + for (int i = 0; i < maxTries; i++) { + final T value = await getValue(); + if (predicate(value)) { + return value; + } + await tester.pump(); + } + return null; + } + testWidgets('uses surface view', (WidgetTester tester) async { final GoogleMapsFlutterAndroid instance = GoogleMapsFlutterPlatform.instance as GoogleMapsFlutterAndroid; @@ -484,12 +506,13 @@ void main() { final ExampleGoogleMapController mapController = await mapControllerCompleter.future; + // Wait for the visible region to be non-zero. final LatLngBounds firstVisibleRegion = - await mapController.getVisibleRegion(); - - expect(firstVisibleRegion, isNotNull); - expect(firstVisibleRegion.southwest, isNotNull); - expect(firstVisibleRegion.northeast, isNotNull); + await waitForValueMatchingPredicate( + tester, + () => mapController.getVisibleRegion(), + (LatLngBounds bounds) => bounds != zeroLatLngBounds) ?? + zeroLatLngBounds; expect(firstVisibleRegion, isNot(zeroLatLngBounds)); expect(firstVisibleRegion.contains(_kInitialMapCenter), isTrue); @@ -520,9 +543,6 @@ void main() { final LatLngBounds secondVisibleRegion = await mapController.getVisibleRegion(); - expect(secondVisibleRegion, isNotNull); - expect(secondVisibleRegion.southwest, isNotNull); - expect(secondVisibleRegion.northeast, isNotNull); expect(secondVisibleRegion, isNot(zeroLatLngBounds)); expect(firstVisibleRegion, isNot(secondVisibleRegion)); @@ -922,7 +942,13 @@ void main() { expect(iwVisibleStatus, false); await controller.showMarkerInfoWindow(marker.markerId); - iwVisibleStatus = await controller.isMarkerInfoWindowShown(marker.markerId); + // The Maps SDK doesn't always return true for whether it is shown + // immediately after showing it, so wait for it to report as shown. + iwVisibleStatus = await waitForValueMatchingPredicate( + tester, + () => controller.isMarkerInfoWindowShown(marker.markerId), + (bool visible) => visible) ?? + false; expect(iwVisibleStatus, true); await controller.hideMarkerInfoWindow(marker.markerId); diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/readme_excerpts.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/readme_excerpts.dart new file mode 100644 index 000000000000..5911c062e444 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/readme_excerpts.dart @@ -0,0 +1,21 @@ +// 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'; +// #docregion DisplayMode +import 'package:google_maps_flutter_android/google_maps_flutter_android.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +void main() { + // Require Hybrid Composition mode on Android. + final GoogleMapsFlutterPlatform mapsImplementation = + GoogleMapsFlutterPlatform.instance; + if (mapsImplementation is GoogleMapsFlutterAndroid) { + mapsImplementation.useAndroidViewSurface = true; + } + // #enddocregion DisplayMode + runApp(const MaterialApp()); + // #docregion DisplayMode +} +// #enddocregion DisplayMode 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 778af9a018fa..cd7123050a38 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 @@ -21,6 +21,7 @@ dependencies: google_maps_flutter_platform_interface: ^2.2.1 dev_dependencies: + build_runner: ^2.1.10 espresso: ^0.1.0+2 flutter_driver: sdk: flutter 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 95dea4c5d938..06c5bdcd7e0f 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 @@ -471,19 +471,14 @@ class GoogleMapsFlutterAndroid extends GoogleMapsFlutterPlatform { return _channel(mapId).invokeMethod('map#takeSnapshot'); } - /// Set [GoogleMapsFlutterPlatform] to use [AndroidViewSurface] to build the Google Maps widget. + /// Set [GoogleMapsFlutterPlatform] to use [AndroidViewSurface] to build the + /// Google Maps widget. /// - /// This implementation uses hybrid composition to render the Google Maps - /// Widget on Android. This comes at the cost of some performance on Android - /// versions below 10. See - /// https://flutter.dev/docs/development/platform-integration/platform-views#performance for more - /// information. + /// See https://pub.dev/packages/google_maps_flutter_android#display-mode + /// for more information. /// - /// If set to true, the google map widget should be built with - /// [buildViewWithTextDirection] instead of [buildView]. - /// - /// Defaults to false. - bool useAndroidViewSurface = false; + /// Currently defaults to true, but the default is subject to change. + bool useAndroidViewSurface = true; Widget _buildView( int creationId, 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 2ee7c820db43..c820f31d1c46 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/plugins/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.2.0 +version: 2.3.0 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart b/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart index cba23d072dbf..431c2472945e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/test/google_maps_flutter_android_test.dart @@ -124,9 +124,10 @@ void main() { }); test( - 'Default widget is AndroidView', + 'Does not use PlatformViewLink when using TLHC', () async { final GoogleMapsFlutterAndroid maps = GoogleMapsFlutterAndroid(); + maps.useAndroidViewSurface = false; final Widget widget = maps.buildViewWithConfiguration(1, (int _) {}, widgetConfiguration: const MapWidgetConfiguration( initialCameraPosition: @@ -150,4 +151,16 @@ void main() { expect(widget, isA()); }); + + testWidgets('Defaults to surface view', (WidgetTester tester) async { + final GoogleMapsFlutterAndroid maps = GoogleMapsFlutterAndroid(); + + final Widget widget = maps.buildViewWithConfiguration(1, (int _) {}, + widgetConfiguration: const MapWidgetConfiguration( + initialCameraPosition: + CameraPosition(target: LatLng(0, 0), zoom: 1), + textDirection: TextDirection.ltr)); + + expect(widget, isA()); + }); } From d1da5a8fa1136b61689174cbfb15c86fcb48e670 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 1 Sep 2022 12:23:55 -0400 Subject: [PATCH 681/844] Roll Flutter from 069f5042973d to 759d0e1b20c1 (16 revisions) (#6349) --- .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 e262c65abbe9..44ff2a555eb4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -069f5042973dc4aafbf76a44ca14af7c0e2c2ca1 +759d0e1b20c1759f2d6469e9e6d16b545a2e0792 From 3b5e641fc281eeabd65ee2ef65967664ad1b83b4 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 1 Sep 2022 14:04:22 -0400 Subject: [PATCH 682/844] [google_maps_flutter] Fix integration tests (#6350) Some integration tests behave slightly differently on Android when using Hybrid Composition, and need to wait for a change to become true rather than it being immediately true. This replicates the changes from `google_maps_flutter_android` to the app-facing copy of those integration tests. Fixes tree breakage from publishing https://github.com/flutter/plugins/pull/6334 --- .../integration_test/google_maps_test.dart | 44 +++++++++++++++---- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart index 7479917402ee..38a02ea0d8f1 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/integration_test/google_maps_test.dart @@ -22,6 +22,28 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); GoogleMapsFlutterPlatform.instance.enableDebugInspection(); + // Repeatedly checks an asynchronous value against a test condition, waiting + // one frame between each check, returing the value if it passes the predicate + // before [maxTries] is reached. + // + // Returns null if the predicate is never satisfied. + // + // This is useful for cases where the Maps SDK has some internally + // asynchronous operation that we don't have visibility into (e.g., native UI + // animations). + Future waitForValueMatchingPredicate(WidgetTester tester, + Future Function() getValue, bool Function(T) predicate, + {int maxTries = 100}) async { + for (int i = 0; i < maxTries; i++) { + final T value = await getValue(); + if (predicate(value)) { + return value; + } + await tester.pump(); + } + return null; + } + testWidgets('testCompassToggle', (WidgetTester tester) async { final Key key = GlobalKey(); final Completer mapIdCompleter = Completer(); @@ -481,12 +503,13 @@ void main() { final GoogleMapController mapController = await mapControllerCompleter.future; + // Wait for the visible region to be non-zero. final LatLngBounds firstVisibleRegion = - await mapController.getVisibleRegion(); - - expect(firstVisibleRegion, isNotNull); - expect(firstVisibleRegion.southwest, isNotNull); - expect(firstVisibleRegion.northeast, isNotNull); + await waitForValueMatchingPredicate( + tester, + () => mapController.getVisibleRegion(), + (LatLngBounds bounds) => bounds != zeroLatLngBounds) ?? + zeroLatLngBounds; expect(firstVisibleRegion, isNot(zeroLatLngBounds)); expect(firstVisibleRegion.contains(_kInitialMapCenter), isTrue); @@ -517,9 +540,6 @@ void main() { final LatLngBounds secondVisibleRegion = await mapController.getVisibleRegion(); - expect(secondVisibleRegion, isNotNull); - expect(secondVisibleRegion.southwest, isNotNull); - expect(secondVisibleRegion.northeast, isNotNull); expect(secondVisibleRegion, isNot(zeroLatLngBounds)); expect(firstVisibleRegion, isNot(secondVisibleRegion)); @@ -906,7 +926,13 @@ void main() { expect(iwVisibleStatus, false); await controller.showMarkerInfoWindow(marker.markerId); - iwVisibleStatus = await controller.isMarkerInfoWindowShown(marker.markerId); + // The Maps SDK doesn't always return true for whether it is shown + // immediately after showing it, so wait for it to report as shown. + iwVisibleStatus = await waitForValueMatchingPredicate( + tester, + () => controller.isMarkerInfoWindowShown(marker.markerId), + (bool visible) => visible) ?? + false; expect(iwVisibleStatus, true); await controller.hideMarkerInfoWindow(marker.markerId); From 237425c59d0c81a43f4d45bb13395454c7083d5e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 20:30:16 +0000 Subject: [PATCH 683/844] [video_player]: Bump mockito-core from 4.6.1 to 4.7.0 in /packages/video_player/video_player/example/android/app (#6263) --- .../video_player/video_player/example/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player/example/android/app/build.gradle b/packages/video_player/video_player/example/android/app/build.gradle index a2658065f17d..338eeb8944f7 100644 --- a/packages/video_player/video_player/example/android/app/build.gradle +++ b/packages/video_player/video_player/example/android/app/build.gradle @@ -60,7 +60,7 @@ flutter { dependencies { testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.8.1' - testImplementation 'org.mockito:mockito-core:4.6.1' + testImplementation 'org.mockito:mockito-core:4.7.0' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } From e863d62ee68ad8bba1716fa2140c0e8175b3da86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 20:31:46 +0000 Subject: [PATCH 684/844] [google_maps]: Bump rules from 1.2.0 to 1.4.0 in /packages/google_maps_flutter/google_maps_flutter_android/android (#6197) --- .../google_maps_flutter_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle index 50428892f555..54195fa40c1b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle @@ -37,7 +37,7 @@ android { implementation "androidx.annotation:annotation:1.1.0" implementation 'com.google.android.gms:play-services-maps:18.0.2' androidTestImplementation 'androidx.test:runner:1.2.0' - androidTestImplementation 'androidx.test:rules:1.2.0' + androidTestImplementation 'androidx.test:rules:1.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:4.7.0' From 060d1509330453005275e820a8a37fd27cbb1740 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Fri, 2 Sep 2022 06:22:13 -0700 Subject: [PATCH 685/844] Add missing submodule command to setup for packages repo. (#6276) --- script/tool/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/script/tool/README.md b/script/tool/README.md index 9b91b03eadaf..0e44bf16c263 100644 --- a/script/tool/README.md +++ b/script/tool/README.md @@ -17,6 +17,12 @@ flutter/packages.) The commands in tools require the Flutter-bundled version of Dart to be the first `dart` loaded in the path. +### Extra Setup + +When updating sample code excerpts (`update-excerpts`) for the README.md files, +there is some [extra setup for +submodules](#update-readmemd-from-example-sources) that is necessary. + ### From Source (flutter/plugins only) Set up: From c6e92af0ca0ff2520a5888c1cbcf88aead1ede45 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 2 Sep 2022 12:56:54 -0400 Subject: [PATCH 686/844] Roll Flutter from 759d0e1b20c1 to 02857c90cc6b (25 revisions) (#6353) --- .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 44ff2a555eb4..d4232d2a1eca 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -759d0e1b20c1759f2d6469e9e6d16b545a2e0792 +02857c90cc6b3eab83d7b8f778eb3fb32ec9ecbf From 644d85be6adedbe8f56cbb3c5efb6cdabe78e50f Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Sun, 4 Sep 2022 00:01:55 +0200 Subject: [PATCH 687/844] [path_provider] Update win32 (#6352) --- packages/path_provider/path_provider_windows/CHANGELOG.md | 3 ++- packages/path_provider/path_provider_windows/pubspec.yaml | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/path_provider/path_provider_windows/CHANGELOG.md b/packages/path_provider/path_provider_windows/CHANGELOG.md index 9f42b3d9471b..757f13dbb533 100644 --- a/packages/path_provider/path_provider_windows/CHANGELOG.md +++ b/packages/path_provider/path_provider_windows/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.1.3 * Updates minimum Flutter version to 2.10. +* Adds compatibility with `package:win32` 3.x. ## 2.1.2 diff --git a/packages/path_provider/path_provider_windows/pubspec.yaml b/packages/path_provider/path_provider_windows/pubspec.yaml index 421a0957dcb2..c89e9a833f72 100644 --- a/packages/path_provider/path_provider_windows/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider_windows description: Windows implementation of the path_provider plugin repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/path_provider_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%22 -version: 2.1.2 +version: 2.1.3 environment: sdk: ">=2.17.0 <3.0.0" @@ -21,7 +21,7 @@ dependencies: sdk: flutter path: ^1.8.0 path_provider_platform_interface: ^2.0.0 - win32: ^2.1.0 + win32: ">=2.1.0 <4.0.0" dev_dependencies: flutter_test: From 3c7209408af83a89943d2074862f070d1665023f Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Sun, 4 Sep 2022 00:21:55 -0400 Subject: [PATCH 688/844] [video_player] Remove default method channel implementation (#6341) Eliminates `MethodChannelVideoPlayer`, and instead makes the default a no-op subclass of the interface (i.e., one that throws `UnimplementedError` for everything). This moves the plugin from option 3 to option 2 in https://docs.flutter.dev/go/platform-channels-in-federated-plugins. This implementation is being removed because its use of Pigeon made ongoing maintenance impractical. Since default implementations are intended for backward compatibility with third-party native-only implementations, the platform channel internals of default implementations are considered part of the API surface. Pigeon considers those details implementation details, which means it was impossible to ever update the version of Pigeon used here without breaking changes. This means that fundamentally, Pigeon is not a viable choice for default implementations. The alternative to removing it would be replacing it with a new non-Pigeon method channel implementation, but that would also be a breaking change, defeating the purpose of backward compatibility. This would only benefit new third-party implementations, but new implementations should implement the platform interface rather than using the legacy default implementation. Fixes https://github.com/flutter/flutter/issues/110017 --- .../CHANGELOG.md | 6 +- .../CONTRIBUTING.md | 46 -- .../lib/messages.g.dart | 425 ------------------ .../lib/method_channel_video_player.dart | 171 ------- .../lib/video_player_platform_interface.dart | 11 +- .../pigeons/messages.dart | 63 --- .../pubspec.yaml | 3 +- .../method_channel_video_player_test.dart | 368 --------------- .../test/test.dart | 199 -------- .../video_player_platform_interface_test.dart | 15 + 10 files changed, 27 insertions(+), 1280 deletions(-) delete mode 100644 packages/video_player/video_player_platform_interface/CONTRIBUTING.md delete mode 100644 packages/video_player/video_player_platform_interface/lib/messages.g.dart delete mode 100644 packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart delete mode 100644 packages/video_player/video_player_platform_interface/pigeons/messages.dart delete mode 100644 packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart delete mode 100644 packages/video_player/video_player_platform_interface/test/test.dart create mode 100644 packages/video_player/video_player_platform_interface/test/video_player_platform_interface_test.dart diff --git a/packages/video_player/video_player_platform_interface/CHANGELOG.md b/packages/video_player/video_player_platform_interface/CHANGELOG.md index 89e3a05b20e4..05cb63835da4 100644 --- a/packages/video_player/video_player_platform_interface/CHANGELOG.md +++ b/packages/video_player/video_player_platform_interface/CHANGELOG.md @@ -1,5 +1,9 @@ -## NEXT +## 6.0.0 +* **BREAKING CHANGE**: Removes `MethodChannelVideoPlayer`. The default + implementation is now only a placeholder with no functionality; + implementations of `video_player` must include their own `VideoPlayerPlatform` + Dart implementation. * Updates minimum Flutter version to 2.10. * Fixes violations of new analysis option use_named_constants. diff --git a/packages/video_player/video_player_platform_interface/CONTRIBUTING.md b/packages/video_player/video_player_platform_interface/CONTRIBUTING.md deleted file mode 100644 index 4108ae0d0030..000000000000 --- a/packages/video_player/video_player_platform_interface/CONTRIBUTING.md +++ /dev/null @@ -1,46 +0,0 @@ -## Updating pigeon-generated files - -**WARNING**: Because `messages.dart` is part of the public API of this package, -breaking changes in that file are breaking changes for the package. This means -that: -- You should never update the version of Pigeon used for this package unless - making a breaking change to the package for other reasons. -- Because the method channel is a legacy implementation for compatibility with - existing third-party `video_player` implementations, in many cases the best - option may be to simply not implemented new features in - `MethodChannelVideoPlayer`. Breaking changes in this package should never - be made solely to change `MethodChannelVideoPlayer`. - -### Update process - -If you update files in the pigeons/ directory, run the following -command in this directory (ignore the errors you get about -dependencies in the examples directory): - -```bash -flutter pub upgrade -flutter pub run pigeon --dart_null_safety --input pigeons/messages.dart -# git commit your changes so that your working environment is clean -(cd ../../../; ./script/tool_runner.sh format --clang-format=clang-format-7) -``` - -If you update pigeon itself and want to test the changes here, -temporarily update the pubspec.yaml by adding the following to the -`dependency_overrides` section, assuming you have checked out the -`flutter/packages` repo in a sibling directory to the `plugins` repo: - -```yaml - pigeon: - path: - ../../../../packages/packages/pigeon/ -``` - -Then, run the commands above. When you run `pub get` it should warn -you that you're using an override. If you do this, you will need to -publish pigeon before you can land the updates to this package, since -the CI tests run the analysis using latest published version of -pigeon, not your version or the version on `main`. - -In either case, the configuration will be obtained automatically from -the `pigeons/messages.dart` file (see `configurePigeon` at the bottom -of that file). diff --git a/packages/video_player/video_player_platform_interface/lib/messages.g.dart b/packages/video_player/video_player_platform_interface/lib/messages.g.dart deleted file mode 100644 index 831f4e3755d9..000000000000 --- a/packages/video_player/video_player_platform_interface/lib/messages.g.dart +++ /dev/null @@ -1,425 +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. - -// Autogenerated from Pigeon (v0.1.21), 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, cast_nullable_to_non_nullable -// @dart = 2.12 -import 'dart:async'; -import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; - -import 'package:flutter/services.dart'; - -class TextureMessage { - int? textureId; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['textureId'] = textureId; - return pigeonMap; - } - - static TextureMessage decode(Object message) { - final Map pigeonMap = message as Map; - return TextureMessage()..textureId = pigeonMap['textureId'] as int?; - } -} - -class CreateMessage { - String? asset; - String? uri; - String? packageName; - String? formatHint; - Map? httpHeaders; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['asset'] = asset; - pigeonMap['uri'] = uri; - pigeonMap['packageName'] = packageName; - pigeonMap['formatHint'] = formatHint; - pigeonMap['httpHeaders'] = httpHeaders; - return pigeonMap; - } - - static CreateMessage decode(Object message) { - final Map pigeonMap = message as Map; - return CreateMessage() - ..asset = pigeonMap['asset'] as String? - ..uri = pigeonMap['uri'] as String? - ..packageName = pigeonMap['packageName'] as String? - ..formatHint = pigeonMap['formatHint'] as String? - ..httpHeaders = pigeonMap['httpHeaders'] as Map?; - } -} - -class LoopingMessage { - int? textureId; - bool? isLooping; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['textureId'] = textureId; - pigeonMap['isLooping'] = isLooping; - return pigeonMap; - } - - static LoopingMessage decode(Object message) { - final Map pigeonMap = message as Map; - return LoopingMessage() - ..textureId = pigeonMap['textureId'] as int? - ..isLooping = pigeonMap['isLooping'] as bool?; - } -} - -class VolumeMessage { - int? textureId; - double? volume; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['textureId'] = textureId; - pigeonMap['volume'] = volume; - return pigeonMap; - } - - static VolumeMessage decode(Object message) { - final Map pigeonMap = message as Map; - return VolumeMessage() - ..textureId = pigeonMap['textureId'] as int? - ..volume = pigeonMap['volume'] as double?; - } -} - -class PlaybackSpeedMessage { - int? textureId; - double? speed; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['textureId'] = textureId; - pigeonMap['speed'] = speed; - return pigeonMap; - } - - static PlaybackSpeedMessage decode(Object message) { - final Map pigeonMap = message as Map; - return PlaybackSpeedMessage() - ..textureId = pigeonMap['textureId'] as int? - ..speed = pigeonMap['speed'] as double?; - } -} - -class PositionMessage { - int? textureId; - int? position; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['textureId'] = textureId; - pigeonMap['position'] = position; - return pigeonMap; - } - - static PositionMessage decode(Object message) { - final Map pigeonMap = message as Map; - return PositionMessage() - ..textureId = pigeonMap['textureId'] as int? - ..position = pigeonMap['position'] as int?; - } -} - -class MixWithOthersMessage { - bool? mixWithOthers; - - Object encode() { - final Map pigeonMap = {}; - pigeonMap['mixWithOthers'] = mixWithOthers; - return pigeonMap; - } - - static MixWithOthersMessage decode(Object message) { - final Map pigeonMap = message as Map; - return MixWithOthersMessage() - ..mixWithOthers = pigeonMap['mixWithOthers'] as bool?; - } -} - -class VideoPlayerApi { - Future initialize() async { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.initialize', StandardMessageCodec()); - final Map? replyMap = - await channel.send(null) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future create(CreateMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.create', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - return TextureMessage.decode(replyMap['result']!); - } - } - - Future dispose(TextureMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.dispose', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future setLooping(LoopingMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setLooping', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future setVolume(VolumeMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setVolume', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future setPlaybackSpeed(PlaybackSpeedMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', - StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future play(TextureMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.play', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future position(TextureMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - return PositionMessage.decode(replyMap['result']!); - } - } - - Future seekTo(PositionMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.seekTo', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future pause(TextureMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.pause', StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } - - Future setMixWithOthers(MixWithOthersMessage arg) async { - final Object encoded = arg.encode(); - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', - StandardMessageCodec()); - final Map? replyMap = - await channel.send(encoded) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - details: null, - ); - } else if (replyMap['error'] != null) { - final Map error = - replyMap['error'] as Map; - throw PlatformException( - code: error['code'] as String, - message: error['message'] as String?, - details: error['details'], - ); - } else { - // noop - } - } -} diff --git a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart b/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart deleted file mode 100644 index 0c2b4b8d6b14..000000000000 --- a/packages/video_player/video_player_platform_interface/lib/method_channel_video_player.dart +++ /dev/null @@ -1,171 +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 'dart:async'; - -import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; - -import 'messages.g.dart'; -import 'video_player_platform_interface.dart'; - -/// An implementation of [VideoPlayerPlatform] that uses method channels. -/// -/// This is the default implementation, for compatibility with existing -/// third-party implementations. It is not used by other implementations in -/// this repository. -class MethodChannelVideoPlayer extends VideoPlayerPlatform { - final VideoPlayerApi _api = VideoPlayerApi(); - - @override - Future init() { - return _api.initialize(); - } - - @override - Future dispose(int textureId) { - return _api.dispose(TextureMessage()..textureId = textureId); - } - - @override - Future create(DataSource dataSource) async { - final CreateMessage message = CreateMessage(); - - switch (dataSource.sourceType) { - case DataSourceType.asset: - message.asset = dataSource.asset; - message.packageName = dataSource.package; - break; - case DataSourceType.network: - message.uri = dataSource.uri; - message.formatHint = _videoFormatStringMap[dataSource.formatHint]; - message.httpHeaders = dataSource.httpHeaders; - break; - case DataSourceType.file: - message.uri = dataSource.uri; - break; - case DataSourceType.contentUri: - message.uri = dataSource.uri; - break; - } - - final TextureMessage response = await _api.create(message); - return response.textureId; - } - - @override - Future setLooping(int textureId, bool looping) { - return _api.setLooping(LoopingMessage() - ..textureId = textureId - ..isLooping = looping); - } - - @override - Future play(int textureId) { - return _api.play(TextureMessage()..textureId = textureId); - } - - @override - Future pause(int textureId) { - return _api.pause(TextureMessage()..textureId = textureId); - } - - @override - Future setVolume(int textureId, double volume) { - return _api.setVolume(VolumeMessage() - ..textureId = textureId - ..volume = volume); - } - - @override - Future setPlaybackSpeed(int textureId, double speed) { - assert(speed > 0); - - return _api.setPlaybackSpeed(PlaybackSpeedMessage() - ..textureId = textureId - ..speed = speed); - } - - @override - Future seekTo(int textureId, Duration position) { - return _api.seekTo(PositionMessage() - ..textureId = textureId - ..position = position.inMilliseconds); - } - - @override - Future getPosition(int textureId) async { - final PositionMessage response = - await _api.position(TextureMessage()..textureId = textureId); - return Duration(milliseconds: response.position!); - } - - @override - Stream videoEventsFor(int textureId) { - return _eventChannelFor(textureId) - .receiveBroadcastStream() - .map((dynamic event) { - final Map map = event as Map; - switch (map['event']) { - case 'initialized': - return VideoEvent( - eventType: VideoEventType.initialized, - duration: Duration(milliseconds: map['duration']! as int), - size: Size((map['width'] as num?)?.toDouble() ?? 0.0, - (map['height'] as num?)?.toDouble() ?? 0.0), - rotationCorrection: map['rotationCorrection'] as int? ?? 0, - ); - case 'completed': - return VideoEvent( - eventType: VideoEventType.completed, - ); - case 'bufferingUpdate': - final List values = map['values']! as List; - - return VideoEvent( - buffered: values.map(_toDurationRange).toList(), - eventType: VideoEventType.bufferingUpdate, - ); - case 'bufferingStart': - return VideoEvent(eventType: VideoEventType.bufferingStart); - case 'bufferingEnd': - return VideoEvent(eventType: VideoEventType.bufferingEnd); - default: - return VideoEvent(eventType: VideoEventType.unknown); - } - }); - } - - @override - Widget buildView(int textureId) { - return Texture(textureId: textureId); - } - - @override - Future setMixWithOthers(bool mixWithOthers) { - return _api.setMixWithOthers( - MixWithOthersMessage()..mixWithOthers = mixWithOthers, - ); - } - - EventChannel _eventChannelFor(int textureId) { - return EventChannel('flutter.io/videoPlayer/videoEvents$textureId'); - } - - static const Map _videoFormatStringMap = - { - VideoFormat.ss: 'ss', - VideoFormat.hls: 'hls', - VideoFormat.dash: 'dash', - VideoFormat.other: 'other', - }; - - DurationRange _toDurationRange(dynamic value) { - final List pair = value as List; - return DurationRange( - Duration(milliseconds: pair[0]! as int), - Duration(milliseconds: pair[1]! as int), - ); - } -} diff --git a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart index 78173f1fb63c..92099eb6635a 100644 --- a/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart +++ b/packages/video_player/video_player_platform_interface/lib/video_player_platform_interface.dart @@ -6,8 +6,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'method_channel_video_player.dart'; - /// The interface that implementations of video_player must implement. /// /// Platform implementations should extend this class rather than implement it as `video_player` @@ -21,11 +19,12 @@ abstract class VideoPlayerPlatform extends PlatformInterface { static final Object _token = Object(); - static VideoPlayerPlatform _instance = MethodChannelVideoPlayer(); + static VideoPlayerPlatform _instance = _PlaceholderImplementation(); - /// The default instance of [VideoPlayerPlatform] to use. + /// The instance of [VideoPlayerPlatform] to use. /// - /// Defaults to [MethodChannelVideoPlayer]. + /// Defaults to a placeholder that does not override any methods, and thus + /// throws `UnimplementedError` in most cases. static VideoPlayerPlatform get instance => _instance; /// Platform-specific plugins should override this with their own @@ -105,6 +104,8 @@ abstract class VideoPlayerPlatform extends PlatformInterface { } } +class _PlaceholderImplementation extends VideoPlayerPlatform {} + /// Description of the data source used to create an instance of /// the video player. class DataSource { diff --git a/packages/video_player/video_player_platform_interface/pigeons/messages.dart b/packages/video_player/video_player_platform_interface/pigeons/messages.dart deleted file mode 100644 index 7a3490a955d4..000000000000 --- a/packages/video_player/video_player_platform_interface/pigeons/messages.dart +++ /dev/null @@ -1,63 +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. - -// @dart = 2.9 - -import 'package:pigeon/pigeon_lib.dart'; - -class TextureMessage { - int textureId; -} - -class LoopingMessage { - int textureId; - bool isLooping; -} - -class VolumeMessage { - int textureId; - double volume; -} - -class PlaybackSpeedMessage { - int textureId; - double speed; -} - -class PositionMessage { - int textureId; - int position; -} - -class CreateMessage { - String asset; - String uri; - String packageName; - String formatHint; - Map httpHeaders; -} - -class MixWithOthersMessage { - bool mixWithOthers; -} - -@HostApi(dartHostTestHandler: 'TestHostVideoPlayerApi') -abstract class VideoPlayerApi { - void initialize(); - TextureMessage create(CreateMessage msg); - void dispose(TextureMessage msg); - void setLooping(LoopingMessage msg); - void setVolume(VolumeMessage msg); - void setPlaybackSpeed(PlaybackSpeedMessage msg); - void play(TextureMessage msg); - PositionMessage position(TextureMessage msg); - void seekTo(PositionMessage msg); - void pause(TextureMessage msg); - void setMixWithOthers(MixWithOthersMessage msg); -} - -void configurePigeon(PigeonOptions opts) { - opts.dartOut = 'lib/messages.g.dart'; - opts.dartTestOut = 'test/test.dart'; -} diff --git a/packages/video_player/video_player_platform_interface/pubspec.yaml b/packages/video_player/video_player_platform_interface/pubspec.yaml index 965641d2ee75..56e132dbb3a7 100644 --- a/packages/video_player/video_player_platform_interface/pubspec.yaml +++ b/packages/video_player/video_player_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/video_player/v issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%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: 5.1.4 +version: 6.0.0 environment: sdk: ">=2.12.0 <3.0.0" @@ -18,4 +18,3 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - pigeon: 0.1.21 diff --git a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart b/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart deleted file mode 100644 index 7d64ac8ab7d7..000000000000 --- a/packages/video_player/video_player_platform_interface/test/method_channel_video_player_test.dart +++ /dev/null @@ -1,368 +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. - -// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) -// ignore: unnecessary_import -import 'dart:ui'; - -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:video_player_platform_interface/messages.g.dart'; -import 'package:video_player_platform_interface/method_channel_video_player.dart'; -import 'package:video_player_platform_interface/video_player_platform_interface.dart'; - -import 'test.dart'; - -class _ApiLogger implements TestHostVideoPlayerApi { - final List log = []; - TextureMessage? textureMessage; - CreateMessage? createMessage; - PositionMessage? positionMessage; - LoopingMessage? loopingMessage; - VolumeMessage? volumeMessage; - PlaybackSpeedMessage? playbackSpeedMessage; - MixWithOthersMessage? mixWithOthersMessage; - - @override - TextureMessage create(CreateMessage arg) { - log.add('create'); - createMessage = arg; - return TextureMessage()..textureId = 3; - } - - @override - void dispose(TextureMessage arg) { - log.add('dispose'); - textureMessage = arg; - } - - @override - void initialize() { - log.add('init'); - } - - @override - void pause(TextureMessage arg) { - log.add('pause'); - textureMessage = arg; - } - - @override - void play(TextureMessage arg) { - log.add('play'); - textureMessage = arg; - } - - @override - void setMixWithOthers(MixWithOthersMessage arg) { - log.add('setMixWithOthers'); - mixWithOthersMessage = arg; - } - - @override - PositionMessage position(TextureMessage arg) { - log.add('position'); - textureMessage = arg; - return PositionMessage()..position = 234; - } - - @override - void seekTo(PositionMessage arg) { - log.add('seekTo'); - positionMessage = arg; - } - - @override - void setLooping(LoopingMessage arg) { - log.add('setLooping'); - loopingMessage = arg; - } - - @override - void setVolume(VolumeMessage arg) { - log.add('setVolume'); - volumeMessage = arg; - } - - @override - void setPlaybackSpeed(PlaybackSpeedMessage arg) { - log.add('setPlaybackSpeed'); - playbackSpeedMessage = arg; - } -} - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - // Store the initial instance before any tests change it. - final VideoPlayerPlatform initialInstance = VideoPlayerPlatform.instance; - - group('$VideoPlayerPlatform', () { - test('$MethodChannelVideoPlayer() is the default instance', () { - expect(initialInstance, isInstanceOf()); - }); - }); - - group('$MethodChannelVideoPlayer', () { - final MethodChannelVideoPlayer player = MethodChannelVideoPlayer(); - late _ApiLogger log; - - setUp(() { - log = _ApiLogger(); - TestHostVideoPlayerApi.setup(log); - }); - - test('init', () async { - await player.init(); - expect( - log.log.last, - 'init', - ); - }); - - test('dispose', () async { - await player.dispose(1); - expect(log.log.last, 'dispose'); - expect(log.textureMessage?.textureId, 1); - }); - - test('create with asset', () async { - final int? textureId = await player.create(DataSource( - sourceType: DataSourceType.asset, - asset: 'someAsset', - package: 'somePackage', - )); - expect(log.log.last, 'create'); - expect(log.createMessage?.asset, 'someAsset'); - expect(log.createMessage?.packageName, 'somePackage'); - expect(textureId, 3); - }); - - test('create with network', () async { - final int? textureId = await player.create(DataSource( - sourceType: DataSourceType.network, - uri: 'someUri', - formatHint: VideoFormat.dash, - )); - expect(log.log.last, 'create'); - expect(log.createMessage?.asset, null); - expect(log.createMessage?.uri, 'someUri'); - expect(log.createMessage?.packageName, null); - expect(log.createMessage?.formatHint, 'dash'); - expect(log.createMessage?.httpHeaders, {}); - expect(textureId, 3); - }); - - test('create with network (some headers)', () async { - final int? textureId = await player.create(DataSource( - sourceType: DataSourceType.network, - uri: 'someUri', - httpHeaders: {'Authorization': 'Bearer token'}, - )); - expect(log.log.last, 'create'); - expect(log.createMessage?.asset, null); - expect(log.createMessage?.uri, 'someUri'); - expect(log.createMessage?.packageName, null); - expect(log.createMessage?.formatHint, null); - expect(log.createMessage?.httpHeaders, - {'Authorization': 'Bearer token'}); - expect(textureId, 3); - }); - - test('create with file', () async { - final int? textureId = await player.create(DataSource( - sourceType: DataSourceType.file, - uri: 'someUri', - )); - expect(log.log.last, 'create'); - expect(log.createMessage?.uri, 'someUri'); - expect(textureId, 3); - }); - - test('setLooping', () async { - await player.setLooping(1, true); - expect(log.log.last, 'setLooping'); - expect(log.loopingMessage?.textureId, 1); - expect(log.loopingMessage?.isLooping, true); - }); - - test('play', () async { - await player.play(1); - expect(log.log.last, 'play'); - expect(log.textureMessage?.textureId, 1); - }); - - test('pause', () async { - await player.pause(1); - expect(log.log.last, 'pause'); - expect(log.textureMessage?.textureId, 1); - }); - - test('setMixWithOthers', () async { - await player.setMixWithOthers(true); - expect(log.log.last, 'setMixWithOthers'); - expect(log.mixWithOthersMessage?.mixWithOthers, true); - - await player.setMixWithOthers(false); - expect(log.log.last, 'setMixWithOthers'); - expect(log.mixWithOthersMessage?.mixWithOthers, false); - }); - - test('setVolume', () async { - await player.setVolume(1, 0.7); - expect(log.log.last, 'setVolume'); - expect(log.volumeMessage?.textureId, 1); - expect(log.volumeMessage?.volume, 0.7); - }); - - test('setPlaybackSpeed', () async { - await player.setPlaybackSpeed(1, 1.5); - expect(log.log.last, 'setPlaybackSpeed'); - expect(log.playbackSpeedMessage?.textureId, 1); - expect(log.playbackSpeedMessage?.speed, 1.5); - }); - - test('seekTo', () async { - await player.seekTo(1, const Duration(milliseconds: 12345)); - expect(log.log.last, 'seekTo'); - expect(log.positionMessage?.textureId, 1); - expect(log.positionMessage?.position, 12345); - }); - - test('getPosition', () async { - final Duration position = await player.getPosition(1); - expect(log.log.last, 'position'); - expect(log.textureMessage?.textureId, 1); - expect(position, const Duration(milliseconds: 234)); - }); - - test('videoEventsFor', () async { - _ambiguate(ServicesBinding.instance) - ?.defaultBinaryMessenger - .setMockMessageHandler( - 'flutter.io/videoPlayer/videoEvents123', - (ByteData? message) async { - final MethodCall methodCall = - const StandardMethodCodec().decodeMethodCall(message); - if (methodCall.method == 'listen') { - await _ambiguate(ServicesBinding.instance) - ?.defaultBinaryMessenger - .handlePlatformMessage( - 'flutter.io/videoPlayer/videoEvents123', - const StandardMethodCodec() - .encodeSuccessEnvelope({ - 'event': 'initialized', - 'duration': 98765, - 'width': 1920, - 'height': 1080, - }), - (ByteData? data) {}); - - await _ambiguate(ServicesBinding.instance) - ?.defaultBinaryMessenger - .handlePlatformMessage( - 'flutter.io/videoPlayer/videoEvents123', - const StandardMethodCodec() - .encodeSuccessEnvelope({ - 'event': 'initialized', - 'duration': 98765, - 'width': 1920, - 'height': 1080, - 'rotationCorrection': 180, - }), - (ByteData? data) {}); - - await _ambiguate(ServicesBinding.instance) - ?.defaultBinaryMessenger - .handlePlatformMessage( - 'flutter.io/videoPlayer/videoEvents123', - const StandardMethodCodec() - .encodeSuccessEnvelope({ - 'event': 'completed', - }), - (ByteData? data) {}); - - await _ambiguate(ServicesBinding.instance) - ?.defaultBinaryMessenger - .handlePlatformMessage( - 'flutter.io/videoPlayer/videoEvents123', - const StandardMethodCodec() - .encodeSuccessEnvelope({ - 'event': 'bufferingUpdate', - 'values': >[ - [0, 1234], - [1235, 4000], - ], - }), - (ByteData? data) {}); - - await _ambiguate(ServicesBinding.instance) - ?.defaultBinaryMessenger - .handlePlatformMessage( - 'flutter.io/videoPlayer/videoEvents123', - const StandardMethodCodec() - .encodeSuccessEnvelope({ - 'event': 'bufferingStart', - }), - (ByteData? data) {}); - - await _ambiguate(ServicesBinding.instance) - ?.defaultBinaryMessenger - .handlePlatformMessage( - 'flutter.io/videoPlayer/videoEvents123', - const StandardMethodCodec() - .encodeSuccessEnvelope({ - 'event': 'bufferingEnd', - }), - (ByteData? data) {}); - - return const StandardMethodCodec().encodeSuccessEnvelope(null); - } else if (methodCall.method == 'cancel') { - return const StandardMethodCodec().encodeSuccessEnvelope(null); - } else { - fail('Expected listen or cancel'); - } - }, - ); - expect( - player.videoEventsFor(123), - emitsInOrder([ - VideoEvent( - eventType: VideoEventType.initialized, - duration: const Duration(milliseconds: 98765), - size: const Size(1920, 1080), - rotationCorrection: 0, - ), - VideoEvent( - eventType: VideoEventType.initialized, - duration: const Duration(milliseconds: 98765), - size: const Size(1920, 1080), - rotationCorrection: 180, - ), - VideoEvent(eventType: VideoEventType.completed), - VideoEvent( - eventType: VideoEventType.bufferingUpdate, - buffered: [ - DurationRange( - Duration.zero, - const Duration(milliseconds: 1234), - ), - DurationRange( - const Duration(milliseconds: 1235), - const Duration(milliseconds: 4000), - ), - ]), - VideoEvent(eventType: VideoEventType.bufferingStart), - VideoEvent(eventType: VideoEventType.bufferingEnd), - ])); - }); - }); -} - -/// This allows a value of type T or T? to be treated as a value of type T?. -/// -/// We use this so that APIs that have become non-nullable can still be used -/// with `!` and `?` on the stable branch. -// TODO(ianh): Remove this once we roll stable in late 2021. -T? _ambiguate(T? value) => value; diff --git a/packages/video_player/video_player_platform_interface/test/test.dart b/packages/video_player/video_player_platform_interface/test/test.dart deleted file mode 100644 index c696434826a3..000000000000 --- a/packages/video_player/video_player_platform_interface/test/test.dart +++ /dev/null @@ -1,199 +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. - -// Autogenerated from Pigeon (v0.1.21), 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 -// @dart = 2.12 -import 'dart:async'; -import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:video_player_platform_interface/messages.g.dart'; - -abstract class TestHostVideoPlayerApi { - void initialize(); - TextureMessage create(CreateMessage arg); - void dispose(TextureMessage arg); - void setLooping(LoopingMessage arg); - void setVolume(VolumeMessage arg); - void setPlaybackSpeed(PlaybackSpeedMessage arg); - void play(TextureMessage arg); - PositionMessage position(TextureMessage arg); - void seekTo(PositionMessage arg); - void pause(TextureMessage arg); - void setMixWithOthers(MixWithOthersMessage arg); - static void setup(TestHostVideoPlayerApi? api) { - { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.initialize', - StandardMessageCodec()); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - // ignore message - api.initialize(); - return {}; - }); - } - } - { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.create', StandardMessageCodec()); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.create was null. Expected CreateMessage.'); - final CreateMessage input = CreateMessage.decode(message!); - final TextureMessage output = api.create(input); - return {'result': output.encode()}; - }); - } - } - { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.dispose', StandardMessageCodec()); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.dispose was null. Expected TextureMessage.'); - final TextureMessage input = TextureMessage.decode(message!); - api.dispose(input); - return {}; - }); - } - } - { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setLooping', - StandardMessageCodec()); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setLooping was null. Expected LoopingMessage.'); - final LoopingMessage input = LoopingMessage.decode(message!); - api.setLooping(input); - return {}; - }); - } - } - { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setVolume', - StandardMessageCodec()); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setVolume was null. Expected VolumeMessage.'); - final VolumeMessage input = VolumeMessage.decode(message!); - api.setVolume(input); - return {}; - }); - } - } - { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed', - StandardMessageCodec()); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed was null. Expected PlaybackSpeedMessage.'); - final PlaybackSpeedMessage input = - PlaybackSpeedMessage.decode(message!); - api.setPlaybackSpeed(input); - return {}; - }); - } - } - { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.play', StandardMessageCodec()); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.play was null. Expected TextureMessage.'); - final TextureMessage input = TextureMessage.decode(message!); - api.play(input); - return {}; - }); - } - } - { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.position', StandardMessageCodec()); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.position was null. Expected TextureMessage.'); - final TextureMessage input = TextureMessage.decode(message!); - final PositionMessage output = api.position(input); - return {'result': output.encode()}; - }); - } - } - { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.seekTo', StandardMessageCodec()); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.seekTo was null. Expected PositionMessage.'); - final PositionMessage input = PositionMessage.decode(message!); - api.seekTo(input); - return {}; - }); - } - } - { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.pause', StandardMessageCodec()); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.pause was null. Expected TextureMessage.'); - final TextureMessage input = TextureMessage.decode(message!); - api.pause(input); - return {}; - }); - } - } - { - const BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers', - StandardMessageCodec()); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers was null. Expected MixWithOthersMessage.'); - final MixWithOthersMessage input = - MixWithOthersMessage.decode(message!); - api.setMixWithOthers(input); - return {}; - }); - } - } - } -} diff --git a/packages/video_player/video_player_platform_interface/test/video_player_platform_interface_test.dart b/packages/video_player/video_player_platform_interface/test/video_player_platform_interface_test.dart new file mode 100644 index 000000000000..8aa7ad9bd3c1 --- /dev/null +++ b/packages/video_player/video_player_platform_interface/test/video_player_platform_interface_test.dart @@ -0,0 +1,15 @@ +// 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:video_player_platform_interface/video_player_platform_interface.dart'; + +void main() { + // Store the initial instance before any tests change it. + final VideoPlayerPlatform initialInstance = VideoPlayerPlatform.instance; + + test('default implementation throws uninimpletemented', () async { + await expectLater(() => initialInstance.init(), throwsUnimplementedError); + }); +} From d3cd32b2e2842f396740b6f3884ba05c7d0d542c Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Sat, 3 Sep 2022 21:22:19 -0700 Subject: [PATCH 689/844] [camera] Avoid joining thread causing ANR (#6224) --- packages/camera/camera_android/CHANGELOG.md | 3 ++- .../java/io/flutter/plugins/camera/Camera.java | 12 ------------ .../io/flutter/plugins/camera/CameraTest.java | 17 ++--------------- packages/camera/camera_android/pubspec.yaml | 2 +- 4 files changed, 5 insertions(+), 29 deletions(-) diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index 024836d6f43e..f2417fb20645 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.10.0+2 +* Removes call to `join` on the camera's background `HandlerThread`. * Updates minimum Flutter version to 2.10. ## 0.10.0+1 diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java index 401963c91374..3d2df98b60da 100644 --- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java +++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/Camera.java @@ -131,8 +131,6 @@ class Camera /** An additional thread for running tasks that shouldn't block the UI. */ private HandlerThread backgroundHandlerThread; - /** True when backgroundHandlerThread is in the process of being stopped. */ - private boolean stoppingBackgroundHandlerThread = false; private CameraDeviceWrapper cameraDevice; private CameraCaptureSession captureSession; @@ -671,21 +669,11 @@ public void startBackgroundThread() { /** Stops the background thread and its {@link Handler}. */ public void stopBackgroundThread() { - if (stoppingBackgroundHandlerThread) { - return; - } if (backgroundHandlerThread != null) { - stoppingBackgroundHandlerThread = true; backgroundHandlerThread.quitSafely(); - try { - backgroundHandlerThread.join(); - } catch (InterruptedException e) { - dartMessenger.error(flutterResult, "cameraAccess", e.getMessage(), null); - } } backgroundHandlerThread = null; backgroundHandler = null; - stoppingBackgroundHandlerThread = false; } /** Start capturing a picture, doing autofocus first. */ diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java index b85b685ca90b..9a679017ded2 100644 --- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java +++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/CameraTest.java @@ -838,25 +838,12 @@ public void startBackgroundThread_shouldNotStartNewThreadWhenAlreadyCreated() { } @Test - public void stopBackgroundThread_cancelsDuplicateCalls() throws InterruptedException { - TestUtils.setPrivateField(camera, "stoppingBackgroundHandlerThread", true); - - camera.startBackgroundThread(); - camera.stopBackgroundThread(); - - verify(mockHandlerThread, never()).quitSafely(); - verify(mockHandlerThread, never()).join(); - } - - @Test - public void stopBackgroundThread_proceedsWithoutDuplicateCall() throws InterruptedException { - TestUtils.setPrivateField(camera, "stoppingBackgroundHandlerThread", false); - + public void stopBackgroundThread_quitsSafely() throws InterruptedException { camera.startBackgroundThread(); camera.stopBackgroundThread(); verify(mockHandlerThread).quitSafely(); - verify(mockHandlerThread).join(); + verify(mockHandlerThread, never()).join(); } @Test diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index afb40dee33c5..9f5a2f2b9c16 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android description: Android implementation of the camera plugin. repository: https://github.com/flutter/plugins/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.0+1 +version: 0.10.0+2 environment: sdk: ">=2.14.0 <3.0.0" From 7dba9e1b381b3bcde7af0f72b5fbda28bc8917ac Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 4 Sep 2022 11:50:18 -0400 Subject: [PATCH 690/844] Roll Flutter from 02857c90cc6b to 0e7257e698db (27 revisions) (#6358) --- .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 d4232d2a1eca..d6c55f17bdf4 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -02857c90cc6b3eab83d7b8f778eb3fb32ec9ecbf +0e7257e698db105ec035d164df672b43322a729c From 4d125d3a1e22a2f34aa9ae22131dbfcafc76a496 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 6 Sep 2022 11:51:14 -0400 Subject: [PATCH 691/844] Roll Flutter from 0e7257e698db to 723b82e4f0f3 (14 revisions) (#6365) * 5bb612cc3 Roll Flutter Engine from 14eb83a7d0bd to a8e97892ce25 (1 revision) (flutter/flutter#110938) * 798ce226d Roll Flutter Engine from a8e97892ce25 to 0a2f56cd025f (1 revision) (flutter/flutter#110940) * 5de91d958 Roll Flutter Engine from 0a2f56cd025f to b040ed90175b (1 revision) (flutter/flutter#110954) * 202f5771f Roll Flutter Engine from b040ed90175b to b94120dfe9c6 (1 revision) (flutter/flutter#110960) * d8fdb8355 Update `MaterialBanner` to support Material 3 (flutter/flutter#105957) * fbba1942c Migrate `ListTile` disabled icon color to Material 3 (flutter/flutter#102078) * f625359a9 Roll Flutter Engine from b94120dfe9c6 to 5a2861e499d2 (1 revision) (flutter/flutter#110972) * eb154d54b Roll Flutter Engine from 5a2861e499d2 to 3ff25eb9081c (1 revision) (flutter/flutter#110974) * 0144110c5 Roll Flutter Engine from 3ff25eb9081c to 47c281f218db (1 revision) (flutter/flutter#110983) * b166390ba Roll Flutter Engine from 47c281f218db to 25642a45a55e (1 revision) (flutter/flutter#110991) * bce91637c Roll Flutter Engine from 25642a45a55e to 7b3710878503 (1 revision) (flutter/flutter#110992) * 8cddac7d0 Roll Flutter Engine from 7b3710878503 to 3c0898c0dfed (1 revision) (flutter/flutter#110993) * 96345a4bb Roll Flutter Engine from 3c0898c0dfed to ddc5bb330254 (1 revision) (flutter/flutter#110999) * 723b82e4f Feat: dSYM debug info for iOS & macOS builds (flutter/flutter#101586) --- .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 d6c55f17bdf4..de2852351c75 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -0e7257e698db105ec035d164df672b43322a729c +723b82e4f0f3652dfaffb6e9bbc714e069da2976 From f593240787c703f92014cf098faa234ad93a75dd Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 6 Sep 2022 13:46:18 -0400 Subject: [PATCH 692/844] [webview_flutter] Implementation of the app facing WebViewController for v4 (#6280) --- .../lib/src/v4/src/webview_controller.dart | 260 +++++++++++++ .../src/v4/src/webview_cookie_manager.dart | 10 +- .../lib/src/v4/webview_flutter.dart | 3 +- .../test/v4/webview_controller_test.dart | 353 ++++++++++++++++++ .../v4/webview_controller_test.mocks.dart | 203 ++++++++++ .../test/v4/webview_cookie_manager_test.dart | 4 +- .../test/webview_flutter_test.mocks.dart | 156 ++++---- 7 files changed, 909 insertions(+), 80 deletions(-) create mode 100644 packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_controller.dart create mode 100644 packages/webview_flutter/webview_flutter/test/v4/webview_controller_test.dart create mode 100644 packages/webview_flutter/webview_flutter/test/v4/webview_controller_test.mocks.dart diff --git a/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_controller.dart b/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_controller.dart new file mode 100644 index 000000000000..bd03b247027e --- /dev/null +++ b/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_controller.dart @@ -0,0 +1,260 @@ +// 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:math'; + +// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231) +// ignore: unnecessary_import +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; + +/// Controls a WebView provided by the host platform. +/// +/// Pass this to a [WebViewWidget] to display the WebView. +class WebViewController { + /// Constructs a [WebViewController]. + WebViewController() + : this.fromPlatformCreationParams( + const PlatformWebViewControllerCreationParams(), + ); + + /// Constructs a [WebViewController] from creation params for a specific + /// platform. + WebViewController.fromPlatformCreationParams( + PlatformWebViewControllerCreationParams params, + ) : this.fromPlatform(PlatformWebViewController(params)); + + /// Constructs a [WebViewController] from a specific platform implementation. + WebViewController.fromPlatform(this.platform); + + /// Implementation of [PlatformWebViewController] for the current platform. + final PlatformWebViewController platform; + + /// Loads the file located on the specified [absoluteFilePath]. + /// + /// The [absoluteFilePath] parameter should contain the absolute path to the + /// file as it is stored on the device. For example: + /// `/Users/username/Documents/www/index.html`. + /// + /// Throws a `PlatformException` if the [absoluteFilePath] does not exist. + Future loadFile(String absoluteFilePath) { + return platform.loadFile(absoluteFilePath); + } + + /// Loads the Flutter asset specified in the pubspec.yaml file. + /// + /// Throws a `PlatformException` if [key] is not part of the specified assets + /// in the pubspec.yaml file. + Future loadFlutterAsset(String key) { + assert(key.isNotEmpty); + return platform.loadFlutterAsset(key); + } + + /// Loads the supplied HTML string. + /// + /// The [baseUrl] parameter is used when resolving relative URLs within the + /// HTML string. + Future loadHtmlString(String html, {String? baseUrl}) { + assert(html.isNotEmpty); + return platform.loadHtmlString(html, baseUrl: baseUrl); + } + + /// Makes a specific HTTP request ands loads the response in the webview. + /// + /// [method] must be one of the supported HTTP methods in [LoadRequestMethod]. + /// + /// If [headers] is not empty, its key-value pairs will be added as the + /// headers for the request. + /// + /// If [body] is not null, it will be added as the body for the request. + /// + /// Throws an ArgumentError if [uri] has an empty scheme. + Future loadRequest( + Uri uri, { + LoadRequestMethod method = LoadRequestMethod.get, + Map headers = const {}, + Uint8List? body, + }) { + if (uri.scheme.isEmpty) { + throw ArgumentError('Missing scheme in uri: $uri'); + } + return platform.loadRequest(LoadRequestParams( + uri: uri, + method: method, + headers: headers, + body: body, + )); + } + + /// Returns the current URL that the WebView is displaying. + /// + /// If no URL was ever loaded, returns `null`. + Future currentUrl() { + return platform.currentUrl(); + } + + /// Checks whether there's a back history item. + Future canGoBack() { + return platform.canGoBack(); + } + + /// Checks whether there's a forward history item. + Future canGoForward() { + return platform.canGoForward(); + } + + /// Goes back in the history of this WebView. + /// + /// If there is no back history item this is a no-op. + Future goBack() { + return platform.goBack(); + } + + /// Goes forward in the history of this WebView. + /// + /// If there is no forward history item this is a no-op. + Future goForward() { + return platform.goForward(); + } + + /// Reloads the current URL. + Future reload() { + return platform.reload(); + } + + /// Clears all caches used by the WebView. + /// + /// The following caches are cleared: + /// 1. Browser HTTP Cache. + /// 2. [Cache API](https://developers.google.com/web/fundamentals/instant-and-offline/web-storage/cache-api) + /// caches. Service workers tend to use this cache. + /// 3. Application cache. + Future clearCache() { + return platform.clearCache(); + } + + /// Clears the local storage used by the WebView. + Future clearLocalStorage() { + return platform.clearLocalStorage(); + } + + /// Runs the given JavaScript in the context of the current page. + /// + /// The Future completes with an error if a JavaScript error occurred. + Future runJavaScript(String javaScript) { + return platform.runJavaScript(javaScript); + } + + /// Runs the given JavaScript in the context of the current page, and returns + /// the result. + /// + /// The Future completes with an error if a JavaScript error occurred, or if + /// the type the given expression evaluates to is unsupported. Unsupported + /// values include certain non-primitive types on iOS, as well as `undefined` + /// or `null` on iOS 14+. + Future runJavaScriptReturningResult(String javaScript) { + return platform.runJavaScriptReturningResult(javaScript); + } + + /// Adds a new JavaScript channel to the set of enabled channels. + /// + /// The JavaScript code can then call `postMessage` on that object to send a + /// message that will be passed to [onMessageReceived]. + /// + /// For example, after adding the following JavaScript channel: + /// + /// ```dart + /// final WebViewController controller = WebViewController(); + /// controller.addJavaScriptChannel( + /// name: 'Print', + /// onMessageReceived: (JavascriptMessage message) { + /// print(message.message); + /// }, + /// ); + /// ``` + /// + /// JavaScript code can call: + /// + /// ```javascript + /// Print.postMessage('Hello'); + /// ``` + /// + /// to asynchronously invoke the message handler which will print the message + /// to standard output. + /// + /// Adding a new JavaScript channel only takes affect after the next page is + /// loaded. + /// + /// A channel [name] cannot be the same for multiple channels. + Future addJavaScriptChannel( + String name, { + required void Function(JavaScriptMessage) onMessageReceived, + }) { + assert(name.isNotEmpty); + return platform.addJavaScriptChannel(JavaScriptChannelParams( + name: name, + onMessageReceived: onMessageReceived, + )); + } + + /// Removes the JavaScript channel with the matching name from the set of + /// enabled channels. + /// + /// This disables the channel with the matching name if it was previously + /// enabled through the [addJavaScriptChannel]. + Future removeJavaScriptChannel(String javaScriptChannelName) { + return platform.removeJavaScriptChannel(javaScriptChannelName); + } + + /// The title of the currently loaded page. + Future getTitle() { + return platform.getTitle(); + } + + /// Sets the scrolled position of this view. + /// + /// The parameters `x` and `y` specify the position to scroll to in WebView + /// pixels. + Future scrollTo(int x, int y) { + return platform.scrollTo(x, y); + } + + /// Moves the scrolled position of this view. + /// + /// The parameters `x` and `y` specify the amount of WebView pixels to scroll + /// by. + Future scrollBy(int x, int y) { + return platform.scrollBy(x, y); + } + + /// Returns the current scroll position of this view. + /// + /// Scroll position is measured from the top left. + Future getScrollPosition() async { + final Point position = await platform.getScrollPosition(); + return Offset(position.x.toDouble(), position.y.toDouble()); + } + + /// Whether to support zooming using the on-screen zoom controls and gestures. + Future enableZoom(bool enabled) { + return platform.enableZoom(enabled); + } + + /// Sets the current background color of this view. + Future setBackgroundColor(Color color) { + return platform.setBackgroundColor(color); + } + + /// Sets the JavaScript execution mode to be used by the WebView. + Future setJavaScriptMode(JavaScriptMode javaScriptMode) { + return platform.setJavaScriptMode(javaScriptMode); + } + + /// Sets the value used for the HTTP `User-Agent:` request header. + Future setUserAgent(String? userAgent) { + return platform.setUserAgent(userAgent); + } +} diff --git a/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_cookie_manager.dart b/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_cookie_manager.dart index c209e95a1917..a1091fa3c7b1 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_cookie_manager.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_cookie_manager.dart @@ -8,21 +8,19 @@ import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_i class WebViewCookieManager { /// Constructs a [WebViewCookieManager]. WebViewCookieManager() - : this.fromPlatform( - platform: PlatformWebViewCookieManager( - const PlatformWebViewCookieManagerCreationParams(), - ), + : this.fromPlatformCreationParams( + const PlatformWebViewCookieManagerCreationParams(), ); /// Constructs a [WebViewCookieManager] from creation params for a specific /// platform. WebViewCookieManager.fromPlatformCreationParams( PlatformWebViewCookieManagerCreationParams params, - ) : this.fromPlatform(platform: PlatformWebViewCookieManager(params)); + ) : this.fromPlatform(PlatformWebViewCookieManager(params)); /// Constructs a [WebViewCookieManager] from a specific platform /// implementation. - WebViewCookieManager.fromPlatform({required this.platform}); + WebViewCookieManager.fromPlatform(this.platform); /// Implementation of [PlatformWebViewCookieManager] for the current platform. final PlatformWebViewCookieManager platform; diff --git a/packages/webview_flutter/webview_flutter/lib/src/v4/webview_flutter.dart b/packages/webview_flutter/webview_flutter/lib/src/v4/webview_flutter.dart index 6ab8c3a2f6c8..f3caf84dcf56 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/v4/webview_flutter.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/v4/webview_flutter.dart @@ -5,6 +5,7 @@ library webview_flutter; export 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart' - show WebViewCookie; + show JavaScriptMessage, LoadRequestMethod, WebViewCookie; +export 'src/webview_controller.dart'; export 'src/webview_cookie_manager.dart'; diff --git a/packages/webview_flutter/webview_flutter/test/v4/webview_controller_test.dart b/packages/webview_flutter/webview_flutter/test/v4/webview_controller_test.dart new file mode 100644 index 000000000000..f767a2e48d5e --- /dev/null +++ b/packages/webview_flutter/webview_flutter/test/v4/webview_controller_test.dart @@ -0,0 +1,353 @@ +// 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:math'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:webview_flutter/src/v4/src/webview_controller.dart'; +import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; + +import 'webview_controller_test.mocks.dart'; + +@GenerateMocks([PlatformWebViewController]) +void main() { + test('loadFile', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await webViewController.loadFile('file/path'); + verify(mockPlatformWebViewController.loadFile('file/path')); + }); + + test('loadFlutterAsset', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await webViewController.loadFlutterAsset('file/path'); + verify(mockPlatformWebViewController.loadFlutterAsset('file/path')); + }); + + test('loadHtmlString', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await webViewController.loadHtmlString('html', baseUrl: 'baseUrl'); + verify(mockPlatformWebViewController.loadHtmlString( + 'html', + baseUrl: 'baseUrl', + )); + }); + + test('loadRequest', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await webViewController.loadRequest( + Uri(scheme: 'https', host: 'dart.dev'), + method: LoadRequestMethod.post, + headers: {'a': 'header'}, + body: Uint8List(0), + ); + + final LoadRequestParams params = + verify(mockPlatformWebViewController.loadRequest(captureAny)) + .captured[0] as LoadRequestParams; + expect(params.uri, Uri(scheme: 'https', host: 'dart.dev')); + expect(params.method, LoadRequestMethod.post); + expect(params.headers, {'a': 'header'}); + expect(params.body, Uint8List(0)); + }); + + test('currentUrl', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + when(mockPlatformWebViewController.currentUrl()).thenAnswer( + (_) => Future.value('https://dart.dev'), + ); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await expectLater( + webViewController.currentUrl(), + completion('https://dart.dev'), + ); + }); + + test('canGoBack', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + when(mockPlatformWebViewController.canGoBack()).thenAnswer( + (_) => Future.value(false), + ); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await expectLater(webViewController.canGoBack(), completion(false)); + }); + + test('canGoForward', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + when(mockPlatformWebViewController.canGoForward()).thenAnswer( + (_) => Future.value(true), + ); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await expectLater(webViewController.canGoForward(), completion(true)); + }); + + test('goBack', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await webViewController.goBack(); + verify(mockPlatformWebViewController.goBack()); + }); + + test('goForward', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await webViewController.goForward(); + verify(mockPlatformWebViewController.goForward()); + }); + + test('reload', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await webViewController.reload(); + verify(mockPlatformWebViewController.reload()); + }); + + test('clearCache', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await webViewController.clearCache(); + verify(mockPlatformWebViewController.clearCache()); + }); + + test('clearLocalStorage', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await webViewController.clearLocalStorage(); + verify(mockPlatformWebViewController.clearLocalStorage()); + }); + + test('runJavaScript', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await webViewController.runJavaScript('1 + 1'); + verify(mockPlatformWebViewController.runJavaScript('1 + 1')); + }); + + test('runJavaScriptReturningResult', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + when(mockPlatformWebViewController.runJavaScriptReturningResult('1 + 1')) + .thenAnswer((_) => Future.value('2')); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await expectLater( + webViewController.runJavaScriptReturningResult('1 + 1'), + completion('2'), + ); + }); + + test('addJavaScriptChannel', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + void onMessageReceived(JavaScriptMessage message) {} + await webViewController.addJavaScriptChannel( + 'name', + onMessageReceived: onMessageReceived, + ); + + final JavaScriptChannelParams params = + verify(mockPlatformWebViewController.addJavaScriptChannel(captureAny)) + .captured[0] as JavaScriptChannelParams; + expect(params.name, 'name'); + expect(params.onMessageReceived, onMessageReceived); + }); + + test('removeJavaScriptChannel', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await webViewController.removeJavaScriptChannel('channel'); + verify(mockPlatformWebViewController.removeJavaScriptChannel('channel')); + }); + + test('getTitle', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + when(mockPlatformWebViewController.getTitle()) + .thenAnswer((_) => Future.value('myTitle')); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await expectLater(webViewController.getTitle(), completion('myTitle')); + }); + + test('scrollTo', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await webViewController.scrollTo(2, 3); + verify(mockPlatformWebViewController.scrollTo(2, 3)); + }); + + test('scrollBy', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await webViewController.scrollBy(2, 3); + verify(mockPlatformWebViewController.scrollBy(2, 3)); + }); + + test('getScrollPosition', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + when(mockPlatformWebViewController.getScrollPosition()).thenAnswer( + (_) => Future>.value( + const Point(2, 3), + ), + ); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await expectLater( + webViewController.getScrollPosition(), + completion(const Offset(2.0, 3.0)), + ); + }); + + test('enableZoom', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await webViewController.enableZoom(false); + verify(mockPlatformWebViewController.enableZoom(false)); + }); + + test('setBackgroundColor', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await webViewController.setBackgroundColor(Colors.green); + verify(mockPlatformWebViewController.setBackgroundColor(Colors.green)); + }); + + test('setJavaScriptMode', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await webViewController.setJavaScriptMode(JavaScriptMode.disabled); + verify( + mockPlatformWebViewController.setJavaScriptMode(JavaScriptMode.disabled), + ); + }); + + test('setUserAgent', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await webViewController.setUserAgent('userAgent'); + verify(mockPlatformWebViewController.setUserAgent('userAgent')); + }); +} diff --git a/packages/webview_flutter/webview_flutter/test/v4/webview_controller_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/v4/webview_controller_test.mocks.dart new file mode 100644 index 000000000000..f0fb4b47fc6e --- /dev/null +++ b/packages/webview_flutter/webview_flutter/test/v4/webview_controller_test.mocks.dart @@ -0,0 +1,203 @@ +// Mocks generated by Mockito 5.3.0 from annotations +// in webview_flutter/test/v4/webview_controller_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i5; +import 'dart:math' as _i3; +import 'dart:ui' as _i7; + +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_platform_interface/v4/src/platform_navigation_delegate.dart' + as _i6; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_controller.dart' + as _i4; +import 'package:webview_flutter_platform_interface/v4/src/webview_platform.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: 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 + +class _FakePlatformWebViewControllerCreationParams_0 extends _i1.SmartFake + implements _i2.PlatformWebViewControllerCreationParams { + _FakePlatformWebViewControllerCreationParams_0( + Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +class _FakePoint_1 extends _i1.SmartFake + implements _i3.Point { + _FakePoint_1(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +/// A class which mocks [PlatformWebViewController]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockPlatformWebViewController extends _i1.Mock + implements _i4.PlatformWebViewController { + MockPlatformWebViewController() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.PlatformWebViewControllerCreationParams get params => + (super.noSuchMethod(Invocation.getter(#params), + returnValue: _FakePlatformWebViewControllerCreationParams_0( + this, Invocation.getter(#params))) + as _i2.PlatformWebViewControllerCreationParams); + @override + _i5.Future loadFile(String? absoluteFilePath) => (super.noSuchMethod( + Invocation.method(#loadFile, [absoluteFilePath]), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value()) as _i5.Future); + @override + _i5.Future loadFlutterAsset(String? key) => (super.noSuchMethod( + Invocation.method(#loadFlutterAsset, [key]), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value()) as _i5.Future); + @override + _i5.Future loadHtmlString(String? html, {String? baseUrl}) => + (super.noSuchMethod( + Invocation.method(#loadHtmlString, [html], {#baseUrl: baseUrl}), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value()) + as _i5.Future); + @override + _i5.Future loadRequest(_i2.LoadRequestParams? params) => + (super.noSuchMethod(Invocation.method(#loadRequest, [params]), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value()) + as _i5.Future); + @override + _i5.Future currentUrl() => + (super.noSuchMethod(Invocation.method(#currentUrl, []), + returnValue: _i5.Future.value()) as _i5.Future); + @override + _i5.Future canGoBack() => + (super.noSuchMethod(Invocation.method(#canGoBack, []), + returnValue: _i5.Future.value(false)) as _i5.Future); + @override + _i5.Future canGoForward() => + (super.noSuchMethod(Invocation.method(#canGoForward, []), + returnValue: _i5.Future.value(false)) as _i5.Future); + @override + _i5.Future goBack() => (super.noSuchMethod( + Invocation.method(#goBack, []), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value()) as _i5.Future); + @override + _i5.Future goForward() => (super.noSuchMethod( + Invocation.method(#goForward, []), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value()) as _i5.Future); + @override + _i5.Future reload() => (super.noSuchMethod( + Invocation.method(#reload, []), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value()) as _i5.Future); + @override + _i5.Future clearCache() => (super.noSuchMethod( + Invocation.method(#clearCache, []), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value()) as _i5.Future); + @override + _i5.Future clearLocalStorage() => (super.noSuchMethod( + Invocation.method(#clearLocalStorage, []), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value()) as _i5.Future); + @override + _i5.Future setPlatformNavigationDelegate( + _i6.PlatformNavigationDelegate? handler) => + (super.noSuchMethod( + Invocation.method(#setPlatformNavigationDelegate, [handler]), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value()) + as _i5.Future); + @override + _i5.Future runJavaScript(String? javaScript) => (super.noSuchMethod( + Invocation.method(#runJavaScript, [javaScript]), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value()) as _i5.Future); + @override + _i5.Future runJavaScriptReturningResult(String? javaScript) => + (super.noSuchMethod( + Invocation.method(#runJavaScriptReturningResult, [javaScript]), + returnValue: _i5.Future.value('')) as _i5.Future); + @override + _i5.Future addJavaScriptChannel( + _i4.JavaScriptChannelParams? javaScriptChannelParams) => + (super.noSuchMethod( + Invocation.method(#addJavaScriptChannel, [javaScriptChannelParams]), + returnValue: _i5.Future.value(), + returnValueForMissingStub: + _i5.Future.value()) as _i5.Future); + @override + _i5.Future removeJavaScriptChannel(String? javaScriptChannelName) => + (super.noSuchMethod( + Invocation.method(#removeJavaScriptChannel, [javaScriptChannelName]), + returnValue: _i5.Future.value(), + returnValueForMissingStub: + _i5.Future.value()) as _i5.Future); + @override + _i5.Future getTitle() => + (super.noSuchMethod(Invocation.method(#getTitle, []), + returnValue: _i5.Future.value()) as _i5.Future); + @override + _i5.Future scrollTo(int? x, int? y) => (super.noSuchMethod( + Invocation.method(#scrollTo, [x, y]), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value()) as _i5.Future); + @override + _i5.Future scrollBy(int? x, int? y) => (super.noSuchMethod( + Invocation.method(#scrollBy, [x, y]), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value()) as _i5.Future); + @override + _i5.Future<_i3.Point> getScrollPosition() => + (super.noSuchMethod(Invocation.method(#getScrollPosition, []), + returnValue: _i5.Future<_i3.Point>.value(_FakePoint_1( + this, Invocation.method(#getScrollPosition, [])))) + as _i5.Future<_i3.Point>); + @override + _i5.Future enableDebugging(bool? enabled) => (super.noSuchMethod( + Invocation.method(#enableDebugging, [enabled]), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value()) as _i5.Future); + @override + _i5.Future enableGestureNavigation(bool? enabled) => (super + .noSuchMethod(Invocation.method(#enableGestureNavigation, [enabled]), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value()) + as _i5.Future); + @override + _i5.Future enableZoom(bool? enabled) => (super.noSuchMethod( + Invocation.method(#enableZoom, [enabled]), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value()) as _i5.Future); + @override + _i5.Future setBackgroundColor(_i7.Color? color) => (super.noSuchMethod( + Invocation.method(#setBackgroundColor, [color]), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value()) as _i5.Future); + @override + _i5.Future setJavaScriptMode(_i2.JavaScriptMode? javaScriptMode) => + (super.noSuchMethod( + Invocation.method(#setJavaScriptMode, [javaScriptMode]), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value()) + as _i5.Future); + @override + _i5.Future setUserAgent(String? userAgent) => (super.noSuchMethod( + Invocation.method(#setUserAgent, [userAgent]), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value()) as _i5.Future); +} diff --git a/packages/webview_flutter/webview_flutter/test/v4/webview_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter/test/v4/webview_cookie_manager_test.dart index d7941ab60cc9..e8152407fb92 100644 --- a/packages/webview_flutter/webview_flutter/test/v4/webview_cookie_manager_test.dart +++ b/packages/webview_flutter/webview_flutter/test/v4/webview_cookie_manager_test.dart @@ -22,7 +22,7 @@ void main() { final WebViewCookieManager cookieManager = WebViewCookieManager.fromPlatform( - platform: mockPlatformWebViewCookieManager, + mockPlatformWebViewCookieManager, ); await expectLater(cookieManager.clearCookies(), completion(false)); @@ -34,7 +34,7 @@ void main() { final WebViewCookieManager cookieManager = WebViewCookieManager.fromPlatform( - platform: mockPlatformWebViewCookieManager, + mockPlatformWebViewCookieManager, ); const WebViewCookie cookie = WebViewCookie( diff --git a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.mocks.dart index fe3060164df2..a7a21007d6be 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.mocks.dart @@ -1,11 +1,8 @@ -// 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. - -// Mocks generated by Mockito 5.0.16 from annotations +// Mocks generated by Mockito 5.3.0 from annotations // in webview_flutter/test/webview_flutter_test.dart. // Do not manually edit this file. +// ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i9; import 'package:flutter/foundation.dart' as _i3; @@ -22,6 +19,7 @@ import 'package:webview_flutter_platform_interface/src/platform_interface/webvie as _i10; import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i5; +// ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references @@ -30,8 +28,12 @@ import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i5; // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeWidget_0 extends _i1.SmartFake implements _i2.Widget { + _FakeWidget_0(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); -class _FakeWidget_0 extends _i1.Fake implements _i2.Widget { @override String toString({_i3.DiagnosticLevel? minLevel = _i3.DiagnosticLevel.info}) => super.toString(); @@ -63,13 +65,21 @@ class MockWebViewPlatform extends _i1.Mock implements _i4.WebViewPlatform { #onWebViewPlatformCreated: onWebViewPlatformCreated, #gestureRecognizers: gestureRecognizers }), - returnValue: _FakeWidget_0()) as _i2.Widget); + returnValue: _FakeWidget_0( + this, + Invocation.method(#build, [], { + #context: context, + #creationParams: creationParams, + #webViewPlatformCallbacksHandler: + webViewPlatformCallbacksHandler, + #javascriptChannelRegistry: javascriptChannelRegistry, + #onWebViewPlatformCreated: onWebViewPlatformCreated, + #gestureRecognizers: gestureRecognizers + }))) as _i2.Widget); @override _i9.Future clearCookies() => (super.noSuchMethod(Invocation.method(#clearCookies, []), - returnValue: Future.value(false)) as _i9.Future); - @override - String toString() => super.toString(); + returnValue: _i9.Future.value(false)) as _i9.Future); } /// A class which mocks [WebViewPlatformController]. @@ -82,118 +92,122 @@ class MockWebViewPlatformController extends _i1.Mock } @override - _i9.Future loadFile(String? absoluteFilePath) => - (super.noSuchMethod(Invocation.method(#loadFile, [absoluteFilePath]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i9.Future); + _i9.Future loadFile(String? absoluteFilePath) => (super.noSuchMethod( + Invocation.method(#loadFile, [absoluteFilePath]), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value()) as _i9.Future); @override - _i9.Future loadFlutterAsset(String? key) => - (super.noSuchMethod(Invocation.method(#loadFlutterAsset, [key]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i9.Future); + _i9.Future loadFlutterAsset(String? key) => (super.noSuchMethod( + Invocation.method(#loadFlutterAsset, [key]), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value()) as _i9.Future); @override _i9.Future loadHtmlString(String? html, {String? baseUrl}) => (super.noSuchMethod( - Invocation.method(#loadHtmlString, [html], {#baseUrl: baseUrl}), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i9.Future); + Invocation.method(#loadHtmlString, [html], {#baseUrl: baseUrl}), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value()) + as _i9.Future); @override _i9.Future loadUrl(String? url, Map? headers) => (super.noSuchMethod(Invocation.method(#loadUrl, [url, headers]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i9.Future); + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value()) + as _i9.Future); @override _i9.Future loadRequest(_i5.WebViewRequest? request) => (super.noSuchMethod(Invocation.method(#loadRequest, [request]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i9.Future); + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value()) + as _i9.Future); @override _i9.Future updateSettings(_i5.WebSettings? setting) => (super.noSuchMethod(Invocation.method(#updateSettings, [setting]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i9.Future); + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value()) + as _i9.Future); @override _i9.Future currentUrl() => (super.noSuchMethod(Invocation.method(#currentUrl, []), - returnValue: Future.value()) as _i9.Future); + returnValue: _i9.Future.value()) as _i9.Future); @override _i9.Future canGoBack() => (super.noSuchMethod(Invocation.method(#canGoBack, []), - returnValue: Future.value(false)) as _i9.Future); + returnValue: _i9.Future.value(false)) as _i9.Future); @override _i9.Future canGoForward() => (super.noSuchMethod(Invocation.method(#canGoForward, []), - returnValue: Future.value(false)) as _i9.Future); + returnValue: _i9.Future.value(false)) as _i9.Future); @override - _i9.Future goBack() => - (super.noSuchMethod(Invocation.method(#goBack, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i9.Future); + _i9.Future goBack() => (super.noSuchMethod( + Invocation.method(#goBack, []), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value()) as _i9.Future); @override - _i9.Future goForward() => - (super.noSuchMethod(Invocation.method(#goForward, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i9.Future); + _i9.Future goForward() => (super.noSuchMethod( + Invocation.method(#goForward, []), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value()) as _i9.Future); @override - _i9.Future reload() => - (super.noSuchMethod(Invocation.method(#reload, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i9.Future); + _i9.Future reload() => (super.noSuchMethod( + Invocation.method(#reload, []), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value()) as _i9.Future); @override - _i9.Future clearCache() => - (super.noSuchMethod(Invocation.method(#clearCache, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i9.Future); + _i9.Future clearCache() => (super.noSuchMethod( + Invocation.method(#clearCache, []), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value()) as _i9.Future); @override _i9.Future evaluateJavascript(String? javascript) => (super.noSuchMethod(Invocation.method(#evaluateJavascript, [javascript]), - returnValue: Future.value('')) as _i9.Future); + returnValue: _i9.Future.value('')) as _i9.Future); @override - _i9.Future runJavascript(String? javascript) => - (super.noSuchMethod(Invocation.method(#runJavascript, [javascript]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i9.Future); + _i9.Future runJavascript(String? javascript) => (super.noSuchMethod( + Invocation.method(#runJavascript, [javascript]), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value()) as _i9.Future); @override _i9.Future runJavascriptReturningResult(String? javascript) => (super.noSuchMethod( Invocation.method(#runJavascriptReturningResult, [javascript]), - returnValue: Future.value('')) as _i9.Future); + returnValue: _i9.Future.value('')) as _i9.Future); @override _i9.Future addJavascriptChannels(Set? javascriptChannelNames) => (super.noSuchMethod( Invocation.method(#addJavascriptChannels, [javascriptChannelNames]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i9.Future); + returnValue: _i9.Future.value(), + returnValueForMissingStub: + _i9.Future.value()) as _i9.Future); @override _i9.Future removeJavascriptChannels( Set? javascriptChannelNames) => (super.noSuchMethod( - Invocation.method( - #removeJavascriptChannels, [javascriptChannelNames]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i9.Future); + Invocation.method( + #removeJavascriptChannels, [javascriptChannelNames]), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value()) + as _i9.Future); @override _i9.Future getTitle() => (super.noSuchMethod(Invocation.method(#getTitle, []), - returnValue: Future.value()) as _i9.Future); + returnValue: _i9.Future.value()) as _i9.Future); @override - _i9.Future scrollTo(int? x, int? y) => - (super.noSuchMethod(Invocation.method(#scrollTo, [x, y]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i9.Future); + _i9.Future scrollTo(int? x, int? y) => (super.noSuchMethod( + Invocation.method(#scrollTo, [x, y]), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value()) as _i9.Future); @override - _i9.Future scrollBy(int? x, int? y) => - (super.noSuchMethod(Invocation.method(#scrollBy, [x, y]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i9.Future); + _i9.Future scrollBy(int? x, int? y) => (super.noSuchMethod( + Invocation.method(#scrollBy, [x, y]), + returnValue: _i9.Future.value(), + returnValueForMissingStub: _i9.Future.value()) as _i9.Future); @override _i9.Future getScrollX() => (super.noSuchMethod(Invocation.method(#getScrollX, []), - returnValue: Future.value(0)) as _i9.Future); + returnValue: _i9.Future.value(0)) as _i9.Future); @override _i9.Future getScrollY() => (super.noSuchMethod(Invocation.method(#getScrollY, []), - returnValue: Future.value(0)) as _i9.Future); - @override - String toString() => super.toString(); + returnValue: _i9.Future.value(0)) as _i9.Future); } From 52862f3386e9e3d8d1280d4a43625161f681ae9e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Sep 2022 19:04:48 +0000 Subject: [PATCH 693/844] [image_picker]: Bump gradle from 3.3.0 to 7.2.1 in /packages/image_picker/image_picker_android/android (#5839) --- packages/image_picker/image_picker_android/CHANGELOG.md | 3 ++- .../image_picker/image_picker_android/android/build.gradle | 2 +- packages/image_picker/image_picker_android/pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) mode change 100755 => 100644 packages/image_picker/image_picker_android/android/build.gradle diff --git a/packages/image_picker/image_picker_android/CHANGELOG.md b/packages/image_picker/image_picker_android/CHANGELOG.md index 286efbeb934b..3075f5b1d976 100644 --- a/packages/image_picker/image_picker_android/CHANGELOG.md +++ b/packages/image_picker/image_picker_android/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 0.8.5+3 * Updates minimum Flutter version to 2.10. +* Bumps gradle from 7.1.2 to 7.2.1. ## 0.8.5+2 diff --git a/packages/image_picker/image_picker_android/android/build.gradle b/packages/image_picker/image_picker_android/android/build.gradle old mode 100755 new mode 100644 index 393c4bc4b559..1d428462bea3 --- a/packages/image_picker/image_picker_android/android/build.gradle +++ b/packages/image_picker/image_picker_android/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.1.2' + classpath 'com.android.tools.build:gradle:7.2.1' } } diff --git a/packages/image_picker/image_picker_android/pubspec.yaml b/packages/image_picker/image_picker_android/pubspec.yaml index 341a98ac63db..c0092493b715 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/plugins/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.5+2 +version: 0.8.5+3 environment: sdk: ">=2.14.0 <3.0.0" From 5a7d86010f2d0191b83e34af12597c2c06e2c8c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Sep 2022 19:39:57 +0000 Subject: [PATCH 694/844] [url_launcher]: Bump gradle from 3.4.2 to 7.2.2 in /packages/url_launcher/url_launcher_android/android (#6194) --- packages/url_launcher/url_launcher_android/CHANGELOG.md | 3 ++- .../url_launcher/url_launcher_android/android/build.gradle | 2 +- packages/url_launcher/url_launcher_android/pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md index d3655fa032bd..be98dc2ddc5e 100644 --- a/packages/url_launcher/url_launcher_android/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 6.0.18 +* Updates gradle to 7.2.2. * Updates minimum Flutter version to 2.10. ## 6.0.17 diff --git a/packages/url_launcher/url_launcher_android/android/build.gradle b/packages/url_launcher/url_launcher_android/android/build.gradle index 9c95688eca6d..a81080077997 100644 --- a/packages/url_launcher/url_launcher_android/android/build.gradle +++ b/packages/url_launcher/url_launcher_android/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.4.2' + classpath 'com.android.tools.build:gradle:7.2.2' } } diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index d07005406769..2b0ba2e1b75e 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_android description: Android implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.17 +version: 6.0.18 environment: sdk: ">=2.14.0 <3.0.0" From 70663c79baef20d670966736084b95944100d97a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Sep 2022 20:35:15 +0000 Subject: [PATCH 695/844] [shared_pref]: Bump gradle from 3.4.0 to 7.2.2 in /packages/shared_preferences/shared_preferences_android/android (#6193) --- .../shared_preferences/shared_preferences_android/CHANGELOG.md | 3 ++- .../shared_preferences_android/android/build.gradle | 2 +- .../shared_preferences/shared_preferences_android/pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index 57de5e712777..91a922d9de64 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.0.13 +* Updates gradle to 7.2.2. * Updates minimum Flutter version to 2.10. ## 2.0.12 diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index 89ca75c40d75..bd6adf411077 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.4.0' + classpath 'com.android.tools.build:gradle:7.2.2' } } diff --git a/packages/shared_preferences/shared_preferences_android/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/pubspec.yaml index 8b6fa3af1590..7d7dfa85b71a 100644 --- a/packages/shared_preferences/shared_preferences_android/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_android description: Android implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.12 +version: 2.0.13 environment: sdk: ">=2.14.0 <3.0.0" From b17adabd16228b552c2abb6d822a7cbdaf18f59a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 7 Sep 2022 14:15:48 -0400 Subject: [PATCH 696/844] Roll Flutter from 723b82e4f0f3 to ad1e76a20939 (31 revisions) (#6373) --- .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 de2852351c75..301d8a02a310 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -723b82e4f0f3652dfaffb6e9bbc714e069da2976 +ad1e76a2093970d0fc99a198d7d3381bbbca0f34 From fd03cdeffa4e332d8fa97defcac4dd1e7b000af3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 8 Sep 2022 11:57:04 -0400 Subject: [PATCH 697/844] Roll Flutter from ad1e76a20939 to 66c306b45228 (27 revisions) (#6375) --- .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 301d8a02a310..3e933cd88a72 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -ad1e76a2093970d0fc99a198d7d3381bbbca0f34 +66c306b452282016e4a146a8f1c11f44836560f1 From c6f5f9421f00ca7f0111224c174f2d16e0919665 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Sep 2022 20:26:48 +0000 Subject: [PATCH 698/844] [sign_in]: Bump mockito-inline from 4.6.1 to 4.7.0 in /packages/google_sign_in/google_sign_in_android/android (#6255) --- .../google_sign_in/google_sign_in_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 901dc8cf2147..0c891918aa08 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 @@ -51,5 +51,5 @@ dependencies { implementation 'com.google.android.gms:play-services-auth:20.0.1' implementation 'com.google.guava:guava:28.1-android' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-inline:4.6.1' + testImplementation 'org.mockito:mockito-inline:4.7.0' } From beacea53ff9c5a1183e804fd1198ff8775c5077f Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Thu, 8 Sep 2022 17:52:22 -0700 Subject: [PATCH 699/844] [ci] Rename PLUGIN_SHARDING to PACKAGE_SHARDING. (#6374) --- .cirrus.yml | 22 +++++++++++----------- script/tool_runner.sh | 3 ++- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 27d98baf8923..21c7c0d93581 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -237,11 +237,11 @@ task: skip: $CIRRUS_PR != '' && $CHANNEL == 'stable' env: matrix: - PLUGIN_SHARDING: "--shardIndex 0 --shardCount 5" - PLUGIN_SHARDING: "--shardIndex 1 --shardCount 5" - PLUGIN_SHARDING: "--shardIndex 2 --shardCount 5" - PLUGIN_SHARDING: "--shardIndex 3 --shardCount 5" - PLUGIN_SHARDING: "--shardIndex 4 --shardCount 5" + PACKAGE_SHARDING: "--shardIndex 0 --shardCount 5" + PACKAGE_SHARDING: "--shardIndex 1 --shardCount 5" + PACKAGE_SHARDING: "--shardIndex 2 --shardCount 5" + PACKAGE_SHARDING: "--shardIndex 3 --shardCount 5" + PACKAGE_SHARDING: "--shardIndex 4 --shardCount 5" matrix: CHANNEL: "master" CHANNEL: "stable" @@ -300,8 +300,8 @@ task: - name: web-platform_tests env: matrix: - PLUGIN_SHARDING: "--shardIndex 0 --shardCount 2" - PLUGIN_SHARDING: "--shardIndex 1 --shardCount 2" + PACKAGE_SHARDING: "--shardIndex 0 --shardCount 2" + PACKAGE_SHARDING: "--shardIndex 1 --shardCount 2" matrix: CHANNEL: "master" CHANNEL: "stable" @@ -377,10 +377,10 @@ task: env: PATH: $PATH:/usr/local/bin matrix: - PLUGIN_SHARDING: "--shardIndex 0 --shardCount 4" - PLUGIN_SHARDING: "--shardIndex 1 --shardCount 4" - PLUGIN_SHARDING: "--shardIndex 2 --shardCount 4" - PLUGIN_SHARDING: "--shardIndex 3 --shardCount 4" + PACKAGE_SHARDING: "--shardIndex 0 --shardCount 4" + PACKAGE_SHARDING: "--shardIndex 1 --shardCount 4" + PACKAGE_SHARDING: "--shardIndex 2 --shardCount 4" + PACKAGE_SHARDING: "--shardIndex 3 --shardCount 4" matrix: CHANNEL: "master" CHANNEL: "stable" diff --git a/script/tool_runner.sh b/script/tool_runner.sh index 66181543f2cf..221071550cc1 100755 --- a/script/tool_runner.sh +++ b/script/tool_runner.sh @@ -19,4 +19,5 @@ readonly TOOL_PATH="$REPO_DIR/script/tool/bin/flutter_plugin_tools.dart" # The tool expects to be run from the repo root. cd "$REPO_DIR" # Run from the in-tree source. -dart run "$TOOL_PATH" "$@" --packages-for-branch --log-timing $PLUGIN_SHARDING +# PACKAGE_SHARDING is (optionally) set from Cirrus. See .cirrus.yml +dart run "$TOOL_PATH" "$@" --packages-for-branch --log-timing $PACKAGE_SHARDING From 22c16a9b0aa47e0964b59f7b7b8be21a140dbc02 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Thu, 8 Sep 2022 19:43:41 -0700 Subject: [PATCH 700/844] [misc] Add OpenSSF Scorecard badge to README. (#6376) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fc9972e1ef72..92098af809e9 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Build Status](https://api.cirrus-ci.com/github/flutter/plugins.svg)](https://cirrus-ci.com/github/flutter/plugins/main) [![Release Status](https://github.com/flutter/plugins/actions/workflows/release.yml/badge.svg)](https://github.com/flutter/plugins/actions/workflows/release.yml) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/flutter/plugins/badge)](https://api.securityscorecards.dev/projects/github.com/flutter/plugins) This repo is a companion repo to the main [flutter repo](https://github.com/flutter/flutter). It contains the source code for From 0725b10c12a8d233626a9ab423b8c29bdd52eb4a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Sep 2022 03:59:20 +0000 Subject: [PATCH 701/844] [local_auth]: Bump fragment from 1.5.1 to 1.5.2 in /packages/local_auth/local_auth_android/android (#6251) --- packages/local_auth/local_auth_android/CHANGELOG.md | 3 ++- packages/local_auth/local_auth_android/android/build.gradle | 2 +- packages/local_auth/local_auth_android/pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 942a9948ecc0..5a94fac16fe8 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 1.0.12 +* Updates androidx.fragment version to 1.5.2. * Updates minimum Flutter version to 2.10. ## 1.0.11 diff --git a/packages/local_auth/local_auth_android/android/build.gradle b/packages/local_auth/local_auth_android/android/build.gradle index b3b258683797..399d8a4fbb45 100644 --- a/packages/local_auth/local_auth_android/android/build.gradle +++ b/packages/local_auth/local_auth_android/android/build.gradle @@ -51,7 +51,7 @@ android { dependencies { api "androidx.core:core:1.8.0" api "androidx.biometric:biometric:1.1.0" - api "androidx.fragment:fragment:1.5.1" + api "androidx.fragment:fragment:1.5.2" testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:4.7.0' androidTestImplementation 'androidx.test:runner:1.2.0' diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index 0bfe680a20a6..d621874106b3 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.11 +version: 1.0.12 environment: sdk: ">=2.14.0 <3.0.0" From 15fec3d41de61fe1dc08a4970ecac3eb04730c52 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 9 Sep 2022 13:02:26 -0400 Subject: [PATCH 702/844] Roll Flutter from 66c306b45228 to 4cd734ae3a1c (24 revisions) (#6379) --- .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 3e933cd88a72..af8df44327ff 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -66c306b452282016e4a146a8f1c11f44836560f1 +4cd734ae3a1cc0c8f132ba07e6d2e0a3253c53e7 From d4d3bd76fd54b8d3a663115a3704c7970082f9ee Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 9 Sep 2022 13:52:16 -0400 Subject: [PATCH 703/844] [tools] Improves version-check logic (#6354) Improves the logic used to determine whether to require a version and/or CHANGELOG change: - Removes the requirement that dev-only (e.g., test) changes update the CHANGELOG, since in practice we were essentially always overriding in that case. - Adds file-level analysis of `build.gradle` files to determine whether they are only changing test dependencies. - Improves the "is this a published example file" logic to better match pub.dev's logic, to fix some false positives and false negatives (e.g., `rfw`'s `example//lib/main.dart` being considered published). Removes the no-longer-necessary special-case handling of some Dependabot PRs, as well as the PR-description-based system it was built on (and that turned out not to be very useful due to the way `CIRRUS_CHANGE_MESSAGE` actually worked). `build.gradle` analysis should not cover all such cases, and without the need to hard-code them by package name. --- .cirrus.yml | 12 +- script/tool/CHANGELOG.md | 10 + .../lib/src/common/git_version_finder.dart | 21 ++ .../lib/src/common/package_state_utils.dart | 153 +++++++-- .../lib/src/update_release_info_command.dart | 2 +- .../tool/lib/src/version_check_command.dart | 94 +---- script/tool/pubspec.yaml | 2 +- .../test/common/package_state_utils_test.dart | 214 +++++++++++- .../tool/test/version_check_command_test.dart | 323 ++---------------- 9 files changed, 407 insertions(+), 424 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 21c7c0d93581..7a8e31dae0fb 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -85,20 +85,16 @@ task: - cd script/tool - dart pub run test - name: publishable - env: - CHANGE_DESC: "$TMPDIR/change-description.txt" version_check_script: - # For pre-submit, pass the PR label, as well as the PR description or - # incremental commit message (for Dependabot checks), to the script to - # allow for version check overrides. + # For pre-submit, pass the PR labels to the script to allow for version + # check overrides. # For post-submit, ignore platform version breaking version changes and - # missing version/CHANGELOG detection since the overrides aren't + # missing version/CHANGELOG detection since the labels aren't # available outside of the context of the PR. - if [[ $CIRRUS_PR == "" ]]; then - ./script/tool_runner.sh version-check --ignore-platform-interface-breaks - else - - echo "$CIRRUS_CHANGE_MESSAGE" > "$CHANGE_DESC" - - ./script/tool_runner.sh version-check --check-for-missing-changes --change-description-file="$CHANGE_DESC" --pr-labels="$CIRRUS_PR_LABELS" + - ./script/tool_runner.sh version-check --check-for-missing-changes --pr-labels="$CIRRUS_PR_LABELS" - fi publish_check_script: ./script/tool_runner.sh publish-check - name: format diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index ebfbe80b0184..8dbb4840a672 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,13 @@ +## 0.10.0 + +* Improves the logic in `version-check` to determine what changes don't require + version changes, as well as making any dev-only changes also not require + changelog changes since in practice we almost always override the check in + that case. +* Removes special-case handling of Dependabot PRs, and the (fragile) + `--change-description-file` flag was only still used for that case, as + the improved diff analysis now handles that case more robustly. + ## 0.9.3 * Raises minimum `compileSdkVersion` to 32 for the `all-plugins-app` command. diff --git a/script/tool/lib/src/common/git_version_finder.dart b/script/tool/lib/src/common/git_version_finder.dart index 32d30e60feb5..eb8fba6b76b8 100644 --- a/script/tool/lib/src/common/git_version_finder.dart +++ b/script/tool/lib/src/common/git_version_finder.dart @@ -50,6 +50,27 @@ class GitVersionFinder { return changedFiles.toList(); } + /// Get a list of all the changed files. + Future> getDiffContents({ + String? targetPath, + bool includeUncommitted = false, + }) async { + final String baseSha = await getBaseSha(); + final io.ProcessResult diffCommand = await baseGitDir.runCommand([ + 'diff', + baseSha, + if (!includeUncommitted) 'HEAD', + if (targetPath != null) ...['--', targetPath], + ]); + final String diffStdout = diffCommand.stdout.toString(); + if (diffStdout.isEmpty) { + return []; + } + final List changedFiles = diffStdout.split('\n') + ..removeWhere((String element) => element.isEmpty); + return changedFiles.toList(); + } + /// Get the package version specified in the pubspec file in `pubspecPath` and /// at the revision of `gitRef` (defaulting to the base if not provided). Future getPackageVersion(String pubspecPath, diff --git a/script/tool/lib/src/common/package_state_utils.dart b/script/tool/lib/src/common/package_state_utils.dart index a03d643bdab0..1cff65bb6b0c 100644 --- a/script/tool/lib/src/common/package_state_utils.dart +++ b/script/tool/lib/src/common/package_state_utils.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 'package:file/file.dart'; +import 'package:flutter_plugin_tools/src/common/git_version_finder.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; @@ -14,6 +16,7 @@ class PackageChangeState { const PackageChangeState({ required this.hasChanges, required this.hasChangelogChange, + required this.needsChangelogChange, required this.needsVersionChange, }); @@ -26,6 +29,10 @@ class PackageChangeState { /// True if any changes in the package require a version change according /// to repository policy. final bool needsVersionChange; + + /// True if any changes in the package require a CHANGELOG change according + /// to repository policy. + final bool needsChangelogChange; } /// Checks [package] against [changedPaths] to determine what changes it has @@ -38,11 +45,15 @@ class PackageChangeState { /// and `getRelativePosixPath(package.directory, gitDir.path)` respectively; /// they are arguments mainly to allow for caching the changed paths for an /// entire command run. -PackageChangeState checkPackageChangeState( +/// +/// If [git] is provided, [changedPaths] must be repository-relative +/// paths, and change type detection can use file diffs in addition to paths. +Future checkPackageChangeState( RepositoryPackage package, { required List changedPaths, required String relativePackagePath, -}) { + GitVersionFinder? git, +}) async { final String packagePrefix = relativePackagePath.endsWith('/') ? relativePackagePath : '$relativePackagePath/'; @@ -50,6 +61,7 @@ PackageChangeState checkPackageChangeState( bool hasChanges = false; bool hasChangelogChange = false; bool needsVersionChange = false; + bool needsChangelogChange = false; for (final String path in changedPaths) { // Only consider files within the package. if (!path.startsWith(packagePrefix)) { @@ -62,34 +74,131 @@ PackageChangeState checkPackageChangeState( if (components.isEmpty) { continue; } - final bool isChangelog = components.first == 'CHANGELOG.md'; - if (isChangelog) { + + if (components.first == 'CHANGELOG.md') { hasChangelogChange = true; + continue; } - if (!needsVersionChange && - !isChangelog && - // One of a few special files example will be shown on pub.dev, but for - // anything else in the example publishing has no purpose. - !(components.first == 'example' && - !{'main.dart', 'readme.md', 'example.md'} - .contains(components.last.toLowerCase())) && - // Changes to tests don't need to be published. - !components.contains('test') && - !components.contains('androidTest') && - !components.contains('RunnerTests') && - !components.contains('RunnerUITests') && - // The top-level "tool" directory is for non-client-facing utility code, - // so doesn't need to be published. - components.first != 'tool' && - // Ignoring lints doesn't affect clients. - !components.contains('lint-baseline.xml')) { - needsVersionChange = true; + if (!needsVersionChange) { + // Developer-only changes don't need version changes or changelog changes. + if (await _isDevChange(components, git: git, repoPath: path)) { + continue; + } + + // Some other changes don't need version changes, but might benefit from + // changelog changes. + needsChangelogChange = true; + if ( + // One of a few special files example will be shown on pub.dev, but + // for anything else in the example publishing has no purpose. + !_isUnpublishedExampleChange(components, package)) { + needsVersionChange = true; + } } } return PackageChangeState( hasChanges: hasChanges, hasChangelogChange: hasChangelogChange, + needsChangelogChange: needsChangelogChange, needsVersionChange: needsVersionChange); } + +bool _isTestChange(List pathComponents) { + return pathComponents.contains('test') || + pathComponents.contains('androidTest') || + pathComponents.contains('RunnerTests') || + pathComponents.contains('RunnerUITests'); +} + +// True if the given file is an example file other than the one that will be +// published according to https://dart.dev/tools/pub/package-layout#examples. +// +// This is not exhastive; it currently only handles variations we actually have +// in our repositories. +bool _isUnpublishedExampleChange( + List pathComponents, RepositoryPackage package) { + if (pathComponents.first != 'example') { + return false; + } + final List exampleComponents = pathComponents.sublist(1); + if (exampleComponents.isEmpty) { + return false; + } + + final Directory exampleDirectory = + package.directory.childDirectory('example'); + + // Check for example.md/EXAMPLE.md first, as that has priority. If it's + // present, any other example file is unpublished. + final bool hasExampleMd = + exampleDirectory.childFile('example.md').existsSync() || + exampleDirectory.childFile('EXAMPLE.md').existsSync(); + if (hasExampleMd) { + return !(exampleComponents.length == 1 && + exampleComponents.first.toLowerCase() == 'example.md'); + } + + // Most packages have an example/lib/main.dart (or occasionally + // example/main.dart), so check for that. The other naming variations aren't + // currently used. + const String mainName = 'main.dart'; + final bool hasExampleCode = + exampleDirectory.childDirectory('lib').childFile(mainName).existsSync() || + exampleDirectory.childFile(mainName).existsSync(); + if (hasExampleCode) { + // If there is an example main, only that example file is published. + return !((exampleComponents.length == 1 && + exampleComponents.first == mainName) || + (exampleComponents.length == 2 && + exampleComponents.first == 'lib' && + exampleComponents[1] == mainName)); + } + + // If there's no example code either, the example README.md, if any, is the + // file that will be published. + return exampleComponents.first.toLowerCase() != 'readme.md'; +} + +// True if the change is only relevant to people working on the plugin. +Future _isDevChange(List pathComponents, + {GitVersionFinder? git, String? repoPath}) async { + return _isTestChange(pathComponents) || + // The top-level "tool" directory is for non-client-facing utility + // code, such as test scripts. + pathComponents.first == 'tool' || + // Ignoring lints doesn't affect clients. + pathComponents.contains('lint-baseline.xml') || + await _isGradleTestDependencyChange(pathComponents, + git: git, repoPath: repoPath); +} + +Future _isGradleTestDependencyChange(List pathComponents, + {GitVersionFinder? git, String? repoPath}) async { + if (git == null) { + return false; + } + if (pathComponents.last != 'build.gradle') { + return false; + } + final List diff = await git.getDiffContents(targetPath: repoPath); + final RegExp changeLine = RegExp(r'[+-] '); + final RegExp testDependencyLine = + RegExp(r'[+-]\s*(?:androidT|t)estImplementation\s'); + bool foundTestDependencyChange = false; + for (final String line in diff) { + if (!changeLine.hasMatch(line) || + line.startsWith('--- ') || + line.startsWith('+++ ')) { + continue; + } + if (!testDependencyLine.hasMatch(line)) { + return false; + } + foundTestDependencyChange = true; + } + // Only return true if a test dependency change was found, as a failsafe + // against having the wrong (e.g., incorrectly empty) diff output. + return foundTestDependencyChange; +} diff --git a/script/tool/lib/src/update_release_info_command.dart b/script/tool/lib/src/update_release_info_command.dart index b998615ead17..465b475eb9b5 100644 --- a/script/tool/lib/src/update_release_info_command.dart +++ b/script/tool/lib/src/update_release_info_command.dart @@ -133,7 +133,7 @@ class UpdateReleaseInfoCommand extends PackageLoopingCommand { packagesDir.fileSystem.directory((await gitDir).path); final String relativePackagePath = getRelativePosixPath(package.directory, from: gitRoot); - final PackageChangeState state = checkPackageChangeState(package, + final PackageChangeState state = await checkPackageChangeState(package, changedPaths: _changedFiles, relativePackagePath: relativePackagePath); diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart index 2e5f1efd7934..235611492b2d 100644 --- a/script/tool/lib/src/version_check_command.dart +++ b/script/tool/lib/src/version_check_command.dart @@ -18,8 +18,6 @@ import 'common/process_runner.dart'; import 'common/pub_version_finder.dart'; import 'common/repository_package.dart'; -const int _exitMissingChangeDescriptionFile = 3; - /// Categories of version change types. enum NextVersionType { /// A breaking change. @@ -116,11 +114,6 @@ class VersionCheckCommand extends PackageLoopingCommand { 'Defaults to false, which means the version check only run against ' 'the previous version in code.', ); - argParser.addOption(_changeDescriptionFile, - help: 'The path to a file containing the description of the change ' - '(e.g., PR description or commit message).\n\n' - 'If supplied, this is used to allow overrides to some version ' - 'checks.'); argParser.addOption(_prLabelsArg, help: 'A comma-separated list of labels associated with this PR, ' 'if applicable.\n\n' @@ -144,7 +137,6 @@ class VersionCheckCommand extends PackageLoopingCommand { } static const String _againstPubFlag = 'against-pub'; - static const String _changeDescriptionFile = 'change-description-file'; static const String _prLabelsArg = 'pr-labels'; static const String _checkForMissingChanges = 'check-for-missing-changes'; static const String _ignorePlatformInterfaceBreaks = @@ -171,7 +163,6 @@ class VersionCheckCommand extends PackageLoopingCommand { late final String _mergeBase; late final List _changedFiles; - late final String _changeDescription = _loadChangeDescription(); late final Set _prLabels = _getPRLabels(); @override @@ -519,21 +510,6 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. return labels.split(',').map((String label) => label.trim()).toSet(); } - /// Returns the contents of the file pointed to by [_changeDescriptionFile], - /// or an empty string if that flag is not provided. - String _loadChangeDescription() { - final String path = getStringArg(_changeDescriptionFile); - if (path.isEmpty) { - return ''; - } - final File file = packagesDir.fileSystem.file(path); - if (!file.existsSync()) { - printError('${indentation}No such file: $path'); - throw ToolExit(_exitMissingChangeDescriptionFile); - } - return file.readAsStringSync(); - } - /// Returns true if the given version transition should be allowed. bool _shouldAllowVersionChange( {required Version oldVersion, required Version newVersion}) { @@ -569,8 +545,10 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. final String relativePackagePath = getRelativePosixPath(package.directory, from: gitRoot); - final PackageChangeState state = checkPackageChangeState(package, - changedPaths: _changedFiles, relativePackagePath: relativePackagePath); + final PackageChangeState state = await checkPackageChangeState(package, + changedPaths: _changedFiles, + relativePackagePath: relativePackagePath, + git: await retrieveVersionFinder()); if (!state.hasChanges) { return null; @@ -580,9 +558,6 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. if (_prLabels.contains(_missingVersionChangeOverrideLabel)) { logWarning('Ignoring lack of version change due to the ' '"$_missingVersionChangeOverrideLabel" label.'); - } else if (_isAllowedDependabotChange(package, _changeDescription)) { - logWarning('Ignoring lack of version change for Dependabot change to ' - 'a known internal dependency.'); } else { printError( 'No version change found, but the change to this package could ' @@ -595,76 +570,23 @@ ${indentation}The first version listed in CHANGELOG.md is $fromChangeLog. } } - if (!state.hasChangelogChange) { + if (!state.hasChangelogChange && state.needsChangelogChange) { if (_prLabels.contains(_missingChangelogChangeOverrideLabel)) { logWarning('Ignoring lack of CHANGELOG update due to the ' '"$_missingChangelogChangeOverrideLabel" label.'); - } else if (_isAllowedDependabotChange(package, _changeDescription)) { - logWarning('Ignoring lack of CHANGELOG update for Dependabot change to ' - 'a known internal dependency.'); } else { printError( 'No CHANGELOG change found. If this PR needs an exemption from ' 'the standard policy of listing all changes in the CHANGELOG, ' 'comment in the PR to explain why the PR is exempt, and add (or ' 'ask your reviewer to add) the ' - '"$_missingChangelogChangeOverrideLabel" label.'); + '"$_missingChangelogChangeOverrideLabel" label. Otherwise, ' + 'please add a NEXT entry in the CHANGELOG as described in ' + 'the contributing guide.'); return 'Missing CHANGELOG change'; } } return null; } - - /// Returns true if [changeDescription] matches a Dependabot change for a - /// dependency roll that should bypass the normal version and CHANGELOG change - /// checks (for dependencies that are known not to have client impact). - /// - /// Depending on CI, [changeDescription] may either be the PR description, or - /// the description of the last commit (see for example discussion in - /// https://github.com/cirruslabs/cirrus-ci-docs/issues/1029), so this needs - /// to handle both. - bool _isAllowedDependabotChange( - RepositoryPackage package, String changeDescription) { - // Espresso exports some dependencies that are normally just internal test - // utils, so always require reviewers to check that. - if (package.directory.basename == 'espresso') { - return false; - } - - // A string that is in all Dependabot PRs, but extremely unlikely to be in - // any other PR, to identify Dependabot PRs. - const String dependabotPRDescriptionMarker = - 'Dependabot commands and options'; - // The same thing, but for the Dependabot commit message. - const String dependabotCommitMessageMarker = - 'Signed-off-by: dependabot[bot]'; - // Expression to extract the name of the dependency being updated. - final RegExp dependencyRegex = - RegExp(r'Bumps? \[(.*?)\]\(.*?\) from [\d.]+ to [\d.]+'); - - // Allowed exact dependency names. - const Set allowedDependencies = { - 'junit', - 'robolectric', - }; - const Set allowedDependencyPrefixes = { - 'mockito-' // mockito-core, mockito-inline, etc. - }; - - if (changeDescription.contains(dependabotPRDescriptionMarker) || - changeDescription.contains(dependabotCommitMessageMarker)) { - final Match? match = dependencyRegex.firstMatch(changeDescription); - if (match != null) { - final String dependency = match.group(1)!; - if (allowedDependencies.contains(dependency) || - allowedDependencyPrefixes - .any((String prefix) => dependency.startsWith(prefix))) { - return true; - } - } - } - - return false; - } } diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index b1d55e0fe550..e51c7433aa5c 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.9.3 +version: 0.10.0 dependencies: args: ^2.1.0 diff --git a/script/tool/test/common/package_state_utils_test.dart b/script/tool/test/common/package_state_utils_test.dart index cc9116a9ea25..63ac1802e70c 100644 --- a/script/tool/test/common/package_state_utils_test.dart +++ b/script/tool/test/common/package_state_utils_test.dart @@ -3,7 +3,9 @@ // found in the LICENSE file. import 'package:file/file.dart'; import 'package:file/memory.dart'; +import 'package:flutter_plugin_tools/src/common/git_version_finder.dart'; import 'package:flutter_plugin_tools/src/common/package_state_utils.dart'; +import 'package:test/fake.dart'; import 'package:test/test.dart'; import '../util.dart'; @@ -26,12 +28,13 @@ void main() { 'packages/a_package/lib/plugin.dart', ]; - final PackageChangeState state = checkPackageChangeState(package, + final PackageChangeState state = await checkPackageChangeState(package, changedPaths: changedFiles, relativePackagePath: 'packages/a_package'); expect(state.hasChanges, true); expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); }); test('handles trailing slash on package path', () async { @@ -42,16 +45,18 @@ void main() { 'packages/a_package/lib/plugin.dart', ]; - final PackageChangeState state = checkPackageChangeState(package, + final PackageChangeState state = await checkPackageChangeState(package, changedPaths: changedFiles, relativePackagePath: 'packages/a_package/'); expect(state.hasChanges, true); expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); expect(state.hasChangelogChange, false); }); - test('does not report version change exempt changes', () async { + test('does not flag version- and changelog-change-exempt changes', + () async { final RepositoryPackage package = createFakePlugin('a_plugin', packagesDir); @@ -64,12 +69,13 @@ void main() { 'packages/a_plugin/CHANGELOG.md', ]; - final PackageChangeState state = checkPackageChangeState(package, + final PackageChangeState state = await checkPackageChangeState(package, changedPaths: changedFiles, relativePackagePath: 'packages/a_plugin/'); expect(state.hasChanges, true); expect(state.needsVersionChange, false); + expect(state.needsChangelogChange, false); expect(state.hasChangelogChange, true); }); @@ -81,28 +87,49 @@ void main() { 'packages/a_plugin/lib/foo/tool/tool_thing.dart', ]; - final PackageChangeState state = checkPackageChangeState(package, + final PackageChangeState state = await checkPackageChangeState(package, changedPaths: changedFiles, relativePackagePath: 'packages/a_plugin/'); expect(state.hasChanges, true); expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); }); - test('requires a version change for example main', () async { - final RepositoryPackage package = - createFakePlugin('a_plugin', packagesDir); + test('requires a version change for example/lib/main.dart', () async { + final RepositoryPackage package = createFakePlugin( + 'a_plugin', packagesDir, + extraFiles: ['example/lib/main.dart']); const List changedFiles = [ 'packages/a_plugin/example/lib/main.dart', ]; - final PackageChangeState state = checkPackageChangeState(package, + final PackageChangeState state = await checkPackageChangeState(package, changedPaths: changedFiles, relativePackagePath: 'packages/a_plugin/'); expect(state.hasChanges, true); expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); + }); + + test('requires a version change for example/main.dart', () async { + final RepositoryPackage package = createFakePlugin( + 'a_plugin', packagesDir, + extraFiles: ['example/main.dart']); + + const List changedFiles = [ + 'packages/a_plugin/example/main.dart', + ]; + + final PackageChangeState state = await checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); }); test('requires a version change for example readme.md', () async { @@ -113,28 +140,189 @@ void main() { 'packages/a_plugin/example/README.md', ]; - final PackageChangeState state = checkPackageChangeState(package, + final PackageChangeState state = await checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); + }); + + test('requires a version change for example/example.md', () async { + final RepositoryPackage package = createFakePlugin( + 'a_plugin', packagesDir, + extraFiles: ['example/example.md']); + + const List changedFiles = [ + 'packages/a_plugin/example/example.md', + ]; + + final PackageChangeState state = await checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); + }); + + test( + 'requires a changelog change but no version change for ' + 'lower-priority examples when example.md is present', () async { + final RepositoryPackage package = createFakePlugin( + 'a_plugin', packagesDir, + extraFiles: ['example/example.md']); + + const List changedFiles = [ + 'packages/a_plugin/example/lib/main.dart', + 'packages/a_plugin/example/main.dart', + 'packages/a_plugin/example/README.md', + ]; + + final PackageChangeState state = await checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/'); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, false); + expect(state.needsChangelogChange, true); + }); + + test( + 'requires a changelog change but no version change for README.md when ' + 'code example is present', () async { + final RepositoryPackage package = createFakePlugin( + 'a_plugin', packagesDir, + extraFiles: ['example/lib/main.dart']); + + const List changedFiles = [ + 'packages/a_plugin/example/README.md', + ]; + + final PackageChangeState state = await checkPackageChangeState(package, changedPaths: changedFiles, relativePackagePath: 'packages/a_plugin/'); + expect(state.hasChanges, true); + expect(state.needsVersionChange, false); + expect(state.needsChangelogChange, true); + }); + + test( + 'does not requires changelog or version change for build.gradle ' + 'test-dependency-only changes', () async { + final RepositoryPackage package = + createFakePlugin('a_plugin', packagesDir); + + const List changedFiles = [ + 'packages/a_plugin/android/build.gradle', + ]; + + final GitVersionFinder git = FakeGitVersionFinder(>{ + 'packages/a_plugin/android/build.gradle': [ + "- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'", + "- testImplementation 'junit:junit:4.10.0'", + "+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'", + "+ testImplementation 'junit:junit:4.13.2'", + ] + }); + + final PackageChangeState state = await checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/', + git: git); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, false); + expect(state.needsChangelogChange, false); + }); + + test('requires changelog or version change for other build.gradle changes', + () async { + final RepositoryPackage package = + createFakePlugin('a_plugin', packagesDir); + + const List changedFiles = [ + 'packages/a_plugin/android/build.gradle', + ]; + + final GitVersionFinder git = FakeGitVersionFinder(>{ + 'packages/a_plugin/android/build.gradle': [ + "- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'", + "- testImplementation 'junit:junit:4.10.0'", + "+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'", + "+ testImplementation 'junit:junit:4.13.2'", + "- implementation 'com.google.android.gms:play-services-maps:18.0.0'", + "+ implementation 'com.google.android.gms:play-services-maps:18.0.2'", + ] + }); + + final PackageChangeState state = await checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/', + git: git); + expect(state.hasChanges, true); expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); }); - test('requires a version change for example example.md', () async { + test( + 'requires changelog or version change if build.gradle diffs cannot ' + 'be checked', () async { final RepositoryPackage package = createFakePlugin('a_plugin', packagesDir); const List changedFiles = [ - 'packages/a_plugin/example/lib/example.md', + 'packages/a_plugin/android/build.gradle', ]; - final PackageChangeState state = checkPackageChangeState(package, + final PackageChangeState state = await checkPackageChangeState(package, changedPaths: changedFiles, relativePackagePath: 'packages/a_plugin/'); expect(state.hasChanges, true); expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); + }); + + test( + 'requires changelog or version change if build.gradle diffs cannot ' + 'be determined', () async { + final RepositoryPackage package = + createFakePlugin('a_plugin', packagesDir); + + const List changedFiles = [ + 'packages/a_plugin/android/build.gradle', + ]; + + final GitVersionFinder git = FakeGitVersionFinder(>{ + 'packages/a_plugin/android/build.gradle': [] + }); + + final PackageChangeState state = await checkPackageChangeState(package, + changedPaths: changedFiles, + relativePackagePath: 'packages/a_plugin/', + git: git); + + expect(state.hasChanges, true); + expect(state.needsVersionChange, true); + expect(state.needsChangelogChange, true); }); }); } + +class FakeGitVersionFinder extends Fake implements GitVersionFinder { + FakeGitVersionFinder(this.fileDiffs); + + final Map> fileDiffs; + + @override + Future> getDiffContents({ + String? targetPath, + bool includeUncommitted = false, + }) async { + return fileDiffs[targetPath]!; + } +} diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart index f8153750122a..2ff48d618258 100644 --- a/script/tool/test/version_check_command_test.dart +++ b/script/tool/test/version_check_command_test.dart @@ -40,72 +40,6 @@ void testAllowedVersion( } } -String _generateFakeDependabotPRDescription(String package) { - return ''' -Bumps [$package](https://github.com/foo/$package) from 1.0.0 to 2.0.0. -
-Release notes -

Sourced from $package's releases.

-
-... -
-
-
-Commits -
    -
  • ...
  • -
-
-
- - -[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=$package&package-manager=gradle&previous-version=1.0.0&new-version=2.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-automerge-start) -[//]: # (dependabot-automerge-end) - ---- - -
-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 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) - - -
-'''; -} - -String _generateFakeDependabotCommitMessage(String package) { - return ''' -Bumps [$package](https://github.com/foo/$package) from 1.0.0 to 2.0.0. -- [Release notes](https://github.com/foo/$package/releases) -- [Commits](foo/$package@v4.3.1...v4.6.1) - ---- -updated-dependencies: -- dependency-name: $package - dependency-type: direct:production - update-type: version-update:semver-minor -... - -Signed-off-by: dependabot[bot] -'''; -} - class MockProcessResult extends Mock implements io.ProcessResult {} void main() { @@ -1085,242 +1019,45 @@ packages/plugin/example/lib/foo.dart ); }); - group('dependabot', () { - test('throws if a nonexistent change description file is specified', - () async { - final RepositoryPackage plugin = - createFakePlugin('plugin', packagesDir, version: '1.0.0'); - - const String changelog = ''' -## 1.0.0 -* Some changes. -'''; - plugin.changelogFile.writeAsStringSync(changelog); - processRunner.mockProcessesForExecutable['git-show'] = [ - MockProcess(stdout: 'version: 1.0.0'), - ]; - processRunner.mockProcessesForExecutable['git-diff'] = [ - MockProcess(stdout: ''' -packages/plugin/android/build.gradle -'''), - ]; - - Error? commandError; - final List output = await _runWithMissingChangeDetection( - ['--change-description-file=a_missing_file.txt'], - errorHandler: (Error e) { - commandError = e; - }); - - expect(commandError, isA()); - expect( - output, - containsAllInOrder([ - contains('No such file: a_missing_file.txt'), - ]), - ); - }); - - test('allows missing version and CHANGELOG change for mockito', - () async { - final RepositoryPackage plugin = - createFakePlugin('plugin', packagesDir, version: '1.0.0'); - - const String changelog = ''' -## 1.0.0 -* Some changes. -'''; - plugin.changelogFile.writeAsStringSync(changelog); - processRunner.mockProcessesForExecutable['git-show'] = [ - MockProcess(stdout: 'version: 1.0.0'), - ]; - processRunner.mockProcessesForExecutable['git-diff'] = [ - MockProcess(stdout: ''' -packages/plugin/android/build.gradle -'''), - ]; - - final File changeDescriptionFile = - fileSystem.file('change_description.txt'); - changeDescriptionFile.writeAsStringSync( - _generateFakeDependabotPRDescription('mockito-core')); - - final List output = - await _runWithMissingChangeDetection([ - '--change-description-file=${changeDescriptionFile.path}' - ]); - - expect( - output, - containsAllInOrder([ - contains('Ignoring lack of version change for Dependabot ' - 'change to a known internal dependency.'), - contains('Ignoring lack of CHANGELOG update for Dependabot ' - 'change to a known internal dependency.'), - ]), - ); - }); - - test('allows missing version and CHANGELOG change for robolectric', - () async { - final RepositoryPackage plugin = - createFakePlugin('plugin', packagesDir, version: '1.0.0'); - - const String changelog = ''' -## 1.0.0 -* Some changes. -'''; - plugin.changelogFile.writeAsStringSync(changelog); - processRunner.mockProcessesForExecutable['git-show'] = [ - MockProcess(stdout: 'version: 1.0.0'), - ]; - processRunner.mockProcessesForExecutable['git-diff'] = [ - MockProcess(stdout: ''' -packages/plugin/android/build.gradle -'''), - ]; - - final File changeDescriptionFile = - fileSystem.file('change_description.txt'); - changeDescriptionFile.writeAsStringSync( - _generateFakeDependabotPRDescription('robolectric')); - - final List output = - await _runWithMissingChangeDetection([ - '--change-description-file=${changeDescriptionFile.path}' - ]); - - expect( - output, - containsAllInOrder([ - contains('Ignoring lack of version change for Dependabot ' - 'change to a known internal dependency.'), - contains('Ignoring lack of CHANGELOG update for Dependabot ' - 'change to a known internal dependency.'), - ]), - ); - }); - - test('allows missing version and CHANGELOG change for junit', () async { - final RepositoryPackage plugin = - createFakePlugin('plugin', packagesDir, version: '1.0.0'); - - const String changelog = ''' -## 1.0.0 -* Some changes. -'''; - plugin.changelogFile.writeAsStringSync(changelog); - processRunner.mockProcessesForExecutable['git-show'] = [ - MockProcess(stdout: 'version: 1.0.0'), - ]; - processRunner.mockProcessesForExecutable['git-diff'] = [ - MockProcess(stdout: ''' -packages/plugin/android/build.gradle -'''), - ]; - - final File changeDescriptionFile = - fileSystem.file('change_description.txt'); - changeDescriptionFile - .writeAsStringSync(_generateFakeDependabotPRDescription('junit')); - - final List output = - await _runWithMissingChangeDetection([ - '--change-description-file=${changeDescriptionFile.path}' - ]); - - expect( - output, - containsAllInOrder([ - contains('Ignoring lack of version change for Dependabot ' - 'change to a known internal dependency.'), - contains('Ignoring lack of CHANGELOG update for Dependabot ' - 'change to a known internal dependency.'), - ]), - ); - }); - - test('fails for dependencies that are not explicitly allowed', - () async { - final RepositoryPackage plugin = - createFakePlugin('plugin', packagesDir, version: '1.0.0'); + // This test ensures that Dependabot Gradle changes to test-only files + // aren't flagged by the version check. + test( + 'allows missing CHANGELOG and version change for test-only Gradle changes', + () async { + final RepositoryPackage plugin = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); - const String changelog = ''' + const String changelog = ''' ## 1.0.0 * Some changes. '''; - plugin.changelogFile.writeAsStringSync(changelog); - processRunner.mockProcessesForExecutable['git-show'] = [ - MockProcess(stdout: 'version: 1.0.0'), - ]; - processRunner.mockProcessesForExecutable['git-diff'] = [ - MockProcess(stdout: ''' + plugin.changelogFile.writeAsStringSync(changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + // File list. + MockProcess(stdout: ''' packages/plugin/android/build.gradle '''), - ]; - - final File changeDescriptionFile = - fileSystem.file('change_description.txt'); - changeDescriptionFile.writeAsStringSync( - _generateFakeDependabotPRDescription('somethingelse')); - - Error? commandError; - final List output = - await _runWithMissingChangeDetection([ - '--change-description-file=${changeDescriptionFile.path}' - ], errorHandler: (Error e) { - commandError = e; - }); - - expect(commandError, isA()); - expect( - output, - containsAllInOrder([ - contains('No version change found'), - contains('plugin:\n' - ' Missing version change'), - ]), - ); - }); - - test('allow list works for commit messages', () async { - final RepositoryPackage plugin = - createFakePlugin('plugin', packagesDir, version: '1.0.0'); - - const String changelog = ''' -## 1.0.0 -* Some changes. -'''; - plugin.changelogFile.writeAsStringSync(changelog); - processRunner.mockProcessesForExecutable['git-show'] = [ - MockProcess(stdout: 'version: 1.0.0'), - ]; - processRunner.mockProcessesForExecutable['git-diff'] = [ - MockProcess(stdout: ''' -packages/plugin/android/build.gradle + // build.gradle diff + MockProcess(stdout: ''' +- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' +- testImplementation 'junit:junit:4.10.0' ++ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' ++ testImplementation 'junit:junit:4.13.2' '''), - ]; - - final File changeDescriptionFile = - fileSystem.file('change_description.txt'); - changeDescriptionFile.writeAsStringSync( - _generateFakeDependabotCommitMessage('mockito-core')); + ]; - final List output = - await _runWithMissingChangeDetection([ - '--change-description-file=${changeDescriptionFile.path}' - ]); + final List output = + await _runWithMissingChangeDetection([]); - expect( - output, - containsAllInOrder([ - contains('Ignoring lack of version change for Dependabot ' - 'change to a known internal dependency.'), - contains('Ignoring lack of CHANGELOG update for Dependabot ' - 'change to a known internal dependency.'), - ]), - ); - }); + expect( + output, + containsAllInOrder([ + contains('Running for plugin'), + ]), + ); }); }); From f2e11aa086a8d0d5a5da57568134e361fa01a864 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Sep 2022 20:18:10 +0000 Subject: [PATCH 704/844] [google_maps]: Bump espresso-core from 3.2.0 to 3.4.0 in /packages/google_maps_flutter/google_maps_flutter_android/android (#6199) --- .../google_maps_flutter_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle index 54195fa40c1b..ecc85217c5bf 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle @@ -38,7 +38,7 @@ android { implementation 'com.google.android.gms:play-services-maps:18.0.2' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:rules:1.4.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:4.7.0' testImplementation 'androidx.test:core:1.2.0' From 6edd4e6fe944ea58b96a327575862565ab5795ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Sep 2022 20:32:54 +0000 Subject: [PATCH 705/844] [gh_actions]: Bump github/codeql-action from 2.1.21 to 2.1.22 (#6362) --- .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 725eac6d6400..5f51be13913c 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -48,6 +48,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@c7f292ea4f542c473194b33813ccd4c207a6c725 + uses: github/codeql-action/upload-sarif@b398f525a5587552e573b247ac661067fafa920b with: sarif_file: results.sarif From c04e5a36b51ebe6257fc1c71c43895355419e95e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Sep 2022 20:34:10 +0000 Subject: [PATCH 706/844] [camera]: Bump robolectric from 4.5 to 4.8.2 in /packages/camera/camera_android/android (#6329) --- packages/camera/camera_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android/android/build.gradle b/packages/camera/camera_android/android/build.gradle index b060181316c1..89156b602582 100644 --- a/packages/camera/camera_android/android/build.gradle +++ b/packages/camera/camera_android/android/build.gradle @@ -63,5 +63,5 @@ dependencies { testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:4.7.0' testImplementation 'androidx.test:core:1.4.0' - testImplementation 'org.robolectric:robolectric:4.5' + testImplementation 'org.robolectric:robolectric:4.8.2' } From 79194fed81d3e9f8634fefe52a618817c03d3b83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Sep 2022 20:34:13 +0000 Subject: [PATCH 707/844] [video_player]: Bump robolectric from 4.4 to 4.8.2 in /packages/video_player/video_player_android/example/android/app (#6326) --- .../video_player_android/example/android/app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player_android/example/android/app/build.gradle b/packages/video_player/video_player_android/example/android/app/build.gradle index 84389f89e5e6..93caaa5c7c61 100644 --- a/packages/video_player/video_player_android/example/android/app/build.gradle +++ b/packages/video_player/video_player_android/example/android/app/build.gradle @@ -59,7 +59,7 @@ flutter { dependencies { testImplementation 'junit:junit:4.13' - testImplementation 'org.robolectric:robolectric:4.4' + testImplementation 'org.robolectric:robolectric:4.8.2' testImplementation 'org.mockito:mockito-core:4.7.0' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' From f032cdc44897ae480b2f7ec99eb6abcd076b7c4d Mon Sep 17 00:00:00 2001 From: Pierre-Louis <6655696+guidezpl@users.noreply.github.com> Date: Mon, 12 Sep 2022 11:44:12 +0200 Subject: [PATCH 708/844] [in_app_purchase] Replace `errorColor` to land deprecations (#6377) * Replace `errorColor` to land deprecations * x * x From 9abe69064a633a603482631f324c68cf22e75b2e Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Mon, 12 Sep 2022 07:33:32 -0700 Subject: [PATCH 709/844] Revert "[camera]: Bump robolectric from 4.5 to 4.8.2 in /packages/camera/camera_android/android (#6329)" (#6385) This reverts commit c04e5a36b51ebe6257fc1c71c43895355419e95e. --- packages/camera/camera_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/camera/camera_android/android/build.gradle b/packages/camera/camera_android/android/build.gradle index 89156b602582..b060181316c1 100644 --- a/packages/camera/camera_android/android/build.gradle +++ b/packages/camera/camera_android/android/build.gradle @@ -63,5 +63,5 @@ dependencies { testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:4.7.0' testImplementation 'androidx.test:core:1.4.0' - testImplementation 'org.robolectric:robolectric:4.8.2' + testImplementation 'org.robolectric:robolectric:4.5' } From 3b8addccfbec216d4bedded3ec275fd067e0edb8 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Mon, 12 Sep 2022 11:37:16 -0400 Subject: [PATCH 710/844] [webview_flutter_wkwebview] Fixes typo in an internal method name, from `setCookieForInsances` to `setCookieForInstances` (#6384) --- .../webview_flutter/webview_flutter_wkwebview/CHANGELOG.md | 3 ++- .../webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart | 2 +- .../lib/src/web_kit/web_kit_api_impls.dart | 2 +- .../webview_flutter/webview_flutter_wkwebview/pubspec.yaml | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index fff0110dd4ea..056d1cb1c26a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.9.4 * Fixes avoid_redundant_argument_values lint warnings and minor typos. +* Fixes typo in an internal method name, from `setCookieForInsances` to `setCookieForInstances`. ## 2.9.3 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 a671064ddc0f..566c46fda8bb 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 @@ -385,7 +385,7 @@ class WKHttpCookieStore extends NSObject { /// Adds a cookie to the cookie store. Future setCookie(NSHttpCookie cookie) { - return _httpCookieStoreApi.setCookieForInsances(this, cookie); + return _httpCookieStoreApi.setCookieForInstances(this, cookie); } @override 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 6a7fb6254889..614d0793e5f9 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 @@ -481,7 +481,7 @@ class WKHttpCookieStoreHostApiImpl extends WKHttpCookieStoreHostApi { } /// Calls [setCookie] with the ids of the provided object instances. - Future setCookieForInsances( + Future setCookieForInstances( WKHttpCookieStore instance, NSHttpCookie cookie, ) { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 804d4f3b12bc..40b7bf89601d 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/plugins/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: 2.9.3 +version: 2.9.4 environment: sdk: ">=2.17.0 <3.0.0" From 2fcecb109db51d18d2f32cdd4dcd502542f2427e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Sep 2022 17:35:53 +0000 Subject: [PATCH 711/844] [webview]: Bump mockito-inline from 4.7.0 to 4.8.0 in /packages/webview_flutter/webview_flutter_android/android (#6403) --- .../webview_flutter_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webview_flutter/webview_flutter_android/android/build.gradle b/packages/webview_flutter/webview_flutter_android/android/build.gradle index 8fd52e396da2..0b95a4be66ff 100644 --- a/packages/webview_flutter/webview_flutter_android/android/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/android/build.gradle @@ -38,7 +38,7 @@ android { implementation 'androidx.annotation:annotation:1.4.0' implementation 'androidx.webkit:webkit:1.5.0' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-inline:4.7.0' + testImplementation 'org.mockito:mockito-inline:4.8.0' testImplementation 'androidx.test:core:1.3.0' } From 2925683777816751e96e66e3310dca0d1856bb6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Sep 2022 17:36:31 +0000 Subject: [PATCH 712/844] [url_launcher]: Bump mockito-core from 1.10.19 to 4.8.0 in /packages/url_launcher/url_launcher_android/android (#6380) --- .../url_launcher/url_launcher_android/android/build.gradle | 2 +- .../plugins/urllauncher/MethodCallHandlerImplTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/url_launcher/url_launcher_android/android/build.gradle b/packages/url_launcher/url_launcher_android/android/build.gradle index a81080077997..a7d49e929a20 100644 --- a/packages/url_launcher/url_launcher_android/android/build.gradle +++ b/packages/url_launcher/url_launcher_android/android/build.gradle @@ -50,7 +50,7 @@ android { dependencies { compileOnly 'androidx.annotation:annotation:1.0.0' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-core:1.10.19' + testImplementation 'org.mockito:mockito-core:4.8.0' testImplementation 'androidx.test:core:1.0.0' testImplementation 'org.robolectric:robolectric:4.3' } diff --git a/packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java b/packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java index b60192531dbd..6bd88b650802 100644 --- a/packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java +++ b/packages/url_launcher/url_launcher_android/android/src/test/java/io/flutter/plugins/urllauncher/MethodCallHandlerImplTest.java @@ -4,8 +4,8 @@ package io.flutter.plugins.urllauncher; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; From f6e933ba59fec68bca100bed9f3fb364593444d6 Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Mon, 12 Sep 2022 12:18:35 -0700 Subject: [PATCH 713/844] Configure dependabot to ignore minor versions (#6355) --- .github/dependabot.yml | 246 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9a1011efaaba..16346f9a0b8c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,6 +7,17 @@ updates: 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"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/camera/camera_android/example/android/app" @@ -15,6 +26,9 @@ updates: 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/camera/camera_android_camerax/android" @@ -23,6 +37,17 @@ updates: 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"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/camera/camera_android_camerax/example/android/app" @@ -31,6 +56,9 @@ updates: 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/camera/camera/example/android/app" @@ -39,6 +67,9 @@ updates: 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/espresso/android" @@ -47,6 +78,17 @@ updates: 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"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/espresso/example/android/app" @@ -55,6 +97,9 @@ updates: 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/flutter_plugin_android_lifecycle/android" @@ -63,6 +108,17 @@ updates: 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"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/flutter_plugin_android_lifecycle/example/android/app" @@ -71,6 +127,9 @@ updates: 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/google_maps_flutter/google_maps_flutter/example/android/app" @@ -79,6 +138,9 @@ updates: 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/google_maps_flutter/google_maps_flutter_android/android" @@ -87,6 +149,17 @@ updates: 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"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/google_maps_flutter/google_maps_flutter_android/example/android/app" @@ -95,6 +168,9 @@ updates: 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/google_sign_in/google_sign_in/example/android/app" @@ -103,6 +179,9 @@ updates: 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/google_sign_in/google_sign_in_android/android" @@ -111,6 +190,17 @@ updates: 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"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/google_sign_in/google_sign_in_android/example/android/app" @@ -119,6 +209,9 @@ updates: 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/in_app_purchase/in_app_purchase_android/android" @@ -127,6 +220,17 @@ updates: 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"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/in_app_purchase/in_app_purchase_android/example/android/app" @@ -135,6 +239,9 @@ updates: 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/in_app_purchase/in_app_purchase/example/android/app" @@ -143,6 +250,9 @@ updates: 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" @@ -151,6 +261,9 @@ updates: 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_android/android" @@ -159,6 +272,17 @@ updates: 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"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/image_picker/image_picker_android/example/android/app" @@ -167,6 +291,9 @@ updates: 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/local_auth/local_auth_android/android" @@ -175,6 +302,17 @@ updates: 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"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/local_auth/local_auth_android/example/android/app" @@ -183,6 +321,9 @@ updates: 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/local_auth/local_auth/example/android/app" @@ -191,6 +332,9 @@ updates: 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/path_provider/path_provider/example/android/app" @@ -199,6 +343,9 @@ updates: 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/path_provider/path_provider_android/android" @@ -207,6 +354,17 @@ updates: 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"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/path_provider/path_provider_android/example/android/app" @@ -215,6 +373,9 @@ updates: 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/quick_actions/quick_actions_android/android" @@ -223,6 +384,17 @@ updates: 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"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/quick_actions/quick_actions_android/example/android/app" @@ -231,6 +403,9 @@ updates: 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/quick_actions/quick_actions/example/android/app" @@ -239,6 +414,9 @@ updates: 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/shared_preferences/shared_preferences/example/android/app" @@ -247,6 +425,9 @@ updates: 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/shared_preferences/shared_preferences_android/android" @@ -255,6 +436,17 @@ updates: 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"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/shared_preferences/shared_preferences_android/example/android/app" @@ -263,6 +455,9 @@ updates: 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/url_launcher/url_launcher_android/android" @@ -271,6 +466,17 @@ updates: 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"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/url_launcher/url_launcher_android/example/android/app" @@ -279,6 +485,9 @@ updates: 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/url_launcher/url_launcher/example/android/app" @@ -287,6 +496,9 @@ updates: 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/video_player/video_player/example/android/app" @@ -295,6 +507,9 @@ updates: 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/video_player/video_player_android/android" @@ -303,6 +518,17 @@ updates: 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"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/video_player/video_player_android/example/android/app" @@ -311,6 +537,9 @@ updates: 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/webview_flutter/webview_flutter/example/android/app" @@ -319,6 +548,9 @@ updates: 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/webview_flutter/webview_flutter_android/android" @@ -327,6 +559,17 @@ updates: 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"] + - dependency-name: "org.robolectric:*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "gradle" directory: "/packages/webview_flutter/webview_flutter_android/example/android/app" @@ -335,6 +578,9 @@ updates: schedule: interval: "weekly" open-pull-requests-limit: 10 + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-minor", "version-update:semver-patch"] - package-ecosystem: "github-actions" directory: "/" From 4209dba5ec6f04e7f13e3ddcc718764e17865645 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Mon, 12 Sep 2022 16:06:17 -0400 Subject: [PATCH 714/844] [webview_flutter] Implementation of the app facing WebViewWidget for v4 (#6367) --- .../lib/src/v4/src/webview_widget.dart | 64 +++++ .../lib/src/v4/webview_flutter.dart | 1 + .../test/v4/webview_widget_test.dart | 87 +++++++ .../test/v4/webview_widget_test.mocks.dart | 246 ++++++++++++++++++ 4 files changed, 398 insertions(+) create mode 100644 packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_widget.dart create mode 100644 packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.dart create mode 100644 packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.mocks.dart diff --git a/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_widget.dart b/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_widget.dart new file mode 100644 index 000000000000..06e4f78028df --- /dev/null +++ b/packages/webview_flutter/webview_flutter/lib/src/v4/src/webview_widget.dart @@ -0,0 +1,64 @@ +// 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/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; + +import 'webview_controller.dart'; + +/// Displays a native WebView as a Widget. +class WebViewWidget extends StatelessWidget { + /// Constructs a [WebViewWidget]. + WebViewWidget({ + Key? key, + required WebViewController controller, + TextDirection layoutDirection = TextDirection.ltr, + Set> gestureRecognizers = + const >{}, + }) : this.fromPlatformCreationParams( + key: key, + params: PlatformWebViewWidgetCreationParams( + controller: controller.platform, + layoutDirection: layoutDirection, + gestureRecognizers: gestureRecognizers, + ), + ); + + /// Constructs a [WebViewWidget] from creation params for a specific + /// platform. + WebViewWidget.fromPlatformCreationParams({ + Key? key, + required PlatformWebViewWidgetCreationParams params, + }) : this.fromPlatform(key: key, platform: PlatformWebViewWidget(params)); + + /// Constructs a [WebViewWidget] from a specific platform implementation. + WebViewWidget.fromPlatform({Key? key, required this.platform}) + : super(key: key); + + /// Implementation of [PlatformWebViewWidget] for the current platform. + final PlatformWebViewWidget platform; + + /// The layout direction to use for the embedded WebView. + late final TextDirection layoutDirection = platform.params.layoutDirection; + + /// Specifies which gestures should be consumed by the web view. + /// + /// It is possible for other gesture recognizers to be competing with the web + /// view on pointer events, e.g if the web view is inside a [ListView] the + /// [ListView] will want to handle vertical drags. The web view will claim + /// gestures that are recognized by any of the recognizers on this list. + /// + /// When `gestureRecognizers` is empty (default), the web view will only + /// handle pointer events for gestures that were not claimed by any other + /// gesture recognizer. + late final Set> gestureRecognizers = + platform.params.gestureRecognizers; + + @override + Widget build(BuildContext context) { + return platform.build(context); + } +} diff --git a/packages/webview_flutter/webview_flutter/lib/src/v4/webview_flutter.dart b/packages/webview_flutter/webview_flutter/lib/src/v4/webview_flutter.dart index f3caf84dcf56..f4a0b207e27a 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/v4/webview_flutter.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/v4/webview_flutter.dart @@ -9,3 +9,4 @@ export 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_i export 'src/webview_controller.dart'; export 'src/webview_cookie_manager.dart'; +export 'src/webview_widget.dart'; diff --git a/packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.dart b/packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.dart new file mode 100644 index 000000000000..455d8b371ec7 --- /dev/null +++ b/packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.dart @@ -0,0 +1,87 @@ +// 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/gestures.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:webview_flutter/src/v4/webview_flutter.dart'; +import 'package:webview_flutter_platform_interface/v4/webview_flutter_platform_interface.dart'; + +import 'webview_widget_test.mocks.dart'; + +@GenerateMocks([PlatformWebViewController, PlatformWebViewWidget]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('WebViewWidget', () { + testWidgets('build', (WidgetTester tester) async { + final MockPlatformWebViewWidget mockPlatformWebViewWidget = + MockPlatformWebViewWidget(); + when(mockPlatformWebViewWidget.build(any)).thenReturn(Container()); + + await tester.pumpWidget(WebViewWidget.fromPlatform( + platform: mockPlatformWebViewWidget, + )); + + expect(find.byType(Container), findsOneWidget); + }); + + testWidgets( + 'constructor parameters are correctly passed to creation params', + (WidgetTester tester) async { + WebViewPlatform.instance = TestWebViewPlatform(); + + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + final WebViewController webViewController = + WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + final WebViewWidget webViewWidget = WebViewWidget( + key: GlobalKey(), + controller: webViewController, + layoutDirection: TextDirection.rtl, + gestureRecognizers: >{ + Factory(() => EagerGestureRecognizer()), + }, + ); + + // The key passed to the default constructor is used by the super class + // and not passed to the platform implementation. + expect(webViewWidget.platform.params.key, isNull); + expect( + webViewWidget.platform.params.controller, + webViewController.platform, + ); + expect(webViewWidget.platform.params.layoutDirection, TextDirection.rtl); + expect( + webViewWidget.platform.params.gestureRecognizers.isNotEmpty, + isTrue, + ); + }); + }); +} + +class TestWebViewPlatform extends WebViewPlatform { + @override + PlatformWebViewWidget createPlatformWebViewWidget( + PlatformWebViewWidgetCreationParams params, + ) { + return TestPlatformWebViewWidget(params); + } +} + +class TestPlatformWebViewWidget extends PlatformWebViewWidget { + TestPlatformWebViewWidget(PlatformWebViewWidgetCreationParams params) + : super.implementation(params); + + @override + Widget build(BuildContext context) { + throw UnimplementedError(); + } +} diff --git a/packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.mocks.dart new file mode 100644 index 000000000000..e481d752be5d --- /dev/null +++ b/packages/webview_flutter/webview_flutter/test/v4/webview_widget_test.mocks.dart @@ -0,0 +1,246 @@ +// Mocks generated by Mockito 5.3.0 from annotations +// in webview_flutter/test/v4/webview_widget_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i7; +import 'dart:math' as _i3; +import 'dart:ui' as _i9; + +import 'package:flutter/foundation.dart' as _i5; +import 'package:flutter/widgets.dart' as _i4; +import 'package:mockito/mockito.dart' as _i1; +import 'package:webview_flutter_platform_interface/v4/src/platform_navigation_delegate.dart' + as _i8; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_controller.dart' + as _i6; +import 'package:webview_flutter_platform_interface/v4/src/platform_webview_widget.dart' + as _i10; +import 'package:webview_flutter_platform_interface/v4/src/webview_platform.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: 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 + +class _FakePlatformWebViewControllerCreationParams_0 extends _i1.SmartFake + implements _i2.PlatformWebViewControllerCreationParams { + _FakePlatformWebViewControllerCreationParams_0( + Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +class _FakePoint_1 extends _i1.SmartFake + implements _i3.Point { + _FakePoint_1(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +class _FakePlatformWebViewWidgetCreationParams_2 extends _i1.SmartFake + implements _i2.PlatformWebViewWidgetCreationParams { + _FakePlatformWebViewWidgetCreationParams_2( + Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +class _FakeWidget_3 extends _i1.SmartFake implements _i4.Widget { + _FakeWidget_3(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); + + @override + String toString({_i5.DiagnosticLevel? minLevel = _i5.DiagnosticLevel.info}) => + super.toString(); +} + +/// A class which mocks [PlatformWebViewController]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockPlatformWebViewController extends _i1.Mock + implements _i6.PlatformWebViewController { + MockPlatformWebViewController() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.PlatformWebViewControllerCreationParams get params => + (super.noSuchMethod(Invocation.getter(#params), + returnValue: _FakePlatformWebViewControllerCreationParams_0( + this, Invocation.getter(#params))) + as _i2.PlatformWebViewControllerCreationParams); + @override + _i7.Future loadFile(String? absoluteFilePath) => (super.noSuchMethod( + Invocation.method(#loadFile, [absoluteFilePath]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future loadFlutterAsset(String? key) => (super.noSuchMethod( + Invocation.method(#loadFlutterAsset, [key]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future loadHtmlString(String? html, {String? baseUrl}) => + (super.noSuchMethod( + Invocation.method(#loadHtmlString, [html], {#baseUrl: baseUrl}), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) + as _i7.Future); + @override + _i7.Future loadRequest(_i2.LoadRequestParams? params) => + (super.noSuchMethod(Invocation.method(#loadRequest, [params]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) + as _i7.Future); + @override + _i7.Future currentUrl() => + (super.noSuchMethod(Invocation.method(#currentUrl, []), + returnValue: _i7.Future.value()) as _i7.Future); + @override + _i7.Future canGoBack() => + (super.noSuchMethod(Invocation.method(#canGoBack, []), + returnValue: _i7.Future.value(false)) as _i7.Future); + @override + _i7.Future canGoForward() => + (super.noSuchMethod(Invocation.method(#canGoForward, []), + returnValue: _i7.Future.value(false)) as _i7.Future); + @override + _i7.Future goBack() => (super.noSuchMethod( + Invocation.method(#goBack, []), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future goForward() => (super.noSuchMethod( + Invocation.method(#goForward, []), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future reload() => (super.noSuchMethod( + Invocation.method(#reload, []), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future clearCache() => (super.noSuchMethod( + Invocation.method(#clearCache, []), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future clearLocalStorage() => (super.noSuchMethod( + Invocation.method(#clearLocalStorage, []), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future setPlatformNavigationDelegate( + _i8.PlatformNavigationDelegate? handler) => + (super.noSuchMethod( + Invocation.method(#setPlatformNavigationDelegate, [handler]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) + as _i7.Future); + @override + _i7.Future runJavaScript(String? javaScript) => (super.noSuchMethod( + Invocation.method(#runJavaScript, [javaScript]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future runJavaScriptReturningResult(String? javaScript) => + (super.noSuchMethod( + Invocation.method(#runJavaScriptReturningResult, [javaScript]), + returnValue: _i7.Future.value('')) as _i7.Future); + @override + _i7.Future addJavaScriptChannel( + _i6.JavaScriptChannelParams? javaScriptChannelParams) => + (super.noSuchMethod( + Invocation.method(#addJavaScriptChannel, [javaScriptChannelParams]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: + _i7.Future.value()) as _i7.Future); + @override + _i7.Future removeJavaScriptChannel(String? javaScriptChannelName) => + (super.noSuchMethod( + Invocation.method(#removeJavaScriptChannel, [javaScriptChannelName]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: + _i7.Future.value()) as _i7.Future); + @override + _i7.Future getTitle() => + (super.noSuchMethod(Invocation.method(#getTitle, []), + returnValue: _i7.Future.value()) as _i7.Future); + @override + _i7.Future scrollTo(int? x, int? y) => (super.noSuchMethod( + Invocation.method(#scrollTo, [x, y]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future scrollBy(int? x, int? y) => (super.noSuchMethod( + Invocation.method(#scrollBy, [x, y]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future<_i3.Point> getScrollPosition() => + (super.noSuchMethod(Invocation.method(#getScrollPosition, []), + returnValue: _i7.Future<_i3.Point>.value(_FakePoint_1( + this, Invocation.method(#getScrollPosition, [])))) + as _i7.Future<_i3.Point>); + @override + _i7.Future enableDebugging(bool? enabled) => (super.noSuchMethod( + Invocation.method(#enableDebugging, [enabled]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future enableGestureNavigation(bool? enabled) => (super + .noSuchMethod(Invocation.method(#enableGestureNavigation, [enabled]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) + as _i7.Future); + @override + _i7.Future enableZoom(bool? enabled) => (super.noSuchMethod( + Invocation.method(#enableZoom, [enabled]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future setBackgroundColor(_i9.Color? color) => (super.noSuchMethod( + Invocation.method(#setBackgroundColor, [color]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); + @override + _i7.Future setJavaScriptMode(_i2.JavaScriptMode? javaScriptMode) => + (super.noSuchMethod( + Invocation.method(#setJavaScriptMode, [javaScriptMode]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) + as _i7.Future); + @override + _i7.Future setUserAgent(String? userAgent) => (super.noSuchMethod( + Invocation.method(#setUserAgent, [userAgent]), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value()) as _i7.Future); +} + +/// A class which mocks [PlatformWebViewWidget]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockPlatformWebViewWidget extends _i1.Mock + implements _i10.PlatformWebViewWidget { + MockPlatformWebViewWidget() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.PlatformWebViewWidgetCreationParams get params => + (super.noSuchMethod(Invocation.getter(#params), + returnValue: _FakePlatformWebViewWidgetCreationParams_2( + this, Invocation.getter(#params))) + as _i2.PlatformWebViewWidgetCreationParams); + @override + _i4.Widget build(_i4.BuildContext? context) => + (super.noSuchMethod(Invocation.method(#build, [context]), + returnValue: + _FakeWidget_3(this, Invocation.method(#build, [context]))) + as _i4.Widget); +} From 35084f139c15c89cc45e9cb0199d2b212090f0fa Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Mon, 12 Sep 2022 14:15:55 -0700 Subject: [PATCH 715/844] Revert "[url_launcher]: Bump gradle from 3.4.2 to 7.2.2 in /packages/url_launcher/url_launcher_android/android #6194" (#6383) --- packages/url_launcher/url_launcher_android/CHANGELOG.md | 4 ++++ .../url_launcher/url_launcher_android/android/build.gradle | 2 +- packages/url_launcher/url_launcher_android/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md index be98dc2ddc5e..c75e93e821a1 100644 --- a/packages/url_launcher/url_launcher_android/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.0.19 + +* Revert gradle back to 3.4.2. + ## 6.0.18 * Updates gradle to 7.2.2. diff --git a/packages/url_launcher/url_launcher_android/android/build.gradle b/packages/url_launcher/url_launcher_android/android/build.gradle index a7d49e929a20..a4fa269cbfc7 100644 --- a/packages/url_launcher/url_launcher_android/android/build.gradle +++ b/packages/url_launcher/url_launcher_android/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.2.2' + classpath 'com.android.tools.build:gradle:3.4.2' } } diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index 2b0ba2e1b75e..db2bddcc54c3 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_android description: Android implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.18 +version: 6.0.19 environment: sdk: ">=2.14.0 <3.0.0" From 0497c90841ea31cd0a493ff6e0fa896dbcc0215e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 13 Sep 2022 11:00:45 -0400 Subject: [PATCH 716/844] Roll Flutter (stable) from ffccd96b62ee to 4f9d92fbbdf0 (1 revision) --- .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 14a210a6a125..d9b146dd7f87 100644 --- a/.ci/flutter_stable.version +++ b/.ci/flutter_stable.version @@ -1 +1 @@ -ffccd96b62ee8cec7740dab303538c5fc26ac543 +4f9d92fbbdf072a70a70d2179a9f87392b94104c From 7e65cdd1d4079bd2bf8034952a7a7b50fca082d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Sep 2022 11:02:30 -0400 Subject: [PATCH 717/844] [image_picker]: Bump mockito-core from 3.10.0 to 4.8.0 in /packages/image_picker/image_picker_android/android (#6381) * [image_picker]: Bump mockito-core Bumps [mockito-core](https://github.com/mockito/mockito) from 3.10.0 to 4.8.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v3.10.0...v4.8.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * Update tests for API changes * format Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Stuart Morgan --- .../image_picker_android/android/build.gradle | 2 +- .../imagepicker/ImagePickerPluginTest.java | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/image_picker/image_picker_android/android/build.gradle b/packages/image_picker/image_picker_android/android/build.gradle index 1d428462bea3..a6c01e386448 100644 --- a/packages/image_picker/image_picker_android/android/build.gradle +++ b/packages/image_picker/image_picker_android/android/build.gradle @@ -38,7 +38,7 @@ android { implementation 'androidx.exifinterface:exifinterface:1.3.3' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-core:3.10.0' + testImplementation 'org.mockito:mockito-core:4.8.0' testImplementation 'androidx.test:core:1.4.0' testImplementation "org.robolectric:robolectric:4.8.1" } diff --git a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java index ce41343e3d2c..36452479776e 100644 --- a/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java +++ b/packages/image_picker/image_picker_android/android/src/test/java/io/flutter/plugins/imagepicker/ImagePickerPluginTest.java @@ -14,7 +14,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import android.app.Activity; @@ -76,7 +76,7 @@ public void onMethodCall_WhenActivityIsNull_FinishesWithForegroundActivityRequir imagePickerPluginWithNullActivity.onMethodCall(call, mockResult); verify(mockResult) .error("no_activity", "image_picker plugin requires a foreground activity.", null); - verifyZeroInteractions(mockImagePickerDelegate); + verifyNoInteractions(mockImagePickerDelegate); } @Test @@ -84,8 +84,8 @@ public void onMethodCall_WhenCalledWithUnknownMethod_ThrowsException() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Unknown method test"); plugin.onMethodCall(new MethodCall("test", null), mockResult); - verifyZeroInteractions(mockImagePickerDelegate); - verifyZeroInteractions(mockResult); + verifyNoInteractions(mockImagePickerDelegate); + verifyNoInteractions(mockResult); } @Test @@ -93,8 +93,8 @@ public void onMethodCall_WhenCalledWithUnknownImageSource_ThrowsException() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Invalid image source: -1"); plugin.onMethodCall(buildMethodCall(PICK_IMAGE, -1), mockResult); - verifyZeroInteractions(mockImagePickerDelegate); - verifyZeroInteractions(mockResult); + verifyNoInteractions(mockImagePickerDelegate); + verifyNoInteractions(mockResult); } @Test @@ -102,7 +102,7 @@ public void onMethodCall_WhenSourceIsGallery_InvokesChooseImageFromGallery() { MethodCall call = buildMethodCall(PICK_IMAGE, SOURCE_GALLERY); plugin.onMethodCall(call, mockResult); verify(mockImagePickerDelegate).chooseImageFromGallery(eq(call), any()); - verifyZeroInteractions(mockResult); + verifyNoInteractions(mockResult); } @Test @@ -110,7 +110,7 @@ public void onMethodCall_InvokesChooseMultiImageFromGallery() { MethodCall call = buildMethodCall(PICK_MULTI_IMAGE); plugin.onMethodCall(call, mockResult); verify(mockImagePickerDelegate).chooseMultiImageFromGallery(eq(call), any()); - verifyZeroInteractions(mockResult); + verifyNoInteractions(mockResult); } @Test @@ -118,7 +118,7 @@ public void onMethodCall_WhenSourceIsCamera_InvokesTakeImageWithCamera() { MethodCall call = buildMethodCall(PICK_IMAGE, SOURCE_CAMERA); plugin.onMethodCall(call, mockResult); verify(mockImagePickerDelegate).takeImageWithCamera(eq(call), any()); - verifyZeroInteractions(mockResult); + verifyNoInteractions(mockResult); } @Test From 3cf0ec5f262c1b727df7d3f72580284d1c2b8920 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 13 Sep 2022 09:25:02 -0700 Subject: [PATCH 718/844] [ci] Manually roll master (#6418) Rolls Flutter master to the version that first failed due to flutter/engine#33688 Updates the camera_windows mocks to include the new method so that the tests will compile on master. Fixes the blocked roll. --- .ci/flutter_master.version | 2 +- .../windows/test/capture_controller_test.cpp | 4 ++-- .../camera_windows/windows/test/mocks.h | 21 ++++++++++++++++++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index af8df44327ff..74c1c9a9cd2b 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -4cd734ae3a1cc0c8f132ba07e6d2e0a3253c53e7 +4930444f4afe85272bbdc84b43196d59dad71166 diff --git a/packages/camera/camera_windows/windows/test/capture_controller_test.cpp b/packages/camera/camera_windows/windows/test/capture_controller_test.cpp index 4662d3340456..8d6632cbc3f0 100644 --- a/packages/camera/camera_windows/windows/test/capture_controller_test.cpp +++ b/packages/camera/camera_windows/windows/test/capture_controller_test.cpp @@ -291,7 +291,7 @@ TEST(CaptureController, InitCaptureEngineReportsFailure) { EXPECT_CALL(*engine.Get(), Initialize).Times(1).WillOnce(Return(E_FAIL)); EXPECT_CALL(*texture_registrar, RegisterTexture).Times(0); - EXPECT_CALL(*texture_registrar, UnregisterTexture).Times(0); + EXPECT_CALL(*texture_registrar, UnregisterTexture(_)).Times(0); EXPECT_CALL(*camera, OnCreateCaptureEngineSucceeded).Times(0); EXPECT_CALL(*camera, OnCreateCaptureEngineFailed(Eq(CameraResult::kError), @@ -335,7 +335,7 @@ TEST(CaptureController, InitCaptureEngineReportsAccessDenied) { .WillOnce(Return(E_ACCESSDENIED)); EXPECT_CALL(*texture_registrar, RegisterTexture).Times(0); - EXPECT_CALL(*texture_registrar, UnregisterTexture).Times(0); + EXPECT_CALL(*texture_registrar, UnregisterTexture(_)).Times(0); EXPECT_CALL(*camera, OnCreateCaptureEngineSucceeded).Times(0); EXPECT_CALL(*camera, OnCreateCaptureEngineFailed(Eq(CameraResult::kAccessDenied), diff --git a/packages/camera/camera_windows/windows/test/mocks.h b/packages/camera/camera_windows/windows/test/mocks.h index 678b75899bc2..b6416eb7c710 100644 --- a/packages/camera/camera_windows/windows/test/mocks.h +++ b/packages/camera/camera_windows/windows/test/mocks.h @@ -67,7 +67,8 @@ class MockTextureRegistrar : public flutter::TextureRegistrar { return this->texture_id_; }); - ON_CALL(*this, UnregisterTexture) + // Deprecated pre-Flutter-3.4 version. + ON_CALL(*this, UnregisterTexture(_)) .WillByDefault([this](int64_t tid) -> bool { if (tid == this->texture_id_) { texture_ = nullptr; @@ -77,6 +78,18 @@ class MockTextureRegistrar : public flutter::TextureRegistrar { return false; }); + // Flutter 3.4+ version. + ON_CALL(*this, UnregisterTexture(_, _)) + .WillByDefault( + [this](int64_t tid, std::function callback) -> void { + // Forward to the pre-3.4 implementation so that expectations can + // be the same for all versions. + this->UnregisterTexture(tid); + if (callback) { + callback(); + } + }); + ON_CALL(*this, MarkTextureFrameAvailable) .WillByDefault([this](int64_t tid) -> bool { if (tid == this->texture_id_) { @@ -91,7 +104,13 @@ class MockTextureRegistrar : public flutter::TextureRegistrar { MOCK_METHOD(int64_t, RegisterTexture, (flutter::TextureVariant * texture), (override)); + // Pre-Flutter-3.4 version. MOCK_METHOD(bool, UnregisterTexture, (int64_t), (override)); + // Flutter 3.4+ version. + // TODO(cbracken): Add an override annotation to this once 3.4+ is the + // minimum version tested in CI. + MOCK_METHOD(void, UnregisterTexture, + (int64_t, std::function callback), ()); MOCK_METHOD(bool, MarkTextureFrameAvailable, (int64_t), (override)); int64_t texture_id_ = -1; From 0fca680fbca9cf6a9ace2e94e42c4f8bd8de11e6 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 13 Sep 2022 13:12:57 -0400 Subject: [PATCH 719/844] [webview_flutter_android] Adds the scroll position method to WebView and updates pigeon instructions (#6366) --- .../webview_flutter_android/CHANGELOG.md | 5 + .../webview_flutter_android/README.md | 17 +- .../GeneratedAndroidWebView.java | 125 ++++++++- .../webviewflutter/WebViewHostApiImpl.java | 11 + .../plugins/webviewflutter/WebViewTest.java | 9 + .../lib/src/android_webview.dart | 5 + .../lib/src/android_webview.pigeon.dart | 83 +++++- .../lib/src/android_webview_api_impls.dart | 10 +- .../pigeons/android_webview.dart | 9 + .../webview_flutter_android/pubspec.yaml | 6 +- .../test/android_webview_test.dart | 9 + .../test/android_webview_test.mocks.dart | 165 ++++++------ .../test/test_android_webview.pigeon.dart | 49 +++- .../webview_android_widget_test.mocks.dart | 237 +++++++++--------- 14 files changed, 518 insertions(+), 222 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 0eb1aac530df..910d20dda6d1 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.10.1 + +* Adds a method to the `WebView` wrapper to retrieve the X and Y positions simultaneously. +* Removes reference to https://github.com/flutter/flutter/issues/97744 from `README`. + ## 2.10.0 * Bumps webkit from 1.0.0 to 1.5.0. diff --git a/packages/webview_flutter/webview_flutter_android/README.md b/packages/webview_flutter/webview_flutter_android/README.md index 80f1f6e0b9cb..bdd9edf99eaa 100644 --- a/packages/webview_flutter/webview_flutter_android/README.md +++ b/packages/webview_flutter/webview_flutter_android/README.md @@ -14,25 +14,10 @@ platform (Android). The communication interface is defined in the `pigeons/andro file. After editing the communication interface regenerate the communication layer by running `flutter pub run pigeon --input pigeons/android_webview.dart`. -Due to [flutter/flutter#97744](https://github.com/flutter/flutter/issues/97744), the generated test -pigeon file needs one of its imports updated to properly work with `mockito`. - -In `test/android_webview.pigeon.dart`, change - -```dart -import '../lib/src/android_webview.pigeon.dart'; -``` - -to - -```dart -import 'package:webview_flutter_android/src/android_webview.pigeon.dart'; -``` - Besides [pigeon][3] this package also uses [mockito][4] to generate mock objects for testing purposes. To generate the mock objects run the following command: ```bash -flutter packages pub run build_runner build --delete-conflicting-outputs +flutter pub run build_runner build --delete-conflicting-outputs ``` If you would like to contribute to the plugin, check out our [contribution guide][5]. 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 34fd8b79b315..9a6b1c44d39d 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 @@ -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 (v3.2.0), do not edit directly. +// Autogenerated from Pigeon (v4.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.webviewflutter; @@ -265,6 +265,78 @@ Map toMap() { } } + /** Generated class from Pigeon that represents data sent in messages. */ + public static class WebViewPoint { + private @NonNull Long x; + + public @NonNull Long getX() { + return x; + } + + public void setX(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"x\" is null."); + } + this.x = setterArg; + } + + private @NonNull Long y; + + public @NonNull Long getY() { + return y; + } + + public void setY(@NonNull Long setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"y\" is null."); + } + this.y = setterArg; + } + + /** Constructor is private to enforce null safety; use Builder. */ + private WebViewPoint() {} + + public static final class Builder { + private @Nullable Long x; + + public @NonNull Builder setX(@NonNull Long setterArg) { + this.x = setterArg; + return this; + } + + private @Nullable Long y; + + public @NonNull Builder setY(@NonNull Long setterArg) { + this.y = setterArg; + return this; + } + + public @NonNull WebViewPoint build() { + WebViewPoint pigeonReturn = new WebViewPoint(); + pigeonReturn.setX(x); + pigeonReturn.setY(y); + return pigeonReturn; + } + } + + @NonNull + Map toMap() { + Map toMapResult = new HashMap<>(); + toMapResult.put("x", x); + toMapResult.put("y", y); + return toMapResult; + } + + static @NonNull WebViewPoint fromMap(@NonNull Map map) { + WebViewPoint pigeonResult = new WebViewPoint(); + Object x = map.get("x"); + pigeonResult.setX((x == null) ? null : ((x instanceof Integer) ? (Integer) x : (Long) x)); + Object y = map.get("y"); + pigeonResult.setY((y == null) ? null : ((y instanceof Integer) ? (Integer) y : (Long) y)); + return pigeonResult; + } + } + public interface Result { void success(T result); @@ -444,6 +516,27 @@ private static class WebViewHostApiCodec extends StandardMessageCodec { public static final WebViewHostApiCodec INSTANCE = new WebViewHostApiCodec(); private WebViewHostApiCodec() {} + + @Override + protected Object readValueOfType(byte type, ByteBuffer buffer) { + switch (type) { + case (byte) 128: + return WebViewPoint.fromMap((Map) readValue(buffer)); + + default: + return super.readValueOfType(type, buffer); + } + } + + @Override + protected void writeValue(ByteArrayOutputStream stream, Object value) { + if (value instanceof WebViewPoint) { + stream.write(128); + writeValue(stream, ((WebViewPoint) value).toMap()); + } else { + super.writeValue(stream, value); + } + } } /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ @@ -504,6 +597,9 @@ void evaluateJavascript( @NonNull Long getScrollY(@NonNull Long instanceId); + @NonNull + WebViewPoint getScrollPosition(@NonNull Long instanceId); + void setWebContentsDebuggingEnabled(@NonNull Boolean enabled); void setWebViewClient(@NonNull Long instanceId, @NonNull Long webViewClientInstanceId); @@ -1108,6 +1204,33 @@ public void error(Throwable error) { channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.WebViewHostApi.getScrollPosition", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number instanceIdArg = (Number) args.get(0); + if (instanceIdArg == null) { + throw new NullPointerException("instanceIdArg unexpectedly null."); + } + WebViewPoint output = + api.getScrollPosition( + (instanceIdArg == null) ? null : instanceIdArg.longValue()); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } { BasicMessageChannel channel = new BasicMessageChannel<>( diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java index 56761d100133..f257ace71b0d 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java @@ -21,6 +21,7 @@ import io.flutter.plugins.webviewflutter.WebViewClientHostApiImpl.ReleasableWebViewClient; import java.util.HashMap; import java.util.Map; +import java.util.Objects; /** * Host api implementation for {@link WebView}. @@ -456,6 +457,16 @@ public Long getScrollY(Long instanceId) { return (long) webView.getScrollY(); } + @NonNull + @Override + public GeneratedAndroidWebView.WebViewPoint getScrollPosition(@NonNull Long instanceId) { + final WebView webView = Objects.requireNonNull(instanceManager.getInstance(instanceId)); + return new GeneratedAndroidWebView.WebViewPoint.Builder() + .setX((long) webView.getScrollX()) + .setY((long) webView.getScrollY()) + .build(); + } + @Override public void setWebContentsDebuggingEnabled(Boolean enabled) { webViewProxy.setWebContentsDebuggingEnabled(enabled); diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java index 89bbd7c6c7c0..30bc256cd985 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java @@ -312,6 +312,15 @@ public void getScrollY() { assertEquals((long) testHostApiImpl.getScrollY(0L), 23); } + @Test + public void getScrollPosition() { + when(mockWebView.getScrollX()).thenReturn(1); + when(mockWebView.getScrollY()).thenReturn(2); + final GeneratedAndroidWebView.WebViewPoint position = testHostApiImpl.getScrollPosition(0L); + assertEquals((long) position.getX(), 1L); + assertEquals((long) position.getY(), 2L); + } + @Test public void setWebViewClient() { final WebViewClient mockWebViewClient = mock(WebViewClient.class); 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 30c59ea43d2c..e08703106990 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 @@ -321,6 +321,11 @@ class WebView extends JavaObject { return api.getScrollYFromInstance(this); } + /// Returns the X and Y scroll position of this view. + Future getScrollPosition() { + return api.getScrollPositionFromInstance(this); + } + /// Sets the [WebViewClient] that will receive various notifications and requests. /// /// This will replace the current handler. diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart index f7c83b79d25a..e793dbb950a8 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart @@ -1,14 +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. -// Autogenerated from Pigeon (v3.2.0), do not edit directly. +// Autogenerated from Pigeon (v4.0.2), 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 -// @dart = 2.12 +// 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 import 'dart:async'; -import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; -import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; class WebResourceRequestData { @@ -78,6 +77,31 @@ class WebResourceErrorData { } } +class WebViewPoint { + WebViewPoint({ + required this.x, + required this.y, + }); + + int x; + int y; + + Object encode() { + final Map pigeonMap = {}; + pigeonMap['x'] = x; + pigeonMap['y'] = y; + return pigeonMap; + } + + static WebViewPoint decode(Object message) { + final Map pigeonMap = message as Map; + return WebViewPoint( + x: pigeonMap['x']! as int, + y: pigeonMap['y']! as int, + ); + } +} + class _JavaObjectHostApiCodec extends StandardMessageCodec { const _JavaObjectHostApiCodec(); } @@ -221,6 +245,26 @@ class CookieManagerHostApi { class _WebViewHostApiCodec extends StandardMessageCodec { const _WebViewHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is WebViewPoint) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return WebViewPoint.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } } class WebViewHostApi { @@ -734,6 +778,35 @@ class WebViewHostApi { } } + Future getScrollPosition(int arg_instanceId) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebViewHostApi.getScrollPosition', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as WebViewPoint?)!; + } + } + Future setWebContentsDebuggingEnabled(bool arg_enabled) async { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.setWebContentsDebuggingEnabled', 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 00ededabd977..4f2c51c4a13a 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 @@ -5,8 +5,9 @@ // TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#106316) // ignore: unnecessary_import import 'dart:typed_data'; +import 'dart:ui'; -import 'package:flutter/services.dart'; +import 'package:flutter/services.dart' show BinaryMessenger; import 'android_webview.dart'; import 'android_webview.pigeon.dart'; @@ -278,6 +279,13 @@ class WebViewHostApiImpl extends WebViewHostApi { return getScrollY(instanceManager.getIdentifier(instance)!); } + /// Helper method to convert instances ids to objects. + Future getScrollPositionFromInstance(WebView instance) async { + final WebViewPoint position = + await getScrollPosition(instanceManager.getIdentifier(instance)!); + return Offset(position.x.toDouble(), position.y.toDouble()); + } + /// Helper method to convert instances ids to objects. Future setWebViewClientFromInstance( WebView instance, 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 eb979870a1d3..f3946ed4212a 100644 --- a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart +++ b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart @@ -51,6 +51,13 @@ class WebResourceErrorData { String description; } +class WebViewPoint { + WebViewPoint(this.x, this.y); + + int x; + int y; +} + /// Handles methods calls to the native Java Object class. /// /// Also handles calls to remove the reference to an instance with `dispose`. @@ -141,6 +148,8 @@ abstract class WebViewHostApi { int getScrollY(int instanceId); + WebViewPoint getScrollPosition(int instanceId); + void setWebContentsDebuggingEnabled(bool enabled); void setWebViewClient(int instanceId, int webViewClientInstanceId); diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 4c1f5766e712..62f4cc97aa06 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/plugins/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: 2.10.0 +version: 2.10.1 environment: sdk: ">=2.14.0 <3.0.0" @@ -27,5 +27,5 @@ dev_dependencies: sdk: flutter flutter_test: sdk: flutter - mockito: ^5.1.0 - pigeon: ^3.0.3 + mockito: ^5.2.0 + pigeon: ^4.0.2 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 d57eadc1d5cc..f3ec4bd0cb9f 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 @@ -252,6 +252,15 @@ void main() { expect(webView.getScrollY(), completion(56)); }); + test('getScrollPosition', () async { + when(mockPlatformHostApi.getScrollPosition(webViewInstanceId)) + .thenReturn(WebViewPoint(x: 2, y: 16)); + await expectLater( + webView.getScrollPosition(), + completion(const Offset(2.0, 16.0)), + ); + }); + test('setWebViewClient', () { TestWebViewClientHostApi.setup(MockTestWebViewClientHostApi()); WebViewClient.api = WebViewClientHostApiImpl( 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 6afda598d93b..116ac834f1d6 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 @@ -2,15 +2,15 @@ // in webview_flutter_android/test/android_webview_test.dart. // Do not manually edit this file. -import 'dart:async' as _i4; -import 'dart:typed_data' as _i6; -import 'dart:ui' as _i7; +import 'dart:async' as _i5; +import 'dart:typed_data' as _i7; +import 'dart:ui' as _i4; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_android/src/android_webview.dart' as _i2; import 'package:webview_flutter_android/src/android_webview.pigeon.dart' as _i3; -import 'test_android_webview.pigeon.dart' as _i5; +import 'test_android_webview.pigeon.dart' as _i6; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -28,13 +28,17 @@ class _FakeDownloadListener_0 extends _i1.Fake class _FakeJavaScriptChannel_1 extends _i1.Fake implements _i2.JavaScriptChannel {} -class _FakeWebChromeClient_2 extends _i1.Fake implements _i2.WebChromeClient {} +class _FakeWebViewPoint_2 extends _i1.Fake implements _i3.WebViewPoint {} -class _FakeWebSettings_3 extends _i1.Fake implements _i2.WebSettings {} +class _FakeWebChromeClient_3 extends _i1.Fake implements _i2.WebChromeClient {} -class _FakeWebView_4 extends _i1.Fake implements _i2.WebView {} +class _FakeWebSettings_4 extends _i1.Fake implements _i2.WebSettings {} -class _FakeWebViewClient_5 extends _i1.Fake implements _i2.WebViewClient {} +class _FakeOffset_5 extends _i1.Fake implements _i4.Offset {} + +class _FakeWebView_6 extends _i1.Fake implements _i2.WebView {} + +class _FakeWebViewClient_7 extends _i1.Fake implements _i2.WebViewClient {} /// A class which mocks [CookieManagerHostApi]. /// @@ -46,14 +50,14 @@ class MockCookieManagerHostApi extends _i1.Mock } @override - _i4.Future clearCookies() => + _i5.Future clearCookies() => (super.noSuchMethod(Invocation.method(#clearCookies, []), - returnValue: Future.value(false)) as _i4.Future); + returnValue: Future.value(false)) as _i5.Future); @override - _i4.Future setCookie(String? arg_url, String? arg_value) => + _i5.Future setCookie(String? arg_url, String? arg_value) => (super.noSuchMethod(Invocation.method(#setCookie, [arg_url, arg_value]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); } /// A class which mocks [DownloadListener]. @@ -103,7 +107,7 @@ class MockJavaScriptChannel extends _i1.Mock implements _i2.JavaScriptChannel { /// /// See the documentation for Mockito's code generation for more information. class MockTestDownloadListenerHostApi extends _i1.Mock - implements _i5.TestDownloadListenerHostApi { + implements _i6.TestDownloadListenerHostApi { MockTestDownloadListenerHostApi() { _i1.throwOnMissingStub(this); } @@ -118,7 +122,7 @@ class MockTestDownloadListenerHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestJavaObjectHostApi extends _i1.Mock - implements _i5.TestJavaObjectHostApi { + implements _i6.TestJavaObjectHostApi { MockTestJavaObjectHostApi() { _i1.throwOnMissingStub(this); } @@ -133,7 +137,7 @@ class MockTestJavaObjectHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestJavaScriptChannelHostApi extends _i1.Mock - implements _i5.TestJavaScriptChannelHostApi { + implements _i6.TestJavaScriptChannelHostApi { MockTestJavaScriptChannelHostApi() { _i1.throwOnMissingStub(this); } @@ -148,7 +152,7 @@ class MockTestJavaScriptChannelHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestWebChromeClientHostApi extends _i1.Mock - implements _i5.TestWebChromeClientHostApi { + implements _i6.TestWebChromeClientHostApi { MockTestWebChromeClientHostApi() { _i1.throwOnMissingStub(this); } @@ -164,7 +168,7 @@ class MockTestWebChromeClientHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestWebSettingsHostApi extends _i1.Mock - implements _i5.TestWebSettingsHostApi { + implements _i6.TestWebSettingsHostApi { MockTestWebSettingsHostApi() { _i1.throwOnMissingStub(this); } @@ -240,7 +244,7 @@ class MockTestWebSettingsHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestWebStorageHostApi extends _i1.Mock - implements _i5.TestWebStorageHostApi { + implements _i6.TestWebStorageHostApi { MockTestWebStorageHostApi() { _i1.throwOnMissingStub(this); } @@ -259,7 +263,7 @@ class MockTestWebStorageHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestWebViewClientHostApi extends _i1.Mock - implements _i5.TestWebViewClientHostApi { + implements _i6.TestWebViewClientHostApi { MockTestWebViewClientHostApi() { _i1.throwOnMissingStub(this); } @@ -275,7 +279,7 @@ class MockTestWebViewClientHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestWebViewHostApi extends _i1.Mock - implements _i5.TestWebViewHostApi { + implements _i6.TestWebViewHostApi { MockTestWebViewHostApi() { _i1.throwOnMissingStub(this); } @@ -308,7 +312,7 @@ class MockTestWebViewHostApi extends _i1.Mock Invocation.method(#loadUrl, [instanceId, url, headers]), returnValueForMissingStub: null); @override - void postUrl(int? instanceId, String? url, _i6.Uint8List? data) => + void postUrl(int? instanceId, String? url, _i7.Uint8List? data) => super.noSuchMethod(Invocation.method(#postUrl, [instanceId, url, data]), returnValueForMissingStub: null); @override @@ -340,12 +344,12 @@ class MockTestWebViewHostApi extends _i1.Mock Invocation.method(#clearCache, [instanceId, includeDiskFiles]), returnValueForMissingStub: null); @override - _i4.Future evaluateJavascript( + _i5.Future evaluateJavascript( int? instanceId, String? javascriptString) => (super.noSuchMethod( Invocation.method( #evaluateJavascript, [instanceId, javascriptString]), - returnValue: Future.value()) as _i4.Future); + returnValue: Future.value()) as _i5.Future); @override String? getTitle(int? instanceId) => (super.noSuchMethod(Invocation.method(#getTitle, [instanceId])) @@ -367,6 +371,10 @@ class MockTestWebViewHostApi extends _i1.Mock (super.noSuchMethod(Invocation.method(#getScrollY, [instanceId]), returnValue: 0) as int); @override + _i3.WebViewPoint getScrollPosition(int? instanceId) => + (super.noSuchMethod(Invocation.method(#getScrollPosition, [instanceId]), + returnValue: _FakeWebViewPoint_2()) as _i3.WebViewPoint); + @override void setWebContentsDebuggingEnabled(bool? enabled) => super.noSuchMethod( Invocation.method(#setWebContentsDebuggingEnabled, [enabled]), returnValueForMissingStub: null); @@ -412,7 +420,7 @@ class MockTestWebViewHostApi extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockTestAssetManagerHostApi extends _i1.Mock - implements _i5.TestAssetManagerHostApi { + implements _i6.TestAssetManagerHostApi { MockTestAssetManagerHostApi() { _i1.throwOnMissingStub(this); } @@ -442,7 +450,7 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { @override _i2.WebChromeClient copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebChromeClient_2()) as _i2.WebChromeClient); + returnValue: _FakeWebChromeClient_3()) as _i2.WebChromeClient); } /// A class which mocks [WebView]. @@ -460,17 +468,17 @@ class MockWebView extends _i1.Mock implements _i2.WebView { @override _i2.WebSettings get settings => (super.noSuchMethod(Invocation.getter(#settings), - returnValue: _FakeWebSettings_3()) as _i2.WebSettings); + returnValue: _FakeWebSettings_4()) as _i2.WebSettings); @override - _i4.Future loadData( + _i5.Future loadData( {String? data, String? mimeType, String? encoding}) => (super.noSuchMethod( Invocation.method(#loadData, [], {#data: data, #mimeType: mimeType, #encoding: encoding}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future loadDataWithBaseUrl( + _i5.Future loadDataWithBaseUrl( {String? baseUrl, String? data, String? mimeType, @@ -485,117 +493,122 @@ class MockWebView extends _i1.Mock implements _i2.WebView { #historyUrl: historyUrl }), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future loadUrl(String? url, Map? headers) => + _i5.Future loadUrl(String? url, Map? headers) => (super.noSuchMethod(Invocation.method(#loadUrl, [url, headers]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future postUrl(String? url, _i6.Uint8List? data) => + _i5.Future postUrl(String? url, _i7.Uint8List? data) => (super.noSuchMethod(Invocation.method(#postUrl, [url, data]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future getUrl() => + _i5.Future getUrl() => (super.noSuchMethod(Invocation.method(#getUrl, []), - returnValue: Future.value()) as _i4.Future); + returnValue: Future.value()) as _i5.Future); @override - _i4.Future canGoBack() => + _i5.Future canGoBack() => (super.noSuchMethod(Invocation.method(#canGoBack, []), - returnValue: Future.value(false)) as _i4.Future); + returnValue: Future.value(false)) as _i5.Future); @override - _i4.Future canGoForward() => + _i5.Future canGoForward() => (super.noSuchMethod(Invocation.method(#canGoForward, []), - returnValue: Future.value(false)) as _i4.Future); + returnValue: Future.value(false)) as _i5.Future); @override - _i4.Future goBack() => + _i5.Future goBack() => (super.noSuchMethod(Invocation.method(#goBack, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future goForward() => + _i5.Future goForward() => (super.noSuchMethod(Invocation.method(#goForward, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future reload() => + _i5.Future reload() => (super.noSuchMethod(Invocation.method(#reload, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future clearCache(bool? includeDiskFiles) => + _i5.Future clearCache(bool? includeDiskFiles) => (super.noSuchMethod(Invocation.method(#clearCache, [includeDiskFiles]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future evaluateJavascript(String? javascriptString) => (super + _i5.Future evaluateJavascript(String? javascriptString) => (super .noSuchMethod(Invocation.method(#evaluateJavascript, [javascriptString]), - returnValue: Future.value()) as _i4.Future); + returnValue: Future.value()) as _i5.Future); @override - _i4.Future getTitle() => + _i5.Future getTitle() => (super.noSuchMethod(Invocation.method(#getTitle, []), - returnValue: Future.value()) as _i4.Future); + returnValue: Future.value()) as _i5.Future); @override - _i4.Future scrollTo(int? x, int? y) => + _i5.Future scrollTo(int? x, int? y) => (super.noSuchMethod(Invocation.method(#scrollTo, [x, y]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future scrollBy(int? x, int? y) => + _i5.Future scrollBy(int? x, int? y) => (super.noSuchMethod(Invocation.method(#scrollBy, [x, y]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future getScrollX() => + _i5.Future getScrollX() => (super.noSuchMethod(Invocation.method(#getScrollX, []), - returnValue: Future.value(0)) as _i4.Future); + returnValue: Future.value(0)) as _i5.Future); @override - _i4.Future getScrollY() => + _i5.Future getScrollY() => (super.noSuchMethod(Invocation.method(#getScrollY, []), - returnValue: Future.value(0)) as _i4.Future); + returnValue: Future.value(0)) as _i5.Future); + @override + _i5.Future<_i4.Offset> getScrollPosition() => + (super.noSuchMethod(Invocation.method(#getScrollPosition, []), + returnValue: Future<_i4.Offset>.value(_FakeOffset_5())) + as _i5.Future<_i4.Offset>); @override - _i4.Future setWebViewClient(_i2.WebViewClient? webViewClient) => + _i5.Future setWebViewClient(_i2.WebViewClient? webViewClient) => (super.noSuchMethod(Invocation.method(#setWebViewClient, [webViewClient]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future addJavaScriptChannel( + _i5.Future addJavaScriptChannel( _i2.JavaScriptChannel? javaScriptChannel) => (super.noSuchMethod( Invocation.method(#addJavaScriptChannel, [javaScriptChannel]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future removeJavaScriptChannel( + _i5.Future removeJavaScriptChannel( _i2.JavaScriptChannel? javaScriptChannel) => (super.noSuchMethod( Invocation.method(#removeJavaScriptChannel, [javaScriptChannel]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setDownloadListener(_i2.DownloadListener? listener) => + _i5.Future setDownloadListener(_i2.DownloadListener? listener) => (super.noSuchMethod(Invocation.method(#setDownloadListener, [listener]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setWebChromeClient(_i2.WebChromeClient? client) => + _i5.Future setWebChromeClient(_i2.WebChromeClient? client) => (super.noSuchMethod(Invocation.method(#setWebChromeClient, [client]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setBackgroundColor(_i7.Color? color) => + _i5.Future setBackgroundColor(_i4.Color? color) => (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future release() => + _i5.Future release() => (super.noSuchMethod(Invocation.method(#release, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override _i2.WebView copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebView_4()) as _i2.WebView); + returnValue: _FakeWebView_6()) as _i2.WebView); } /// A class which mocks [WebViewClient]. @@ -641,5 +654,5 @@ class MockWebViewClient extends _i1.Mock implements _i2.WebViewClient { returnValueForMissingStub: null); @override _i2.WebViewClient copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebViewClient_5()) as _i2.WebViewClient); + returnValue: _FakeWebViewClient_7()) as _i2.WebViewClient); } diff --git a/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart index 88429fdb337e..afc80ab53b41 100644 --- a/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart @@ -1,14 +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. -// Autogenerated from Pigeon (v3.2.0), do not edit directly. +// Autogenerated from Pigeon (v4.0.2), 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 +// 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 -// @dart = 2.12 import 'dart:async'; -import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; -import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -48,6 +47,26 @@ abstract class TestJavaObjectHostApi { class _TestWebViewHostApiCodec extends StandardMessageCodec { const _TestWebViewHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is WebViewPoint) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return WebViewPoint.decode(readValue(buffer)!); + + default: + return super.readValueOfType(type, buffer); + } + } } abstract class TestWebViewHostApi { @@ -74,6 +93,7 @@ abstract class TestWebViewHostApi { void scrollBy(int instanceId, int x, int y); int getScrollX(int instanceId); int getScrollY(int instanceId); + WebViewPoint getScrollPosition(int instanceId); void setWebContentsDebuggingEnabled(bool enabled); void setWebViewClient(int instanceId, int webViewClientInstanceId); void addJavaScriptChannel(int instanceId, int javaScriptChannelInstanceId); @@ -492,6 +512,25 @@ abstract class TestWebViewHostApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WebViewHostApi.getScrollPosition', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollPosition 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.WebViewHostApi.getScrollPosition was null, expected non-null int.'); + final WebViewPoint output = api.getScrollPosition(arg_instanceId!); + return {'result': output}; + }); + } + } { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WebViewHostApi.setWebContentsDebuggingEnabled', diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart index e17b31b3d857..acaff1c0af8b 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart @@ -2,15 +2,15 @@ // in webview_flutter_android/test/webview_android_widget_test.dart. // Do not manually edit this file. -import 'dart:async' as _i4; -import 'dart:typed_data' as _i5; -import 'dart:ui' as _i6; +import 'dart:async' as _i5; +import 'dart:typed_data' as _i6; +import 'dart:ui' as _i3; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_android/src/android_webview.dart' as _i2; import 'package:webview_flutter_android/webview_android_widget.dart' as _i7; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart' - as _i3; + as _i4; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -26,20 +26,22 @@ class _FakeWebSettings_0 extends _i1.Fake implements _i2.WebSettings {} class _FakeWebStorage_1 extends _i1.Fake implements _i2.WebStorage {} -class _FakeWebView_2 extends _i1.Fake implements _i2.WebView {} +class _FakeOffset_2 extends _i1.Fake implements _i3.Offset {} -class _FakeDownloadListener_3 extends _i1.Fake +class _FakeWebView_3 extends _i1.Fake implements _i2.WebView {} + +class _FakeDownloadListener_4 extends _i1.Fake implements _i2.DownloadListener {} -class _FakeJavascriptChannelRegistry_4 extends _i1.Fake - implements _i3.JavascriptChannelRegistry {} +class _FakeJavascriptChannelRegistry_5 extends _i1.Fake + implements _i4.JavascriptChannelRegistry {} -class _FakeJavaScriptChannel_5 extends _i1.Fake +class _FakeJavaScriptChannel_6 extends _i1.Fake implements _i2.JavaScriptChannel {} -class _FakeWebChromeClient_6 extends _i1.Fake implements _i2.WebChromeClient {} +class _FakeWebChromeClient_7 extends _i1.Fake implements _i2.WebChromeClient {} -class _FakeWebViewClient_7 extends _i1.Fake implements _i2.WebViewClient {} +class _FakeWebViewClient_8 extends _i1.Fake implements _i2.WebViewClient {} /// A class which mocks [FlutterAssetManager]. /// @@ -51,14 +53,14 @@ class MockFlutterAssetManager extends _i1.Mock } @override - _i4.Future> list(String? path) => + _i5.Future> list(String? path) => (super.noSuchMethod(Invocation.method(#list, [path]), returnValue: Future>.value([])) - as _i4.Future>); + as _i5.Future>); @override - _i4.Future getAssetFilePathByName(String? name) => + _i5.Future getAssetFilePathByName(String? name) => (super.noSuchMethod(Invocation.method(#getAssetFilePathByName, [name]), - returnValue: Future.value('')) as _i4.Future); + returnValue: Future.value('')) as _i5.Future); } /// A class which mocks [WebSettings]. @@ -70,67 +72,67 @@ class MockWebSettings extends _i1.Mock implements _i2.WebSettings { } @override - _i4.Future setDomStorageEnabled(bool? flag) => + _i5.Future setDomStorageEnabled(bool? flag) => (super.noSuchMethod(Invocation.method(#setDomStorageEnabled, [flag]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setJavaScriptCanOpenWindowsAutomatically(bool? flag) => + _i5.Future setJavaScriptCanOpenWindowsAutomatically(bool? flag) => (super.noSuchMethod( Invocation.method(#setJavaScriptCanOpenWindowsAutomatically, [flag]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setSupportMultipleWindows(bool? support) => (super + _i5.Future setSupportMultipleWindows(bool? support) => (super .noSuchMethod(Invocation.method(#setSupportMultipleWindows, [support]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setJavaScriptEnabled(bool? flag) => + _i5.Future setJavaScriptEnabled(bool? flag) => (super.noSuchMethod(Invocation.method(#setJavaScriptEnabled, [flag]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setUserAgentString(String? userAgentString) => (super + _i5.Future setUserAgentString(String? userAgentString) => (super .noSuchMethod(Invocation.method(#setUserAgentString, [userAgentString]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setMediaPlaybackRequiresUserGesture(bool? require) => + _i5.Future setMediaPlaybackRequiresUserGesture(bool? require) => (super.noSuchMethod( Invocation.method(#setMediaPlaybackRequiresUserGesture, [require]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setSupportZoom(bool? support) => + _i5.Future setSupportZoom(bool? support) => (super.noSuchMethod(Invocation.method(#setSupportZoom, [support]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setLoadWithOverviewMode(bool? overview) => (super + _i5.Future setLoadWithOverviewMode(bool? overview) => (super .noSuchMethod(Invocation.method(#setLoadWithOverviewMode, [overview]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setUseWideViewPort(bool? use) => + _i5.Future setUseWideViewPort(bool? use) => (super.noSuchMethod(Invocation.method(#setUseWideViewPort, [use]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setDisplayZoomControls(bool? enabled) => + _i5.Future setDisplayZoomControls(bool? enabled) => (super.noSuchMethod(Invocation.method(#setDisplayZoomControls, [enabled]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setBuiltInZoomControls(bool? enabled) => + _i5.Future setBuiltInZoomControls(bool? enabled) => (super.noSuchMethod(Invocation.method(#setBuiltInZoomControls, [enabled]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setAllowFileAccess(bool? enabled) => + _i5.Future setAllowFileAccess(bool? enabled) => (super.noSuchMethod(Invocation.method(#setAllowFileAccess, [enabled]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override _i2.WebSettings copy() => (super.noSuchMethod(Invocation.method(#copy, []), returnValue: _FakeWebSettings_0()) as _i2.WebSettings); @@ -145,10 +147,10 @@ class MockWebStorage extends _i1.Mock implements _i2.WebStorage { } @override - _i4.Future deleteAllData() => + _i5.Future deleteAllData() => (super.noSuchMethod(Invocation.method(#deleteAllData, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override _i2.WebStorage copy() => (super.noSuchMethod(Invocation.method(#copy, []), returnValue: _FakeWebStorage_1()) as _i2.WebStorage); @@ -171,15 +173,15 @@ class MockWebView extends _i1.Mock implements _i2.WebView { (super.noSuchMethod(Invocation.getter(#settings), returnValue: _FakeWebSettings_0()) as _i2.WebSettings); @override - _i4.Future loadData( + _i5.Future loadData( {String? data, String? mimeType, String? encoding}) => (super.noSuchMethod( Invocation.method(#loadData, [], {#data: data, #mimeType: mimeType, #encoding: encoding}), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future loadDataWithBaseUrl( + _i5.Future loadDataWithBaseUrl( {String? baseUrl, String? data, String? mimeType, @@ -194,117 +196,122 @@ class MockWebView extends _i1.Mock implements _i2.WebView { #historyUrl: historyUrl }), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future loadUrl(String? url, Map? headers) => + _i5.Future loadUrl(String? url, Map? headers) => (super.noSuchMethod(Invocation.method(#loadUrl, [url, headers]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future postUrl(String? url, _i5.Uint8List? data) => + _i5.Future postUrl(String? url, _i6.Uint8List? data) => (super.noSuchMethod(Invocation.method(#postUrl, [url, data]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future getUrl() => + _i5.Future getUrl() => (super.noSuchMethod(Invocation.method(#getUrl, []), - returnValue: Future.value()) as _i4.Future); + returnValue: Future.value()) as _i5.Future); @override - _i4.Future canGoBack() => + _i5.Future canGoBack() => (super.noSuchMethod(Invocation.method(#canGoBack, []), - returnValue: Future.value(false)) as _i4.Future); + returnValue: Future.value(false)) as _i5.Future); @override - _i4.Future canGoForward() => + _i5.Future canGoForward() => (super.noSuchMethod(Invocation.method(#canGoForward, []), - returnValue: Future.value(false)) as _i4.Future); + returnValue: Future.value(false)) as _i5.Future); @override - _i4.Future goBack() => + _i5.Future goBack() => (super.noSuchMethod(Invocation.method(#goBack, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future goForward() => + _i5.Future goForward() => (super.noSuchMethod(Invocation.method(#goForward, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future reload() => + _i5.Future reload() => (super.noSuchMethod(Invocation.method(#reload, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future clearCache(bool? includeDiskFiles) => + _i5.Future clearCache(bool? includeDiskFiles) => (super.noSuchMethod(Invocation.method(#clearCache, [includeDiskFiles]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future evaluateJavascript(String? javascriptString) => (super + _i5.Future evaluateJavascript(String? javascriptString) => (super .noSuchMethod(Invocation.method(#evaluateJavascript, [javascriptString]), - returnValue: Future.value()) as _i4.Future); + returnValue: Future.value()) as _i5.Future); @override - _i4.Future getTitle() => + _i5.Future getTitle() => (super.noSuchMethod(Invocation.method(#getTitle, []), - returnValue: Future.value()) as _i4.Future); + returnValue: Future.value()) as _i5.Future); @override - _i4.Future scrollTo(int? x, int? y) => + _i5.Future scrollTo(int? x, int? y) => (super.noSuchMethod(Invocation.method(#scrollTo, [x, y]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future scrollBy(int? x, int? y) => + _i5.Future scrollBy(int? x, int? y) => (super.noSuchMethod(Invocation.method(#scrollBy, [x, y]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future getScrollX() => + _i5.Future getScrollX() => (super.noSuchMethod(Invocation.method(#getScrollX, []), - returnValue: Future.value(0)) as _i4.Future); + returnValue: Future.value(0)) as _i5.Future); @override - _i4.Future getScrollY() => + _i5.Future getScrollY() => (super.noSuchMethod(Invocation.method(#getScrollY, []), - returnValue: Future.value(0)) as _i4.Future); + returnValue: Future.value(0)) as _i5.Future); + @override + _i5.Future<_i3.Offset> getScrollPosition() => + (super.noSuchMethod(Invocation.method(#getScrollPosition, []), + returnValue: Future<_i3.Offset>.value(_FakeOffset_2())) + as _i5.Future<_i3.Offset>); @override - _i4.Future setWebViewClient(_i2.WebViewClient? webViewClient) => + _i5.Future setWebViewClient(_i2.WebViewClient? webViewClient) => (super.noSuchMethod(Invocation.method(#setWebViewClient, [webViewClient]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future addJavaScriptChannel( + _i5.Future addJavaScriptChannel( _i2.JavaScriptChannel? javaScriptChannel) => (super.noSuchMethod( Invocation.method(#addJavaScriptChannel, [javaScriptChannel]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future removeJavaScriptChannel( + _i5.Future removeJavaScriptChannel( _i2.JavaScriptChannel? javaScriptChannel) => (super.noSuchMethod( Invocation.method(#removeJavaScriptChannel, [javaScriptChannel]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setDownloadListener(_i2.DownloadListener? listener) => + _i5.Future setDownloadListener(_i2.DownloadListener? listener) => (super.noSuchMethod(Invocation.method(#setDownloadListener, [listener]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setWebChromeClient(_i2.WebChromeClient? client) => + _i5.Future setWebChromeClient(_i2.WebChromeClient? client) => (super.noSuchMethod(Invocation.method(#setWebChromeClient, [client]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future setBackgroundColor(_i6.Color? color) => + _i5.Future setBackgroundColor(_i3.Color? color) => (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override - _i4.Future release() => + _i5.Future release() => (super.noSuchMethod(Invocation.method(#release, []), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); @override _i2.WebView copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebView_2()) as _i2.WebView); + returnValue: _FakeWebView_3()) as _i2.WebView); } /// A class which mocks [WebResourceRequest]. @@ -347,10 +354,10 @@ class MockWebViewAndroidDownloadListener extends _i1.Mock } @override - _i4.Future Function(String, Map?) get loadUrl => + _i5.Future Function(String, Map?) get loadUrl => (super.noSuchMethod(Invocation.getter(#loadUrl), returnValue: (String url, Map? headers) => - Future.value()) as _i4.Future Function( + Future.value()) as _i5.Future Function( String, Map?)); @override void onDownloadStart(String? url, String? userAgent, @@ -362,7 +369,7 @@ class MockWebViewAndroidDownloadListener extends _i1.Mock @override _i2.DownloadListener copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeDownloadListener_3()) as _i2.DownloadListener); + returnValue: _FakeDownloadListener_4()) as _i2.DownloadListener); } /// A class which mocks [WebViewAndroidJavaScriptChannel]. @@ -375,10 +382,10 @@ class MockWebViewAndroidJavaScriptChannel extends _i1.Mock } @override - _i3.JavascriptChannelRegistry get javascriptChannelRegistry => + _i4.JavascriptChannelRegistry get javascriptChannelRegistry => (super.noSuchMethod(Invocation.getter(#javascriptChannelRegistry), - returnValue: _FakeJavascriptChannelRegistry_4()) - as _i3.JavascriptChannelRegistry); + returnValue: _FakeJavascriptChannelRegistry_5()) + as _i4.JavascriptChannelRegistry); @override String get channelName => (super.noSuchMethod(Invocation.getter(#channelName), returnValue: '') @@ -390,7 +397,7 @@ class MockWebViewAndroidJavaScriptChannel extends _i1.Mock @override _i2.JavaScriptChannel copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeJavaScriptChannel_5()) as _i2.JavaScriptChannel); + returnValue: _FakeJavaScriptChannel_6()) as _i2.JavaScriptChannel); } /// A class which mocks [WebViewAndroidWebChromeClient]. @@ -409,7 +416,7 @@ class MockWebViewAndroidWebChromeClient extends _i1.Mock @override _i2.WebChromeClient copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebChromeClient_6()) as _i2.WebChromeClient); + returnValue: _FakeWebChromeClient_7()) as _i2.WebChromeClient); } /// A class which mocks [WebViewAndroidWebViewClient]. @@ -430,13 +437,13 @@ class MockWebViewAndroidWebViewClient extends _i1.Mock (super.noSuchMethod(Invocation.getter(#onPageFinishedCallback), returnValue: (String url) {}) as void Function(String)); @override - void Function(_i3.WebResourceError) get onWebResourceErrorCallback => + void Function(_i4.WebResourceError) get onWebResourceErrorCallback => (super.noSuchMethod(Invocation.getter(#onWebResourceErrorCallback), - returnValue: (_i3.WebResourceError error) {}) - as void Function(_i3.WebResourceError)); + returnValue: (_i4.WebResourceError error) {}) + as void Function(_i4.WebResourceError)); @override set onWebResourceErrorCallback( - void Function(_i3.WebResourceError)? _onWebResourceErrorCallback) => + void Function(_i4.WebResourceError)? _onWebResourceErrorCallback) => super.noSuchMethod( Invocation.setter( #onWebResourceErrorCallback, _onWebResourceErrorCallback), @@ -480,30 +487,30 @@ class MockWebViewAndroidWebViewClient extends _i1.Mock returnValueForMissingStub: null); @override _i2.WebViewClient copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebViewClient_7()) as _i2.WebViewClient); + returnValue: _FakeWebViewClient_8()) as _i2.WebViewClient); } /// A class which mocks [JavascriptChannelRegistry]. /// /// See the documentation for Mockito's code generation for more information. class MockJavascriptChannelRegistry extends _i1.Mock - implements _i3.JavascriptChannelRegistry { + implements _i4.JavascriptChannelRegistry { MockJavascriptChannelRegistry() { _i1.throwOnMissingStub(this); } @override - Map get channels => + Map get channels => (super.noSuchMethod(Invocation.getter(#channels), - returnValue: {}) - as Map); + returnValue: {}) + as Map); @override void onJavascriptChannelMessage(String? channel, String? message) => super.noSuchMethod( Invocation.method(#onJavascriptChannelMessage, [channel, message]), returnValueForMissingStub: null); @override - void updateJavascriptChannelsFromSet(Set<_i3.JavascriptChannel>? channels) => + void updateJavascriptChannelsFromSet(Set<_i4.JavascriptChannel>? channels) => super.noSuchMethod( Invocation.method(#updateJavascriptChannelsFromSet, [channels]), returnValueForMissingStub: null); @@ -513,17 +520,17 @@ class MockJavascriptChannelRegistry extends _i1.Mock /// /// See the documentation for Mockito's code generation for more information. class MockWebViewPlatformCallbacksHandler extends _i1.Mock - implements _i3.WebViewPlatformCallbacksHandler { + implements _i4.WebViewPlatformCallbacksHandler { MockWebViewPlatformCallbacksHandler() { _i1.throwOnMissingStub(this); } @override - _i4.FutureOr onNavigationRequest({String? url, bool? isForMainFrame}) => + _i5.FutureOr onNavigationRequest({String? url, bool? isForMainFrame}) => (super.noSuchMethod( Invocation.method(#onNavigationRequest, [], {#url: url, #isForMainFrame: isForMainFrame}), - returnValue: Future.value(false)) as _i4.FutureOr); + returnValue: Future.value(false)) as _i5.FutureOr); @override void onPageStarted(String? url) => super.noSuchMethod(Invocation.method(#onPageStarted, [url]), @@ -537,7 +544,7 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock super.noSuchMethod(Invocation.method(#onProgress, [progress]), returnValueForMissingStub: null); @override - void onWebResourceError(_i3.WebResourceError? error) => + void onWebResourceError(_i4.WebResourceError? error) => super.noSuchMethod(Invocation.method(#onWebResourceError, [error]), returnValueForMissingStub: null); } @@ -555,11 +562,11 @@ class MockWebViewProxy extends _i1.Mock implements _i7.WebViewProxy { (super.noSuchMethod( Invocation.method(#createWebView, [], {#useHybridComposition: useHybridComposition}), - returnValue: _FakeWebView_2()) as _i2.WebView); + returnValue: _FakeWebView_3()) as _i2.WebView); @override - _i4.Future setWebContentsDebuggingEnabled(bool? enabled) => + _i5.Future setWebContentsDebuggingEnabled(bool? enabled) => (super.noSuchMethod( Invocation.method(#setWebContentsDebuggingEnabled, [enabled]), returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i4.Future); + returnValueForMissingStub: Future.value()) as _i5.Future); } From c7a578125098ba0cce06b7c550cd84989fb79cec Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Tue, 13 Sep 2022 11:00:57 -0700 Subject: [PATCH 720/844] [camera] Add CameraInfo class and clean CameraX plugin (#6345) --- .../camera/camera_android_camerax/AUTHORS | 2 + .../camera_android_camerax/CHANGELOG.md | 1 + .../android/build.gradle | 29 ++- .../camerax/CameraAndroidCameraxPlugin.java | 76 +++++-- .../camerax/CameraInfoFlutterApiImpl.java | 24 ++ .../camerax/CameraInfoHostApiImpl.java | 23 ++ .../camerax/GeneratedCameraXLibrary.java | 199 +++++++++++++++++ .../plugins/camerax/InstanceManager.java | 205 ++++++++++++++++++ .../camerax/JavaObjectHostApiImpl.java | 33 +++ .../plugins/camerax/CameraInfoTest.java | 66 ++++++ .../plugins/camerax/CameraxPluginTest.java | 7 - .../plugins/camerax/InstanceManagerTest.java | 62 ++++++ .../camerax/JavaObjectHostApiTest.java | 32 +++ .../example/android/app/build.gradle | 4 +- .../example/android/build.gradle | 2 +- .../example/lib/main.dart | 22 +- .../example/pubspec.yaml | 65 +----- .../lib/camera_android_camerax.dart | 10 +- ...camera_android_camerax_method_channel.dart | 23 -- ...ra_android_camerax_platform_interface.dart | 36 --- .../lib/src/android_camera_camerax.dart | 21 ++ ...roid_camera_camerax_flutter_api_impls.dart | 44 ++++ .../lib/src/camera_info.dart | 91 ++++++++ .../lib/src/camerax_library.pigeon.dart | 160 ++++++++++++++ .../lib/src/instance_manager.dart | 199 +++++++++++++++++ .../lib/src/java_object.dart | 76 +++++++ .../pigeons/camerax_library.dart | 47 ++++ .../camera_android_camerax/pubspec.yaml | 59 +---- ...a_android_camerax_method_channel_test.dart | 29 --- .../test/camera_android_camerax_test.dart | 35 --- .../test/camera_info_test.dart | 60 +++++ .../test/camera_info_test.mocks.dart | 34 +++ .../test/instance_manager_test.dart | 174 +++++++++++++++ .../test/test_camerax_library.pigeon.dart | 79 +++++++ 34 files changed, 1744 insertions(+), 285 deletions(-) create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoHostApiImpl.java create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/InstanceManager.java create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/JavaObjectHostApiImpl.java create mode 100644 packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraInfoTest.java delete mode 100644 packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraxPluginTest.java create mode 100644 packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/InstanceManagerTest.java create mode 100644 packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/JavaObjectHostApiTest.java delete mode 100644 packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart delete mode 100644 packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart create mode 100644 packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart create mode 100644 packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart create mode 100644 packages/camera/camera_android_camerax/lib/src/camera_info.dart create mode 100644 packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart create mode 100644 packages/camera/camera_android_camerax/lib/src/instance_manager.dart create mode 100644 packages/camera/camera_android_camerax/lib/src/java_object.dart create mode 100644 packages/camera/camera_android_camerax/pigeons/camerax_library.dart delete mode 100644 packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart delete mode 100644 packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart create mode 100644 packages/camera/camera_android_camerax/test/camera_info_test.dart create mode 100644 packages/camera/camera_android_camerax/test/camera_info_test.mocks.dart create mode 100644 packages/camera/camera_android_camerax/test/instance_manager_test.dart create mode 100644 packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart diff --git a/packages/camera/camera_android_camerax/AUTHORS b/packages/camera/camera_android_camerax/AUTHORS index 513c0b0e2f86..557dff97933b 100644 --- a/packages/camera/camera_android_camerax/AUTHORS +++ b/packages/camera/camera_android_camerax/AUTHORS @@ -2,3 +2,5 @@ # to the Flutter project. Names should be added to the list like so: # # Name/Organization + +Google Inc. diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 044fecbb5272..0f305eb8b4fd 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -1,3 +1,4 @@ ## NEXT * Creates camera_android_camerax plugin for development. +* Adds CameraInfo class and removes unnecessary code from plugin. diff --git a/packages/camera/camera_android_camerax/android/build.gradle b/packages/camera/camera_android_camerax/android/build.gradle index 4cb85eed3ba4..5e9a9a8ec106 100644 --- a/packages/camera/camera_android_camerax/android/build.gradle +++ b/packages/camera/camera_android_camerax/android/build.gradle @@ -22,7 +22,8 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 31 + // CameraX dependencies require compilation against version 33 or later. + compileSdkVersion 33 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -30,6 +31,30 @@ android { } defaultConfig { - minSdkVersion 16 + // Many of the CameraX APIs require API 21. + minSdkVersion 21 + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + +dependencies { + // CameraX core library using the camera2 implementation must use same version number. + def camerax_version = "1.2.0-beta01" + implementation "androidx.camera:camera-core:${camerax_version}" + implementation "androidx.camera:camera-camera2:${camerax_version}" + implementation "androidx.camera:camera-lifecycle:${camerax_version}" + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.mockito:mockito-inline:4.7.0' + testImplementation 'androidx.test:core:1.4.0' +} + testOptions { + unitTests.includeAndroidResources = true + unitTests.returnDefaultValues = true + unitTests.all { + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + outputs.upToDateWhen {false} + showStandardStreams = true + } + } } } 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 0e60b13c2cfa..029a6dc4c8b7 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 @@ -4,39 +4,69 @@ package io.flutter.plugins.camerax; +import android.content.Context; import androidx.annotation.NonNull; 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; - -/** CameraAndroidCameraxPlugin */ -public class CameraAndroidCameraxPlugin implements 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 MethodChannel channel; +import io.flutter.embedding.engine.plugins.activity.ActivityAware; +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; +import io.flutter.plugin.common.BinaryMessenger; + +/** Platform implementation of the camera_plugin implemented with the CameraX library. */ +public final class CameraAndroidCameraxPlugin implements FlutterPlugin, ActivityAware { + private InstanceManager instanceManager; + private FlutterPluginBinding pluginBinding; + + /** + * Initialize this within the {@code #configureFlutterEngine} of a Flutter activity or fragment. + * + *

See {@code io.flutter.plugins.camera.MainActivity} for an example. + */ + public CameraAndroidCameraxPlugin() {} + + void setUp(BinaryMessenger binaryMessenger, Context context) { + // Set up instance manager. + instanceManager = + InstanceManager.open( + identifier -> { + new GeneratedCameraXLibrary.JavaObjectFlutterApi(binaryMessenger) + .dispose(identifier, reply -> {}); + }); + + // Set up Host APIs. + GeneratedCameraXLibrary.CameraInfoHostApi.setup( + binaryMessenger, new CameraInfoHostApiImpl(instanceManager)); + GeneratedCameraXLibrary.JavaObjectHostApi.setup( + binaryMessenger, new JavaObjectHostApiImpl(instanceManager)); + } @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { - channel = - new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "camera_android_camerax"); - channel.setMethodCallHandler(this); + pluginBinding = flutterPluginBinding; + (new CameraAndroidCameraxPlugin()) + .setUp( + flutterPluginBinding.getBinaryMessenger(), + flutterPluginBinding.getApplicationContext()); } @Override - public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { - if (call.method.equals("getPlatformVersion")) { - result.success("Android " + android.os.Build.VERSION.RELEASE); - } else { - result.notImplemented(); + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + if (instanceManager != null) { + instanceManager.close(); } } + // Activity Lifecycle methods: + @Override - public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - channel.setMethodCallHandler(null); - } + public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) {} + + @Override + public void onDetachedFromActivityForConfigChanges() {} + + @Override + public void onReattachedToActivityForConfigChanges( + @NonNull ActivityPluginBinding activityPluginBinding) {} + + @Override + public void onDetachedFromActivity() {} } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java new file mode 100644 index 000000000000..b5ba9fc1ff3b --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java @@ -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. + +package io.flutter.plugins.camerax; + +import androidx.camera.core.CameraInfo; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraInfoFlutterApi; + +public class CameraInfoFlutterApiImpl extends CameraInfoFlutterApi { + private final InstanceManager instanceManager; + + public CameraInfoFlutterApiImpl( + BinaryMessenger binaryMessenger, InstanceManager instanceManager) { + super(binaryMessenger); + this.instanceManager = instanceManager; + } + + void create(CameraInfo cameraInfo, Reply reply) { + instanceManager.addHostCreatedInstance(cameraInfo); + create(instanceManager.getIdentifierForStrongReference(cameraInfo), reply); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoHostApiImpl.java new file mode 100644 index 000000000000..7daba0d38d6a --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoHostApiImpl.java @@ -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. + +package io.flutter.plugins.camerax; + +import androidx.annotation.NonNull; +import androidx.camera.core.CameraInfo; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraInfoHostApi; + +public class CameraInfoHostApiImpl implements CameraInfoHostApi { + private final InstanceManager instanceManager; + + public CameraInfoHostApiImpl(InstanceManager instanceManager) { + this.instanceManager = instanceManager; + } + + @Override + public Long getSensorRotationDegrees(@NonNull Long identifier) { + CameraInfo cameraInfo = (CameraInfo) instanceManager.getInstance(identifier); + return Long.valueOf(cameraInfo.getSensorRotationDegrees()); + } +} 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 new file mode 100644 index 000000000000..ec3b81a8e9d8 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java @@ -0,0 +1,199 @@ +// 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 (v3.2.9), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +package io.flutter.plugins.camerax; + +import android.util.Log; +import androidx.annotation.NonNull; +import io.flutter.plugin.common.BasicMessageChannel; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.MessageCodec; +import io.flutter.plugin.common.StandardMessageCodec; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** Generated class from Pigeon. */ +@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"}) +public class GeneratedCameraXLibrary { + private static class JavaObjectHostApiCodec extends StandardMessageCodec { + public static final JavaObjectHostApiCodec INSTANCE = new JavaObjectHostApiCodec(); + + private JavaObjectHostApiCodec() {} + } + + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface JavaObjectHostApi { + void dispose(@NonNull Long identifier); + + /** The codec used by JavaObjectHostApi. */ + static MessageCodec getCodec() { + return JavaObjectHostApiCodec.INSTANCE; + } + + /** + * Sets up an instance of `JavaObjectHostApi` to handle messages through the `binaryMessenger`. + */ + static void setup(BinaryMessenger binaryMessenger, JavaObjectHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.JavaObjectHostApi.dispose", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + api.dispose((identifierArg == null) ? null : identifierArg.longValue()); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } + + private static class JavaObjectFlutterApiCodec extends StandardMessageCodec { + public static final JavaObjectFlutterApiCodec INSTANCE = new JavaObjectFlutterApiCodec(); + + private JavaObjectFlutterApiCodec() {} + } + + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ + public static class JavaObjectFlutterApi { + private final BinaryMessenger binaryMessenger; + + public JavaObjectFlutterApi(BinaryMessenger argBinaryMessenger) { + this.binaryMessenger = argBinaryMessenger; + } + + public interface Reply { + void reply(T reply); + } + + static MessageCodec getCodec() { + return JavaObjectFlutterApiCodec.INSTANCE; + } + + public void dispose(@NonNull Long identifierArg, Reply callback) { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.JavaObjectFlutterApi.dispose", getCodec()); + channel.send( + new ArrayList(Arrays.asList(identifierArg)), + channelReply -> { + callback.reply(null); + }); + } + } + + private static class CameraInfoHostApiCodec extends StandardMessageCodec { + public static final CameraInfoHostApiCodec INSTANCE = new CameraInfoHostApiCodec(); + + private CameraInfoHostApiCodec() {} + } + + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface CameraInfoHostApi { + @NonNull + Long getSensorRotationDegrees(@NonNull Long identifier); + + /** The codec used by CameraInfoHostApi. */ + static MessageCodec getCodec() { + return CameraInfoHostApiCodec.INSTANCE; + } + + /** + * Sets up an instance of `CameraInfoHostApi` to handle messages through the `binaryMessenger`. + */ + static void setup(BinaryMessenger binaryMessenger, CameraInfoHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.CameraInfoHostApi.getSensorRotationDegrees", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + Long output = + api.getSensorRotationDegrees( + (identifierArg == null) ? null : identifierArg.longValue()); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } + + private static class CameraInfoFlutterApiCodec extends StandardMessageCodec { + public static final CameraInfoFlutterApiCodec INSTANCE = new CameraInfoFlutterApiCodec(); + + private CameraInfoFlutterApiCodec() {} + } + + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ + public static class CameraInfoFlutterApi { + private final BinaryMessenger binaryMessenger; + + public CameraInfoFlutterApi(BinaryMessenger argBinaryMessenger) { + this.binaryMessenger = argBinaryMessenger; + } + + public interface Reply { + void reply(T reply); + } + + static MessageCodec getCodec() { + return CameraInfoFlutterApiCodec.INSTANCE; + } + + public void create(@NonNull Long identifierArg, Reply callback) { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.CameraInfoFlutterApi.create", getCodec()); + channel.send( + new ArrayList(Arrays.asList(identifierArg)), + channelReply -> { + callback.reply(null); + }); + } + } + + private static Map wrapError(Throwable exception) { + Map errorMap = new HashMap<>(); + errorMap.put("message", exception.toString()); + errorMap.put("code", exception.getClass().getSimpleName()); + errorMap.put( + "details", + "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); + return errorMap; + } +} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/InstanceManager.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/InstanceManager.java new file mode 100644 index 000000000000..8981227073b5 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/InstanceManager.java @@ -0,0 +1,205 @@ +// 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.os.Handler; +import android.os.Looper; +import androidx.annotation.Nullable; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.WeakHashMap; + +/** + * 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 `{@link #remove(long)}` and the weak reference is deallocated, the + * `finalizationListener` 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 {@link #getIdentifierForStrongReference(Object)}), the strong reference to the + * instance is recreated. The strong reference will then need to be removed manually again. + */ +@SuppressWarnings("unchecked") +public class InstanceManager { + // Identifiers are locked to a specific range to avoid collisions with objects + // created simultaneously from Dart. + // Host uses identifiers >= 2^16 and Dart is expected to use values n where, + // 0 <= n < 2^16. + private static final long MIN_HOST_CREATED_IDENTIFIER = 65536; + private static final long CLEAR_FINALIZED_WEAK_REFERENCES_INTERVAL = 30000; + + /** Interface for listening when a weak reference of an instance is removed from the manager. */ + public interface FinalizationListener { + void onFinalize(long identifier); + } + + private final WeakHashMap identifiers = new WeakHashMap<>(); + private final HashMap> weakInstances = new HashMap<>(); + private final HashMap strongInstances = new HashMap<>(); + + private final ReferenceQueue referenceQueue = new ReferenceQueue<>(); + private final HashMap, Long> weakReferencesToIdentifiers = new HashMap<>(); + + private final Handler handler = new Handler(Looper.getMainLooper()); + + private final FinalizationListener finalizationListener; + + private long nextIdentifier = MIN_HOST_CREATED_IDENTIFIER; + private boolean isClosed = false; + + /** + * Instantiate a new manager. + * + *

When the manager is no longer needed, {@link #close()} must be called. + * + * @param finalizationListener the listener for garbage collected weak references. + * @return a new `InstanceManager`. + */ + public static InstanceManager open(FinalizationListener finalizationListener) { + return new InstanceManager(finalizationListener); + } + + private InstanceManager(FinalizationListener finalizationListener) { + this.finalizationListener = finalizationListener; + handler.postDelayed( + this::releaseAllFinalizedInstances, CLEAR_FINALIZED_WEAK_REFERENCES_INTERVAL); + } + + /** + * Removes `identifier` and its associated strongly referenced instance, if present, from the + * manager. + * + * @param identifier the identifier paired to an instance. + * @param the expected return type. + * @return the removed instance if the manager contains the given identifier, otherwise null. + */ + @Nullable + public T remove(long identifier) { + assertManagerIsNotClosed(); + return (T) strongInstances.remove(identifier); + } + + /** + * 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 {@link #remove(long)}. + * + * @param instance an instance that may be stored in the manager. + * @return the identifier associated with `instance` if the manager contains the value, otherwise + * null. + */ + @Nullable + public Long getIdentifierForStrongReference(Object instance) { + assertManagerIsNotClosed(); + final Long identifier = identifiers.get(instance); + if (identifier != null) { + strongInstances.put(identifier, instance); + } + return identifier; + } + + /** + * 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 identifier the identifier to be paired with instance. This value must be >= 0. + */ + public void addDartCreatedInstance(Object instance, long identifier) { + assertManagerIsNotClosed(); + addInstance(instance, identifier); + } + + /** + * 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. + */ + public long addHostCreatedInstance(Object instance) { + assertManagerIsNotClosed(); + final long identifier = nextIdentifier++; + addInstance(instance, identifier); + return identifier; + } + + /** + * Retrieves the instance associated with identifier. + * + * @param identifier the identifier paired to an instance. + * @param the expected return type. + * @return the instance associated with `identifier` if the manager contains the value, otherwise + * null. + */ + @Nullable + public T getInstance(long identifier) { + assertManagerIsNotClosed(); + final WeakReference instance = (WeakReference) weakInstances.get(identifier); + if (instance != null) { + return instance.get(); + } + return (T) strongInstances.get(identifier); + } + + /** + * Returns whether this manager contains the given `instance`. + * + * @param instance the instance whose presence in this manager is to be tested. + * @return whether this manager contains the given `instance`. + */ + public boolean containsInstance(Object instance) { + assertManagerIsNotClosed(); + return identifiers.containsKey(instance); + } + + /** + * Closes the manager and releases resources. + * + *

Calling a method after calling this one will throw an {@link AssertionError}. This method + * excluded. + */ + public void close() { + handler.removeCallbacks(this::releaseAllFinalizedInstances); + isClosed = true; + } + + private void releaseAllFinalizedInstances() { + WeakReference reference; + while ((reference = (WeakReference) referenceQueue.poll()) != null) { + final Long identifier = weakReferencesToIdentifiers.remove(reference); + if (identifier != null) { + weakInstances.remove(identifier); + strongInstances.remove(identifier); + finalizationListener.onFinalize(identifier); + } + } + handler.postDelayed( + this::releaseAllFinalizedInstances, CLEAR_FINALIZED_WEAK_REFERENCES_INTERVAL); + } + + private void addInstance(Object instance, long identifier) { + if (identifier < 0) { + throw new IllegalArgumentException("Identifier must be >= 0."); + } + final WeakReference weakReference = new WeakReference<>(instance, referenceQueue); + identifiers.put(instance, identifier); + weakInstances.put(identifier, weakReference); + weakReferencesToIdentifiers.put(weakReference, identifier); + strongInstances.put(identifier, instance); + } + + private void assertManagerIsNotClosed() { + if (isClosed) { + throw new AssertionError("Manager has already been closed."); + } + } +} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/JavaObjectHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/JavaObjectHostApiImpl.java new file mode 100644 index 000000000000..5dc0ba7fc8ba --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/JavaObjectHostApiImpl.java @@ -0,0 +1,33 @@ +// 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 androidx.annotation.NonNull; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.JavaObjectHostApi; + +/** + * A pigeon Host API implementation that handles creating {@link Object}s and invoking its static + * and instance methods. + * + *

{@link Object} instances created by {@link JavaObjectHostApiImpl} are used to intercommunicate + * with a paired Dart object. + */ +public class JavaObjectHostApiImpl implements JavaObjectHostApi { + private final InstanceManager instanceManager; + + /** + * Constructs a {@link JavaObjectHostApiImpl}. + * + * @param instanceManager maintains instances stored to communicate with Dart objects + */ + public JavaObjectHostApiImpl(InstanceManager instanceManager) { + this.instanceManager = instanceManager; + } + + @Override + public void dispose(@NonNull Long identifier) { + instanceManager.remove(identifier); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraInfoTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraInfoTest.java new file mode 100644 index 000000000000..663d0e2f26d6 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraInfoTest.java @@ -0,0 +1,66 @@ +// 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 static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import androidx.camera.core.CameraInfo; +import io.flutter.plugin.common.BinaryMessenger; +import java.util.Objects; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +public class CameraInfoTest { + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock public CameraInfo mockCameraInfo; + @Mock public BinaryMessenger mockBinaryMessenger; + + InstanceManager testInstanceManager; + + @Before + public void setUp() { + testInstanceManager = InstanceManager.open(identifier -> {}); + } + + @After + public void tearDown() { + testInstanceManager.close(); + } + + @Test + public void getSensorRotationDegreesTest() { + final CameraInfoHostApiImpl cameraInfoHostApi = new CameraInfoHostApiImpl(testInstanceManager); + + testInstanceManager.addDartCreatedInstance(mockCameraInfo, 1); + + when(mockCameraInfo.getSensorRotationDegrees()).thenReturn(90); + + assertEquals((long) cameraInfoHostApi.getSensorRotationDegrees(1L), 90L); + verify(mockCameraInfo).getSensorRotationDegrees(); + } + + @Test + public void flutterApiCreateTest() { + final CameraInfoFlutterApiImpl spyFlutterApi = + spy(new CameraInfoFlutterApiImpl(mockBinaryMessenger, testInstanceManager)); + + spyFlutterApi.create(mockCameraInfo, reply -> {}); + + final long identifier = + Objects.requireNonNull(testInstanceManager.getIdentifierForStrongReference(mockCameraInfo)); + verify(spyFlutterApi).create(eq(identifier), any()); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraxPluginTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraxPluginTest.java deleted file mode 100644 index 69b0e34f6bb6..000000000000 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraxPluginTest.java +++ /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. - -package io.flutter.plugins.camerax; - -public class CameraxPluginTest {} diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/InstanceManagerTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/InstanceManagerTest.java new file mode 100644 index 000000000000..3878e05a40e8 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/InstanceManagerTest.java @@ -0,0 +1,62 @@ +// 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 static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class InstanceManagerTest { + @Test + public void addDartCreatedInstance() { + final InstanceManager instanceManager = InstanceManager.open(identifier -> {}); + + final Object object = new Object(); + instanceManager.addDartCreatedInstance(object, 0); + + assertEquals(object, instanceManager.getInstance(0)); + assertEquals((Long) 0L, instanceManager.getIdentifierForStrongReference(object)); + assertTrue(instanceManager.containsInstance(object)); + + instanceManager.close(); + } + + @Test + public void addHostCreatedInstance() { + final InstanceManager instanceManager = InstanceManager.open(identifier -> {}); + + final Object object = new Object(); + long identifier = instanceManager.addHostCreatedInstance(object); + + assertNotNull(instanceManager.getInstance(identifier)); + assertEquals(object, instanceManager.getInstance(identifier)); + assertTrue(instanceManager.containsInstance(object)); + + instanceManager.close(); + } + + @Test + public void remove() { + final InstanceManager instanceManager = InstanceManager.open(identifier -> {}); + + Object object = new Object(); + instanceManager.addDartCreatedInstance(object, 0); + + assertEquals(object, instanceManager.remove(0)); + + // To allow for object to be garbage collected. + //noinspection UnusedAssignment + object = null; + + Runtime.getRuntime().gc(); + + assertNull(instanceManager.getInstance(0)); + + instanceManager.close(); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/JavaObjectHostApiTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/JavaObjectHostApiTest.java new file mode 100644 index 000000000000..cce3341aaa89 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/JavaObjectHostApiTest.java @@ -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. + +package io.flutter.plugins.camerax; + +import static org.junit.Assert.assertNull; + +import org.junit.Test; + +public class JavaObjectHostApiTest { + @Test + public void dispose() { + final InstanceManager instanceManager = InstanceManager.open(identifier -> {}); + + final JavaObjectHostApiImpl hostApi = new JavaObjectHostApiImpl(instanceManager); + + Object object = new Object(); + instanceManager.addDartCreatedInstance(object, 0); + + // To free object for garbage collection. + //noinspection UnusedAssignment + object = null; + + hostApi.dispose(0L); + Runtime.getRuntime().gc(); + + assertNull(instanceManager.getInstance(0)); + + instanceManager.close(); + } +} diff --git a/packages/camera/camera_android_camerax/example/android/app/build.gradle b/packages/camera/camera_android_camerax/example/android/app/build.gradle index 1dbeb6653915..0c0cbcd06921 100644 --- a/packages/camera/camera_android_camerax/example/android/app/build.gradle +++ b/packages/camera/camera_android_camerax/example/android/app/build.gradle @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion flutter.compileSdkVersion + compileSdkVersion 33 ndkVersion flutter.ndkVersion compileOptions { @@ -39,7 +39,7 @@ android { applicationId "io.flutter.plugins.cameraxexample" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. - minSdkVersion flutter.minSdkVersion + minSdkVersion 21 targetSdkVersion 30 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/packages/camera/camera_android_camerax/example/android/build.gradle b/packages/camera/camera_android_camerax/example/android/build.gradle index 58a8c74b1474..20411f5f31a9 100644 --- a/packages/camera/camera_android_camerax/example/android/build.gradle +++ b/packages/camera/camera_android_camerax/example/android/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.2.0' + classpath 'com.android.tools.build:gradle:7.2.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/packages/camera/camera_android_camerax/example/lib/main.dart b/packages/camera/camera_android_camerax/example/lib/main.dart index c5391096374e..244a15281e3f 100644 --- a/packages/camera/camera_android_camerax/example/lib/main.dart +++ b/packages/camera/camera_android_camerax/example/lib/main.dart @@ -2,17 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/material.dart'; -void main() { +late List _cameras; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + _cameras = await CameraPlatform.instance.availableCameras(); + runApp(const MyApp()); } /// Example app class MyApp extends StatefulWidget { /// App instantiation - const MyApp({Key? key}) : super(key: key); - + const MyApp({super.key}); @override State createState() => _MyAppState(); } @@ -20,13 +25,18 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { @override Widget build(BuildContext context) { + String availableCameraNames = 'Available cameras:'; + for (final CameraDescription cameraDescription in _cameras) { + availableCameraNames = '$availableCameraNames ${cameraDescription.name},'; + } return MaterialApp( home: Scaffold( appBar: AppBar( - title: const Text('Plugin example app'), + title: const Text('Camera Example'), ), - body: const Center( - child: Text('Hello, world!'), + body: Center( + child: Text(availableCameraNames.substring( + 0, availableCameraNames.length - 1)), ), ), ); diff --git a/packages/camera/camera_android_camerax/example/pubspec.yaml b/packages/camera/camera_android_camerax/example/pubspec.yaml index 891e9486e846..2884e8685d8c 100644 --- a/packages/camera/camera_android_camerax/example/pubspec.yaml +++ b/packages/camera/camera_android_camerax/example/pubspec.yaml @@ -1,19 +1,11 @@ name: camera_android_camerax_example description: Demonstrates how to use the camera_android_camerax plugin. - -# The following line prevents the package from being accidentally published to -# pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: 'none' environment: - sdk: '>=2.14.0 <3.0.0' + sdk: '>=2.17.0 <3.0.0' + flutter: ">=3.0.0" -# Dependencies specify other packages that your package needs in order to work. -# To automatically upgrade your package dependencies to the latest versions -# consider running `flutter pub upgrade --major-versions`. Alternatively, -# dependencies can be manually updated by changing the version numbers below to -# the latest version available on pub.dev. To see which dependencies have newer -# versions available, run `flutter pub outdated`. dependencies: camera_android_camerax: # When depending on this package from a real application you should use: @@ -22,64 +14,15 @@ 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: ../ - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 flutter: sdk: flutter dev_dependencies: - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. - flutter_lints: ^2.0.0 - flutter_test: sdk: flutter - integration_test: sdk: flutter -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages + \ No newline at end of file diff --git a/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart b/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart index 40f8c703fa9f..4ddecd71397b 100644 --- a/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart +++ b/packages/camera/camera_android_camerax/lib/camera_android_camerax.dart @@ -2,12 +2,4 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'camera_android_camerax_platform_interface.dart'; - -/// Android Camera implented with the CameraX library. -class CameraAndroidCamerax { - /// Returns platform version of this instance. - Future getPlatformVersion() { - return CameraAndroidCameraxPlatform.instance.getPlatformVersion(); - } -} +export 'src/android_camera_camerax.dart'; diff --git a/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart b/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.dart deleted file mode 100644 index 699a3667d14d..000000000000 --- a/packages/camera/camera_android_camerax/lib/camera_android_camerax_method_channel.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/foundation.dart'; -import 'package:flutter/services.dart'; - -import 'camera_android_camerax_platform_interface.dart'; - -/// An implementation of [CameraAndroidCameraxPlatform] that uses method channels. -class MethodChannelCameraAndroidCamerax extends CameraAndroidCameraxPlatform { - /// The method channel used to interact with the native platform. - @visibleForTesting - final MethodChannel methodChannel = - const MethodChannel('camera_android_camerax'); - - @override - Future getPlatformVersion() async { - final String? version = - await methodChannel.invokeMethod('getPlatformVersion'); - return version; - } -} diff --git a/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart b/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart deleted file mode 100644 index 03554caf3245..000000000000 --- a/packages/camera/camera_android_camerax/lib/camera_android_camerax_platform_interface.dart +++ /dev/null @@ -1,36 +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:plugin_platform_interface/plugin_platform_interface.dart'; - -import 'camera_android_camerax_method_channel.dart'; - -/// The platform interface for the Android Camera implemented with CameraX. -abstract class CameraAndroidCameraxPlatform extends PlatformInterface { - /// Constructs a CameraAndroidCameraxPlatform. - CameraAndroidCameraxPlatform() : super(token: _token); - - static final Object _token = Object(); - - static CameraAndroidCameraxPlatform _instance = - MethodChannelCameraAndroidCamerax(); - - /// The default instance of [CameraAndroidCameraxPlatform] to use. - /// - /// Defaults to [MethodChannelCameraAndroidCamerax]. - static CameraAndroidCameraxPlatform get instance => _instance; - - /// Platform-specific implementations should set this with their own - /// platform-specific class that extends [CameraAndroidCameraxPlatform] when - /// they register themselves. - static set instance(CameraAndroidCameraxPlatform instance) { - PlatformInterface.verifyToken(instance, _token); - _instance = instance; - } - - /// Returns the platform version of this isntance. - Future getPlatformVersion() { - throw UnimplementedError('platformVersion() has not been implemented.'); - } -} 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 new file mode 100644 index 000000000000..f03273861793 --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart @@ -0,0 +1,21 @@ +// 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:async'; + +import 'package:camera_platform_interface/camera_platform_interface.dart'; + +/// The Android implementation of [CameraPlatform] that uses the CameraX library. +class AndroidCameraCameraX extends CameraPlatform { + /// Registers this class as the default instance of [CameraPlatform]. + static void registerWith() { + CameraPlatform.instance = AndroidCameraCameraX(); + } + + /// Returns list of all available cameras and their descriptions. + @override + Future> availableCameras() async { + throw UnimplementedError('availableCameras() is not implemented.'); + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart new file mode 100644 index 000000000000..8190a1ce1161 --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.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 'camera_info.dart'; +import 'camerax_library.pigeon.dart'; +import 'java_object.dart'; + +/// Handles initialization of Flutter APIs for the Android CameraX library. +class AndroidCameraXCameraFlutterApis { + /// Creates a [AndroidCameraXCameraFlutterApis]. + AndroidCameraXCameraFlutterApis({ + JavaObjectFlutterApiImpl? javaObjectFlutterApi, + CameraInfoFlutterApiImpl? cameraInfoFlutterApi, + }) { + this.javaObjectFlutterApi = + javaObjectFlutterApi ?? JavaObjectFlutterApiImpl(); + this.cameraInfoFlutterApi = + cameraInfoFlutterApi ?? CameraInfoFlutterApiImpl(); + } + + static bool _haveBeenSetUp = false; + + /// Mutable instance containing all Flutter Apis for Android CameraX Camera. + /// + /// This should only be changed for testing purposes. + static AndroidCameraXCameraFlutterApis instance = + AndroidCameraXCameraFlutterApis(); + + /// Handles callbacks methods for the native Java Object class. + late final JavaObjectFlutterApi javaObjectFlutterApi; + + /// Flutter Api for [CameraInfo]. + late final CameraInfoFlutterApiImpl cameraInfoFlutterApi; + + /// Ensures all the Flutter APIs have been setup to receive calls from native code. + void ensureSetUp() { + if (!_haveBeenSetUp) { + JavaObjectFlutterApi.setup(javaObjectFlutterApi); + CameraInfoFlutterApi.setup(cameraInfoFlutterApi); + _haveBeenSetUp = true; + } + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/camera_info.dart b/packages/camera/camera_android_camerax/lib/src/camera_info.dart new file mode 100644 index 000000000000..3edc7b4afad2 --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/camera_info.dart @@ -0,0 +1,91 @@ +// 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' show BinaryMessenger; + +import 'android_camera_camerax_flutter_api_impls.dart'; +import 'camerax_library.pigeon.dart'; +import 'instance_manager.dart'; +import 'java_object.dart'; + +/// Represents the metadata of a camera. +/// +/// See https://developer.android.com/reference/androidx/camera/core/CameraInfo. +class CameraInfo extends JavaObject { + /// Constructs a [CameraInfo] that is not automatically attached to a native object. + CameraInfo.detached( + {BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) + : super.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager) { + _api = CameraInfoHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); + } + + late final CameraInfoHostApiImpl _api; + + /// Gets sensor orientation degrees of camera. + Future getSensorRotationDegrees() => + _api.getSensorRotationDegreesFromInstance(this); +} + +/// Host API implementation of [CameraInfo]. +class CameraInfoHostApiImpl extends CameraInfoHostApi { + /// Constructs a [CameraInfoHostApiImpl]. + CameraInfoHostApiImpl({ + this.binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); + + /// Sends binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; + + /// Gets sensor orientation degrees of [CameraInfo]. + Future getSensorRotationDegreesFromInstance(CameraInfo instance) async { + final int sensorRotationDegrees = await getSensorRotationDegrees( + instanceManager.getIdentifier(instance)!); + return sensorRotationDegrees; + } +} + +/// Flutter API implementation of [CameraInfo]. +class CameraInfoFlutterApiImpl extends CameraInfoFlutterApi { + /// Constructs a [CameraInfoFlutterApiImpl]. + CameraInfoFlutterApiImpl({ + this.binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; + + /// Receives binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; + + @override + void create(int identifier) { + instanceManager.addHostCreatedInstance( + CameraInfo.detached( + binaryMessenger: binaryMessenger, instanceManager: instanceManager), + identifier, + onCopy: (CameraInfo original) { + return CameraInfo.detached( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + }, + ); + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart b/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart new file mode 100644 index 000000000000..8d421836a587 --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart @@ -0,0 +1,160 @@ +// 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 (v3.2.9), 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 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; + +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; + +class _JavaObjectHostApiCodec extends StandardMessageCodec { + const _JavaObjectHostApiCodec(); +} + +class JavaObjectHostApi { + /// Constructor for [JavaObjectHostApi]. 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. + JavaObjectHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _JavaObjectHostApiCodec(); + + Future dispose(int arg_identifier) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.JavaObjectHostApi.dispose', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_identifier]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } +} + +class _JavaObjectFlutterApiCodec extends StandardMessageCodec { + const _JavaObjectFlutterApiCodec(); +} + +abstract class JavaObjectFlutterApi { + static const MessageCodec codec = _JavaObjectFlutterApiCodec(); + + void dispose(int identifier); + static void setup(JavaObjectFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.JavaObjectFlutterApi.dispose', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.JavaObjectFlutterApi.dispose 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.JavaObjectFlutterApi.dispose was null, expected non-null int.'); + api.dispose(arg_identifier!); + return; + }); + } + } + } +} + +class _CameraInfoHostApiCodec extends StandardMessageCodec { + const _CameraInfoHostApiCodec(); +} + +class CameraInfoHostApi { + /// Constructor for [CameraInfoHostApi]. 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. + CameraInfoHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _CameraInfoHostApiCodec(); + + Future getSensorRotationDegrees(int arg_identifier) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraInfoHostApi.getSensorRotationDegrees', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_identifier]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as int?)!; + } + } +} + +class _CameraInfoFlutterApiCodec extends StandardMessageCodec { + const _CameraInfoFlutterApiCodec(); +} + +abstract class CameraInfoFlutterApi { + static const MessageCodec codec = _CameraInfoFlutterApiCodec(); + + void create(int identifier); + static void setup(CameraInfoFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraInfoFlutterApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.CameraInfoFlutterApi.create 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.CameraInfoFlutterApi.create was null, expected non-null int.'); + api.create(arg_identifier!); + return; + }); + } + } + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/instance_manager.dart b/packages/camera/camera_android_camerax/lib/src/instance_manager.dart new file mode 100644 index 000000000000..dd48610c8b56 --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/instance_manager.dart @@ -0,0 +1,199 @@ +// 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'; + +/// 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 InstanceManager { + /// Constructs an [InstanceManager]. + InstanceManager({required void Function(int) onWeakReferenceRemoved}) { + this.onWeakReferenceRemoved = (int identifier) { + debugPrint('Releasing weak reference with identifier: $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; + + // 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 = {}; + final Map _copyCallbacks = {}; + 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; + + /// 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( + T instance, { + required T Function(T original) onCopy, + }) { + assert(getIdentifier(instance) == null); + + final int identifier = _nextUniqueIdentifier(); + _addInstanceWithIdentifier(instance, identifier, onCopy: onCopy); + 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 the strong referenced instance associated with + /// [instance]. This can be done with [remove]. + int? removeWeakReference(Object 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 the weak referenced instance associtated with + /// [identifier]. This can be done with [removeWeakReference]. + T? remove(int identifier) { + debugPrint('Releasing strong reference with identifier: $identifier'); + _copyCallbacks.remove(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 Object? weakInstance = _weakInstances[identifier]?.target; + + if (weakInstance == null) { + final Object? strongInstance = _strongInstances[identifier]; + if (strongInstance != null) { + final Object copy = + _copyCallbacks[identifier]!(strongInstance)! as Object; + _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(Object 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( + T instance, + int identifier, { + required T Function(T original) onCopy, + }) { + assert(!containsIdentifier(identifier)); + assert(getIdentifier(instance) == null); + assert(identifier >= 0); + _addInstanceWithIdentifier(instance, identifier, onCopy: onCopy); + } + + void _addInstanceWithIdentifier( + T instance, + int identifier, { + required T Function(T original) onCopy, + }) { + _identifiers[instance] = identifier; + _weakInstances[identifier] = WeakReference(instance); + _finalizer.attach(instance, identifier, detach: instance); + + final Object copy = onCopy(instance); + _identifiers[copy] = identifier; + _strongInstances[identifier] = copy; + _copyCallbacks[identifier] = onCopy; + } + + /// 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; + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/java_object.dart b/packages/camera/camera_android_camerax/lib/src/java_object.dart new file mode 100644 index 000000000000..36a29ed0517b --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/java_object.dart @@ -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. + +import 'package:flutter/foundation.dart' show immutable; +import 'package:flutter/services.dart'; + +import 'camerax_library.pigeon.dart'; +import 'instance_manager.dart'; + +/// Root of the Java class hierarchy. +/// +/// See https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html. +@immutable +class JavaObject { + /// Constructs a [JavaObject] without creating the associated Java object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies. + JavaObject.detached({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) : _api = JavaObjectHostApiImpl( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + ); + + /// Global instance of [InstanceManager]. + static final InstanceManager globalInstanceManager = InstanceManager( + onWeakReferenceRemoved: (int identifier) { + JavaObjectHostApiImpl().dispose(identifier); + }, + ); + + /// Pigeon Host Api implementation for [JavaObject]. + final JavaObjectHostApiImpl _api; + + /// Release the reference to a native Java instance. + static void dispose(JavaObject instance) { + instance._api.instanceManager.removeWeakReference(instance); + } +} + +/// Handles methods calls to the native Java Object class. +class JavaObjectHostApiImpl extends JavaObjectHostApi { + /// Constructs a [JavaObjectHostApiImpl]. + JavaObjectHostApiImpl({ + this.binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, + super(binaryMessenger: binaryMessenger); + + /// Receives binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; +} + +/// Handles callbacks methods for the native Java Object class. +class JavaObjectFlutterApiImpl implements JavaObjectFlutterApi { + /// Constructs a [JavaObjectFlutterApiImpl]. + JavaObjectFlutterApiImpl({InstanceManager? instanceManager}) + : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; + + @override + void dispose(int identifier) { + instanceManager.remove(identifier); + } +} diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart new file mode 100644 index 000000000000..d36634a9cac3 --- /dev/null +++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.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:pigeon/pigeon.dart'; + +@ConfigurePigeon( + PigeonOptions( + dartOut: 'lib/src/camerax_library.pigeon.dart', + dartTestOut: 'test/test_camerax_library.pigeon.dart', + dartOptions: DartOptions(copyrightHeader: [ + '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.', + ]), + javaOut: + 'android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java', + javaOptions: JavaOptions( + package: 'io.flutter.plugins.camerax', + className: 'GeneratedCameraXLibrary', + copyrightHeader: [ + '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.', + ], + ), + ), +) +@HostApi(dartHostTestHandler: 'TestJavaObjectHostApi') +abstract class JavaObjectHostApi { + void dispose(int identifier); +} + +@FlutterApi() +abstract class JavaObjectFlutterApi { + void dispose(int identifier); +} + +@HostApi(dartHostTestHandler: 'TestCameraInfoHostApi') +abstract class CameraInfoHostApi { + int getSensorRotationDegrees(int identifier); +} + +@FlutterApi() +abstract class CameraInfoFlutterApi { + void create(int identifier); +} diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml index bb5b15b6baf4..9873db1a0121 100644 --- a/packages/camera/camera_android_camerax/pubspec.yaml +++ b/packages/camera/camera_android_camerax/pubspec.yaml @@ -1,73 +1,30 @@ name: camera_android_camerax -description: A new Flutter plugin project. +description: Android implementation of the camera plugin using the CameraX library. repository: https://github.com/flutter/plugins/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 -homepage: publish_to: 'none' environment: - sdk: '>=2.14.0 <3.0.0' - flutter: ">=2.5.0" + sdk: '>=2.17.0 <3.0.0' + flutter: ">=3.0.0" -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. flutter: - # This section identifies this Flutter project as a plugin project. - # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) - # which should be registered in the plugin registry. This is required for - # using method channels. - # The Android 'package' specifies package in which the registered class is. - # This is required for using method channels on Android. - # The 'ffiPlugin' specifies that native code should be built and bundled. - # This is required for using `dart:ffi`. - # All these are used by the tooling to maintain consistency when - # adding or updating assets for this project. plugin: implements: camera platforms: android: package: io.flutter.plugins.camerax pluginClass: CameraAndroidCameraxPlugin - - # To add assets to your plugin package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # To add custom fonts to your plugin package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages + dartPluginClass: AndroidCameraCameraX dependencies: + camera_platform_interface: ^2.2.0 flutter: sdk: flutter - plugin_platform_interface: ^2.0.2 dev_dependencies: - flutter_lints: ^2.0.0 + build_runner: ^2.1.4 flutter_test: sdk: flutter + mockito: ^5.1.0 + pigeon: ^3.2.6 diff --git a/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart b/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart deleted file mode 100644 index 0a1ea847e1a5..000000000000 --- a/packages/camera/camera_android_camerax/test/camera_android_camerax_method_channel_test.dart +++ /dev/null @@ -1,29 +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:camera_android_camerax/camera_android_camerax_method_channel.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - final MethodChannelCameraAndroidCamerax platform = - MethodChannelCameraAndroidCamerax(); - const MethodChannel channel = MethodChannel('camera_android_camerax'); - - TestWidgetsFlutterBinding.ensureInitialized(); - - setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - return '42'; - }); - }); - - tearDown(() { - channel.setMockMethodCallHandler(null); - }); - - test('getPlatformVersion', () async { - expect(await platform.getPlatformVersion(), '42'); - }); -} diff --git a/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart b/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart deleted file mode 100644 index 05ff46c1c55d..000000000000 --- a/packages/camera/camera_android_camerax/test/camera_android_camerax_test.dart +++ /dev/null @@ -1,35 +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:camera_android_camerax/camera_android_camerax.dart'; -import 'package:camera_android_camerax/camera_android_camerax_method_channel.dart'; -import 'package:camera_android_camerax/camera_android_camerax_platform_interface.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -class MockCameraAndroidCameraxPlatform - with MockPlatformInterfaceMixin - implements CameraAndroidCameraxPlatform { - @override - Future getPlatformVersion() => Future.value('42'); -} - -void main() { - final CameraAndroidCameraxPlatform initialPlatform = - CameraAndroidCameraxPlatform.instance; - - test('$MethodChannelCameraAndroidCamerax is the default instance', () { - expect(initialPlatform, isInstanceOf()); - }); - - test('getPlatformVersion', () async { - final CameraAndroidCamerax cameraAndroidCameraxPlugin = - CameraAndroidCamerax(); - final MockCameraAndroidCameraxPlatform fakePlatform = - MockCameraAndroidCameraxPlatform(); - CameraAndroidCameraxPlatform.instance = fakePlatform; - - expect(await cameraAndroidCameraxPlugin.getPlatformVersion(), '42'); - }); -} diff --git a/packages/camera/camera_android_camerax/test/camera_info_test.dart b/packages/camera/camera_android_camerax/test/camera_info_test.dart new file mode 100644 index 000000000000..eda822b33f73 --- /dev/null +++ b/packages/camera/camera_android_camerax/test/camera_info_test.dart @@ -0,0 +1,60 @@ +// 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:camera_android_camerax/src/camera_info.dart'; +import 'package:camera_android_camerax/src/camerax_library.pigeon.dart'; +import 'package:camera_android_camerax/src/instance_manager.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'camera_info_test.mocks.dart'; +import 'test_camerax_library.pigeon.dart'; + +@GenerateMocks([TestCameraInfoHostApi]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('CameraInfo', () { + tearDown(() => TestCameraInfoHostApi.setup(null)); + + test('getSensorRotationDegreesTest', () async { + final MockTestCameraInfoHostApi mockApi = MockTestCameraInfoHostApi(); + TestCameraInfoHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final CameraInfo cameraInfo = CameraInfo.detached( + instanceManager: instanceManager, + ); + instanceManager.addHostCreatedInstance( + cameraInfo, + 0, + onCopy: (_) => CameraInfo.detached(), + ); + + when(mockApi.getSensorRotationDegrees( + instanceManager.getIdentifier(cameraInfo))) + .thenReturn(90); + expect(await cameraInfo.getSensorRotationDegrees(), equals(90)); + + verify(mockApi.getSensorRotationDegrees(0)); + }); + + test('flutterApiCreateTest', () { + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final CameraInfoFlutterApi flutterApi = CameraInfoFlutterApiImpl( + instanceManager: instanceManager, + ); + + flutterApi.create(0); + + expect( + instanceManager.getInstanceWithWeakReference(0), isA()); + }); + }); +} diff --git a/packages/camera/camera_android_camerax/test/camera_info_test.mocks.dart b/packages/camera/camera_android_camerax/test/camera_info_test.mocks.dart new file mode 100644 index 000000000000..e1f1e3ca9e9b --- /dev/null +++ b/packages/camera/camera_android_camerax/test/camera_info_test.mocks.dart @@ -0,0 +1,34 @@ +// Mocks generated by Mockito 5.3.0 from annotations +// in camera_android_camerax/test/camera_info_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:mockito/mockito.dart' as _i1; + +import 'test_camerax_library.pigeon.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: 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 [TestCameraInfoHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestCameraInfoHostApi extends _i1.Mock + implements _i2.TestCameraInfoHostApi { + MockTestCameraInfoHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + int getSensorRotationDegrees(int? identifier) => (super.noSuchMethod( + Invocation.method(#getSensorRotationDegrees, [identifier]), + returnValue: 0) as int); +} diff --git a/packages/camera/camera_android_camerax/test/instance_manager_test.dart b/packages/camera/camera_android_camerax/test/instance_manager_test.dart new file mode 100644 index 000000000000..9562c41674ae --- /dev/null +++ b/packages/camera/camera_android_camerax/test/instance_manager_test.dart @@ -0,0 +1,174 @@ +// 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:camera_android_camerax/src/instance_manager.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('InstanceManager', () { + test('addHostCreatedInstance', () { + final Object object = Object(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance( + object, + 0, + onCopy: (_) => Object(), + ); + + expect(instanceManager.getIdentifier(object), 0); + expect( + instanceManager.getInstanceWithWeakReference(0), + object, + ); + }); + + test('addHostCreatedInstance prevents already used objects and ids', () { + final Object object = Object(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance( + object, + 0, + onCopy: (_) => Object(), + ); + + expect( + () => instanceManager.addHostCreatedInstance( + object, + 0, + onCopy: (_) => Object(), + ), + throwsAssertionError, + ); + + expect( + () => instanceManager.addHostCreatedInstance( + Object(), + 0, + onCopy: (_) => Object(), + ), + throwsAssertionError, + ); + }); + + test('addDartCreatedInstance', () { + final Object object = Object(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addDartCreatedInstance( + object, + onCopy: (_) => Object(), + ); + + final int? instanceId = instanceManager.getIdentifier(object); + expect(instanceId, isNotNull); + expect( + instanceManager.getInstanceWithWeakReference(instanceId!), + object, + ); + }); + + test('removeWeakReference', () { + final Object object = Object(); + + int? weakInstanceId; + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (int instanceId) { + weakInstanceId = instanceId; + }); + + instanceManager.addHostCreatedInstance( + object, + 0, + onCopy: (_) => Object(), + ); + + expect(instanceManager.removeWeakReference(object), 0); + expect( + instanceManager.getInstanceWithWeakReference(0), + isA(), + ); + expect(weakInstanceId, 0); + }); + + test('removeWeakReference removes only weak reference', () { + final Object object = Object(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance( + object, + 0, + onCopy: (_) => Object(), + ); + + expect(instanceManager.removeWeakReference(object), 0); + final Object copy = instanceManager.getInstanceWithWeakReference( + 0, + )!; + expect(identical(object, copy), isFalse); + }); + + test('removeStrongReference', () { + final Object object = Object(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance( + object, + 0, + onCopy: (_) => Object(), + ); + instanceManager.removeWeakReference(object); + expect(instanceManager.remove(0), isA()); + expect(instanceManager.containsIdentifier(0), isFalse); + }); + + test('removeStrongReference removes only strong reference', () { + final Object object = Object(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance( + object, + 0, + onCopy: (_) => Object(), + ); + expect(instanceManager.remove(0), isA()); + expect( + instanceManager.getInstanceWithWeakReference(0), + object, + ); + }); + + test('getInstance can add a new weak reference', () { + final Object object = Object(); + + final InstanceManager instanceManager = + InstanceManager(onWeakReferenceRemoved: (_) {}); + + instanceManager.addHostCreatedInstance( + object, + 0, + onCopy: (_) => Object(), + ); + instanceManager.removeWeakReference(object); + + final Object newWeakCopy = instanceManager.getInstanceWithWeakReference( + 0, + )!; + expect(identical(object, newWeakCopy), isFalse); + }); + }); +} diff --git a/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart b/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart new file mode 100644 index 000000000000..59e76190188e --- /dev/null +++ b/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart @@ -0,0 +1,79 @@ +// 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 (v3.2.9), 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 +import 'dart:async'; +import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; +import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:camera_android_camerax/src/camerax_library.pigeon.dart'; + +class _TestJavaObjectHostApiCodec extends StandardMessageCodec { + const _TestJavaObjectHostApiCodec(); +} + +abstract class TestJavaObjectHostApi { + static const MessageCodec codec = _TestJavaObjectHostApiCodec(); + + void dispose(int identifier); + static void setup(TestJavaObjectHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.JavaObjectHostApi.dispose', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.JavaObjectHostApi.dispose 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.JavaObjectHostApi.dispose was null, expected non-null int.'); + api.dispose(arg_identifier!); + return {}; + }); + } + } + } +} + +class _TestCameraInfoHostApiCodec extends StandardMessageCodec { + const _TestCameraInfoHostApiCodec(); +} + +abstract class TestCameraInfoHostApi { + static const MessageCodec codec = _TestCameraInfoHostApiCodec(); + + int getSensorRotationDegrees(int identifier); + static void setup(TestCameraInfoHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraInfoHostApi.getSensorRotationDegrees', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.CameraInfoHostApi.getSensorRotationDegrees 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.CameraInfoHostApi.getSensorRotationDegrees was null, expected non-null int.'); + final int output = api.getSensorRotationDegrees(arg_identifier!); + return {'result': output}; + }); + } + } + } +} From 822f6682c32722c16e4ef0a0fb6a10d76b41d0ac Mon Sep 17 00:00:00 2001 From: godofredoc Date: Tue, 13 Sep 2022 11:01:00 -0700 Subject: [PATCH 721/844] Manual update of scorecards to 2.0.3 (#6416) --- .github/workflows/scorecards-analysis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 5f51be13913c..3bcfdad7fb6b 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -17,6 +17,8 @@ jobs: security-events: write actions: read contents: read + # Needed to access OIDC token. + id-token: write steps: - name: "Checkout code" @@ -25,7 +27,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@ce330fde6b1a5c9c75b417e7efc510b822a35564 + uses: ossf/scorecard-action@865b4092859256271290c77adbd10a43f4779972 with: results_file: results.sarif results_format: sarif From 17aed719aaed423d70e29cd79a594e3369182edf Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Tue, 13 Sep 2022 11:02:36 -0700 Subject: [PATCH 722/844] [camera] Move ignores for body_might_complete_normally_catch_error (#6339) --- .../camera/camera_android/lib/src/android_camera.dart | 11 +++++------ .../lib/src/avfoundation_camera.dart | 11 +++++------ .../lib/src/method_channel/method_channel_camera.dart | 11 +++++------ 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index b02929ec8c8c..3e9e52af04a7 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -138,12 +138,11 @@ class AndroidCamera extends CameraPlatform { 'cameraId': cameraId, 'imageFormatGroup': imageFormatGroup.name(), }, - ) - // TODO(srawlins): This should return a value of the future's type. This - // will fail upcoming analysis checks with - // https://github.com/flutter/flutter/issues/105750. - // ignore: body_might_complete_normally_catch_error - .catchError( + ).catchError( + // TODO(srawlins): This should return a value of the future's type. This + // will fail upcoming analysis checks with + // https://github.com/flutter/flutter/issues/105750. + // ignore: body_might_complete_normally_catch_error (Object error, StackTrace stackTrace) { if (error is! PlatformException) { throw error; diff --git a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart index d4f986074671..19054fe5c561 100644 --- a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart +++ b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart @@ -138,12 +138,11 @@ class AVFoundationCamera extends CameraPlatform { 'cameraId': cameraId, 'imageFormatGroup': imageFormatGroup.name(), }, - ) - // TODO(srawlins): This should return a value of the future's type. This - // will fail upcoming analysis checks with - // https://github.com/flutter/flutter/issues/105750. - // ignore: body_might_complete_normally_catch_error - .catchError( + ).catchError( + // TODO(srawlins): This should return a value of the future's type. This + // will fail upcoming analysis checks with + // https://github.com/flutter/flutter/issues/105750. + // ignore: body_might_complete_normally_catch_error (Object error, StackTrace stackTrace) { if (error is! PlatformException) { throw error; diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index fd9ad530bd27..eb6e59d9b4a1 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -130,12 +130,11 @@ class MethodChannelCamera extends CameraPlatform { 'cameraId': cameraId, 'imageFormatGroup': imageFormatGroup.name(), }, - ) - // TODO(srawlins): This should return a value of the future's type. This - // will fail upcoming analysis checks with - // https://github.com/flutter/flutter/issues/105750. - // ignore: body_might_complete_normally_catch_error - .catchError( + ).catchError( + // TODO(srawlins): This should return a value of the future's type. This + // will fail upcoming analysis checks with + // https://github.com/flutter/flutter/issues/105750. + // ignore: body_might_complete_normally_catch_error (Object error, StackTrace stackTrace) { if (error is! PlatformException) { throw error; From 2802ca46904b11236f9a93f2a12982541690597d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 13 Sep 2022 14:56:09 -0400 Subject: [PATCH 723/844] Roll Flutter from 4930444f4afe to 2d5e181b7308 (32 revisions) (#6422) --- .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 74c1c9a9cd2b..aa2123aef757 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -4930444f4afe85272bbdc84b43196d59dad71166 +2d5e181b7308f5b639123b231cbf65ca94690b3d From 5e7c779e78c16a4ed00289ac9c3e3b5ad4c37921 Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Tue, 13 Sep 2022 13:28:29 -0700 Subject: [PATCH 724/844] [in_app_purchase] Add immediateAndChargeFullPrice ProrationMode (#6415) --- .../in_app_purchase_android/CHANGELOG.md | 3 +- .../billing_client_wrapper.dart | 15 +++++-- .../billing_client_wrapper.g.dart | 1 + .../in_app_purchase_android/pubspec.yaml | 2 +- .../billing_client_wrapper_test.dart | 39 +++++++++++++++++++ 5 files changed, 55 insertions(+), 5 deletions(-) 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 910c5cdc1fc0..8733a1c4133c 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 0.2.3+4 * Updates minimum Flutter version to 2.10. +* Adds IMMEDIATE_AND_CHARGE_FULL_PRICE to the `ProrationMode`. ## 0.2.3+3 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 4c64a99e7a22..b64eaab49a9d 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 @@ -495,7 +495,8 @@ enum ProrationMode { @JsonValue(0) unknownSubscriptionUpgradeDowngradePolicy, - /// Replacement takes effect immediately, and the remaining time will be prorated and credited to the user. + /// Replacement takes effect immediately, and the remaining time will be prorated + /// and credited to the user. /// /// This is the current default behavior. @JsonValue(1) @@ -508,15 +509,23 @@ enum ProrationMode { @JsonValue(2) immediateAndChargeProratedPrice, - /// Replacement takes effect immediately, and the new price will be charged on next recurrence time. + /// Replacement takes effect immediately, and the new price will be charged on next + /// recurrence time. /// /// The billing cycle stays the same. @JsonValue(3) immediateWithoutProration, - /// Replacement takes effect when the old plan expires, and the new price will be charged at the same time. + /// Replacement takes effect when the old plan expires, and the new price will + /// be charged at the same time. @JsonValue(4) deferred, + + /// Replacement takes effect immediately, and the user is charged full price + /// of new plan and is given a full billing cycle of subscription, plus + /// remaining prorated time from the old plan. + @JsonValue(5) + immediateAndChargeFullPrice, } /// Serializer for [ProrationMode]. 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 efe7656d2138..99355a1b91fb 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 @@ -32,6 +32,7 @@ const _$ProrationModeEnumMap = { ProrationMode.immediateAndChargeProratedPrice: 2, ProrationMode.immediateWithoutProration: 3, ProrationMode.deferred: 4, + ProrationMode.immediateAndChargeFullPrice: 5, }; const _$BillingClientFeatureEnumMap = { 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 320e4b818e83..a0563f1434b8 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/plugins/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.2.3+3 +version: 0.2.3+4 environment: sdk: ">=2.14.0 <3.0.0" 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 e803e81f7803..4dae957e21eb 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 @@ -311,6 +311,45 @@ void main() { const ProrationModeConverter().toJson(prorationMode)); }); + test( + 'serializes and deserializes data when using immediateAndChargeFullPrice', + () async { + const String debugMessage = 'dummy message'; + const BillingResponse responseCode = BillingResponse.ok; + const BillingResultWrapper expectedBillingResult = BillingResultWrapper( + responseCode: responseCode, debugMessage: debugMessage); + stubPlatform.addResponse( + name: launchMethodName, + value: buildBillingResultMap(expectedBillingResult), + ); + const SkuDetailsWrapper skuDetails = dummySkuDetails; + const String accountId = 'hashedAccountId'; + const String profileId = 'hashedProfileId'; + const ProrationMode prorationMode = + ProrationMode.immediateAndChargeFullPrice; + + expect( + await billingClient.launchBillingFlow( + sku: skuDetails.sku, + accountId: accountId, + obfuscatedProfileId: profileId, + oldSku: dummyOldPurchase.sku, + prorationMode: prorationMode, + purchaseToken: dummyOldPurchase.purchaseToken), + equals(expectedBillingResult)); + final Map arguments = stubPlatform + .previousCallMatching(launchMethodName) + .arguments as Map; + expect(arguments['sku'], equals(skuDetails.sku)); + expect(arguments['accountId'], equals(accountId)); + expect(arguments['oldSku'], equals(dummyOldPurchase.sku)); + expect(arguments['obfuscatedProfileId'], equals(profileId)); + expect( + arguments['purchaseToken'], equals(dummyOldPurchase.purchaseToken)); + expect(arguments['prorationMode'], + const ProrationModeConverter().toJson(prorationMode)); + }); + test('handles null accountId', () async { const String debugMessage = 'dummy message'; const BillingResponse responseCode = BillingResponse.ok; From b512d780b4e23de817ba37afe1842d03612764bd Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 14 Sep 2022 11:51:01 -0400 Subject: [PATCH 725/844] Roll Flutter from 2d5e181b7308 to 4e8a28d29db7 (26 revisions) (#6427) --- .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 aa2123aef757..20cb21b1533d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -2d5e181b7308f5b639123b231cbf65ca94690b3d +4e8a28d29db7c26988c58ecaa692cb79f5e71394 From 993170f9d9167b3cef7da64da4e458bfcd60a3a8 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 14 Sep 2022 11:53:06 -0400 Subject: [PATCH 726/844] Roll Flutter (stable) from 4f9d92fbbdf0 to e3c29ec00c9c (3 revisions) (#6426) --- .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 d9b146dd7f87..4c15f9c261c3 100644 --- a/.ci/flutter_stable.version +++ b/.ci/flutter_stable.version @@ -1 +1 @@ -4f9d92fbbdf072a70a70d2179a9f87392b94104c +e3c29ec00c9c825c891d75054c63fcc46454dca1 From 64a2931d7f8e093c2adb8ea78187749ce1cfd259 Mon Sep 17 00:00:00 2001 From: Milvintsiss <38794405+Milvintsiss@users.noreply.github.com> Date: Wed, 14 Sep 2022 19:19:19 +0200 Subject: [PATCH 727/844] [google_sign_in_android] Corrects typo in logs (#6216) --- .../google_sign_in_android/CHANGELOG.md | 3 ++- .../plugins/googlesignin/GoogleSignInPlugin.java | 16 ++++++---------- .../google_sign_in_android/pubspec.yaml | 2 +- 3 files changed, 9 insertions(+), 12 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 e3e404fb48b3..b10c578cd125 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 6.1.1 +* Corrects typos in plugin error logs and removes not actionable warnings. * Updates minimum Flutter version to 2.10. ## 6.1.0 diff --git a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java index e84d196a6a0d..d345d4976c63 100644 --- a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java +++ b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java @@ -356,16 +356,12 @@ public void init( // Android apps are identified by their package name and the SHA-1 of their signing key. // https://developers.google.com/android/guides/client-auth // https://developers.google.com/identity/sign-in/android/start#configure-a-google-api-project - if (!Strings.isNullOrEmpty(clientId)) { - if (Strings.isNullOrEmpty(serverClientId)) { - Log.w( - "google_sing_in", - "clientId is not supported on Android and is interpreted as serverClientId." - + "Use serverClientId instead to suppress this warning."); - serverClientId = clientId; - } else { - Log.w("google_sing_in", "clientId is not supported on Android and is ignored."); - } + if (!Strings.isNullOrEmpty(clientId) && Strings.isNullOrEmpty(serverClientId)) { + Log.w( + "google_sign_in", + "clientId is not supported on Android and is interpreted as serverClientId. " + + "Use serverClientId instead to suppress this warning."); + serverClientId = clientId; } if (Strings.isNullOrEmpty(serverClientId)) { 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 941ca2c8d774..086a15117c2e 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/plugins/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.0 +version: 6.1.1 environment: sdk: ">=2.14.0 <3.0.0" From dc3bfc6d371e842e860c4d5b9c847f6f119049d5 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 15 Sep 2022 11:57:58 -0400 Subject: [PATCH 728/844] Roll Flutter from 4e8a28d29db7 to 9c0901ed63d0 (25 revisions) (#6431) --- .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 20cb21b1533d..8fef684e8ba6 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -4e8a28d29db7c26988c58ecaa692cb79f5e71394 +9c0901ed63d036f73fdf52ca9cf485149b56f798 From feed66b874176c67ae55a6cc3e9203694bc9ef2d Mon Sep 17 00:00:00 2001 From: Paul Berry Date: Thu, 15 Sep 2022 09:53:28 -0700 Subject: [PATCH 729/844] [plugin_platform_interface] Switch `PlatformInterface._instanceToken` to an expando. (#6411) This change replaces `PlatformInterface._instanceToken` (an instance field that points from a `PlatformInterface` to its corresponding token) with an expando that maps from `PlatformInterface` to `Object`. There is no change to the public API, and no change to the behavior of users' production code. This change ensures that if a customer tries to implement `PlatformInterface` using `implements` rather than `extends`, the code in `PlatformInterface._verify` won't try to access the private `_instanceToken` field in `PlatformInterface`. This is important because an upcoming change to the dart language is going to cause such accesses to throw exceptions rather than deferring to `noSuchMethod` (see https://github.com/dart-lang/language/issues/2020 for details). Fixes https://github.com/flutter/flutter/issues/109339. --- .../plugin_platform_interface/CHANGELOG.md | 6 +++++- .../lib/plugin_platform_interface.dart | 19 +++++++++++++++---- .../plugin_platform_interface/pubspec.yaml | 2 +- .../test/plugin_platform_interface_test.dart | 13 +++++++++++++ 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/packages/plugin_platform_interface/CHANGELOG.md b/packages/plugin_platform_interface/CHANGELOG.md index 0e9b701444fd..0b5a6b63a52f 100644 --- a/packages/plugin_platform_interface/CHANGELOG.md +++ b/packages/plugin_platform_interface/CHANGELOG.md @@ -1,7 +1,11 @@ -## NEXT +## 2.1.3 * Minor fixes for new analysis options. * Adds additional tests for `PlatformInterface` and `MockPlatformInterfaceMixin`. +* Modifies `PlatformInterface` to use an expando for detecting if a customer + tries to implement PlatformInterface using `implements` rather than `extends`. + This ensures that `verify` will continue to work as advertized after + https://github.com/dart-lang/language/issues/2020 is implemented. ## 2.1.2 diff --git a/packages/plugin_platform_interface/lib/plugin_platform_interface.dart b/packages/plugin_platform_interface/lib/plugin_platform_interface.dart index a03c9ce2d367..6733b29953b0 100644 --- a/packages/plugin_platform_interface/lib/plugin_platform_interface.dart +++ b/packages/plugin_platform_interface/lib/plugin_platform_interface.dart @@ -44,9 +44,20 @@ abstract class PlatformInterface { /// derived classes. /// /// @param token The same, non-`const` `Object` that will be passed to `verify`. - PlatformInterface({required Object token}) : _instanceToken = token; + PlatformInterface({required Object token}) { + _instanceTokens[this] = token; + } - final Object? _instanceToken; + /// Expando mapping instances of PlatformInterface to their associated tokens. + /// The reason this is not simply a private field of type `Object?` is because + /// as of the implementation of field promotion in Dart + /// (https://github.com/dart-lang/language/issues/2020), it is a runtime error + /// to invoke a private member that is mocked in another library. The expando + /// approach prevents [_verify] from triggering this runtime exception when + /// encountering an implementation that uses `implements` rather than + /// `extends`. This in turn allows [_verify] to throw an [AssertionError] (as + /// documented). + static final Expando _instanceTokens = Expando(); /// Ensures that the platform instance was constructed with a non-`const` token /// that matches the provided token and throws [AssertionError] if not. @@ -89,10 +100,10 @@ abstract class PlatformInterface { return; } if (preventConstObject && - identical(instance._instanceToken, const Object())) { + identical(_instanceTokens[instance], const Object())) { throw AssertionError('`const Object()` cannot be used as the token.'); } - if (!identical(token, instance._instanceToken)) { + if (!identical(token, _instanceTokens[instance])) { throw AssertionError( 'Platform interfaces must not be implemented with `implements`'); } diff --git a/packages/plugin_platform_interface/pubspec.yaml b/packages/plugin_platform_interface/pubspec.yaml index f4800d444e66..6a4bc488693b 100644 --- a/packages/plugin_platform_interface/pubspec.yaml +++ b/packages/plugin_platform_interface/pubspec.yaml @@ -15,7 +15,7 @@ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+ # be done when absolutely necessary and after the ecosystem has already migrated to 2.X.Y version # that is forward compatible with 3.0.0 (ideally the ecosystem have migrated to depend on: # `plugin_platform_interface: >=2.X.Y <4.0.0`). -version: 2.1.2 +version: 2.1.3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart b/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart index 329cecb16091..869017cd4f23 100644 --- a/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart +++ b/packages/plugin_platform_interface/test/plugin_platform_interface_test.dart @@ -21,6 +21,12 @@ class SamplePluginPlatform extends PlatformInterface { class ImplementsSamplePluginPlatform extends Mock implements SamplePluginPlatform {} +class ImplementsSamplePluginPlatformUsingNoSuchMethod + implements SamplePluginPlatform { + @override + dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +} + class ImplementsSamplePluginPlatformUsingMockPlatformInterfaceMixin extends Mock with MockPlatformInterfaceMixin implements SamplePluginPlatform {} @@ -98,6 +104,13 @@ void main() { }, throwsA(isA())); }); + test('prevents implmentation with `implements` and `noSuchMethod`', () { + expect(() { + SamplePluginPlatform.instance = + ImplementsSamplePluginPlatformUsingNoSuchMethod(); + }, throwsA(isA())); + }); + test('allows mocking with `implements`', () { final SamplePluginPlatform mock = ImplementsSamplePluginPlatformUsingMockPlatformInterfaceMixin(); From 5159d5ec00478950a8ce24945a73a9567e4b9aaa Mon Sep 17 00:00:00 2001 From: Pierre-Louis <6655696+guidezpl@users.noreply.github.com> Date: Thu, 15 Sep 2022 23:40:57 +0200 Subject: [PATCH 730/844] [in_app_purchase] Replace errorColor to land deprecations (Part 2) (#6417) --- packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md | 3 ++- .../in_app_purchase_storekit/example/lib/main.dart | 2 +- packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 3 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 1982a45e3e99..2952c4b31abf 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 0.3.2+1 * Updates minimum Flutter version to 2.10. +* Replaces deprecated ThemeData.primaryColor. ## 0.3.2 diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart index 72086eeb65a5..aa03190b0454 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart @@ -336,7 +336,7 @@ class _MyAppState extends State<_MyApp> { children: [ TextButton( style: TextButton.styleFrom( - backgroundColor: Theme.of(context).primaryColor, + backgroundColor: Theme.of(context).colorScheme.primary, // TODO(darrenaustin): Migrate to new API once it lands in stable: https://github.com/flutter/flutter/issues/105724 // ignore: deprecated_member_use primary: Colors.white, 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 69bc6c652c5a..076837198c76 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 platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/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.2 +version: 0.3.2+1 environment: sdk: ">=2.14.0 <3.0.0" From a6bf3b6332d3d5b0232256c486a2eb0058568f5d Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Fri, 16 Sep 2022 07:48:17 -0700 Subject: [PATCH 731/844] [ci] Update Cirrus to macOS Ventura and Xcode 14 (#6436) --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index 7a8e31dae0fb..01de99c23e9c 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -64,7 +64,7 @@ macos_intel_template: &MACOS_INTEL_TEMPLATE macos_arm_template: &MACOS_ARM_TEMPLATE << : *MACOS_TEMPLATE macos_instance: - image: ghcr.io/cirruslabs/macos-monterey-xcode:13.4 + image: ghcr.io/cirruslabs/macos-ventura-xcode:14 # Light-workload Linux tasks. # These use default machines, with fewer CPUs, to reduce pressure on the From 6b24ec1e91f8d28ccb584c0e7c1b82df53858d0e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 16 Sep 2022 15:11:21 -0400 Subject: [PATCH 732/844] Roll Flutter from 9c0901ed63d0 to 7714a8d034ad (33 revisions) (#6440) --- .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 8fef684e8ba6..1da87c32c07a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -9c0901ed63d036f73fdf52ca9cf485149b56f798 +7714a8d034ad418bcfcf421e12f45f121c356948 From c240f5dea2b922535292deae915565130633b18a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 17 Sep 2022 11:45:23 -0400 Subject: [PATCH 733/844] Roll Flutter from 7714a8d034ad to 5816d20b86b9 (23 revisions) (#6443) --- .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 1da87c32c07a..c78f85061bcb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -7714a8d034ad418bcfcf421e12f45f121c356948 +5816d20b86b95205c40921fa91ee3434b9c97ac6 From 7d4cdf2e8854307d03502e8309b34eccd2564e04 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 18 Sep 2022 12:35:24 -0400 Subject: [PATCH 734/844] Roll Flutter from 5816d20b86b9 to c07af53b3cb2 (5 revisions) (#6445) --- .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 c78f85061bcb..faf70b19aedf 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -5816d20b86b95205c40921fa91ee3434b9c97ac6 +c07af53b3cb2182d7735f022969efb00f2ddbd27 From 451f400f3c732896bd7b029d255e4056ecbeb72c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 19 Sep 2022 11:51:14 -0400 Subject: [PATCH 735/844] Roll Flutter from c07af53b3cb2 to 99475b1b0bee (10 revisions) (#6455) --- .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 faf70b19aedf..a9baeda10e84 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c07af53b3cb2182d7735f022969efb00f2ddbd27 +99475b1b0bee6ab09d0282b255c6b8c7e01ca4fe From 2cbb314d99d800cb4e1229ce6b304755303bbd64 Mon Sep 17 00:00:00 2001 From: Piotr Mitkowski Date: Mon, 19 Sep 2022 21:05:16 +0200 Subject: [PATCH 736/844] [image_picker] Allow saving photos picked with PHPicker without permissions (#6429) --- .../image_picker_ios/CHANGELOG.md | 4 +++ .../PickerSaveImageToPathOperationTests.m | 26 +++++++++++++++---- .../image_picker_ios/example/lib/main.dart | 23 ++++++++++------ .../ios/Classes/FLTImagePickerPlugin.m | 17 ++++++------ .../FLTPHPickerSaveImageToPathOperation.h | 1 + .../FLTPHPickerSaveImageToPathOperation.m | 10 ++++++- .../image_picker_ios/pubspec.yaml | 2 +- 7 files changed, 60 insertions(+), 23 deletions(-) diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index 33e2b61b0e88..986f5c0ff6ca 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.6+1 + +* Fixes issue with crashing the app when picking images with PHPicker without providing `Photo Library Usage` permission. + ## 0.8.6 * Adds `requestFullMetadata` option to `pickImage`, so images on iOS can be picked without `Photo Library Usage` permission. diff --git a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m index 688f5fbee032..e04c4f2abb50 100644 --- a/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m +++ b/packages/image_picker/image_picker_ios/example/ios/RunnerTests/PickerSaveImageToPathOperationTests.m @@ -22,7 +22,7 @@ - (void)testSaveWebPImage API_AVAILABLE(ios(14)) { PHPickerResult *result = [self createPickerResultWithProvider:itemProvider withIdentifier:UTTypeWebP.identifier]; - [self verifySavingImageWithPickerResult:result]; + [self verifySavingImageWithPickerResult:result fullMetadata:YES]; } - (void)testSavePNGImage API_AVAILABLE(ios(14)) { @@ -32,7 +32,7 @@ - (void)testSavePNGImage API_AVAILABLE(ios(14)) { PHPickerResult *result = [self createPickerResultWithProvider:itemProvider withIdentifier:UTTypeWebP.identifier]; - [self verifySavingImageWithPickerResult:result]; + [self verifySavingImageWithPickerResult:result fullMetadata:YES]; } - (void)testSaveJPGImage API_AVAILABLE(ios(14)) { @@ -42,7 +42,7 @@ - (void)testSaveJPGImage API_AVAILABLE(ios(14)) { PHPickerResult *result = [self createPickerResultWithProvider:itemProvider withIdentifier:UTTypeWebP.identifier]; - [self verifySavingImageWithPickerResult:result]; + [self verifySavingImageWithPickerResult:result fullMetadata:YES]; } - (void)testSaveGIFImage API_AVAILABLE(ios(14)) { @@ -52,7 +52,21 @@ - (void)testSaveGIFImage API_AVAILABLE(ios(14)) { PHPickerResult *result = [self createPickerResultWithProvider:itemProvider withIdentifier:UTTypeWebP.identifier]; - [self verifySavingImageWithPickerResult:result]; + [self verifySavingImageWithPickerResult:result fullMetadata:YES]; +} + +- (void)testSavePNGImageWithoutFullMetadata API_AVAILABLE(ios(14)) { + id photoAssetUtil = OCMClassMock([PHAsset class]); + + NSURL *imageURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"pngImage" + withExtension:@"png"]; + NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithContentsOfURL:imageURL]; + PHPickerResult *result = [self createPickerResultWithProvider:itemProvider + withIdentifier:UTTypeWebP.identifier]; + + [self verifySavingImageWithPickerResult:result fullMetadata:NO]; + OCMVerify(times(0), [photoAssetUtil fetchAssetsWithLocalIdentifiers:[OCMArg any] + options:[OCMArg any]]); } /** @@ -79,7 +93,8 @@ - (PHPickerResult *)createPickerResultWithProvider:(NSItemProvider *)itemProvide * * @param result the picker result */ -- (void)verifySavingImageWithPickerResult:(PHPickerResult *)result API_AVAILABLE(ios(14)) { +- (void)verifySavingImageWithPickerResult:(PHPickerResult *)result + fullMetadata:(BOOL)fullMetadata API_AVAILABLE(ios(14)) { XCTestExpectation *pathExpectation = [self expectationWithDescription:@"Path was created"]; FLTPHPickerSaveImageToPathOperation *operation = [[FLTPHPickerSaveImageToPathOperation alloc] @@ -87,6 +102,7 @@ - (void)verifySavingImageWithPickerResult:(PHPickerResult *)result API_AVAILABLE maxHeight:@100 maxWidth:@100 desiredImageQuality:@100 + fullMetadata:fullMetadata savedPathBlock:^(NSString *savedPath) { if ([[NSFileManager defaultManager] fileExistsAtPath:savedPath]) { [pathExpectation fulfill]; diff --git a/packages/image_picker/image_picker_ios/example/lib/main.dart b/packages/image_picker/image_picker_ios/example/lib/main.dart index c5372b8e7ad8..440f2f1d7cca 100755 --- a/packages/image_picker/image_picker_ios/example/lib/main.dart +++ b/packages/image_picker/image_picker_ios/example/lib/main.dart @@ -93,10 +93,15 @@ class _MyHomePageState extends State { await _displayPickImageDialog(context!, (double? maxWidth, double? maxHeight, int? quality) async { try { - final List? pickedFileList = await _picker.getMultiImage( - maxWidth: maxWidth, - maxHeight: maxHeight, - imageQuality: quality, + final List pickedFileList = + await _picker.getMultiImageWithOptions( + options: MultiImagePickerOptions( + imageOptions: ImageOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ), + ), ); setState(() { _imageFileList = pickedFileList; @@ -111,11 +116,13 @@ class _MyHomePageState extends State { await _displayPickImageDialog(context!, (double? maxWidth, double? maxHeight, int? quality) async { try { - final XFile? pickedFile = await _picker.getImage( + final XFile? pickedFile = await _picker.getImageFromSource( source: source, - maxWidth: maxWidth, - maxHeight: maxHeight, - imageQuality: quality, + options: ImagePickerOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ), ); setState(() { _setImageFileListFromFile(pickedFile); 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 fa1bb6650501..27b06ba994ef 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTImagePickerPlugin.m @@ -490,14 +490,15 @@ - (void)picker:(PHPickerViewController *)picker for (int i = 0; i < results.count; i++) { PHPickerResult *result = results[i]; - FLTPHPickerSaveImageToPathOperation *operation = - [[FLTPHPickerSaveImageToPathOperation alloc] initWithResult:result - maxHeight:maxHeight - maxWidth:maxWidth - desiredImageQuality:desiredImageQuality - savedPathBlock:^(NSString *savedPath) { - pathList[i] = savedPath; - }]; + FLTPHPickerSaveImageToPathOperation *operation = [[FLTPHPickerSaveImageToPathOperation alloc] + initWithResult:result + maxHeight:maxHeight + maxWidth:maxWidth + desiredImageQuality:desiredImageQuality + fullMetadata:self.callContext.requestFullMetadata + savedPathBlock:^(NSString *savedPath) { + pathList[i] = savedPath; + }]; [operationQueue addOperation:operation]; } [operationQueue waitUntilAllOperationsAreFinished]; 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 7ba3d28ef3dd..8e0970725e90 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.h +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.h @@ -26,6 +26,7 @@ maxHeight:(NSNumber *)maxHeight maxWidth:(NSNumber *)maxWidth desiredImageQuality:(NSNumber *)desiredImageQuality + fullMetadata:(BOOL)fullMetadata savedPathBlock:(void (^)(NSString *))savedPathBlock API_AVAILABLE(ios(14)); @end 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 a81c95f1b120..7c8fbc9ca7cf 100644 --- a/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m +++ b/packages/image_picker/image_picker_ios/ios/Classes/FLTPHPickerSaveImageToPathOperation.m @@ -13,6 +13,7 @@ @interface FLTPHPickerSaveImageToPathOperation () @property(assign, nonatomic) NSNumber *maxHeight; @property(assign, nonatomic) NSNumber *maxWidth; @property(assign, nonatomic) NSNumber *desiredImageQuality; +@property(assign, nonatomic) BOOL requestFullMetadata; @end @@ -28,6 +29,7 @@ - (instancetype)initWithResult:(PHPickerResult *)result maxHeight:(NSNumber *)maxHeight maxWidth:(NSNumber *)maxWidth desiredImageQuality:(NSNumber *)desiredImageQuality + fullMetadata:(BOOL)fullMetadata savedPathBlock:(GetSavedPath)savedPathBlock API_AVAILABLE(ios(14)) { if (self = [super init]) { if (result) { @@ -35,6 +37,7 @@ - (instancetype)initWithResult:(PHPickerResult *)result self.maxHeight = maxHeight; self.maxWidth = maxWidth; self.desiredImageQuality = desiredImageQuality; + self.requestFullMetadata = fullMetadata; getSavedPath = savedPathBlock; executing = NO; finished = NO; @@ -113,7 +116,12 @@ - (void)start { * Processes the image. */ - (void)processImage:(UIImage *)localImage API_AVAILABLE(ios(14)) { - PHAsset *originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromPHPickerResult:self.result]; + PHAsset *originalAsset; + // Only if requested, fetch the full "PHAsset" metadata, which requires "Photo Library Usage" + // permissions. + if (self.requestFullMetadata) { + originalAsset = [FLTImagePickerPhotoAssetUtil getAssetFromPHPickerResult:self.result]; + } if (self.maxWidth != nil || self.maxHeight != nil) { localImage = [FLTImagePickerImageUtil scaledImage:localImage diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index 5c30bf9d8c35..6c78b2340ed0 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/plugins/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.6 +version: 0.8.6+1 environment: sdk: ">=2.14.0 <3.0.0" From 8a96431792e80054ce8c236538baa54fa147406c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 20 Sep 2022 13:07:06 -0400 Subject: [PATCH 737/844] Roll Flutter from 99475b1b0bee to b55bbb7bffc7 (36 revisions) (#6456) --- .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 a9baeda10e84..0371be11bf87 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -99475b1b0bee6ab09d0282b255c6b8c7e01ca4fe +b55bbb7bffc79adf9cce33ebda0582df41aae280 From 7992b0f720750cd48e5a7fa9277973ca73046ff9 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 21 Sep 2022 12:36:06 -0400 Subject: [PATCH 738/844] Roll Flutter from b55bbb7bffc7 to cf01ecd19e61 (29 revisions) (#6458) --- .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 0371be11bf87..427e69ccf7d7 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b55bbb7bffc79adf9cce33ebda0582df41aae280 +cf01ecd19e61b436c6d7f9c0f6d57ef84aeb0257 From 7d5ff597e35b540f662c0134fa4a243c5055a1bb Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 22 Sep 2022 12:11:56 -0400 Subject: [PATCH 739/844] [tools] Require implementation package README warning (#6459) --- .../camera_android_camerax/example/README.md | 10 +- packages/camera/camera_web/example/README.md | 14 +- .../camera/camera_windows/example/README.md | 10 +- .../file_selector_ios/example/README.md | 11 +- .../file_selector_linux/example/README.md | 11 +- .../file_selector_macos/example/README.md | 11 +- .../file_selector_web/example/README.md | 14 +- .../file_selector_windows/example/README.md | 11 +- .../example/README.md | 10 +- .../google_maps_flutter_ios/example/README.md | 10 +- .../google_maps_flutter_web/example/README.md | 15 +- .../google_sign_in_android/example/README.md | 10 +- .../google_sign_in_ios/example/README.md | 10 +- .../google_sign_in_web/example/README.md | 14 +- .../image_picker_android/example/README.md | 10 +- .../image_picker_for_web/example/README.md | 12 +- .../image_picker_ios/example/README.md | 10 +- .../image_picker_windows/example/README.md | 10 +- .../in_app_purchase_android/example/README.md | 63 +------- .../example/README.md | 80 +--------- .../local_auth_android/example/README.md | 10 +- .../local_auth_ios/example/README.md | 10 +- .../local_auth_windows/example/README.md | 10 +- .../path_provider_android/example/README.md | 10 +- .../path_provider_ios/example/README.md | 10 +- .../path_provider_linux/example/README.md | 10 +- .../path_provider_macos/example/README.md | 10 +- .../path_provider_windows/example/README.md | 10 +- .../quick_actions_android/example/README.md | 10 +- .../quick_actions_ios/example/README.md | 10 +- .../example/README.md | 10 +- .../shared_preferences_ios/example/README.md | 10 +- .../example/README.md | 10 +- .../example/README.md | 10 +- .../shared_preferences_web/example/README.md | 12 +- .../example/README.md | 10 +- .../url_launcher_android/example/README.md | 10 +- .../url_launcher_ios/example/README.md | 10 +- .../url_launcher_linux/example/README.md | 10 +- .../url_launcher_macos/example/README.md | 10 +- .../url_launcher_web/example/README.md | 15 +- .../url_launcher_windows/example/README.md | 10 +- .../video_player_android/example/README.md | 10 +- .../example/README.md | 10 +- .../video_player_web/example/README.md | 14 +- .../webview_flutter_android/example/README.md | 10 +- .../webview_flutter_web/example/README.md | 10 +- .../example/README.md | 10 +- script/tool/CHANGELOG.md | 6 + script/tool/lib/src/readme_check_command.dart | 66 +++++++- .../tool/test/readme_check_command_test.dart | 141 ++++++++++++++++++ 51 files changed, 615 insertions(+), 235 deletions(-) diff --git a/packages/camera/camera_android_camerax/example/README.md b/packages/camera/camera_android_camerax/example/README.md index 438e446d1921..96b8bb17dbff 100644 --- a/packages/camera/camera_android_camerax/example/README.md +++ b/packages/camera/camera_android_camerax/example/README.md @@ -1,3 +1,9 @@ -# camera_android_camerax_example +# Platform Implementation Test App -Demonstrates how to use the camera_android_camerax plugin. +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/camera/camera_web/example/README.md b/packages/camera/camera_web/example/README.md index 8a6e74b107ea..0e51ae5ecbd2 100644 --- a/packages/camera/camera_web/example/README.md +++ b/packages/camera/camera_web/example/README.md @@ -1,4 +1,14 @@ -# Testing +# 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. + +## Testing This package uses `package:integration_test` to run its tests in a web browser. @@ -6,4 +16,4 @@ See [Plugin Tests > Web Tests](https://github.com/flutter/flutter/wiki/Plugin-Te in the Flutter wiki for instructions to setup and run the tests in this package. Check [flutter.dev > Integration testing](https://flutter.dev/docs/testing/integration-tests) -for more info. \ No newline at end of file +for more info. diff --git a/packages/camera/camera_windows/example/README.md b/packages/camera/camera_windows/example/README.md index ee7326472eaf..96b8bb17dbff 100644 --- a/packages/camera/camera_windows/example/README.md +++ b/packages/camera/camera_windows/example/README.md @@ -1,3 +1,9 @@ -# camera_windows_example +# Platform Implementation Test App -Demonstrates how to use the camera_windows plugin. +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/file_selector/file_selector_ios/example/README.md b/packages/file_selector/file_selector_ios/example/README.md index 9ed63fdb669f..96b8bb17dbff 100644 --- a/packages/file_selector/file_selector_ios/example/README.md +++ b/packages/file_selector/file_selector_ios/example/README.md @@ -1,4 +1,9 @@ -# `file_selector_ios` example +# Platform Implementation Test App -Demonstrates iOS implementation of the -[`file_selector` plugin](https://pub.dev/packages/file_selector). \ No newline at end of file +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/file_selector/file_selector_linux/example/README.md b/packages/file_selector/file_selector_linux/example/README.md index 2f9f8c0f824b..96b8bb17dbff 100644 --- a/packages/file_selector/file_selector_linux/example/README.md +++ b/packages/file_selector/file_selector_linux/example/README.md @@ -1,4 +1,9 @@ -# `file_selector_linux` example +# Platform Implementation Test App -Demonstrates Linux implementation of the -[`file_selector` plugin](https://pub.dev/packages/file_selector). +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/file_selector/file_selector_macos/example/README.md b/packages/file_selector/file_selector_macos/example/README.md index 782fe679fcb0..96b8bb17dbff 100644 --- a/packages/file_selector/file_selector_macos/example/README.md +++ b/packages/file_selector/file_selector_macos/example/README.md @@ -1,4 +1,9 @@ -# `file_selector_macos` example +# Platform Implementation Test App -Demonstrates macOS implementation of the -[`file_selector` plugin](https://pub.dev/packages/file_selector). +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/file_selector/file_selector_web/example/README.md b/packages/file_selector/file_selector_web/example/README.md index 8a6e74b107ea..0e51ae5ecbd2 100644 --- a/packages/file_selector/file_selector_web/example/README.md +++ b/packages/file_selector/file_selector_web/example/README.md @@ -1,4 +1,14 @@ -# Testing +# 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. + +## Testing This package uses `package:integration_test` to run its tests in a web browser. @@ -6,4 +16,4 @@ See [Plugin Tests > Web Tests](https://github.com/flutter/flutter/wiki/Plugin-Te in the Flutter wiki for instructions to setup and run the tests in this package. Check [flutter.dev > Integration testing](https://flutter.dev/docs/testing/integration-tests) -for more info. \ No newline at end of file +for more info. diff --git a/packages/file_selector/file_selector_windows/example/README.md b/packages/file_selector/file_selector_windows/example/README.md index c8a3cce44a9a..96b8bb17dbff 100644 --- a/packages/file_selector/file_selector_windows/example/README.md +++ b/packages/file_selector/file_selector_windows/example/README.md @@ -1,4 +1,9 @@ -# `file_selector_windows` Example +# Platform Implementation Test App -Demonstrates Windows implementation of the -[`file_selector` plugin](https://pub.dev/packages/file_selector). +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/google_maps_flutter/google_maps_flutter_android/example/README.md b/packages/google_maps_flutter/google_maps_flutter_android/example/README.md index c8852649b065..96b8bb17dbff 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/README.md +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/README.md @@ -1,3 +1,9 @@ -# google_maps_flutter_example +# Platform Implementation Test App -Demonstrates how to use the google_maps_flutter plugin. +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/google_maps_flutter/google_maps_flutter_ios/example/README.md b/packages/google_maps_flutter/google_maps_flutter_ios/example/README.md index c8852649b065..96b8bb17dbff 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/README.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/README.md @@ -1,3 +1,9 @@ -# google_maps_flutter_example +# Platform Implementation Test App -Demonstrates how to use the google_maps_flutter plugin. +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/google_maps_flutter/google_maps_flutter_web/example/README.md b/packages/google_maps_flutter/google_maps_flutter_web/example/README.md index 3cdecfab2ab9..0e51ae5ecbd2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/README.md +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/README.md @@ -1,4 +1,14 @@ -# Testing +# 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. + +## Testing This package uses `package:integration_test` to run its tests in a web browser. @@ -7,6 +17,3 @@ in the Flutter wiki for instructions to setup and run the tests in this package. Check [flutter.dev > Integration testing](https://flutter.dev/docs/testing/integration-tests) for more info. - -See [Plugin Tests > Web Tests > Mocks](https://github.com/flutter/flutter/wiki/Plugin-Tests#mocks) -in the Flutter wiki for more information about the `.mocks.dart` files in this package. \ No newline at end of file diff --git a/packages/google_sign_in/google_sign_in_android/example/README.md b/packages/google_sign_in/google_sign_in_android/example/README.md index 8eb153eb8efd..96b8bb17dbff 100644 --- a/packages/google_sign_in/google_sign_in_android/example/README.md +++ b/packages/google_sign_in/google_sign_in_android/example/README.md @@ -1,3 +1,9 @@ -# google_sign_in_android example +# Platform Implementation Test App -Exercises the Android implementation of `GoogleSignInPlatform`. +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/google_sign_in/google_sign_in_ios/example/README.md b/packages/google_sign_in/google_sign_in_ios/example/README.md index 04c3372dc3b0..96b8bb17dbff 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/README.md +++ b/packages/google_sign_in/google_sign_in_ios/example/README.md @@ -1,3 +1,9 @@ -# google_sign_in_ios example +# Platform Implementation Test App -Exercises the iOS implementation of `GoogleSignInPlatform`. +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/google_sign_in/google_sign_in_web/example/README.md b/packages/google_sign_in/google_sign_in_web/example/README.md index 8a6e74b107ea..0e51ae5ecbd2 100644 --- a/packages/google_sign_in/google_sign_in_web/example/README.md +++ b/packages/google_sign_in/google_sign_in_web/example/README.md @@ -1,4 +1,14 @@ -# Testing +# 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. + +## Testing This package uses `package:integration_test` to run its tests in a web browser. @@ -6,4 +16,4 @@ See [Plugin Tests > Web Tests](https://github.com/flutter/flutter/wiki/Plugin-Te in the Flutter wiki for instructions to setup and run the tests in this package. Check [flutter.dev > Integration testing](https://flutter.dev/docs/testing/integration-tests) -for more info. \ No newline at end of file +for more info. diff --git a/packages/image_picker/image_picker_android/example/README.md b/packages/image_picker/image_picker_android/example/README.md index 16b5c51839f8..96b8bb17dbff 100755 --- a/packages/image_picker/image_picker_android/example/README.md +++ b/packages/image_picker/image_picker_android/example/README.md @@ -1,3 +1,9 @@ -# image_picker_example +# Platform Implementation Test App -Demonstrates how to use the `image_picker` plugin. +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/image_picker/image_picker_for_web/example/README.md b/packages/image_picker/image_picker_for_web/example/README.md index 4348451b14e2..0e51ae5ecbd2 100644 --- a/packages/image_picker/image_picker_for_web/example/README.md +++ b/packages/image_picker/image_picker_for_web/example/README.md @@ -1,4 +1,14 @@ -# Testing +# 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. + +## Testing This package uses `package:integration_test` to run its tests in a web browser. diff --git a/packages/image_picker/image_picker_ios/example/README.md b/packages/image_picker/image_picker_ios/example/README.md index 16b5c51839f8..96b8bb17dbff 100755 --- a/packages/image_picker/image_picker_ios/example/README.md +++ b/packages/image_picker/image_picker_ios/example/README.md @@ -1,3 +1,9 @@ -# image_picker_example +# Platform Implementation Test App -Demonstrates how to use the `image_picker` plugin. +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/image_picker/image_picker_windows/example/README.md b/packages/image_picker/image_picker_windows/example/README.md index 7f61053c6e30..96b8bb17dbff 100644 --- a/packages/image_picker/image_picker_windows/example/README.md +++ b/packages/image_picker/image_picker_windows/example/README.md @@ -1,3 +1,9 @@ -# image_picker_windows_example +# Platform Implementation Test App -Demonstrates how to use the image_picker_windows plugin. +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/in_app_purchase/in_app_purchase_android/example/README.md b/packages/in_app_purchase/in_app_purchase_android/example/README.md index 255e838e5b93..96b8bb17dbff 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/README.md +++ b/packages/in_app_purchase/in_app_purchase_android/example/README.md @@ -1,58 +1,9 @@ -# In App Purchase Example +# Platform Implementation Test App -Demonstrates how to use the In App Purchase Android (IAP) Plugin. +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. -## Getting Started - -### Preparation - -There's a significant amount of setup required for testing in-app purchases -successfully, including registering new app IDs and store entries to use for -testing in the Play Developer Console. Google Play requires developers to -configure an app with in-app items for purchase to call their in-app-purchase -APIs. The Google Play Store has extensive documentation on how to do this, and -we've also included a high level guide below. - -* [Google Play Billing Overview](https://developer.android.com/google/play/billing/billing_overview) - -### Android - -1. Create a new app in the [Play Developer - Console](https://play.google.com/apps/publish/) (PDC). - -2. Sign up for a merchant's account in the PDC. - -3. Create IAPs in the PDC available for purchase in the app. The example assumes - the following SKU IDs exist: - - - `consumable`: A managed product. - - `upgrade`: A managed product. - - `subscription_silver`: A lower level subscription. - - `subscription_gold`: A higher level subscription. - - Make sure that all of the products are set to `ACTIVE`. - -4. Update `APP_ID` in `example/android/app/build.gradle` to match your package - ID in the PDC. - -5. Create an `example/android/keystore.properties` file with all your signing - information. `keystore.example.properties` exists as an example to follow. - It's impossible to use any of the `BillingClient` APIs from an unsigned APK. - See - [here](https://developer.android.com/studio/publish/app-signing#secure-shared-keystore) - and [here](https://developer.android.com/studio/publish/app-signing#sign-apk) - for more information. - -6. Build a signed apk. `flutter build apk` will work for this, the gradle files - in this project have been configured to sign even debug builds. - -7. Upload the signed APK from step 6 to the PDC, and publish that to the alpha - test channel. Add your test account as an approved tester. The - `BillingClient` APIs won't work unless the app has been fully published to - the alpha channel and is being used by an authorized test account. See - [here](https://support.google.com/googleplay/android-developer/answer/3131213) - for more info. - -8. Sign in to the test device with the test account from step #7. Then use - `flutter run` to install the app to the device and test like normal. - \ No newline at end of file +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/README.md b/packages/in_app_purchase/in_app_purchase_storekit/example/README.md index 9cf98bf02e79..96b8bb17dbff 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/README.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/README.md @@ -1,75 +1,9 @@ -# In App Purchase iOS Example +# Platform Implementation Test App -Demonstrates how to use the In App Purchase iOS (IAP) Plugin. +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. -## Getting Started - -### Preparation - -There's a significant amount of setup required for testing in app purchases -successfully, including registering new app IDs and store entries to use for -testing in App Store Connect. The App Store requires developers to configure -an app with in-app items for purchase to call their in-app-purchase APIs. -The App Store has extensive documentation on how to do this, and we've also -included a high level guide below. - -* [In-App Purchase (App Store)](https://developer.apple.com/in-app-purchase/) - -### iOS - -When using Xcode 12 and iOS 14 or higher you can run the example in the simulator or on a device without -having to configure an App in App Store Connect. The example app is set up to use StoreKit Testing configured -in the `example/ios/Runner/Configuration.storekit` file (as documented in the article [Setting Up StoreKit Testing in Xcode](https://developer.apple.com/documentation/xcode/setting_up_storekit_testing_in_xcode?language=objc)). -To run the application take the following steps (note that it will only work when running from Xcode): - -1. Open the example app with Xcode, `File > Open File` `example/ios/Runner.xcworkspace`; - -2. Within Xcode edit the current scheme, `Product > Scheme > Edit Scheme...` (or press `Command + Shift + ,`); - -3. Enable StoreKit testing: - a. Select the `Run` action; - b. Click `Options` in the action settings; - c. Select the `Configuration.storekit` for the StoreKit Configuration option. - -4. Click the `Close` button to close the scheme editor; - -5. Select the device you want to run the example App on; - -6. Run the application using `Product > Run` (or hit the run button). - -When testing on pre-iOS 14 you can't run the example app on a simulator and you will need to configure an app in App Store Connect. You can do so by following the steps below: - -1. Follow ["Workflow for configuring in-app - purchases"](https://help.apple.com/app-store-connect/#/devb57be10e7), a - detailed guide on all the steps needed to enable IAPs for an app. Complete - steps 1 ("Sign a Paid Applications Agreement") and 2 ("Configure in-app - purchases"). - - For step #2, "Configure in-app purchases in App Store Connect," you'll want - to create the following products: - - - A consumable with product ID `consumable` - - An upgrade with product ID `upgrade` - - An auto-renewing subscription with product ID `subscription_silver` - - An non-renewing subscription with product ID `subscription_gold` - -2. In XCode, `File > Open File` `example/ios/Runner.xcworkspace`. Update the - Bundle ID to match the Bundle ID of the app created in step #1. - -3. [Create a Sandbox tester - account](https://help.apple.com/app-store-connect/#/dev8b997bee1) to test the - in-app purchases with. - -4. Use `flutter run` to install the app and test it. Note that you need to test - it on a real device instead of a simulator. Next click on one of the products - in the example App, this enables the "SANDBOX ACCOUNT" section in the iOS - settings. You will now be asked to sign in with your sandbox test account to - complete the purchase (no worries you won't be charged). If for some reason - you aren't asked to sign-in or the wrong user is listed, go into the iOS - settings ("Settings" -> "App Store" -> "SANDBOX ACCOUNT") and update your - sandbox account from there. This procedure is explained in great detail in - the [Testing In-App Purchases with Sandbox](https://developer.apple.com/documentation/storekit/in-app_purchase/testing_in-app_purchases_with_sandbox?language=objc) article. - - -**Important:** signing into any production service (including iTunes!) with the -sandbox test account will permanently invalidate it. +Unless you are making changes to this implementation package, this example is +very unlikely to be relevant. diff --git a/packages/local_auth/local_auth_android/example/README.md b/packages/local_auth/local_auth_android/example/README.md index bd004a77d86b..96b8bb17dbff 100644 --- a/packages/local_auth/local_auth_android/example/README.md +++ b/packages/local_auth/local_auth_android/example/README.md @@ -1,3 +1,9 @@ -# local_auth_example +# Platform Implementation Test App -Demonstrates how to use the local_auth plugin. +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/local_auth/local_auth_ios/example/README.md b/packages/local_auth/local_auth_ios/example/README.md index bd004a77d86b..96b8bb17dbff 100644 --- a/packages/local_auth/local_auth_ios/example/README.md +++ b/packages/local_auth/local_auth_ios/example/README.md @@ -1,3 +1,9 @@ -# local_auth_example +# Platform Implementation Test App -Demonstrates how to use the local_auth plugin. +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/local_auth/local_auth_windows/example/README.md b/packages/local_auth/local_auth_windows/example/README.md index 8f48b8563cad..96b8bb17dbff 100644 --- a/packages/local_auth/local_auth_windows/example/README.md +++ b/packages/local_auth/local_auth_windows/example/README.md @@ -1,3 +1,9 @@ -# local_auth_example +# Platform Implementation Test App -Demonstrates how to use the local_auth plugin. \ No newline at end of file +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/path_provider/path_provider_android/example/README.md b/packages/path_provider/path_provider_android/example/README.md index 801f44409938..96b8bb17dbff 100644 --- a/packages/path_provider/path_provider_android/example/README.md +++ b/packages/path_provider/path_provider_android/example/README.md @@ -1,3 +1,9 @@ -# path_provider_example +# Platform Implementation Test App -Demonstrates how to use the path_provider plugin. +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/path_provider/path_provider_ios/example/README.md b/packages/path_provider/path_provider_ios/example/README.md index 801f44409938..96b8bb17dbff 100644 --- a/packages/path_provider/path_provider_ios/example/README.md +++ b/packages/path_provider/path_provider_ios/example/README.md @@ -1,3 +1,9 @@ -# path_provider_example +# Platform Implementation Test App -Demonstrates how to use the path_provider plugin. +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/path_provider/path_provider_linux/example/README.md b/packages/path_provider/path_provider_linux/example/README.md index 333d0f55cec7..96b8bb17dbff 100644 --- a/packages/path_provider/path_provider_linux/example/README.md +++ b/packages/path_provider/path_provider_linux/example/README.md @@ -1,3 +1,9 @@ -# path_provider_linux_example +# Platform Implementation Test App -Demonstrates how to use the path_provider_linux plugin. +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/path_provider/path_provider_macos/example/README.md b/packages/path_provider/path_provider_macos/example/README.md index 158869595c01..96b8bb17dbff 100644 --- a/packages/path_provider/path_provider_macos/example/README.md +++ b/packages/path_provider/path_provider_macos/example/README.md @@ -1,3 +1,9 @@ -# path_provider_macos_example +# Platform Implementation Test App -Demonstrates how to use the path_provider_macos plugin. +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/path_provider/path_provider_windows/example/README.md b/packages/path_provider/path_provider_windows/example/README.md index 63723991a2e9..96b8bb17dbff 100644 --- a/packages/path_provider/path_provider_windows/example/README.md +++ b/packages/path_provider/path_provider_windows/example/README.md @@ -1,3 +1,9 @@ -# path_provider_windows_example +# Platform Implementation Test App -Demonstrates how to use the path_provider_windows plugin. +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/quick_actions/quick_actions_android/example/README.md b/packages/quick_actions/quick_actions_android/example/README.md index c8a629019fc9..96b8bb17dbff 100644 --- a/packages/quick_actions/quick_actions_android/example/README.md +++ b/packages/quick_actions/quick_actions_android/example/README.md @@ -1,3 +1,9 @@ -# quick_actions_example +# Platform Implementation Test App -Demonstrates how to use the quick_actions plugin. +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/quick_actions/quick_actions_ios/example/README.md b/packages/quick_actions/quick_actions_ios/example/README.md index c8a629019fc9..96b8bb17dbff 100644 --- a/packages/quick_actions/quick_actions_ios/example/README.md +++ b/packages/quick_actions/quick_actions_ios/example/README.md @@ -1,3 +1,9 @@ -# quick_actions_example +# Platform Implementation Test App -Demonstrates how to use the quick_actions plugin. +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/shared_preferences/shared_preferences_android/example/README.md b/packages/shared_preferences/shared_preferences_android/example/README.md index c060637c7ec5..96b8bb17dbff 100644 --- a/packages/shared_preferences/shared_preferences_android/example/README.md +++ b/packages/shared_preferences/shared_preferences_android/example/README.md @@ -1,3 +1,9 @@ -# shared_preferences_example +# Platform Implementation Test App -Demonstrates how to use the shared_preferences plugin. +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/shared_preferences/shared_preferences_ios/example/README.md b/packages/shared_preferences/shared_preferences_ios/example/README.md index c060637c7ec5..96b8bb17dbff 100644 --- a/packages/shared_preferences/shared_preferences_ios/example/README.md +++ b/packages/shared_preferences/shared_preferences_ios/example/README.md @@ -1,3 +1,9 @@ -# shared_preferences_example +# Platform Implementation Test App -Demonstrates how to use the shared_preferences plugin. +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/shared_preferences/shared_preferences_linux/example/README.md b/packages/shared_preferences/shared_preferences_linux/example/README.md index c060637c7ec5..96b8bb17dbff 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/README.md +++ b/packages/shared_preferences/shared_preferences_linux/example/README.md @@ -1,3 +1,9 @@ -# shared_preferences_example +# Platform Implementation Test App -Demonstrates how to use the shared_preferences plugin. +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/shared_preferences/shared_preferences_macos/example/README.md b/packages/shared_preferences/shared_preferences_macos/example/README.md index c060637c7ec5..96b8bb17dbff 100644 --- a/packages/shared_preferences/shared_preferences_macos/example/README.md +++ b/packages/shared_preferences/shared_preferences_macos/example/README.md @@ -1,3 +1,9 @@ -# shared_preferences_example +# Platform Implementation Test App -Demonstrates how to use the shared_preferences plugin. +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/shared_preferences/shared_preferences_web/example/README.md b/packages/shared_preferences/shared_preferences_web/example/README.md index 4348451b14e2..0e51ae5ecbd2 100644 --- a/packages/shared_preferences/shared_preferences_web/example/README.md +++ b/packages/shared_preferences/shared_preferences_web/example/README.md @@ -1,4 +1,14 @@ -# Testing +# 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. + +## Testing This package uses `package:integration_test` to run its tests in a web browser. diff --git a/packages/shared_preferences/shared_preferences_windows/example/README.md b/packages/shared_preferences/shared_preferences_windows/example/README.md index 30c7f7e50c3b..96b8bb17dbff 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/README.md +++ b/packages/shared_preferences/shared_preferences_windows/example/README.md @@ -1,3 +1,9 @@ -# shared_preferences_windows_example +# Platform Implementation Test App -Demonstrates how to use the shared_preferences_windows plugin. +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/url_launcher/url_launcher_android/example/README.md b/packages/url_launcher/url_launcher_android/example/README.md index 35b4bdb7031e..96b8bb17dbff 100644 --- a/packages/url_launcher/url_launcher_android/example/README.md +++ b/packages/url_launcher/url_launcher_android/example/README.md @@ -1,3 +1,9 @@ -# url_launcher_example +# Platform Implementation Test App -Demonstrates how to use the url_launcher plugin. +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/url_launcher/url_launcher_ios/example/README.md b/packages/url_launcher/url_launcher_ios/example/README.md index 35b4bdb7031e..96b8bb17dbff 100644 --- a/packages/url_launcher/url_launcher_ios/example/README.md +++ b/packages/url_launcher/url_launcher_ios/example/README.md @@ -1,3 +1,9 @@ -# url_launcher_example +# Platform Implementation Test App -Demonstrates how to use the url_launcher plugin. +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/url_launcher/url_launcher_linux/example/README.md b/packages/url_launcher/url_launcher_linux/example/README.md index 35b4bdb7031e..96b8bb17dbff 100644 --- a/packages/url_launcher/url_launcher_linux/example/README.md +++ b/packages/url_launcher/url_launcher_linux/example/README.md @@ -1,3 +1,9 @@ -# url_launcher_example +# Platform Implementation Test App -Demonstrates how to use the url_launcher plugin. +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/url_launcher/url_launcher_macos/example/README.md b/packages/url_launcher/url_launcher_macos/example/README.md index 35b4bdb7031e..96b8bb17dbff 100644 --- a/packages/url_launcher/url_launcher_macos/example/README.md +++ b/packages/url_launcher/url_launcher_macos/example/README.md @@ -1,3 +1,9 @@ -# url_launcher_example +# Platform Implementation Test App -Demonstrates how to use the url_launcher plugin. +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/url_launcher/url_launcher_web/example/README.md b/packages/url_launcher/url_launcher_web/example/README.md index 3cdecfab2ab9..0e51ae5ecbd2 100644 --- a/packages/url_launcher/url_launcher_web/example/README.md +++ b/packages/url_launcher/url_launcher_web/example/README.md @@ -1,4 +1,14 @@ -# Testing +# 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. + +## Testing This package uses `package:integration_test` to run its tests in a web browser. @@ -7,6 +17,3 @@ in the Flutter wiki for instructions to setup and run the tests in this package. Check [flutter.dev > Integration testing](https://flutter.dev/docs/testing/integration-tests) for more info. - -See [Plugin Tests > Web Tests > Mocks](https://github.com/flutter/flutter/wiki/Plugin-Tests#mocks) -in the Flutter wiki for more information about the `.mocks.dart` files in this package. \ No newline at end of file diff --git a/packages/url_launcher/url_launcher_windows/example/README.md b/packages/url_launcher/url_launcher_windows/example/README.md index e444852697b9..96b8bb17dbff 100644 --- a/packages/url_launcher/url_launcher_windows/example/README.md +++ b/packages/url_launcher/url_launcher_windows/example/README.md @@ -1,3 +1,9 @@ -# url_launcher_windows_example +# Platform Implementation Test App -Demonstrates the url_launcher_windows plugin. +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/video_player/video_player_android/example/README.md b/packages/video_player/video_player_android/example/README.md index f5974e947c00..96b8bb17dbff 100644 --- a/packages/video_player/video_player_android/example/README.md +++ b/packages/video_player/video_player_android/example/README.md @@ -1,3 +1,9 @@ -# video_player_example +# Platform Implementation Test App -Demonstrates how to use the video_player plugin. +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/video_player/video_player_avfoundation/example/README.md b/packages/video_player/video_player_avfoundation/example/README.md index f5974e947c00..96b8bb17dbff 100644 --- a/packages/video_player/video_player_avfoundation/example/README.md +++ b/packages/video_player/video_player_avfoundation/example/README.md @@ -1,3 +1,9 @@ -# video_player_example +# Platform Implementation Test App -Demonstrates how to use the video_player plugin. +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/video_player/video_player_web/example/README.md b/packages/video_player/video_player_web/example/README.md index 8a6e74b107ea..0e51ae5ecbd2 100644 --- a/packages/video_player/video_player_web/example/README.md +++ b/packages/video_player/video_player_web/example/README.md @@ -1,4 +1,14 @@ -# Testing +# 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. + +## Testing This package uses `package:integration_test` to run its tests in a web browser. @@ -6,4 +16,4 @@ See [Plugin Tests > Web Tests](https://github.com/flutter/flutter/wiki/Plugin-Te in the Flutter wiki for instructions to setup and run the tests in this package. Check [flutter.dev > Integration testing](https://flutter.dev/docs/testing/integration-tests) -for more info. \ No newline at end of file +for more info. diff --git a/packages/webview_flutter/webview_flutter_android/example/README.md b/packages/webview_flutter/webview_flutter_android/example/README.md index e5bd6e20db63..96b8bb17dbff 100644 --- a/packages/webview_flutter/webview_flutter_android/example/README.md +++ b/packages/webview_flutter/webview_flutter_android/example/README.md @@ -1,3 +1,9 @@ -# webview_flutter_example +# Platform Implementation Test App -Demonstrates how to use the webview_flutter plugin. +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/webview_flutter/webview_flutter_web/example/README.md b/packages/webview_flutter/webview_flutter_web/example/README.md index e5bd6e20db63..96b8bb17dbff 100644 --- a/packages/webview_flutter/webview_flutter_web/example/README.md +++ b/packages/webview_flutter/webview_flutter_web/example/README.md @@ -1,3 +1,9 @@ -# webview_flutter_example +# Platform Implementation Test App -Demonstrates how to use the webview_flutter plugin. +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/webview_flutter/webview_flutter_wkwebview/example/README.md b/packages/webview_flutter/webview_flutter_wkwebview/example/README.md index e5bd6e20db63..96b8bb17dbff 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/README.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/README.md @@ -1,3 +1,9 @@ -# webview_flutter_example +# Platform Implementation Test App -Demonstrates how to use the webview_flutter plugin. +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/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 8dbb4840a672..f6dfb0e58266 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,9 @@ +## NEXT + +* Adds `readme-check` validation that the example/README.md for a federated + plugin's implementation packages has a warning about the intended use of the + example instead of the template boilerplate. + ## 0.10.0 * Improves the logic in `version-check` to determine what changes don't require diff --git a/script/tool/lib/src/readme_check_command.dart b/script/tool/lib/src/readme_check_command.dart index 4acf997a0085..e3fbc7bc454d 100644 --- a/script/tool/lib/src/readme_check_command.dart +++ b/script/tool/lib/src/readme_check_command.dart @@ -104,11 +104,8 @@ class ReadmeCheckCommand extends PackageLoopingCommand { errors.add(blockValidationError); } - if (_containsTemplateBoilerplate(readmeLines)) { - printError('${indentation}The boilerplate section about getting started ' - 'with Flutter should not be left in.'); - errors.add('Contains template boilerplate'); - } + errors.addAll(_validateBoilerplate(readmeLines, + mainPackage: mainPackage, isExample: isExample)); // Check if this is the main readme for a plugin, and if so enforce extra // checks. @@ -284,10 +281,63 @@ ${indentation * 2}Please use standard capitalizations: ${sortedListString(expect return null; } - /// Returns true if the README still has the boilerplate from the - /// `flutter create` templates. - bool _containsTemplateBoilerplate(List readmeLines) { + /// Validates [readmeLines], outputing error messages for any issue and + /// returning an array of error summaries (if any). + /// + /// Returns an empty array if validation passes. + List _validateBoilerplate( + List readmeLines, { + required RepositoryPackage mainPackage, + required bool isExample, + }) { + final List errors = []; + + if (_containsTemplateFlutterBoilerplate(readmeLines)) { + printError('${indentation}The boilerplate section about getting started ' + 'with Flutter should not be left in.'); + errors.add('Contains template boilerplate'); + } + + // Enforce a repository-standard message in implementation plugin examples, + // since they aren't typical examples, which has been a source of + // confusion for plugin clients who find them. + if (isExample && mainPackage.isPlatformImplementation) { + if (_containsExampleBoilerplate(readmeLines)) { + printError('${indentation}The boilerplate should not be left in for a ' + "federated plugin implementation package's example."); + errors.add('Contains template boilerplate'); + } + if (!_containsImplementationExampleExplanation(readmeLines)) { + printError('${indentation}The example README for a platform ' + 'implementation package should warn readers about its intended ' + 'use. Please copy the example README from another implementation ' + 'package in this repository.'); + errors.add('Missing implementation package example warning'); + } + } + + return errors; + } + + /// Returns true if the README still has unwanted parts of the boilerplate + /// from the `flutter create` templates. + bool _containsTemplateFlutterBoilerplate(List readmeLines) { return readmeLines.any((String line) => line.contains('For help getting started with Flutter')); } + + /// Returns true if the README still has the generic description of an + /// example from the `flutter create` templates. + bool _containsExampleBoilerplate(List readmeLines) { + return readmeLines + .any((String line) => line.contains('Demonstrates how to use the')); + } + + /// Returns true if the README contains the repository-standard explanation of + /// the purpose of a federated plugin implementation's example. + bool _containsImplementationExampleExplanation(List readmeLines) { + return readmeLines.contains('# Platform Implementation Test App') && + readmeLines + .any((String line) => line.contains('This is a test app for')); + } } diff --git a/script/tool/test/readme_check_command_test.dart b/script/tool/test/readme_check_command_test.dart index 37224fddc56b..eb2b6c8e7512 100644 --- a/script/tool/test/readme_check_command_test.dart +++ b/script/tool/test/readme_check_command_test.dart @@ -176,6 +176,147 @@ samples, guidance on mobile development, and a full API reference. ); }); + test( + 'fails when a plugin implementation package example README has the ' + 'template boilerplate', () async { + final RepositoryPackage package = createFakePlugin( + 'a_plugin_ios', packagesDir.childDirectory('a_plugin')); + package.getExamples().first.readmeFile.writeAsStringSync(''' +# a_plugin_ios_example + +Demonstrates how to use the a_plugin_ios plugin. +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The boilerplate should not be left in for a federated plugin ' + "implementation package's example."), + contains('Contains template boilerplate'), + ]), + ); + }); + + test( + 'allows the template boilerplate in the example README for packages ' + 'other than plugin implementation packages', () async { + final RepositoryPackage package = createFakePlugin( + 'a_plugin', + packagesDir.childDirectory('a_plugin'), + platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline), + }, + ); + // Write a README with an OS support table so that the main README check + // passes. + package.readmeFile.writeAsStringSync(''' +# a_plugin + +| | Android | +|----------------|---------| +| **Support** | SDK 19+ | + +A great plugin. +'''); + package.getExamples().first.readmeFile.writeAsStringSync(''' +# a_plugin_example + +Demonstrates how to use the a_plugin plugin. +'''); + + final List output = + await runCapturingPrint(runner, ['readme-check']); + + expect( + output, + containsAll([ + contains(' Checking README.md...'), + contains(' Checking example/README.md...'), + ]), + ); + }); + + test( + 'fails when a plugin implementation package example README does not have ' + 'the repo-standard message', () async { + final RepositoryPackage package = createFakePlugin( + 'a_plugin_ios', packagesDir.childDirectory('a_plugin')); + package.getExamples().first.readmeFile.writeAsStringSync(''' +# a_plugin_ios_example + +Some random description. +'''); + + Error? commandError; + final List output = await runCapturingPrint( + runner, ['readme-check'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('The example README for a platform implementation package ' + 'should warn readers about its intended use. Please copy the ' + 'example README from another implementation package in this ' + 'repository.'), + contains('Missing implementation package example warning'), + ]), + ); + }); + + test('passes for a plugin implementation package with the expected content', + () async { + final RepositoryPackage package = createFakePlugin( + 'a_plugin', + packagesDir.childDirectory('a_plugin'), + platformSupport: { + platformAndroid: const PlatformDetails(PlatformSupport.inline), + }, + ); + // Write a README with an OS support table so that the main README check + // passes. + package.readmeFile.writeAsStringSync(''' +# a_plugin + +| | Android | +|----------------|---------| +| **Support** | SDK 19+ | + +A great plugin. +'''); + package.getExamples().first.readmeFile.writeAsStringSync(''' +# 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. +'''); + + final List output = + await runCapturingPrint(runner, ['readme-check']); + + expect( + output, + containsAll([ + contains(' Checking README.md...'), + contains(' Checking example/README.md...'), + ]), + ); + }); + test( 'fails when multi-example top-level example directory README still has ' 'application template boilerplate', () async { From 7c6c784c2bf51e99af25ad7a7c3bc2f7370e964d Mon Sep 17 00:00:00 2001 From: Alex Li Date: Fri, 23 Sep 2022 04:09:33 +0800 Subject: [PATCH 740/844] [video_player] Fix invalid link in CHANGELOG on pub.dev (#6446) --- packages/video_player/video_player/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 7a47cce54a6e..53cfabf31e78 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -18,7 +18,7 @@ ## 2.4.3 -* Fixes Android to correctly display videos recorded in landscapeRight (https://github.com/flutter/flutter/issues/60327). +* Fixes Android to correctly display videos recorded in landscapeRight ([#60327](https://github.com/flutter/flutter/issues/60327)). * Fixes order-dependent unit tests. ## 2.4.2 From 0177355a8dd9962ca65ada80ad995557784bf285 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Thu, 22 Sep 2022 18:50:45 -0400 Subject: [PATCH 741/844] [webview_flutter_android] Expose the Java InstanceManager (#6421) --- .../webview_flutter_android/CHANGELOG.md | 4 ++ .../webviewflutter/InstanceManager.java | 3 ++ .../webviewflutter/WebViewFlutterPlugin.java | 7 +++ .../webviewflutter/WebViewHostApiImpl.java | 5 +++ .../WebViewFlutterPluginTest.java | 45 +++++++++++++++++++ .../webview_flutter_android/pubspec.yaml | 2 +- 6 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewFlutterPluginTest.java diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 910d20dda6d1..b37ada2225f0 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.10.2 + +* Adds a getter to expose the Java InstanceManager. + ## 2.10.1 * Adds a method to the `WebView` wrapper to retrieve the X and Y positions simultaneously. diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java index 306dc20be47d..fefd577ee9b5 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/InstanceManager.java @@ -15,6 +15,9 @@ /** * Maintains instances used to communicate with the corresponding objects in Dart. * + *

Objects stored in this container are represented by an object in Dart that is also stored in + * an InstanceManager with the same identifier. + * *

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 diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java index 8db976a26937..fe7615c664a4 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java @@ -8,6 +8,7 @@ import android.os.Handler; import android.view.View; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; @@ -166,4 +167,10 @@ private void updateContext(Context context) { webViewHostApi.setContext(context); javaScriptChannelHostApi.setPlatformThreadHandler(new Handler(context.getMainLooper())); } + + /** Maintains instances used to communicate with the corresponding objects in Dart. */ + @Nullable + public InstanceManager getInstanceManager() { + return instanceManager; + } } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java index f257ace71b0d..778ad611d05f 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java @@ -511,4 +511,9 @@ public void setBackgroundColor(Long instanceId, Long color) { final WebView webView = (WebView) instanceManager.getInstance(instanceId); webView.setBackgroundColor(color.intValue()); } + + /** Maintains instances used to communicate with the corresponding WebView Dart object. */ + public InstanceManager getInstanceManager() { + return instanceManager; + } } diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewFlutterPluginTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewFlutterPluginTest.java new file mode 100644 index 000000000000..16dc6cf5de2b --- /dev/null +++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewFlutterPluginTest.java @@ -0,0 +1,45 @@ +// 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.webviewflutter; + +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.when; + +import android.content.Context; +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.platform.PlatformViewRegistry; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +public class WebViewFlutterPluginTest { + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock Context mockContext; + + @Mock BinaryMessenger mockBinaryMessenger; + + @Mock PlatformViewRegistry mockViewRegistry; + + @Mock FlutterPlugin.FlutterPluginBinding mockPluginBinding; + + @Test + public void getInstanceManagerAfterOnAttachedToEngine() { + final WebViewFlutterPlugin webViewFlutterPlugin = new WebViewFlutterPlugin(); + + when(mockPluginBinding.getApplicationContext()).thenReturn(mockContext); + when(mockPluginBinding.getPlatformViewRegistry()).thenReturn(mockViewRegistry); + when(mockPluginBinding.getBinaryMessenger()).thenReturn(mockBinaryMessenger); + + webViewFlutterPlugin.onAttachedToEngine(mockPluginBinding); + + assertNotNull(webViewFlutterPlugin.getInstanceManager()); + + webViewFlutterPlugin.onDetachedFromEngine(mockPluginBinding); + } +} diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index 62f4cc97aa06..f5f2e3c34e69 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/plugins/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: 2.10.1 +version: 2.10.2 environment: sdk: ">=2.14.0 <3.0.0" From ff5b28a8af065094ba6256df3fcd6325357c8883 Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Thu, 22 Sep 2022 15:52:25 -0700 Subject: [PATCH 742/844] [quick_actions]Migrate the plugin class to Swift, and remove custom modulemap (#6369) --- .../quick_actions_ios/CHANGELOG.md | 5 + .../RunnerTests/FLTQuickActionsPluginTests.m | 80 ++++++++------- .../FLTShortcutStateManagerTests.m | 1 - .../ios/Classes/FLTQuickActionsPlugin.h | 14 --- .../ios/Classes/FLTQuickActionsPlugin.m | 99 ------------------- .../FLTShortcutStateManager.h | 0 .../FLTQuickActionsPlugin_Test.h | 23 ----- .../ios/Classes/QuickActionsPlugin.modulemap | 11 --- .../ios/Classes/QuickActionsPlugin.swift | 90 +++++++++++++++++ .../ios/Classes/quick_actions_ios-umbrella.h | 10 -- .../ios/quick_actions_ios.podspec | 11 ++- .../quick_actions_ios/pubspec.yaml | 4 +- 12 files changed, 146 insertions(+), 202 deletions(-) delete mode 100644 packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.h delete mode 100644 packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m rename packages/quick_actions/quick_actions_ios/ios/Classes/{PrivateHeaders => }/FLTShortcutStateManager.h (100%) delete mode 100644 packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h delete mode 100644 packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.modulemap create mode 100644 packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.swift delete mode 100644 packages/quick_actions/quick_actions_ios/ios/Classes/quick_actions_ios-umbrella.h diff --git a/packages/quick_actions/quick_actions_ios/CHANGELOG.md b/packages/quick_actions/quick_actions_ios/CHANGELOG.md index 7334bbd6a632..e002bd70ac09 100644 --- a/packages/quick_actions/quick_actions_ios/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_ios/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.0.1 + +* Removes custom modulemap file with "Test" submodule and private headers for Swift migration. +* Migrates `FLTQuickActionsPlugin` class to Swift. + ## 1.0.0 * Updates version to 1.0 to reflect current status. diff --git a/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTQuickActionsPluginTests.m b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTQuickActionsPluginTests.m index b47f89848bbc..89651b573822 100644 --- a/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTQuickActionsPluginTests.m +++ b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTQuickActionsPluginTests.m @@ -4,7 +4,6 @@ @import Flutter; @import quick_actions_ios; -@import quick_actions_ios.Test; @import XCTest; #import @@ -26,9 +25,9 @@ - (void)testHandleMethodCall_setShortcutItems { FLTShortcutStateManager *mockShortcutStateManager = OCMClassMock([FLTShortcutStateManager class]); - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) - shortcutStateManager:mockShortcutStateManager]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) + shortcutStateManager:mockShortcutStateManager]; XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result block must be called."]; [plugin handleMethodCall:call @@ -45,9 +44,9 @@ - (void)testHandleMethodCall_clearShortcutItems { FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"clearShortcutItems" arguments:nil]; FLTShortcutStateManager *mockShortcutStateManager = OCMClassMock([FLTShortcutStateManager class]); - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) - shortcutStateManager:mockShortcutStateManager]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) + shortcutStateManager:mockShortcutStateManager]; XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result block must be called."]; [plugin handleMethodCall:call @@ -63,9 +62,9 @@ - (void)testHandleMethodCall_getLaunchAction { FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"getLaunchAction" arguments:nil]; - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) - shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) + shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result block must be called."]; [plugin handleMethodCall:call @@ -79,9 +78,9 @@ - (void)testHandleMethodCall_getLaunchAction { - (void)testHandleMethodCall_nonExistMethods { FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"nonExist" arguments:nil]; - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) - shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) + shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result must be called."]; [plugin @@ -97,9 +96,9 @@ - (void)testHandleMethodCall_nonExistMethods { - (void)testApplicationPerformActionForShortcutItem { id mockChannel = OCMClassMock([FlutterMethodChannel class]); - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:mockChannel - shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:mockChannel + shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; UIApplicationShortcutItem *item = [[UIApplicationShortcutItem alloc] initWithType:@"SearchTheThing" @@ -118,9 +117,9 @@ - (void)testApplicationPerformActionForShortcutItem { - (void)testApplicationDidFinishLaunchingWithOptions_launchWithShortcut { id mockShortcutStateManager = OCMClassMock([FLTShortcutStateManager class]); - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) - shortcutStateManager:mockShortcutStateManager]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) + shortcutStateManager:mockShortcutStateManager]; UIApplicationShortcutItem *item = [[UIApplicationShortcutItem alloc] initWithType:@"SearchTheThing" @@ -138,9 +137,9 @@ - (void)testApplicationDidFinishLaunchingWithOptions_launchWithShortcut { } - (void)testApplicationDidFinishLaunchingWithOptions_launchWithoutShortcut { - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) - shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:OCMClassMock([FlutterMethodChannel class]) + shortcutStateManager:OCMClassMock([FLTShortcutStateManager class])]; BOOL launchResult = [plugin application:[UIApplication sharedApplication] didFinishLaunchingWithOptions:@{}]; XCTAssertTrue(launchResult, @@ -150,11 +149,14 @@ - (void)testApplicationDidFinishLaunchingWithOptions_launchWithoutShortcut { - (void)testApplicationDidBecomeActive_launchWithoutShortcut { id mockChannel = OCMClassMock([FlutterMethodChannel class]); id mockShortcutStateManager = OCMClassMock([FLTShortcutStateManager class]); - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:mockChannel - shortcutStateManager:mockShortcutStateManager]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:mockChannel + shortcutStateManager:mockShortcutStateManager]; - [plugin application:[UIApplication sharedApplication] didFinishLaunchingWithOptions:@{}]; + BOOL launchResult = [plugin application:[UIApplication sharedApplication] + didFinishLaunchingWithOptions:@{}]; + XCTAssertTrue(launchResult, + @"didFinishLaunchingWithOptions must return true if not launched from shortcut."); [plugin applicationDidBecomeActive:[UIApplication sharedApplication]]; OCMVerify(never(), [mockChannel invokeMethod:OCMOCK_ANY arguments:OCMOCK_ANY]); } @@ -162,9 +164,9 @@ - (void)testApplicationDidBecomeActive_launchWithoutShortcut { - (void)testApplicationDidBecomeActive_launchWithShortcut { id mockChannel = OCMClassMock([FlutterMethodChannel class]); id mockShortcutStateManager = OCMClassMock([FLTShortcutStateManager class]); - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:mockChannel - shortcutStateManager:mockShortcutStateManager]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:mockChannel + shortcutStateManager:mockShortcutStateManager]; UIApplicationShortcutItem *item = [[UIApplicationShortcutItem alloc] initWithType:@"SearchTheThing" @@ -173,9 +175,10 @@ - (void)testApplicationDidBecomeActive_launchWithShortcut { icon:[UIApplicationShortcutIcon iconWithTemplateImageName:@"search_the_thing.png"] userInfo:nil]; - [plugin application:[UIApplication sharedApplication] - didFinishLaunchingWithOptions:@{UIApplicationLaunchOptionsShortcutItemKey : item}]; - + BOOL launchResult = [plugin application:[UIApplication sharedApplication] + didFinishLaunchingWithOptions:@{UIApplicationLaunchOptionsShortcutItemKey : item}]; + XCTAssertFalse(launchResult, + @"didFinishLaunchingWithOptions must return false if launched from shortcut."); [plugin applicationDidBecomeActive:[UIApplication sharedApplication]]; OCMVerify([mockChannel invokeMethod:@"launch" arguments:item.type]); } @@ -183,9 +186,9 @@ - (void)testApplicationDidBecomeActive_launchWithShortcut { - (void)testApplicationDidBecomeActive_launchWithShortcut_becomeActiveTwice { id mockChannel = OCMClassMock([FlutterMethodChannel class]); id mockShortcutStateManager = OCMClassMock([FLTShortcutStateManager class]); - FLTQuickActionsPlugin *plugin = - [[FLTQuickActionsPlugin alloc] initWithChannel:mockChannel - shortcutStateManager:mockShortcutStateManager]; + QuickActionsPlugin *plugin = + [[QuickActionsPlugin alloc] initWithChannel:mockChannel + shortcutStateManager:mockShortcutStateManager]; UIApplicationShortcutItem *item = [[UIApplicationShortcutItem alloc] initWithType:@"SearchTheThing" @@ -194,9 +197,10 @@ - (void)testApplicationDidBecomeActive_launchWithShortcut_becomeActiveTwice { icon:[UIApplicationShortcutIcon iconWithTemplateImageName:@"search_the_thing.png"] userInfo:nil]; - [plugin application:[UIApplication sharedApplication] - didFinishLaunchingWithOptions:@{UIApplicationLaunchOptionsShortcutItemKey : item}]; - + BOOL launchResult = [plugin application:[UIApplication sharedApplication] + didFinishLaunchingWithOptions:@{UIApplicationLaunchOptionsShortcutItemKey : item}]; + XCTAssertFalse(launchResult, + @"didFinishLaunchingWithOptions must return false if launched from shortcut."); [plugin applicationDidBecomeActive:[UIApplication sharedApplication]]; [plugin applicationDidBecomeActive:[UIApplication sharedApplication]]; // shortcut should only be handled once per launch. diff --git a/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTShortcutStateManagerTests.m b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTShortcutStateManagerTests.m index f5b8b3405fc8..96fbf229e566 100644 --- a/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTShortcutStateManagerTests.m +++ b/packages/quick_actions/quick_actions_ios/example/ios/RunnerTests/FLTShortcutStateManagerTests.m @@ -3,7 +3,6 @@ // found in the LICENSE file. @import quick_actions_ios; -@import quick_actions_ios.Test; @import XCTest; #import diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.h b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.h deleted file mode 100644 index 63f615440dea..000000000000 --- a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.h +++ /dev/null @@ -1,14 +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; - -@interface FLTQuickActionsPlugin : NSObject - -/// Unavailable. -- (instancetype)init NS_UNAVAILABLE; - -/// Unavailable. -+ (instancetype)new NS_UNAVAILABLE; -@end diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m deleted file mode 100644 index fb8994322a9b..000000000000 --- a/packages/quick_actions/quick_actions_ios/ios/Classes/FLTQuickActionsPlugin.m +++ /dev/null @@ -1,99 +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 "FLTQuickActionsPlugin.h" -#import "FLTQuickActionsPlugin_Test.h" -#import "FLTShortcutStateManager.h" - -static NSString *const kChannelName = @"plugins.flutter.io/quick_actions_ios"; - -@interface FLTQuickActionsPlugin () -@property(nonatomic, strong) FlutterMethodChannel *channel; -/// The type of the shortcut item selected when launching the app. -@property(nonatomic, strong, nullable) NSString *launchingShortcutType; -@property(nonatomic, strong) FLTShortcutStateManager *shortcutStateManager; -@end - -@implementation FLTQuickActionsPlugin - -+ (void)registerWithRegistrar:(NSObject *)registrar { - FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:kChannelName - binaryMessenger:[registrar messenger]]; - FLTQuickActionsPlugin *instance = - [[FLTQuickActionsPlugin alloc] initWithChannel:channel - shortcutStateManager:[[FLTShortcutStateManager alloc] init]]; - [registrar addMethodCallDelegate:instance channel:channel]; - [registrar addApplicationDelegate:instance]; -} - -- (instancetype)initWithChannel:(FlutterMethodChannel *)channel - shortcutStateManager:(FLTShortcutStateManager *)shortcutStateManager { - if ((self = [super init])) { - _channel = channel; - _shortcutStateManager = shortcutStateManager; - } - return self; -} - -- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { - if ([call.method isEqualToString:@"setShortcutItems"]) { - [self.shortcutStateManager setShortcutItems:call.arguments]; - result(nil); - } else if ([call.method isEqualToString:@"clearShortcutItems"]) { - [self.shortcutStateManager setShortcutItems:@[]]; - result(nil); - } else if ([call.method isEqualToString:@"getLaunchAction"]) { - result(nil); - } else { - result(FlutterMethodNotImplemented); - } -} - -- (void)dealloc { - [_channel setMethodCallHandler:nil]; - _channel = nil; -} - -- (BOOL)application:(UIApplication *)application - performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem - completionHandler:(void (^)(BOOL succeeded))completionHandler - API_AVAILABLE(ios(9.0)) { - [self handleShortcut:shortcutItem.type]; - return YES; -} - -- (BOOL)application:(UIApplication *)application - didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - UIApplicationShortcutItem *shortcutItem = - launchOptions[UIApplicationLaunchOptionsShortcutItemKey]; - if (shortcutItem) { - // Keep hold of the shortcut type and handle it in the - // `applicationDidBecomeActure:` method once the Dart MethodChannel - // is initialized. - self.launchingShortcutType = shortcutItem.type; - - // Return NO to indicate we handled the quick action to ensure - // the `application:performActionFor:` method is not called (as - // per Apple's documentation: - // https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622935-application?language=objc). - return NO; - } - return YES; -} - -- (void)applicationDidBecomeActive:(UIApplication *)application { - if (self.launchingShortcutType) { - [self handleShortcut:self.launchingShortcutType]; - self.launchingShortcutType = nil; - } -} - -#pragma mark Private functions - -- (void)handleShortcut:(NSString *)shortcut { - [self.channel invokeMethod:@"launch" arguments:shortcut]; -} - -@end diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTShortcutStateManager.h b/packages/quick_actions/quick_actions_ios/ios/Classes/FLTShortcutStateManager.h similarity index 100% rename from packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTShortcutStateManager.h rename to packages/quick_actions/quick_actions_ios/ios/Classes/FLTShortcutStateManager.h diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h b/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h deleted file mode 100644 index 514d0633f198..000000000000 --- a/packages/quick_actions/quick_actions_ios/ios/Classes/PrivateHeaders/FLTQuickActionsPlugin_Test.h +++ /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 Flutter; -#import "FLTShortcutStateManager.h" - -NS_ASSUME_NONNULL_BEGIN - -/// APIs exposed for unit tests. -@interface FLTQuickActionsPlugin () - -/// Initializes a FLTQuickActionsPlugin with the given method channel. -/// API exposed for unit tests. -/// @param channel A method channel. -/// @param shortcutStateManager An FLTShortcutStateManager that manages shortcut related states. -/// @return The initialized FLTQuickActionsPlugin. -- (instancetype)initWithChannel:(FlutterMethodChannel *)channel - shortcutStateManager:(FLTShortcutStateManager *)shortcutStateManager; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.modulemap b/packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.modulemap deleted file mode 100644 index 3f7d7ce08203..000000000000 --- a/packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.modulemap +++ /dev/null @@ -1,11 +0,0 @@ -framework module quick_actions_ios { - umbrella header "quick_actions_ios-umbrella.h" - - export * - module * { export * } - - explicit module Test { - header "FLTQuickActionsPlugin_Test.h" - header "FLTShortcutStateManager.h" - } -} diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.swift b/packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.swift new file mode 100644 index 000000000000..26d6d20f8c02 --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/ios/Classes/QuickActionsPlugin.swift @@ -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 Flutter + +public final class QuickActionsPlugin: NSObject, FlutterPlugin { + + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel( + name: "plugins.flutter.io/quick_actions_ios", + binaryMessenger: registrar.messenger()) + let instance = QuickActionsPlugin(channel: channel) + registrar.addMethodCallDelegate(instance, channel: channel) + registrar.addApplicationDelegate(instance) + } + + private let channel: FlutterMethodChannel + private let shortcutStateManager: FLTShortcutStateManager + /// The type of the shortcut item selected when launching the app. + private var launchingShortcutType: String? = nil + + // TODO: (hellohuanlin) remove `@objc` attribute and make it non-public after migrating tests to Swift. + @objc + public init( + channel: FlutterMethodChannel, + shortcutStateManager: FLTShortcutStateManager = FLTShortcutStateManager() + ) { + self.channel = channel + self.shortcutStateManager = shortcutStateManager + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "setShortcutItems": + // `arguments` must be an array of dictionaries + let items = call.arguments as! [[String: Any]] + shortcutStateManager.setShortcutItems(items) + result(nil) + case "clearShortcutItems": + shortcutStateManager.setShortcutItems([]) + result(nil) + case "getLaunchAction": + result(nil) + case _: + result(FlutterMethodNotImplemented) + } + } + + public func application( + _ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, + completionHandler: @escaping (Bool) -> Void + ) -> Bool { + handleShortcut(shortcutItem.type) + return true + } + + public func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [AnyHashable: Any] = [:] + ) -> Bool { + if let shortcutItem = launchOptions[UIApplication.LaunchOptionsKey.shortcutItem] + as? UIApplicationShortcutItem + { + // Keep hold of the shortcut type and handle it in the + // `applicationDidBecomeActive:` method once the Dart MethodChannel + // is initialized. + launchingShortcutType = shortcutItem.type + + // Return false to indicate we handled the quick action to ensure + // the `application:performActionFor:` method is not called (as + // per Apple's documentation: + // https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622935-application). + return false + } + return true + } + + public func applicationDidBecomeActive(_ application: UIApplication) { + if let shortcutType = launchingShortcutType { + handleShortcut(shortcutType) + launchingShortcutType = nil + } + } + + private func handleShortcut(_ shortcut: String) { + channel.invokeMethod("launch", arguments: shortcut) + } + +} diff --git a/packages/quick_actions/quick_actions_ios/ios/Classes/quick_actions_ios-umbrella.h b/packages/quick_actions/quick_actions_ios/ios/Classes/quick_actions_ios-umbrella.h deleted file mode 100644 index d099a0411cf0..000000000000 --- a/packages/quick_actions/quick_actions_ios/ios/Classes/quick_actions_ios-umbrella.h +++ /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. - -#import - -FOUNDATION_EXPORT double quickActionsIOSVersionNumber; -FOUNDATION_EXPORT const unsigned char quickActionsIOSVersionString[]; - -#import diff --git a/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios.podspec b/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios.podspec index 68eaa6ff7dc9..d8090caa8ef6 100644 --- a/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios.podspec +++ b/packages/quick_actions/quick_actions_ios/ios/quick_actions_ios.podspec @@ -14,11 +14,14 @@ Downloaded by pub (not CocoaPods). s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } s.source = { :http => 'https://github.com/flutter/plugins/tree/main/packages/quick_actions' } s.documentation_url = 'https://pub.dev/packages/quick_actions' - s.source_files = 'Classes/**/*.{h,m}' - s.public_header_files = 'Classes/*.h' - s.private_header_files = 'Classes/PrivateHeaders/*.h' + s.swift_version = '5.0' + s.source_files = 'Classes/**/*.{h,m,swift}' + s.xcconfig = { + 'LIBRARY_SEARCH_PATHS' => '$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)/ $(SDKROOT)/usr/lib/swift', + 'LD_RUNPATH_SEARCH_PATHS' => '/usr/lib/swift', + } + s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' s.platform = :ios, '9.0' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } - s.module_map = 'Classes/QuickActionsPlugin.modulemap' end diff --git a/packages/quick_actions/quick_actions_ios/pubspec.yaml b/packages/quick_actions/quick_actions_ios/pubspec.yaml index 82fd25f06b2c..f01ae4aed9c3 100644 --- a/packages/quick_actions/quick_actions_ios/pubspec.yaml +++ b/packages/quick_actions/quick_actions_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: quick_actions_ios description: An implementation for the iOS platform of the Flutter `quick_actions` plugin. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 1.0.0 +version: 1.0.1 environment: sdk: ">=2.15.0 <3.0.0" @@ -13,7 +13,7 @@ flutter: implements: quick_actions platforms: ios: - pluginClass: FLTQuickActionsPlugin + pluginClass: QuickActionsPlugin dartPluginClass: QuickActionsIos dependencies: From 8a8e67a7ec86ddf7d917442fe7cc438797e101da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Sep 2022 00:01:10 +0000 Subject: [PATCH 743/844] [gh_actions]: Bump github/codeql-action from 2.1.22 to 2.1.24 (#6451) --- .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 3bcfdad7fb6b..6650853754af 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -50,6 +50,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@b398f525a5587552e573b247ac661067fafa920b + uses: github/codeql-action/upload-sarif@904260d7d935dff982205cbdb42025ce30b7a34f with: sarif_file: results.sarif From 88dc5e33a327787a89b0e014425e8f2c528cfe10 Mon Sep 17 00:00:00 2001 From: Jami Couch Date: Thu, 22 Sep 2022 19:02:31 -0500 Subject: [PATCH 744/844] [google_sign_in] Fix issue obtaining serverAuthCode on Android and add forceCodeForRefreshToken parameter (#3356) --- AUTHORS | 1 + packages/google_sign_in/google_sign_in/AUTHORS | 1 + packages/google_sign_in/google_sign_in/CHANGELOG.md | 4 +++- .../google_sign_in/lib/google_sign_in.dart | 8 ++++++++ packages/google_sign_in/google_sign_in/pubspec.yaml | 6 +++--- .../google_sign_in/test/google_sign_in_test.dart | 10 ++++++++++ 6 files changed, 26 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 31402c79d54a..3112c3b3fd05 100644 --- a/AUTHORS +++ b/AUTHORS @@ -68,3 +68,4 @@ Daniel Roek TheOneWithTheBraid Rulong Chen(陈汝龙) Hwanseok Kang +Twin Sun, LLC diff --git a/packages/google_sign_in/google_sign_in/AUTHORS b/packages/google_sign_in/google_sign_in/AUTHORS index 493a0b4ef9c2..35d24a5ae0b5 100644 --- a/packages/google_sign_in/google_sign_in/AUTHORS +++ b/packages/google_sign_in/google_sign_in/AUTHORS @@ -64,3 +64,4 @@ Aleksandr Yurkovskiy Anton Borries Alex Li Rahul Raj <64.rahulraj@gmail.com> +Twin Sun, LLC diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md index 286365ef9601..93497841fbd5 100644 --- a/packages/google_sign_in/google_sign_in/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md @@ -1,6 +1,8 @@ -## NEXT +## 5.4.2 * Updates minimum Flutter version to 2.10. +* Adds override for `GoogleSignInPlatform.initWithParams`. +* Fixes tests to recognize new default `forceCodeForRefreshToken` request attribute. ## 5.4.1 diff --git a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart index 135c422adfb5..3ae022306fe6 100644 --- a/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart +++ b/packages/google_sign_in/google_sign_in/lib/google_sign_in.dart @@ -179,12 +179,16 @@ class GoogleSignIn { /// The [hostedDomain] argument specifies a hosted domain restriction. By /// setting this, sign in will be restricted to accounts of the user in the /// specified domain. By default, the list of accounts will not be restricted. + /// + /// The [forceCodeForRefreshToken] is used on Android to ensure the authentication + /// code can be exchanged for a refresh token after the first request. GoogleSignIn({ this.signInOption = SignInOption.standard, this.scopes = const [], this.hostedDomain, this.clientId, this.serverClientId, + this.forceCodeForRefreshToken = false, }); /// Factory for creating default sign in user experience. @@ -250,6 +254,9 @@ class GoogleSignIn { /// server. final String? serverClientId; + /// Force the authorization code to be valid for a refresh token every time. Only needed on Android. + final bool forceCodeForRefreshToken; + final StreamController _currentUserController = StreamController.broadcast(); @@ -286,6 +293,7 @@ class GoogleSignIn { hostedDomain: hostedDomain, clientId: clientId, serverClientId: serverClientId, + forceCodeForRefreshToken: forceCodeForRefreshToken, )) ..catchError((dynamic _) { // Invalidate initialization if it errors out. diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml index 50339902fe75..c32dee78468b 100644 --- a/packages/google_sign_in/google_sign_in/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/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 and iOS. repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 5.4.1 +version: 5.4.2 environment: @@ -23,8 +23,8 @@ flutter: dependencies: flutter: sdk: flutter - google_sign_in_android: ^6.0.0 - google_sign_in_ios: ^5.4.0 + google_sign_in_android: ^6.1.0 + google_sign_in_ios: ^5.5.0 google_sign_in_platform_interface: ^2.2.0 google_sign_in_web: ^0.10.0 diff --git a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart index b8676bda298e..b8a596b02065 100644 --- a/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart +++ b/packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart @@ -80,6 +80,16 @@ void main() { verify(mockPlatform.signIn()); }); + test('forceCodeForRefreshToken sent with init method call', () async { + final GoogleSignIn googleSignIn = + GoogleSignIn(forceCodeForRefreshToken: true); + + await googleSignIn.signIn(); + + _verifyInit(mockPlatform, forceCodeForRefreshToken: true); + verify(mockPlatform.signIn()); + }); + test('signOut', () async { final GoogleSignIn googleSignIn = GoogleSignIn(); From 446c6f7ff79c845c87b527a93264ca246eef3555 Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Thu, 22 Sep 2022 18:03:37 -0700 Subject: [PATCH 745/844] [video_player]fix ios 16 bug where encrypted video stream is not showing (#6442) --- .../video_player_avfoundation/CHANGELOG.md | 3 +- .../ios/RunnerTests/VideoPlayerTests.m | 43 +++++++++++++ .../ios/RunnerUITests/VideoPlayerUITests.m | 53 ++++++++++++++- .../example/lib/main.dart | 64 ++++++++++++++++++- .../ios/Classes/FLTVideoPlayerPlugin.m | 26 ++++++++ .../video_player_avfoundation/pubspec.yaml | 2 +- 6 files changed, 185 insertions(+), 6 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 3c9cc2d371fd..cc4411c8c651 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.3.6 +* Fixes a bug in iOS 16 where videos from protected live streams are not shown. * Updates minimum Flutter version to 2.10. * Fixes violations of new analysis option use_named_constants. * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m index 7decd04bd168..813fca2b8e7d 100644 --- a/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m @@ -11,6 +11,10 @@ @interface FLTVideoPlayer : NSObject @property(readonly, nonatomic) AVPlayer *player; +// This is to fix a bug (https://github.com/flutter/flutter/issues/111457) in iOS 16 with blank +// video for encrypted video streams. An invisible AVPlayerLayer is used to overwrite the +// protection of pixel buffers in those streams. +@property(readonly, nonatomic) AVPlayerLayer *playerLayer; @end @interface FLTVideoPlayerPlugin (Test) @@ -61,6 +65,45 @@ @interface VideoPlayerTests : XCTestCase @implementation VideoPlayerTests +- (void)testIOS16BugWithEncryptedVideoStream { + // This is to fix a bug (https://github.com/flutter/flutter/issues/111457) in iOS 16 with blank + // video for encrypted video streams. An invisible AVPlayerLayer is used to overwrite the + // protection of pixel buffers in those streams. + // Note that a better unit test is to validate that `copyPixelBuffer` API returns the pixel + // buffers as expected, which requires setting up the video player properly with a protected video + // stream (.m3u8 file). + NSObject *registry = + (NSObject *)[[UIApplication sharedApplication] delegate]; + NSObject *registrar = + [registry registrarForPlugin:@"testPlayerLayerWorkaround"]; + FLTVideoPlayerPlugin *videoPlayerPlugin = + [[FLTVideoPlayerPlugin alloc] initWithRegistrar:registrar]; + + FlutterError *error; + [videoPlayerPlugin initialize:&error]; + XCTAssertNil(error); + + FLTCreateMessage *create = [FLTCreateMessage + makeWithAsset:nil + uri:@"https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4" + packageName:nil + formatHint:nil + httpHeaders:@{}]; + FLTTextureMessage *textureMessage = [videoPlayerPlugin create:create error:&error]; + XCTAssertNil(error); + XCTAssertNotNil(textureMessage); + FLTVideoPlayer *player = videoPlayerPlugin.playersByTextureId[textureMessage.textureId]; + XCTAssertNotNil(player); + + if (@available(iOS 16.0, *)) { + XCTAssertNotNil(player.playerLayer, @"AVPlayerLayer should be present for iOS 16."); + XCTAssertNotNil(player.playerLayer.superlayer, + @"AVPlayerLayer should be added on screen for iOS 16."); + } else { + XCTAssertNil(player.playerLayer, @"AVPlayerLayer should not be present before iOS 16."); + } +} + - (void)testSeekToInvokesTextureFrameAvailableOnTextureRegistry { NSObject *mockTextureRegistry = OCMProtocolMock(@protocol(FlutterTextureRegistry)); diff --git a/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m b/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m index b9f0f16bb27b..531d4fdf213c 100644 --- a/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m +++ b/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m @@ -4,6 +4,7 @@ @import os.log; @import XCTest; +@import CoreGraphics; @interface VideoPlayerUITests : XCTestCase @property(nonatomic, strong) XCUIApplication *app; @@ -46,7 +47,7 @@ - (void)testPlayVideo { XCTAssertTrue(foundPlaybackSpeed5x); // Cycle through tabs. - for (NSString *tabName in @[ @"Asset", @"Remote" ]) { + for (NSString *tabName in @[ @"Asset mp4", @"Remote mp4" ]) { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"label BEGINSWITH %@", tabName]; XCUIElement *unselectedTab = [app.staticTexts elementMatchingPredicate:predicate]; XCTAssertTrue([unselectedTab waitForExistenceWithTimeout:30.0]); @@ -60,4 +61,54 @@ - (void)testPlayVideo { } } +- (void)testEncryptedVideoStream { + // This is to fix a bug (https://github.com/flutter/flutter/issues/111457) in iOS 16 with blank + // video for encrypted video streams. + + NSString *tabName = @"Remote enc m3u8"; + + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"label BEGINSWITH %@", tabName]; + XCUIElement *unselectedTab = [self.app.staticTexts elementMatchingPredicate:predicate]; + XCTAssertTrue([unselectedTab waitForExistenceWithTimeout:30.0]); + XCTAssertFalse(unselectedTab.isSelected); + [unselectedTab tap]; + + XCUIElement *selectedTab = [self.app.otherElements + elementMatchingPredicate:[NSPredicate predicateWithFormat:@"label BEGINSWITH %@", tabName]]; + XCTAssertTrue([selectedTab waitForExistenceWithTimeout:30.0]); + XCTAssertTrue(selectedTab.isSelected); + + // Wait until the video is loaded. + [NSThread sleepForTimeInterval:60]; + + NSMutableSet *frames = [NSMutableSet set]; + int numberOfFrames = 60; + for (int i = 0; i < numberOfFrames; i++) { + UIImage *image = self.app.screenshot.image; + + // Plugin CI does not support attaching screenshot. + // Convert the image to base64 encoded string, and print it out for debugging purpose. + // NSLog truncates long strings, so need to scale downn image. + CGSize smallerSize = CGSizeMake(100, 200); + UIGraphicsBeginImageContextWithOptions(smallerSize, NO, 0.0); + [image drawInRect:CGRectMake(0, 0, smallerSize.width, smallerSize.height)]; + UIImage *smallerImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + // 0.5 compression is good enough for debugging purpose. + NSData *imageData = UIImageJPEGRepresentation(smallerImage, 0.5); + NSString *imageString = [imageData base64EncodedStringWithOptions:0]; + NSLog(@"frame %d image data:\n%@", i, imageString); + + [frames addObject:imageString]; + + // The sample interval must NOT be the same as video length. + // Otherwise it would always result in the same frame. + [NSThread sleepForTimeInterval:1]; + } + + // At least 1 loading and 2 distinct frames (3 in total) to validate that the video is playing. + XCTAssert(frames.count >= 3, @"Must have at least 3 distinct frames."); +} + @end diff --git a/packages/video_player/video_player_avfoundation/example/lib/main.dart b/packages/video_player/video_player_avfoundation/example/lib/main.dart index bca4e291efff..d385fd0ee66a 100644 --- a/packages/video_player/video_player_avfoundation/example/lib/main.dart +++ b/packages/video_player/video_player_avfoundation/example/lib/main.dart @@ -20,7 +20,7 @@ class _App extends StatelessWidget { @override Widget build(BuildContext context) { return DefaultTabController( - length: 2, + length: 3, child: Scaffold( key: const ValueKey('home_page'), appBar: AppBar( @@ -30,15 +30,20 @@ class _App extends StatelessWidget { tabs: [ Tab( icon: Icon(Icons.cloud), - text: 'Remote', + text: 'Remote mp4', ), - Tab(icon: Icon(Icons.insert_drive_file), text: 'Asset'), + Tab( + icon: Icon(Icons.favorite), + text: 'Remote enc m3u8', + ), + Tab(icon: Icon(Icons.insert_drive_file), text: 'Asset mp4'), ], ), ), body: TabBarView( children: [ _BumbleBeeRemoteVideo(), + _BumbleBeeEncryptedLiveStream(), _ButterFlyAssetVideo(), ], ), @@ -156,6 +161,59 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { } } +class _BumbleBeeEncryptedLiveStream extends StatefulWidget { + @override + _BumbleBeeEncryptedLiveStreamState createState() => + _BumbleBeeEncryptedLiveStreamState(); +} + +class _BumbleBeeEncryptedLiveStreamState + extends State<_BumbleBeeEncryptedLiveStream> { + late MiniController _controller; + + @override + void initState() { + super.initState(); + _controller = MiniController.network( + 'https://flutter.github.io/assets-for-api-docs/assets/videos/hls/encrypted_bee.m3u8', + ); + + _controller.addListener(() { + setState(() {}); + }); + _controller.initialize(); + + _controller.play(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + children: [ + Container(padding: const EdgeInsets.only(top: 20.0)), + const Text('With remote encrypted m3u8'), + Container( + padding: const EdgeInsets.all(20), + child: _controller.value.isInitialized + ? AspectRatio( + aspectRatio: _controller.value.aspectRatio, + child: VideoPlayer(_controller), + ) + : const Text('loading...'), + ), + ], + ), + ); + } +} + class _ControlsOverlay extends StatelessWidget { const _ControlsOverlay({Key? key, required this.controller}) : super(key: key); diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m index a95779b1cbab..645c86d6eade 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m @@ -36,6 +36,8 @@ - (void)onDisplayLink:(CADisplayLink *)link { @interface FLTVideoPlayer : NSObject @property(readonly, nonatomic) AVPlayer *player; @property(readonly, nonatomic) AVPlayerItemVideoOutput *videoOutput; +/// An invisible player layer used to access the pixel buffers in protected video streams in iOS 16. +@property(readonly, nonatomic) AVPlayerLayer *playerLayer; @property(readonly, nonatomic) CADisplayLink *displayLink; @property(nonatomic) FlutterEventChannel *eventChannel; @property(nonatomic) FlutterEventSink eventSink; @@ -132,6 +134,19 @@ NS_INLINE CGFloat radiansToDegrees(CGFloat radians) { return degrees; }; +NS_INLINE UIViewController *rootViewController() API_AVAILABLE(ios(16.0)) { + for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) { + if ([scene isKindOfClass:UIWindowScene.class]) { + for (UIWindow *window in ((UIWindowScene *)scene).windows) { + if (window.isKeyWindow) { + return window.rootViewController; + } + } + } + } + return nil; +} + - (AVMutableVideoComposition *)getVideoCompositionWithTransform:(CGAffineTransform)transform withAsset:(AVAsset *)asset withVideoTrack:(AVAssetTrack *)videoTrack { @@ -227,6 +242,14 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem *)item _player = [AVPlayer playerWithPlayerItem:item]; _player.actionAtItemEnd = AVPlayerActionAtItemEndNone; + // This is to fix a bug (https://github.com/flutter/flutter/issues/111457) in iOS 16 with blank + // video for encrypted video streams. An invisible AVPlayerLayer is used to overwrite the + // protection of pixel buffers in those streams. + if (@available(iOS 16.0, *)) { + _playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player]; + [rootViewController().view.layer addSublayer:_playerLayer]; + } + [self createVideoOutputAndDisplayLink:frameUpdater]; [self addObservers:item]; @@ -458,6 +481,9 @@ - (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments /// so the channel is going to die or is already dead. - (void)disposeSansEventChannel { _disposed = YES; + if (@available(iOS 16.0, *)) { + [_playerLayer removeFromSuperlayer]; + } [_displayLink invalidate]; AVPlayerItem *currentItem = self.player.currentItem; [currentItem removeObserver:self forKeyPath:@"status"]; diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index 06042c34bad6..bd88ddf94876 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.5 +version: 2.3.6 environment: sdk: ">=2.14.0 <3.0.0" From 3ba81503e7f71c5896ee2c1645f225086c082704 Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Thu, 22 Sep 2022 19:24:41 -0700 Subject: [PATCH 746/844] [video_player]remove integration tests (#6471) --- .../ios/RunnerUITests/VideoPlayerUITests.m | 50 ------------------- 1 file changed, 50 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m b/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m index 531d4fdf213c..54c97030c3ae 100644 --- a/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m +++ b/packages/video_player/video_player_avfoundation/example/ios/RunnerUITests/VideoPlayerUITests.m @@ -61,54 +61,4 @@ - (void)testPlayVideo { } } -- (void)testEncryptedVideoStream { - // This is to fix a bug (https://github.com/flutter/flutter/issues/111457) in iOS 16 with blank - // video for encrypted video streams. - - NSString *tabName = @"Remote enc m3u8"; - - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"label BEGINSWITH %@", tabName]; - XCUIElement *unselectedTab = [self.app.staticTexts elementMatchingPredicate:predicate]; - XCTAssertTrue([unselectedTab waitForExistenceWithTimeout:30.0]); - XCTAssertFalse(unselectedTab.isSelected); - [unselectedTab tap]; - - XCUIElement *selectedTab = [self.app.otherElements - elementMatchingPredicate:[NSPredicate predicateWithFormat:@"label BEGINSWITH %@", tabName]]; - XCTAssertTrue([selectedTab waitForExistenceWithTimeout:30.0]); - XCTAssertTrue(selectedTab.isSelected); - - // Wait until the video is loaded. - [NSThread sleepForTimeInterval:60]; - - NSMutableSet *frames = [NSMutableSet set]; - int numberOfFrames = 60; - for (int i = 0; i < numberOfFrames; i++) { - UIImage *image = self.app.screenshot.image; - - // Plugin CI does not support attaching screenshot. - // Convert the image to base64 encoded string, and print it out for debugging purpose. - // NSLog truncates long strings, so need to scale downn image. - CGSize smallerSize = CGSizeMake(100, 200); - UIGraphicsBeginImageContextWithOptions(smallerSize, NO, 0.0); - [image drawInRect:CGRectMake(0, 0, smallerSize.width, smallerSize.height)]; - UIImage *smallerImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - // 0.5 compression is good enough for debugging purpose. - NSData *imageData = UIImageJPEGRepresentation(smallerImage, 0.5); - NSString *imageString = [imageData base64EncodedStringWithOptions:0]; - NSLog(@"frame %d image data:\n%@", i, imageString); - - [frames addObject:imageString]; - - // The sample interval must NOT be the same as video length. - // Otherwise it would always result in the same frame. - [NSThread sleepForTimeInterval:1]; - } - - // At least 1 loading and 2 distinct frames (3 in total) to validate that the video is playing. - XCTAssert(frames.count >= 3, @"Must have at least 3 distinct frames."); -} - @end From 97d48f4cb610b4f1346e38d5a9919de145be20e6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 23 Sep 2022 11:50:05 -0400 Subject: [PATCH 747/844] Roll Flutter from cf01ecd19e61 to b31f41bd1aa6 (50 revisions) (#6475) --- .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 427e69ccf7d7..6db47f1c12cd 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -cf01ecd19e61b436c6d7f9c0f6d57ef84aeb0257 +b31f41bd1aa68531d30854937ac004d2f4ab2361 From 376a64fe7d27c18d6cf9c9750f5a3c4067b0e558 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 23 Sep 2022 17:38:04 -0400 Subject: [PATCH 748/844] [tools] Add 'run_tests.sh' to the dev-only list (#6474) --- script/tool/CHANGELOG.md | 3 +- .../lib/src/common/package_state_utils.dart | 3 ++ script/tool/pubspec.yaml | 2 +- .../test/common/package_state_utils_test.dart | 1 + .../tool/test/version_check_command_test.dart | 32 +++++++++++++++++++ 5 files changed, 39 insertions(+), 2 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index f6dfb0e58266..2417d0fa833c 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.10.0+1 +* Recognizes `run_test.sh` as a developer-only file in `version-check`. * Adds `readme-check` validation that the example/README.md for a federated plugin's implementation packages has a warning about the intended use of the example instead of the template boilerplate. diff --git a/script/tool/lib/src/common/package_state_utils.dart b/script/tool/lib/src/common/package_state_utils.dart index 1cff65bb6b0c..c9465876f290 100644 --- a/script/tool/lib/src/common/package_state_utils.dart +++ b/script/tool/lib/src/common/package_state_utils.dart @@ -168,6 +168,9 @@ Future _isDevChange(List pathComponents, // The top-level "tool" directory is for non-client-facing utility // code, such as test scripts. pathComponents.first == 'tool' || + // Entry point for the 'custom-test' command, which is only for CI and + // local testing. + pathComponents.first == 'run_tests.sh' || // Ignoring lints doesn't affect clients. pathComponents.contains('lint-baseline.xml') || await _isGradleTestDependencyChange(pathComponents, diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index e51c7433aa5c..9b9d73288552 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.10.0 +version: 0.10.0+1 dependencies: args: ^2.1.0 diff --git a/script/tool/test/common/package_state_utils_test.dart b/script/tool/test/common/package_state_utils_test.dart index 63ac1802e70c..c20951876e39 100644 --- a/script/tool/test/common/package_state_utils_test.dart +++ b/script/tool/test/common/package_state_utils_test.dart @@ -66,6 +66,7 @@ void main() { 'packages/a_plugin/example/ios/RunnerTests/Foo.m', 'packages/a_plugin/example/ios/RunnerUITests/info.plist', 'packages/a_plugin/tool/a_development_tool.dart', + 'packages/a_plugin/run_tests.sh', 'packages/a_plugin/CHANGELOG.md', ]; diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart index 2ff48d618258..0e94712e9ef0 100644 --- a/script/tool/test/version_check_command_test.dart +++ b/script/tool/test/version_check_command_test.dart @@ -1059,6 +1059,38 @@ packages/plugin/android/build.gradle ]), ); }); + + test('allows missing CHANGELOG and version change for dev-only changes', + () async { + final RepositoryPackage plugin = + createFakePlugin('plugin', packagesDir, version: '1.0.0'); + + const String changelog = ''' +## 1.0.0 +* Some changes. +'''; + plugin.changelogFile.writeAsStringSync(changelog); + processRunner.mockProcessesForExecutable['git-show'] = [ + MockProcess(stdout: 'version: 1.0.0'), + ]; + processRunner.mockProcessesForExecutable['git-diff'] = [ + // File list. + MockProcess(stdout: ''' +packages/plugin/tool/run_tests.dart +packages/plugin/run_tests.sh +'''), + ]; + + final List output = + await _runWithMissingChangeDetection([]); + + expect( + output, + containsAllInOrder([ + contains('Running for plugin'), + ]), + ); + }); }); test('allows valid against pub', () async { From b4e53b337c6dbfb1e606f77184a0f0ead79a2d5e Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 24 Sep 2022 11:50:35 -0400 Subject: [PATCH 749/844] Roll Flutter from b31f41bd1aa6 to 07665fba77bc (22 revisions) (#6479) --- .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 6db47f1c12cd..f3d11c356984 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -b31f41bd1aa68531d30854937ac004d2f4ab2361 +07665fba77bc6add0c729ea3c89e6f71e18f7110 From 45d6e0df271064d270bdd3b69053d3f719b51509 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 25 Sep 2022 11:50:39 -0400 Subject: [PATCH 750/844] Roll Flutter from 07665fba77bc to 64f84f6a47d2 (4 revisions) (#6480) --- .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 f3d11c356984..886179e55e79 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -07665fba77bc6add0c729ea3c89e6f71e18f7110 +64f84f6a47d276e4d79cb10c203adbddeeeae336 From fe3b542e5528a7d9bc1d8014885b55277d30caee Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Mon, 26 Sep 2022 05:36:03 -0700 Subject: [PATCH 751/844] [camera] Add CameraSelector class to CameraX plugin (#6348) --- .../camera_android_camerax/CHANGELOG.md | 1 + .../android/build.gradle | 2 +- .../camerax/CameraAndroidCameraxPlugin.java | 2 + .../camerax/CameraSelectorFlutterApiImpl.java | 23 +++ .../camerax/CameraSelectorHostApiImpl.java | 67 +++++++ .../flutter/plugins/camerax/CameraXProxy.java | 13 ++ .../camerax/GeneratedCameraXLibrary.java | 124 ++++++++++++ .../plugins/camerax/InstanceManager.java | 5 + .../plugins/camerax/CameraSelectorTest.java | 97 ++++++++++ ...roid_camera_camerax_flutter_api_impls.dart | 8 + .../lib/src/camera_info.dart | 25 +-- .../lib/src/camera_selector.dart | 177 ++++++++++++++++++ .../lib/src/camerax_library.pigeon.dart | 105 +++++++++++ .../pigeons/camerax_library.dart | 12 ++ .../test/camera_selector_test.dart | 121 ++++++++++++ .../test/camera_selector_test.mocks.dart | 38 ++++ .../test/test_camerax_library.pigeon.dart | 58 ++++++ 17 files changed, 861 insertions(+), 17 deletions(-) create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraSelectorFlutterApiImpl.java create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraSelectorHostApiImpl.java create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java create mode 100644 packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraSelectorTest.java create mode 100644 packages/camera/camera_android_camerax/lib/src/camera_selector.dart create mode 100644 packages/camera/camera_android_camerax/test/camera_selector_test.dart create mode 100644 packages/camera/camera_android_camerax/test/camera_selector_test.mocks.dart diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 0f305eb8b4fd..2bdfa594b3be 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -2,3 +2,4 @@ * Creates camera_android_camerax plugin for development. * Adds CameraInfo class and removes unnecessary code from plugin. +* Adds CameraSelector class. diff --git a/packages/camera/camera_android_camerax/android/build.gradle b/packages/camera/camera_android_camerax/android/build.gradle index 5e9a9a8ec106..1772afebe429 100644 --- a/packages/camera/camera_android_camerax/android/build.gradle +++ b/packages/camera/camera_android_camerax/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.2.2' + classpath 'com.android.tools.build:gradle:7.3.0' } } 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 029a6dc4c8b7..bb5756a7e3b9 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 @@ -37,6 +37,8 @@ void setUp(BinaryMessenger binaryMessenger, Context context) { binaryMessenger, new CameraInfoHostApiImpl(instanceManager)); GeneratedCameraXLibrary.JavaObjectHostApi.setup( binaryMessenger, new JavaObjectHostApiImpl(instanceManager)); + GeneratedCameraXLibrary.CameraSelectorHostApi.setup( + binaryMessenger, new CameraSelectorHostApiImpl(binaryMessenger, instanceManager)); } @Override diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraSelectorFlutterApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraSelectorFlutterApiImpl.java new file mode 100644 index 000000000000..6ca3782d8b59 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraSelectorFlutterApiImpl.java @@ -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. + +package io.flutter.plugins.camerax; + +import androidx.camera.core.CameraSelector; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraSelectorFlutterApi; + +public class CameraSelectorFlutterApiImpl extends CameraSelectorFlutterApi { + private final InstanceManager instanceManager; + + public CameraSelectorFlutterApiImpl( + BinaryMessenger binaryMessenger, InstanceManager instanceManager) { + super(binaryMessenger); + this.instanceManager = instanceManager; + } + + void create(CameraSelector cameraSelector, Long lensFacing, Reply reply) { + create(instanceManager.addHostCreatedInstance(cameraSelector), lensFacing, reply); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraSelectorHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraSelectorHostApiImpl.java new file mode 100644 index 000000000000..9c559a72e63c --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraSelectorHostApiImpl.java @@ -0,0 +1,67 @@ +// 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 androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import androidx.camera.core.CameraInfo; +import androidx.camera.core.CameraSelector; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraSelectorHostApi; +import java.util.ArrayList; +import java.util.List; + +public class CameraSelectorHostApiImpl implements CameraSelectorHostApi { + private final BinaryMessenger binaryMessenger; + private final InstanceManager instanceManager; + + @VisibleForTesting public CameraXProxy cameraXProxy = new CameraXProxy(); + + public CameraSelectorHostApiImpl( + BinaryMessenger binaryMessenger, InstanceManager instanceManager) { + this.binaryMessenger = binaryMessenger; + this.instanceManager = instanceManager; + } + + @Override + public void create(@NonNull Long identifier, Long lensFacing) { + CameraSelector.Builder cameraSelectorBuilder = cameraXProxy.createCameraSelectorBuilder(); + CameraSelector cameraSelector; + + if (lensFacing != null) { + cameraSelector = cameraSelectorBuilder.requireLensFacing(Math.toIntExact(lensFacing)).build(); + } else { + cameraSelector = cameraSelectorBuilder.build(); + } + + instanceManager.addDartCreatedInstance(cameraSelector, identifier); + } + + @Override + public List filter(@NonNull Long identifier, @NonNull List cameraInfoIds) { + CameraSelector cameraSelector = (CameraSelector) instanceManager.getInstance(identifier); + List cameraInfosForFilter = new ArrayList(); + + for (Number cameraInfoAsNumber : cameraInfoIds) { + Long cameraInfoId = cameraInfoAsNumber.longValue(); + + CameraInfo cameraInfo = (CameraInfo) instanceManager.getInstance(cameraInfoId); + cameraInfosForFilter.add(cameraInfo); + } + + List filteredCameraInfos = cameraSelector.filter(cameraInfosForFilter); + final CameraInfoFlutterApiImpl cameraInfoFlutterApiImpl = + new CameraInfoFlutterApiImpl(binaryMessenger, instanceManager); + List filteredCameraInfosIds = new ArrayList(); + + for (CameraInfo cameraInfo : filteredCameraInfos) { + cameraInfoFlutterApiImpl.create(cameraInfo, result -> {}); + Long filteredCameraInfoId = instanceManager.getIdentifierForStrongReference(cameraInfo); + filteredCameraInfosIds.add(filteredCameraInfoId); + } + + return filteredCameraInfosIds; + } +} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java new file mode 100644 index 000000000000..8063866d2fc6 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraXProxy.java @@ -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. + +package io.flutter.plugins.camerax; + +import androidx.camera.core.CameraSelector; + +public class CameraXProxy { + public CameraSelector.Builder createCameraSelectorBuilder() { + return new CameraSelector.Builder(); + } +} 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 ec3b81a8e9d8..e87a80db030c 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 @@ -8,6 +8,7 @@ import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import io.flutter.plugin.common.BasicMessageChannel; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MessageCodec; @@ -15,6 +16,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; /** Generated class from Pigeon. */ @@ -187,6 +189,128 @@ public void create(@NonNull Long identifierArg, Reply callback) { } } + private static class CameraSelectorHostApiCodec extends StandardMessageCodec { + public static final CameraSelectorHostApiCodec INSTANCE = new CameraSelectorHostApiCodec(); + + private CameraSelectorHostApiCodec() {} + } + + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface CameraSelectorHostApi { + void create(@NonNull Long identifier, @Nullable Long lensFacing); + + @NonNull + List filter(@NonNull Long identifier, @NonNull List cameraInfoIds); + + /** The codec used by CameraSelectorHostApi. */ + static MessageCodec getCodec() { + return CameraSelectorHostApiCodec.INSTANCE; + } + + /** + * Sets up an instance of `CameraSelectorHostApi` to handle messages through the + * `binaryMessenger`. + */ + static void setup(BinaryMessenger binaryMessenger, CameraSelectorHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.CameraSelectorHostApi.create", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + Number lensFacingArg = (Number) args.get(1); + api.create( + (identifierArg == null) ? null : identifierArg.longValue(), + (lensFacingArg == null) ? null : lensFacingArg.longValue()); + wrapped.put("result", null); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.CameraSelectorHostApi.filter", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + List cameraInfoIdsArg = (List) args.get(1); + if (cameraInfoIdsArg == null) { + throw new NullPointerException("cameraInfoIdsArg unexpectedly null."); + } + List output = + api.filter( + (identifierArg == null) ? null : identifierArg.longValue(), + cameraInfoIdsArg); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } + + private static class CameraSelectorFlutterApiCodec extends StandardMessageCodec { + public static final CameraSelectorFlutterApiCodec INSTANCE = + new CameraSelectorFlutterApiCodec(); + + private CameraSelectorFlutterApiCodec() {} + } + + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ + public static class CameraSelectorFlutterApi { + private final BinaryMessenger binaryMessenger; + + public CameraSelectorFlutterApi(BinaryMessenger argBinaryMessenger) { + this.binaryMessenger = argBinaryMessenger; + } + + public interface Reply { + void reply(T reply); + } + + static MessageCodec getCodec() { + return CameraSelectorFlutterApiCodec.INSTANCE; + } + + public void create( + @NonNull Long identifierArg, @Nullable Long lensFacingArg, Reply callback) { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.CameraSelectorFlutterApi.create", getCodec()); + channel.send( + new ArrayList(Arrays.asList(identifierArg, lensFacingArg)), + channelReply -> { + callback.reply(null); + }); + } + } + private static Map wrapError(Throwable exception) { Map errorMap = new HashMap<>(); errorMap.put("message", exception.toString()); diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/InstanceManager.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/InstanceManager.java index 8981227073b5..9b549d7bd1ea 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/InstanceManager.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/InstanceManager.java @@ -122,11 +122,16 @@ public void addDartCreatedInstance(Object instance, long identifier) { /** * Adds a new instance that was instantiated from the host platform. * + *

If an instance has already been added, the identifier of the instance will be returned. + * * @param instance the instance to be stored. * @return the unique identifier stored with instance. */ public long addHostCreatedInstance(Object instance) { assertManagerIsNotClosed(); + if (containsInstance(instance)) { + return getIdentifierForStrongReference(instance); + } final long identifier = nextIdentifier++; addInstance(instance, identifier); return identifier; diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraSelectorTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraSelectorTest.java new file mode 100644 index 000000000000..2b27e08b5790 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraSelectorTest.java @@ -0,0 +1,97 @@ +// 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 static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import androidx.camera.core.CameraInfo; +import androidx.camera.core.CameraSelector; +import io.flutter.plugin.common.BinaryMessenger; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +public class CameraSelectorTest { + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock public CameraSelector mockCameraSelector; + @Mock public BinaryMessenger mockBinaryMessenger; + + InstanceManager testInstanceManager; + + @Before + public void setUp() { + testInstanceManager = InstanceManager.open(identifier -> {}); + } + + @After + public void tearDown() { + testInstanceManager.close(); + } + + @Test + public void createTest() { + final CameraSelectorHostApiImpl cameraSelectorHostApi = + new CameraSelectorHostApiImpl(mockBinaryMessenger, testInstanceManager); + final CameraXProxy mockCameraXProxy = mock(CameraXProxy.class); + final CameraSelector.Builder mockCameraSelectorBuilder = mock(CameraSelector.Builder.class); + + cameraSelectorHostApi.cameraXProxy = mockCameraXProxy; + when(mockCameraXProxy.createCameraSelectorBuilder()).thenReturn(mockCameraSelectorBuilder); + + when(mockCameraSelectorBuilder.requireLensFacing(1)).thenReturn(mockCameraSelectorBuilder); + when(mockCameraSelectorBuilder.build()).thenReturn(mockCameraSelector); + + cameraSelectorHostApi.create(0L, 1L); + + verify(mockCameraSelectorBuilder).requireLensFacing(CameraSelector.LENS_FACING_BACK); + assertEquals(testInstanceManager.getInstance(0L), mockCameraSelector); + } + + @Test + public void filterTest() { + final CameraSelectorHostApiImpl cameraSelectorHostApi = + new CameraSelectorHostApiImpl(mockBinaryMessenger, testInstanceManager); + final CameraInfo cameraInfo = mock(CameraInfo.class); + final List cameraInfosForFilter = Arrays.asList(cameraInfo); + final List cameraInfosIds = Arrays.asList(1L); + + testInstanceManager.addDartCreatedInstance(mockCameraSelector, 0); + testInstanceManager.addDartCreatedInstance(cameraInfo, 1); + + when(mockCameraSelector.filter(cameraInfosForFilter)).thenReturn(cameraInfosForFilter); + + assertEquals( + cameraSelectorHostApi.filter(0L, cameraInfosIds), + Arrays.asList(testInstanceManager.getIdentifierForStrongReference(cameraInfo))); + verify(mockCameraSelector).filter(cameraInfosForFilter); + } + + @Test + public void flutterApiCreateTest() { + final CameraSelectorFlutterApiImpl spyFlutterApi = + spy(new CameraSelectorFlutterApiImpl(mockBinaryMessenger, testInstanceManager)); + + spyFlutterApi.create(mockCameraSelector, 0L, reply -> {}); + + final long identifier = + Objects.requireNonNull( + testInstanceManager.getIdentifierForStrongReference(mockCameraSelector)); + verify(spyFlutterApi).create(eq(identifier), eq(0L), any()); + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart index 8190a1ce1161..576260c0b7b8 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'camera_info.dart'; +import 'camera_selector.dart'; import 'camerax_library.pigeon.dart'; import 'java_object.dart'; @@ -12,11 +13,14 @@ class AndroidCameraXCameraFlutterApis { AndroidCameraXCameraFlutterApis({ JavaObjectFlutterApiImpl? javaObjectFlutterApi, CameraInfoFlutterApiImpl? cameraInfoFlutterApi, + CameraSelectorFlutterApiImpl? cameraSelectorFlutterApi, }) { this.javaObjectFlutterApi = javaObjectFlutterApi ?? JavaObjectFlutterApiImpl(); this.cameraInfoFlutterApi = cameraInfoFlutterApi ?? CameraInfoFlutterApiImpl(); + this.cameraSelectorFlutterApi = + cameraSelectorFlutterApi ?? CameraSelectorFlutterApiImpl(); } static bool _haveBeenSetUp = false; @@ -33,11 +37,15 @@ class AndroidCameraXCameraFlutterApis { /// Flutter Api for [CameraInfo]. late final CameraInfoFlutterApiImpl cameraInfoFlutterApi; + /// Flutter Api for [CameraSelector]. + late final CameraSelectorFlutterApiImpl cameraSelectorFlutterApi; + /// Ensures all the Flutter APIs have been setup to receive calls from native code. void ensureSetUp() { if (!_haveBeenSetUp) { JavaObjectFlutterApi.setup(javaObjectFlutterApi); CameraInfoFlutterApi.setup(cameraInfoFlutterApi); + CameraSelectorFlutterApi.setup(cameraSelectorFlutterApi); _haveBeenSetUp = true; } } diff --git a/packages/camera/camera_android_camerax/lib/src/camera_info.dart b/packages/camera/camera_android_camerax/lib/src/camera_info.dart index 3edc7b4afad2..d03426f40027 100644 --- a/packages/camera/camera_android_camerax/lib/src/camera_info.dart +++ b/packages/camera/camera_android_camerax/lib/src/camera_info.dart @@ -20,9 +20,7 @@ class CameraInfo extends JavaObject { binaryMessenger: binaryMessenger, instanceManager: instanceManager) { _api = CameraInfoHostApiImpl( - binaryMessenger: binaryMessenger, - instanceManager: instanceManager, - ); + binaryMessenger: binaryMessenger, instanceManager: instanceManager); AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); } @@ -36,23 +34,18 @@ class CameraInfo extends JavaObject { /// Host API implementation of [CameraInfo]. class CameraInfoHostApiImpl extends CameraInfoHostApi { /// Constructs a [CameraInfoHostApiImpl]. - CameraInfoHostApiImpl({ - this.binaryMessenger, - InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager, - super(binaryMessenger: binaryMessenger); - - /// Sends binary data across the Flutter platform barrier. - /// - /// If it is null, the default BinaryMessenger will be used which routes to - /// the host platform. - final BinaryMessenger? binaryMessenger; + CameraInfoHostApiImpl( + {super.binaryMessenger, InstanceManager? instanceManager}) { + this.instanceManager = instanceManager ?? JavaObject.globalInstanceManager; + } /// Maintains instances stored to communicate with native language objects. - final InstanceManager instanceManager; + late final InstanceManager instanceManager; /// Gets sensor orientation degrees of [CameraInfo]. - Future getSensorRotationDegreesFromInstance(CameraInfo instance) async { + Future getSensorRotationDegreesFromInstance( + CameraInfo instance, + ) async { final int sensorRotationDegrees = await getSensorRotationDegrees( instanceManager.getIdentifier(instance)!); return sensorRotationDegrees; diff --git a/packages/camera/camera_android_camerax/lib/src/camera_selector.dart b/packages/camera/camera_android_camerax/lib/src/camera_selector.dart new file mode 100644 index 000000000000..dd08b9bc571d --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/camera_selector.dart @@ -0,0 +1,177 @@ +// 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 'android_camera_camerax_flutter_api_impls.dart'; +import 'camera_info.dart'; +import 'camerax_library.pigeon.dart'; +import 'instance_manager.dart'; +import 'java_object.dart'; + +/// Selects a camera for use. +/// +/// See https://developer.android.com/reference/androidx/camera/core/CameraSelector. +class CameraSelector extends JavaObject { + /// Creates a [CameraSelector]. + CameraSelector( + {BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + this.lensFacing}) + : super.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager) { + _api = CameraSelectorHostApiImpl( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); + _api.createFromInstance(this, lensFacing); + } + + /// Creates a detached [CameraSelector]. + CameraSelector.detached( + {BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + this.lensFacing}) + : super.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager) { + _api = CameraSelectorHostApiImpl( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); + } + + late final CameraSelectorHostApiImpl _api; + + /// ID for front facing lens. + static const int LENS_FACING_FRONT = 0; + + /// ID for back facing lens. + static const int LENS_FACING_BACK = 1; + + /// Selector for default front facing camera. + static CameraSelector getDefaultFrontCamera({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) { + return CameraSelector( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + lensFacing: LENS_FACING_FRONT, + ); + } + + /// Selector for default back facing camera. + static CameraSelector getDefaultBackCamera({ + BinaryMessenger? binaryMessenger, + InstanceManager? instanceManager, + }) { + return CameraSelector( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + lensFacing: LENS_FACING_BACK, + ); + } + + /// Lens direction of this selector. + final int? lensFacing; + + /// Filters available cameras based on provided [CameraInfo]s. + Future> filter(List cameraInfos) { + return _api.filterFromInstance(this, cameraInfos); + } +} + +/// Host API implementation of [CameraSelector]. +class CameraSelectorHostApiImpl extends CameraSelectorHostApi { + /// Constructs a [CameraSelectorHostApiImpl]. + CameraSelectorHostApiImpl( + {this.binaryMessenger, InstanceManager? instanceManager}) + : super(binaryMessenger: binaryMessenger) { + this.instanceManager = instanceManager ?? JavaObject.globalInstanceManager; + } + + /// Receives binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + late final InstanceManager instanceManager; + + /// Creates a [CameraSelector] with the lens direction provided if specified. + void createFromInstance(CameraSelector instance, int? lensFacing) { + int? identifier = instanceManager.getIdentifier(instance); + identifier ??= instanceManager.addDartCreatedInstance(instance, + onCopy: (CameraSelector original) { + return CameraSelector.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + lensFacing: original.lensFacing); + }); + + create(identifier, lensFacing); + } + + /// Filters a list of [CameraInfo]s based on the [CameraSelector]. + Future> filterFromInstance( + CameraSelector instance, + List cameraInfos, + ) async { + int? identifier = instanceManager.getIdentifier(instance); + identifier ??= instanceManager.addDartCreatedInstance(instance, + onCopy: (CameraSelector original) { + return CameraSelector.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + lensFacing: original.lensFacing); + }); + + final List cameraInfoIds = (cameraInfos.map( + (CameraInfo info) => instanceManager.getIdentifier(info)!)).toList(); + final List filteredCameraInfoIds = + await filter(identifier, cameraInfoIds); + if (filteredCameraInfoIds.isEmpty) { + return []; + } + return (filteredCameraInfoIds.map((int? id) => + instanceManager.getInstanceWithWeakReference(id!)! as CameraInfo)) + .toList(); + } +} + +/// Flutter API implementation of [CameraSelector]. +class CameraSelectorFlutterApiImpl implements CameraSelectorFlutterApi { + /// Constructs a [CameraSelectorFlutterApiImpl]. + CameraSelectorFlutterApiImpl({ + this.binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; + + /// Receives binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; + + @override + void create(int identifier, int? lensFacing) { + instanceManager.addHostCreatedInstance( + CameraSelector.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + lensFacing: lensFacing), + identifier, + onCopy: (CameraSelector original) { + return CameraSelector.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager, + lensFacing: original.lensFacing); + }, + ); + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart b/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart index 8d421836a587..a399001d35b0 100644 --- a/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart +++ b/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart @@ -158,3 +158,108 @@ abstract class CameraInfoFlutterApi { } } } + +class _CameraSelectorHostApiCodec extends StandardMessageCodec { + const _CameraSelectorHostApiCodec(); +} + +class CameraSelectorHostApi { + /// Constructor for [CameraSelectorHostApi]. 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. + CameraSelectorHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _CameraSelectorHostApiCodec(); + + Future create(int arg_identifier, int? arg_lensFacing) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraSelectorHostApi.create', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_identifier, arg_lensFacing]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else { + return; + } + } + + Future> filter( + int arg_identifier, List arg_cameraInfoIds) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraSelectorHostApi.filter', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_identifier, arg_cameraInfoIds]) + as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as List?)!.cast(); + } + } +} + +class _CameraSelectorFlutterApiCodec extends StandardMessageCodec { + const _CameraSelectorFlutterApiCodec(); +} + +abstract class CameraSelectorFlutterApi { + static const MessageCodec codec = _CameraSelectorFlutterApiCodec(); + + void create(int identifier, int? lensFacing); + static void setup(CameraSelectorFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraSelectorFlutterApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.CameraSelectorFlutterApi.create 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.CameraSelectorFlutterApi.create was null, expected non-null int.'); + final int? arg_lensFacing = (args[1] as int?); + api.create(arg_identifier!, arg_lensFacing); + return; + }); + } + } + } +} diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart index d36634a9cac3..aace7a06b1fd 100644 --- a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart +++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart @@ -45,3 +45,15 @@ abstract class CameraInfoHostApi { abstract class CameraInfoFlutterApi { void create(int identifier); } + +@HostApi(dartHostTestHandler: 'TestCameraSelectorHostApi') +abstract class CameraSelectorHostApi { + void create(int identifier, int? lensFacing); + + List filter(int identifier, List cameraInfoIds); +} + +@FlutterApi() +abstract class CameraSelectorFlutterApi { + void create(int identifier, int? lensFacing); +} diff --git a/packages/camera/camera_android_camerax/test/camera_selector_test.dart b/packages/camera/camera_android_camerax/test/camera_selector_test.dart new file mode 100644 index 000000000000..c4ccd6262376 --- /dev/null +++ b/packages/camera/camera_android_camerax/test/camera_selector_test.dart @@ -0,0 +1,121 @@ +// 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:camera_android_camerax/src/camera_info.dart'; +import 'package:camera_android_camerax/src/camera_selector.dart'; +import 'package:camera_android_camerax/src/camerax_library.pigeon.dart'; +import 'package:camera_android_camerax/src/instance_manager.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'camera_selector_test.mocks.dart'; +import 'test_camerax_library.pigeon.dart'; + +@GenerateMocks([TestCameraSelectorHostApi]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('CameraSelector', () { + tearDown(() => TestCameraSelectorHostApi.setup(null)); + + test('detachedCreateTest', () async { + final MockTestCameraSelectorHostApi mockApi = + MockTestCameraSelectorHostApi(); + TestCameraSelectorHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + CameraSelector.detached( + instanceManager: instanceManager, + ); + + verifyNever(mockApi.create(argThat(isA()), null)); + }); + + test('createTestWithoutLensSpecified', () async { + final MockTestCameraSelectorHostApi mockApi = + MockTestCameraSelectorHostApi(); + TestCameraSelectorHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + CameraSelector( + instanceManager: instanceManager, + ); + + verify(mockApi.create(argThat(isA()), null)); + }); + + test('createTestWithLensSpecified', () async { + final MockTestCameraSelectorHostApi mockApi = + MockTestCameraSelectorHostApi(); + TestCameraSelectorHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + CameraSelector( + instanceManager: instanceManager, + lensFacing: CameraSelector.LENS_FACING_BACK); + + verify( + mockApi.create(argThat(isA()), CameraSelector.LENS_FACING_BACK)); + }); + + test('filterTest', () async { + final MockTestCameraSelectorHostApi mockApi = + MockTestCameraSelectorHostApi(); + TestCameraSelectorHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final CameraSelector cameraSelector = CameraSelector.detached( + instanceManager: instanceManager, + ); + const int cameraInfoId = 3; + final CameraInfo cameraInfo = + CameraInfo.detached(instanceManager: instanceManager); + + instanceManager.addHostCreatedInstance( + cameraSelector, + 0, + onCopy: (_) => CameraSelector.detached(), + ); + instanceManager.addHostCreatedInstance( + cameraInfo, + cameraInfoId, + onCopy: (_) => CameraInfo.detached(), + ); + + when(mockApi.filter(instanceManager.getIdentifier(cameraSelector), + [cameraInfoId])).thenReturn([cameraInfoId]); + expect(await cameraSelector.filter([cameraInfo]), + equals([cameraInfo])); + + verify(mockApi.filter(0, [cameraInfoId])); + }); + + test('flutterApiCreateTest', () { + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final CameraSelectorFlutterApi flutterApi = CameraSelectorFlutterApiImpl( + instanceManager: instanceManager, + ); + + flutterApi.create(0, CameraSelector.LENS_FACING_BACK); + + expect(instanceManager.getInstanceWithWeakReference(0), + isA()); + expect( + (instanceManager.getInstanceWithWeakReference(0)! as CameraSelector) + .lensFacing, + equals(CameraSelector.LENS_FACING_BACK)); + }); + }); +} diff --git a/packages/camera/camera_android_camerax/test/camera_selector_test.mocks.dart b/packages/camera/camera_android_camerax/test/camera_selector_test.mocks.dart new file mode 100644 index 000000000000..456db1eaf822 --- /dev/null +++ b/packages/camera/camera_android_camerax/test/camera_selector_test.mocks.dart @@ -0,0 +1,38 @@ +// Mocks generated by Mockito 5.3.0 from annotations +// in camera_android_camerax/test/camera_selector_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'package:mockito/mockito.dart' as _i1; + +import 'test_camerax_library.pigeon.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: 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 [TestCameraSelectorHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestCameraSelectorHostApi extends _i1.Mock + implements _i2.TestCameraSelectorHostApi { + MockTestCameraSelectorHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + void create(int? identifier, int? lensFacing) => + super.noSuchMethod(Invocation.method(#create, [identifier, lensFacing]), + returnValueForMissingStub: null); + @override + List filter(int? identifier, List? cameraInfoIds) => (super + .noSuchMethod(Invocation.method(#filter, [identifier, cameraInfoIds]), + returnValue: []) as List); +} diff --git a/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart b/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart index 59e76190188e..b10e14e9d518 100644 --- a/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart +++ b/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart @@ -77,3 +77,61 @@ abstract class TestCameraInfoHostApi { } } } + +class _TestCameraSelectorHostApiCodec extends StandardMessageCodec { + const _TestCameraSelectorHostApiCodec(); +} + +abstract class TestCameraSelectorHostApi { + static const MessageCodec codec = _TestCameraSelectorHostApiCodec(); + + void create(int identifier, int? lensFacing); + List filter(int identifier, List cameraInfoIds); + static void setup(TestCameraSelectorHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraSelectorHostApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.CameraSelectorHostApi.create 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.CameraSelectorHostApi.create was null, expected non-null int.'); + final int? arg_lensFacing = (args[1] as int?); + api.create(arg_identifier!, arg_lensFacing); + return {}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.CameraSelectorHostApi.filter', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.CameraSelectorHostApi.filter 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.CameraSelectorHostApi.filter was null, expected non-null int.'); + final List? arg_cameraInfoIds = + (args[1] as List?)?.cast(); + assert(arg_cameraInfoIds != null, + 'Argument for dev.flutter.pigeon.CameraSelectorHostApi.filter was null, expected non-null List.'); + final List output = + api.filter(arg_identifier!, arg_cameraInfoIds!); + return {'result': output}; + }); + } + } + } +} From de55e76b9e5eabc756143b2bd7db99f2e1421f75 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 26 Sep 2022 12:02:23 -0400 Subject: [PATCH 752/844] Roll Flutter from 64f84f6a47d2 to 7b6074fbc542 (7 revisions) (#6496) --- .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 886179e55e79..06ef561146db 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -64f84f6a47d276e4d79cb10c203adbddeeeae336 +7b6074fbc542efb41da5618bc97711fac6ab7dee From 4b2a636943eb58d36f4a5099253e8f3c1c280c8f Mon Sep 17 00:00:00 2001 From: eugerossetto <101729072+eugerossetto@users.noreply.github.com> Date: Mon, 26 Sep 2022 15:27:13 -0300 Subject: [PATCH 753/844] [file_selector] Annotate all creation of XTypeGroup with // ignore: prefer_const_contructor (#6463) --- .../example/lib/open_image_page.dart | 3 ++ .../lib/open_multiple_images_page.dart | 3 ++ .../example/lib/open_text_page.dart | 2 ++ .../test/file_selector_test.dart | 4 +++ .../example/lib/open_image_page.dart | 2 ++ .../lib/open_multiple_images_page.dart | 4 +++ .../example/lib/open_text_page.dart | 2 ++ .../test/file_selector_ios_test.dart | 16 ++++++++++ .../example/lib/open_image_page.dart | 2 ++ .../lib/open_multiple_images_page.dart | 4 +++ .../example/lib/open_text_page.dart | 2 ++ .../test/file_selector_linux_test.dart | 24 +++++++++++++++ .../example/lib/open_image_page.dart | 2 ++ .../lib/open_multiple_images_page.dart | 4 +++ .../example/lib/open_text_page.dart | 2 ++ .../test/file_selector_macos_test.dart | 30 +++++++++++++++++++ .../file_selector_web_test.dart | 4 +++ .../file_selector_web/test/utils_test.dart | 22 ++++++++++++++ .../example/lib/open_image_page.dart | 2 ++ .../lib/open_multiple_images_page.dart | 4 +++ .../example/lib/open_text_page.dart | 2 ++ .../test/file_selector_windows_test.dart | 24 +++++++++++++++ 22 files changed, 164 insertions(+) diff --git a/packages/file_selector/file_selector/example/lib/open_image_page.dart b/packages/file_selector/file_selector/example/lib/open_image_page.dart index 28afca065121..6e3ae8940ce4 100644 --- a/packages/file_selector/file_selector/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_image_page.dart @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 +// ignore_for_file: prefer_const_constructors + import 'dart:io'; import 'package:file_selector/file_selector.dart'; diff --git a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart index 22703425b47b..0d70380cd906 100644 --- a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 +// ignore_for_file: prefer_const_constructors + import 'dart:io'; import 'package:file_selector/file_selector.dart'; diff --git a/packages/file_selector/file_selector/example/lib/open_text_page.dart b/packages/file_selector/file_selector/example/lib/open_text_page.dart index 27fb0b749c3b..8d5f7c863c53 100644 --- a/packages/file_selector/file_selector/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_text_page.dart @@ -12,6 +12,8 @@ class OpenTextPage extends StatelessWidget { const OpenTextPage({Key? key}) : super(key: key); Future _openTextFile(BuildContext context) async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'text', extensions: ['txt', 'json'], diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index 887ab64c3c0c..cd7d540f954d 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -13,10 +13,14 @@ void main() { const String confirmButtonText = 'Use this profile picture'; const String suggestedName = 'suggested_name'; final List acceptedTypeGroups = [ + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors XTypeGroup(label: 'documents', mimeTypes: [ 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessing', ]), + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors XTypeGroup(label: 'images', extensions: [ 'jpg', 'png', diff --git a/packages/file_selector/file_selector_ios/example/lib/open_image_page.dart b/packages/file_selector/file_selector_ios/example/lib/open_image_page.dart index fd0f4e711d7f..740dcc2c1658 100644 --- a/packages/file_selector/file_selector_ios/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_ios/example/lib/open_image_page.dart @@ -15,6 +15,8 @@ class OpenImagePage extends StatelessWidget { const OpenImagePage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'images', extensions: ['jpg', 'png'], diff --git a/packages/file_selector/file_selector_ios/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_ios/example/lib/open_multiple_images_page.dart index 29b27c1d637c..934a694d503c 100644 --- a/packages/file_selector/file_selector_ios/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_ios/example/lib/open_multiple_images_page.dart @@ -15,11 +15,15 @@ class OpenMultipleImagesPage extends StatelessWidget { const OpenMultipleImagesPage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', extensions: ['jpg', 'jpeg'], macUTIs: ['public.jpeg'], ); + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup pngTypeGroup = XTypeGroup( label: 'PNGs', extensions: ['png'], diff --git a/packages/file_selector/file_selector_ios/example/lib/open_text_page.dart b/packages/file_selector/file_selector_ios/example/lib/open_text_page.dart index b747aa89611c..4b07d93a66bd 100644 --- a/packages/file_selector/file_selector_ios/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_ios/example/lib/open_text_page.dart @@ -12,6 +12,8 @@ class OpenTextPage extends StatelessWidget { const OpenTextPage({Key? key}) : super(key: key); Future _openTextFile(BuildContext context) async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'text', extensions: ['txt', 'json'], diff --git a/packages/file_selector/file_selector_ios/test/file_selector_ios_test.dart b/packages/file_selector/file_selector_ios/test/file_selector_ios_test.dart index f9ef40628991..70e5f83a6b0a 100644 --- a/packages/file_selector/file_selector_ios/test/file_selector_ios_test.dart +++ b/packages/file_selector/file_selector_ios/test/file_selector_ios_test.dart @@ -36,6 +36,8 @@ void main() { }); test('passes the accepted type groups correctly', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], @@ -43,6 +45,8 @@ void main() { macUTIs: ['public.text'], ); + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], @@ -62,6 +66,8 @@ void main() { expect(config.allowMultiSelection, isFalse); }); test('throws for a type group that does not support iOS', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'images', webWildCards: ['images/*'], @@ -73,6 +79,8 @@ void main() { }); test('allows a wildcard group', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', ); @@ -88,6 +96,8 @@ void main() { }); test('passes the accepted type groups correctly', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], @@ -95,6 +105,8 @@ void main() { macUTIs: ['public.text'], ); + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], @@ -114,6 +126,8 @@ void main() { expect(config.allowMultiSelection, isTrue); }); test('throws for a type group that does not support iOS', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'images', webWildCards: ['images/*'], @@ -125,6 +139,8 @@ void main() { }); test('allows a wildcard group', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', ); diff --git a/packages/file_selector/file_selector_linux/example/lib/open_image_page.dart b/packages/file_selector/file_selector_linux/example/lib/open_image_page.dart index 9e1d2074d5f2..8f11d048189d 100644 --- a/packages/file_selector/file_selector_linux/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_linux/example/lib/open_image_page.dart @@ -15,6 +15,8 @@ class OpenImagePage extends StatelessWidget { const OpenImagePage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'images', extensions: ['jpg', 'png'], diff --git a/packages/file_selector/file_selector_linux/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_linux/example/lib/open_multiple_images_page.dart index 21da8c22fa4d..04c8310616eb 100644 --- a/packages/file_selector/file_selector_linux/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_linux/example/lib/open_multiple_images_page.dart @@ -15,10 +15,14 @@ class OpenMultipleImagesPage extends StatelessWidget { const OpenMultipleImagesPage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', extensions: ['jpg', 'jpeg'], ); + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup pngTypeGroup = XTypeGroup( label: 'PNGs', extensions: ['png'], diff --git a/packages/file_selector/file_selector_linux/example/lib/open_text_page.dart b/packages/file_selector/file_selector_linux/example/lib/open_text_page.dart index 05c6d166fb6f..89bc1c70c3ca 100644 --- a/packages/file_selector/file_selector_linux/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_linux/example/lib/open_text_page.dart @@ -12,6 +12,8 @@ class OpenTextPage extends StatelessWidget { const OpenTextPage({Key? key}) : super(key: key); Future _openTextFile(BuildContext context) async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'text', extensions: ['txt', 'json'], diff --git a/packages/file_selector/file_selector_linux/test/file_selector_linux_test.dart b/packages/file_selector/file_selector_linux/test/file_selector_linux_test.dart index 67b99298adc3..a85340875041 100644 --- a/packages/file_selector/file_selector_linux/test/file_selector_linux_test.dart +++ b/packages/file_selector/file_selector_linux/test/file_selector_linux_test.dart @@ -29,6 +29,8 @@ void main() { group('#openFile', () { test('passes the accepted type groups correctly', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], @@ -36,6 +38,8 @@ void main() { macUTIs: ['public.text'], ); + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], @@ -101,6 +105,8 @@ void main() { }); test('throws for a type group that does not support Linux', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'images', webWildCards: ['images/*'], @@ -112,6 +118,8 @@ void main() { }); test('passes a wildcard group correctly', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'any', ); @@ -139,6 +147,8 @@ void main() { group('#openFiles', () { test('passes the accepted type groups correctly', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], @@ -146,6 +156,8 @@ void main() { macUTIs: ['public.text'], ); + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], @@ -211,6 +223,8 @@ void main() { }); test('throws for a type group that does not support Linux', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'images', webWildCards: ['images/*'], @@ -222,6 +236,8 @@ void main() { }); test('passes a wildcard group correctly', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'any', ); @@ -249,6 +265,8 @@ void main() { group('#getSavePath', () { test('passes the accepted type groups correctly', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], @@ -256,6 +274,8 @@ void main() { macUTIs: ['public.text'], ); + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], @@ -322,6 +342,8 @@ void main() { }); test('throws for a type group that does not support Linux', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'images', webWildCards: ['images/*'], @@ -333,6 +355,8 @@ void main() { }); test('passes a wildcard group correctly', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'any', ); diff --git a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart index 9e1d2074d5f2..8f11d048189d 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart @@ -15,6 +15,8 @@ class OpenImagePage extends StatelessWidget { const OpenImagePage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'images', extensions: ['jpg', 'png'], diff --git a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart index 21da8c22fa4d..04c8310616eb 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart @@ -15,10 +15,14 @@ class OpenMultipleImagesPage extends StatelessWidget { const OpenMultipleImagesPage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', extensions: ['jpg', 'jpeg'], ); + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup pngTypeGroup = XTypeGroup( label: 'PNGs', extensions: ['png'], diff --git a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart index 05c6d166fb6f..89bc1c70c3ca 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart @@ -12,6 +12,8 @@ class OpenTextPage extends StatelessWidget { const OpenTextPage({Key? key}) : super(key: key); Future _openTextFile(BuildContext context) async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'text', extensions: ['txt', 'json'], diff --git a/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart b/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart index 40ab58534abd..2f98881d82f4 100644 --- a/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart +++ b/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart @@ -30,6 +30,8 @@ void main() { group('openFile', () { test('passes the accepted type groups correctly', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], @@ -37,6 +39,8 @@ void main() { macUTIs: ['public.text'], ); + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], @@ -96,6 +100,8 @@ void main() { }); test('throws for a type group that does not support macOS', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'images', webWildCards: ['images/*'], @@ -107,6 +113,8 @@ void main() { }); test('allows a wildcard group', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', ); @@ -118,6 +126,8 @@ void main() { group('openFiles', () { test('passes the accepted type groups correctly', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], @@ -125,6 +135,8 @@ void main() { macUTIs: ['public.text'], ); + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], @@ -184,6 +196,8 @@ void main() { }); test('throws for a type group that does not support macOS', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'images', webWildCards: ['images/*'], @@ -195,6 +209,8 @@ void main() { }); test('allows a wildcard group', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', ); @@ -206,6 +222,8 @@ void main() { group('getSavePath', () { test('passes the accepted type groups correctly', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], @@ -213,6 +231,8 @@ void main() { macUTIs: ['public.text'], ); + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], @@ -273,6 +293,8 @@ void main() { }); test('throws for a type group that does not support macOS', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'images', webWildCards: ['images/*'], @@ -284,6 +306,8 @@ void main() { }); test('allows a wildcard group', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', ); @@ -326,18 +350,24 @@ void main() { test('ignores all type groups if any of them is a wildcard', () async { await plugin.getSavePath(acceptedTypeGroups: [ + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors XTypeGroup( label: 'text', extensions: ['txt'], mimeTypes: ['text/plain'], macUTIs: ['public.text'], ), + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors XTypeGroup( label: 'image', extensions: ['jpg'], mimeTypes: ['image/jpg'], macUTIs: ['public.image'], ), + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors XTypeGroup( label: 'any', ), 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 d3d058122aa6..206bc35d550b 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 @@ -26,6 +26,8 @@ void main() { final FileSelectorWeb plugin = FileSelectorWeb(domHelper: mockDomHelper); + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'images', extensions: ['jpg', 'jpeg'], @@ -56,6 +58,8 @@ void main() { final FileSelectorWeb plugin = FileSelectorWeb(domHelper: mockDomHelper); + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'files', extensions: ['.txt'], diff --git a/packages/file_selector/file_selector_web/test/utils_test.dart b/packages/file_selector/file_selector_web/test/utils_test.dart index 3281c4cdca8d..f335de057a12 100644 --- a/packages/file_selector/file_selector_web/test/utils_test.dart +++ b/packages/file_selector/file_selector_web/test/utils_test.dart @@ -11,8 +11,14 @@ void main() { group('acceptedTypesToString', () { test('works', () { final List acceptedTypes = [ + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors XTypeGroup(label: 'images', webWildCards: ['images/*']), + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors XTypeGroup(label: 'jpgs', extensions: ['jpg', 'jpeg']), + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors XTypeGroup(label: 'pngs', mimeTypes: ['image/png']), ]; final String accepts = acceptedTypesToString(acceptedTypes); @@ -27,7 +33,11 @@ void main() { test('works with extensions', () { final List acceptedTypes = [ + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors XTypeGroup(label: 'jpgs', extensions: ['jpeg', 'jpg']), + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors XTypeGroup(label: 'pngs', extensions: ['png']), ]; final String accepts = acceptedTypesToString(acceptedTypes); @@ -36,8 +46,12 @@ void main() { test('works with mime types', () { final List acceptedTypes = [ + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors XTypeGroup( label: 'jpgs', mimeTypes: ['image/jpeg', 'image/jpg']), + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors XTypeGroup(label: 'pngs', mimeTypes: ['image/png']), ]; final String accepts = acceptedTypesToString(acceptedTypes); @@ -46,8 +60,14 @@ void main() { test('works with web wild cards', () { final List acceptedTypes = [ + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors XTypeGroup(label: 'images', webWildCards: ['image/*']), + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors XTypeGroup(label: 'audios', webWildCards: ['audio/*']), + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors XTypeGroup(label: 'videos', webWildCards: ['video/*']), ]; final String accepts = acceptedTypesToString(acceptedTypes); @@ -56,6 +76,8 @@ void main() { test('throws for a type group that does not support web', () { final List acceptedTypes = [ + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors XTypeGroup(label: 'text', macUTIs: ['public.text']), ]; expect(() => acceptedTypesToString(acceptedTypes), throwsArgumentError); diff --git a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart index 9e1d2074d5f2..8f11d048189d 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart @@ -15,6 +15,8 @@ class OpenImagePage extends StatelessWidget { const OpenImagePage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'images', extensions: ['jpg', 'png'], diff --git a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart index 21da8c22fa4d..04c8310616eb 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart @@ -15,10 +15,14 @@ class OpenMultipleImagesPage extends StatelessWidget { const OpenMultipleImagesPage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', extensions: ['jpg', 'jpeg'], ); + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup pngTypeGroup = XTypeGroup( label: 'PNGs', extensions: ['png'], diff --git a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart index 05c6d166fb6f..89bc1c70c3ca 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart @@ -12,6 +12,8 @@ class OpenTextPage extends StatelessWidget { const OpenTextPage({Key? key}) : super(key: key); Future _openTextFile(BuildContext context) async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'text', extensions: ['txt', 'json'], diff --git a/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart b/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart index 79277fae472b..5792099de768 100644 --- a/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart +++ b/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart @@ -47,6 +47,8 @@ void main() { }); test('passes the accepted type groups correctly', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], @@ -54,6 +56,8 @@ void main() { macUTIs: ['public.text'], ); + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], @@ -86,6 +90,8 @@ void main() { }); test('throws for a type group that does not support Windows', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', mimeTypes: ['text/plain'], @@ -97,6 +103,8 @@ void main() { }); test('allows a wildcard group', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', ); @@ -125,6 +133,8 @@ void main() { }); test('passes the accepted type groups correctly', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], @@ -132,6 +142,8 @@ void main() { macUTIs: ['public.text'], ); + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], @@ -164,6 +176,8 @@ void main() { }); test('throws for a type group that does not support Windows', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', mimeTypes: ['text/plain'], @@ -175,6 +189,8 @@ void main() { }); test('allows a wildcard group', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', ); @@ -231,6 +247,8 @@ void main() { }); test('passes the accepted type groups correctly', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], @@ -238,6 +256,8 @@ void main() { macUTIs: ['public.text'], ); + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], @@ -277,6 +297,8 @@ void main() { }); test('throws for a type group that does not support Windows', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', mimeTypes: ['text/plain'], @@ -288,6 +310,8 @@ void main() { }); test('allows a wildcard group', () async { + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors final XTypeGroup group = XTypeGroup( label: 'text', ); From 8ef30019eca7f43d54b426e22645f9953c7aa489 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 26 Sep 2022 17:55:07 -0400 Subject: [PATCH 754/844] Enable depend_on_referenced_packages lint (#6498) --- analysis_options.yaml | 2 +- packages/camera/camera_android/example/pubspec.yaml | 1 + .../camera/camera_android_camerax/example/pubspec.yaml | 2 +- packages/camera/camera_avfoundation/example/pubspec.yaml | 1 + packages/camera/camera_web/example/pubspec.yaml | 3 +++ packages/camera/camera_windows/example/pubspec.yaml | 1 + .../file_selector/file_selector_web/example/pubspec.yaml | 5 +++-- .../google_maps_flutter/example/pubspec.yaml | 2 ++ .../google_maps_flutter_android/example/pubspec.yaml | 2 ++ .../google_maps_flutter_ios/example/pubspec.yaml | 3 +++ .../google_maps_flutter_web/example/pubspec.yaml | 1 + .../google_sign_in/google_sign_in/example/pubspec.yaml | 2 ++ .../google_sign_in_android/example/pubspec.yaml | 2 ++ .../google_sign_in_ios/example/pubspec.yaml | 2 ++ .../google_sign_in_web/example/pubspec.yaml | 1 + packages/image_picker/image_picker/example/pubspec.yaml | 2 ++ .../image_picker_android/example/pubspec.yaml | 2 ++ .../image_picker_for_web/example/pubspec.yaml | 1 + .../image_picker/image_picker_ios/example/pubspec.yaml | 2 ++ .../image_picker_windows/example/pubspec.yaml | 1 + .../in_app_purchase/in_app_purchase/example/pubspec.yaml | 5 ++++- .../in_app_purchase_android/example/pubspec.yaml | 2 ++ .../in_app_purchase_storekit/example/pubspec.yaml | 2 ++ packages/ios_platform_images/example/pubspec.yaml | 8 ++++---- packages/local_auth/local_auth/example/pubspec.yaml | 4 ++++ .../local_auth/local_auth_android/example/pubspec.yaml | 2 ++ packages/local_auth/local_auth_ios/example/pubspec.yaml | 2 ++ .../local_auth/local_auth_windows/example/pubspec.yaml | 2 ++ packages/path_provider/path_provider/example/pubspec.yaml | 2 ++ .../path_provider_android/example/pubspec.yaml | 2 ++ .../path_provider/path_provider_ios/example/pubspec.yaml | 2 ++ .../path_provider_macos/example/pubspec.yaml | 2 ++ .../path_provider_windows/example/pubspec.yaml | 2 ++ packages/quick_actions/quick_actions/example/pubspec.yaml | 2 ++ .../quick_actions_android/example/pubspec.yaml | 2 ++ .../quick_actions/quick_actions_ios/example/pubspec.yaml | 2 ++ .../shared_preferences/example/pubspec.yaml | 2 ++ .../shared_preferences_android/example/pubspec.yaml | 2 ++ .../shared_preferences_ios/example/pubspec.yaml | 2 ++ .../shared_preferences_linux/example/pubspec.yaml | 2 ++ .../shared_preferences_macos/example/pubspec.yaml | 2 ++ .../shared_preferences_web/example/pubspec.yaml | 1 + .../shared_preferences_windows/example/pubspec.yaml | 2 ++ packages/url_launcher/url_launcher/example/pubspec.yaml | 2 ++ .../url_launcher_android/example/pubspec.yaml | 3 +++ .../url_launcher/url_launcher_ios/example/pubspec.yaml | 3 +++ .../url_launcher/url_launcher_linux/example/pubspec.yaml | 2 ++ .../url_launcher/url_launcher_macos/example/pubspec.yaml | 2 ++ .../url_launcher/url_launcher_web/example/pubspec.yaml | 3 +++ .../url_launcher_windows/example/pubspec.yaml | 2 ++ .../video_player/video_player_web/example/pubspec.yaml | 1 + .../webview_flutter_android/example/pubspec.yaml | 7 +++---- .../webview_flutter_web/example/pubspec.yaml | 1 + .../webview_flutter_wkwebview/example/pubspec.yaml | 3 +-- 54 files changed, 108 insertions(+), 15 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 891e2ef49180..5f10583a5c1a 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -114,7 +114,7 @@ linter: # - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204 - control_flow_in_finally # - curly_braces_in_flow_control_structures # not required by flutter style - # - depend_on_referenced_packages # LOCAL CHANGE - Needs to be enabled and violations fixed. + - depend_on_referenced_packages - deprecated_consistency # - diagnostic_describe_all_properties # enabled only at the framework level (packages/flutter/lib) - directives_ordering diff --git a/packages/camera/camera_android/example/pubspec.yaml b/packages/camera/camera_android/example/pubspec.yaml index 5fcc8093d29e..2e530e02ca71 100644 --- a/packages/camera/camera_android/example/pubspec.yaml +++ b/packages/camera/camera_android/example/pubspec.yaml @@ -14,6 +14,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: ../ + camera_platform_interface: ^2.2.0 flutter: sdk: flutter path_provider: ^2.0.0 diff --git a/packages/camera/camera_android_camerax/example/pubspec.yaml b/packages/camera/camera_android_camerax/example/pubspec.yaml index 2884e8685d8c..d9756f7ebd9b 100644 --- a/packages/camera/camera_android_camerax/example/pubspec.yaml +++ b/packages/camera/camera_android_camerax/example/pubspec.yaml @@ -14,6 +14,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: ../ + camera_platform_interface: ^2.2.0 flutter: sdk: flutter @@ -25,4 +26,3 @@ dev_dependencies: flutter: uses-material-design: true - \ No newline at end of file diff --git a/packages/camera/camera_avfoundation/example/pubspec.yaml b/packages/camera/camera_avfoundation/example/pubspec.yaml index 55296a6bf4db..a9252cbd6d61 100644 --- a/packages/camera/camera_avfoundation/example/pubspec.yaml +++ b/packages/camera/camera_avfoundation/example/pubspec.yaml @@ -14,6 +14,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: ../ + camera_platform_interface: ^2.2.0 flutter: sdk: flutter path_provider: ^2.0.0 diff --git a/packages/camera/camera_web/example/pubspec.yaml b/packages/camera/camera_web/example/pubspec.yaml index 78ff61a5f883..e82bbe392ceb 100644 --- a/packages/camera/camera_web/example/pubspec.yaml +++ b/packages/camera/camera_web/example/pubspec.yaml @@ -10,8 +10,11 @@ dependencies: sdk: flutter dev_dependencies: + async: ^2.5.0 + camera_platform_interface: ^2.1.0 camera_web: path: ../ + cross_file: ^0.3.1 flutter_driver: sdk: flutter flutter_test: diff --git a/packages/camera/camera_windows/example/pubspec.yaml b/packages/camera/camera_windows/example/pubspec.yaml index e1d63a3f81e9..80ce958a0e84 100644 --- a/packages/camera/camera_windows/example/pubspec.yaml +++ b/packages/camera/camera_windows/example/pubspec.yaml @@ -19,6 +19,7 @@ dependencies: sdk: flutter dev_dependencies: + async: ^2.5.0 flutter_test: sdk: flutter integration_test: diff --git a/packages/file_selector/file_selector_web/example/pubspec.yaml b/packages/file_selector/file_selector_web/example/pubspec.yaml index 042f412b816b..3a36ce5c74f3 100644 --- a/packages/file_selector/file_selector_web/example/pubspec.yaml +++ b/packages/file_selector/file_selector_web/example/pubspec.yaml @@ -6,12 +6,13 @@ environment: flutter: ">=2.10.0" dependencies: + file_selector_platform_interface: ^2.1.0 + file_selector_web: + path: ../ flutter: sdk: flutter dev_dependencies: - file_selector_web: - path: ../ flutter_driver: sdk: flutter flutter_test: 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 77e9b9497410..ad1ff2cad0a7 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml @@ -25,6 +25,8 @@ dev_dependencies: espresso: ^0.1.0+2 flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter 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 cd7123050a38..bc9a4dd03657 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 @@ -25,6 +25,8 @@ dev_dependencies: espresso: ^0.1.0+2 flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/example/pubspec.yaml index d9d8d5899bdd..ae61f5d92f3c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/pubspec.yaml @@ -18,10 +18,13 @@ 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.2.1 dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter 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 8ec25c6bee72..7e25b6e3b483 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,6 +9,7 @@ environment: dependencies: flutter: sdk: flutter + google_maps_flutter_platform_interface: ^2.2.1 google_maps_flutter_web: path: ../ diff --git a/packages/google_sign_in/google_sign_in/example/pubspec.yaml b/packages/google_sign_in/google_sign_in/example/pubspec.yaml index 2ade7d324201..b836ffea4664 100644 --- a/packages/google_sign_in/google_sign_in/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/example/pubspec.yaml @@ -22,6 +22,8 @@ dev_dependencies: espresso: ^0.1.0+2 flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml index b7b104c3307e..f81df99b7d28 100644 --- a/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml @@ -23,6 +23,8 @@ dev_dependencies: espresso: ^0.1.0+2 flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml index a6c97a5678cb..aedc4b01aade 100644 --- a/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_ios/example/pubspec.yaml @@ -22,6 +22,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter 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 ea4b1002d79f..e5abdacf944d 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 @@ -16,6 +16,7 @@ dev_dependencies: sdk: flutter flutter_test: sdk: flutter + google_sign_in_platform_interface: ^2.2.0 http: ^0.13.0 integration_test: sdk: flutter diff --git a/packages/image_picker/image_picker/example/pubspec.yaml b/packages/image_picker/image_picker/example/pubspec.yaml index b511cde0a554..e9511e27ab6d 100755 --- a/packages/image_picker/image_picker/example/pubspec.yaml +++ b/packages/image_picker/image_picker/example/pubspec.yaml @@ -23,6 +23,8 @@ dev_dependencies: espresso: ^0.2.0 flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/image_picker/image_picker_android/example/pubspec.yaml b/packages/image_picker/image_picker_android/example/pubspec.yaml index ac4cb232f02d..02ef8a02af4c 100755 --- a/packages/image_picker/image_picker_android/example/pubspec.yaml +++ b/packages/image_picker/image_picker_android/example/pubspec.yaml @@ -24,6 +24,8 @@ dev_dependencies: espresso: ^0.2.0 flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter 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 bb226e0c9c10..c39bd81f9de0 100644 --- a/packages/image_picker/image_picker_for_web/example/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/example/pubspec.yaml @@ -10,6 +10,7 @@ dependencies: sdk: flutter image_picker_for_web: path: ../ + image_picker_platform_interface: ^2.2.0 dev_dependencies: flutter_driver: diff --git a/packages/image_picker/image_picker_ios/example/pubspec.yaml b/packages/image_picker/image_picker_ios/example/pubspec.yaml index bca58a553682..856f775cc641 100755 --- a/packages/image_picker/image_picker_ios/example/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/example/pubspec.yaml @@ -22,6 +22,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/image_picker/image_picker_windows/example/pubspec.yaml b/packages/image_picker/image_picker_windows/example/pubspec.yaml index 1a48e6ddc14b..b87000a6caff 100644 --- a/packages/image_picker/image_picker_windows/example/pubspec.yaml +++ b/packages/image_picker/image_picker_windows/example/pubspec.yaml @@ -10,6 +10,7 @@ environment: dependencies: flutter: sdk: flutter + image_picker_platform_interface: ^2.4.3 image_picker_windows: # When depending on this package from a real application you should use: # image_picker_windows: ^x.y.z diff --git a/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml index 0b4ea28bc7f6..74ad9aafb768 100644 --- a/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/example/pubspec.yaml @@ -16,12 +16,15 @@ 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: ../ + in_app_purchase_android: ^0.2.1 + in_app_purchase_storekit: ^0.3.0+1 shared_preferences: ^2.0.0 dev_dependencies: flutter_driver: sdk: flutter - + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml index 69e3ca755993..af760a3ada46 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_android/example/pubspec.yaml @@ -22,6 +22,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml index 9e4f88b79bc5..e71b85d4b447 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/pubspec.yaml @@ -22,6 +22,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/ios_platform_images/example/pubspec.yaml b/packages/ios_platform_images/example/pubspec.yaml index 34bd431e97fc..6045b3f67cfc 100644 --- a/packages/ios_platform_images/example/pubspec.yaml +++ b/packages/ios_platform_images/example/pubspec.yaml @@ -10,10 +10,6 @@ dependencies: cupertino_icons: ^1.0.2 flutter: sdk: flutter - -dev_dependencies: - flutter_test: - sdk: flutter ios_platform_images: # When depending on this package from a real application you should use: # ios_platform_images: ^x.y.z @@ -22,5 +18,9 @@ dev_dependencies: # the parent directory to use the current plugin's version. path: ../ +dev_dependencies: + flutter_test: + sdk: flutter + flutter: uses-material-design: true diff --git a/packages/local_auth/local_auth/example/pubspec.yaml b/packages/local_auth/local_auth/example/pubspec.yaml index 14331dab78bf..f7dc2fc5b9e7 100644 --- a/packages/local_auth/local_auth/example/pubspec.yaml +++ b/packages/local_auth/local_auth/example/pubspec.yaml @@ -16,11 +16,15 @@ 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: ../ + local_auth_android: ^1.0.0 + local_auth_ios: ^1.0.1 dev_dependencies: build_runner: ^2.1.10 flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/local_auth/local_auth_android/example/pubspec.yaml b/packages/local_auth/local_auth_android/example/pubspec.yaml index b1c68d49e555..c95b89ad0c2a 100644 --- a/packages/local_auth/local_auth_android/example/pubspec.yaml +++ b/packages/local_auth/local_auth_android/example/pubspec.yaml @@ -21,6 +21,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/local_auth/local_auth_ios/example/pubspec.yaml b/packages/local_auth/local_auth_ios/example/pubspec.yaml index 52a95010d43b..720d5a732bd5 100644 --- a/packages/local_auth/local_auth_ios/example/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/example/pubspec.yaml @@ -21,6 +21,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/local_auth/local_auth_windows/example/pubspec.yaml b/packages/local_auth/local_auth_windows/example/pubspec.yaml index 664f8623abb7..4bb2671f6826 100644 --- a/packages/local_auth/local_auth_windows/example/pubspec.yaml +++ b/packages/local_auth/local_auth_windows/example/pubspec.yaml @@ -21,6 +21,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/path_provider/path_provider/example/pubspec.yaml b/packages/path_provider/path_provider/example/pubspec.yaml index 13be15f0c1a1..5964a267f96d 100644 --- a/packages/path_provider/path_provider/example/pubspec.yaml +++ b/packages/path_provider/path_provider/example/pubspec.yaml @@ -20,6 +20,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/path_provider/path_provider_android/example/pubspec.yaml b/packages/path_provider/path_provider_android/example/pubspec.yaml index 4ab05835158d..b460d6ba49ce 100644 --- a/packages/path_provider/path_provider_android/example/pubspec.yaml +++ b/packages/path_provider/path_provider_android/example/pubspec.yaml @@ -21,6 +21,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/path_provider/path_provider_ios/example/pubspec.yaml b/packages/path_provider/path_provider_ios/example/pubspec.yaml index 0df5e6141fdf..f1d885513948 100644 --- a/packages/path_provider/path_provider_ios/example/pubspec.yaml +++ b/packages/path_provider/path_provider_ios/example/pubspec.yaml @@ -21,6 +21,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/path_provider/path_provider_macos/example/pubspec.yaml b/packages/path_provider/path_provider_macos/example/pubspec.yaml index f5bd2ba024b5..8c69e69ce122 100644 --- a/packages/path_provider/path_provider_macos/example/pubspec.yaml +++ b/packages/path_provider/path_provider_macos/example/pubspec.yaml @@ -21,6 +21,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/path_provider/path_provider_windows/example/pubspec.yaml b/packages/path_provider/path_provider_windows/example/pubspec.yaml index c7a12cae4573..d70a4a84f504 100644 --- a/packages/path_provider/path_provider_windows/example/pubspec.yaml +++ b/packages/path_provider/path_provider_windows/example/pubspec.yaml @@ -20,6 +20,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/quick_actions/quick_actions/example/pubspec.yaml b/packages/quick_actions/quick_actions/example/pubspec.yaml index 3b197c219109..46be008390d8 100644 --- a/packages/quick_actions/quick_actions/example/pubspec.yaml +++ b/packages/quick_actions/quick_actions/example/pubspec.yaml @@ -21,6 +21,8 @@ dev_dependencies: espresso: ^0.1.0+2 flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/quick_actions/quick_actions_android/example/pubspec.yaml b/packages/quick_actions/quick_actions_android/example/pubspec.yaml index 6ff13f063c5f..17d50f5d0829 100644 --- a/packages/quick_actions/quick_actions_android/example/pubspec.yaml +++ b/packages/quick_actions/quick_actions_android/example/pubspec.yaml @@ -21,6 +21,8 @@ dev_dependencies: espresso: ^0.1.0+2 flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/quick_actions/quick_actions_ios/example/pubspec.yaml b/packages/quick_actions/quick_actions_ios/example/pubspec.yaml index f93e162e7fa9..ecac371720d6 100644 --- a/packages/quick_actions/quick_actions_ios/example/pubspec.yaml +++ b/packages/quick_actions/quick_actions_ios/example/pubspec.yaml @@ -20,6 +20,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/shared_preferences/shared_preferences/example/pubspec.yaml b/packages/shared_preferences/shared_preferences/example/pubspec.yaml index 35eb6f159bcd..6964656d16ef 100644 --- a/packages/shared_preferences/shared_preferences/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/example/pubspec.yaml @@ -20,6 +20,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml index 2838b262885e..bd1272c71d80 100644 --- a/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml @@ -21,6 +21,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml index e3db3c348946..446cea1e0508 100644 --- a/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_ios/example/pubspec.yaml @@ -21,6 +21,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml index 65a61112e588..9418c0581ed7 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml @@ -20,6 +20,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml index 96d8b5f0ba2e..f650fb7a6268 100644 --- a/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_macos/example/pubspec.yaml @@ -21,6 +21,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml index 5e637144a257..050275489efa 100644 --- a/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml @@ -8,6 +8,7 @@ environment: dependencies: flutter: sdk: flutter + shared_preferences_platform_interface: ^2.0.0 shared_preferences_web: path: ../ diff --git a/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml index b1f6db4f67c7..43c2145b32ae 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml @@ -20,6 +20,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/url_launcher/url_launcher/example/pubspec.yaml b/packages/url_launcher/url_launcher/example/pubspec.yaml index 77af2dc2b65a..573dc0d9ed01 100644 --- a/packages/url_launcher/url_launcher/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher/example/pubspec.yaml @@ -22,6 +22,8 @@ dev_dependencies: build_runner: ^2.1.10 flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter mockito: ^5.0.0 diff --git a/packages/url_launcher/url_launcher_android/example/pubspec.yaml b/packages/url_launcher/url_launcher_android/example/pubspec.yaml index 52d761baa120..6c922c7a0f7d 100644 --- a/packages/url_launcher/url_launcher_android/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/example/pubspec.yaml @@ -16,10 +16,13 @@ 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: ../ + url_launcher_platform_interface: ^2.0.3 dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter mockito: ^5.0.0 diff --git a/packages/url_launcher/url_launcher_ios/example/pubspec.yaml b/packages/url_launcher/url_launcher_ios/example/pubspec.yaml index 84748f17a781..9a134c747fa4 100644 --- a/packages/url_launcher/url_launcher_ios/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_ios/example/pubspec.yaml @@ -16,10 +16,13 @@ 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: ../ + url_launcher_platform_interface: ^2.0.3 dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter mockito: ^5.0.0 diff --git a/packages/url_launcher/url_launcher_linux/example/pubspec.yaml b/packages/url_launcher/url_launcher_linux/example/pubspec.yaml index 0d5357055e96..17effeb1ffcb 100644 --- a/packages/url_launcher/url_launcher_linux/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_linux/example/pubspec.yaml @@ -21,6 +21,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/url_launcher/url_launcher_macos/example/pubspec.yaml b/packages/url_launcher/url_launcher_macos/example/pubspec.yaml index efa4f63bd48f..3b802ea229ba 100644 --- a/packages/url_launcher/url_launcher_macos/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_macos/example/pubspec.yaml @@ -21,6 +21,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/url_launcher/url_launcher_web/example/pubspec.yaml b/packages/url_launcher/url_launcher_web/example/pubspec.yaml index 15bdc70517d8..881cb29c7599 100644 --- a/packages/url_launcher/url_launcher_web/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/example/pubspec.yaml @@ -15,8 +15,11 @@ dev_dependencies: sdk: flutter flutter_test: sdk: flutter + flutter_web_plugins: + sdk: flutter integration_test: sdk: flutter mockito: ^5.0.0 + url_launcher_platform_interface: ^2.0.3 url_launcher_web: path: ../ diff --git a/packages/url_launcher/url_launcher_windows/example/pubspec.yaml b/packages/url_launcher/url_launcher_windows/example/pubspec.yaml index 3bd8492709af..966d32c779e8 100644 --- a/packages/url_launcher/url_launcher_windows/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/example/pubspec.yaml @@ -21,6 +21,8 @@ dependencies: dev_dependencies: flutter_driver: sdk: flutter + flutter_test: + sdk: flutter integration_test: sdk: flutter diff --git a/packages/video_player/video_player_web/example/pubspec.yaml b/packages/video_player/video_player_web/example/pubspec.yaml index 9c0a9e188daf..57728f323d55 100644 --- a/packages/video_player/video_player_web/example/pubspec.yaml +++ b/packages/video_player/video_player_web/example/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: flutter: sdk: flutter js: ^0.6.0 + video_player_platform_interface: ">=4.2.0 <6.0.0" video_player_web: path: ../ diff --git a/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml index db72a146c110..de6980a736cc 100644 --- a/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml @@ -8,9 +8,9 @@ environment: dependencies: flutter: sdk: flutter - + flutter_driver: + sdk: flutter path_provider: ^2.0.6 - webview_flutter_android: # When depending on this package from a real application you should use: # webview_flutter: ^x.y.z @@ -18,11 +18,10 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ + webview_flutter_platform_interface: ^1.8.0 dev_dependencies: espresso: ^0.2.0 - flutter_driver: - sdk: flutter flutter_test: sdk: flutter integration_test: diff --git a/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml index fbeb44923d83..7ceb586a9316 100644 --- a/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml @@ -10,6 +10,7 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter + webview_flutter_platform_interface: ^1.8.0 webview_flutter_web: # When depending on this package from a real application you should use: # webview_flutter_web: ^x.y.z diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml index 1daa96854f90..495f5aeb87e6 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml @@ -8,9 +8,8 @@ environment: dependencies: flutter: sdk: flutter - path_provider: ^2.0.6 - + webview_flutter_platform_interface: ^1.8.0 webview_flutter_wkwebview: # When depending on this package from a real application you should use: # webview_flutter: ^x.y.z From 0825da164282ac69559e702c9b284dde996adc97 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Sep 2022 11:11:26 +0000 Subject: [PATCH 755/844] [gh_actions]: Bump github/codeql-action from 2.1.24 to 2.1.25 (#6491) --- .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 6650853754af..e14935f4e455 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -50,6 +50,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@904260d7d935dff982205cbdb42025ce30b7a34f + uses: github/codeql-action/upload-sarif@86f3159a697a097a813ad9bfa0002412d97690a4 with: sarif_file: results.sarif From 9c9185c8919f4b984217be5212bc6fea4013bc92 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 27 Sep 2022 11:52:47 -0400 Subject: [PATCH 756/844] Roll Flutter from 7b6074fbc542 to f57c2fe8ff49 (23 revisions) (#6502) --- .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 06ef561146db..aa1942ca8ea6 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -7b6074fbc542efb41da5618bc97711fac6ab7dee +f57c2fe8ff4993fbe017d1f5fb7c7a4145d66492 From d4c69135254b8aa3ad9b0649b441782b1413f1f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Sep 2022 16:43:39 +0000 Subject: [PATCH 757/844] [in_app_pur]: Bump json from 20180813 to 20220924 in /packages/in_app_purchase/in_app_purchase/example/android/app (#6481) --- .../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 d0c62753faa1..9bb39660cf05 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 @@ -109,7 +109,7 @@ dependencies { implementation 'com.android.billingclient:billing:3.0.2' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:4.7.0' - testImplementation 'org.json:json:20180813' + testImplementation 'org.json:json:20220924' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } From 6c84f5d52f21ae8955b1e4eadfec5b4644d689bb Mon Sep 17 00:00:00 2001 From: eugerossetto <101729072+eugerossetto@users.noreply.github.com> Date: Tue, 27 Sep 2022 14:31:51 -0300 Subject: [PATCH 758/844] [file_selector] Annotate all parameters of XTypeGroup with // ignore: prefer_const_literals_to_create_immutables (#6499) --- .../file_selector/example/lib/open_image_page.dart | 2 +- .../file_selector/example/lib/open_multiple_images_page.dart | 2 +- .../file_selector/example/lib/open_text_page.dart | 2 ++ .../file_selector/file_selector/test/file_selector_test.dart | 3 +++ .../file_selector_ios/example/lib/open_image_page.dart | 4 ++++ .../example/lib/open_multiple_images_page.dart | 3 +++ .../file_selector_ios/example/lib/open_text_page.dart | 4 ++++ .../file_selector_ios/test/file_selector_ios_test.dart | 3 +++ .../file_selector_linux/example/lib/open_image_page.dart | 2 ++ .../example/lib/open_multiple_images_page.dart | 4 ++++ .../file_selector_linux/example/lib/open_text_page.dart | 2 ++ .../file_selector_linux/test/file_selector_linux_test.dart | 3 +++ .../file_selector_macos/example/lib/open_image_page.dart | 2 ++ .../example/lib/open_multiple_images_page.dart | 4 ++++ .../file_selector_macos/example/lib/open_text_page.dart | 2 ++ .../file_selector_macos/test/file_selector_macos_test.dart | 3 +++ .../example/integration_test/file_selector_web_test.dart | 3 +++ packages/file_selector/file_selector_web/test/utils_test.dart | 3 +++ .../file_selector_windows/example/lib/open_image_page.dart | 2 ++ .../example/lib/open_multiple_images_page.dart | 4 ++++ .../file_selector_windows/example/lib/open_text_page.dart | 2 ++ .../test/file_selector_windows_test.dart | 3 +++ 22 files changed, 60 insertions(+), 2 deletions(-) diff --git a/packages/file_selector/file_selector/example/lib/open_image_page.dart b/packages/file_selector/file_selector/example/lib/open_image_page.dart index 6e3ae8940ce4..b72d4e5f562f 100644 --- a/packages/file_selector/file_selector/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_image_page.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 -// ignore_for_file: prefer_const_constructors +// ignore_for_file: prefer_const_constructors, prefer_const_literals_to_create_immutables import 'dart:io'; diff --git a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart index 0d70380cd906..87549e046549 100644 --- a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 -// ignore_for_file: prefer_const_constructors +// ignore_for_file: prefer_const_constructors, prefer_const_literals_to_create_immutables import 'dart:io'; diff --git a/packages/file_selector/file_selector/example/lib/open_text_page.dart b/packages/file_selector/file_selector/example/lib/open_text_page.dart index 8d5f7c863c53..04d48afc21e4 100644 --- a/packages/file_selector/file_selector/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_text_page.dart @@ -16,6 +16,8 @@ class OpenTextPage extends StatelessWidget { // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'text', + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_literals_to_create_immutables extensions: ['txt', 'json'], ); // This demonstrates using an initial directory for the prompt, which should diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index cd7d540f954d..0bef946142ee 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 +// ignore_for_file: prefer_const_literals_to_create_immutables + import 'package:file_selector/file_selector.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/packages/file_selector/file_selector_ios/example/lib/open_image_page.dart b/packages/file_selector/file_selector_ios/example/lib/open_image_page.dart index 740dcc2c1658..eff6c4f7c028 100644 --- a/packages/file_selector/file_selector_ios/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_ios/example/lib/open_image_page.dart @@ -19,7 +19,11 @@ class OpenImagePage extends StatelessWidget { // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'images', + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_literals_to_create_immutables extensions: ['jpg', 'png'], + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_literals_to_create_immutables macUTIs: ['public.image'], ); final XFile? file = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_ios/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_ios/example/lib/open_multiple_images_page.dart index 934a694d503c..0035ebcba5eb 100644 --- a/packages/file_selector/file_selector_ios/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_ios/example/lib/open_multiple_images_page.dart @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 +// ignore_for_file: prefer_const_literals_to_create_immutables + import 'dart:io'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; diff --git a/packages/file_selector/file_selector_ios/example/lib/open_text_page.dart b/packages/file_selector/file_selector_ios/example/lib/open_text_page.dart index 4b07d93a66bd..b91195ef4a3d 100644 --- a/packages/file_selector/file_selector_ios/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_ios/example/lib/open_text_page.dart @@ -16,7 +16,11 @@ class OpenTextPage extends StatelessWidget { // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'text', + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_literals_to_create_immutables extensions: ['txt', 'json'], + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_literals_to_create_immutables macUTIs: ['public.text'], ); final XFile? file = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_ios/test/file_selector_ios_test.dart b/packages/file_selector/file_selector_ios/test/file_selector_ios_test.dart index 70e5f83a6b0a..a388fe951a4c 100644 --- a/packages/file_selector/file_selector_ios/test/file_selector_ios_test.dart +++ b/packages/file_selector/file_selector_ios/test/file_selector_ios_test.dart @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 +// ignore_for_file: prefer_const_literals_to_create_immutables + import 'package:file_selector_ios/file_selector_ios.dart'; import 'package:file_selector_ios/src/messages.g.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; diff --git a/packages/file_selector/file_selector_linux/example/lib/open_image_page.dart b/packages/file_selector/file_selector_linux/example/lib/open_image_page.dart index 8f11d048189d..1925b27002df 100644 --- a/packages/file_selector/file_selector_linux/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_linux/example/lib/open_image_page.dart @@ -19,6 +19,8 @@ class OpenImagePage extends StatelessWidget { // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'images', + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_literals_to_create_immutables extensions: ['jpg', 'png'], ); final XFile? file = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_linux/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_linux/example/lib/open_multiple_images_page.dart index 04c8310616eb..ee7e7d1a967d 100644 --- a/packages/file_selector/file_selector_linux/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_linux/example/lib/open_multiple_images_page.dart @@ -19,12 +19,16 @@ class OpenMultipleImagesPage extends StatelessWidget { // ignore: prefer_const_constructors final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_literals_to_create_immutables extensions: ['jpg', 'jpeg'], ); // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 // ignore: prefer_const_constructors final XTypeGroup pngTypeGroup = XTypeGroup( label: 'PNGs', + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_literals_to_create_immutables extensions: ['png'], ); final List files = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_linux/example/lib/open_text_page.dart b/packages/file_selector/file_selector_linux/example/lib/open_text_page.dart index 89bc1c70c3ca..d5e8d462de1d 100644 --- a/packages/file_selector/file_selector_linux/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_linux/example/lib/open_text_page.dart @@ -16,6 +16,8 @@ class OpenTextPage extends StatelessWidget { // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'text', + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_literals_to_create_immutables extensions: ['txt', 'json'], ); final XFile? file = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_linux/test/file_selector_linux_test.dart b/packages/file_selector/file_selector_linux/test/file_selector_linux_test.dart index a85340875041..54111c27140e 100644 --- a/packages/file_selector/file_selector_linux/test/file_selector_linux_test.dart +++ b/packages/file_selector/file_selector_linux/test/file_selector_linux_test.dart @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 +// ignore_for_file: prefer_const_literals_to_create_immutables + import 'package:file_selector_linux/file_selector_linux.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter/services.dart'; diff --git a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart index 8f11d048189d..1925b27002df 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart @@ -19,6 +19,8 @@ class OpenImagePage extends StatelessWidget { // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'images', + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_literals_to_create_immutables extensions: ['jpg', 'png'], ); final XFile? file = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart index 04c8310616eb..ee7e7d1a967d 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart @@ -19,12 +19,16 @@ class OpenMultipleImagesPage extends StatelessWidget { // ignore: prefer_const_constructors final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_literals_to_create_immutables extensions: ['jpg', 'jpeg'], ); // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 // ignore: prefer_const_constructors final XTypeGroup pngTypeGroup = XTypeGroup( label: 'PNGs', + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_literals_to_create_immutables extensions: ['png'], ); final List files = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart index 89bc1c70c3ca..d5e8d462de1d 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart @@ -16,6 +16,8 @@ class OpenTextPage extends StatelessWidget { // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'text', + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_literals_to_create_immutables extensions: ['txt', 'json'], ); final XFile? file = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart b/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart index 2f98881d82f4..29702a5421b1 100644 --- a/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart +++ b/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 +// ignore_for_file: prefer_const_literals_to_create_immutables + import 'package:file_selector_macos/file_selector_macos.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter/services.dart'; 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 206bc35d550b..5f476d937977 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 @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 +// ignore_for_file: prefer_const_literals_to_create_immutables + import 'dart:html'; import 'dart:typed_data'; diff --git a/packages/file_selector/file_selector_web/test/utils_test.dart b/packages/file_selector/file_selector_web/test/utils_test.dart index f335de057a12..dcb7c3d8c627 100644 --- a/packages/file_selector/file_selector_web/test/utils_test.dart +++ b/packages/file_selector/file_selector_web/test/utils_test.dart @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 +// ignore_for_file: prefer_const_literals_to_create_immutables + import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:file_selector_web/src/utils.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart index 8f11d048189d..1925b27002df 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart @@ -19,6 +19,8 @@ class OpenImagePage extends StatelessWidget { // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'images', + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_literals_to_create_immutables extensions: ['jpg', 'png'], ); final XFile? file = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart index 04c8310616eb..ee7e7d1a967d 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart @@ -19,12 +19,16 @@ class OpenMultipleImagesPage extends StatelessWidget { // ignore: prefer_const_constructors final XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_literals_to_create_immutables extensions: ['jpg', 'jpeg'], ); // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 // ignore: prefer_const_constructors final XTypeGroup pngTypeGroup = XTypeGroup( label: 'PNGs', + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_literals_to_create_immutables extensions: ['png'], ); final List files = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart index 89bc1c70c3ca..d5e8d462de1d 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart @@ -16,6 +16,8 @@ class OpenTextPage extends StatelessWidget { // ignore: prefer_const_constructors final XTypeGroup typeGroup = XTypeGroup( label: 'text', + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_literals_to_create_immutables extensions: ['txt', 'json'], ); final XFile? file = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart b/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart index 5792099de768..2d8dc4b78688 100644 --- a/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart +++ b/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 +// ignore_for_file: prefer_const_literals_to_create_immutables + import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:file_selector_windows/file_selector_windows.dart'; import 'package:file_selector_windows/src/messages.g.dart'; From 709e7d4f7eda40b0d6b54a18775e0e64e979c9b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Sep 2022 17:31:53 +0000 Subject: [PATCH 759/844] [sign_in]: Bump play-services-auth from 20.0.1 to 20.3.0 in /packages/google_sign_in/google_sign_in_android/android (#6361) --- packages/google_sign_in/google_sign_in_android/CHANGELOG.md | 1 + .../google_sign_in/google_sign_in_android/android/build.gradle | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) 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 b10c578cd125..342166a8a6af 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -2,6 +2,7 @@ * Corrects typos in plugin error logs and removes not actionable warnings. * Updates minimum Flutter version to 2.10. +* Updates play-services-auth version to 20.3.0. ## 6.1.0 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 0c891918aa08..3384029d666a 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 @@ -48,7 +48,7 @@ android { } dependencies { - implementation 'com.google.android.gms:play-services-auth:20.0.1' + implementation 'com.google.android.gms:play-services-auth:20.3.0' implementation 'com.google.guava:guava:28.1-android' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:4.7.0' From d5a4157ff4f23078df0698794a8f36ebee84619a Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 27 Sep 2022 14:39:06 -0400 Subject: [PATCH 760/844] [ci] Update Dockerfile (#6477) --- .ci/Dockerfile | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/.ci/Dockerfile b/.ci/Dockerfile index 59d4064f0a91..bec62f22cc89 100644 --- a/.ci/Dockerfile +++ b/.ci/Dockerfile @@ -1,47 +1,40 @@ # The Flutter version is not important here, since the CI scripts update Flutter # before running. What matters is that the base image is pinned to minimize # unintended changes when modifying this file. -FROM cirrusci/flutter@sha256:505fe8bce2896c75b4df9ccf500b1604155bf932af7465ffcc66fcae8612f82f +# This is the hash for the 3.0.0 image. +FROM cirrusci/flutter@sha256:0224587bba33241cf908184283ec2b544f1b672d87043ead1c00521c368cf844 RUN apt-get update -y +# Set up Firebase Test Lab requirements. RUN apt-get install -y --no-install-recommends gnupg - -# Add repo for gcloud sdk and install it RUN echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | \ sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list - RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | \ sudo apt-key --keyring /usr/share/keyrings/cloud.google.gpg add - - RUN apt-get update && apt-get install -y google-cloud-sdk && \ gcloud config set core/disable_usage_reporting true && \ gcloud config set component_manager/disable_update_check true -RUN yes | sdkmanager \ - "platforms;android-27" \ - "build-tools;27.0.3" \ - "extras;google;m2repository" \ - "extras;android;m2repository" - -RUN yes | sdkmanager --licenses - -# Install formatter. +# Install formatter for C-based languages. RUN apt-get install -y clang-format -# Install xvfb to allow running headless -RUN apt-get install -y xvfb libegl1-mesa -# Install Linux desktop build tool requirements. +# Install Linux desktop requirements: +# - build tools. RUN apt-get install -y clang cmake ninja-build file pkg-config -# Install necessary libraries. +# - libraries. RUN apt-get install -y libgtk-3-dev libblkid-dev liblzma-dev libgcrypt20-dev +# - xvfb to allow running headless. +RUN apt-get install -y xvfb libegl1-mesa -# Add repo for Google Chrome and install it +# Install Chrome and make it the default browser, for url_launcher tests. +# IMPORTANT: Web tests should use a pinned version of Chromium, not this, since +# this isn't pinned, so any time the docker image is re-created the version of +# Chrome may change. RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add - RUN echo 'deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main' | sudo tee /etc/apt/sources.list.d/google-chrome.list RUN apt-get update && apt-get install -y --no-install-recommends google-chrome-stable - -# Make Chrome the default so http: and file: has a handler for url_launcher tests. +# Make Chrome the default for http:, https: and file:. RUN apt-get install -y xdg-utils RUN xdg-settings set default-web-browser google-chrome.desktop RUN xdg-mime default google-chrome.desktop inode/directory From b3018c887653050bb711e39a52d68533c528c259 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 27 Sep 2022 15:41:16 -0400 Subject: [PATCH 761/844] Update deprecated analysis options (#6505) --- analysis_options.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 5f10583a5c1a..34f50180906d 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -23,8 +23,8 @@ analyzer: strong-mode: - implicit-casts: false - implicit-dynamic: false + strict-casts: true + strict-raw-types: true errors: # treat missing required parameters as a warning (not a hint) missing_required_param: warning From 2592df96f6ae2f1d2687de9fa4f9eae87e6e378d Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 27 Sep 2022 15:43:04 -0400 Subject: [PATCH 762/844] [tool] Clean up "plugin" references (#6503) --- .github/workflows/release.yml | 2 +- script/tool/CHANGELOG.md | 8 +++ script/tool/README.md | 26 ++++---- ...ugin_command.dart => package_command.dart} | 62 ++++++++++--------- .../src/common/package_looping_command.dart | 4 +- .../lib/src/common/package_state_utils.dart | 2 +- .../src/create_all_plugins_app_command.dart | 4 +- .../tool/lib/src/drive_examples_command.dart | 2 +- script/tool/lib/src/format_command.dart | 4 +- .../tool/lib/src/license_check_command.dart | 4 +- script/tool/lib/src/list_command.dart | 14 ++--- script/tool/lib/src/main.dart | 4 +- .../lib/src/make_deps_path_based_command.dart | 6 +- .../tool/lib/src/publish_check_command.dart | 2 +- ...ugin_command.dart => publish_command.dart} | 10 +-- .../tool/lib/src/version_check_command.dart | 4 +- script/tool/pubspec.yaml | 2 +- script/tool/test/analyze_command_test.dart | 8 +-- .../test/common/git_version_finder_test.dart | 2 +- ...nd_test.dart => package_command_test.dart} | 20 +++--- ...s.dart => package_command_test.mocks.dart} | 0 .../common/package_looping_command_test.dart | 21 +++---- .../tool/test/custom_test_command_test.dart | 24 +++---- .../test/dependabot_check_command_test.dart | 2 +- .../federation_safety_check_command_test.dart | 2 +- script/tool/test/format_command_test.dart | 10 +-- .../tool/test/license_check_command_test.dart | 2 +- script/tool/test/list_command_test.dart | 20 +++--- .../make_deps_path_based_command_test.dart | 2 +- ...nd_test.dart => publish_command_test.dart} | 62 +++++++++---------- .../test/update_excerpts_command_test.dart | 2 +- .../update_release_info_command_test.dart | 2 +- .../tool/test/version_check_command_test.dart | 2 +- 33 files changed, 174 insertions(+), 167 deletions(-) rename script/tool/lib/src/common/{plugin_command.dart => package_command.dart} (92%) rename script/tool/lib/src/{publish_plugin_command.dart => publish_command.dart} (98%) rename script/tool/test/common/{plugin_command_test.dart => package_command_test.dart} (98%) rename script/tool/test/common/{plugin_command_test.mocks.dart => package_command_test.mocks.dart} (100%) rename script/tool/test/{publish_plugin_command_test.dart => publish_command_test.dart} (94%) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 06154081619b..5ff15569f842 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,5 +50,5 @@ jobs: run: | git config --global user.name ${{ secrets.USER_NAME }} git config --global user.email ${{ secrets.USER_EMAIL }} - dart ./script/tool/lib/src/main.dart publish-plugin --all-changed --base-sha=HEAD~ --skip-confirmation --remote=origin + dart ./script/tool/lib/src/main.dart publish --all-changed --base-sha=HEAD~ --skip-confirmation --remote=origin env: {PUB_CREDENTIALS: "${{ secrets.PUB_CREDENTIALS }}"} diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 2417d0fa833c..830d3ca931e6 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.11 + +* Renames `publish-plugin` to `publish`. +* Renames arguments to `list`: + * `--package` now lists top-level packages (previously `--plugin`). + * `--package-or-subpackage` now lists top-level packages (previously + `--package`). + ## 0.10.0+1 * Recognizes `run_test.sh` as a developer-only file in `version-check`. diff --git a/script/tool/README.md b/script/tool/README.md index 0e44bf16c263..9f0ac84145f2 100644 --- a/script/tool/README.md +++ b/script/tool/README.md @@ -54,7 +54,7 @@ dart pub global run flutter_plugin_tools ## Commands Run with `--help` for a full list of commands and arguments, but the -following shows a number of common commands being run for a specific plugin. +following shows a number of common commands being run for a specific package. All examples assume running from source; see above for running the published version instead. @@ -71,29 +71,29 @@ command is targetting. An package name can be any of: ```sh cd -dart run ./script/tool/bin/flutter_plugin_tools.dart format --packages plugin_name +dart run ./script/tool/bin/flutter_plugin_tools.dart format --packages package_name ``` ### Run the Dart Static Analyzer ```sh cd -dart run ./script/tool/bin/flutter_plugin_tools.dart analyze --packages plugin_name +dart run ./script/tool/bin/flutter_plugin_tools.dart analyze --packages package_name ``` ### Run Dart Unit Tests ```sh cd -dart run ./script/tool/bin/flutter_plugin_tools.dart test --packages plugin_name +dart run ./script/tool/bin/flutter_plugin_tools.dart test --packages package_name ``` ### Run Dart Integration Tests ```sh cd -dart run ./script/tool/bin/flutter_plugin_tools.dart build-examples --apk --packages plugin_name -dart run ./script/tool/bin/flutter_plugin_tools.dart drive-examples --android --packages plugin_name +dart run ./script/tool/bin/flutter_plugin_tools.dart build-examples --apk --packages package_name +dart run ./script/tool/bin/flutter_plugin_tools.dart drive-examples --android --packages package_name ``` Replace `--apk`/`--android` with the platform you want to test against @@ -110,11 +110,11 @@ Examples: ```sh cd # Run just unit tests for iOS and Android: -dart run ./script/tool/bin/flutter_plugin_tools.dart native-test --ios --android --no-integration --packages plugin_name +dart run ./script/tool/bin/flutter_plugin_tools.dart native-test --ios --android --no-integration --packages package_name # Run all tests for macOS: -dart run ./script/tool/bin/flutter_plugin_tools.dart native-test --macos --packages plugin_name +dart run ./script/tool/bin/flutter_plugin_tools.dart native-test --macos --packages package_name # Run all tests for Windows: -dart run ./script/tool/bin/flutter_plugin_tools.dart native-test --windows --packages plugin_name +dart run ./script/tool/bin/flutter_plugin_tools.dart native-test --windows --packages package_name ``` ### Update README.md from Example Sources @@ -125,7 +125,7 @@ before running this command. ```sh cd -dart run ./script/tool/bin/flutter_plugin_tools.dart update-excerpts --packages plugin_name +dart run ./script/tool/bin/flutter_plugin_tools.dart update-excerpts --packages package_name ``` ### Update CHANGELOG and Version @@ -165,18 +165,18 @@ on the Flutter Wiki first. ```sh cd git checkout -dart run ./script/tool/bin/flutter_plugin_tools.dart publish-plugin --packages +dart run ./script/tool/bin/flutter_plugin_tools.dart publish --packages ``` By default the tool tries to push tags to the `upstream` remote, but some additional settings can be configured. Run `dart run ./script/tool/bin/flutter_plugin_tools.dart -publish-plugin --help` for more usage information. +publish --help` for more usage information. The tool wraps `pub publish` for pushing the package to pub, and then will automatically use git to try to create and push tags. It has some additional safety checking around `pub publish` too. By default `pub publish` publishes _everything_, including untracked or uncommitted files in version control. -`publish-plugin` will first check the status of the local +`publish` will first check the status of the local directory and refuse to publish if there are any mismatched files with version control present. diff --git a/script/tool/lib/src/common/plugin_command.dart b/script/tool/lib/src/common/package_command.dart similarity index 92% rename from script/tool/lib/src/common/plugin_command.dart rename to script/tool/lib/src/common/package_command.dart index 843768fd4c61..60d7ecc80076 100644 --- a/script/tool/lib/src/common/plugin_command.dart +++ b/script/tool/lib/src/common/package_command.dart @@ -34,9 +34,9 @@ class PackageEnumerationEntry { /// Interface definition for all commands in this tool. // TODO(stuartmorgan): Move most of this logic to PackageLoopingCommand. -abstract class PluginCommand extends Command { +abstract class PackageCommand extends Command { /// Creates a command to operate on [packagesDir] with the given environment. - PluginCommand( + PackageCommand( this.packagesDir, { this.processRunner = const ProcessRunner(), this.platform = const LocalPlatform(), @@ -47,7 +47,7 @@ abstract class PluginCommand extends Command { help: 'Specifies which packages the command should run on (before sharding).\n', valueHelp: 'package1,package2,...', - aliases: [_pluginsArg], + aliases: [_pluginsLegacyAliasArg], ); argParser.addOption( _shardIndexArg, @@ -58,7 +58,7 @@ abstract class PluginCommand extends Command { ); argParser.addOption( _shardCountArg, - help: 'Specifies the number of shards into which plugins are divided.', + help: 'Specifies the number of shards into which packages are divided.', valueHelp: 'n', defaultsTo: '1', ); @@ -71,7 +71,7 @@ abstract class PluginCommand extends Command { defaultsTo: [], ); argParser.addFlag(_runOnChangedPackagesArg, - help: 'Run the command on changed packages/plugins.\n' + help: 'Run the command on changed packages.\n' 'If no packages have changed, or if there have been changes that may\n' 'affect all packages, the command runs on all packages.\n' 'Packages excluded with $_excludeArg are excluded even if changed.\n' @@ -106,13 +106,13 @@ abstract class PluginCommand extends Command { static const String _logTimingArg = 'log-timing'; static const String _packagesArg = 'packages'; static const String _packagesForBranchArg = 'packages-for-branch'; - static const String _pluginsArg = 'plugins'; + static const String _pluginsLegacyAliasArg = 'plugins'; static const String _runOnChangedPackagesArg = 'run-on-changed-packages'; static const String _runOnDirtyPackagesArg = 'run-on-dirty-packages'; static const String _shardCountArg = 'shardCount'; static const String _shardIndexArg = 'shardIndex'; - /// The directory containing the plugin packages. + /// The directory containing the packages. final Directory packagesDir; /// The process runner. @@ -221,7 +221,7 @@ abstract class PluginCommand extends Command { _shardCount = shardCount; } - /// Returns the set of plugins to exclude based on the `--exclude` argument. + /// Returns the set of packages to exclude based on the `--exclude` argument. Set getExcludedPackageNames() { final Set excludedPackages = _excludedPackages ?? getStringListArg(_excludeArg).expand((String item) { @@ -250,22 +250,22 @@ abstract class PluginCommand extends Command { Stream getTargetPackages( {bool filterExcluded = true}) async* { // To avoid assuming consistency of `Directory.list` across command - // invocations, we collect and sort the plugin folders before sharding. + // invocations, we collect and sort the package folders before sharding. // This is considered an implementation detail which is why the API still // uses streams. - final List allPlugins = + final List allPackages = await _getAllPackages().toList(); - allPlugins.sort((PackageEnumerationEntry p1, PackageEnumerationEntry p2) => + allPackages.sort((PackageEnumerationEntry p1, PackageEnumerationEntry p2) => p1.package.path.compareTo(p2.package.path)); - final int shardSize = allPlugins.length ~/ shardCount + - (allPlugins.length % shardCount == 0 ? 0 : 1); - final int start = min(shardIndex * shardSize, allPlugins.length); - final int end = min(start + shardSize, allPlugins.length); - - for (final PackageEnumerationEntry plugin - in allPlugins.sublist(start, end)) { - if (!(filterExcluded && plugin.excluded)) { - yield plugin; + final int shardSize = allPackages.length ~/ shardCount + + (allPackages.length % shardCount == 0 ? 0 : 1); + final int start = min(shardIndex * shardSize, allPackages.length); + final int end = min(start + shardSize, allPackages.length); + + for (final PackageEnumerationEntry package + in allPackages.sublist(start, end)) { + if (!(filterExcluded && package.excluded)) { + yield package; } } } @@ -330,7 +330,7 @@ abstract class PluginCommand extends Command { runOnChangedPackages = false; } - final Set excludedPluginNames = getExcludedPackageNames(); + final Set excludedPackageNames = getExcludedPackageNames(); if (runOnChangedPackages) { final GitVersionFinder gitVersionFinder = await retrieveVersionFinder(); @@ -368,15 +368,16 @@ abstract class PluginCommand extends Command { ]) { await for (final FileSystemEntity entity in dir.list(followLinks: false)) { - // A top-level Dart package is a plugin package. + // 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: excludedPluginNames.contains(entity.basename)); + excluded: excludedPackageNames.contains(entity.basename)); } } else if (entity is Directory) { - // Look for Dart packages under this top-level 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)) { @@ -394,7 +395,7 @@ abstract class PluginCommand extends Command { packages.intersection(possibleMatches).isNotEmpty) { yield PackageEnumerationEntry( RepositoryPackage(subdir as Directory), - excluded: excludedPluginNames + excluded: excludedPackageNames .intersection(possibleMatches) .isNotEmpty); } @@ -415,11 +416,12 @@ abstract class PluginCommand extends Command { /// stream. Stream getTargetPackagesAndSubpackages( {bool filterExcluded = true}) async* { - await for (final PackageEnumerationEntry plugin + await for (final PackageEnumerationEntry package in getTargetPackages(filterExcluded: filterExcluded)) { - yield plugin; - yield* getSubpackages(plugin.package).map((RepositoryPackage package) => - PackageEnumerationEntry(package, excluded: plugin.excluded)); + yield package; + yield* getSubpackages(package.package).map( + (RepositoryPackage subPackage) => + PackageEnumerationEntry(subPackage, excluded: package.excluded)); } } @@ -524,7 +526,7 @@ abstract class PluginCommand extends Command { } // Returns true if one or more files changed that have the potential to affect - // any plugin (e.g., CI script changes). + // any packages (e.g., CI script changes). bool _changesRequireFullTest(List changedFiles) { const List specialFiles = [ '.ci.yaml', // LUCI config. diff --git a/script/tool/lib/src/common/package_looping_command.dart b/script/tool/lib/src/common/package_looping_command.dart index 9e696e956192..a32ada2c35e8 100644 --- a/script/tool/lib/src/common/package_looping_command.dart +++ b/script/tool/lib/src/common/package_looping_command.dart @@ -12,7 +12,7 @@ import 'package:platform/platform.dart'; import 'package:pub_semver/pub_semver.dart'; import 'core.dart'; -import 'plugin_command.dart'; +import 'package_command.dart'; import 'process_runner.dart'; import 'repository_package.dart'; @@ -82,7 +82,7 @@ class PackageResult { /// An abstract base class for a command that iterates over a set of packages /// controlled by a standard set of flags, running some actions on each package, /// and collecting and reporting the success/failure of those actions. -abstract class PackageLoopingCommand extends PluginCommand { +abstract class PackageLoopingCommand extends PackageCommand { /// Creates a command to operate on [packagesDir] with the given environment. PackageLoopingCommand( Directory packagesDir, { diff --git a/script/tool/lib/src/common/package_state_utils.dart b/script/tool/lib/src/common/package_state_utils.dart index c9465876f290..870956a24b10 100644 --- a/script/tool/lib/src/common/package_state_utils.dart +++ b/script/tool/lib/src/common/package_state_utils.dart @@ -161,7 +161,7 @@ bool _isUnpublishedExampleChange( return exampleComponents.first.toLowerCase() != 'readme.md'; } -// True if the change is only relevant to people working on the plugin. +// True if the change is only relevant to people working on the package. Future _isDevChange(List pathComponents, {GitVersionFinder? git, String? repoPath}) async { return _isTestChange(pathComponents) || diff --git a/script/tool/lib/src/create_all_plugins_app_command.dart b/script/tool/lib/src/create_all_plugins_app_command.dart index 7bf007ac72a5..a23dc83d98f3 100644 --- a/script/tool/lib/src/create_all_plugins_app_command.dart +++ b/script/tool/lib/src/create_all_plugins_app_command.dart @@ -10,13 +10,13 @@ import 'package:pub_semver/pub_semver.dart'; import 'package:pubspec_parse/pubspec_parse.dart'; import 'common/core.dart'; -import 'common/plugin_command.dart'; +import 'common/package_command.dart'; import 'common/repository_package.dart'; const String _outputDirectoryFlag = 'output-dir'; /// A command to create an application that builds all in a single application. -class CreateAllPluginsAppCommand extends PluginCommand { +class CreateAllPluginsAppCommand extends PackageCommand { /// Creates an instance of the builder command. CreateAllPluginsAppCommand( Directory packagesDir, { diff --git a/script/tool/lib/src/drive_examples_command.dart b/script/tool/lib/src/drive_examples_command.dart index 45e20c0f13cf..e8fb11b5f289 100644 --- a/script/tool/lib/src/drive_examples_command.dart +++ b/script/tool/lib/src/drive_examples_command.dart @@ -49,7 +49,7 @@ class DriveExamplesCommand extends PackageLoopingCommand { final String name = 'drive-examples'; @override - final String description = 'Runs driver tests for plugin example apps.\n\n' + final String description = 'Runs driver tests for package example apps.\n\n' 'For each *_test.dart in test_driver/ it drives an application with ' 'either the corresponding test in test_driver (for example, ' 'test_driver/app_test.dart would match test_driver/app.dart), or the ' diff --git a/script/tool/lib/src/format_command.dart b/script/tool/lib/src/format_command.dart index f640cbaa5f6c..cc6936c566e1 100644 --- a/script/tool/lib/src/format_command.dart +++ b/script/tool/lib/src/format_command.dart @@ -11,7 +11,7 @@ import 'package:meta/meta.dart'; import 'package:platform/platform.dart'; import 'common/core.dart'; -import 'common/plugin_command.dart'; +import 'common/package_command.dart'; import 'common/process_runner.dart'; /// In theory this should be 8191, but in practice that was still resulting in @@ -37,7 +37,7 @@ final Uri _googleFormatterUrl = Uri.https('github.com', '/google/google-java-format/releases/download/google-java-format-1.3/google-java-format-1.3-all-deps.jar'); /// A command to format all package code. -class FormatCommand extends PluginCommand { +class FormatCommand extends PackageCommand { /// Creates an instance of the format command. FormatCommand( Directory packagesDir, { diff --git a/script/tool/lib/src/license_check_command.dart b/script/tool/lib/src/license_check_command.dart index 5e74d846c13f..0517bcf43298 100644 --- a/script/tool/lib/src/license_check_command.dart +++ b/script/tool/lib/src/license_check_command.dart @@ -8,7 +8,7 @@ import 'package:path/path.dart' as p; import 'package:platform/platform.dart'; import 'common/core.dart'; -import 'common/plugin_command.dart'; +import 'common/package_command.dart'; const Set _codeFileExtensions = { '.c', @@ -105,7 +105,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. '''; /// Validates that code files have copyright and license blocks. -class LicenseCheckCommand extends PluginCommand { +class LicenseCheckCommand extends PackageCommand { /// Creates a new license check command for [packagesDir]. LicenseCheckCommand(Directory packagesDir, {Platform platform = const LocalPlatform(), GitDir? gitDir}) diff --git a/script/tool/lib/src/list_command.dart b/script/tool/lib/src/list_command.dart index e45c09bfd2ef..b47657e47eff 100644 --- a/script/tool/lib/src/list_command.dart +++ b/script/tool/lib/src/list_command.dart @@ -5,11 +5,11 @@ import 'package:file/file.dart'; import 'package:platform/platform.dart'; -import 'common/plugin_command.dart'; +import 'common/package_command.dart'; import 'common/repository_package.dart'; /// A command to list different types of repository content. -class ListCommand extends PluginCommand { +class ListCommand extends PackageCommand { /// Creates an instance of the list command, whose behavior depends on the /// 'type' argument it provides. ListCommand( @@ -18,14 +18,14 @@ class ListCommand extends PluginCommand { }) : super(packagesDir, platform: platform) { argParser.addOption( _type, - defaultsTo: _plugin, - allowed: [_plugin, _example, _package, _file], + defaultsTo: _package, + allowed: [_package, _example, _allPackage, _file], help: 'What type of file system content to list.', ); } static const String _type = 'type'; - static const String _plugin = 'plugin'; + static const String _allPackage = 'package-or-subpackage'; static const String _example = 'example'; static const String _package = 'package'; static const String _file = 'file'; @@ -39,7 +39,7 @@ class ListCommand extends PluginCommand { @override Future run() async { switch (getStringArg(_type)) { - case _plugin: + case _package: await for (final PackageEnumerationEntry entry in getTargetPackages()) { print(entry.package.path); } @@ -52,7 +52,7 @@ class ListCommand extends PluginCommand { print(package.path); } break; - case _package: + case _allPackage: await for (final PackageEnumerationEntry entry in getTargetPackagesAndSubpackages()) { print(entry.package.path); diff --git a/script/tool/lib/src/main.dart b/script/tool/lib/src/main.dart index ea1ec067c82f..0f73c71e8437 100644 --- a/script/tool/lib/src/main.dart +++ b/script/tool/lib/src/main.dart @@ -25,7 +25,7 @@ import 'list_command.dart'; import 'make_deps_path_based_command.dart'; import 'native_test_command.dart'; import 'publish_check_command.dart'; -import 'publish_plugin_command.dart'; +import 'publish_command.dart'; import 'pubspec_check_command.dart'; import 'readme_check_command.dart'; import 'remove_dev_dependencies.dart'; @@ -69,7 +69,7 @@ void main(List args) { ..addCommand(NativeTestCommand(packagesDir)) ..addCommand(MakeDepsPathBasedCommand(packagesDir)) ..addCommand(PublishCheckCommand(packagesDir)) - ..addCommand(PublishPluginCommand(packagesDir)) + ..addCommand(PublishCommand(packagesDir)) ..addCommand(PubspecCheckCommand(packagesDir)) ..addCommand(ReadmeCheckCommand(packagesDir)) ..addCommand(RemoveDevDependenciesCommand(packagesDir)) diff --git a/script/tool/lib/src/make_deps_path_based_command.dart b/script/tool/lib/src/make_deps_path_based_command.dart index 805dd68a0afe..a09511ad7f42 100644 --- a/script/tool/lib/src/make_deps_path_based_command.dart +++ b/script/tool/lib/src/make_deps_path_based_command.dart @@ -9,7 +9,7 @@ import 'package:pub_semver/pub_semver.dart'; import 'common/core.dart'; import 'common/git_version_finder.dart'; -import 'common/plugin_command.dart'; +import 'common/package_command.dart'; import 'common/repository_package.dart'; const int _exitPackageNotFound = 3; @@ -24,7 +24,7 @@ enum _RewriteOutcome { changed, noChangesNeeded, alreadyChanged } /// where a non-breaking change to a platform interface package of a federated /// plugin would cause post-publish analyzer failures in another package of that /// plugin. -class MakeDepsPathBasedCommand extends PluginCommand { +class MakeDepsPathBasedCommand extends PackageCommand { /// Creates an instance of the command to convert selected dependencies to /// path-based. MakeDepsPathBasedCommand( @@ -150,7 +150,7 @@ class MakeDepsPathBasedCommand extends PluginCommand { return _RewriteOutcome.alreadyChanged; } printError( - 'Plugins with dependency overrides are not currently supported.'); + 'Packages with dependency overrides are not currently supported.'); throw ToolExit(_exitCannotUpdatePubspec); } diff --git a/script/tool/lib/src/publish_check_command.dart b/script/tool/lib/src/publish_check_command.dart index d98a286ff582..38e1b7bdebbb 100644 --- a/script/tool/lib/src/publish_check_command.dart +++ b/script/tool/lib/src/publish_check_command.dart @@ -55,7 +55,7 @@ class PublishCheckCommand extends PackageLoopingCommand { @override final String description = - 'Checks to make sure that a plugin *could* be published.'; + 'Checks to make sure that a package *could* be published.'; final PubVersionFinder _pubVersionFinder; diff --git a/script/tool/lib/src/publish_plugin_command.dart b/script/tool/lib/src/publish_command.dart similarity index 98% rename from script/tool/lib/src/publish_plugin_command.dart rename to script/tool/lib/src/publish_command.dart index cae8edac71ca..e7b3d110c5fa 100644 --- a/script/tool/lib/src/publish_plugin_command.dart +++ b/script/tool/lib/src/publish_command.dart @@ -18,8 +18,8 @@ import 'package:yaml/yaml.dart'; import 'common/core.dart'; import 'common/file_utils.dart'; import 'common/git_version_finder.dart'; +import 'common/package_command.dart'; import 'common/package_looping_command.dart'; -import 'common/plugin_command.dart'; import 'common/process_runner.dart'; import 'common/pub_version_finder.dart'; import 'common/repository_package.dart'; @@ -42,13 +42,13 @@ class _RemoteInfo { /// 2. Tags the release with the format -v. /// 3. Pushes the release to a remote. /// -/// Both 2 and 3 are optional, see `plugin_tools help publish-plugin` for full +/// Both 2 and 3 are optional, see `plugin_tools help publish` for full /// usage information. /// /// [processRunner], [print], and [stdin] can be overriden for easier testing. -class PublishPluginCommand extends PackageLoopingCommand { +class PublishCommand extends PackageLoopingCommand { /// Creates an instance of the publish command. - PublishPluginCommand( + PublishCommand( Directory packagesDir, { ProcessRunner processRunner = const ProcessRunner(), Platform platform = const LocalPlatform(), @@ -100,7 +100,7 @@ class PublishPluginCommand extends PackageLoopingCommand { static const String _tagFormat = '%PACKAGE%-v%VERSION%'; @override - final String name = 'publish-plugin'; + final String name = 'publish'; @override final String description = diff --git a/script/tool/lib/src/version_check_command.dart b/script/tool/lib/src/version_check_command.dart index 235611492b2d..b3be672066d9 100644 --- a/script/tool/lib/src/version_check_command.dart +++ b/script/tool/lib/src/version_check_command.dart @@ -170,7 +170,7 @@ class VersionCheckCommand extends PackageLoopingCommand { @override final String description = - 'Checks if the versions of the plugins have been incremented per pub specification.\n' + 'Checks if the versions of packages have been incremented per pub specification.\n' 'Also checks if the latest version in CHANGELOG matches the version in pubspec.\n\n' 'This command requires "pub" and "flutter" to be in your path.'; @@ -318,7 +318,7 @@ ${indentation}HTTP response: ${pubVersionFinderResponse.httpResponse.body} print('${indentation}Unable to find previous version ' '${getBoolArg(_againstPubFlag) ? 'on pub server' : 'at git base'}.'); logWarning( - '${indentation}If this plugin is not new, something has gone wrong.'); + '${indentation}If this package is not new, something has gone wrong.'); return _CurrentVersionState.validIncrease; // Assume new, thus valid. } diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 9b9d73288552..246c2ade2564 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.10.0+1 +version: 0.11.0 dependencies: args: ^2.1.0 diff --git a/script/tool/test/analyze_command_test.dart b/script/tool/test/analyze_command_test.dart index e10780fa9ecb..e6b910960846 100644 --- a/script/tool/test/analyze_command_test.dart +++ b/script/tool/test/analyze_command_test.dart @@ -37,7 +37,7 @@ void main() { }); test('analyzes all packages', () async { - final RepositoryPackage plugin1 = createFakePlugin('a', packagesDir); + final RepositoryPackage package1 = createFakePackage('a', packagesDir); final RepositoryPackage plugin2 = createFakePlugin('b', packagesDir); await runCapturingPrint(runner, ['analyze']); @@ -45,9 +45,9 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - ProcessCall('flutter', const ['pub', 'get'], plugin1.path), - ProcessCall( - 'dart', const ['analyze', '--fatal-infos'], plugin1.path), + ProcessCall('flutter', const ['pub', 'get'], package1.path), + ProcessCall('dart', const ['analyze', '--fatal-infos'], + package1.path), ProcessCall('flutter', const ['pub', 'get'], plugin2.path), ProcessCall( 'dart', const ['analyze', '--fatal-infos'], plugin2.path), diff --git a/script/tool/test/common/git_version_finder_test.dart b/script/tool/test/common/git_version_finder_test.dart index ad1a26ffc165..d5a5dd4fe876 100644 --- a/script/tool/test/common/git_version_finder_test.dart +++ b/script/tool/test/common/git_version_finder_test.dart @@ -8,7 +8,7 @@ import 'package:flutter_plugin_tools/src/common/git_version_finder.dart'; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; -import 'plugin_command_test.mocks.dart'; +import 'package_command_test.mocks.dart'; void main() { late List?> gitDirCommands; diff --git a/script/tool/test/common/plugin_command_test.dart b/script/tool/test/common/package_command_test.dart similarity index 98% rename from script/tool/test/common/plugin_command_test.dart rename to script/tool/test/common/package_command_test.dart index 8c6b38682418..c3d1ee343ff4 100644 --- a/script/tool/test/common/plugin_command_test.dart +++ b/script/tool/test/common/package_command_test.dart @@ -8,7 +8,7 @@ import 'package:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_plugin_tools/src/common/core.dart'; -import 'package:flutter_plugin_tools/src/common/plugin_command.dart'; +import 'package:flutter_plugin_tools/src/common/package_command.dart'; import 'package:flutter_plugin_tools/src/common/process_runner.dart'; import 'package:git/git.dart'; import 'package:mockito/annotations.dart'; @@ -18,12 +18,12 @@ import 'package:test/test.dart'; import '../mocks.dart'; import '../util.dart'; -import 'plugin_command_test.mocks.dart'; +import 'package_command_test.mocks.dart'; @GenerateMocks([GitDir]) void main() { late RecordingProcessRunner processRunner; - late SamplePluginCommand command; + late SamplePackageCommand command; late CommandRunner runner; late FileSystem fileSystem; late MockPlatform mockPlatform; @@ -49,7 +49,7 @@ void main() { return processRunner.run('git-$gitCommand', arguments); }); processRunner = RecordingProcessRunner(); - command = SamplePluginCommand( + command = SamplePackageCommand( packagesDir, processRunner: processRunner, platform: mockPlatform, @@ -282,7 +282,7 @@ packages/plugin1/plugin1/plugin1.dart }); test('returns subpackages after the enclosing package', () async { - final SamplePluginCommand localCommand = SamplePluginCommand( + final SamplePackageCommand localCommand = SamplePackageCommand( packagesDir, processRunner: processRunner, platform: mockPlatform, @@ -848,7 +848,7 @@ packages/b_package/lib/src/foo.dart ]; for (int i = 0; i < expectedShards.length; ++i) { - final SamplePluginCommand localCommand = SamplePluginCommand( + final SamplePackageCommand localCommand = SamplePackageCommand( packagesDir, processRunner: processRunner, platform: mockPlatform, @@ -892,7 +892,7 @@ packages/b_package/lib/src/foo.dart ]; for (int i = 0; i < expectedShards.length; ++i) { - final SamplePluginCommand localCommand = SamplePluginCommand( + final SamplePackageCommand localCommand = SamplePackageCommand( packagesDir, processRunner: processRunner, platform: mockPlatform, @@ -945,7 +945,7 @@ packages/b_package/lib/src/foo.dart createFakePackage('package9', packagesDir); for (int i = 0; i < expectedShards.length; ++i) { - final SamplePluginCommand localCommand = SamplePluginCommand( + final SamplePackageCommand localCommand = SamplePackageCommand( packagesDir, processRunner: processRunner, platform: mockPlatform, @@ -971,8 +971,8 @@ packages/b_package/lib/src/foo.dart }); } -class SamplePluginCommand extends PluginCommand { - SamplePluginCommand( +class SamplePackageCommand extends PackageCommand { + SamplePackageCommand( Directory packagesDir, { ProcessRunner processRunner = const ProcessRunner(), Platform platform = const LocalPlatform(), diff --git a/script/tool/test/common/plugin_command_test.mocks.dart b/script/tool/test/common/package_command_test.mocks.dart similarity index 100% rename from script/tool/test/common/plugin_command_test.mocks.dart rename to script/tool/test/common/package_command_test.mocks.dart diff --git a/script/tool/test/common/package_looping_command_test.dart b/script/tool/test/common/package_looping_command_test.dart index 7e9f63cb0344..c858df0022cc 100644 --- a/script/tool/test/common/package_looping_command_test.dart +++ b/script/tool/test/common/package_looping_command_test.dart @@ -18,7 +18,7 @@ import 'package:test/test.dart'; import '../mocks.dart'; import '../util.dart'; -import 'plugin_command_test.mocks.dart'; +import 'package_command_test.mocks.dart'; // Constants for colorized output start and end. const String _startElapsedTimeColor = '\x1B[90m'; @@ -373,9 +373,10 @@ void main() { test('skips unsupported Dart versions when requested', () async { final RepositoryPackage excluded = createFakePackage( - 'excluded_package', packagesDir, dartConstraint: '>=2.17.0 <3.0.0'); - final RepositoryPackage included = createFakePackage( - 'a_package', packagesDir); + 'excluded_package', packagesDir, + dartConstraint: '>=2.17.0 <3.0.0'); + final RepositoryPackage included = + createFakePackage('a_package', packagesDir); final TestPackageLoopingCommand command = createTestCommand( packageLoopingType: PackageLoopingType.includeAllSubpackages, @@ -406,8 +407,7 @@ void main() { createFakePlugin('package_a', packagesDir); createFakePackage('package_b', packagesDir); - final TestPackageLoopingCommand command = - createTestCommand(); + final TestPackageLoopingCommand command = createTestCommand(); final List output = await runCommand(command); const String separator = @@ -440,8 +440,7 @@ void main() { createFakePlugin('package_a', packagesDir); createFakePackage('package_b', packagesDir); - final TestPackageLoopingCommand command = - createTestCommand(); + final TestPackageLoopingCommand command = createTestCommand(); final List output = await runCommand(command, arguments: ['--log-timing']); @@ -783,8 +782,7 @@ void main() { createFakePackage('package_f', packagesDir); - final TestPackageLoopingCommand command = - createTestCommand(); + final TestPackageLoopingCommand command = createTestCommand(); final List output = await runCommand(command); expect( @@ -809,8 +807,7 @@ void main() { test('prints exclusions as skips in long-form run summary', () async { createFakePackage('package_a', packagesDir); - final TestPackageLoopingCommand command = - createTestCommand(); + final TestPackageLoopingCommand command = createTestCommand(); final List output = await runCommand(command, arguments: ['--exclude=package_a']); diff --git a/script/tool/test/custom_test_command_test.dart b/script/tool/test/custom_test_command_test.dart index a28b47505e9d..8b0c021b1255 100644 --- a/script/tool/test/custom_test_command_test.dart +++ b/script/tool/test/custom_test_command_test.dart @@ -40,7 +40,7 @@ void main() { test('runs both new and legacy when both are present', () async { final RepositoryPackage package = - createFakePlugin('a_package', packagesDir, extraFiles: [ + createFakePackage('a_package', packagesDir, extraFiles: [ 'tool/run_tests.dart', 'run_tests.sh', ]); @@ -65,7 +65,7 @@ void main() { }); test('runs when only new is present', () async { - final RepositoryPackage package = createFakePlugin( + final RepositoryPackage package = createFakePackage( 'a_package', packagesDir, extraFiles: ['tool/run_tests.dart']); @@ -87,7 +87,7 @@ void main() { }); test('runs pub get before running Dart test script', () async { - final RepositoryPackage package = createFakePlugin( + final RepositoryPackage package = createFakePackage( 'a_package', packagesDir, extraFiles: ['tool/run_tests.dart']); @@ -103,7 +103,7 @@ void main() { }); test('runs when only legacy is present', () async { - final RepositoryPackage package = createFakePlugin( + final RepositoryPackage package = createFakePackage( 'a_package', packagesDir, extraFiles: ['run_tests.sh']); @@ -125,7 +125,7 @@ void main() { }); test('skips when neither is present', () async { - createFakePlugin('a_package', packagesDir); + createFakePackage('a_package', packagesDir); final List output = await runCapturingPrint(runner, ['custom-test']); @@ -140,7 +140,7 @@ void main() { }); test('fails if new fails', () async { - createFakePlugin('a_package', packagesDir, extraFiles: [ + createFakePackage('a_package', packagesDir, extraFiles: [ 'tool/run_tests.dart', 'run_tests.sh', ]); @@ -166,7 +166,7 @@ void main() { }); test('fails if pub get fails', () async { - createFakePlugin('a_package', packagesDir, extraFiles: [ + createFakePackage('a_package', packagesDir, extraFiles: [ 'tool/run_tests.dart', 'run_tests.sh', ]); @@ -193,7 +193,7 @@ void main() { test('fails if legacy fails', () async { final RepositoryPackage package = - createFakePlugin('a_package', packagesDir, extraFiles: [ + createFakePackage('a_package', packagesDir, extraFiles: [ 'tool/run_tests.dart', 'run_tests.sh', ]); @@ -238,7 +238,7 @@ void main() { test('runs new and skips old when both are present', () async { final RepositoryPackage package = - createFakePlugin('a_package', packagesDir, extraFiles: [ + createFakePackage('a_package', packagesDir, extraFiles: [ 'tool/run_tests.dart', 'run_tests.sh', ]); @@ -261,7 +261,7 @@ void main() { }); test('runs when only new is present', () async { - final RepositoryPackage package = createFakePlugin( + final RepositoryPackage package = createFakePackage( 'a_package', packagesDir, extraFiles: ['tool/run_tests.dart']); @@ -283,7 +283,7 @@ void main() { }); test('skips package when only legacy is present', () async { - createFakePlugin('a_package', packagesDir, + createFakePackage('a_package', packagesDir, extraFiles: ['run_tests.sh']); final List output = @@ -300,7 +300,7 @@ void main() { }); test('fails if new fails', () async { - createFakePlugin('a_package', packagesDir, extraFiles: [ + createFakePackage('a_package', packagesDir, extraFiles: [ 'tool/run_tests.dart', 'run_tests.sh', ]); diff --git a/script/tool/test/dependabot_check_command_test.dart b/script/tool/test/dependabot_check_command_test.dart index a4c8693b2c21..b0558c3d22ac 100644 --- a/script/tool/test/dependabot_check_command_test.dart +++ b/script/tool/test/dependabot_check_command_test.dart @@ -10,7 +10,7 @@ import 'package:flutter_plugin_tools/src/dependabot_check_command.dart'; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; -import 'common/plugin_command_test.mocks.dart'; +import 'common/package_command_test.mocks.dart'; import 'util.dart'; void main() { diff --git a/script/tool/test/federation_safety_check_command_test.dart b/script/tool/test/federation_safety_check_command_test.dart index 015a0eb634d9..6b6b1a514531 100644 --- a/script/tool/test/federation_safety_check_command_test.dart +++ b/script/tool/test/federation_safety_check_command_test.dart @@ -12,7 +12,7 @@ import 'package:flutter_plugin_tools/src/federation_safety_check_command.dart'; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; -import 'common/plugin_command_test.mocks.dart'; +import 'common/package_command_test.mocks.dart'; import 'mocks.dart'; import 'util.dart'; diff --git a/script/tool/test/format_command_test.dart b/script/tool/test/format_command_test.dart index 5bd6f97832f7..c1c4a212a019 100644 --- a/script/tool/test/format_command_test.dart +++ b/script/tool/test/format_command_test.dart @@ -60,15 +60,15 @@ void main() { } /// Returns a list of [count] relative paths to pass to [createFakePlugin] - /// with name [pluginName] such that each path will be 99 characters long - /// relative to [packagesDir]. + /// or [createFakePackage] with name [packageName] such that each path will + /// be 99 characters long relative to [packagesDir]. /// /// This is for each of testing batching, since it means each file will /// consume 100 characters of the batch length. - List _get99CharacterPathExtraFiles(String pluginName, int count) { + List _get99CharacterPathExtraFiles(String packageName, int count) { final int padding = 99 - - pluginName.length - - 1 - // the path separator after the plugin name + packageName.length - + 1 - // the path separator after the package name 1 - // the path separator after the padding 10; // the file name const int filenameBase = 10000; diff --git a/script/tool/test/license_check_command_test.dart b/script/tool/test/license_check_command_test.dart index 43fbd6b5eca8..005b77d99a13 100644 --- a/script/tool/test/license_check_command_test.dart +++ b/script/tool/test/license_check_command_test.dart @@ -11,7 +11,7 @@ import 'package:mockito/mockito.dart'; import 'package:platform/platform.dart'; import 'package:test/test.dart'; -import 'common/plugin_command_test.mocks.dart'; +import 'common/package_command_test.mocks.dart'; import 'mocks.dart'; import 'util.dart'; diff --git a/script/tool/test/list_command_test.dart b/script/tool/test/list_command_test.dart index f74431c5cee7..f19215c89b9e 100644 --- a/script/tool/test/list_command_test.dart +++ b/script/tool/test/list_command_test.dart @@ -29,17 +29,17 @@ void main() { runner.addCommand(command); }); - test('lists plugins', () async { - createFakePlugin('plugin1', packagesDir); + test('lists top-level packages', () async { + createFakePackage('package1', packagesDir); createFakePlugin('plugin2', packagesDir); final List plugins = - await runCapturingPrint(runner, ['list', '--type=plugin']); + await runCapturingPrint(runner, ['list', '--type=package']); expect( plugins, orderedEquals([ - '/packages/plugin1', + '/packages/package1', '/packages/plugin2', ]), ); @@ -64,20 +64,20 @@ void main() { ); }); - test('lists packages', () async { - createFakePlugin('plugin1', packagesDir); + test('lists packages and subpackages', () async { + createFakePackage('package1', packagesDir); createFakePlugin('plugin2', packagesDir, examples: ['example1', 'example2']); createFakePlugin('plugin3', packagesDir, examples: []); - final List packages = - await runCapturingPrint(runner, ['list', '--type=package']); + final List packages = await runCapturingPrint( + runner, ['list', '--type=package-or-subpackage']); expect( packages, unorderedEquals([ - '/packages/plugin1', - '/packages/plugin1/example', + '/packages/package1', + '/packages/package1/example', '/packages/plugin2', '/packages/plugin2/example/example1', '/packages/plugin2/example/example2', diff --git a/script/tool/test/make_deps_path_based_command_test.dart b/script/tool/test/make_deps_path_based_command_test.dart index 33d6be261e87..7e52dd6bbbc4 100644 --- a/script/tool/test/make_deps_path_based_command_test.dart +++ b/script/tool/test/make_deps_path_based_command_test.dart @@ -11,7 +11,7 @@ import 'package:flutter_plugin_tools/src/make_deps_path_based_command.dart'; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; -import 'common/plugin_command_test.mocks.dart'; +import 'common/package_command_test.mocks.dart'; import 'mocks.dart'; import 'util.dart'; diff --git a/script/tool/test/publish_plugin_command_test.dart b/script/tool/test/publish_command_test.dart similarity index 94% rename from script/tool/test/publish_plugin_command_test.dart rename to script/tool/test/publish_command_test.dart index f3be3b48b1f1..19e8bdd233ec 100644 --- a/script/tool/test/publish_plugin_command_test.dart +++ b/script/tool/test/publish_command_test.dart @@ -10,14 +10,14 @@ import 'package:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_plugin_tools/src/common/core.dart'; -import 'package:flutter_plugin_tools/src/publish_plugin_command.dart'; +import 'package:flutter_plugin_tools/src/publish_command.dart'; import 'package:http/http.dart' as http; import 'package:http/testing.dart'; import 'package:mockito/mockito.dart'; import 'package:platform/platform.dart'; import 'package:test/test.dart'; -import 'common/plugin_command_test.mocks.dart'; +import 'common/package_command_test.mocks.dart'; import 'mocks.dart'; import 'util.dart'; @@ -34,7 +34,7 @@ void main() { late Map> mockHttpResponses; void _createMockCredentialFile() { - final String credentialPath = PublishPluginCommand.getCredentialPath(); + final String credentialPath = PublishCommand.getCredentialPath(); fileSystem.file(credentialPath) ..createSync(recursive: true) ..writeAsStringSync('some credential'); @@ -72,7 +72,7 @@ void main() { mockStdin = MockStdin(); commandRunner = CommandRunner('tester', '') - ..addCommand(PublishPluginCommand( + ..addCommand(PublishCommand( packagesDir, processRunner: processRunner, stdinput: mockStdin, @@ -93,7 +93,7 @@ void main() { Error? commandError; final List output = await runCapturingPrint(commandRunner, [ - 'publish-plugin', + 'publish', '--packages=foo', ], errorHandler: (Error e) { commandError = e; @@ -122,7 +122,7 @@ void main() { Error? commandError; final List output = await runCapturingPrint( - commandRunner, ['publish-plugin', '--packages=foo'], + commandRunner, ['publish', '--packages=foo'], errorHandler: (Error e) { commandError = e; }); @@ -154,8 +154,8 @@ void main() { stderrEncoding: utf8), // pub publish for plugin1 ]; - final List output = await runCapturingPrint(commandRunner, - ['publish-plugin', '--packages=plugin1,plugin2']); + final List output = await runCapturingPrint( + commandRunner, ['publish', '--packages=plugin1,plugin2']); expect( output, @@ -176,7 +176,7 @@ void main() { mockStdin.mockUserInputs.add(utf8.encode('user input')); await runCapturingPrint( - commandRunner, ['publish-plugin', '--packages=foo']); + commandRunner, ['publish', '--packages=foo']); expect(processRunner.mockPublishProcess.stdinMock.lines, contains('user input')); @@ -187,7 +187,7 @@ void main() { createFakePlugin('foo', packagesDir, examples: []); await runCapturingPrint(commandRunner, [ - 'publish-plugin', + 'publish', '--packages=foo', '--pub-publish-flags', '--dry-run,--server=bar' @@ -209,7 +209,7 @@ void main() { createFakePlugin('foo', packagesDir, examples: []); await runCapturingPrint(commandRunner, [ - 'publish-plugin', + 'publish', '--packages=foo', '--skip-confirmation', '--pub-publish-flags', @@ -232,7 +232,7 @@ void main() { createFakePlugin('plugin_b', packagesDir, examples: []); await runCapturingPrint(commandRunner, [ - 'publish-plugin', + 'publish', '--packages=plugin_a,plugin_b', '--skip-confirmation', '--pub-publish-flags', @@ -263,7 +263,7 @@ void main() { Error? commandError; final List output = await runCapturingPrint(commandRunner, [ - 'publish-plugin', + 'publish', '--packages=foo', ], errorHandler: (Error e) { commandError = e; @@ -283,7 +283,7 @@ void main() { final List output = await runCapturingPrint(commandRunner, [ - 'publish-plugin', + 'publish', '--packages=foo', '--dry-run', ]); @@ -310,7 +310,7 @@ void main() { final List output = await runCapturingPrint(commandRunner, [ - 'publish-plugin', + 'publish', '--packages=$packageName', ]); @@ -330,7 +330,7 @@ void main() { test('with the version and name from the pubspec.yaml', () async { createFakePlugin('foo', packagesDir, examples: []); await runCapturingPrint(commandRunner, [ - 'publish-plugin', + 'publish', '--packages=foo', ]); @@ -348,7 +348,7 @@ void main() { Error? commandError; final List output = await runCapturingPrint(commandRunner, [ - 'publish-plugin', + 'publish', '--packages=foo', ], errorHandler: (Error e) { commandError = e; @@ -375,7 +375,7 @@ void main() { final List output = await runCapturingPrint(commandRunner, [ - 'publish-plugin', + 'publish', '--packages=foo', ]); @@ -398,7 +398,7 @@ void main() { final List output = await runCapturingPrint(commandRunner, [ - 'publish-plugin', + 'publish', '--skip-confirmation', '--packages=foo', ]); @@ -420,8 +420,8 @@ void main() { mockStdin.readLineOutput = 'y'; - final List output = await runCapturingPrint(commandRunner, - ['publish-plugin', '--packages=foo', '--dry-run']); + final List output = await runCapturingPrint( + commandRunner, ['publish', '--packages=foo', '--dry-run']); expect( processRunner.recordedCalls @@ -445,7 +445,7 @@ void main() { final List output = await runCapturingPrint(commandRunner, [ - 'publish-plugin', + 'publish', '--packages=foo', '--remote', 'origin', @@ -491,7 +491,7 @@ void main() { mockStdin.readLineOutput = 'y'; final List output = await runCapturingPrint(commandRunner, - ['publish-plugin', '--all-changed', '--base-sha=HEAD~']); + ['publish', '--all-changed', '--base-sha=HEAD~']); expect( output, @@ -553,7 +553,7 @@ void main() { mockStdin.readLineOutput = 'y'; final List output = await runCapturingPrint(commandRunner, - ['publish-plugin', '--all-changed', '--base-sha=HEAD~']); + ['publish', '--all-changed', '--base-sha=HEAD~']); expect( output, @@ -598,7 +598,7 @@ void main() { final List output = await runCapturingPrint( commandRunner, [ - 'publish-plugin', + 'publish', '--all-changed', '--base-sha=HEAD~', '--dry-run' @@ -651,7 +651,7 @@ void main() { mockStdin.readLineOutput = 'y'; final List output2 = await runCapturingPrint(commandRunner, - ['publish-plugin', '--all-changed', '--base-sha=HEAD~']); + ['publish', '--all-changed', '--base-sha=HEAD~']); expect( output2, containsAllInOrder([ @@ -700,7 +700,7 @@ void main() { mockStdin.readLineOutput = 'y'; final List output2 = await runCapturingPrint(commandRunner, - ['publish-plugin', '--all-changed', '--base-sha=HEAD~']); + ['publish', '--all-changed', '--base-sha=HEAD~']); expect( output2, containsAllInOrder([ @@ -749,7 +749,7 @@ void main() { ]; final List output = await runCapturingPrint(commandRunner, - ['publish-plugin', '--all-changed', '--base-sha=HEAD~']); + ['publish', '--all-changed', '--base-sha=HEAD~']); expect( output, @@ -795,7 +795,7 @@ void main() { Error? commandError; final List output = await runCapturingPrint(commandRunner, - ['publish-plugin', '--all-changed', '--base-sha=HEAD~'], + ['publish', '--all-changed', '--base-sha=HEAD~'], errorHandler: (Error e) { commandError = e; }); @@ -830,7 +830,7 @@ void main() { ]; final List output = await runCapturingPrint(commandRunner, - ['publish-plugin', '--all-changed', '--base-sha=HEAD~']); + ['publish', '--all-changed', '--base-sha=HEAD~']); expect(output, containsAllInOrder(['Ran for 0 package(s)'])); expect( @@ -852,7 +852,7 @@ void main() { ]; final List output = await runCapturingPrint(commandRunner, - ['publish-plugin', '--all-changed', '--base-sha=HEAD~']); + ['publish', '--all-changed', '--base-sha=HEAD~']); expect( output, diff --git a/script/tool/test/update_excerpts_command_test.dart b/script/tool/test/update_excerpts_command_test.dart index 34c85cc172fe..79f53d8779bb 100644 --- a/script/tool/test/update_excerpts_command_test.dart +++ b/script/tool/test/update_excerpts_command_test.dart @@ -12,7 +12,7 @@ import 'package:flutter_plugin_tools/src/update_excerpts_command.dart'; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; -import 'common/plugin_command_test.mocks.dart'; +import 'common/package_command_test.mocks.dart'; import 'mocks.dart'; import 'util.dart'; diff --git a/script/tool/test/update_release_info_command_test.dart b/script/tool/test/update_release_info_command_test.dart index 7e7ff54d5947..8cd2e9591e70 100644 --- a/script/tool/test/update_release_info_command_test.dart +++ b/script/tool/test/update_release_info_command_test.dart @@ -12,7 +12,7 @@ import 'package:flutter_plugin_tools/src/update_release_info_command.dart'; import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; -import 'common/plugin_command_test.mocks.dart'; +import 'common/package_command_test.mocks.dart'; import 'mocks.dart'; import 'util.dart'; diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart index 0e94712e9ef0..598176bbe209 100644 --- a/script/tool/test/version_check_command_test.dart +++ b/script/tool/test/version_check_command_test.dart @@ -16,7 +16,7 @@ import 'package:mockito/mockito.dart'; import 'package:pub_semver/pub_semver.dart'; import 'package:test/test.dart'; -import 'common/plugin_command_test.mocks.dart'; +import 'common/package_command_test.mocks.dart'; import 'mocks.dart'; import 'util.dart'; From 1c47c3b9d2788104985ebfc4660cafb390968b6e Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Tue, 27 Sep 2022 13:34:08 -0700 Subject: [PATCH 763/844] [camera] Add ProcessCameraProvider class to CameraX plugin (#6469) --- .../camera_android_camerax/CHANGELOG.md | 1 + .../android/build.gradle | 4 +- .../camerax/CameraAndroidCameraxPlugin.java | 31 +++- .../camerax/CameraInfoFlutterApiImpl.java | 3 +- .../camerax/GeneratedCameraXLibrary.java | 134 ++++++++++++++++++ .../ProcessCameraProviderFlutterApiImpl.java | 23 +++ .../ProcessCameraProviderHostApiImpl.java | 87 ++++++++++++ .../camerax/ProcessCameraProviderTest.java | 106 ++++++++++++++ ...roid_camera_camerax_flutter_api_impls.dart | 9 ++ .../lib/src/camerax_library.pigeon.dart | 109 ++++++++++++++ .../lib/src/process_camera_provider.dart | 119 ++++++++++++++++ .../pigeons/camerax_library.dart | 13 ++ .../test/process_camera_provider_test.dart | 96 +++++++++++++ .../process_camera_provider_test.mocks.dart | 40 ++++++ .../test/test_camerax_library.pigeon.dart | 50 +++++++ 15 files changed, 818 insertions(+), 7 deletions(-) create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderFlutterApiImpl.java create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java create mode 100644 packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java create mode 100644 packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart create mode 100644 packages/camera/camera_android_camerax/test/process_camera_provider_test.dart create mode 100644 packages/camera/camera_android_camerax/test/process_camera_provider_test.mocks.dart diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 2bdfa594b3be..ce2fb9046c69 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -3,3 +3,4 @@ * Creates camera_android_camerax plugin for development. * Adds CameraInfo class and removes unnecessary code from plugin. * Adds CameraSelector class. +* Adds ProcessCameraProvider class. diff --git a/packages/camera/camera_android_camerax/android/build.gradle b/packages/camera/camera_android_camerax/android/build.gradle index 1772afebe429..a193a298c640 100644 --- a/packages/camera/camera_android_camerax/android/build.gradle +++ b/packages/camera/camera_android_camerax/android/build.gradle @@ -38,13 +38,15 @@ android { dependencies { // CameraX core library using the camera2 implementation must use same version number. - def camerax_version = "1.2.0-beta01" + def camerax_version = "1.2.0-beta02" implementation "androidx.camera:camera-core:${camerax_version}" implementation "androidx.camera:camera-camera2:${camerax_version}" implementation "androidx.camera:camera-lifecycle:${camerax_version}" + implementation 'com.google.guava:guava:28.1-android' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:4.7.0' testImplementation 'androidx.test:core:1.4.0' + testImplementation 'org.robolectric:robolectric:4.3' } testOptions { unitTests.includeAndroidResources = true 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 bb5756a7e3b9..b8fbaf539c32 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 @@ -15,6 +15,7 @@ public final class CameraAndroidCameraxPlugin implements FlutterPlugin, ActivityAware { private InstanceManager instanceManager; private FlutterPluginBinding pluginBinding; + private ProcessCameraProviderHostApiImpl processCameraProviderHostApi; /** * Initialize this within the {@code #configureFlutterEngine} of a Flutter activity or fragment. @@ -39,6 +40,10 @@ void setUp(BinaryMessenger binaryMessenger, Context context) { binaryMessenger, new JavaObjectHostApiImpl(instanceManager)); GeneratedCameraXLibrary.CameraSelectorHostApi.setup( binaryMessenger, new CameraSelectorHostApiImpl(binaryMessenger, instanceManager)); + processCameraProviderHostApi = + new ProcessCameraProviderHostApiImpl(binaryMessenger, instanceManager, context); + GeneratedCameraXLibrary.ProcessCameraProviderHostApi.setup( + binaryMessenger, processCameraProviderHostApi); } @Override @@ -60,15 +65,33 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { // Activity Lifecycle methods: @Override - public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) {} + public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) { + updateContext(activityPluginBinding.getActivity()); + } @Override - public void onDetachedFromActivityForConfigChanges() {} + public void onDetachedFromActivityForConfigChanges() { + updateContext(pluginBinding.getApplicationContext()); + } @Override public void onReattachedToActivityForConfigChanges( - @NonNull ActivityPluginBinding activityPluginBinding) {} + @NonNull ActivityPluginBinding activityPluginBinding) { + updateContext(activityPluginBinding.getActivity()); + } @Override - public void onDetachedFromActivity() {} + public void onDetachedFromActivity() { + updateContext(pluginBinding.getApplicationContext()); + } + + /** + * Updates context that is used to fetch the corresponding instance of a {@code + * ProcessCameraProvider}. + */ + private void updateContext(Context context) { + if (processCameraProviderHostApi != null) { + processCameraProviderHostApi.setContext(context); + } + } } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java index b5ba9fc1ff3b..c538e420cc7e 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java @@ -18,7 +18,6 @@ public CameraInfoFlutterApiImpl( } void create(CameraInfo cameraInfo, Reply reply) { - instanceManager.addHostCreatedInstance(cameraInfo); - create(instanceManager.getIdentifierForStrongReference(cameraInfo), reply); + create(instanceManager.addHostCreatedInstance(cameraInfo), reply); } } 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 e87a80db030c..041564c3bfcb 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 @@ -22,6 +22,13 @@ /** Generated class from Pigeon. */ @SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"}) public class GeneratedCameraXLibrary { + + public interface Result { + void success(T result); + + void error(Throwable error); + } + private static class JavaObjectHostApiCodec extends StandardMessageCodec { public static final JavaObjectHostApiCodec INSTANCE = new JavaObjectHostApiCodec(); @@ -311,6 +318,133 @@ public void create( } } + private static class ProcessCameraProviderHostApiCodec extends StandardMessageCodec { + public static final ProcessCameraProviderHostApiCodec INSTANCE = + new ProcessCameraProviderHostApiCodec(); + + private ProcessCameraProviderHostApiCodec() {} + } + + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface ProcessCameraProviderHostApi { + void getInstance(Result result); + + @NonNull + List getAvailableCameraInfos(@NonNull Long identifier); + + /** The codec used by ProcessCameraProviderHostApi. */ + static MessageCodec getCodec() { + return ProcessCameraProviderHostApiCodec.INSTANCE; + } + + /** + * Sets up an instance of `ProcessCameraProviderHostApi` to handle messages through the + * `binaryMessenger`. + */ + static void setup(BinaryMessenger binaryMessenger, ProcessCameraProviderHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.ProcessCameraProviderHostApi.getInstance", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + Result resultCallback = + new Result() { + public void success(Long result) { + wrapped.put("result", result); + reply.reply(wrapped); + } + + public void error(Throwable error) { + wrapped.put("error", wrapError(error)); + reply.reply(wrapped); + } + }; + + api.getInstance(resultCallback); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + reply.reply(wrapped); + } + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + List output = + api.getAvailableCameraInfos( + (identifierArg == null) ? null : identifierArg.longValue()); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } + + private static class ProcessCameraProviderFlutterApiCodec extends StandardMessageCodec { + public static final ProcessCameraProviderFlutterApiCodec INSTANCE = + new ProcessCameraProviderFlutterApiCodec(); + + private ProcessCameraProviderFlutterApiCodec() {} + } + + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ + public static class ProcessCameraProviderFlutterApi { + private final BinaryMessenger binaryMessenger; + + public ProcessCameraProviderFlutterApi(BinaryMessenger argBinaryMessenger) { + this.binaryMessenger = argBinaryMessenger; + } + + public interface Reply { + void reply(T reply); + } + + static MessageCodec getCodec() { + return ProcessCameraProviderFlutterApiCodec.INSTANCE; + } + + public void create(@NonNull Long identifierArg, Reply callback) { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.ProcessCameraProviderFlutterApi.create", + getCodec()); + channel.send( + new ArrayList(Arrays.asList(identifierArg)), + channelReply -> { + callback.reply(null); + }); + } + } + private static Map wrapError(Throwable exception) { Map errorMap = new HashMap<>(); errorMap.put("message", exception.toString()); diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderFlutterApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderFlutterApiImpl.java new file mode 100644 index 000000000000..90c94d0c26cb --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderFlutterApiImpl.java @@ -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. + +package io.flutter.plugins.camerax; + +import androidx.camera.lifecycle.ProcessCameraProvider; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ProcessCameraProviderFlutterApi; + +public class ProcessCameraProviderFlutterApiImpl extends ProcessCameraProviderFlutterApi { + public ProcessCameraProviderFlutterApiImpl( + BinaryMessenger binaryMessenger, InstanceManager instanceManager) { + super(binaryMessenger); + this.instanceManager = instanceManager; + } + + private final InstanceManager instanceManager; + + void create(ProcessCameraProvider processCameraProvider, Reply reply) { + create(instanceManager.addHostCreatedInstance(processCameraProvider), reply); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java new file mode 100644 index 000000000000..19c5eb5b3f70 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java @@ -0,0 +1,87 @@ +// 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.content.Context; +import androidx.annotation.NonNull; +import androidx.camera.core.CameraInfo; +import androidx.camera.lifecycle.ProcessCameraProvider; +import androidx.core.content.ContextCompat; +import com.google.common.util.concurrent.ListenableFuture; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ProcessCameraProviderHostApi; +import java.util.ArrayList; +import java.util.List; + +public class ProcessCameraProviderHostApiImpl implements ProcessCameraProviderHostApi { + private final BinaryMessenger binaryMessenger; + private final InstanceManager instanceManager; + + private Context context; + + public ProcessCameraProviderHostApiImpl( + BinaryMessenger binaryMessenger, InstanceManager instanceManager, Context context) { + this.binaryMessenger = binaryMessenger; + this.instanceManager = instanceManager; + this.context = context; + } + + /** + * Sets the context that the {@code ProcessCameraProvider} will use to attach the lifecycle of the + * camera to. + * + *

If using the camera plugin in an add-to-app context, ensure that a new instance of the + * {@code ProcessCameraProvider} is fetched via {@code #getInstance} anytime the context changes. + */ + public void setContext(Context context) { + this.context = context; + } + + /** + * Returns the instance of the ProcessCameraProvider to manage the lifecycle of the camera for the + * current {@code Context}. + */ + @Override + public void getInstance(GeneratedCameraXLibrary.Result result) { + ListenableFuture processCameraProviderFuture = + ProcessCameraProvider.getInstance(context); + + processCameraProviderFuture.addListener( + () -> { + try { + // Camera provider is now guaranteed to be available. + ProcessCameraProvider processCameraProvider = processCameraProviderFuture.get(); + + if (!instanceManager.containsInstance(processCameraProvider)) { + final ProcessCameraProviderFlutterApiImpl flutterApi = + new ProcessCameraProviderFlutterApiImpl(binaryMessenger, instanceManager); + flutterApi.create(processCameraProvider, reply -> {}); + } + result.success(instanceManager.getIdentifierForStrongReference(processCameraProvider)); + } catch (Exception e) { + result.error(e); + } + }, + ContextCompat.getMainExecutor(context)); + } + + /** Returns cameras available to the ProcessCameraProvider. */ + @Override + public List getAvailableCameraInfos(@NonNull Long identifier) { + ProcessCameraProvider processCameraProvider = + (ProcessCameraProvider) instanceManager.getInstance(identifier); + + List availableCameras = processCameraProvider.getAvailableCameraInfos(); + List availableCamerasIds = new ArrayList(); + final CameraInfoFlutterApiImpl cameraInfoFlutterApi = + new CameraInfoFlutterApiImpl(binaryMessenger, instanceManager); + + for (CameraInfo cameraInfo : availableCameras) { + cameraInfoFlutterApi.create(cameraInfo, result -> {}); + availableCamerasIds.add(instanceManager.getIdentifierForStrongReference(cameraInfo)); + } + return availableCamerasIds; + } +} diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java new file mode 100644 index 000000000000..ec321f8dbfea --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java @@ -0,0 +1,106 @@ +// 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 static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import androidx.camera.core.CameraInfo; +import androidx.camera.lifecycle.ProcessCameraProvider; +import androidx.test.core.app.ApplicationProvider; +import com.google.common.util.concurrent.SettableFuture; +import io.flutter.plugin.common.BinaryMessenger; +import java.util.Arrays; +import java.util.Objects; +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; + +@RunWith(RobolectricTestRunner.class) +public class ProcessCameraProviderTest { + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock public ProcessCameraProvider processCameraProvider; + @Mock public BinaryMessenger mockBinaryMessenger; + + InstanceManager testInstanceManager; + private Context context; + + @Before + public void setUp() { + testInstanceManager = InstanceManager.open(identifier -> {}); + context = ApplicationProvider.getApplicationContext(); + } + + @After + public void tearDown() { + testInstanceManager.close(); + } + + @Test + public void getInstanceTest() { + final ProcessCameraProviderHostApiImpl processCameraProviderHostApi = + new ProcessCameraProviderHostApiImpl(mockBinaryMessenger, testInstanceManager, context); + SettableFuture processCameraProviderFuture = SettableFuture.create(); + processCameraProviderFuture.set(processCameraProvider); + final GeneratedCameraXLibrary.Result mockResult = + mock(GeneratedCameraXLibrary.Result.class); + + testInstanceManager.addDartCreatedInstance(processCameraProvider, 0); + + try (MockedStatic mockedProcessCameraProvider = + Mockito.mockStatic(ProcessCameraProvider.class)) { + mockedProcessCameraProvider + .when(() -> ProcessCameraProvider.getInstance(context)) + .thenAnswer((Answer) invocation -> processCameraProviderFuture); + + processCameraProviderHostApi.getInstance(mockResult); + verify(mockResult).success(0L); + } + } + + @Test + public void getAvailableCameraInfosTest() { + final ProcessCameraProviderHostApiImpl processCameraProviderHostApi = + new ProcessCameraProviderHostApiImpl(mockBinaryMessenger, testInstanceManager, context); + final CameraInfo mockCameraInfo = mock(CameraInfo.class); + + testInstanceManager.addDartCreatedInstance(processCameraProvider, 0); + testInstanceManager.addDartCreatedInstance(mockCameraInfo, 1); + + when(processCameraProvider.getAvailableCameraInfos()).thenReturn(Arrays.asList(mockCameraInfo)); + + assertEquals(processCameraProviderHostApi.getAvailableCameraInfos(0L), Arrays.asList(1L)); + verify(processCameraProvider).getAvailableCameraInfos(); + } + + @Test + public void flutterApiCreateTest() { + final ProcessCameraProviderFlutterApiImpl spyFlutterApi = + spy(new ProcessCameraProviderFlutterApiImpl(mockBinaryMessenger, testInstanceManager)); + + spyFlutterApi.create(processCameraProvider, reply -> {}); + + final long identifier = + Objects.requireNonNull( + testInstanceManager.getIdentifierForStrongReference(processCameraProvider)); + verify(spyFlutterApi).create(eq(identifier), any()); + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart index 576260c0b7b8..9c6564a06c08 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart @@ -6,6 +6,7 @@ import 'camera_info.dart'; import 'camera_selector.dart'; import 'camerax_library.pigeon.dart'; import 'java_object.dart'; +import 'process_camera_provider.dart'; /// Handles initialization of Flutter APIs for the Android CameraX library. class AndroidCameraXCameraFlutterApis { @@ -14,6 +15,7 @@ class AndroidCameraXCameraFlutterApis { JavaObjectFlutterApiImpl? javaObjectFlutterApi, CameraInfoFlutterApiImpl? cameraInfoFlutterApi, CameraSelectorFlutterApiImpl? cameraSelectorFlutterApi, + ProcessCameraProviderFlutterApiImpl? processCameraProviderFlutterApi, }) { this.javaObjectFlutterApi = javaObjectFlutterApi ?? JavaObjectFlutterApiImpl(); @@ -21,6 +23,8 @@ class AndroidCameraXCameraFlutterApis { cameraInfoFlutterApi ?? CameraInfoFlutterApiImpl(); this.cameraSelectorFlutterApi = cameraSelectorFlutterApi ?? CameraSelectorFlutterApiImpl(); + this.processCameraProviderFlutterApi = processCameraProviderFlutterApi ?? + ProcessCameraProviderFlutterApiImpl(); } static bool _haveBeenSetUp = false; @@ -40,12 +44,17 @@ class AndroidCameraXCameraFlutterApis { /// Flutter Api for [CameraSelector]. late final CameraSelectorFlutterApiImpl cameraSelectorFlutterApi; + /// Flutter Api for [ProcessCameraProvider]. + late final ProcessCameraProviderFlutterApiImpl + processCameraProviderFlutterApi; + /// Ensures all the Flutter APIs have been setup to receive calls from native code. void ensureSetUp() { if (!_haveBeenSetUp) { JavaObjectFlutterApi.setup(javaObjectFlutterApi); CameraInfoFlutterApi.setup(cameraInfoFlutterApi); CameraSelectorFlutterApi.setup(cameraSelectorFlutterApi); + ProcessCameraProviderFlutterApi.setup(processCameraProviderFlutterApi); _haveBeenSetUp = true; } } diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart b/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart index a399001d35b0..c0b052378def 100644 --- a/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart +++ b/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart @@ -263,3 +263,112 @@ abstract class CameraSelectorFlutterApi { } } } + +class _ProcessCameraProviderHostApiCodec extends StandardMessageCodec { + const _ProcessCameraProviderHostApiCodec(); +} + +class ProcessCameraProviderHostApi { + /// Constructor for [ProcessCameraProviderHostApi]. 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. + ProcessCameraProviderHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = + _ProcessCameraProviderHostApiCodec(); + + Future getInstance() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getInstance', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send(null) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as int?)!; + } + } + + Future> getAvailableCameraInfos(int arg_identifier) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_identifier]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as List?)!.cast(); + } + } +} + +class _ProcessCameraProviderFlutterApiCodec extends StandardMessageCodec { + const _ProcessCameraProviderFlutterApiCodec(); +} + +abstract class ProcessCameraProviderFlutterApi { + static const MessageCodec codec = + _ProcessCameraProviderFlutterApiCodec(); + + void create(int identifier); + static void setup(ProcessCameraProviderFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ProcessCameraProviderFlutterApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderFlutterApi.create 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.ProcessCameraProviderFlutterApi.create was null, expected non-null int.'); + api.create(arg_identifier!); + return; + }); + } + } + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart b/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart new file mode 100644 index 000000000000..e2b588d15faa --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/process_camera_provider.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/services.dart'; + +import 'android_camera_camerax_flutter_api_impls.dart'; +import 'camera_info.dart'; +import 'camerax_library.pigeon.dart'; +import 'instance_manager.dart'; +import 'java_object.dart'; + +/// Provides an object to manage the camera. +/// +/// See https://developer.android.com/reference/androidx/camera/lifecycle/ProcessCameraProvider. +class ProcessCameraProvider extends JavaObject { + /// Creates a detached [ProcessCameraProvider]. + ProcessCameraProvider.detached( + {BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) + : super.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager) { + _api = ProcessCameraProviderHostApiImpl( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); + } + + late final ProcessCameraProviderHostApiImpl _api; + + /// Gets an instance of [ProcessCameraProvider]. + static Future getInstance( + {BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) { + AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); + final ProcessCameraProviderHostApiImpl api = + ProcessCameraProviderHostApiImpl( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + + return api.getInstancefromInstances(); + } + + /// Retrieves the cameras available to the device. + Future> getAvailableCameraInfos() { + return _api.getAvailableCameraInfosFromInstances(this); + } +} + +/// Host API implementation of [ProcessCameraProvider]. +class ProcessCameraProviderHostApiImpl extends ProcessCameraProviderHostApi { + /// Creates a [ProcessCameraProviderHostApiImpl]. + ProcessCameraProviderHostApiImpl( + {this.binaryMessenger, InstanceManager? instanceManager}) + : super(binaryMessenger: binaryMessenger) { + this.instanceManager = instanceManager ?? JavaObject.globalInstanceManager; + } + + /// Receives binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + late final InstanceManager instanceManager; + + /// Retrieves an instance of a ProcessCameraProvider from the context of + /// the FlutterActivity. + Future getInstancefromInstances() async { + return instanceManager.getInstanceWithWeakReference(await getInstance())! + as ProcessCameraProvider; + } + + /// Retrives the list of CameraInfos corresponding to the available cameras. + Future> getAvailableCameraInfosFromInstances( + ProcessCameraProvider instance) async { + int? identifier = instanceManager.getIdentifier(instance); + identifier ??= instanceManager.addDartCreatedInstance(instance, + onCopy: (ProcessCameraProvider original) { + return ProcessCameraProvider.detached( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + }); + + final List cameraInfos = await getAvailableCameraInfos(identifier); + return (cameraInfos.map((int? id) => + instanceManager.getInstanceWithWeakReference(id!)! as CameraInfo)) + .toList(); + } +} + +/// Flutter API Implementation of [ProcessCameraProvider]. +class ProcessCameraProviderFlutterApiImpl + implements ProcessCameraProviderFlutterApi { + /// Constructs a [ProcessCameraProviderFlutterApiImpl]. + ProcessCameraProviderFlutterApiImpl({ + this.binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; + + /// Receives binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; + + @override + void create(int identifier) { + instanceManager.addHostCreatedInstance( + ProcessCameraProvider.detached( + binaryMessenger: binaryMessenger, instanceManager: instanceManager), + identifier, + onCopy: (ProcessCameraProvider original) { + return ProcessCameraProvider.detached( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + }, + ); + } +} diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart index aace7a06b1fd..4d7d96910246 100644 --- a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart +++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart @@ -57,3 +57,16 @@ abstract class CameraSelectorHostApi { abstract class CameraSelectorFlutterApi { void create(int identifier, int? lensFacing); } + +@HostApi(dartHostTestHandler: 'TestProcessCameraProviderHostApi') +abstract class ProcessCameraProviderHostApi { + @async + int getInstance(); + + List getAvailableCameraInfos(int identifier); +} + +@FlutterApi() +abstract class ProcessCameraProviderFlutterApi { + void create(int identifier); +} diff --git a/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart b/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart new file mode 100644 index 000000000000..65e7d00ddaea --- /dev/null +++ b/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart @@ -0,0 +1,96 @@ +// 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:camera_android_camerax/src/camera_info.dart'; +import 'package:camera_android_camerax/src/instance_manager.dart'; +import 'package:camera_android_camerax/src/process_camera_provider.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'process_camera_provider_test.mocks.dart'; +import 'test_camerax_library.pigeon.dart'; + +@GenerateMocks([TestProcessCameraProviderHostApi]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('ProcessCameraProvider', () { + tearDown(() => TestProcessCameraProviderHostApi.setup(null)); + + test('getInstanceTest', () async { + final MockTestProcessCameraProviderHostApi mockApi = + MockTestProcessCameraProviderHostApi(); + TestProcessCameraProviderHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final ProcessCameraProvider processCameraProvider = + ProcessCameraProvider.detached( + instanceManager: instanceManager, + ); + + instanceManager.addHostCreatedInstance( + processCameraProvider, + 0, + onCopy: (_) => ProcessCameraProvider.detached(), + ); + + when(mockApi.getInstance()).thenAnswer((_) async => 0); + expect( + await ProcessCameraProvider.getInstance( + instanceManager: instanceManager), + equals(processCameraProvider)); + verify(mockApi.getInstance()); + }); + + test('getAvailableCameraInfosTest', () async { + final MockTestProcessCameraProviderHostApi mockApi = + MockTestProcessCameraProviderHostApi(); + TestProcessCameraProviderHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final ProcessCameraProvider processCameraProvider = + ProcessCameraProvider.detached( + instanceManager: instanceManager, + ); + + instanceManager.addHostCreatedInstance( + processCameraProvider, + 0, + onCopy: (_) => ProcessCameraProvider.detached(), + ); + final CameraInfo fakeAvailableCameraInfo = + CameraInfo.detached(instanceManager: instanceManager); + instanceManager.addHostCreatedInstance( + fakeAvailableCameraInfo, + 1, + onCopy: (_) => CameraInfo.detached(), + ); + + when(mockApi.getAvailableCameraInfos(0)).thenReturn([1]); + expect(await processCameraProvider.getAvailableCameraInfos(), + equals([fakeAvailableCameraInfo])); + verify(mockApi.getAvailableCameraInfos(0)); + }); + + test('flutterApiCreateTest', () { + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final ProcessCameraProviderFlutterApiImpl flutterApi = + ProcessCameraProviderFlutterApiImpl( + instanceManager: instanceManager, + ); + + flutterApi.create(0); + + expect(instanceManager.getInstanceWithWeakReference(0), + isA()); + }); + }); +} diff --git a/packages/camera/camera_android_camerax/test/process_camera_provider_test.mocks.dart b/packages/camera/camera_android_camerax/test/process_camera_provider_test.mocks.dart new file mode 100644 index 000000000000..9fcfe690c062 --- /dev/null +++ b/packages/camera/camera_android_camerax/test/process_camera_provider_test.mocks.dart @@ -0,0 +1,40 @@ +// Mocks generated by Mockito 5.3.0 from annotations +// in camera_android_camerax/test/process_camera_provider_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i3; + +import 'package:mockito/mockito.dart' as _i1; + +import 'test_camerax_library.pigeon.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: 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 [TestProcessCameraProviderHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestProcessCameraProviderHostApi extends _i1.Mock + implements _i2.TestProcessCameraProviderHostApi { + MockTestProcessCameraProviderHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future getInstance() => + (super.noSuchMethod(Invocation.method(#getInstance, []), + returnValue: _i3.Future.value(0)) as _i3.Future); + @override + List getAvailableCameraInfos(int? identifier) => (super.noSuchMethod( + Invocation.method(#getAvailableCameraInfos, [identifier]), + returnValue: []) as List); +} diff --git a/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart b/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart index b10e14e9d518..2196b73d7fdb 100644 --- a/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart +++ b/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart @@ -135,3 +135,53 @@ abstract class TestCameraSelectorHostApi { } } } + +class _TestProcessCameraProviderHostApiCodec extends StandardMessageCodec { + const _TestProcessCameraProviderHostApiCodec(); +} + +abstract class TestProcessCameraProviderHostApi { + static const MessageCodec codec = + _TestProcessCameraProviderHostApiCodec(); + + Future getInstance(); + List getAvailableCameraInfos(int identifier); + static void setup(TestProcessCameraProviderHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getInstance', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + // ignore message + final int output = await api.getInstance(); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos 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.ProcessCameraProviderHostApi.getAvailableCameraInfos was null, expected non-null int.'); + final List output = + api.getAvailableCameraInfos(arg_identifier!); + return {'result': output}; + }); + } + } + } +} From f75cfb45e1ae7d9a37f5335a346faad80c929ddf Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 28 Sep 2022 08:23:08 -0400 Subject: [PATCH 764/844] Revert "[camera] Add ProcessCameraProvider class to CameraX plugin (#6469)" (#6506) This reverts commit 1c47c3b9d2788104985ebfc4660cafb390968b6e. --- .../camera_android_camerax/CHANGELOG.md | 1 - .../android/build.gradle | 4 +- .../camerax/CameraAndroidCameraxPlugin.java | 31 +--- .../camerax/CameraInfoFlutterApiImpl.java | 3 +- .../camerax/GeneratedCameraXLibrary.java | 134 ------------------ .../ProcessCameraProviderFlutterApiImpl.java | 23 --- .../ProcessCameraProviderHostApiImpl.java | 87 ------------ .../camerax/ProcessCameraProviderTest.java | 106 -------------- ...roid_camera_camerax_flutter_api_impls.dart | 9 -- .../lib/src/camerax_library.pigeon.dart | 109 -------------- .../lib/src/process_camera_provider.dart | 119 ---------------- .../pigeons/camerax_library.dart | 13 -- .../test/process_camera_provider_test.dart | 96 ------------- .../process_camera_provider_test.mocks.dart | 40 ------ .../test/test_camerax_library.pigeon.dart | 50 ------- 15 files changed, 7 insertions(+), 818 deletions(-) delete mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderFlutterApiImpl.java delete mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java delete mode 100644 packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java delete mode 100644 packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart delete mode 100644 packages/camera/camera_android_camerax/test/process_camera_provider_test.dart delete mode 100644 packages/camera/camera_android_camerax/test/process_camera_provider_test.mocks.dart diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index ce2fb9046c69..2bdfa594b3be 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -3,4 +3,3 @@ * Creates camera_android_camerax plugin for development. * Adds CameraInfo class and removes unnecessary code from plugin. * Adds CameraSelector class. -* Adds ProcessCameraProvider class. diff --git a/packages/camera/camera_android_camerax/android/build.gradle b/packages/camera/camera_android_camerax/android/build.gradle index a193a298c640..1772afebe429 100644 --- a/packages/camera/camera_android_camerax/android/build.gradle +++ b/packages/camera/camera_android_camerax/android/build.gradle @@ -38,15 +38,13 @@ android { dependencies { // CameraX core library using the camera2 implementation must use same version number. - def camerax_version = "1.2.0-beta02" + def camerax_version = "1.2.0-beta01" implementation "androidx.camera:camera-core:${camerax_version}" implementation "androidx.camera:camera-camera2:${camerax_version}" implementation "androidx.camera:camera-lifecycle:${camerax_version}" - implementation 'com.google.guava:guava:28.1-android' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:4.7.0' testImplementation 'androidx.test:core:1.4.0' - testImplementation 'org.robolectric:robolectric:4.3' } testOptions { unitTests.includeAndroidResources = true 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 b8fbaf539c32..bb5756a7e3b9 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 @@ -15,7 +15,6 @@ public final class CameraAndroidCameraxPlugin implements FlutterPlugin, ActivityAware { private InstanceManager instanceManager; private FlutterPluginBinding pluginBinding; - private ProcessCameraProviderHostApiImpl processCameraProviderHostApi; /** * Initialize this within the {@code #configureFlutterEngine} of a Flutter activity or fragment. @@ -40,10 +39,6 @@ void setUp(BinaryMessenger binaryMessenger, Context context) { binaryMessenger, new JavaObjectHostApiImpl(instanceManager)); GeneratedCameraXLibrary.CameraSelectorHostApi.setup( binaryMessenger, new CameraSelectorHostApiImpl(binaryMessenger, instanceManager)); - processCameraProviderHostApi = - new ProcessCameraProviderHostApiImpl(binaryMessenger, instanceManager, context); - GeneratedCameraXLibrary.ProcessCameraProviderHostApi.setup( - binaryMessenger, processCameraProviderHostApi); } @Override @@ -65,33 +60,15 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { // Activity Lifecycle methods: @Override - public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) { - updateContext(activityPluginBinding.getActivity()); - } + public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) {} @Override - public void onDetachedFromActivityForConfigChanges() { - updateContext(pluginBinding.getApplicationContext()); - } + public void onDetachedFromActivityForConfigChanges() {} @Override public void onReattachedToActivityForConfigChanges( - @NonNull ActivityPluginBinding activityPluginBinding) { - updateContext(activityPluginBinding.getActivity()); - } + @NonNull ActivityPluginBinding activityPluginBinding) {} @Override - public void onDetachedFromActivity() { - updateContext(pluginBinding.getApplicationContext()); - } - - /** - * Updates context that is used to fetch the corresponding instance of a {@code - * ProcessCameraProvider}. - */ - private void updateContext(Context context) { - if (processCameraProviderHostApi != null) { - processCameraProviderHostApi.setContext(context); - } - } + public void onDetachedFromActivity() {} } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java index c538e420cc7e..b5ba9fc1ff3b 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java @@ -18,6 +18,7 @@ public CameraInfoFlutterApiImpl( } void create(CameraInfo cameraInfo, Reply reply) { - create(instanceManager.addHostCreatedInstance(cameraInfo), reply); + instanceManager.addHostCreatedInstance(cameraInfo); + create(instanceManager.getIdentifierForStrongReference(cameraInfo), reply); } } 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 041564c3bfcb..e87a80db030c 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 @@ -22,13 +22,6 @@ /** Generated class from Pigeon. */ @SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"}) public class GeneratedCameraXLibrary { - - public interface Result { - void success(T result); - - void error(Throwable error); - } - private static class JavaObjectHostApiCodec extends StandardMessageCodec { public static final JavaObjectHostApiCodec INSTANCE = new JavaObjectHostApiCodec(); @@ -318,133 +311,6 @@ public void create( } } - private static class ProcessCameraProviderHostApiCodec extends StandardMessageCodec { - public static final ProcessCameraProviderHostApiCodec INSTANCE = - new ProcessCameraProviderHostApiCodec(); - - private ProcessCameraProviderHostApiCodec() {} - } - - /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ - public interface ProcessCameraProviderHostApi { - void getInstance(Result result); - - @NonNull - List getAvailableCameraInfos(@NonNull Long identifier); - - /** The codec used by ProcessCameraProviderHostApi. */ - static MessageCodec getCodec() { - return ProcessCameraProviderHostApiCodec.INSTANCE; - } - - /** - * Sets up an instance of `ProcessCameraProviderHostApi` to handle messages through the - * `binaryMessenger`. - */ - static void setup(BinaryMessenger binaryMessenger, ProcessCameraProviderHostApi api) { - { - BasicMessageChannel channel = - new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.ProcessCameraProviderHostApi.getInstance", - getCodec()); - if (api != null) { - channel.setMessageHandler( - (message, reply) -> { - Map wrapped = new HashMap<>(); - try { - Result resultCallback = - new Result() { - public void success(Long result) { - wrapped.put("result", result); - reply.reply(wrapped); - } - - public void error(Throwable error) { - wrapped.put("error", wrapError(error)); - reply.reply(wrapped); - } - }; - - api.getInstance(resultCallback); - } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - reply.reply(wrapped); - } - }); - } else { - channel.setMessageHandler(null); - } - } - { - BasicMessageChannel channel = - new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos", - getCodec()); - if (api != null) { - channel.setMessageHandler( - (message, reply) -> { - Map wrapped = new HashMap<>(); - try { - ArrayList args = (ArrayList) message; - Number identifierArg = (Number) args.get(0); - if (identifierArg == null) { - throw new NullPointerException("identifierArg unexpectedly null."); - } - List output = - api.getAvailableCameraInfos( - (identifierArg == null) ? null : identifierArg.longValue()); - wrapped.put("result", output); - } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); - } else { - channel.setMessageHandler(null); - } - } - } - } - - private static class ProcessCameraProviderFlutterApiCodec extends StandardMessageCodec { - public static final ProcessCameraProviderFlutterApiCodec INSTANCE = - new ProcessCameraProviderFlutterApiCodec(); - - private ProcessCameraProviderFlutterApiCodec() {} - } - - /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ - public static class ProcessCameraProviderFlutterApi { - private final BinaryMessenger binaryMessenger; - - public ProcessCameraProviderFlutterApi(BinaryMessenger argBinaryMessenger) { - this.binaryMessenger = argBinaryMessenger; - } - - public interface Reply { - void reply(T reply); - } - - static MessageCodec getCodec() { - return ProcessCameraProviderFlutterApiCodec.INSTANCE; - } - - public void create(@NonNull Long identifierArg, Reply callback) { - BasicMessageChannel channel = - new BasicMessageChannel<>( - binaryMessenger, - "dev.flutter.pigeon.ProcessCameraProviderFlutterApi.create", - getCodec()); - channel.send( - new ArrayList(Arrays.asList(identifierArg)), - channelReply -> { - callback.reply(null); - }); - } - } - private static Map wrapError(Throwable exception) { Map errorMap = new HashMap<>(); errorMap.put("message", exception.toString()); diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderFlutterApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderFlutterApiImpl.java deleted file mode 100644 index 90c94d0c26cb..000000000000 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderFlutterApiImpl.java +++ /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. - -package io.flutter.plugins.camerax; - -import androidx.camera.lifecycle.ProcessCameraProvider; -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ProcessCameraProviderFlutterApi; - -public class ProcessCameraProviderFlutterApiImpl extends ProcessCameraProviderFlutterApi { - public ProcessCameraProviderFlutterApiImpl( - BinaryMessenger binaryMessenger, InstanceManager instanceManager) { - super(binaryMessenger); - this.instanceManager = instanceManager; - } - - private final InstanceManager instanceManager; - - void create(ProcessCameraProvider processCameraProvider, Reply reply) { - create(instanceManager.addHostCreatedInstance(processCameraProvider), reply); - } -} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java deleted file mode 100644 index 19c5eb5b3f70..000000000000 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java +++ /dev/null @@ -1,87 +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.camerax; - -import android.content.Context; -import androidx.annotation.NonNull; -import androidx.camera.core.CameraInfo; -import androidx.camera.lifecycle.ProcessCameraProvider; -import androidx.core.content.ContextCompat; -import com.google.common.util.concurrent.ListenableFuture; -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ProcessCameraProviderHostApi; -import java.util.ArrayList; -import java.util.List; - -public class ProcessCameraProviderHostApiImpl implements ProcessCameraProviderHostApi { - private final BinaryMessenger binaryMessenger; - private final InstanceManager instanceManager; - - private Context context; - - public ProcessCameraProviderHostApiImpl( - BinaryMessenger binaryMessenger, InstanceManager instanceManager, Context context) { - this.binaryMessenger = binaryMessenger; - this.instanceManager = instanceManager; - this.context = context; - } - - /** - * Sets the context that the {@code ProcessCameraProvider} will use to attach the lifecycle of the - * camera to. - * - *

If using the camera plugin in an add-to-app context, ensure that a new instance of the - * {@code ProcessCameraProvider} is fetched via {@code #getInstance} anytime the context changes. - */ - public void setContext(Context context) { - this.context = context; - } - - /** - * Returns the instance of the ProcessCameraProvider to manage the lifecycle of the camera for the - * current {@code Context}. - */ - @Override - public void getInstance(GeneratedCameraXLibrary.Result result) { - ListenableFuture processCameraProviderFuture = - ProcessCameraProvider.getInstance(context); - - processCameraProviderFuture.addListener( - () -> { - try { - // Camera provider is now guaranteed to be available. - ProcessCameraProvider processCameraProvider = processCameraProviderFuture.get(); - - if (!instanceManager.containsInstance(processCameraProvider)) { - final ProcessCameraProviderFlutterApiImpl flutterApi = - new ProcessCameraProviderFlutterApiImpl(binaryMessenger, instanceManager); - flutterApi.create(processCameraProvider, reply -> {}); - } - result.success(instanceManager.getIdentifierForStrongReference(processCameraProvider)); - } catch (Exception e) { - result.error(e); - } - }, - ContextCompat.getMainExecutor(context)); - } - - /** Returns cameras available to the ProcessCameraProvider. */ - @Override - public List getAvailableCameraInfos(@NonNull Long identifier) { - ProcessCameraProvider processCameraProvider = - (ProcessCameraProvider) instanceManager.getInstance(identifier); - - List availableCameras = processCameraProvider.getAvailableCameraInfos(); - List availableCamerasIds = new ArrayList(); - final CameraInfoFlutterApiImpl cameraInfoFlutterApi = - new CameraInfoFlutterApiImpl(binaryMessenger, instanceManager); - - for (CameraInfo cameraInfo : availableCameras) { - cameraInfoFlutterApi.create(cameraInfo, result -> {}); - availableCamerasIds.add(instanceManager.getIdentifierForStrongReference(cameraInfo)); - } - return availableCamerasIds; - } -} diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java deleted file mode 100644 index ec321f8dbfea..000000000000 --- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java +++ /dev/null @@ -1,106 +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.camerax; - -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import androidx.camera.core.CameraInfo; -import androidx.camera.lifecycle.ProcessCameraProvider; -import androidx.test.core.app.ApplicationProvider; -import com.google.common.util.concurrent.SettableFuture; -import io.flutter.plugin.common.BinaryMessenger; -import java.util.Arrays; -import java.util.Objects; -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; - -@RunWith(RobolectricTestRunner.class) -public class ProcessCameraProviderTest { - @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); - - @Mock public ProcessCameraProvider processCameraProvider; - @Mock public BinaryMessenger mockBinaryMessenger; - - InstanceManager testInstanceManager; - private Context context; - - @Before - public void setUp() { - testInstanceManager = InstanceManager.open(identifier -> {}); - context = ApplicationProvider.getApplicationContext(); - } - - @After - public void tearDown() { - testInstanceManager.close(); - } - - @Test - public void getInstanceTest() { - final ProcessCameraProviderHostApiImpl processCameraProviderHostApi = - new ProcessCameraProviderHostApiImpl(mockBinaryMessenger, testInstanceManager, context); - SettableFuture processCameraProviderFuture = SettableFuture.create(); - processCameraProviderFuture.set(processCameraProvider); - final GeneratedCameraXLibrary.Result mockResult = - mock(GeneratedCameraXLibrary.Result.class); - - testInstanceManager.addDartCreatedInstance(processCameraProvider, 0); - - try (MockedStatic mockedProcessCameraProvider = - Mockito.mockStatic(ProcessCameraProvider.class)) { - mockedProcessCameraProvider - .when(() -> ProcessCameraProvider.getInstance(context)) - .thenAnswer((Answer) invocation -> processCameraProviderFuture); - - processCameraProviderHostApi.getInstance(mockResult); - verify(mockResult).success(0L); - } - } - - @Test - public void getAvailableCameraInfosTest() { - final ProcessCameraProviderHostApiImpl processCameraProviderHostApi = - new ProcessCameraProviderHostApiImpl(mockBinaryMessenger, testInstanceManager, context); - final CameraInfo mockCameraInfo = mock(CameraInfo.class); - - testInstanceManager.addDartCreatedInstance(processCameraProvider, 0); - testInstanceManager.addDartCreatedInstance(mockCameraInfo, 1); - - when(processCameraProvider.getAvailableCameraInfos()).thenReturn(Arrays.asList(mockCameraInfo)); - - assertEquals(processCameraProviderHostApi.getAvailableCameraInfos(0L), Arrays.asList(1L)); - verify(processCameraProvider).getAvailableCameraInfos(); - } - - @Test - public void flutterApiCreateTest() { - final ProcessCameraProviderFlutterApiImpl spyFlutterApi = - spy(new ProcessCameraProviderFlutterApiImpl(mockBinaryMessenger, testInstanceManager)); - - spyFlutterApi.create(processCameraProvider, reply -> {}); - - final long identifier = - Objects.requireNonNull( - testInstanceManager.getIdentifierForStrongReference(processCameraProvider)); - verify(spyFlutterApi).create(eq(identifier), any()); - } -} diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart index 9c6564a06c08..576260c0b7b8 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart @@ -6,7 +6,6 @@ import 'camera_info.dart'; import 'camera_selector.dart'; import 'camerax_library.pigeon.dart'; import 'java_object.dart'; -import 'process_camera_provider.dart'; /// Handles initialization of Flutter APIs for the Android CameraX library. class AndroidCameraXCameraFlutterApis { @@ -15,7 +14,6 @@ class AndroidCameraXCameraFlutterApis { JavaObjectFlutterApiImpl? javaObjectFlutterApi, CameraInfoFlutterApiImpl? cameraInfoFlutterApi, CameraSelectorFlutterApiImpl? cameraSelectorFlutterApi, - ProcessCameraProviderFlutterApiImpl? processCameraProviderFlutterApi, }) { this.javaObjectFlutterApi = javaObjectFlutterApi ?? JavaObjectFlutterApiImpl(); @@ -23,8 +21,6 @@ class AndroidCameraXCameraFlutterApis { cameraInfoFlutterApi ?? CameraInfoFlutterApiImpl(); this.cameraSelectorFlutterApi = cameraSelectorFlutterApi ?? CameraSelectorFlutterApiImpl(); - this.processCameraProviderFlutterApi = processCameraProviderFlutterApi ?? - ProcessCameraProviderFlutterApiImpl(); } static bool _haveBeenSetUp = false; @@ -44,17 +40,12 @@ class AndroidCameraXCameraFlutterApis { /// Flutter Api for [CameraSelector]. late final CameraSelectorFlutterApiImpl cameraSelectorFlutterApi; - /// Flutter Api for [ProcessCameraProvider]. - late final ProcessCameraProviderFlutterApiImpl - processCameraProviderFlutterApi; - /// Ensures all the Flutter APIs have been setup to receive calls from native code. void ensureSetUp() { if (!_haveBeenSetUp) { JavaObjectFlutterApi.setup(javaObjectFlutterApi); CameraInfoFlutterApi.setup(cameraInfoFlutterApi); CameraSelectorFlutterApi.setup(cameraSelectorFlutterApi); - ProcessCameraProviderFlutterApi.setup(processCameraProviderFlutterApi); _haveBeenSetUp = true; } } diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart b/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart index c0b052378def..a399001d35b0 100644 --- a/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart +++ b/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart @@ -263,112 +263,3 @@ abstract class CameraSelectorFlutterApi { } } } - -class _ProcessCameraProviderHostApiCodec extends StandardMessageCodec { - const _ProcessCameraProviderHostApiCodec(); -} - -class ProcessCameraProviderHostApi { - /// Constructor for [ProcessCameraProviderHostApi]. 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. - ProcessCameraProviderHostApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - - final BinaryMessenger? _binaryMessenger; - - static const MessageCodec codec = - _ProcessCameraProviderHostApiCodec(); - - Future getInstance() async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getInstance', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send(null) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; - throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], - ); - } else if (replyMap['result'] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyMap['result'] as int?)!; - } - } - - Future> getAvailableCameraInfos(int arg_identifier) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos', - codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = - await channel.send([arg_identifier]) as Map?; - if (replyMap == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; - throw PlatformException( - code: (error['code'] as String?)!, - message: error['message'] as String?, - details: error['details'], - ); - } else if (replyMap['result'] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyMap['result'] as List?)!.cast(); - } - } -} - -class _ProcessCameraProviderFlutterApiCodec extends StandardMessageCodec { - const _ProcessCameraProviderFlutterApiCodec(); -} - -abstract class ProcessCameraProviderFlutterApi { - static const MessageCodec codec = - _ProcessCameraProviderFlutterApiCodec(); - - void create(int identifier); - static void setup(ProcessCameraProviderFlutterApi? api, - {BinaryMessenger? binaryMessenger}) { - { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ProcessCameraProviderFlutterApi.create', codec, - binaryMessenger: binaryMessenger); - if (api == null) { - channel.setMessageHandler(null); - } else { - channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.ProcessCameraProviderFlutterApi.create 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.ProcessCameraProviderFlutterApi.create was null, expected non-null int.'); - api.create(arg_identifier!); - return; - }); - } - } - } -} diff --git a/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart b/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart deleted file mode 100644 index e2b588d15faa..000000000000 --- a/packages/camera/camera_android_camerax/lib/src/process_camera_provider.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/services.dart'; - -import 'android_camera_camerax_flutter_api_impls.dart'; -import 'camera_info.dart'; -import 'camerax_library.pigeon.dart'; -import 'instance_manager.dart'; -import 'java_object.dart'; - -/// Provides an object to manage the camera. -/// -/// See https://developer.android.com/reference/androidx/camera/lifecycle/ProcessCameraProvider. -class ProcessCameraProvider extends JavaObject { - /// Creates a detached [ProcessCameraProvider]. - ProcessCameraProvider.detached( - {BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) - : super.detached( - binaryMessenger: binaryMessenger, - instanceManager: instanceManager) { - _api = ProcessCameraProviderHostApiImpl( - binaryMessenger: binaryMessenger, instanceManager: instanceManager); - AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); - } - - late final ProcessCameraProviderHostApiImpl _api; - - /// Gets an instance of [ProcessCameraProvider]. - static Future getInstance( - {BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) { - AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); - final ProcessCameraProviderHostApiImpl api = - ProcessCameraProviderHostApiImpl( - binaryMessenger: binaryMessenger, instanceManager: instanceManager); - - return api.getInstancefromInstances(); - } - - /// Retrieves the cameras available to the device. - Future> getAvailableCameraInfos() { - return _api.getAvailableCameraInfosFromInstances(this); - } -} - -/// Host API implementation of [ProcessCameraProvider]. -class ProcessCameraProviderHostApiImpl extends ProcessCameraProviderHostApi { - /// Creates a [ProcessCameraProviderHostApiImpl]. - ProcessCameraProviderHostApiImpl( - {this.binaryMessenger, InstanceManager? instanceManager}) - : super(binaryMessenger: binaryMessenger) { - this.instanceManager = instanceManager ?? JavaObject.globalInstanceManager; - } - - /// Receives binary data across the Flutter platform barrier. - /// - /// If it is null, the default BinaryMessenger will be used which routes to - /// the host platform. - final BinaryMessenger? binaryMessenger; - - /// Maintains instances stored to communicate with native language objects. - late final InstanceManager instanceManager; - - /// Retrieves an instance of a ProcessCameraProvider from the context of - /// the FlutterActivity. - Future getInstancefromInstances() async { - return instanceManager.getInstanceWithWeakReference(await getInstance())! - as ProcessCameraProvider; - } - - /// Retrives the list of CameraInfos corresponding to the available cameras. - Future> getAvailableCameraInfosFromInstances( - ProcessCameraProvider instance) async { - int? identifier = instanceManager.getIdentifier(instance); - identifier ??= instanceManager.addDartCreatedInstance(instance, - onCopy: (ProcessCameraProvider original) { - return ProcessCameraProvider.detached( - binaryMessenger: binaryMessenger, instanceManager: instanceManager); - }); - - final List cameraInfos = await getAvailableCameraInfos(identifier); - return (cameraInfos.map((int? id) => - instanceManager.getInstanceWithWeakReference(id!)! as CameraInfo)) - .toList(); - } -} - -/// Flutter API Implementation of [ProcessCameraProvider]. -class ProcessCameraProviderFlutterApiImpl - implements ProcessCameraProviderFlutterApi { - /// Constructs a [ProcessCameraProviderFlutterApiImpl]. - ProcessCameraProviderFlutterApiImpl({ - this.binaryMessenger, - InstanceManager? instanceManager, - }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; - - /// Receives binary data across the Flutter platform barrier. - /// - /// If it is null, the default BinaryMessenger will be used which routes to - /// the host platform. - final BinaryMessenger? binaryMessenger; - - /// Maintains instances stored to communicate with native language objects. - final InstanceManager instanceManager; - - @override - void create(int identifier) { - instanceManager.addHostCreatedInstance( - ProcessCameraProvider.detached( - binaryMessenger: binaryMessenger, instanceManager: instanceManager), - identifier, - onCopy: (ProcessCameraProvider original) { - return ProcessCameraProvider.detached( - binaryMessenger: binaryMessenger, instanceManager: instanceManager); - }, - ); - } -} diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart index 4d7d96910246..aace7a06b1fd 100644 --- a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart +++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart @@ -57,16 +57,3 @@ abstract class CameraSelectorHostApi { abstract class CameraSelectorFlutterApi { void create(int identifier, int? lensFacing); } - -@HostApi(dartHostTestHandler: 'TestProcessCameraProviderHostApi') -abstract class ProcessCameraProviderHostApi { - @async - int getInstance(); - - List getAvailableCameraInfos(int identifier); -} - -@FlutterApi() -abstract class ProcessCameraProviderFlutterApi { - void create(int identifier); -} diff --git a/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart b/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart deleted file mode 100644 index 65e7d00ddaea..000000000000 --- a/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart +++ /dev/null @@ -1,96 +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:camera_android_camerax/src/camera_info.dart'; -import 'package:camera_android_camerax/src/instance_manager.dart'; -import 'package:camera_android_camerax/src/process_camera_provider.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/annotations.dart'; -import 'package:mockito/mockito.dart'; - -import 'process_camera_provider_test.mocks.dart'; -import 'test_camerax_library.pigeon.dart'; - -@GenerateMocks([TestProcessCameraProviderHostApi]) -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - group('ProcessCameraProvider', () { - tearDown(() => TestProcessCameraProviderHostApi.setup(null)); - - test('getInstanceTest', () async { - final MockTestProcessCameraProviderHostApi mockApi = - MockTestProcessCameraProviderHostApi(); - TestProcessCameraProviderHostApi.setup(mockApi); - - final InstanceManager instanceManager = InstanceManager( - onWeakReferenceRemoved: (_) {}, - ); - final ProcessCameraProvider processCameraProvider = - ProcessCameraProvider.detached( - instanceManager: instanceManager, - ); - - instanceManager.addHostCreatedInstance( - processCameraProvider, - 0, - onCopy: (_) => ProcessCameraProvider.detached(), - ); - - when(mockApi.getInstance()).thenAnswer((_) async => 0); - expect( - await ProcessCameraProvider.getInstance( - instanceManager: instanceManager), - equals(processCameraProvider)); - verify(mockApi.getInstance()); - }); - - test('getAvailableCameraInfosTest', () async { - final MockTestProcessCameraProviderHostApi mockApi = - MockTestProcessCameraProviderHostApi(); - TestProcessCameraProviderHostApi.setup(mockApi); - - final InstanceManager instanceManager = InstanceManager( - onWeakReferenceRemoved: (_) {}, - ); - final ProcessCameraProvider processCameraProvider = - ProcessCameraProvider.detached( - instanceManager: instanceManager, - ); - - instanceManager.addHostCreatedInstance( - processCameraProvider, - 0, - onCopy: (_) => ProcessCameraProvider.detached(), - ); - final CameraInfo fakeAvailableCameraInfo = - CameraInfo.detached(instanceManager: instanceManager); - instanceManager.addHostCreatedInstance( - fakeAvailableCameraInfo, - 1, - onCopy: (_) => CameraInfo.detached(), - ); - - when(mockApi.getAvailableCameraInfos(0)).thenReturn([1]); - expect(await processCameraProvider.getAvailableCameraInfos(), - equals([fakeAvailableCameraInfo])); - verify(mockApi.getAvailableCameraInfos(0)); - }); - - test('flutterApiCreateTest', () { - final InstanceManager instanceManager = InstanceManager( - onWeakReferenceRemoved: (_) {}, - ); - final ProcessCameraProviderFlutterApiImpl flutterApi = - ProcessCameraProviderFlutterApiImpl( - instanceManager: instanceManager, - ); - - flutterApi.create(0); - - expect(instanceManager.getInstanceWithWeakReference(0), - isA()); - }); - }); -} diff --git a/packages/camera/camera_android_camerax/test/process_camera_provider_test.mocks.dart b/packages/camera/camera_android_camerax/test/process_camera_provider_test.mocks.dart deleted file mode 100644 index 9fcfe690c062..000000000000 --- a/packages/camera/camera_android_camerax/test/process_camera_provider_test.mocks.dart +++ /dev/null @@ -1,40 +0,0 @@ -// Mocks generated by Mockito 5.3.0 from annotations -// in camera_android_camerax/test/process_camera_provider_test.dart. -// Do not manually edit this file. - -// ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i3; - -import 'package:mockito/mockito.dart' as _i1; - -import 'test_camerax_library.pigeon.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: 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 [TestProcessCameraProviderHostApi]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockTestProcessCameraProviderHostApi extends _i1.Mock - implements _i2.TestProcessCameraProviderHostApi { - MockTestProcessCameraProviderHostApi() { - _i1.throwOnMissingStub(this); - } - - @override - _i3.Future getInstance() => - (super.noSuchMethod(Invocation.method(#getInstance, []), - returnValue: _i3.Future.value(0)) as _i3.Future); - @override - List getAvailableCameraInfos(int? identifier) => (super.noSuchMethod( - Invocation.method(#getAvailableCameraInfos, [identifier]), - returnValue: []) as List); -} diff --git a/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart b/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart index 2196b73d7fdb..b10e14e9d518 100644 --- a/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart +++ b/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart @@ -135,53 +135,3 @@ abstract class TestCameraSelectorHostApi { } } } - -class _TestProcessCameraProviderHostApiCodec extends StandardMessageCodec { - const _TestProcessCameraProviderHostApiCodec(); -} - -abstract class TestProcessCameraProviderHostApi { - static const MessageCodec codec = - _TestProcessCameraProviderHostApiCodec(); - - Future getInstance(); - List getAvailableCameraInfos(int identifier); - static void setup(TestProcessCameraProviderHostApi? api, - {BinaryMessenger? binaryMessenger}) { - { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getInstance', codec, - binaryMessenger: binaryMessenger); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - // ignore message - final int output = await api.getInstance(); - return {'result': output}; - }); - } - } - { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos', - codec, - binaryMessenger: binaryMessenger); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos 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.ProcessCameraProviderHostApi.getAvailableCameraInfos was null, expected non-null int.'); - final List output = - api.getAvailableCameraInfos(arg_identifier!); - return {'result': output}; - }); - } - } - } -} From ded5528fa0224f943f9bd92f25e9ff1260f59490 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 28 Sep 2022 10:25:24 -0400 Subject: [PATCH 765/844] Enable `prefer_relative_imports` (#6501) --- analysis_options.yaml | 2 +- packages/camera/camera/CHANGELOG.md | 3 +- .../camera/lib/src/camera_controller.dart | 3 +- .../camera/camera/lib/src/camera_preview.dart | 3 +- packages/camera/camera/pubspec.yaml | 2 +- .../camera_platform_interface/CHANGELOG.md | 3 +- .../lib/src/events/device_event.dart | 3 +- .../method_channel/method_channel_camera.dart | 4 +-- .../platform_interface/camera_platform.dart | 5 +-- .../lib/src/utils/utils.dart | 3 +- .../camera_platform_interface/pubspec.yaml | 2 +- packages/camera/camera_web/CHANGELOG.md | 3 +- .../camera/camera_web/lib/src/camera.dart | 4 +-- .../camera_web/lib/src/camera_service.dart | 7 ++-- .../camera/camera_web/lib/src/camera_web.dart | 7 ++-- .../lib/src/types/camera_web_exception.dart | 2 +- packages/camera/camera_web/pubspec.yaml | 2 +- .../file_selector_macos/CHANGELOG.md | 3 +- .../file_selector_macos/example/lib/main.dart | 13 +++---- .../file_selector_macos/pubspec.yaml | 2 +- .../CHANGELOG.md | 3 +- .../method_channel_file_selector.dart | 3 +- .../file_selector_interface.dart | 2 +- .../pubspec.yaml | 2 +- .../file_selector_web/CHANGELOG.md | 3 +- .../lib/file_selector_web.dart | 5 +-- .../file_selector_web/pubspec.yaml | 2 +- .../file_selector_windows/CHANGELOG.md | 3 +- .../example/lib/main.dart | 13 +++---- .../file_selector_windows/pubspec.yaml | 2 +- .../google_maps_flutter/CHANGELOG.md | 4 +++ .../google_maps_flutter/example/lib/main.dart | 4 +-- .../google_maps_flutter/pubspec.yaml | 2 +- .../google_maps_flutter_android/CHANGELOG.md | 4 +++ .../example/lib/main.dart | 2 +- .../google_maps_flutter_android/pubspec.yaml | 2 +- .../google_maps_flutter_ios/CHANGELOG.md | 3 +- .../example/lib/main.dart | 2 +- .../google_maps_flutter_ios/pubspec.yaml | 2 +- .../CHANGELOG.md | 3 +- .../lib/src/events/map_event.dart | 2 +- .../method_channel_google_maps_flutter.dart | 2 +- .../google_maps_flutter_platform.dart | 5 +-- .../google_maps_inspector_platform.dart | 3 +- .../pubspec.yaml | 2 +- .../google_maps_flutter_web/CHANGELOG.md | 3 +- .../lib/src/types.dart | 3 +- .../google_maps_flutter_web/pubspec.yaml | 2 +- .../image_picker_for_web/CHANGELOG.md | 3 +- .../lib/image_picker_for_web.dart | 3 +- .../lib/src/image_resizer.dart | 3 +- .../image_picker_for_web/pubspec.yaml | 2 +- .../CHANGELOG.md | 3 +- .../method_channel_image_picker.dart | 2 +- .../image_picker_platform.dart | 5 +-- .../lib/src/types/image_picker_options.dart | 2 +- .../lib/src/types/lost_data_response.dart | 3 +- .../src/types/multi_image_picker_options.dart | 2 +- .../lib/src/types/picked_file/lost_data.dart | 3 +- .../pubspec.yaml | 2 +- .../in_app_purchase_android/CHANGELOG.md | 4 +++ .../src/in_app_purchase_android_platform.dart | 2 +- ...pp_purchase_android_platform_addition.dart | 2 +- .../types/google_play_product_details.dart | 3 +- .../in_app_purchase_android/pubspec.yaml | 2 +- .../CHANGELOG.md | 3 +- .../in_app_purchase_platform_addition.dart | 2 +- ...p_purchase_platform_addition_provider.dart | 2 +- .../pubspec.yaml | 2 +- .../in_app_purchase_storekit/CHANGELOG.md | 4 +++ .../example/lib/main.dart | 2 +- ...p_purchase_storekit_platform_addition.dart | 2 +- .../sk_payment_queue_delegate_wrapper.dart | 2 +- .../sk_payment_queue_wrapper.dart | 2 +- .../in_app_purchase_storekit/pubspec.yaml | 2 +- .../local_auth_android/CHANGELOG.md | 4 +++ .../lib/local_auth_android.dart | 3 +- .../local_auth_android/pubspec.yaml | 2 +- .../local_auth/local_auth_ios/CHANGELOG.md | 3 +- .../local_auth_ios/lib/local_auth_ios.dart | 3 +- .../local_auth/local_auth_ios/pubspec.yaml | 2 +- .../CHANGELOG.md | 3 +- .../lib/default_method_channel_platform.dart | 2 +- .../lib/local_auth_platform_interface.dart | 5 +-- .../pubspec.yaml | 2 +- .../local_auth_windows/CHANGELOG.md | 3 +- .../lib/local_auth_windows.dart | 2 +- .../local_auth_windows/pubspec.yaml | 2 +- .../CHANGELOG.md | 3 +- .../lib/src/method_channel_path_provider.dart | 3 +- .../pubspec.yaml | 2 +- .../CHANGELOG.md | 3 +- .../method_channel_quick_actions.dart | 2 +- .../quick_actions_platform.dart | 2 +- .../pubspec.yaml | 2 +- .../url_launcher/url_launcher/CHANGELOG.md | 3 +- .../lib/src/url_launcher_uri.dart | 2 +- .../url_launcher/url_launcher/pubspec.yaml | 2 +- .../CHANGELOG.md | 3 +- .../lib/src/url_launcher_platform.dart | 4 +-- .../pubspec.yaml | 2 +- .../webview_flutter_android/CHANGELOG.md | 4 +++ .../lib/webview_android_cookie_manager.dart | 4 +-- .../lib/webview_android_widget.dart | 2 +- .../webview_flutter_android/pubspec.yaml | 2 +- .../CHANGELOG.md | 4 +++ .../webview_cookie_manager.dart | 3 +- .../platform_interface/webview_platform.dart | 2 +- .../lib/src/types/creation_params.dart | 3 +- .../pubspec.yaml | 2 +- .../webview_flutter_wkwebview/CHANGELOG.md | 4 +++ .../lib/src/foundation/foundation.dart | 2 +- .../lib/src/ui_kit/ui_kit_api_impls.dart | 3 +- .../lib/src/webview_cupertino.dart | 2 +- .../lib/src/wkwebview_cookie_manager.dart | 5 +-- .../webview_flutter_wkwebview/pubspec.yaml | 2 +- script/tool/lib/src/common/cmake.dart | 2 +- .../lib/src/common/package_state_utils.dart | 2 +- script/tool/lib/src/common/plugin_utils.dart | 2 +- .../src/federation_safety_check_command.dart | 2 +- script/tool/lib/src/lint_android_command.dart | 2 +- script/tool/lib/src/main.dart | 2 +- .../lib/src/make_deps_path_based_command.dart | 4 ++- .../tool/lib/src/update_excerpts_command.dart | 2 +- .../lib/src/update_release_info_command.dart | 2 +- .../make_deps_path_based_command_test.dart | 36 +++++++++++++++++++ 126 files changed, 259 insertions(+), 143 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 34f50180906d..a8cff3ca7d01 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -193,7 +193,7 @@ linter: # - prefer_mixin # Has false positives, see https://github.com/dart-lang/linter/issues/3018 # - prefer_null_aware_method_calls # "call()" is confusing to people new to the language since it's not documented anywhere - prefer_null_aware_operators - # - prefer_relative_imports # LOCAL CHANGE - Needs to be enabled and violations fixed. + - prefer_relative_imports - prefer_single_quotes - prefer_spread_collections - prefer_typing_uninitialized_variables diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 67b8b7970bbe..6f4a9f491542 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.10.0+2 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. ## 0.10.0+1 diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index 6566e2abc883..13bb63700ad3 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -5,13 +5,14 @@ import 'dart:async'; import 'dart:math'; -import 'package:camera/camera.dart'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:quiver/core.dart'; +import '../camera.dart'; + /// Signature for a callback receiving the a camera image. /// /// This is used by [CameraController.startImageStream]. diff --git a/packages/camera/camera/lib/src/camera_preview.dart b/packages/camera/camera/lib/src/camera_preview.dart index 94ffca649fa6..d8eadd8c93ae 100644 --- a/packages/camera/camera/lib/src/camera_preview.dart +++ b/packages/camera/camera/lib/src/camera_preview.dart @@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:camera/camera.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import '../camera.dart'; + /// A widget showing a live camera preview. class CameraPreview extends StatelessWidget { /// Creates a preview widget for the given camera controller. diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 5a68f6da8036..2770e977bcc9 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.10.0+1 +version: 0.10.0+2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index 7411db738483..bdfbd44a229a 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.2.1 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). diff --git a/packages/camera/camera_platform_interface/lib/src/events/device_event.dart b/packages/camera/camera_platform_interface/lib/src/events/device_event.dart index d6bb5df05980..65a378f16f12 100644 --- a/packages/camera/camera_platform_interface/lib/src/events/device_event.dart +++ b/packages/camera/camera_platform_interface/lib/src/events/device_event.dart @@ -2,10 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:camera_platform_interface/src/utils/utils.dart'; import 'package:flutter/foundation.dart' show immutable; import 'package:flutter/services.dart'; +import '../utils/utils.dart'; + /// Generic Event coming from the native side of Camera, /// not related to a specific camera module. /// diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index eb6e59d9b4a1..68e1315f8bbe 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -5,13 +5,13 @@ import 'dart:async'; import 'dart:math'; -import 'package:camera_platform_interface/camera_platform_interface.dart'; -import 'package:camera_platform_interface/src/utils/utils.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:stream_transform/stream_transform.dart'; +import '../../camera_platform_interface.dart'; +import '../utils/utils.dart'; import 'type_conversion.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/camera'); 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 eaa779a943db..b086dc87851f 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 @@ -5,12 +5,13 @@ import 'dart:async'; import 'dart:math'; -import 'package:camera_platform_interface/camera_platform_interface.dart'; -import 'package:camera_platform_interface/src/method_channel/method_channel_camera.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import '../../camera_platform_interface.dart'; +import '../method_channel/method_channel_camera.dart'; + /// The interface that implementations of camera must implement. /// /// Platform implementations should extend this class rather than implement it as `camera` diff --git a/packages/camera/camera_platform_interface/lib/src/utils/utils.dart b/packages/camera/camera_platform_interface/lib/src/utils/utils.dart index 663ec6da7a97..d86880afd216 100644 --- a/packages/camera/camera_platform_interface/lib/src/utils/utils.dart +++ b/packages/camera/camera_platform_interface/lib/src/utils/utils.dart @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/services.dart'; +import '../../camera_platform_interface.dart'; + /// Parses a string into a corresponding CameraLensDirection. CameraLensDirection parseCameraLensDirection(String string) { switch (string) { diff --git a/packages/camera/camera_platform_interface/pubspec.yaml b/packages/camera/camera_platform_interface/pubspec.yaml index 1c874725e9ce..9cbd51b1bd2d 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/plugins/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.2.0 +version: 2.2.1 environment: sdk: '>=2.12.0 <3.0.0' diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index c6254ac11d38..f4989cfd5bff 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.3.0+1 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/106316). diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart index 210a0df59eec..13ef21b1ea46 100644 --- a/packages/camera/camera_web/lib/src/camera.dart +++ b/packages/camera/camera_web/lib/src/camera.dart @@ -7,11 +7,11 @@ import 'dart:html' as html; import 'dart:ui'; import 'package:camera_platform_interface/camera_platform_interface.dart'; -import 'package:camera_web/src/camera_service.dart'; -import 'package:camera_web/src/types/types.dart'; import 'package:flutter/foundation.dart'; +import 'camera_service.dart'; import 'shims/dart_ui.dart' as ui; +import 'types/types.dart'; String _getViewType(int cameraId) => 'plugins.flutter.io/camera_$cameraId'; diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index 5f4a5fdde9a4..6e20c7d74f78 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -8,12 +8,13 @@ import 'dart:html' as html; import 'dart:ui'; import 'package:camera_platform_interface/camera_platform_interface.dart'; -import 'package:camera_web/src/camera.dart'; -import 'package:camera_web/src/shims/dart_js_util.dart'; -import 'package:camera_web/src/types/types.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; +import 'camera.dart'; +import 'shims/dart_js_util.dart'; +import 'types/types.dart'; + /// A service to fetch, map camera settings and /// obtain the camera stream. class CameraService { diff --git a/packages/camera/camera_web/lib/src/camera_web.dart b/packages/camera/camera_web/lib/src/camera_web.dart index 26f965d49e16..d440653cd424 100644 --- a/packages/camera/camera_web/lib/src/camera_web.dart +++ b/packages/camera/camera_web/lib/src/camera_web.dart @@ -7,14 +7,15 @@ import 'dart:html' as html; import 'dart:math'; import 'package:camera_platform_interface/camera_platform_interface.dart'; -import 'package:camera_web/src/camera.dart'; -import 'package:camera_web/src/camera_service.dart'; -import 'package:camera_web/src/types/types.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:stream_transform/stream_transform.dart'; +import 'camera.dart'; +import 'camera_service.dart'; +import 'types/types.dart'; + // The default error message, when the error is an empty string. // See: https://developer.mozilla.org/en-US/docs/Web/API/MediaError/message const String _kDefaultErrorMessage = diff --git a/packages/camera/camera_web/lib/src/types/camera_web_exception.dart b/packages/camera/camera_web/lib/src/types/camera_web_exception.dart index c21106cc462e..e6c6d7a0fed0 100644 --- a/packages/camera/camera_web/lib/src/types/camera_web_exception.dart +++ b/packages/camera/camera_web/lib/src/types/camera_web_exception.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:camera_web/src/types/types.dart'; +import 'types.dart'; /// An exception thrown when the camera with id [cameraId] reports /// an initialization, configuration or video streaming error, diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 527b28367eaf..ef9c45c71796 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.3.0 +version: 0.3.0+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_macos/CHANGELOG.md b/packages/file_selector/file_selector_macos/CHANGELOG.md index ec09997a52d2..f9241da91476 100644 --- a/packages/file_selector/file_selector_macos/CHANGELOG.md +++ b/packages/file_selector/file_selector_macos/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.9.0+2 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. ## 0.9.0+1 diff --git a/packages/file_selector/file_selector_macos/example/lib/main.dart b/packages/file_selector/file_selector_macos/example/lib/main.dart index cbe268e1c7ab..3e447104ef9f 100644 --- a/packages/file_selector/file_selector_macos/example/lib/main.dart +++ b/packages/file_selector/file_selector_macos/example/lib/main.dart @@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:example/get_directory_page.dart'; -import 'package:example/home_page.dart'; -import 'package:example/open_image_page.dart'; -import 'package:example/open_multiple_images_page.dart'; -import 'package:example/open_text_page.dart'; -import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; +import 'get_directory_page.dart'; +import 'home_page.dart'; +import 'open_image_page.dart'; +import 'open_multiple_images_page.dart'; +import 'open_text_page.dart'; +import 'save_text_page.dart'; + void main() { runApp(const MyApp()); } diff --git a/packages/file_selector/file_selector_macos/pubspec.yaml b/packages/file_selector/file_selector_macos/pubspec.yaml index 34d418490a14..b921cc8e8070 100644 --- a/packages/file_selector/file_selector_macos/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_macos description: macOS implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.9.0+1 +version: 0.9.0+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md index e6949b09fa3b..c4ee86a4ce19 100644 --- a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md +++ b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.1.1 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. ## 2.1.0 diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart index c6d0f4a56155..d6aebd01730f 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/method_channel/method_channel_file_selector.dart @@ -2,10 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; +import '../../file_selector_platform_interface.dart'; + const MethodChannel _channel = MethodChannel('plugins.flutter.io/file_selector'); diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart index a23957af9110..eb4563c47917 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/platform_interface/file_selector_interface.dart @@ -4,9 +4,9 @@ import 'dart:async'; -import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import '../../file_selector_platform_interface.dart'; import '../method_channel/method_channel_file_selector.dart'; /// The interface that implementations of file_selector must implement. diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml index ae24a4b93b5d..b01af3501cc7 100644 --- a/packages/file_selector/file_selector_platform_interface/pubspec.yaml +++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%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.1.0 +version: 2.1.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md index 873a76e60a25..69ca20363866 100644 --- a/packages/file_selector/file_selector_web/CHANGELOG.md +++ b/packages/file_selector/file_selector_web/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.9.0+1 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/file_selector/file_selector_web/lib/file_selector_web.dart b/packages/file_selector/file_selector_web/lib/file_selector_web.dart index 915a2a806496..748bb3aa0df0 100644 --- a/packages/file_selector/file_selector_web/lib/file_selector_web.dart +++ b/packages/file_selector/file_selector_web/lib/file_selector_web.dart @@ -5,11 +5,12 @@ import 'dart:async'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; -import 'package:file_selector_web/src/dom_helper.dart'; -import 'package:file_selector_web/src/utils.dart'; import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import 'src/dom_helper.dart'; +import 'src/utils.dart'; + /// The web implementation of [FileSelectorPlatform]. /// /// This class implements the `package:file_selector` functionality for the web. diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index c1854aea4a88..38229588fd27 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/plugins/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.0 +version: 0.9.0+1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_windows/CHANGELOG.md b/packages/file_selector/file_selector_windows/CHANGELOG.md index e0e6152e9b2f..1221bbd4d508 100644 --- a/packages/file_selector/file_selector_windows/CHANGELOG.md +++ b/packages/file_selector/file_selector_windows/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.9.1+3 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. ## 0.9.1+2 diff --git a/packages/file_selector/file_selector_windows/example/lib/main.dart b/packages/file_selector/file_selector_windows/example/lib/main.dart index cbe268e1c7ab..3e447104ef9f 100644 --- a/packages/file_selector/file_selector_windows/example/lib/main.dart +++ b/packages/file_selector/file_selector_windows/example/lib/main.dart @@ -2,14 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:example/get_directory_page.dart'; -import 'package:example/home_page.dart'; -import 'package:example/open_image_page.dart'; -import 'package:example/open_multiple_images_page.dart'; -import 'package:example/open_text_page.dart'; -import 'package:example/save_text_page.dart'; import 'package:flutter/material.dart'; +import 'get_directory_page.dart'; +import 'home_page.dart'; +import 'open_image_page.dart'; +import 'open_multiple_images_page.dart'; +import 'open_text_page.dart'; +import 'save_text_page.dart'; + void main() { runApp(const MyApp()); } diff --git a/packages/file_selector/file_selector_windows/pubspec.yaml b/packages/file_selector/file_selector_windows/pubspec.yaml index 52b9f97174b4..7859822701e1 100644 --- a/packages/file_selector/file_selector_windows/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_windows description: Windows implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.9.1+2 +version: 0.9.1+3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 291ba6230a60..70298bc667b0 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.1 + +* Updates imports for `prefer_relative_imports`. + ## 2.2.0 * Deprecates `AndroidGoogleMapsFlutter.useAndroidViewSurface` in favor of diff --git a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart index b11f29977f23..60d4fdd95dcf 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart @@ -3,11 +3,11 @@ // found in the LICENSE file. import 'package:flutter/material.dart'; - import 'package:google_maps_flutter_android/google_maps_flutter_android.dart'; -import 'package:google_maps_flutter_example/lite_mode.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + import 'animate_camera.dart'; +import 'lite_mode.dart'; import 'map_click.dart'; import 'map_coordinates.dart'; import 'map_ui.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml index dd8c82d4d4e5..540f5d810966 100644 --- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter description: A Flutter plugin for integrating Google Maps in iOS and Android applications. repository: https://github.com/flutter/plugins/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.2.0 +version: 2.2.1 environment: sdk: ">=2.14.0 <3.0.0" 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 01c98f388e61..2ea420daec8a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.1 + +* Updates imports for `prefer_relative_imports`. + ## 2.3.0 * Switches the default for `useAndroidViewSurface` to true, and adds diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/main.dart index 6b96e6f0dff8..4adec524f87b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/lib/main.dart @@ -4,10 +4,10 @@ import 'package:flutter/material.dart'; import 'package:google_maps_flutter_android/google_maps_flutter_android.dart'; -import 'package:google_maps_flutter_example/lite_mode.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'animate_camera.dart'; +import 'lite_mode.dart'; import 'map_click.dart'; import 'map_coordinates.dart'; import 'map_ui.dart'; 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 c820f31d1c46..1ae73ca97cda 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/plugins/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.3.0 +version: 2.3.1 environment: sdk: ">=2.14.0 <3.0.0" 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 e3a033f3df07..7788e9146809 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.1.12 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. * Fixes violations of new analysis option use_named_constants. * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/main.dart index c02e4afe428d..de75162b09dd 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/lib/main.dart @@ -3,9 +3,9 @@ // found in the LICENSE file. import 'package:flutter/material.dart'; -import 'package:google_maps_flutter_example/lite_mode.dart'; import 'animate_camera.dart'; +import 'lite_mode.dart'; import 'map_click.dart'; import 'map_coordinates.dart'; import 'map_ui.dart'; 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 f1e613806eb9..7ca13a9273f2 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/plugins/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.1.11 +version: 2.1.12 environment: sdk: ">=2.14.0 <3.0.0" 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 5ef9142beea8..26e70d3b4c35 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,6 @@ -## NEXT +## 2.2.3 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. ## 2.2.2 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 8759126d4b67..5961406c155c 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 @@ -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:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import '../../google_maps_flutter_platform_interface.dart'; /// Generic Event coming from the native side of Maps. /// 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 a34ee48ac79a..e17510f90624 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 @@ -12,9 +12,9 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; -import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:stream_transform/stream_transform.dart'; +import '../../google_maps_flutter_platform_interface.dart'; import '../types/tile_overlay_updates.dart'; import '../types/utils/map_configuration_serialization.dart'; 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 d4621a70c249..147d64f715b7 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 @@ -12,10 +12,11 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; -import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; -import 'package:google_maps_flutter_platform_interface/src/types/utils/map_configuration_serialization.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import '../../google_maps_flutter_platform_interface.dart'; +import '../types/utils/map_configuration_serialization.dart'; + /// The interface that platform-specific implementations of `google_maps_flutter` must extend. /// /// Avoid `implements` of this interface. Using `implements` makes adding any new 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 70e332907939..1e07b97c300d 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 @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import '../../google_maps_flutter_platform_interface.dart'; + /// The interface that platform-specific implementations of /// `google_maps_flutter` can extend to support state inpsection in tests. /// 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 160819912a0a..853a23d5cdc3 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/plugins/tree/main/packages/google_maps_fl issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.2.2 +version: 2.2.3 environment: sdk: '>=2.12.0 <3.0.0' 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 541ac49f0a16..b1ddd412a3dc 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,6 @@ -## NEXT +## 0.4.0+3 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. ## 0.4.0+2 diff --git a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/types.dart b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/types.dart index 84c66264db7b..d4e87799f4b3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/lib/src/types.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/lib/src/types.dart @@ -3,7 +3,8 @@ // found in the LICENSE file. import 'package:google_maps/google_maps.dart' as gmaps; -import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; + +import '../../google_maps_flutter_web.dart'; /// A void function that handles a [gmaps.LatLng] as a parameter. /// 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 cb4063e315bc..572d9110be8e 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/plugins/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.4.0+2 +version: 0.4.0+3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index 99d3dbc11121..23bfe17ce9e1 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.1.9 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. * Fixes violations of new analysis option use_named_constants. 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 88d439c5487f..5e0f18aaebcc 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 @@ -7,9 +7,10 @@ import 'dart:html' as html; import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; -import 'package:image_picker_for_web/src/image_resizer.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'src/image_resizer.dart'; + const String _kImagePickerInputsDomId = '__image_picker_web-file-input'; const String _kAcceptImageMimeType = 'image/*'; const String _kAcceptVideoMimeType = 'video/3gpp,video/x-m4v,video/mp4,video/*'; 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 ba794acae3be..7cca935c6c91 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 @@ -7,9 +7,10 @@ import 'dart:html' as html; import 'dart:math'; import 'dart:ui'; -import 'package:image_picker_for_web/src/image_resizer_utils.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import 'image_resizer_utils.dart'; + /// Helper class that resizes images. class ImageResizer { /// Resizes the image if needed. diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index e19147b6306e..14d9197e821c 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_for_web description: Web platform implementation of image_picker repository: https://github.com/flutter/plugins/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: 2.1.8 +version: 2.1.9 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md index 8defffe95a7a..05b03a37cb98 100644 --- a/packages/image_picker/image_picker_platform_interface/CHANGELOG.md +++ b/packages/image_picker/image_picker_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.6.2 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart index 80d33807b70d..c2c39f93fe18 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/method_channel/method_channel_image_picker.dart @@ -7,7 +7,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; +import '../../image_picker_platform_interface.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/image_picker'); diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart index f704025f581b..9572742e62e0 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/platform_interface/image_picker_platform.dart @@ -5,10 +5,11 @@ import 'dart:async'; import 'package:cross_file/cross_file.dart'; -import 'package:image_picker_platform_interface/src/method_channel/method_channel_image_picker.dart'; -import 'package:image_picker_platform_interface/src/types/types.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import '../method_channel/method_channel_image_picker.dart'; +import '../types/types.dart'; + /// The interface that implementations of image_picker must implement. /// /// Platform implementations should extend this class rather than implement it as `image_picker` diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/image_picker_options.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/image_picker_options.dart index cdc89a920178..0d85c918f649 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/image_picker_options.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/image_picker_options.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:image_picker_platform_interface/src/types/types.dart'; +import 'types.dart'; /// Specifies options for picking a single image from the device's camera or gallery. class ImagePickerOptions { diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/lost_data_response.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/lost_data_response.dart index 65f5d7e15c90..10af812a3109 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/lost_data_response.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/lost_data_response.dart @@ -4,7 +4,8 @@ import 'package:cross_file/cross_file.dart'; import 'package:flutter/services.dart'; -import 'package:image_picker_platform_interface/src/types/types.dart'; + +import 'types.dart'; /// The response object of [ImagePicker.getLostData]. /// diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/multi_image_picker_options.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/multi_image_picker_options.dart index 4d7971c59a81..c860297ce33f 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/multi_image_picker_options.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/multi_image_picker_options.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:image_picker_platform_interface/src/types/image_options.dart'; +import 'image_options.dart'; /// Specifies options for picking multiple images from the device's gallery. class MultiImagePickerOptions { diff --git a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart index 64f6a1f27538..ddd36b62c023 100644 --- a/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart +++ b/packages/image_picker/image_picker_platform_interface/lib/src/types/picked_file/lost_data.dart @@ -3,7 +3,8 @@ // found in the LICENSE file. import 'package:flutter/services.dart'; -import 'package:image_picker_platform_interface/src/types/types.dart'; + +import '../types.dart'; /// The response object of [ImagePicker.retrieveLostData]. /// diff --git a/packages/image_picker/image_picker_platform_interface/pubspec.yaml b/packages/image_picker/image_picker_platform_interface/pubspec.yaml index f4b745f2c1bf..eb4d2b649eac 100644 --- a/packages/image_picker/image_picker_platform_interface/pubspec.yaml +++ b/packages/image_picker/image_picker_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/i 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.6.1 +version: 2.6.2 environment: sdk: ">=2.12.0 <3.0.0" 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 8733a1c4133c..6540fdfedafe 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.3+5 + +* Updates imports for `prefer_relative_imports`. + ## 0.2.3+4 * Updates minimum Flutter version to 2.10. diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart index 14dd69364497..d73ca8ef2e00 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/in_app_purchase_android_platform.dart @@ -6,10 +6,10 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; -import 'package:in_app_purchase_android/in_app_purchase_android.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'; /// [IAPError.code] code for failed purchases. const String kPurchaseErrorCode = 'purchase_error'; 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 db53ff4077d2..d5657d1a38d8 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 @@ -3,10 +3,10 @@ // found in the LICENSE file. import 'package:flutter/services.dart'; -import 'package:in_app_purchase_android/in_app_purchase_android.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'; /// Contains InApp Purchase features that are only available on PlayStore. class InAppPurchaseAndroidPlatformAddition diff --git a/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_product_details.dart b/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_product_details.dart index 15ed16c7e2ec..7affa242055b 100644 --- a/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_product_details.dart +++ b/packages/in_app_purchase/in_app_purchase_android/lib/src/types/google_play_product_details.dart @@ -2,9 +2,10 @@ // 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_platform_interface/in_app_purchase_platform_interface.dart'; +import '../../billing_client_wrappers.dart'; + /// The class represents the information of a product as registered in at /// Google Play store front. class GooglePlayProductDetails extends ProductDetails { 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 a0563f1434b8..8c57a982937b 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/plugins/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.2.3+4 +version: 0.2.3+5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md index f2a76ae55db2..17ba02986088 100644 --- a/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 1.3.2 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. * Removes unnecessary imports. diff --git a/packages/in_app_purchase/in_app_purchase_platform_interface/lib/src/in_app_purchase_platform_addition.dart b/packages/in_app_purchase/in_app_purchase_platform_interface/lib/src/in_app_purchase_platform_addition.dart index 746675549295..e93787e95d43 100644 --- a/packages/in_app_purchase/in_app_purchase_platform_interface/lib/src/in_app_purchase_platform_addition.dart +++ b/packages/in_app_purchase/in_app_purchase_platform_interface/lib/src/in_app_purchase_platform_addition.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:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; +import '../in_app_purchase_platform_interface.dart'; // ignore: avoid_classes_with_only_static_members /// The interface that platform implementations must implement when they want to diff --git a/packages/in_app_purchase/in_app_purchase_platform_interface/lib/src/in_app_purchase_platform_addition_provider.dart b/packages/in_app_purchase/in_app_purchase_platform_interface/lib/src/in_app_purchase_platform_addition_provider.dart index 642bbb419c6e..adeaa3e53397 100644 --- a/packages/in_app_purchase/in_app_purchase_platform_interface/lib/src/in_app_purchase_platform_addition_provider.dart +++ b/packages/in_app_purchase/in_app_purchase_platform_interface/lib/src/in_app_purchase_platform_addition_provider.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:in_app_purchase_platform_interface/src/in_app_purchase_platform_addition.dart'; +import 'in_app_purchase_platform_addition.dart'; /// The [InAppPurchasePlatformAdditionProvider] is responsible for providing /// a platform-specific [InAppPurchasePlatformAddition]. diff --git a/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml index e181c8a5ab49..46e38b0a03fa 100644 --- a/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchas issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%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: 1.3.1 +version: 1.3.2 environment: sdk: ">=2.12.0 <3.0.0" 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 2952c4b31abf..52f59efacd6a 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.2+2 + +* Updates imports for `prefer_relative_imports`. + ## 0.3.2+1 * Updates minimum Flutter version to 2.10. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart index aa03190b0454..09058ea2e89a 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/lib/main.dart @@ -7,9 +7,9 @@ import 'dart:async'; import 'package:flutter/material.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'; -import 'package:in_app_purchase_storekit_example/example_payment_queue_delegate.dart'; import 'consumable_store.dart'; +import 'example_payment_queue_delegate.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform_addition.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform_addition.dart index 87655df53d34..070c138b32e6 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform_addition.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform_addition.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. import 'package:in_app_purchase_platform_interface/in_app_purchase_platform_interface.dart'; -import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart'; +import '../in_app_purchase_storekit.dart'; import '../store_kit_wrappers.dart'; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_delegate_wrapper.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_delegate_wrapper.dart index eb88953096e6..1d98dd3f4250 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_delegate_wrapper.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_payment_queue_delegate_wrapper.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:in_app_purchase_storekit/store_kit_wrappers.dart'; +import '../../store_kit_wrappers.dart'; /// A wrapper around /// [`SKPaymentQueueDelegate`](https://developer.apple.com/documentation/storekit/skpaymentqueuedelegate?language=objc). 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 70db7da2e275..78e16e22416c 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 @@ -7,9 +7,9 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:in_app_purchase_storekit/store_kit_wrappers.dart'; import 'package:json_annotation/json_annotation.dart'; +import '../../store_kit_wrappers.dart'; import '../channel.dart'; import '../in_app_purchase_storekit_platform.dart'; 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 076837198c76..f2193e53b591 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 platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/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.2+1 +version: 0.3.2+2 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index 5a94fac16fe8..a26846d5ed01 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.13 + +* Updates imports for `prefer_relative_imports`. + ## 1.0.12 * Updates androidx.fragment version to 1.5.2. diff --git a/packages/local_auth/local_auth_android/lib/local_auth_android.dart b/packages/local_auth/local_auth_android/lib/local_auth_android.dart index dfe785cc176f..e2134173691e 100644 --- a/packages/local_auth/local_auth_android/lib/local_auth_android.dart +++ b/packages/local_auth/local_auth_android/lib/local_auth_android.dart @@ -3,9 +3,10 @@ // found in the LICENSE file. import 'package:flutter/services.dart'; -import 'package:local_auth_android/types/auth_messages_android.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'types/auth_messages_android.dart'; + export 'package:local_auth_android/types/auth_messages_android.dart'; export 'package:local_auth_platform_interface/types/auth_messages.dart'; export 'package:local_auth_platform_interface/types/auth_options.dart'; diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index d621874106b3..35c2d3af983c 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.12 +version: 1.0.13 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_ios/CHANGELOG.md b/packages/local_auth/local_auth_ios/CHANGELOG.md index 748db7a7e695..e67f2a4e2ef1 100644 --- a/packages/local_auth/local_auth_ios/CHANGELOG.md +++ b/packages/local_auth/local_auth_ios/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 1.0.10 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. ## 1.0.9 diff --git a/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart b/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart index d9df89a656a8..217fd39d9901 100644 --- a/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart +++ b/packages/local_auth/local_auth_ios/lib/local_auth_ios.dart @@ -3,9 +3,10 @@ // found in the LICENSE file. import 'package:flutter/services.dart'; -import 'package:local_auth_ios/types/auth_messages_ios.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'types/auth_messages_ios.dart'; + export 'package:local_auth_ios/types/auth_messages_ios.dart'; export 'package:local_auth_platform_interface/types/auth_messages.dart'; export 'package:local_auth_platform_interface/types/auth_options.dart'; diff --git a/packages/local_auth/local_auth_ios/pubspec.yaml b/packages/local_auth/local_auth_ios/pubspec.yaml index 18c39187b411..9cdeef963c34 100644 --- a/packages/local_auth/local_auth_ios/pubspec.yaml +++ b/packages/local_auth/local_auth_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_ios description: iOS implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.9 +version: 1.0.10 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md index ade26dd62e47..f0313ce99be6 100644 --- a/packages/local_auth/local_auth_platform_interface/CHANGELOG.md +++ b/packages/local_auth/local_auth_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 1.0.5 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. ## 1.0.4 diff --git a/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart b/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart index 9ded078c3a90..b3b0a653b514 100644 --- a/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart +++ b/packages/local_auth/local_auth_platform_interface/lib/default_method_channel_platform.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. import 'package:flutter/services.dart'; -import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; +import 'local_auth_platform_interface.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/local_auth'); diff --git a/packages/local_auth/local_auth_platform_interface/lib/local_auth_platform_interface.dart b/packages/local_auth/local_auth_platform_interface/lib/local_auth_platform_interface.dart index de652b20f462..4c6d58238edd 100644 --- a/packages/local_auth/local_auth_platform_interface/lib/local_auth_platform_interface.dart +++ b/packages/local_auth/local_auth_platform_interface/lib/local_auth_platform_interface.dart @@ -2,10 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:local_auth_platform_interface/default_method_channel_platform.dart'; -import 'package:local_auth_platform_interface/types/types.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'default_method_channel_platform.dart'; +import 'types/types.dart'; + export 'package:local_auth_platform_interface/types/types.dart'; /// The interface that implementations of local_auth must implement. diff --git a/packages/local_auth/local_auth_platform_interface/pubspec.yaml b/packages/local_auth/local_auth_platform_interface/pubspec.yaml index 27b5cb33c0f5..92da218d8be5 100644 --- a/packages/local_auth/local_auth_platform_interface/pubspec.yaml +++ b/packages/local_auth/local_auth_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/loc issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%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: 1.0.4 +version: 1.0.5 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/local_auth/local_auth_windows/CHANGELOG.md b/packages/local_auth/local_auth_windows/CHANGELOG.md index 4e36f75aea1d..b4f2061f2c27 100644 --- a/packages/local_auth/local_auth_windows/CHANGELOG.md +++ b/packages/local_auth/local_auth_windows/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 1.0.4 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. ## 1.0.3 diff --git a/packages/local_auth/local_auth_windows/lib/local_auth_windows.dart b/packages/local_auth/local_auth_windows/lib/local_auth_windows.dart index 1d65e81050f1..b373782c2187 100644 --- a/packages/local_auth/local_auth_windows/lib/local_auth_windows.dart +++ b/packages/local_auth/local_auth_windows/lib/local_auth_windows.dart @@ -4,7 +4,7 @@ import 'package:flutter/services.dart'; import 'package:local_auth_platform_interface/local_auth_platform_interface.dart'; -import 'package:local_auth_windows/types/auth_messages_windows.dart'; +import 'types/auth_messages_windows.dart'; export 'package:local_auth_platform_interface/types/auth_messages.dart'; export 'package:local_auth_platform_interface/types/auth_options.dart'; diff --git a/packages/local_auth/local_auth_windows/pubspec.yaml b/packages/local_auth/local_auth_windows/pubspec.yaml index 7f8d7f631211..9a2effed92ee 100644 --- a/packages/local_auth/local_auth_windows/pubspec.yaml +++ b/packages/local_auth/local_auth_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_windows description: Windows implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.3 +version: 1.0.4 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/path_provider/path_provider_platform_interface/CHANGELOG.md b/packages/path_provider/path_provider_platform_interface/CHANGELOG.md index 6baf950a397a..f12e1ec53ade 100644 --- a/packages/path_provider/path_provider_platform_interface/CHANGELOG.md +++ b/packages/path_provider/path_provider_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.0.5 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. ## 2.0.4 diff --git a/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart b/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart index fe632743b098..991be55bce8c 100644 --- a/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart +++ b/packages/path_provider/path_provider_platform_interface/lib/src/method_channel_path_provider.dart @@ -4,9 +4,10 @@ import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:flutter/services.dart'; -import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; import 'package:platform/platform.dart'; +import '../path_provider_platform_interface.dart'; + /// An implementation of [PathProviderPlatform] that uses method channels. class MethodChannelPathProvider extends PathProviderPlatform { /// The method channel used to interact with the native platform. diff --git a/packages/path_provider/path_provider_platform_interface/pubspec.yaml b/packages/path_provider/path_provider_platform_interface/pubspec.yaml index ef16037de71f..6ce7ec662b33 100644 --- a/packages/path_provider/path_provider_platform_interface/pubspec.yaml +++ b/packages/path_provider/path_provider_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/path_provider/ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+path_provider%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.0.4 +version: 2.0.5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md b/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md index 26c57ca3be27..950864f96653 100644 --- a/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 1.0.3 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. ## 1.0.2 diff --git a/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart b/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart index 560c199ee77a..0f936db870c7 100644 --- a/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart +++ b/packages/quick_actions/quick_actions_platform_interface/lib/method_channel/method_channel_quick_actions.dart @@ -4,9 +4,9 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'package:quick_actions_platform_interface/types/types.dart'; import '../platform_interface/quick_actions_platform.dart'; +import '../types/types.dart'; const MethodChannel _channel = MethodChannel('plugins.flutter.io/quick_actions'); diff --git a/packages/quick_actions/quick_actions_platform_interface/lib/platform_interface/quick_actions_platform.dart b/packages/quick_actions/quick_actions_platform_interface/lib/platform_interface/quick_actions_platform.dart index 7a70bba5c81d..057cb5642293 100644 --- a/packages/quick_actions/quick_actions_platform_interface/lib/platform_interface/quick_actions_platform.dart +++ b/packages/quick_actions/quick_actions_platform_interface/lib/platform_interface/quick_actions_platform.dart @@ -3,9 +3,9 @@ // found in the LICENSE file. import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:quick_actions_platform_interface/types/types.dart'; import '../method_channel/method_channel_quick_actions.dart'; +import '../types/types.dart'; /// The interface that implementations of quick_actions must implement. /// diff --git a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml index aa331dc54544..2990da603c14 100644 --- a/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml +++ b/packages/quick_actions/quick_actions_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%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: 1.0.2 +version: 1.0.3 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 0b590d5deaad..18a0289eb43f 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 6.1.6 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart b/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart index 9061b517e0d5..30321026ceb9 100644 --- a/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart +++ b/packages/url_launcher/url_launcher/lib/src/url_launcher_uri.dart @@ -4,9 +4,9 @@ import 'dart:async'; -import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; +import '../url_launcher_string.dart'; import 'type_conversion.dart'; /// Passes [url] to the underlying platform for handling. diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index ff21c6360e10..8efda4afb580 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for launching a URL. Supports web, phone, SMS, and email schemes. repository: https://github.com/flutter/plugins/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.1.5 +version: 6.1.6 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md b/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md index fcf362ceb7d9..d45ca36e3906 100644 --- a/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.1.1 +* Updates imports for `prefer_relative_imports`. * Updates minimum Flutter version to 2.10. ## 2.1.0 diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/src/url_launcher_platform.dart b/packages/url_launcher/url_launcher_platform_interface/lib/src/url_launcher_platform.dart index aa499db4ce6f..8928d4249e90 100644 --- a/packages/url_launcher/url_launcher_platform_interface/lib/src/url_launcher_platform.dart +++ b/packages/url_launcher/url_launcher_platform_interface/lib/src/url_launcher_platform.dart @@ -5,10 +5,10 @@ import 'dart:async'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:url_launcher_platform_interface/link.dart'; -import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; +import '../link.dart'; import '../method_channel_url_launcher.dart'; +import '../url_launcher_platform_interface.dart'; /// The interface that implementations of url_launcher must implement. /// diff --git a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml index 94ee1ecb29a4..4364e116c508 100644 --- a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml +++ b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/u issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%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.1.0 +version: 2.1.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index b37ada2225f0..4853aec23f9a 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.10.3 + +* Updates imports for `prefer_relative_imports`. + ## 2.10.2 * Adds a getter to expose the Java InstanceManager. diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_cookie_manager.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_cookie_manager.dart index bba75ff4f613..6e3f6f28d8ef 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_cookie_manager.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_cookie_manager.dart @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:webview_flutter_android/src/android_webview.dart' - as android_webview; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; +import 'src/android_webview.dart' as android_webview; + /// Handles all cookie operations for the current platform. class WebViewAndroidCookieManager extends WebViewCookieManagerPlatform { @override diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart index ff6265dbad00..140d0da7e7f7 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart @@ -6,10 +6,10 @@ import 'dart:async'; import 'dart:typed_data'; import 'package:flutter/widgets.dart'; -import 'package:webview_flutter_android/webview_android_cookie_manager.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'src/android_webview.dart' as android_webview; +import 'webview_android_cookie_manager.dart'; /// Creates a [Widget] with a [android_webview.WebView]. class WebViewAndroidWidget extends StatefulWidget { diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index f5f2e3c34e69..c2a326f7d4ac 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/plugins/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: 2.10.2 +version: 2.10.3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index 17b86523f29a..e8eb01e6590e 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.9.4 + +* Updates imports for `prefer_relative_imports`. + ## 1.9.3 * Updates minimum Flutter version to 2.10. diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_cookie_manager.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_cookie_manager.dart index 1f87e6a26ba6..90dfc2a548b5 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_cookie_manager.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_cookie_manager.dart @@ -3,7 +3,8 @@ // found in the LICENSE file. import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'package:webview_flutter_platform_interface/src/types/webview_cookie.dart'; + +import '../types/webview_cookie.dart'; /// Interface for a platform implementation of a cookie manager. /// diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform.dart index e35635d73a9e..8d1df6ae1040 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform.dart @@ -5,8 +5,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/widgets.dart'; -import 'package:webview_flutter_platform_interface/src/platform_interface/javascript_channel_registry.dart'; +import '../platform_interface/javascript_channel_registry.dart'; import '../types/types.dart'; import 'webview_platform_callbacks_handler.dart'; import 'webview_platform_controller.dart'; diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.dart index c1763cdae501..7c3edf3cf8b0 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.dart @@ -3,7 +3,8 @@ // found in the LICENSE file. import 'package:flutter/widgets.dart'; -import 'package:webview_flutter_platform_interface/src/types/types.dart'; + +import 'types.dart'; /// Configuration to use when creating a new [WebViewPlatformController]. /// diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml index 5b98154998a8..4b148725ff8f 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutte issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview_flutter%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.9.3 +version: 1.9.4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 056d1cb1c26a..318ffe65593a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.9.5 + +* Updates imports for `prefer_relative_imports`. + ## 2.9.4 * Fixes avoid_redundant_argument_values lint warnings and minor typos. 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 2059aa544207..9f121e66d5cb 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 @@ -8,9 +8,9 @@ import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'package:webview_flutter_wkwebview/src/common/weak_reference_utils.dart'; import '../common/instance_manager.dart'; +import '../common/weak_reference_utils.dart'; import 'foundation_api_impls.dart'; /// The values that can be returned in a change map. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart index b6e4cadec879..ae12a11820d8 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/ui_kit/ui_kit_api_impls.dart @@ -10,10 +10,9 @@ import 'dart:math'; import 'package:flutter/painting.dart' show Color; import 'package:flutter/services.dart'; -import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; - import '../common/instance_manager.dart'; import '../common/web_kit.pigeon.dart'; +import '../foundation/foundation.dart'; import '../web_kit/web_kit.dart'; import 'ui_kit.dart'; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webview_cupertino.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webview_cupertino.dart index f046ea4378b8..616f00c59fa3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webview_cupertino.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webview_cupertino.dart @@ -9,9 +9,9 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; -import 'package:webview_flutter_wkwebview/src/web_kit_webview_widget.dart'; import 'foundation/foundation.dart'; +import 'web_kit_webview_widget.dart'; /// Builds an iOS webview. /// diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/wkwebview_cookie_manager.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/wkwebview_cookie_manager.dart index 59c9f580db74..cdbf2620f968 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/wkwebview_cookie_manager.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/wkwebview_cookie_manager.dart @@ -3,8 +3,9 @@ // found in the LICENSE file. import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; -import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart'; -import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart'; + +import 'foundation/foundation.dart'; +import 'web_kit/web_kit.dart'; /// Handles all cookie operations for the WebView platform. class WKWebViewCookieManager extends WebViewCookieManagerPlatform { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 40b7bf89601d..3de385fec06c 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/plugins/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: 2.9.4 +version: 2.9.5 environment: sdk: ">=2.17.0 <3.0.0" diff --git a/script/tool/lib/src/common/cmake.dart b/script/tool/lib/src/common/cmake.dart index 04ad880292b9..3f5d8452bd44 100644 --- a/script/tool/lib/src/common/cmake.dart +++ b/script/tool/lib/src/common/cmake.dart @@ -3,9 +3,9 @@ // found in the LICENSE file. import 'package:file/file.dart'; -import 'package:flutter_plugin_tools/src/common/core.dart'; import 'package:platform/platform.dart'; +import 'core.dart'; import 'process_runner.dart'; const String _cacheCommandKey = 'CMAKE_COMMAND:INTERNAL'; diff --git a/script/tool/lib/src/common/package_state_utils.dart b/script/tool/lib/src/common/package_state_utils.dart index 870956a24b10..a0c82400e1db 100644 --- a/script/tool/lib/src/common/package_state_utils.dart +++ b/script/tool/lib/src/common/package_state_utils.dart @@ -3,10 +3,10 @@ // found in the LICENSE file. import 'package:file/file.dart'; -import 'package:flutter_plugin_tools/src/common/git_version_finder.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; +import 'git_version_finder.dart'; import 'repository_package.dart'; /// The state of a package on disk relative to git state. diff --git a/script/tool/lib/src/common/plugin_utils.dart b/script/tool/lib/src/common/plugin_utils.dart index f33d3d73bb75..94677fe7e5a3 100644 --- a/script/tool/lib/src/common/plugin_utils.dart +++ b/script/tool/lib/src/common/plugin_utils.dart @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter_plugin_tools/src/common/repository_package.dart'; import 'package:yaml/yaml.dart'; import 'core.dart'; +import 'repository_package.dart'; /// Possible plugin support options for a platform. enum PlatformSupport { diff --git a/script/tool/lib/src/federation_safety_check_command.dart b/script/tool/lib/src/federation_safety_check_command.dart index 383637a9e896..93a832eb0e29 100644 --- a/script/tool/lib/src/federation_safety_check_command.dart +++ b/script/tool/lib/src/federation_safety_check_command.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'package:file/file.dart'; -import 'package:flutter_plugin_tools/src/common/plugin_utils.dart'; import 'package:git/git.dart'; import 'package:path/path.dart' as p; import 'package:platform/platform.dart'; @@ -13,6 +12,7 @@ import 'common/core.dart'; import 'common/file_utils.dart'; import 'common/git_version_finder.dart'; import 'common/package_looping_command.dart'; +import 'common/plugin_utils.dart'; import 'common/process_runner.dart'; import 'common/repository_package.dart'; diff --git a/script/tool/lib/src/lint_android_command.dart b/script/tool/lib/src/lint_android_command.dart index 607674c80d38..eb78ce891685 100644 --- a/script/tool/lib/src/lint_android_command.dart +++ b/script/tool/lib/src/lint_android_command.dart @@ -3,12 +3,12 @@ // found in the LICENSE file. import 'package:file/file.dart'; -import 'package:flutter_plugin_tools/src/common/plugin_utils.dart'; import 'package:platform/platform.dart'; import 'common/core.dart'; import 'common/gradle.dart'; import 'common/package_looping_command.dart'; +import 'common/plugin_utils.dart'; import 'common/process_runner.dart'; import 'common/repository_package.dart'; diff --git a/script/tool/lib/src/main.dart b/script/tool/lib/src/main.dart index 0f73c71e8437..078976d97376 100644 --- a/script/tool/lib/src/main.dart +++ b/script/tool/lib/src/main.dart @@ -7,13 +7,13 @@ import 'dart:io' as io; import 'package:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:file/local.dart'; -import 'package:flutter_plugin_tools/src/dependabot_check_command.dart'; import 'analyze_command.dart'; import 'build_examples_command.dart'; import 'common/core.dart'; import 'create_all_plugins_app_command.dart'; import 'custom_test_command.dart'; +import 'dependabot_check_command.dart'; import 'drive_examples_command.dart'; import 'federation_safety_check_command.dart'; import 'firebase_test_lab_command.dart'; diff --git a/script/tool/lib/src/make_deps_path_based_command.dart b/script/tool/lib/src/make_deps_path_based_command.dart index a09511ad7f42..10abcd44ae6e 100644 --- a/script/tool/lib/src/make_deps_path_based_command.dart +++ b/script/tool/lib/src/make_deps_path_based_command.dart @@ -158,10 +158,12 @@ class MakeDepsPathBasedCommand extends PackageCommand { ...pubspec.dependencies.keys, ...pubspec.devDependencies.keys, ]; - final Iterable packagesToOverride = combinedDependencies + final List packagesToOverride = combinedDependencies .where( (String packageName) => localDependencies.containsKey(packageName)) .toList(); + // Sort the combined list to avoid sort_pub_dependencies lint violations. + packagesToOverride.sort(); if (packagesToOverride.isNotEmpty) { final String commonBasePath = packagesDir.path; // Find the relative path to the common base. diff --git a/script/tool/lib/src/update_excerpts_command.dart b/script/tool/lib/src/update_excerpts_command.dart index 320a3c596323..5a59104d4e7f 100644 --- a/script/tool/lib/src/update_excerpts_command.dart +++ b/script/tool/lib/src/update_excerpts_command.dart @@ -5,12 +5,12 @@ import 'dart:io' as io; import 'package:file/file.dart'; -import 'package:flutter_plugin_tools/src/common/core.dart'; import 'package:git/git.dart'; import 'package:platform/platform.dart'; import 'package:yaml/yaml.dart'; import 'package:yaml_edit/yaml_edit.dart'; +import 'common/core.dart'; import 'common/package_looping_command.dart'; import 'common/process_runner.dart'; import 'common/repository_package.dart'; diff --git a/script/tool/lib/src/update_release_info_command.dart b/script/tool/lib/src/update_release_info_command.dart index 465b475eb9b5..67aa994d963c 100644 --- a/script/tool/lib/src/update_release_info_command.dart +++ b/script/tool/lib/src/update_release_info_command.dart @@ -4,11 +4,11 @@ import 'package:args/command_runner.dart'; import 'package:file/file.dart'; -import 'package:flutter_plugin_tools/src/common/core.dart'; import 'package:git/git.dart'; import 'package:pub_semver/pub_semver.dart'; import 'package:yaml_edit/yaml_edit.dart'; +import 'common/core.dart'; import 'common/git_version_finder.dart'; import 'common/package_looping_command.dart'; import 'common/package_state_utils.dart'; diff --git a/script/tool/test/make_deps_path_based_command_test.dart b/script/tool/test/make_deps_path_based_command_test.dart index 7e52dd6bbbc4..36753e8001f8 100644 --- a/script/tool/test/make_deps_path_based_command_test.dart +++ b/script/tool/test/make_deps_path_based_command_test.dart @@ -184,6 +184,42 @@ ${devDependencies.map((String dep) => ' $dep: ^1.0.0').join('\n')} ])); }); + test( + 'alphabetizes overrides from different sectinos to avoid lint warnings in analysis', + () async { + createFakePackage('a', packagesDir); + createFakePackage('b', packagesDir); + createFakePackage('c', packagesDir); + final RepositoryPackage targetPackage = + createFakePackage('target', packagesDir); + + _addDependencies(targetPackage, ['a', 'c']); + _addDevDependenciesSection(targetPackage, ['b']); + + final List output = await runCapturingPrint(runner, + ['make-deps-path-based', '--target-dependencies=c,a,b']); + + expect( + output, + containsAllInOrder([ + 'Rewriting references to: c, a, b...', + ' Modified packages/target/pubspec.yaml', + ])); + + expect( + targetPackage.pubspecFile.readAsLinesSync(), + containsAllInOrder([ + '# FOR TESTING ONLY. DO NOT MERGE.', + 'dependency_overrides:', + ' a:', + ' path: ../a', + ' b:', + ' path: ../b', + ' c:', + ' path: ../c', + ])); + }); + // This test case ensures that running CI using this command on an interim // PR that itself used this command won't fail on the rewrite step. test('running a second time no-ops without failing', () async { From 7aa678eaf4b7e79d9eb46dd17ae284a636a1ef35 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 28 Sep 2022 11:54:24 -0400 Subject: [PATCH 766/844] Roll Flutter from f57c2fe8ff49 to 9bf849184d6c (29 revisions) (#6510) --- .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 aa1942ca8ea6..e9a6ba9a1691 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f57c2fe8ff4993fbe017d1f5fb7c7a4145d66492 +9bf849184d6ca75b8d9fa5ac8cdaa10e9bc6521e From 9428081da056daba2ff07e18977768bdbce37187 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 28 Sep 2022 14:22:46 -0400 Subject: [PATCH 767/844] Enable `no_leading_underscores_for_local_identifiers` (#6509) --- analysis_options.yaml | 2 +- packages/camera/camera/CHANGELOG.md | 4 + .../example/integration_test/camera_test.dart | 40 ++-- .../camera/lib/src/camera_controller.dart | 14 +- packages/camera/camera/pubspec.yaml | 2 +- packages/camera/camera_android/CHANGELOG.md | 4 + .../example/integration_test/camera_test.dart | 8 +- .../example/lib/camera_controller.dart | 14 +- .../lib/src/android_camera.dart | 8 +- packages/camera/camera_android/pubspec.yaml | 2 +- .../camera/camera_avfoundation/CHANGELOG.md | 3 +- .../example/integration_test/camera_test.dart | 32 ++-- .../example/lib/camera_controller.dart | 14 +- .../lib/src/avfoundation_camera.dart | 8 +- .../camera/camera_avfoundation/pubspec.yaml | 2 +- .../camera_platform_interface/CHANGELOG.md | 4 + .../method_channel/method_channel_camera.dart | 8 +- .../camera_platform_interface/pubspec.yaml | 2 +- packages/camera/camera_windows/CHANGELOG.md | 3 +- .../camera_windows/example/lib/main.dart | 8 +- packages/camera/camera_windows/pubspec.yaml | 2 +- .../google_maps_flutter/CHANGELOG.md | 4 + .../test/polygon_updates_test.dart | 4 +- .../test/polyline_updates_test.dart | 4 +- .../CHANGELOG.md | 4 + .../lib/src/types/maps_object_updates.dart | 6 +- .../pubspec.yaml | 2 +- .../google_maps_flutter_web/CHANGELOG.md | 4 + .../google_maps_controller_test.dart | 28 +-- .../google_maps_plugin_test.dart | 28 +-- .../example/integration_test/marker_test.dart | 16 +- .../example/integration_test/shape_test.dart | 10 +- .../google_sign_in_web/CHANGELOG.md | 1 + .../auth2_legacy_init_test.dart | 10 +- .../example/integration_test/auth2_test.dart | 10 +- .../image_picker_for_web/CHANGELOG.md | 4 + .../lib/image_picker_for_web.dart | 24 +-- .../image_picker_for_web/pubspec.yaml | 2 +- .../path_provider/path_provider/CHANGELOG.md | 1 + .../integration_test/path_provider_test.dart | 4 +- .../path_provider_android/CHANGELOG.md | 1 + .../integration_test/path_provider_test.dart | 4 +- .../shared_preferences/CHANGELOG.md | 1 + .../test/shared_preferences_test.dart | 12 +- .../shared_preferences_android/CHANGELOG.md | 4 + .../shared_preferences_test.dart | 40 ++-- .../shared_preferences_ios/CHANGELOG.md | 1 + .../shared_preferences_test.dart | 34 ++-- .../shared_preferences_linux/CHANGELOG.md | 1 + .../test/shared_preferences_linux_test.dart | 34 ++-- .../shared_preferences_macos/CHANGELOG.md | 1 + .../shared_preferences_test.dart | 34 ++-- .../shared_preferences_windows/CHANGELOG.md | 1 + .../test/shared_preferences_windows_test.dart | 34 ++-- .../video_player/video_player/CHANGELOG.md | 4 + .../integration_test/video_player_test.dart | 172 +++++++++--------- .../video_player/test/video_player_test.dart | 8 +- .../video_player_android/CHANGELOG.md | 1 + .../integration_test/video_player_test.dart | 62 +++---- .../video_player_avfoundation/CHANGELOG.md | 4 + .../integration_test/video_player_test.dart | 64 +++---- .../webview_flutter/CHANGELOG.md | 1 + .../webview_flutter_test.dart | 14 +- .../webview_flutter_android/CHANGELOG.md | 4 + .../webview_flutter_test.dart | 14 +- .../CHANGELOG.md | 4 + .../webview_method_channel.dart | 21 +-- .../pubspec.yaml | 2 +- .../javascript_channel_registry_test.dart | 30 +-- .../src/types/javascript_channel_test.dart | 16 +- .../webview_flutter_wkwebview/CHANGELOG.md | 4 + .../webview_flutter_test.dart | 14 +- .../src/common/package_looping_command.dart | 4 +- script/tool/lib/src/native_test_command.dart | 4 +- .../test/dependabot_check_command_test.dart | 10 +- .../test/firebase_test_lab_command_test.dart | 32 ++-- script/tool/test/format_command_test.dart | 31 ++-- .../tool/test/license_check_command_test.dart | 38 ++-- .../make_deps_path_based_command_test.dart | 24 +-- .../tool/test/native_test_command_test.dart | 116 ++++++------ script/tool/test/publish_command_test.dart | 8 +- .../test/remove_dev_dependencies_test.dart | 6 +- .../tool/test/version_check_command_test.dart | 27 ++- 83 files changed, 675 insertions(+), 616 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index a8cff3ca7d01..98352af415de 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -143,7 +143,7 @@ linter: # - no_default_cases # LOCAL CHANGE - Needs to be enabled and violations fixed. - no_duplicate_case_values - no_leading_underscores_for_library_prefixes - # - no_leading_underscores_for_local_identifiers # LOCAL CHANGE - Needs to be enabled and violations fixed. + - no_leading_underscores_for_local_identifiers - no_logic_in_create_state # - no_runtimeType_toString # ok in tests; we enable this only in packages/ - non_constant_identifier_names diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 6f4a9f491542..30bb098f8fd4 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.0+3 + +* Updates code for `no_leading_underscores_for_local_identifiers` lint. + ## 0.10.0+2 * Updates imports for `prefer_relative_imports`. diff --git a/packages/camera/camera/example/integration_test/camera_test.dart b/packages/camera/camera/example/integration_test/camera_test.dart index 557f4858acab..b233d4958c8a 100644 --- a/packages/camera/camera/example/integration_test/camera_test.dart +++ b/packages/camera/camera/example/integration_test/camera_test.dart @@ -221,16 +221,16 @@ void main() { ); await controller.initialize(); - bool _isDetecting = false; + bool isDetecting = false; await controller.startImageStream((CameraImage image) { - if (_isDetecting) { + if (isDetecting) { return; } - _isDetecting = true; + isDetecting = true; - expectLater(image, isNotNull).whenComplete(() => _isDetecting = false); + expectLater(image, isNotNull).whenComplete(() => isDetecting = false); }); expect(controller.value.isStreamingImages, true); @@ -254,19 +254,19 @@ void main() { ); await controller.initialize(); - final Completer _completer = Completer(); + final Completer completer = Completer(); await controller.startImageStream((CameraImage image) { - if (!_completer.isCompleted) { + if (!completer.isCompleted) { Future(() async { await controller.stopImageStream(); await controller.dispose(); }).then((Object? value) { - _completer.complete(image); + completer.complete(image); }); } }); - return _completer.future; + return completer.future; } testWidgets( @@ -277,20 +277,20 @@ void main() { return; } - CameraImage _image = await startStreaming(cameras, null); - expect(_image, isNotNull); - expect(_image.format.group, ImageFormatGroup.bgra8888); - expect(_image.planes.length, 1); + CameraImage image = await startStreaming(cameras, null); + expect(image, isNotNull); + expect(image.format.group, ImageFormatGroup.bgra8888); + expect(image.planes.length, 1); - _image = await startStreaming(cameras, ImageFormatGroup.yuv420); - expect(_image, isNotNull); - expect(_image.format.group, ImageFormatGroup.yuv420); - expect(_image.planes.length, 2); + image = await startStreaming(cameras, ImageFormatGroup.yuv420); + expect(image, isNotNull); + expect(image.format.group, ImageFormatGroup.yuv420); + expect(image.planes.length, 2); - _image = await startStreaming(cameras, ImageFormatGroup.bgra8888); - expect(_image, isNotNull); - expect(_image.format.group, ImageFormatGroup.bgra8888); - expect(_image.planes.length, 1); + image = await startStreaming(cameras, ImageFormatGroup.bgra8888); + expect(image, isNotNull); + expect(image.format.group, ImageFormatGroup.bgra8888); + expect(image.planes.length, 1); }, skip: !Platform.isIOS, ); diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index 13bb63700ad3..ed1c951925d8 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -282,7 +282,7 @@ class CameraController extends ValueNotifier { ); } try { - final Completer _initializeCompleter = + final Completer initializeCompleter = Completer(); _deviceOrientationSubscription = CameraPlatform.instance @@ -303,7 +303,7 @@ class CameraController extends ValueNotifier { .onCameraInitialized(_cameraId) .first .then((CameraInitializedEvent event) { - _initializeCompleter.complete(event); + initializeCompleter.complete(event); })); await CameraPlatform.instance.initializeCamera( @@ -313,18 +313,18 @@ class CameraController extends ValueNotifier { value = value.copyWith( isInitialized: true, - previewSize: await _initializeCompleter.future + previewSize: await initializeCompleter.future .then((CameraInitializedEvent event) => Size( event.previewWidth, event.previewHeight, )), - exposureMode: await _initializeCompleter.future + exposureMode: await initializeCompleter.future .then((CameraInitializedEvent event) => event.exposureMode), - focusMode: await _initializeCompleter.future + focusMode: await initializeCompleter.future .then((CameraInitializedEvent event) => event.focusMode), - exposurePointSupported: await _initializeCompleter.future.then( + exposurePointSupported: await initializeCompleter.future.then( (CameraInitializedEvent event) => event.exposurePointSupported), - focusPointSupported: await _initializeCompleter.future + focusPointSupported: await initializeCompleter.future .then((CameraInitializedEvent event) => event.focusPointSupported), ); } on PlatformException catch (e) { diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 2770e977bcc9..1190989a63b7 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.10.0+2 +version: 0.10.0+3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md index f2417fb20645..af871474f68b 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.0+3 + +* Updates code for `no_leading_underscores_for_local_identifiers` lint. + ## 0.10.0+2 * Removes call to `join` on the camera's background `HandlerThread`. diff --git a/packages/camera/camera_android/example/integration_test/camera_test.dart b/packages/camera/camera_android/example/integration_test/camera_test.dart index 99029fcac605..3b1aae6aec51 100644 --- a/packages/camera/camera_android/example/integration_test/camera_test.dart +++ b/packages/camera/camera_android/example/integration_test/camera_test.dart @@ -225,16 +225,16 @@ void main() { ); await controller.initialize(); - bool _isDetecting = false; + bool isDetecting = false; await controller.startImageStream((CameraImageData image) { - if (_isDetecting) { + if (isDetecting) { return; } - _isDetecting = true; + isDetecting = true; - expectLater(image, isNotNull).whenComplete(() => _isDetecting = false); + expectLater(image, isNotNull).whenComplete(() => isDetecting = false); }); expect(controller.value.isStreamingImages, true); diff --git a/packages/camera/camera_android/example/lib/camera_controller.dart b/packages/camera/camera_android/example/lib/camera_controller.dart index 5a7a79c8d96c..09441cc5449c 100644 --- a/packages/camera/camera_android/example/lib/camera_controller.dart +++ b/packages/camera/camera_android/example/lib/camera_controller.dart @@ -203,7 +203,7 @@ class CameraController extends ValueNotifier { /// Initializes the camera on the device. Future initialize() async { - final Completer _initializeCompleter = + final Completer initializeCompleter = Completer(); _deviceOrientationSubscription = CameraPlatform.instance @@ -224,7 +224,7 @@ class CameraController extends ValueNotifier { .onCameraInitialized(_cameraId) .first .then((CameraInitializedEvent event) { - _initializeCompleter.complete(event); + initializeCompleter.complete(event); }); await CameraPlatform.instance.initializeCamera( @@ -234,18 +234,18 @@ class CameraController extends ValueNotifier { value = value.copyWith( isInitialized: true, - previewSize: await _initializeCompleter.future + previewSize: await initializeCompleter.future .then((CameraInitializedEvent event) => Size( event.previewWidth, event.previewHeight, )), - exposureMode: await _initializeCompleter.future + exposureMode: await initializeCompleter.future .then((CameraInitializedEvent event) => event.exposureMode), - focusMode: await _initializeCompleter.future + focusMode: await initializeCompleter.future .then((CameraInitializedEvent event) => event.focusMode), - exposurePointSupported: await _initializeCompleter.future + exposurePointSupported: await initializeCompleter.future .then((CameraInitializedEvent event) => event.exposurePointSupported), - focusPointSupported: await _initializeCompleter.future + focusPointSupported: await initializeCompleter.future .then((CameraInitializedEvent event) => event.focusPointSupported), ); diff --git a/packages/camera/camera_android/lib/src/android_camera.dart b/packages/camera/camera_android/lib/src/android_camera.dart index 3e9e52af04a7..36077eac8eed 100644 --- a/packages/camera/camera_android/lib/src/android_camera.dart +++ b/packages/camera/camera_android/lib/src/android_camera.dart @@ -126,10 +126,10 @@ class AndroidCamera extends CameraPlatform { return channel; }); - final Completer _completer = Completer(); + final Completer completer = Completer(); onCameraInitialized(cameraId).first.then((CameraInitializedEvent value) { - _completer.complete(); + completer.complete(); }); _channel.invokeMapMethod( @@ -147,14 +147,14 @@ class AndroidCamera extends CameraPlatform { if (error is! PlatformException) { throw error; } - _completer.completeError( + completer.completeError( CameraException(error.code, error.message), stackTrace, ); }, ); - return _completer.future; + return completer.future; } @override diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml index 9f5a2f2b9c16..a464759c3c46 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android description: Android implementation of the camera plugin. repository: https://github.com/flutter/plugins/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.0+2 +version: 0.10.0+3 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md index ab33c35dd8da..12d9a53ea248 100644 --- a/packages/camera/camera_avfoundation/CHANGELOG.md +++ b/packages/camera/camera_avfoundation/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.9.8+6 +* Updates code for `no_leading_underscores_for_local_identifiers` lint. * Updates minimum Flutter version to 2.10. ## 0.9.8+5 diff --git a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart index 51eab634c84b..3e62edc2c495 100644 --- a/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart +++ b/packages/camera/camera_avfoundation/example/integration_test/camera_test.dart @@ -213,19 +213,19 @@ void main() { ); await controller.initialize(); - final Completer _completer = Completer(); + final Completer completer = Completer(); await controller.startImageStream((CameraImageData image) { - if (!_completer.isCompleted) { + if (!completer.isCompleted) { Future(() async { await controller.stopImageStream(); await controller.dispose(); }).then((Object? value) { - _completer.complete(image); + completer.complete(image); }); } }); - return _completer.future; + return completer.future; } testWidgets( @@ -237,20 +237,20 @@ void main() { return; } - CameraImageData _image = await startStreaming(cameras, null); - expect(_image, isNotNull); - expect(_image.format.group, ImageFormatGroup.bgra8888); - expect(_image.planes.length, 1); + CameraImageData image = await startStreaming(cameras, null); + expect(image, isNotNull); + expect(image.format.group, ImageFormatGroup.bgra8888); + expect(image.planes.length, 1); - _image = await startStreaming(cameras, ImageFormatGroup.yuv420); - expect(_image, isNotNull); - expect(_image.format.group, ImageFormatGroup.yuv420); - expect(_image.planes.length, 2); + image = await startStreaming(cameras, ImageFormatGroup.yuv420); + expect(image, isNotNull); + expect(image.format.group, ImageFormatGroup.yuv420); + expect(image.planes.length, 2); - _image = await startStreaming(cameras, ImageFormatGroup.bgra8888); - expect(_image, isNotNull); - expect(_image.format.group, ImageFormatGroup.bgra8888); - expect(_image.planes.length, 1); + image = await startStreaming(cameras, ImageFormatGroup.bgra8888); + expect(image, isNotNull); + expect(image.format.group, ImageFormatGroup.bgra8888); + expect(image.planes.length, 1); }, ); } diff --git a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart index 5a7a79c8d96c..09441cc5449c 100644 --- a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart +++ b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart @@ -203,7 +203,7 @@ class CameraController extends ValueNotifier { /// Initializes the camera on the device. Future initialize() async { - final Completer _initializeCompleter = + final Completer initializeCompleter = Completer(); _deviceOrientationSubscription = CameraPlatform.instance @@ -224,7 +224,7 @@ class CameraController extends ValueNotifier { .onCameraInitialized(_cameraId) .first .then((CameraInitializedEvent event) { - _initializeCompleter.complete(event); + initializeCompleter.complete(event); }); await CameraPlatform.instance.initializeCamera( @@ -234,18 +234,18 @@ class CameraController extends ValueNotifier { value = value.copyWith( isInitialized: true, - previewSize: await _initializeCompleter.future + previewSize: await initializeCompleter.future .then((CameraInitializedEvent event) => Size( event.previewWidth, event.previewHeight, )), - exposureMode: await _initializeCompleter.future + exposureMode: await initializeCompleter.future .then((CameraInitializedEvent event) => event.exposureMode), - focusMode: await _initializeCompleter.future + focusMode: await initializeCompleter.future .then((CameraInitializedEvent event) => event.focusMode), - exposurePointSupported: await _initializeCompleter.future + exposurePointSupported: await initializeCompleter.future .then((CameraInitializedEvent event) => event.exposurePointSupported), - focusPointSupported: await _initializeCompleter.future + focusPointSupported: await initializeCompleter.future .then((CameraInitializedEvent event) => event.focusPointSupported), ); diff --git a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart index 19054fe5c561..9bdadfb4536f 100644 --- a/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart +++ b/packages/camera/camera_avfoundation/lib/src/avfoundation_camera.dart @@ -126,10 +126,10 @@ class AVFoundationCamera extends CameraPlatform { return channel; }); - final Completer _completer = Completer(); + final Completer completer = Completer(); onCameraInitialized(cameraId).first.then((CameraInitializedEvent value) { - _completer.complete(); + completer.complete(); }); _channel.invokeMapMethod( @@ -147,14 +147,14 @@ class AVFoundationCamera extends CameraPlatform { if (error is! PlatformException) { throw error; } - _completer.completeError( + completer.completeError( CameraException(error.code, error.message), stackTrace, ); }, ); - return _completer.future; + return completer.future; } @override diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml index 4b5b67dd8943..f394d59e81d5 100644 --- a/packages/camera/camera_avfoundation/pubspec.yaml +++ b/packages/camera/camera_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_avfoundation description: iOS implementation of the camera plugin. repository: https://github.com/flutter/plugins/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.8+5 +version: 0.9.8+6 environment: sdk: ">=2.14.0 <3.0.0" diff --git a/packages/camera/camera_platform_interface/CHANGELOG.md b/packages/camera/camera_platform_interface/CHANGELOG.md index bdfbd44a229a..d410304970cf 100644 --- a/packages/camera/camera_platform_interface/CHANGELOG.md +++ b/packages/camera/camera_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.2 + +* Updates code for `no_leading_underscores_for_local_identifiers` lint. + ## 2.2.1 * Updates imports for `prefer_relative_imports`. diff --git a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart index 68e1315f8bbe..37c00d64ede2 100644 --- a/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart +++ b/packages/camera/camera_platform_interface/lib/src/method_channel/method_channel_camera.dart @@ -118,10 +118,10 @@ class MethodChannelCamera extends CameraPlatform { return channel; }); - final Completer _completer = Completer(); + final Completer completer = Completer(); onCameraInitialized(cameraId).first.then((CameraInitializedEvent value) { - _completer.complete(); + completer.complete(); }); _channel.invokeMapMethod( @@ -139,14 +139,14 @@ class MethodChannelCamera extends CameraPlatform { if (error is! PlatformException) { throw error; } - _completer.completeError( + completer.completeError( CameraException(error.code, error.message), stackTrace, ); }, ); - return _completer.future; + return completer.future; } @override diff --git a/packages/camera/camera_platform_interface/pubspec.yaml b/packages/camera/camera_platform_interface/pubspec.yaml index 9cbd51b1bd2d..e87679261c5a 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/plugins/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.2.1 +version: 2.2.2 environment: sdk: '>=2.12.0 <3.0.0' diff --git a/packages/camera/camera_windows/CHANGELOG.md b/packages/camera/camera_windows/CHANGELOG.md index 10b08874092a..71c5d56524a6 100644 --- a/packages/camera/camera_windows/CHANGELOG.md +++ b/packages/camera/camera_windows/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.2.1+2 +* Updates code for `no_leading_underscores_for_local_identifiers` lint. * Updates minimum Flutter version to 2.10. ## 0.2.1+1 diff --git a/packages/camera/camera_windows/example/lib/main.dart b/packages/camera/camera_windows/example/lib/main.dart index 310630493111..d27edb860975 100644 --- a/packages/camera/camera_windows/example/lib/main.dart +++ b/packages/camera/camera_windows/example/lib/main.dart @@ -187,8 +187,8 @@ class _MyAppState extends State { } Future _takePicture() async { - final XFile _file = await CameraPlatform.instance.takePicture(_cameraId); - _showInSnackBar('Picture captured to: ${_file.path}'); + final XFile file = await CameraPlatform.instance.takePicture(_cameraId); + _showInSnackBar('Picture captured to: ${file.path}'); } Future _recordTimed(int seconds) async { @@ -228,10 +228,10 @@ class _MyAppState extends State { if (!_recording) { await CameraPlatform.instance.startVideoRecording(_cameraId); } else { - final XFile _file = + final XFile file = await CameraPlatform.instance.stopVideoRecording(_cameraId); - _showInSnackBar('Video captured to: ${_file.path}'); + _showInSnackBar('Video captured to: ${file.path}'); } if (mounted) { diff --git a/packages/camera/camera_windows/pubspec.yaml b/packages/camera/camera_windows/pubspec.yaml index 403cb459a6f7..1eab9fa108ef 100644 --- a/packages/camera/camera_windows/pubspec.yaml +++ b/packages/camera/camera_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_windows description: A Flutter plugin for getting information about and controlling the camera on Windows. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.2.1+1 +version: 0.2.1+2 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md index 70298bc667b0..3707aa86e95a 100644 --- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates code for `no_leading_underscores_for_local_identifiers` lint. + ## 2.2.1 * Updates imports for `prefer_relative_imports`. diff --git a/packages/google_maps_flutter/google_maps_flutter/test/polygon_updates_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/polygon_updates_test.dart index cb7263c02e05..34959832b36b 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/polygon_updates_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/polygon_updates_test.dart @@ -123,10 +123,10 @@ void main() { }); testWidgets('Mutate a polygon', (WidgetTester tester) async { - final List _points = [const LatLng(0.0, 0.0)]; + final List points = [const LatLng(0.0, 0.0)]; final Polygon p1 = Polygon( polygonId: const PolygonId('polygon_1'), - points: _points, + points: points, ); await tester.pumpWidget(_mapWithPolygons({p1})); diff --git a/packages/google_maps_flutter/google_maps_flutter/test/polyline_updates_test.dart b/packages/google_maps_flutter/google_maps_flutter/test/polyline_updates_test.dart index a2dad92f8eb4..f9d091695383 100644 --- a/packages/google_maps_flutter/google_maps_flutter/test/polyline_updates_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter/test/polyline_updates_test.dart @@ -117,10 +117,10 @@ void main() { }); testWidgets('Mutate a polyline', (WidgetTester tester) async { - final List _points = [const LatLng(0.0, 0.0)]; + final List points = [const LatLng(0.0, 0.0)]; final Polyline p1 = Polyline( polylineId: const PolylineId('polyline_1'), - points: _points, + points: points, ); await tester.pumpWidget(_mapWithPolylines({p1})); 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 26e70d3b4c35..a41d1fe487f3 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.2.4 + +* Updates code for `no_leading_underscores_for_local_identifiers` lint. + ## 2.2.3 * Updates imports for `prefer_relative_imports`. diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object_updates.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object_updates.dart index 0051afcefbab..efc319b60ced 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object_updates.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/maps_object_updates.dart @@ -31,7 +31,7 @@ class MapsObjectUpdates> { /// /// It is a programming error to call this with an ID that is not guaranteed /// to be in [currentObjects]. - T _idToCurrentObject(MapsObjectId id) { + T idToCurrentObject(MapsObjectId id) { return currentObjects[id]!; } @@ -39,7 +39,7 @@ class MapsObjectUpdates> { _objectsToAdd = currentObjectIds .difference(previousObjectIds) - .map(_idToCurrentObject) + .map(idToCurrentObject) .toSet(); // Returns `true` if [current] is not equals to previous one with the @@ -51,7 +51,7 @@ class MapsObjectUpdates> { _objectsToChange = currentObjectIds .intersection(previousObjectIds) - .map(_idToCurrentObject) + .map(idToCurrentObject) .where(hasChanged) .toSet(); } 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 853a23d5cdc3..5639ee8c6ad7 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/plugins/tree/main/packages/google_maps_fl issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.2.3 +version: 2.2.4 environment: sdk: '>=2.12.0 <3.0.0' 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 b1ddd412a3dc..2333f7d16028 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 @@ +## NEXT + +* Updates code for `no_leading_underscores_for_local_identifiers` lint. + ## 0.4.0+3 * Updates imports for `prefer_relative_imports`. 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 4294f8715524..0226234ea97a 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 @@ -37,7 +37,7 @@ void main() { late StreamController> stream; // Creates a controller with the default mapId and stream controller, and any `options` needed. - GoogleMapController _createController({ + GoogleMapController createController({ CameraPosition initialCameraPosition = const CameraPosition(target: LatLng(0, 0)), MapObjects mapObjects = const MapObjects(), @@ -60,7 +60,7 @@ void main() { group('construct/dispose', () { setUp(() { - controller = _createController(); + controller = createController(); }); testWidgets('constructor creates widget', (WidgetTester tester) async { @@ -223,7 +223,7 @@ void main() { }); testWidgets('listens to map events', (WidgetTester tester) async { - controller = _createController(); + controller = createController(); controller.debugSetOverrides( createMap: (_, __) => map, circles: circles, @@ -262,7 +262,7 @@ void main() { testWidgets("binds geometry controllers to map's", (WidgetTester tester) async { - controller = _createController(); + controller = createController(); controller.debugSetOverrides( createMap: (_, __) => map, circles: circles, @@ -280,7 +280,7 @@ void main() { }); testWidgets('renders initial geometry', (WidgetTester tester) async { - controller = _createController( + controller = createController( mapObjects: MapObjects(circles: { const Circle( circleId: CircleId('circle-1'), @@ -360,7 +360,7 @@ void main() { testWidgets('empty infoWindow does not create InfoWindow instance.', (WidgetTester tester) async { - controller = _createController( + controller = createController( mapObjects: MapObjects(markers: { const Marker(markerId: MarkerId('marker-1')), })); @@ -383,7 +383,7 @@ void main() { capturedOptions = null; }); testWidgets('translates initial options', (WidgetTester tester) async { - controller = _createController( + controller = createController( mapConfiguration: const MapConfiguration( mapType: MapType.satellite, zoomControlsEnabled: true, @@ -406,7 +406,7 @@ void main() { testWidgets('disables gestureHandling with scrollGesturesEnabled false', (WidgetTester tester) async { - controller = _createController( + controller = createController( mapConfiguration: const MapConfiguration( scrollGesturesEnabled: false, )); @@ -426,7 +426,7 @@ void main() { testWidgets('disables gestureHandling with zoomGesturesEnabled false', (WidgetTester tester) async { - controller = _createController( + controller = createController( mapConfiguration: const MapConfiguration( zoomGesturesEnabled: false, )); @@ -446,7 +446,7 @@ void main() { testWidgets('sets initial position when passed', (WidgetTester tester) async { - controller = _createController( + controller = createController( initialCameraPosition: const CameraPosition( target: LatLng(43.308, -5.6910), zoom: 12, @@ -469,14 +469,14 @@ void main() { group('Traffic Layer', () { testWidgets('by default is disabled', (WidgetTester tester) async { - controller = _createController(); + controller = createController(); controller.init(); expect(controller.trafficLayer, isNull); }); testWidgets('initializes with traffic layer', (WidgetTester tester) async { - controller = _createController( + controller = createController( mapConfiguration: const MapConfiguration( trafficEnabled: true, )); @@ -498,7 +498,7 @@ void main() { ..zoom = 10 ..center = gmaps.LatLng(0, 0), ); - controller = _createController(); + controller = createController(); controller.debugSetOverrides(createMap: (_, __) => map); controller.init(); }); @@ -572,7 +572,7 @@ void main() { // These are the methods that get forwarded to other controllers, so we just verify calls. group('Pass-through methods', () { setUp(() { - controller = _createController(); + controller = createController(); }); testWidgets('updateCircles', (WidgetTester tester) async { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart index b4786c73b711..9bd1a68c6207 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_plugin_test.dart @@ -381,7 +381,7 @@ void main() { }); // Dispatches a few events in the global streamController, and expects *only* the passed event to be there. - Future _testStreamFiltering( + Future testStreamFiltering( Stream> stream, MapEvent event) async { Timer.run(() { streamController.add(_OtherMapEvent(mapId)); @@ -403,7 +403,7 @@ void main() { final Stream stream = plugin.onCameraMoveStarted(mapId: mapId); - await _testStreamFiltering(stream, event); + await testStreamFiltering(stream, event); }); testWidgets('onCameraMoveStarted', (WidgetTester tester) async { final CameraMoveEvent event = CameraMoveEvent( @@ -416,7 +416,7 @@ void main() { final Stream stream = plugin.onCameraMove(mapId: mapId); - await _testStreamFiltering(stream, event); + await testStreamFiltering(stream, event); }); testWidgets('onCameraIdle', (WidgetTester tester) async { final CameraIdleEvent event = CameraIdleEvent(mapId); @@ -424,7 +424,7 @@ void main() { final Stream stream = plugin.onCameraIdle(mapId: mapId); - await _testStreamFiltering(stream, event); + await testStreamFiltering(stream, event); }); // Marker events testWidgets('onMarkerTap', (WidgetTester tester) async { @@ -435,7 +435,7 @@ void main() { final Stream stream = plugin.onMarkerTap(mapId: mapId); - await _testStreamFiltering(stream, event); + await testStreamFiltering(stream, event); }); testWidgets('onInfoWindowTap', (WidgetTester tester) async { final InfoWindowTapEvent event = InfoWindowTapEvent( @@ -446,7 +446,7 @@ void main() { final Stream stream = plugin.onInfoWindowTap(mapId: mapId); - await _testStreamFiltering(stream, event); + await testStreamFiltering(stream, event); }); testWidgets('onMarkerDragStart', (WidgetTester tester) async { final MarkerDragStartEvent event = MarkerDragStartEvent( @@ -458,7 +458,7 @@ void main() { final Stream stream = plugin.onMarkerDragStart(mapId: mapId); - await _testStreamFiltering(stream, event); + await testStreamFiltering(stream, event); }); testWidgets('onMarkerDrag', (WidgetTester tester) async { final MarkerDragEvent event = MarkerDragEvent( @@ -470,7 +470,7 @@ void main() { final Stream stream = plugin.onMarkerDrag(mapId: mapId); - await _testStreamFiltering(stream, event); + await testStreamFiltering(stream, event); }); testWidgets('onMarkerDragEnd', (WidgetTester tester) async { final MarkerDragEndEvent event = MarkerDragEndEvent( @@ -482,7 +482,7 @@ void main() { final Stream stream = plugin.onMarkerDragEnd(mapId: mapId); - await _testStreamFiltering(stream, event); + await testStreamFiltering(stream, event); }); // Geometry testWidgets('onPolygonTap', (WidgetTester tester) async { @@ -494,7 +494,7 @@ void main() { final Stream stream = plugin.onPolygonTap(mapId: mapId); - await _testStreamFiltering(stream, event); + await testStreamFiltering(stream, event); }); testWidgets('onPolylineTap', (WidgetTester tester) async { final PolylineTapEvent event = PolylineTapEvent( @@ -505,7 +505,7 @@ void main() { final Stream stream = plugin.onPolylineTap(mapId: mapId); - await _testStreamFiltering(stream, event); + await testStreamFiltering(stream, event); }); testWidgets('onCircleTap', (WidgetTester tester) async { final CircleTapEvent event = CircleTapEvent( @@ -515,7 +515,7 @@ void main() { final Stream stream = plugin.onCircleTap(mapId: mapId); - await _testStreamFiltering(stream, event); + await testStreamFiltering(stream, event); }); // Map taps testWidgets('onTap', (WidgetTester tester) async { @@ -526,7 +526,7 @@ void main() { final Stream stream = plugin.onTap(mapId: mapId); - await _testStreamFiltering(stream, event); + await testStreamFiltering(stream, event); }); testWidgets('onLongPress', (WidgetTester tester) async { final MapLongPressEvent event = MapLongPressEvent( @@ -537,7 +537,7 @@ void main() { final Stream stream = plugin.onLongPress(mapId: mapId); - await _testStreamFiltering(stream, event); + await testStreamFiltering(stream, event); }); }); }); 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 e07ade03bba3..6591b0ca08d7 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 @@ -16,32 +16,32 @@ void main() { // Since onTap/DragEnd events happen asynchronously, we need to store when the event // is fired. We use a completer so the test can wait for the future to be completed. - late Completer _methodCalledCompleter; + late Completer methodCalledCompleter; - /// This is the future value of the [_methodCalledCompleter]. Reinitialized + /// This is the future value of the [methodCalledCompleter]. Reinitialized /// in the [setUp] method, and completed (as `true`) by [onTap] and [onDragEnd] /// when those methods are called from the MarkerController. late Future methodCalled; void onTap() { - _methodCalledCompleter.complete(true); + methodCalledCompleter.complete(true); } void onDragStart(gmaps.LatLng _) { - _methodCalledCompleter.complete(true); + methodCalledCompleter.complete(true); } void onDrag(gmaps.LatLng _) { - _methodCalledCompleter.complete(true); + methodCalledCompleter.complete(true); } void onDragEnd(gmaps.LatLng _) { - _methodCalledCompleter.complete(true); + methodCalledCompleter.complete(true); } setUp(() { - _methodCalledCompleter = Completer(); - methodCalled = _methodCalledCompleter.future; + methodCalledCompleter = Completer(); + methodCalled = methodCalledCompleter.future; }); group('MarkerController', () { diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shape_test.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shape_test.dart index d1426760ceae..11af181cffc2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shape_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/shape_test.dart @@ -15,20 +15,20 @@ void main() { // Since onTap events happen asynchronously, we need to store when the event // is fired. We use a completer so the test can wait for the future to be completed. - late Completer _methodCalledCompleter; + late Completer methodCalledCompleter; - /// This is the future value of the [_methodCalledCompleter]. Reinitialized + /// This is the future value of the [methodCalledCompleter]. Reinitialized /// in the [setUp] method, and completed (as `true`) by [onTap], when it gets /// called by the corresponding Shape Controller. late Future methodCalled; void onTap() { - _methodCalledCompleter.complete(true); + methodCalledCompleter.complete(true); } setUp(() { - _methodCalledCompleter = Completer(); - methodCalled = _methodCalledCompleter.future; + methodCalledCompleter = Completer(); + methodCalled = methodCalledCompleter.future; }); group('CircleController', () { 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 51842e4319a0..2816e7284b30 100644 --- a/packages/google_sign_in/google_sign_in_web/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_web/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates code for `no_leading_underscores_for_local_identifiers` lint. * Updates minimum Flutter version to 2.10. ## 0.10.2 diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_legacy_init_test.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_legacy_init_test.dart index 12f8f2f3f167..5dada90397fa 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_legacy_init_test.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_legacy_init_test.dart @@ -71,7 +71,7 @@ void main() { group('other methods also throw catchable exceptions on initialize fail', () { // This function ensures that initialize gets called, but for some reason, // we ignored that it has thrown stuff... - Future _discardInit() async { + Future discardInit() async { try { await plugin.init( hostedDomain: 'foo', @@ -89,23 +89,23 @@ void main() { }); testWidgets('signInSilently throws', (WidgetTester tester) async { - await _discardInit(); + await discardInit(); await expectLater( plugin.signInSilently(), throwsA(isA())); }); testWidgets('signIn throws', (WidgetTester tester) async { - await _discardInit(); + await discardInit(); await expectLater(plugin.signIn(), throwsA(isA())); }); testWidgets('getTokens throws', (WidgetTester tester) async { - await _discardInit(); + await discardInit(); await expectLater(plugin.getTokens(email: 'test@example.com'), throwsA(isA())); }); testWidgets('requestScopes', (WidgetTester tester) async { - await _discardInit(); + await discardInit(); await expectLater(plugin.requestScopes(['newScope']), throwsA(isA())); }); diff --git a/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart b/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart index 81d9f1489a23..3e803b83fa0c 100644 --- a/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart +++ b/packages/google_sign_in/google_sign_in_web/example/integration_test/auth2_test.dart @@ -66,7 +66,7 @@ void main() { () { // This function ensures that initWithParams gets called, but for some // reason, we ignored that it has thrown stuff... - Future _discardInit() async { + Future discardInit() async { try { await plugin.initWithParams(const SignInInitParameters( hostedDomain: 'foo', @@ -84,23 +84,23 @@ void main() { }); testWidgets('signInSilently throws', (WidgetTester tester) async { - await _discardInit(); + await discardInit(); await expectLater( plugin.signInSilently(), throwsA(isA())); }); testWidgets('signIn throws', (WidgetTester tester) async { - await _discardInit(); + await discardInit(); await expectLater(plugin.signIn(), throwsA(isA())); }); testWidgets('getTokens throws', (WidgetTester tester) async { - await _discardInit(); + await discardInit(); await expectLater(plugin.getTokens(email: 'test@example.com'), throwsA(isA())); }); testWidgets('requestScopes', (WidgetTester tester) async { - await _discardInit(); + await discardInit(); await expectLater(plugin.requestScopes(['newScope']), throwsA(isA())); }); diff --git a/packages/image_picker/image_picker_for_web/CHANGELOG.md b/packages/image_picker/image_picker_for_web/CHANGELOG.md index 23bfe17ce9e1..8a5c089ef807 100644 --- a/packages/image_picker/image_picker_for_web/CHANGELOG.md +++ b/packages/image_picker/image_picker_for_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.10 + +* Updates code for `no_leading_underscores_for_local_identifiers` lint. + ## 2.1.9 * Updates imports for `prefer_relative_imports`. 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 5e0f18aaebcc..bb261f76f320 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 @@ -244,35 +244,35 @@ class ImagePickerPlugin extends ImagePickerPlatform { /// Monitors an and returns the selected file. Future _getSelectedFile(html.FileUploadInputElement input) { - final Completer _completer = Completer(); + final Completer completer = Completer(); // Observe the input until we can return something input.onChange.first.then((html.Event event) { final List? files = _handleOnChangeEvent(event); - if (!_completer.isCompleted && files != null) { - _completer.complete(PickedFile( + if (!completer.isCompleted && files != null) { + completer.complete(PickedFile( html.Url.createObjectUrl(files.first), )); } }); input.onError.first.then((html.Event event) { - if (!_completer.isCompleted) { - _completer.completeError(event); + if (!completer.isCompleted) { + completer.completeError(event); } }); // 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. - return _completer.future; + return completer.future; } /// Monitors an and returns the selected file(s). Future> _getSelectedXFiles(html.FileUploadInputElement input) { - final Completer> _completer = Completer>(); + final Completer> completer = Completer>(); // Observe the input until we can return something input.onChange.first.then((html.Event event) { final List? files = _handleOnChangeEvent(event); - if (!_completer.isCompleted && files != null) { - _completer.complete(files.map((html.File file) { + if (!completer.isCompleted && files != null) { + completer.complete(files.map((html.File file) { return XFile( html.Url.createObjectUrl(file), name: file.name, @@ -286,14 +286,14 @@ class ImagePickerPlugin extends ImagePickerPlatform { } }); input.onError.first.then((html.Event event) { - if (!_completer.isCompleted) { - _completer.completeError(event); + if (!completer.isCompleted) { + completer.completeError(event); } }); // 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. - return _completer.future; + return completer.future; } /// Initializes a DOM container where we can host input elements. diff --git a/packages/image_picker/image_picker_for_web/pubspec.yaml b/packages/image_picker/image_picker_for_web/pubspec.yaml index 14d9197e821c..c2e0975dda57 100644 --- a/packages/image_picker/image_picker_for_web/pubspec.yaml +++ b/packages/image_picker/image_picker_for_web/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_for_web description: Web platform implementation of image_picker repository: https://github.com/flutter/plugins/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: 2.1.9 +version: 2.1.10 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index e19415ff68ac..436523551924 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates code for `no_leading_underscores_for_local_identifiers` lint. * Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. diff --git a/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart b/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart index 1fe31df1fe31..bf150f66f49b 100644 --- a/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart +++ b/packages/path_provider/path_provider/example/integration_test/path_provider_test.dart @@ -58,7 +58,7 @@ void main() { } }); - final List _allDirs = [ + final List allDirs = [ null, StorageDirectory.music, StorageDirectory.podcasts, @@ -69,7 +69,7 @@ void main() { StorageDirectory.movies, ]; - for (final StorageDirectory? type in _allDirs) { + for (final StorageDirectory? type in allDirs) { testWidgets('getExternalStorageDirectories (type: $type)', (WidgetTester tester) async { if (Platform.isIOS) { diff --git a/packages/path_provider/path_provider_android/CHANGELOG.md b/packages/path_provider/path_provider_android/CHANGELOG.md index 93f074795649..c36a771d3340 100644 --- a/packages/path_provider/path_provider_android/CHANGELOG.md +++ b/packages/path_provider/path_provider_android/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates code for `no_leading_underscores_for_local_identifiers` lint. * Updates minimum Flutter version to 2.10. ## 2.0.20 diff --git a/packages/path_provider/path_provider_android/example/integration_test/path_provider_test.dart b/packages/path_provider/path_provider_android/example/integration_test/path_provider_test.dart index 2be88130b4e7..ecd0b973343b 100644 --- a/packages/path_provider/path_provider_android/example/integration_test/path_provider_test.dart +++ b/packages/path_provider/path_provider_android/example/integration_test/path_provider_test.dart @@ -49,7 +49,7 @@ void main() { } }); - final List _allDirs = [ + final List allDirs = [ null, StorageDirectory.music, StorageDirectory.podcasts, @@ -60,7 +60,7 @@ void main() { StorageDirectory.movies, ]; - for (final StorageDirectory? type in _allDirs) { + for (final StorageDirectory? type in allDirs) { testWidgets('getExternalStorageDirectories (type: $type)', (WidgetTester tester) async { final PathProviderPlatform provider = PathProviderPlatform.instance; diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index d7c3aa49e22e..b719ff158ff4 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates code for `no_leading_underscores_for_local_identifiers` lint. * Updates minimum Flutter version to 2.10. ## 2.0.15 diff --git a/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart index 0a02c46404fc..30f7829f670a 100755 --- a/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart @@ -171,22 +171,22 @@ void main() { }); group('mocking', () { - const String _key = 'dummy'; - const String _prefixedKey = 'flutter.$_key'; + const String key = 'dummy'; + const String prefixedKey = 'flutter.$key'; test('test 1', () async { SharedPreferences.setMockInitialValues( - {_prefixedKey: 'my string'}); + {prefixedKey: 'my string'}); final SharedPreferences prefs = await SharedPreferences.getInstance(); - final String? value = prefs.getString(_key); + final String? value = prefs.getString(key); expect(value, 'my string'); }); test('test 2', () async { SharedPreferences.setMockInitialValues( - {_prefixedKey: 'my other string'}); + {prefixedKey: 'my other string'}); final SharedPreferences prefs = await SharedPreferences.getInstance(); - final String? value = prefs.getString(_key); + final String? value = prefs.getString(key); expect(value, 'my other string'); }); }); diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index 91a922d9de64..f21af6aff7aa 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates code for `no_leading_underscores_for_local_identifiers` lint. + ## 2.0.13 * Updates gradle to 7.2.2. diff --git a/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart index 275067d98770..4d4a85a5fcbc 100644 --- a/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart @@ -39,42 +39,42 @@ void main() { // Normally the app-facing package adds the prefix, but since this test // bypasses the app-facing package it needs to be manually added. - String _prefixedKey(String key) { + String prefixedKey(String key) { return 'flutter.$key'; } testWidgets('reading', (WidgetTester _) async { final Map values = await preferences.getAll(); - expect(values[_prefixedKey('String')], isNull); - expect(values[_prefixedKey('bool')], isNull); - expect(values[_prefixedKey('int')], isNull); - expect(values[_prefixedKey('double')], isNull); - expect(values[_prefixedKey('List')], isNull); + expect(values[prefixedKey('String')], isNull); + expect(values[prefixedKey('bool')], isNull); + expect(values[prefixedKey('int')], isNull); + expect(values[prefixedKey('double')], isNull); + expect(values[prefixedKey('List')], isNull); }); testWidgets('writing', (WidgetTester _) async { await Future.wait(>[ preferences.setValue( - 'String', _prefixedKey('String'), kTestValues2['flutter.String']!), + 'String', prefixedKey('String'), kTestValues2['flutter.String']!), preferences.setValue( - 'Bool', _prefixedKey('bool'), kTestValues2['flutter.bool']!), + 'Bool', prefixedKey('bool'), kTestValues2['flutter.bool']!), preferences.setValue( - 'Int', _prefixedKey('int'), kTestValues2['flutter.int']!), + 'Int', prefixedKey('int'), kTestValues2['flutter.int']!), preferences.setValue( - 'Double', _prefixedKey('double'), kTestValues2['flutter.double']!), + 'Double', prefixedKey('double'), kTestValues2['flutter.double']!), preferences.setValue( - 'StringList', _prefixedKey('List'), kTestValues2['flutter.List']!) + 'StringList', prefixedKey('List'), kTestValues2['flutter.List']!) ]); final Map values = await preferences.getAll(); - expect(values[_prefixedKey('String')], kTestValues2['flutter.String']); - expect(values[_prefixedKey('bool')], kTestValues2['flutter.bool']); - expect(values[_prefixedKey('int')], kTestValues2['flutter.int']); - expect(values[_prefixedKey('double')], kTestValues2['flutter.double']); - expect(values[_prefixedKey('List')], kTestValues2['flutter.List']); + expect(values[prefixedKey('String')], kTestValues2['flutter.String']); + expect(values[prefixedKey('bool')], kTestValues2['flutter.bool']); + expect(values[prefixedKey('int')], kTestValues2['flutter.int']); + expect(values[prefixedKey('double')], kTestValues2['flutter.double']); + expect(values[prefixedKey('List')], kTestValues2['flutter.List']); }); testWidgets('removing', (WidgetTester _) async { - final String key = _prefixedKey('testKey'); + final String key = prefixedKey('testKey'); await preferences.setValue('String', key, kTestValues['flutter.String']!); await preferences.setValue('Bool', key, kTestValues['flutter.bool']!); await preferences.setValue('Int', key, kTestValues['flutter.int']!); @@ -108,19 +108,19 @@ void main() { final List> writes = >[]; const int writeCount = 100; for (int i = 1; i <= writeCount; i++) { - writes.add(preferences.setValue('Int', _prefixedKey('int'), i)); + writes.add(preferences.setValue('Int', prefixedKey('int'), i)); } final List result = await Future.wait(writes, eagerError: true); // All writes should succeed. expect(result.where((bool element) => !element), isEmpty); // The last write should win. final Map values = await preferences.getAll(); - expect(values[_prefixedKey('int')], writeCount); + expect(values[prefixedKey('int')], writeCount); }); testWidgets('string clash with lists, big integers and doubles', (WidgetTester _) async { - final String key = _prefixedKey('akey'); + final String key = prefixedKey('akey'); const String value = 'a string value'; await preferences.clear(); diff --git a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md index 99dde9c462b7..d2101e0784cf 100644 --- a/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_ios/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates code for `no_leading_underscores_for_local_identifiers` lint. * Updates minimum Flutter version to 2.10. ## 2.1.1 diff --git a/packages/shared_preferences/shared_preferences_ios/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_ios/example/integration_test/shared_preferences_test.dart index 3a6bab55eced..b4b21871701c 100644 --- a/packages/shared_preferences/shared_preferences_ios/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_ios/example/integration_test/shared_preferences_test.dart @@ -38,42 +38,42 @@ void main() { // Normally the app-facing package adds the prefix, but since this test // bypasses the app-facing package it needs to be manually added. - String _prefixedKey(String key) { + String prefixedKey(String key) { return 'flutter.$key'; } testWidgets('reading', (WidgetTester _) async { final Map values = await preferences.getAll(); - expect(values[_prefixedKey('String')], isNull); - expect(values[_prefixedKey('bool')], isNull); - expect(values[_prefixedKey('int')], isNull); - expect(values[_prefixedKey('double')], isNull); - expect(values[_prefixedKey('List')], isNull); + expect(values[prefixedKey('String')], isNull); + expect(values[prefixedKey('bool')], isNull); + expect(values[prefixedKey('int')], isNull); + expect(values[prefixedKey('double')], isNull); + expect(values[prefixedKey('List')], isNull); }); testWidgets('writing', (WidgetTester _) async { await Future.wait(>[ preferences.setValue( - 'String', _prefixedKey('String'), kTestValues2['flutter.String']!), + 'String', prefixedKey('String'), kTestValues2['flutter.String']!), preferences.setValue( - 'Bool', _prefixedKey('bool'), kTestValues2['flutter.bool']!), + 'Bool', prefixedKey('bool'), kTestValues2['flutter.bool']!), preferences.setValue( - 'Int', _prefixedKey('int'), kTestValues2['flutter.int']!), + 'Int', prefixedKey('int'), kTestValues2['flutter.int']!), preferences.setValue( - 'Double', _prefixedKey('double'), kTestValues2['flutter.double']!), + 'Double', prefixedKey('double'), kTestValues2['flutter.double']!), preferences.setValue( - 'StringList', _prefixedKey('List'), kTestValues2['flutter.List']!) + 'StringList', prefixedKey('List'), kTestValues2['flutter.List']!) ]); final Map values = await preferences.getAll(); - expect(values[_prefixedKey('String')], kTestValues2['flutter.String']); - expect(values[_prefixedKey('bool')], kTestValues2['flutter.bool']); - expect(values[_prefixedKey('int')], kTestValues2['flutter.int']); - expect(values[_prefixedKey('double')], kTestValues2['flutter.double']); - expect(values[_prefixedKey('List')], kTestValues2['flutter.List']); + expect(values[prefixedKey('String')], kTestValues2['flutter.String']); + expect(values[prefixedKey('bool')], kTestValues2['flutter.bool']); + expect(values[prefixedKey('int')], kTestValues2['flutter.int']); + expect(values[prefixedKey('double')], kTestValues2['flutter.double']); + expect(values[prefixedKey('List')], kTestValues2['flutter.List']); }); testWidgets('removing', (WidgetTester _) async { - final String key = _prefixedKey('testKey'); + final String key = prefixedKey('testKey'); await preferences.setValue('String', key, kTestValues['flutter.String']!); await preferences.setValue('Bool', key, kTestValues['flutter.bool']!); await preferences.setValue('Int', key, kTestValues['flutter.int']!); diff --git a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md index 60f93f31be3c..5d59660b4d6b 100644 --- a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates code for `no_leading_underscores_for_local_identifiers` lint. * Updates minimum Flutter version to 2.10. ## 2.1.1 diff --git a/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart b/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart index 57acd17f5094..176d1d9f9ead 100644 --- a/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart +++ b/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart @@ -20,22 +20,22 @@ void main() { pathProvider = FakePathProviderLinux(); }); - Future _getFilePath() async { + Future getFilePath() async { final String? directory = await pathProvider.getApplicationSupportPath(); return path.join(directory!, 'shared_preferences.json'); } - Future _writeTestFile(String value) async { - fs.file(await _getFilePath()) + Future writeTestFile(String value) async { + fs.file(await getFilePath()) ..createSync(recursive: true) ..writeAsStringSync(value); } - Future _readTestFile() async { - return fs.file(await _getFilePath()).readAsStringSync(); + Future readTestFile() async { + return fs.file(await getFilePath()).readAsStringSync(); } - SharedPreferencesLinux _getPreferences() { + SharedPreferencesLinux getPreferences() { final SharedPreferencesLinux prefs = SharedPreferencesLinux(); prefs.fs = fs; prefs.pathProvider = pathProvider; @@ -49,8 +49,8 @@ void main() { }); test('getAll', () async { - await _writeTestFile('{"key1": "one", "key2": 2}'); - final SharedPreferencesLinux prefs = _getPreferences(); + await writeTestFile('{"key1": "one", "key2": 2}'); + final SharedPreferencesLinux prefs = getPreferences(); final Map values = await prefs.getAll(); expect(values, hasLength(2)); @@ -59,30 +59,30 @@ void main() { }); test('remove', () async { - await _writeTestFile('{"key1":"one","key2":2}'); - final SharedPreferencesLinux prefs = _getPreferences(); + await writeTestFile('{"key1":"one","key2":2}'); + final SharedPreferencesLinux prefs = getPreferences(); await prefs.remove('key2'); - expect(await _readTestFile(), '{"key1":"one"}'); + expect(await readTestFile(), '{"key1":"one"}'); }); test('setValue', () async { - await _writeTestFile('{}'); - final SharedPreferencesLinux prefs = _getPreferences(); + await writeTestFile('{}'); + final SharedPreferencesLinux prefs = getPreferences(); await prefs.setValue('', 'key1', 'one'); await prefs.setValue('', 'key2', 2); - expect(await _readTestFile(), '{"key1":"one","key2":2}'); + expect(await readTestFile(), '{"key1":"one","key2":2}'); }); test('clear', () async { - await _writeTestFile('{"key1":"one","key2":2}'); - final SharedPreferencesLinux prefs = _getPreferences(); + await writeTestFile('{"key1":"one","key2":2}'); + final SharedPreferencesLinux prefs = getPreferences(); await prefs.clear(); - expect(await _readTestFile(), '{}'); + expect(await readTestFile(), '{}'); }); } diff --git a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md index bc4e1504be1c..fc8a78af95b9 100644 --- a/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_macos/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates code for `no_leading_underscores_for_local_identifiers` lint. * Updates minimum Flutter version to 2.10. ## 2.0.4 diff --git a/packages/shared_preferences/shared_preferences_macos/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_macos/example/integration_test/shared_preferences_test.dart index 874ceb4c51a7..a980eab26679 100644 --- a/packages/shared_preferences/shared_preferences_macos/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_macos/example/integration_test/shared_preferences_test.dart @@ -38,42 +38,42 @@ void main() { // Normally the app-facing package adds the prefix, but since this test // bypasses the app-facing package it needs to be manually added. - String _prefixedKey(String key) { + String prefixedKey(String key) { return 'flutter.$key'; } testWidgets('reading', (WidgetTester _) async { final Map values = await preferences.getAll(); - expect(values[_prefixedKey('String')], isNull); - expect(values[_prefixedKey('bool')], isNull); - expect(values[_prefixedKey('int')], isNull); - expect(values[_prefixedKey('double')], isNull); - expect(values[_prefixedKey('List')], isNull); + expect(values[prefixedKey('String')], isNull); + expect(values[prefixedKey('bool')], isNull); + expect(values[prefixedKey('int')], isNull); + expect(values[prefixedKey('double')], isNull); + expect(values[prefixedKey('List')], isNull); }); testWidgets('writing', (WidgetTester _) async { await Future.wait(>[ preferences.setValue( - 'String', _prefixedKey('String'), kTestValues2['flutter.String']!), + 'String', prefixedKey('String'), kTestValues2['flutter.String']!), preferences.setValue( - 'Bool', _prefixedKey('bool'), kTestValues2['flutter.bool']!), + 'Bool', prefixedKey('bool'), kTestValues2['flutter.bool']!), preferences.setValue( - 'Int', _prefixedKey('int'), kTestValues2['flutter.int']!), + 'Int', prefixedKey('int'), kTestValues2['flutter.int']!), preferences.setValue( - 'Double', _prefixedKey('double'), kTestValues2['flutter.double']!), + 'Double', prefixedKey('double'), kTestValues2['flutter.double']!), preferences.setValue( - 'StringList', _prefixedKey('List'), kTestValues2['flutter.List']!) + 'StringList', prefixedKey('List'), kTestValues2['flutter.List']!) ]); final Map values = await preferences.getAll(); - expect(values[_prefixedKey('String')], kTestValues2['flutter.String']); - expect(values[_prefixedKey('bool')], kTestValues2['flutter.bool']); - expect(values[_prefixedKey('int')], kTestValues2['flutter.int']); - expect(values[_prefixedKey('double')], kTestValues2['flutter.double']); - expect(values[_prefixedKey('List')], kTestValues2['flutter.List']); + expect(values[prefixedKey('String')], kTestValues2['flutter.String']); + expect(values[prefixedKey('bool')], kTestValues2['flutter.bool']); + expect(values[prefixedKey('int')], kTestValues2['flutter.int']); + expect(values[prefixedKey('double')], kTestValues2['flutter.double']); + expect(values[prefixedKey('List')], kTestValues2['flutter.List']); }); testWidgets('removing', (WidgetTester _) async { - final String key = _prefixedKey('testKey'); + final String key = prefixedKey('testKey'); await preferences.setValue('String', key, kTestValues['flutter.String']!); await preferences.setValue('Bool', key, kTestValues['flutter.bool']!); await preferences.setValue('Int', key, kTestValues['flutter.int']!); diff --git a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md index b8351c8795d1..935a5ee54c2b 100644 --- a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates code for `no_leading_underscores_for_local_identifiers` lint. * Updates minimum Flutter version to 2.10. ## 2.1.1 diff --git a/packages/shared_preferences/shared_preferences_windows/test/shared_preferences_windows_test.dart b/packages/shared_preferences/shared_preferences_windows/test/shared_preferences_windows_test.dart index 0c47e9865765..04fa335b703e 100644 --- a/packages/shared_preferences/shared_preferences_windows/test/shared_preferences_windows_test.dart +++ b/packages/shared_preferences/shared_preferences_windows/test/shared_preferences_windows_test.dart @@ -19,22 +19,22 @@ void main() { pathProvider = FakePathProviderWindows(); }); - Future _getFilePath() async { + Future getFilePath() async { final String? directory = await pathProvider.getApplicationSupportPath(); return path.join(directory!, 'shared_preferences.json'); } - Future _writeTestFile(String value) async { - fileSystem.file(await _getFilePath()) + Future writeTestFile(String value) async { + fileSystem.file(await getFilePath()) ..createSync(recursive: true) ..writeAsStringSync(value); } - Future _readTestFile() async { - return fileSystem.file(await _getFilePath()).readAsStringSync(); + Future readTestFile() async { + return fileSystem.file(await getFilePath()).readAsStringSync(); } - SharedPreferencesWindows _getPreferences() { + SharedPreferencesWindows getPreferences() { final SharedPreferencesWindows prefs = SharedPreferencesWindows(); prefs.fs = fileSystem; prefs.pathProvider = pathProvider; @@ -48,8 +48,8 @@ void main() { }); test('getAll', () async { - await _writeTestFile('{"key1": "one", "key2": 2}'); - final SharedPreferencesWindows prefs = _getPreferences(); + await writeTestFile('{"key1": "one", "key2": 2}'); + final SharedPreferencesWindows prefs = getPreferences(); final Map values = await prefs.getAll(); expect(values, hasLength(2)); @@ -58,30 +58,30 @@ void main() { }); test('remove', () async { - await _writeTestFile('{"key1":"one","key2":2}'); - final SharedPreferencesWindows prefs = _getPreferences(); + await writeTestFile('{"key1":"one","key2":2}'); + final SharedPreferencesWindows prefs = getPreferences(); await prefs.remove('key2'); - expect(await _readTestFile(), '{"key1":"one"}'); + expect(await readTestFile(), '{"key1":"one"}'); }); test('setValue', () async { - await _writeTestFile('{}'); - final SharedPreferencesWindows prefs = _getPreferences(); + await writeTestFile('{}'); + final SharedPreferencesWindows prefs = getPreferences(); await prefs.setValue('', 'key1', 'one'); await prefs.setValue('', 'key2', 2); - expect(await _readTestFile(), '{"key1":"one","key2":2}'); + expect(await readTestFile(), '{"key1":"one","key2":2}'); }); test('clear', () async { - await _writeTestFile('{"key1":"one","key2":2}'); - final SharedPreferencesWindows prefs = _getPreferences(); + await writeTestFile('{"key1":"one","key2":2}'); + final SharedPreferencesWindows prefs = getPreferences(); await prefs.clear(); - expect(await _readTestFile(), '{}'); + expect(await readTestFile(), '{}'); }); } diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 53cfabf31e78..0885f28f9db8 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates code for `no_leading_underscores_for_local_identifiers` lint. + ## 2.4.7 * Updates README via code excerpts. diff --git a/packages/video_player/video_player/example/integration_test/video_player_test.dart b/packages/video_player/video_player/example/integration_test/video_player_test.dart index 023c4b3fcb43..dd77a2f0252a 100644 --- a/packages/video_player/video_player/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player/example/integration_test/video_player_test.dart @@ -37,22 +37,22 @@ String getUrlForAssetAsNetworkSource(String assetKey) { void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - late VideoPlayerController _controller; - tearDown(() async => _controller.dispose()); + late VideoPlayerController controller; + tearDown(() async => controller.dispose()); group('asset videos', () { setUp(() { - _controller = VideoPlayerController.asset(_videoAssetKey); + controller = VideoPlayerController.asset(_videoAssetKey); }); testWidgets('can be initialized', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); - expect(_controller.value.isInitialized, true); - expect(_controller.value.position, Duration.zero); - expect(_controller.value.isPlaying, false); + expect(controller.value.isInitialized, true); + expect(controller.value.position, Duration.zero); + expect(controller.value.isPlaying, false); // The WebM version has a slightly different duration than the MP4. - expect(_controller.value.duration, + expect(controller.value.duration, const Duration(seconds: 7, milliseconds: kIsWeb ? 544 : 540)); }); @@ -77,16 +77,16 @@ void main() { testWidgets( 'can be played', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); // Mute to allow playing without DOM interaction on Web. // See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes - await _controller.setVolume(0); + await controller.setVolume(0); - await _controller.play(); + await controller.play(); await tester.pumpAndSettle(_playDuration); - expect(_controller.value.isPlaying, true); - expect(_controller.value.position, + expect(controller.value.isPlaying, true); + expect(controller.value.position, (Duration position) => position > Duration.zero); }, ); @@ -94,85 +94,85 @@ void main() { testWidgets( 'can seek', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); - await _controller.seekTo(const Duration(seconds: 3)); + await controller.seekTo(const Duration(seconds: 3)); - expect(_controller.value.position, const Duration(seconds: 3)); + expect(controller.value.position, const Duration(seconds: 3)); }, ); testWidgets( 'can be paused', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); // Mute to allow playing without DOM interaction on Web. // See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes - await _controller.setVolume(0); + await controller.setVolume(0); // Play for a second, then pause, and then wait a second. - await _controller.play(); + await controller.play(); await tester.pumpAndSettle(_playDuration); - await _controller.pause(); - final Duration pausedPosition = _controller.value.position; + await controller.pause(); + final Duration pausedPosition = controller.value.position; await tester.pumpAndSettle(_playDuration); // Verify that we stopped playing after the pause. - expect(_controller.value.isPlaying, false); - expect(_controller.value.position, pausedPosition); + expect(controller.value.isPlaying, false); + expect(controller.value.position, pausedPosition); }, ); testWidgets( 'stay paused when seeking after video completed', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); // Mute to allow playing without DOM interaction on Web. // See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes - await _controller.setVolume(0); + await controller.setVolume(0); final Duration tenMillisBeforeEnd = - _controller.value.duration - const Duration(milliseconds: 10); - await _controller.seekTo(tenMillisBeforeEnd); - await _controller.play(); + controller.value.duration - const Duration(milliseconds: 10); + await controller.seekTo(tenMillisBeforeEnd); + await controller.play(); await tester.pumpAndSettle(_playDuration); - expect(_controller.value.isPlaying, false); - expect(_controller.value.position, _controller.value.duration); + expect(controller.value.isPlaying, false); + expect(controller.value.position, controller.value.duration); - await _controller.seekTo(tenMillisBeforeEnd); + await controller.seekTo(tenMillisBeforeEnd); await tester.pumpAndSettle(_playDuration); - expect(_controller.value.isPlaying, false); - expect(_controller.value.position, tenMillisBeforeEnd); + expect(controller.value.isPlaying, false); + expect(controller.value.position, tenMillisBeforeEnd); }, ); testWidgets( 'do not exceed duration on play after video completed', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); // Mute to allow playing without DOM interaction on Web. // See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes - await _controller.setVolume(0); - await _controller.seekTo( - _controller.value.duration - const Duration(milliseconds: 10)); - await _controller.play(); + await controller.setVolume(0); + await controller.seekTo( + controller.value.duration - const Duration(milliseconds: 10)); + await controller.play(); await tester.pumpAndSettle(_playDuration); - expect(_controller.value.isPlaying, false); - expect(_controller.value.position, _controller.value.duration); + expect(controller.value.isPlaying, false); + expect(controller.value.position, controller.value.duration); - await _controller.play(); + await controller.play(); await tester.pumpAndSettle(_playDuration); - expect(_controller.value.position, - lessThanOrEqualTo(_controller.value.duration)); + expect(controller.value.position, + lessThanOrEqualTo(controller.value.duration)); }, ); testWidgets('test video player view with local asset', (WidgetTester tester) async { Future started() async { - await _controller.initialize(); - await _controller.play(); + await controller.initialize(); + await controller.play(); return true; } @@ -185,8 +185,8 @@ void main() { builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.data ?? false) { return AspectRatio( - aspectRatio: _controller.value.aspectRatio, - child: VideoPlayer(_controller), + aspectRatio: controller.value.aspectRatio, + child: VideoPlayer(controller), ); } else { return const Text('waiting for video to load'); @@ -198,7 +198,7 @@ void main() { )); await tester.pumpAndSettle(); - expect(_controller.value.isPlaying, true); + expect(controller.value.isPlaying, true); }, skip: kIsWeb || // Web does not support local assets. // Extremely flaky on iOS: https://github.com/flutter/flutter/issues/86915 @@ -216,54 +216,54 @@ void main() { final File file = File('$tempDir/$filename'); await file.writeAsBytes(bytes.buffer.asInt8List()); - _controller = VideoPlayerController.file(file); + controller = VideoPlayerController.file(file); }); testWidgets('test video player using static file() method as constructor', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); - await _controller.play(); - expect(_controller.value.isPlaying, true); + await controller.play(); + expect(controller.value.isPlaying, true); - await _controller.pause(); - expect(_controller.value.isPlaying, false); + await controller.pause(); + expect(controller.value.isPlaying, false); }, skip: kIsWeb); }); group('network videos', () { setUp(() { - _controller = VideoPlayerController.network( + controller = VideoPlayerController.network( getUrlForAssetAsNetworkSource(_videoAssetKey)); }); testWidgets( 'reports buffering status', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); // Mute to allow playing without DOM interaction on Web. // See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes - await _controller.setVolume(0); + await controller.setVolume(0); final Completer started = Completer(); final Completer ended = Completer(); - _controller.addListener(() { - if (!started.isCompleted && _controller.value.isBuffering) { + controller.addListener(() { + if (!started.isCompleted && controller.value.isBuffering) { started.complete(); } if (started.isCompleted && - !_controller.value.isBuffering && + !controller.value.isBuffering && !ended.isCompleted) { ended.complete(); } }); - await _controller.play(); - await _controller.seekTo(const Duration(seconds: 5)); + await controller.play(); + await controller.seekTo(const Duration(seconds: 5)); await tester.pumpAndSettle(_playDuration); - await _controller.pause(); + await controller.pause(); - expect(_controller.value.isPlaying, false); - expect(_controller.value.position, + expect(controller.value.isPlaying, false); + expect(controller.value.position, (Duration position) => position > Duration.zero); await expectLater(started.future, completes); @@ -277,63 +277,63 @@ void main() { // but could be removed in the future. group('asset audios', () { setUp(() { - _controller = VideoPlayerController.asset('assets/Audio.mp3'); + controller = VideoPlayerController.asset('assets/Audio.mp3'); }); testWidgets('can be initialized', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); - expect(_controller.value.isInitialized, true); - expect(_controller.value.position, Duration.zero); - expect(_controller.value.isPlaying, false); + expect(controller.value.isInitialized, true); + expect(controller.value.position, Duration.zero); + expect(controller.value.isPlaying, false); // Due to the duration calculation accuracy between platforms, // the milliseconds on Web will be a slightly different from natives. // The audio was made with 44100 Hz, 192 Kbps CBR, and 32 bits. expect( - _controller.value.duration, + controller.value.duration, const Duration(seconds: 5, milliseconds: kIsWeb ? 42 : 41), ); }); testWidgets('can be played', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); // Mute to allow playing without DOM interaction on Web. // See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes - await _controller.setVolume(0); + await controller.setVolume(0); - await _controller.play(); + await controller.play(); await tester.pumpAndSettle(_playDuration); - expect(_controller.value.isPlaying, true); + expect(controller.value.isPlaying, true); expect( - _controller.value.position, + controller.value.position, (Duration position) => position > Duration.zero, ); }); testWidgets('can seek', (WidgetTester tester) async { - await _controller.initialize(); - await _controller.seekTo(const Duration(seconds: 3)); + await controller.initialize(); + await controller.seekTo(const Duration(seconds: 3)); - expect(_controller.value.position, const Duration(seconds: 3)); + expect(controller.value.position, const Duration(seconds: 3)); }); testWidgets('can be paused', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); // Mute to allow playing without DOM interaction on Web. // See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes - await _controller.setVolume(0); + await controller.setVolume(0); // Play for a second, then pause, and then wait a second. - await _controller.play(); + await controller.play(); await tester.pumpAndSettle(_playDuration); - await _controller.pause(); - final Duration pausedPosition = _controller.value.position; + await controller.pause(); + final Duration pausedPosition = controller.value.position; await tester.pumpAndSettle(_playDuration); // Verify that we stopped playing after the pause. - expect(_controller.value.isPlaying, false); - expect(_controller.value.position, pausedPosition); + expect(controller.value.isPlaying, false); + expect(controller.value.position, pausedPosition); }); }); } diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index e7b3e1de7e63..8e5e98b68f18 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -105,7 +105,7 @@ class _FakeClosedCaptionFile extends ClosedCaptionFile { } void main() { - void _verifyPlayStateRespondsToLifecycle( + void verifyPlayStateRespondsToLifecycle( VideoPlayerController controller, { required bool shouldPlayInBackground, }) { @@ -248,7 +248,7 @@ void main() { ); await controller.initialize(); await controller.play(); - _verifyPlayStateRespondsToLifecycle(controller, + verifyPlayStateRespondsToLifecycle(controller, shouldPlayInBackground: false); }); @@ -1025,7 +1025,7 @@ void main() { ); await controller.initialize(); await controller.play(); - _verifyPlayStateRespondsToLifecycle( + verifyPlayStateRespondsToLifecycle( controller, shouldPlayInBackground: true, ); @@ -1038,7 +1038,7 @@ void main() { ); await controller.initialize(); await controller.play(); - _verifyPlayStateRespondsToLifecycle( + verifyPlayStateRespondsToLifecycle( controller, shouldPlayInBackground: false, ); diff --git a/packages/video_player/video_player_android/CHANGELOG.md b/packages/video_player/video_player_android/CHANGELOG.md index 7986d1a28625..7298bacf4f78 100644 --- a/packages/video_player/video_player_android/CHANGELOG.md +++ b/packages/video_player/video_player_android/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates code for `no_leading_underscores_for_local_identifiers` lint. * Updates minimum Flutter version to 2.10. * Fixes violations of new analysis option use_named_constants. diff --git a/packages/video_player/video_player_android/example/integration_test/video_player_test.dart b/packages/video_player/video_player_android/example/integration_test/video_player_test.dart index ef7bdeda3503..751412c80f43 100644 --- a/packages/video_player/video_player_android/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player_android/example/integration_test/video_player_test.dart @@ -39,12 +39,12 @@ String getUrlForAssetAsNetworkSource(String assetKey) { void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - late MiniController _controller; - tearDown(() async => _controller.dispose()); + late MiniController controller; + tearDown(() async => controller.dispose()); group('asset videos', () { setUp(() { - _controller = MiniController.asset(_videoAssetKey); + controller = MiniController.asset(_videoAssetKey); }); testWidgets('registers expected implementation', @@ -54,44 +54,44 @@ void main() { }); testWidgets('can be initialized', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); - expect(_controller.value.isInitialized, true); - expect(await _controller.position, Duration.zero); - expect(_controller.value.duration, + expect(controller.value.isInitialized, true); + expect(await controller.position, Duration.zero); + expect(controller.value.duration, const Duration(seconds: 7, milliseconds: 540)); }); testWidgets('can be played', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); - await _controller.play(); + await controller.play(); await tester.pumpAndSettle(_playDuration); - expect(await _controller.position, greaterThan(Duration.zero)); + expect(await controller.position, greaterThan(Duration.zero)); }); testWidgets('can seek', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); - await _controller.seekTo(const Duration(seconds: 3)); + await controller.seekTo(const Duration(seconds: 3)); - expect(await _controller.position, const Duration(seconds: 3)); + expect(await controller.position, const Duration(seconds: 3)); }); testWidgets('can be paused', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); // Play for a second, then pause, and then wait a second. - await _controller.play(); + await controller.play(); await tester.pumpAndSettle(_playDuration); - await _controller.pause(); + await controller.pause(); await tester.pumpAndSettle(_playDuration); - final Duration pausedPosition = (await _controller.position)!; + final Duration pausedPosition = (await controller.position)!; await tester.pumpAndSettle(_playDuration); // Verify that we stopped playing after the pause. - expect(await _controller.position, pausedPosition); + expect(await controller.position, pausedPosition); }); }); @@ -106,48 +106,48 @@ void main() { final File file = File('$tempDir/$filename'); await file.writeAsBytes(bytes.buffer.asInt8List()); - _controller = MiniController.file(file); + controller = MiniController.file(file); }); testWidgets('test video player using static file() method as constructor', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); - await _controller.play(); + await controller.play(); await tester.pumpAndSettle(_playDuration); - expect(await _controller.position, greaterThan(Duration.zero)); + expect(await controller.position, greaterThan(Duration.zero)); }); }); group('network videos', () { setUp(() { final String videoUrl = getUrlForAssetAsNetworkSource(_videoAssetKey); - _controller = MiniController.network(videoUrl); + controller = MiniController.network(videoUrl); }); testWidgets('reports buffering status', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); final Completer started = Completer(); final Completer ended = Completer(); - _controller.addListener(() { - if (!started.isCompleted && _controller.value.isBuffering) { + controller.addListener(() { + if (!started.isCompleted && controller.value.isBuffering) { started.complete(); } if (started.isCompleted && - !_controller.value.isBuffering && + !controller.value.isBuffering && !ended.isCompleted) { ended.complete(); } }); - await _controller.play(); - await _controller.seekTo(const Duration(seconds: 5)); + await controller.play(); + await controller.seekTo(const Duration(seconds: 5)); await tester.pumpAndSettle(_playDuration); - await _controller.pause(); + await controller.pause(); - expect(await _controller.position, greaterThan(Duration.zero)); + expect(await controller.position, greaterThan(Duration.zero)); await expectLater(started.future, completes); await expectLater(ended.future, completes); diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index cc4411c8c651..7cfa025271cf 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates code for `no_leading_underscores_for_local_identifiers` lint. + ## 2.3.6 * Fixes a bug in iOS 16 where videos from protected live streams are not shown. diff --git a/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart b/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart index 15108aa0713a..5027973a660d 100644 --- a/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart +++ b/packages/video_player/video_player_avfoundation/example/integration_test/video_player_test.dart @@ -39,12 +39,12 @@ String getUrlForAssetAsNetworkSource(String assetKey) { void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - late MiniController _controller; - tearDown(() async => _controller.dispose()); + late MiniController controller; + tearDown(() async => controller.dispose()); group('asset videos', () { setUp(() { - _controller = MiniController.asset(_videoAssetKey); + controller = MiniController.asset(_videoAssetKey); }); testWidgets('registers expected implementation', @@ -54,50 +54,50 @@ void main() { }); testWidgets('can be initialized', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); - expect(_controller.value.isInitialized, true); - expect(await _controller.position, Duration.zero); - expect(_controller.value.duration, + expect(controller.value.isInitialized, true); + expect(await controller.position, Duration.zero); + expect(controller.value.duration, const Duration(seconds: 7, milliseconds: 540)); }); testWidgets('can be played', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); - await _controller.play(); + await controller.play(); await tester.pumpAndSettle(_playDuration); - expect(await _controller.position, greaterThan(Duration.zero)); + expect(await controller.position, greaterThan(Duration.zero)); }); testWidgets('can seek', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); - await _controller.seekTo(const Duration(seconds: 3)); + await controller.seekTo(const Duration(seconds: 3)); // TODO(stuartmorgan): Switch to _controller.position once seekTo is // fixed on the native side to wait for completion, so this is testing // the native code rather than the MiniController position cache. - expect(_controller.value.position, const Duration(seconds: 3)); + expect(controller.value.position, const Duration(seconds: 3)); }); testWidgets('can be paused', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); // Play for a second, then pause, and then wait a second. - await _controller.play(); + await controller.play(); await tester.pumpAndSettle(_playDuration); - await _controller.pause(); - final Duration pausedPosition = (await _controller.position)!; + await controller.pause(); + final Duration pausedPosition = (await controller.position)!; await tester.pumpAndSettle(_playDuration); // Verify that we stopped playing after the pause. // TODO(stuartmorgan): Investigate why this has a slight discrepency, and // fix it if possible. Is AVPlayer's pause method internally async? const Duration allowableDelta = Duration(milliseconds: 10); - expect(await _controller.position, - lessThan(pausedPosition + allowableDelta)); + expect( + await controller.position, lessThan(pausedPosition + allowableDelta)); }); }); @@ -112,51 +112,51 @@ void main() { final File file = File('$tempDir/$filename'); await file.writeAsBytes(bytes.buffer.asInt8List()); - _controller = MiniController.file(file); + controller = MiniController.file(file); }); testWidgets('test video player using static file() method as constructor', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); - await _controller.play(); + await controller.play(); await tester.pumpAndSettle(_playDuration); - expect(await _controller.position, greaterThan(Duration.zero)); + expect(await controller.position, greaterThan(Duration.zero)); }); }); group('network videos', () { setUp(() { final String videoUrl = getUrlForAssetAsNetworkSource(_videoAssetKey); - _controller = MiniController.network(videoUrl); + controller = MiniController.network(videoUrl); }); testWidgets('reports buffering status', (WidgetTester tester) async { - await _controller.initialize(); + await controller.initialize(); final Completer started = Completer(); final Completer ended = Completer(); - _controller.addListener(() { - if (!started.isCompleted && _controller.value.isBuffering) { + controller.addListener(() { + if (!started.isCompleted && controller.value.isBuffering) { started.complete(); } if (started.isCompleted && - !_controller.value.isBuffering && + !controller.value.isBuffering && !ended.isCompleted) { ended.complete(); } }); - await _controller.play(); - await _controller.seekTo(const Duration(seconds: 5)); + await controller.play(); + await controller.seekTo(const Duration(seconds: 5)); await tester.pumpAndSettle(_playDuration); - await _controller.pause(); + await controller.pause(); // TODO(stuartmorgan): Switch to _controller.position once seekTo is // fixed on the native side to wait for completion, so this is testing // the native code rather than the MiniController position cache. - expect(_controller.value.position, greaterThan(Duration.zero)); + expect(controller.value.position, greaterThan(Duration.zero)); await expectLater(started.future, completes); await expectLater(ended.future, completes); diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 73dd7ced0279..d5559f9ca7f7 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,5 +1,6 @@ ## NEXT +* Updates code for `no_leading_underscores_for_local_identifiers` lint. * Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index 8e326fef182f..63f43848508b 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -235,12 +235,12 @@ Future main() async { testWidgets('set custom userAgent', (WidgetTester tester) async { final Completer controllerCompleter1 = Completer(); - final GlobalKey _globalKey = GlobalKey(); + final GlobalKey globalKey = GlobalKey(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( - key: _globalKey, + key: globalKey, initialUrl: 'about:blank', javascriptMode: JavascriptMode.unrestricted, userAgent: 'Custom_User_Agent1', @@ -258,7 +258,7 @@ Future main() async { Directionality( textDirection: TextDirection.ltr, child: WebView( - key: _globalKey, + key: globalKey, initialUrl: 'about:blank', javascriptMode: JavascriptMode.unrestricted, userAgent: 'Custom_User_Agent2', @@ -274,13 +274,13 @@ Future main() async { (WidgetTester tester) async { final Completer controllerCompleter = Completer(); - final GlobalKey _globalKey = GlobalKey(); + final GlobalKey globalKey = GlobalKey(); // Build the webView with no user agent to get the default platform user agent. await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( - key: _globalKey, + key: globalKey, initialUrl: primaryUrl, javascriptMode: JavascriptMode.unrestricted, onWebViewCreated: (WebViewController controller) { @@ -296,7 +296,7 @@ Future main() async { Directionality( textDirection: TextDirection.ltr, child: WebView( - key: _globalKey, + key: globalKey, initialUrl: 'about:blank', javascriptMode: JavascriptMode.unrestricted, userAgent: 'Custom_User_Agent', @@ -310,7 +310,7 @@ Future main() async { Directionality( textDirection: TextDirection.ltr, child: WebView( - key: _globalKey, + key: globalKey, initialUrl: 'about:blank', javascriptMode: JavascriptMode.unrestricted, ), diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 4853aec23f9a..dfad03ebaa15 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates code for `no_leading_underscores_for_local_identifiers` lint. + ## 2.10.3 * Updates imports for `prefer_relative_imports`. 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 b7c32f5d77d3..69c1a46d750f 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 @@ -241,12 +241,12 @@ Future main() async { testWidgets('set custom userAgent', (WidgetTester tester) async { final Completer controllerCompleter1 = Completer(); - final GlobalKey _globalKey = GlobalKey(); + final GlobalKey globalKey = GlobalKey(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( - key: _globalKey, + key: globalKey, initialUrl: 'about:blank', javascriptMode: JavascriptMode.unrestricted, userAgent: 'Custom_User_Agent1', @@ -264,7 +264,7 @@ Future main() async { Directionality( textDirection: TextDirection.ltr, child: WebView( - key: _globalKey, + key: globalKey, initialUrl: 'about:blank', javascriptMode: JavascriptMode.unrestricted, userAgent: 'Custom_User_Agent2', @@ -280,13 +280,13 @@ Future main() async { (WidgetTester tester) async { final Completer controllerCompleter = Completer(); - final GlobalKey _globalKey = GlobalKey(); + final GlobalKey globalKey = GlobalKey(); // Build the webView with no user agent to get the default platform user agent. await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( - key: _globalKey, + key: globalKey, initialUrl: primaryUrl, javascriptMode: JavascriptMode.unrestricted, onWebViewCreated: (WebViewController controller) { @@ -302,7 +302,7 @@ Future main() async { Directionality( textDirection: TextDirection.ltr, child: WebView( - key: _globalKey, + key: globalKey, initialUrl: 'about:blank', javascriptMode: JavascriptMode.unrestricted, userAgent: 'Custom_User_Agent', @@ -316,7 +316,7 @@ Future main() async { Directionality( textDirection: TextDirection.ltr, child: WebView( - key: _globalKey, + key: globalKey, initialUrl: 'about:blank', javascriptMode: JavascriptMode.unrestricted, ), diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index e8eb01e6590e..b7050e4f4db3 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.9.5 + +* Updates code for `no_leading_underscores_for_local_identifiers` lint. + ## 1.9.4 * Updates imports for `prefer_relative_imports`. diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart index f32881701cfb..0e98ea08fd16 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart @@ -247,30 +247,29 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController { static Map _webSettingsToMap(WebSettings? settings) { final Map map = {}; - void _addIfNonNull(String key, dynamic value) { + void addIfNonNull(String key, dynamic value) { if (value == null) { return; } map[key] = value; } - void _addSettingIfPresent(String key, WebSetting setting) { + void addSettingIfPresent(String key, WebSetting setting) { if (!setting.isPresent) { return; } map[key] = setting.value; } - _addIfNonNull('jsMode', settings!.javascriptMode?.index); - _addIfNonNull('hasNavigationDelegate', settings.hasNavigationDelegate); - _addIfNonNull('hasProgressTracking', settings.hasProgressTracking); - _addIfNonNull('debuggingEnabled', settings.debuggingEnabled); - _addIfNonNull( - 'gestureNavigationEnabled', settings.gestureNavigationEnabled); - _addIfNonNull( + addIfNonNull('jsMode', settings!.javascriptMode?.index); + addIfNonNull('hasNavigationDelegate', settings.hasNavigationDelegate); + addIfNonNull('hasProgressTracking', settings.hasProgressTracking); + addIfNonNull('debuggingEnabled', settings.debuggingEnabled); + addIfNonNull('gestureNavigationEnabled', settings.gestureNavigationEnabled); + addIfNonNull( 'allowsInlineMediaPlayback', settings.allowsInlineMediaPlayback); - _addSettingIfPresent('userAgent', settings.userAgent); - _addIfNonNull('zoomEnabled', settings.zoomEnabled); + addSettingIfPresent('userAgent', settings.userAgent); + addIfNonNull('zoomEnabled', settings.zoomEnabled); return map; } diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml index 4b148725ff8f..8f60592b852d 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutte issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview_flutter%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.9.4 +version: 1.9.5 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/platform_interface/javascript_channel_registry_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/platform_interface/javascript_channel_registry_test.dart index 30795b01c83f..aec568e92b79 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/test/src/platform_interface/javascript_channel_registry_test.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/platform_interface/javascript_channel_registry_test.dart @@ -7,35 +7,35 @@ import 'package:webview_flutter_platform_interface/src/platform_interface/javasc import 'package:webview_flutter_platform_interface/src/types/types.dart'; void main() { - final Map _log = {}; - final Set _channels = { + final Map log = {}; + final Set channels = { JavascriptChannel( name: 'js_channel_1', onMessageReceived: (JavascriptMessage message) => - _log['js_channel_1'] = message.message, + log['js_channel_1'] = message.message, ), JavascriptChannel( name: 'js_channel_2', onMessageReceived: (JavascriptMessage message) => - _log['js_channel_2'] = message.message, + log['js_channel_2'] = message.message, ), JavascriptChannel( name: 'js_channel_3', onMessageReceived: (JavascriptMessage message) => - _log['js_channel_3'] = message.message, + log['js_channel_3'] = message.message, ), }; tearDown(() { - _log.clear(); + log.clear(); }); test('ctor should initialize with channels.', () { final JavascriptChannelRegistry registry = - JavascriptChannelRegistry(_channels); + JavascriptChannelRegistry(channels); expect(registry.channels.length, 3); - for (final JavascriptChannel channel in _channels) { + for (final JavascriptChannel channel in channels) { expect(registry.channels[channel.name], channel); } }); @@ -43,7 +43,7 @@ void main() { test('onJavascriptChannelMessage should forward message on correct channel.', () { final JavascriptChannelRegistry registry = - JavascriptChannelRegistry(_channels); + JavascriptChannelRegistry(channels); registry.onJavascriptChannelMessage( 'js_channel_2', @@ -51,7 +51,7 @@ void main() { ); expect( - _log, + log, containsPair( 'js_channel_2', 'test message on channel 2', @@ -62,7 +62,7 @@ void main() { 'onJavascriptChannelMessage should throw ArgumentError when message arrives on non-existing channel.', () { final JavascriptChannelRegistry registry = - JavascriptChannelRegistry(_channels); + JavascriptChannelRegistry(channels); expect( () => registry.onJavascriptChannelMessage( @@ -79,7 +79,7 @@ void main() { 'updateJavascriptChannelsFromSet should clear all channels when null is supplied.', () { final JavascriptChannelRegistry registry = - JavascriptChannelRegistry(_channels); + JavascriptChannelRegistry(channels); expect(registry.channels.length, 3); @@ -91,7 +91,7 @@ void main() { test('updateJavascriptChannelsFromSet should update registry with new set.', () { final JavascriptChannelRegistry registry = - JavascriptChannelRegistry(_channels); + JavascriptChannelRegistry(channels); expect(registry.channels.length, 3); @@ -99,12 +99,12 @@ void main() { JavascriptChannel( name: 'new_js_channel_1', onMessageReceived: (JavascriptMessage message) => - _log['new_js_channel_1'] = message.message, + log['new_js_channel_1'] = message.message, ), JavascriptChannel( name: 'new_js_channel_2', onMessageReceived: (JavascriptMessage message) => - _log['new_js_channel_2'] = message.message, + log['new_js_channel_2'] = message.message, ), }; diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/types/javascript_channel_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/types/javascript_channel_test.dart index f481edda1edd..8d7177150b7c 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/test/src/types/javascript_channel_test.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/types/javascript_channel_test.dart @@ -6,17 +6,17 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:webview_flutter_platform_interface/src/types/javascript_channel.dart'; void main() { - final List _validChars = + final List validChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_'.split(''); - final List _commonInvalidChars = + final List commonInvalidChars = r'`~!@#$%^&*()-=+[]{}\|"' ':;/?<>,. '.split(''); - final List _digits = List.generate(10, (int index) => index++); + final List digits = List.generate(10, (int index) => index++); test( 'ctor should create JavascriptChannel when name starts with a valid character followed by a number.', () { - for (final String char in _validChars) { - for (final int digit in _digits) { + for (final String char in validChars) { + for (final int digit in digits) { final JavascriptChannel channel = JavascriptChannel(name: '$char$digit', onMessageReceived: (_) {}); @@ -26,7 +26,7 @@ void main() { }); test('ctor should assert when channel name starts with a number.', () { - for (final int i in _digits) { + for (final int i in digits) { expect( () => JavascriptChannel(name: '$i', onMessageReceived: (_) {}), throwsAssertionError, @@ -35,8 +35,8 @@ void main() { }); test('ctor should assert when channel contains invalid char.', () { - for (final String validChar in _validChars) { - for (final String invalidChar in _commonInvalidChars) { + for (final String validChar in validChars) { + for (final String invalidChar in commonInvalidChars) { expect( () => JavascriptChannel( name: validChar + invalidChar, onMessageReceived: (_) {}), diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 318ffe65593a..c0a2ade72534 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Updates code for `no_leading_underscores_for_local_identifiers` lint. + ## 2.9.5 * Updates imports for `prefer_relative_imports`. 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 e259bc72a67b..047d69f0d0ee 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 @@ -253,12 +253,12 @@ Future main() async { testWidgets('set custom userAgent', (WidgetTester tester) async { final Completer controllerCompleter1 = Completer(); - final GlobalKey _globalKey = GlobalKey(); + final GlobalKey globalKey = GlobalKey(); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( - key: _globalKey, + key: globalKey, initialUrl: 'about:blank', javascriptMode: JavascriptMode.unrestricted, userAgent: 'Custom_User_Agent1', @@ -276,7 +276,7 @@ Future main() async { Directionality( textDirection: TextDirection.ltr, child: WebView( - key: _globalKey, + key: globalKey, initialUrl: 'about:blank', javascriptMode: JavascriptMode.unrestricted, userAgent: 'Custom_User_Agent2', @@ -292,13 +292,13 @@ Future main() async { (WidgetTester tester) async { final Completer controllerCompleter = Completer(); - final GlobalKey _globalKey = GlobalKey(); + final GlobalKey globalKey = GlobalKey(); // Build the webView with no user agent to get the default platform user agent. await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( - key: _globalKey, + key: globalKey, initialUrl: primaryUrl, javascriptMode: JavascriptMode.unrestricted, onWebViewCreated: (WebViewController controller) { @@ -314,7 +314,7 @@ Future main() async { Directionality( textDirection: TextDirection.ltr, child: WebView( - key: _globalKey, + key: globalKey, initialUrl: 'about:blank', javascriptMode: JavascriptMode.unrestricted, userAgent: 'Custom_User_Agent', @@ -328,7 +328,7 @@ Future main() async { Directionality( textDirection: TextDirection.ltr, child: WebView( - key: _globalKey, + key: globalKey, initialUrl: 'about:blank', javascriptMode: JavascriptMode.unrestricted, ), diff --git a/script/tool/lib/src/common/package_looping_command.dart b/script/tool/lib/src/common/package_looping_command.dart index a32ada2c35e8..d8b1cf001d13 100644 --- a/script/tool/lib/src/common/package_looping_command.dart +++ b/script/tool/lib/src/common/package_looping_command.dart @@ -428,9 +428,9 @@ abstract class PackageLoopingCommand extends PackageCommand { .length; // Split the warnings into those from packages that ran, and those that // were skipped. - final Set _skippedPackagesWithWarnings = + final Set skippedPackagesWithWarnings = _packagesWithWarnings.intersection(skippedPackages); - final int skippedWarningCount = _skippedPackagesWithWarnings.length; + final int skippedWarningCount = skippedPackagesWithWarnings.length; final int runWarningCount = _packagesWithWarnings.length - skippedWarningCount; diff --git a/script/tool/lib/src/native_test_command.dart b/script/tool/lib/src/native_test_command.dart index 81b13cbb75e2..af5f4df98e86 100644 --- a/script/tool/lib/src/native_test_command.dart +++ b/script/tool/lib/src/native_test_command.dart @@ -406,9 +406,9 @@ this command. ); // The exit code from 'xcodebuild test' when there are no tests. - const int _xcodebuildNoTestExitCode = 66; + const int xcodebuildNoTestExitCode = 66; switch (exitCode) { - case _xcodebuildNoTestExitCode: + case xcodebuildNoTestExitCode: _printNoExampleTestsMessage(example, platform); break; case 0: diff --git a/script/tool/test/dependabot_check_command_test.dart b/script/tool/test/dependabot_check_command_test.dart index b0558c3d22ac..39dd8f4fcb92 100644 --- a/script/tool/test/dependabot_check_command_test.dart +++ b/script/tool/test/dependabot_check_command_test.dart @@ -36,7 +36,7 @@ void main() { runner.addCommand(command); }); - void _setDependabotCoverage({ + void setDependabotCoverage({ Iterable gradleDirs = const [], }) { final Iterable gradleEntries = @@ -57,7 +57,7 @@ ${gradleEntries.join('\n')} } test('skips with no supported ecosystems', () async { - _setDependabotCoverage(); + setDependabotCoverage(); createFakePackage('a_package', packagesDir); final List output = @@ -71,7 +71,7 @@ ${gradleEntries.join('\n')} }); test('fails for app missing Gradle coverage', () async { - _setDependabotCoverage(); + setDependabotCoverage(); final RepositoryPackage package = createFakePackage('a_package', packagesDir); package.directory @@ -97,7 +97,7 @@ ${gradleEntries.join('\n')} }); test('fails for plugin missing Gradle coverage', () async { - _setDependabotCoverage(); + setDependabotCoverage(); final RepositoryPackage plugin = createFakePlugin('a_plugin', packagesDir); plugin.directory.childDirectory('android').createSync(recursive: true); @@ -118,7 +118,7 @@ ${gradleEntries.join('\n')} }); test('passes for correct Gradle coverage', () async { - _setDependabotCoverage(gradleDirs: [ + setDependabotCoverage(gradleDirs: [ 'packages/a_plugin/android', 'packages/a_plugin/example/android/app', ]); diff --git a/script/tool/test/firebase_test_lab_command_test.dart b/script/tool/test/firebase_test_lab_command_test.dart index 2d3175e171e0..68ea62b2334f 100644 --- a/script/tool/test/firebase_test_lab_command_test.dart +++ b/script/tool/test/firebase_test_lab_command_test.dart @@ -40,7 +40,7 @@ void main() { runner.addCommand(command); }); - void _writeJavaTestFile(RepositoryPackage plugin, String relativeFilePath, + void writeJavaTestFile(RepositoryPackage plugin, String relativeFilePath, {String runnerClass = 'FlutterTestRunner'}) { childFileWithSubcomponents( plugin.directory, p.posix.split(relativeFilePath)) @@ -67,7 +67,7 @@ public class MainActivityTest { 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(plugin, javaTestFileRelativePath); + writeJavaTestFile(plugin, javaTestFileRelativePath); Error? commandError; final List output = await runCapturingPrint( @@ -97,7 +97,7 @@ public class MainActivityTest { 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(plugin, javaTestFileRelativePath); + writeJavaTestFile(plugin, javaTestFileRelativePath); final List output = await runCapturingPrint(runner, ['firebase-test-lab']); @@ -120,7 +120,7 @@ public class MainActivityTest { 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(plugin1, javaTestFileRelativePath); + writeJavaTestFile(plugin1, javaTestFileRelativePath); final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir, extraFiles: [ 'test/plugin_test.dart', @@ -128,7 +128,7 @@ public class MainActivityTest { 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(plugin2, javaTestFileRelativePath); + writeJavaTestFile(plugin2, javaTestFileRelativePath); final List output = await runCapturingPrint(runner, [ 'firebase-test-lab', @@ -207,7 +207,7 @@ public class MainActivityTest { 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(plugin, javaTestFileRelativePath); + writeJavaTestFile(plugin, javaTestFileRelativePath); final List output = await runCapturingPrint(runner, [ 'firebase-test-lab', @@ -286,7 +286,7 @@ public class MainActivityTest { ], ]); for (final String example in examples) { - _writeJavaTestFile( + writeJavaTestFile( plugin, 'example/$example/$javaTestFileExampleRelativePath'); } @@ -347,7 +347,7 @@ public class MainActivityTest { 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(plugin, javaTestFileRelativePath); + writeJavaTestFile(plugin, javaTestFileRelativePath); processRunner.mockProcessesForExecutable['gcloud'] = [ MockProcess(), // auth @@ -393,7 +393,7 @@ public class MainActivityTest { 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(plugin, javaTestFileRelativePath); + writeJavaTestFile(plugin, javaTestFileRelativePath); processRunner.mockProcessesForExecutable['gcloud'] = [ MockProcess(), // auth @@ -460,7 +460,7 @@ public class MainActivityTest { 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(plugin, javaTestFileRelativePath); + writeJavaTestFile(plugin, javaTestFileRelativePath); Error? commandError; final List output = await runCapturingPrint( @@ -501,7 +501,7 @@ public class MainActivityTest { javaTestFileRelativePath, ]); // Use the wrong @RunWith annotation. - _writeJavaTestFile(plugin, javaTestFileRelativePath, + writeJavaTestFile(plugin, javaTestFileRelativePath, runnerClass: 'AndroidJUnit4.class'); Error? commandError; @@ -565,7 +565,7 @@ public class MainActivityTest { 'example/integration_test/foo_test.dart', javaTestFileRelativePath, ]); - _writeJavaTestFile(plugin, javaTestFileRelativePath); + writeJavaTestFile(plugin, javaTestFileRelativePath); final List output = await runCapturingPrint(runner, [ 'firebase-test-lab', @@ -628,7 +628,7 @@ public class MainActivityTest { 'example/integration_test/foo_test.dart', javaTestFileRelativePath, ]); - _writeJavaTestFile(plugin, javaTestFileRelativePath); + writeJavaTestFile(plugin, javaTestFileRelativePath); processRunner.mockProcessesForExecutable['flutter'] = [ MockProcess(exitCode: 1) // flutter build @@ -663,7 +663,7 @@ public class MainActivityTest { 'example/integration_test/foo_test.dart', javaTestFileRelativePath, ]); - _writeJavaTestFile(plugin, javaTestFileRelativePath); + writeJavaTestFile(plugin, javaTestFileRelativePath); final String gradlewPath = plugin .getExamples() @@ -704,7 +704,7 @@ public class MainActivityTest { 'example/integration_test/foo_test.dart', javaTestFileRelativePath, ]); - _writeJavaTestFile(plugin, javaTestFileRelativePath); + writeJavaTestFile(plugin, javaTestFileRelativePath); final String gradlewPath = plugin .getExamples() @@ -750,7 +750,7 @@ public class MainActivityTest { 'example/android/gradlew', javaTestFileRelativePath, ]); - _writeJavaTestFile(plugin, javaTestFileRelativePath); + writeJavaTestFile(plugin, javaTestFileRelativePath); await runCapturingPrint(runner, [ 'firebase-test-lab', diff --git a/script/tool/test/format_command_test.dart b/script/tool/test/format_command_test.dart index c1c4a212a019..9a865053a2b6 100644 --- a/script/tool/test/format_command_test.dart +++ b/script/tool/test/format_command_test.dart @@ -49,7 +49,7 @@ void main() { /// Returns a modified version of a list of [relativePaths] that are relative /// to [package] to instead be relative to [packagesDir]. - List _getPackagesDirRelativePaths( + List getPackagesDirRelativePaths( RepositoryPackage package, List relativePaths) { final p.Context path = analyzeCommand.path; final String relativeBase = @@ -65,7 +65,7 @@ void main() { /// /// This is for each of testing batching, since it means each file will /// consume 100 characters of the batch length. - List _get99CharacterPathExtraFiles(String packageName, int count) { + List get99CharacterPathExtraFiles(String packageName, int count) { final int padding = 99 - packageName.length - 1 - // the path separator after the package name @@ -99,10 +99,7 @@ void main() { orderedEquals([ ProcessCall( getFlutterCommand(mockPlatform), - [ - 'format', - ..._getPackagesDirRelativePaths(plugin, files) - ], + ['format', ...getPackagesDirRelativePaths(plugin, files)], packagesDir.path), ])); }); @@ -138,7 +135,7 @@ void main() { getFlutterCommand(mockPlatform), [ 'format', - ..._getPackagesDirRelativePaths(plugin, formattedFiles) + ...getPackagesDirRelativePaths(plugin, formattedFiles) ], packagesDir.path), ])); @@ -191,7 +188,7 @@ void main() { '-jar', javaFormatPath, '--replace', - ..._getPackagesDirRelativePaths(plugin, files) + ...getPackagesDirRelativePaths(plugin, files) ], packagesDir.path), ])); @@ -271,7 +268,7 @@ void main() { '-jar', javaFormatPath, '--replace', - ..._getPackagesDirRelativePaths(plugin, files) + ...getPackagesDirRelativePaths(plugin, files) ], packagesDir.path), ])); @@ -303,7 +300,7 @@ void main() { [ '-i', '--style=file', - ..._getPackagesDirRelativePaths(plugin, files) + ...getPackagesDirRelativePaths(plugin, files) ], packagesDir.path), ])); @@ -358,7 +355,7 @@ void main() { [ '-i', '--style=file', - ..._getPackagesDirRelativePaths(plugin, files) + ...getPackagesDirRelativePaths(plugin, files) ], packagesDir.path), ])); @@ -426,14 +423,14 @@ void main() { [ '-i', '--style=file', - ..._getPackagesDirRelativePaths(plugin, clangFiles) + ...getPackagesDirRelativePaths(plugin, clangFiles) ], packagesDir.path), ProcessCall( getFlutterCommand(mockPlatform), [ 'format', - ..._getPackagesDirRelativePaths(plugin, dartFiles) + ...getPackagesDirRelativePaths(plugin, dartFiles) ], packagesDir.path), ProcessCall( @@ -442,7 +439,7 @@ void main() { '-jar', javaFormatPath, '--replace', - ..._getPackagesDirRelativePaths(plugin, javaFiles) + ...getPackagesDirRelativePaths(plugin, javaFiles) ], packagesDir.path), ])); @@ -541,7 +538,7 @@ void main() { // Make the file list one file longer than would fit in the batch. final List batch1 = - _get99CharacterPathExtraFiles(pluginName, batchSize + 1); + get99CharacterPathExtraFiles(pluginName, batchSize + 1); final String extraFile = batch1.removeLast(); createFakePlugin( @@ -578,7 +575,7 @@ void main() { // Make the file list one file longer than would fit in a Windows batch. final List batch = - _get99CharacterPathExtraFiles(pluginName, batchSize + 1); + get99CharacterPathExtraFiles(pluginName, batchSize + 1); createFakePlugin( pluginName, @@ -598,7 +595,7 @@ void main() { // Make the file list one file longer than would fit in the batch. final List batch1 = - _get99CharacterPathExtraFiles(pluginName, batchSize + 1); + get99CharacterPathExtraFiles(pluginName, batchSize + 1); final String extraFile = batch1.removeLast(); createFakePlugin( diff --git a/script/tool/test/license_check_command_test.dart b/script/tool/test/license_check_command_test.dart index 005b77d99a13..09841df74e70 100644 --- a/script/tool/test/license_check_command_test.dart +++ b/script/tool/test/license_check_command_test.dart @@ -48,7 +48,7 @@ void main() { /// [commentString] is added to the start of each line. /// [prefix] is added to the start of the entire block. /// [suffix] is added to the end of the entire block. - void _writeLicense( + void writeLicense( File file, { String comment = '// ', String prefix = '', @@ -164,7 +164,7 @@ void main() { test('passes if all checked files have license blocks', () async { final File checked = root.childFile('checked.cc'); checked.createSync(); - _writeLicense(checked); + writeLicense(checked); final File notChecked = root.childFile('not_checked.md'); notChecked.createSync(); @@ -183,7 +183,7 @@ void main() { test('passes correct license blocks on Windows', () async { final File checked = root.childFile('checked.cc'); checked.createSync(); - _writeLicense(checked, useCrlf: true); + writeLicense(checked, useCrlf: true); final List output = await runCapturingPrint(runner, ['license-check']); @@ -200,13 +200,13 @@ void main() { test('handles the comment styles for all supported languages', () async { final File fileA = root.childFile('file_a.cc'); fileA.createSync(); - _writeLicense(fileA); + writeLicense(fileA); final File fileB = root.childFile('file_b.sh'); fileB.createSync(); - _writeLicense(fileB, comment: '# '); + writeLicense(fileB, comment: '# '); final File fileC = root.childFile('file_c.html'); fileC.createSync(); - _writeLicense(fileC, comment: '', prefix: ''); + writeLicense(fileC, comment: '', prefix: ''); final List output = await runCapturingPrint(runner, ['license-check']); @@ -225,10 +225,10 @@ void main() { test('fails if any checked files are missing license blocks', () async { final File goodA = root.childFile('good.cc'); goodA.createSync(); - _writeLicense(goodA); + writeLicense(goodA); final File goodB = root.childFile('good.h'); goodB.createSync(); - _writeLicense(goodB); + writeLicense(goodB); root.childFile('bad.cc').createSync(); root.childFile('bad.h').createSync(); @@ -255,10 +255,10 @@ void main() { test('fails if any checked files are missing just the copyright', () async { final File good = root.childFile('good.cc'); good.createSync(); - _writeLicense(good); + writeLicense(good); final File bad = root.childFile('bad.cc'); bad.createSync(); - _writeLicense(bad, copyright: ''); + writeLicense(bad, copyright: ''); Error? commandError; final List output = await runCapturingPrint( @@ -282,10 +282,10 @@ void main() { test('fails if any checked files are missing just the license', () async { final File good = root.childFile('good.cc'); good.createSync(); - _writeLicense(good); + writeLicense(good); final File bad = root.childFile('bad.cc'); bad.createSync(); - _writeLicense(bad, license: []); + writeLicense(bad, license: []); Error? commandError; final List output = await runCapturingPrint( @@ -310,7 +310,7 @@ void main() { () async { final File thirdPartyFile = root.childFile('third_party.cc'); thirdPartyFile.createSync(); - _writeLicense(thirdPartyFile, copyright: 'Copyright 2017 Someone Else'); + writeLicense(thirdPartyFile, copyright: 'Copyright 2017 Someone Else'); Error? commandError; final List output = await runCapturingPrint( @@ -339,7 +339,7 @@ void main() { .childDirectory('third_party') .childFile('file.cc'); thirdPartyFile.createSync(recursive: true); - _writeLicense(thirdPartyFile, + writeLicense(thirdPartyFile, copyright: 'Copyright 2017 Workiva Inc.', license: [ 'Licensed under the Apache License, Version 2.0 (the "License");', @@ -366,7 +366,7 @@ void main() { .childDirectory('third_party') .childFile('first_party.cc'); firstPartyFileInThirdParty.createSync(recursive: true); - _writeLicense(firstPartyFileInThirdParty); + writeLicense(firstPartyFileInThirdParty); final List output = await runCapturingPrint(runner, ['license-check']); @@ -383,10 +383,10 @@ void main() { test('fails for licenses that the tool does not expect', () async { final File good = root.childFile('good.cc'); good.createSync(); - _writeLicense(good); + writeLicense(good); final File bad = root.childDirectory('third_party').childFile('bad.cc'); bad.createSync(recursive: true); - _writeLicense(bad, license: [ + writeLicense(bad, license: [ 'This program is free software: you can redistribute it and/or modify', 'it under the terms of the GNU General Public License', ]); @@ -414,10 +414,10 @@ void main() { () async { final File good = root.childFile('good.cc'); good.createSync(); - _writeLicense(good); + writeLicense(good); final File bad = root.childDirectory('third_party').childFile('bad.cc'); bad.createSync(recursive: true); - _writeLicense( + writeLicense( bad, copyright: 'Copyright 2017 Some New Authors.', license: [ diff --git a/script/tool/test/make_deps_path_based_command_test.dart b/script/tool/test/make_deps_path_based_command_test.dart index 36753e8001f8..e846a63fc68e 100644 --- a/script/tool/test/make_deps_path_based_command_test.dart +++ b/script/tool/test/make_deps_path_based_command_test.dart @@ -49,7 +49,7 @@ void main() { /// Adds dummy 'dependencies:' entries for each package in [dependencies] /// to [package]. - void _addDependencies( + void addDependencies( RepositoryPackage package, Iterable dependencies) { final List lines = package.pubspecFile.readAsLinesSync(); final int dependenciesStartIndex = lines.indexOf('dependencies:'); @@ -62,7 +62,7 @@ void main() { /// Adds a 'dev_dependencies:' section with entries for each package in /// [dependencies] to [package]. - void _addDevDependenciesSection( + void addDevDependenciesSection( RepositoryPackage package, Iterable devDependencies) { final String originalContent = package.pubspecFile.readAsStringSync(); package.pubspecFile.writeAsStringSync(''' @@ -77,7 +77,7 @@ ${devDependencies.map((String dep) => ' $dep: ^1.0.0').join('\n')} createFakePackage('foo', packagesDir, isFlutter: true); final RepositoryPackage packageBar = createFakePackage('bar', packagesDir, isFlutter: true); - _addDependencies(packageBar, ['foo']); + addDependencies(packageBar, ['foo']); final String originalPubspecContents = packageBar.pubspecFile.readAsStringSync(); @@ -105,16 +105,16 @@ ${devDependencies.map((String dep) => ' $dep: ^1.0.0').join('\n')} final RepositoryPackage pluginAppFacing = createFakePlugin('bar', pluginGroup); - _addDependencies(simplePackage, [ + addDependencies(simplePackage, [ 'bar', 'bar_android', 'bar_platform_interface', ]); - _addDependencies(pluginAppFacing, [ + addDependencies(pluginAppFacing, [ 'bar_platform_interface', 'bar_android', ]); - _addDependencies(pluginImplementation, [ + addDependencies(pluginImplementation, [ 'bar_platform_interface', ]); @@ -160,7 +160,7 @@ ${devDependencies.map((String dep) => ' $dep: ^1.0.0').join('\n')} final RepositoryPackage builderPackage = createFakePackage('foo_builder', packagesDir); - _addDevDependenciesSection(builderPackage, [ + addDevDependenciesSection(builderPackage, [ 'foo', ]); @@ -193,8 +193,8 @@ ${devDependencies.map((String dep) => ' $dep: ^1.0.0').join('\n')} final RepositoryPackage targetPackage = createFakePackage('target', packagesDir); - _addDependencies(targetPackage, ['a', 'c']); - _addDevDependenciesSection(targetPackage, ['b']); + addDependencies(targetPackage, ['a', 'c']); + addDevDependenciesSection(targetPackage, ['b']); final List output = await runCapturingPrint(runner, ['make-deps-path-based', '--target-dependencies=c,a,b']); @@ -233,16 +233,16 @@ ${devDependencies.map((String dep) => ' $dep: ^1.0.0').join('\n')} final RepositoryPackage pluginAppFacing = createFakePlugin('bar', pluginGroup); - _addDependencies(simplePackage, [ + addDependencies(simplePackage, [ 'bar', 'bar_android', 'bar_platform_interface', ]); - _addDependencies(pluginAppFacing, [ + addDependencies(pluginAppFacing, [ 'bar_platform_interface', 'bar_android', ]); - _addDependencies(pluginImplementation, [ + addDependencies(pluginImplementation, [ 'bar_platform_interface', ]); diff --git a/script/tool/test/native_test_command_test.dart b/script/tool/test/native_test_command_test.dart index d420184b6125..f24d014bbfea 100644 --- a/script/tool/test/native_test_command_test.dart +++ b/script/tool/test/native_test_command_test.dart @@ -68,7 +68,7 @@ void _createFakeCMakeCache(RepositoryPackage plugin, Platform platform) { // TODO(stuartmorgan): Rework these tests to use a mock Xcode instead of // doing all the process mocking and validation. void main() { - const String _kDestination = '--ios-destination'; + const String kDestination = '--ios-destination'; group('test native_test_command on Posix', () { late FileSystem fileSystem; @@ -95,7 +95,7 @@ void main() { // Returns a MockProcess to provide for "xcrun xcodebuild -list" for a // project that contains [targets]. - MockProcess _getMockXcodebuildListProcess(List targets) { + MockProcess getMockXcodebuildListProcess(List targets) { final Map projects = { 'project': { 'targets': targets, @@ -106,7 +106,7 @@ void main() { // Returns the ProcessCall to expect for checking the targets present in // the [package]'s [platform]/Runner.xcodeproj. - ProcessCall _getTargetCheckCall(Directory package, String platform) { + ProcessCall getTargetCheckCall(Directory package, String platform) { return ProcessCall( 'xcrun', [ @@ -124,7 +124,7 @@ void main() { // Returns the ProcessCall to expect for running the tests in the // workspace [platform]/Runner.xcworkspace, with the given extra flags. - ProcessCall _getRunTestCall( + ProcessCall getRunTestCall( Directory package, String platform, { String? destination, @@ -150,7 +150,7 @@ void main() { // Returns the ProcessCall to expect for build the Linux unit tests for the // given plugin. - ProcessCall _getLinuxBuildCall(RepositoryPackage plugin) { + ProcessCall getLinuxBuildCall(RepositoryPackage plugin) { return ProcessCall( 'cmake', [ @@ -212,7 +212,7 @@ void main() { final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ - _getMockXcodebuildListProcess(['RunnerTests', 'RunnerUITests']), + getMockXcodebuildListProcess(['RunnerTests', 'RunnerUITests']), // Exit code 66 from testing indicates no tests. MockProcess(exitCode: 66), ]; @@ -229,8 +229,8 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getTargetCheckCall(pluginExampleDirectory, 'macos'), - _getRunTestCall(pluginExampleDirectory, 'macos', + getTargetCheckCall(pluginExampleDirectory, 'macos'), + getRunTestCall(pluginExampleDirectory, 'macos', extraFlags: ['-only-testing:RunnerUITests']), ])); }); @@ -243,7 +243,7 @@ void main() { }); final List output = await runCapturingPrint(runner, - ['native-test', '--ios', _kDestination, 'foo_destination']); + ['native-test', '--ios', kDestination, 'foo_destination']); expect( output, containsAllInOrder([ @@ -260,7 +260,7 @@ void main() { }); final List output = await runCapturingPrint(runner, - ['native-test', '--ios', _kDestination, 'foo_destination']); + ['native-test', '--ios', kDestination, 'foo_destination']); expect( output, containsAllInOrder([ @@ -279,14 +279,14 @@ void main() { final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ - _getMockXcodebuildListProcess( + getMockXcodebuildListProcess( ['RunnerTests', 'RunnerUITests']), ]; final List output = await runCapturingPrint(runner, [ 'native-test', '--ios', - _kDestination, + kDestination, 'foo_destination', ]); @@ -300,8 +300,8 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getTargetCheckCall(pluginExampleDirectory, 'ios'), - _getRunTestCall(pluginExampleDirectory, 'ios', + getTargetCheckCall(pluginExampleDirectory, 'ios'), + getRunTestCall(pluginExampleDirectory, 'ios', destination: 'foo_destination'), ])); }); @@ -316,7 +316,7 @@ void main() { processRunner.mockProcessesForExecutable['xcrun'] = [ MockProcess(stdout: jsonEncode(_kDeviceListMap)), // simctl - _getMockXcodebuildListProcess( + getMockXcodebuildListProcess( ['RunnerTests', 'RunnerUITests']), ]; @@ -336,8 +336,8 @@ void main() { '--json', ], null), - _getTargetCheckCall(pluginExampleDirectory, 'ios'), - _getRunTestCall(pluginExampleDirectory, 'ios', + getTargetCheckCall(pluginExampleDirectory, 'ios'), + getRunTestCall(pluginExampleDirectory, 'ios', destination: 'id=1E76A0FD-38AC-4537-A989-EA639D7D012A'), ])); }); @@ -386,7 +386,7 @@ void main() { final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ - _getMockXcodebuildListProcess( + getMockXcodebuildListProcess( ['RunnerTests', 'RunnerUITests']), ]; @@ -403,8 +403,8 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getTargetCheckCall(pluginExampleDirectory, 'macos'), - _getRunTestCall(pluginExampleDirectory, 'macos'), + getTargetCheckCall(pluginExampleDirectory, 'macos'), + getRunTestCall(pluginExampleDirectory, 'macos'), ])); }); }); @@ -918,7 +918,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getLinuxBuildCall(plugin), + getLinuxBuildCall(plugin), ProcessCall(testBinary.path, const [], null), ])); }); @@ -958,7 +958,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getLinuxBuildCall(plugin), + getLinuxBuildCall(plugin), ProcessCall(releaseTestBinary.path, const [], null), ])); }); @@ -1017,7 +1017,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getLinuxBuildCall(plugin), + getLinuxBuildCall(plugin), ])); }); @@ -1058,7 +1058,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getLinuxBuildCall(plugin), + getLinuxBuildCall(plugin), ProcessCall(testBinary.path, const [], null), ])); }); @@ -1102,7 +1102,7 @@ void main() { final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ - _getMockXcodebuildListProcess( + getMockXcodebuildListProcess( ['RunnerTests', 'RunnerUITests']), ]; @@ -1121,8 +1121,8 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getTargetCheckCall(pluginExampleDirectory, 'macos'), - _getRunTestCall(pluginExampleDirectory, 'macos', + getTargetCheckCall(pluginExampleDirectory, 'macos'), + getRunTestCall(pluginExampleDirectory, 'macos', extraFlags: ['-only-testing:RunnerTests']), ])); }); @@ -1137,7 +1137,7 @@ void main() { final Directory pluginExampleDirectory = getExampleDir(plugin1); processRunner.mockProcessesForExecutable['xcrun'] = [ - _getMockXcodebuildListProcess( + getMockXcodebuildListProcess( ['RunnerTests', 'RunnerUITests']), ]; @@ -1156,8 +1156,8 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getTargetCheckCall(pluginExampleDirectory, 'macos'), - _getRunTestCall(pluginExampleDirectory, 'macos', + getTargetCheckCall(pluginExampleDirectory, 'macos'), + getRunTestCall(pluginExampleDirectory, 'macos', extraFlags: ['-only-testing:RunnerUITests']), ])); }); @@ -1173,7 +1173,7 @@ void main() { // Simulate a project with unit tests but no integration tests... processRunner.mockProcessesForExecutable['xcrun'] = [ - _getMockXcodebuildListProcess(['RunnerTests']), + getMockXcodebuildListProcess(['RunnerTests']), ]; // ... then try to run only integration tests. @@ -1193,7 +1193,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getTargetCheckCall(pluginExampleDirectory, 'macos'), + getTargetCheckCall(pluginExampleDirectory, 'macos'), ])); }); @@ -1207,7 +1207,7 @@ void main() { final Directory pluginExampleDirectory = getExampleDir(plugin1); processRunner.mockProcessesForExecutable['xcrun'] = [ - _getMockXcodebuildListProcess(['RunnerUITests']), + getMockXcodebuildListProcess(['RunnerUITests']), ]; Error? commandError; @@ -1232,7 +1232,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getTargetCheckCall(pluginExampleDirectory, 'macos'), + getTargetCheckCall(pluginExampleDirectory, 'macos'), ])); }); @@ -1269,7 +1269,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getTargetCheckCall(pluginExampleDirectory, 'macos'), + getTargetCheckCall(pluginExampleDirectory, 'macos'), ])); }); }); @@ -1295,10 +1295,10 @@ void main() { pluginExampleDirectory.childDirectory('android'); processRunner.mockProcessesForExecutable['xcrun'] = [ - _getMockXcodebuildListProcess( + getMockXcodebuildListProcess( ['RunnerTests', 'RunnerUITests']), // iOS list MockProcess(), // iOS run - _getMockXcodebuildListProcess( + getMockXcodebuildListProcess( ['RunnerTests', 'RunnerUITests']), // macOS list MockProcess(), // macOS run ]; @@ -1308,7 +1308,7 @@ void main() { '--android', '--ios', '--macos', - _kDestination, + kDestination, 'foo_destination', ]); @@ -1325,11 +1325,11 @@ void main() { orderedEquals([ ProcessCall(androidFolder.childFile('gradlew').path, const ['testDebugUnitTest'], androidFolder.path), - _getTargetCheckCall(pluginExampleDirectory, 'ios'), - _getRunTestCall(pluginExampleDirectory, 'ios', + getTargetCheckCall(pluginExampleDirectory, 'ios'), + getRunTestCall(pluginExampleDirectory, 'ios', destination: 'foo_destination'), - _getTargetCheckCall(pluginExampleDirectory, 'macos'), - _getRunTestCall(pluginExampleDirectory, 'macos'), + getTargetCheckCall(pluginExampleDirectory, 'macos'), + getRunTestCall(pluginExampleDirectory, 'macos'), ])); }); @@ -1342,7 +1342,7 @@ void main() { final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ - _getMockXcodebuildListProcess( + getMockXcodebuildListProcess( ['RunnerTests', 'RunnerUITests']), ]; @@ -1350,7 +1350,7 @@ void main() { 'native-test', '--ios', '--macos', - _kDestination, + kDestination, 'foo_destination', ]); @@ -1364,8 +1364,8 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getTargetCheckCall(pluginExampleDirectory, 'macos'), - _getRunTestCall(pluginExampleDirectory, 'macos'), + getTargetCheckCall(pluginExampleDirectory, 'macos'), + getRunTestCall(pluginExampleDirectory, 'macos'), ])); }); @@ -1378,7 +1378,7 @@ void main() { final Directory pluginExampleDirectory = getExampleDir(plugin); processRunner.mockProcessesForExecutable['xcrun'] = [ - _getMockXcodebuildListProcess( + getMockXcodebuildListProcess( ['RunnerTests', 'RunnerUITests']), ]; @@ -1386,7 +1386,7 @@ void main() { 'native-test', '--ios', '--macos', - _kDestination, + kDestination, 'foo_destination', ]); @@ -1400,8 +1400,8 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getTargetCheckCall(pluginExampleDirectory, 'ios'), - _getRunTestCall(pluginExampleDirectory, 'ios', + getTargetCheckCall(pluginExampleDirectory, 'ios'), + getRunTestCall(pluginExampleDirectory, 'ios', destination: 'foo_destination'), ])); }); @@ -1415,7 +1415,7 @@ void main() { '--ios', '--macos', '--windows', - _kDestination, + kDestination, 'foo_destination', ]); @@ -1447,7 +1447,7 @@ void main() { 'native-test', '--macos', '--windows', - _kDestination, + kDestination, 'foo_destination', ]); @@ -1477,7 +1477,7 @@ void main() { ); processRunner.mockProcessesForExecutable['xcrun'] = [ - _getMockXcodebuildListProcess( + getMockXcodebuildListProcess( ['RunnerTests', 'RunnerUITests']), ]; @@ -1598,7 +1598,7 @@ void main() { // Returns the ProcessCall to expect for build the Windows unit tests for // the given plugin. - ProcessCall _getWindowsBuildCall(RepositoryPackage plugin) { + ProcessCall getWindowsBuildCall(RepositoryPackage plugin) { return ProcessCall( _fakeCmakeCommand, [ @@ -1647,7 +1647,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getWindowsBuildCall(plugin), + getWindowsBuildCall(plugin), ProcessCall(testBinary.path, const [], null), ])); }); @@ -1687,7 +1687,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getWindowsBuildCall(plugin), + getWindowsBuildCall(plugin), ProcessCall(debugTestBinary.path, const [], null), ])); }); @@ -1746,7 +1746,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getWindowsBuildCall(plugin), + getWindowsBuildCall(plugin), ])); }); @@ -1787,7 +1787,7 @@ void main() { expect( processRunner.recordedCalls, orderedEquals([ - _getWindowsBuildCall(plugin), + getWindowsBuildCall(plugin), ProcessCall(testBinary.path, const [], null), ])); }); diff --git a/script/tool/test/publish_command_test.dart b/script/tool/test/publish_command_test.dart index 19e8bdd233ec..da5f9c871f05 100644 --- a/script/tool/test/publish_command_test.dart +++ b/script/tool/test/publish_command_test.dart @@ -33,7 +33,7 @@ void main() { // Map of package name to mock response. late Map> mockHttpResponses; - void _createMockCredentialFile() { + void createMockCredentialFile() { final String credentialPath = PublishCommand.getCredentialPath(); fileSystem.file(credentialPath) ..createSync(recursive: true) @@ -204,7 +204,7 @@ void main() { test( '--skip-confirmation flag automatically adds --force to --pub-publish-flags', () async { - _createMockCredentialFile(); + createMockCredentialFile(); final RepositoryPackage plugin = createFakePlugin('foo', packagesDir, examples: []); @@ -225,7 +225,7 @@ void main() { }); test('--force is only added once, regardless of plugin count', () async { - _createMockCredentialFile(); + createMockCredentialFile(); final RepositoryPackage plugin1 = createFakePlugin('plugin_a', packagesDir, examples: []); final RepositoryPackage plugin2 = @@ -393,7 +393,7 @@ void main() { test('does not ask for user input if the --skip-confirmation flag is on', () async { - _createMockCredentialFile(); + createMockCredentialFile(); createFakePlugin('foo', packagesDir, examples: []); final List output = diff --git a/script/tool/test/remove_dev_dependencies_test.dart b/script/tool/test/remove_dev_dependencies_test.dart index 6b212870c53b..776cbf197838 100644 --- a/script/tool/test/remove_dev_dependencies_test.dart +++ b/script/tool/test/remove_dev_dependencies_test.dart @@ -27,7 +27,7 @@ void main() { runner.addCommand(command); }); - void _addToPubspec(RepositoryPackage package, String addition) { + void addToPubspec(RepositoryPackage package, String addition) { final String originalContent = package.pubspecFile.readAsStringSync(); package.pubspecFile.writeAsStringSync(''' $originalContent @@ -53,7 +53,7 @@ $addition final RepositoryPackage package = createFakePackage('a_package', packagesDir, version: '1.0.0'); - _addToPubspec(package, ''' + addToPubspec(package, ''' dev_dependencies: some_dependency: ^2.1.8 another_dependency: ^1.0.0 @@ -79,7 +79,7 @@ dev_dependencies: createFakePackage('a_package', packagesDir, version: '1.0.0'); final RepositoryPackage example = package.getExamples().first; - _addToPubspec(example, ''' + addToPubspec(example, ''' dev_dependencies: some_dependency: ^2.1.8 another_dependency: ^1.0.0 diff --git a/script/tool/test/version_check_command_test.dart b/script/tool/test/version_check_command_test.dart index 598176bbe209..d485d81ceaf2 100644 --- a/script/tool/test/version_check_command_test.dart +++ b/script/tool/test/version_check_command_test.dart @@ -681,8 +681,7 @@ void main() { }); group('missing change detection', () { - Future> _runWithMissingChangeDetection( - List extraArgs, + Future> runWithMissingChangeDetection(List extraArgs, {void Function(Error error)? errorHandler}) async { return runCapturingPrint( runner, @@ -712,7 +711,7 @@ void main() { ]; final List output = - await _runWithMissingChangeDetection([]); + await runWithMissingChangeDetection([]); expect( output, @@ -743,7 +742,7 @@ packages/plugin/lib/plugin.dart ]; Error? commandError; - final List output = await _runWithMissingChangeDetection( + final List output = await runWithMissingChangeDetection( [], errorHandler: (Error e) { commandError = e; }); @@ -780,7 +779,7 @@ packages/plugin/pubspec.yaml ]; final List output = - await _runWithMissingChangeDetection([]); + await runWithMissingChangeDetection([]); expect( output, @@ -810,7 +809,7 @@ tool/plugin/lib/plugin.dart ]; final List output = - await _runWithMissingChangeDetection([]); + await runWithMissingChangeDetection([]); expect( output, @@ -843,7 +842,7 @@ packages/plugin/CHANGELOG.md ]; final List output = - await _runWithMissingChangeDetection([]); + await runWithMissingChangeDetection([]); expect( output, @@ -874,7 +873,7 @@ packages/plugin/pubspec.yaml ]; final List output = - await _runWithMissingChangeDetection([ + await runWithMissingChangeDetection([ '--pr-labels=some label,override: no versioning needed,another-label' ]); @@ -906,7 +905,7 @@ packages/plugin/example/lib/foo.dart ]; Error? commandError; - final List output = await _runWithMissingChangeDetection( + final List output = await runWithMissingChangeDetection( [], errorHandler: (Error e) { commandError = e; }); @@ -942,7 +941,7 @@ packages/plugin/CHANGELOG.md ]; final List output = - await _runWithMissingChangeDetection([]); + await runWithMissingChangeDetection([]); expect( output, @@ -973,7 +972,7 @@ packages/another_plugin/CHANGELOG.md ]; Error? commandError; - final List output = await _runWithMissingChangeDetection( + final List output = await runWithMissingChangeDetection( [], errorHandler: (Error e) { commandError = e; }); @@ -1006,7 +1005,7 @@ packages/plugin/example/lib/foo.dart ]; final List output = - await _runWithMissingChangeDetection([ + await runWithMissingChangeDetection([ '--pr-labels=some label,override: no changelog needed,another-label' ]); @@ -1050,7 +1049,7 @@ packages/plugin/android/build.gradle ]; final List output = - await _runWithMissingChangeDetection([]); + await runWithMissingChangeDetection([]); expect( output, @@ -1082,7 +1081,7 @@ packages/plugin/run_tests.sh ]; final List output = - await _runWithMissingChangeDetection([]); + await runWithMissingChangeDetection([]); expect( output, From ab5e9e6b7cf44263e28b2e6aec6f827e3268e777 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 28 Sep 2022 15:42:06 -0400 Subject: [PATCH 768/844] [file_selector] Add all iOS files to example app (#6513) --- .../example/ios/Flutter/Debug.xcconfig | 1 + .../example/ios/Flutter/Release.xcconfig | 1 + .../file_selector/example/ios/Podfile | 41 +++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 packages/file_selector/file_selector/example/ios/Podfile diff --git a/packages/file_selector/file_selector/example/ios/Flutter/Debug.xcconfig b/packages/file_selector/file_selector/example/ios/Flutter/Debug.xcconfig index 592ceee85b89..ec97fc6f3021 100644 --- a/packages/file_selector/file_selector/example/ios/Flutter/Debug.xcconfig +++ b/packages/file_selector/file_selector/example/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/packages/file_selector/file_selector/example/ios/Flutter/Release.xcconfig b/packages/file_selector/file_selector/example/ios/Flutter/Release.xcconfig index 592ceee85b89..c4855bfe2000 100644 --- a/packages/file_selector/file_selector/example/ios/Flutter/Release.xcconfig +++ b/packages/file_selector/file_selector/example/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/packages/file_selector/file_selector/example/ios/Podfile b/packages/file_selector/file_selector/example/ios/Podfile new file mode 100644 index 000000000000..88359b225fa1 --- /dev/null +++ b/packages/file_selector/file_selector/example/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '11.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__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end From 5bb51296b00ec32b8df193a38435769d5e26aafa Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 28 Sep 2022 16:51:15 -0400 Subject: [PATCH 769/844] [tool] Adds a `fix` command (#6512) --- script/tool/CHANGELOG.md | 4 ++ script/tool/lib/src/fix_command.dart | 51 +++++++++++++++++ script/tool/lib/src/main.dart | 2 + script/tool/test/fix_command_test.dart | 78 ++++++++++++++++++++++++++ 4 files changed, 135 insertions(+) create mode 100644 script/tool/lib/src/fix_command.dart create mode 100644 script/tool/test/fix_command_test.dart diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index 830d3ca931e6..bf549d4e79dc 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.11.1 + +* Adds a `fix` command to run `dart fix --apply` in target packages. + ## 0.11 * Renames `publish-plugin` to `publish`. diff --git a/script/tool/lib/src/fix_command.dart b/script/tool/lib/src/fix_command.dart new file mode 100644 index 000000000000..2819609eabbd --- /dev/null +++ b/script/tool/lib/src/fix_command.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 'dart:async'; + +import 'package:file/file.dart'; +import 'package:platform/platform.dart'; + +import 'common/core.dart'; +import 'common/package_looping_command.dart'; +import 'common/process_runner.dart'; +import 'common/repository_package.dart'; + +/// A command to run Dart's "fix" command on packages. +class FixCommand extends PackageLoopingCommand { + /// Creates a fix command instance. + FixCommand( + Directory packagesDir, { + ProcessRunner processRunner = const ProcessRunner(), + Platform platform = const LocalPlatform(), + }) : super(packagesDir, processRunner: processRunner, platform: platform); + + @override + final String name = 'fix'; + + @override + final String description = 'Fixes packages using dart fix.\n\n' + 'This command requires "dart" and "flutter" to be in your path, and ' + 'assumes that dependencies have already been fetched (e.g., by running ' + 'the analyze command first).'; + + @override + final bool hasLongOutput = false; + + @override + PackageLoopingType get packageLoopingType => + PackageLoopingType.includeAllSubpackages; + + @override + Future runForPackage(RepositoryPackage package) async { + final int exitCode = await processRunner.runAndStream( + 'dart', ['fix', '--apply'], + workingDir: package.directory); + if (exitCode != 0) { + printError('Unable to automatically fix package.'); + return PackageResult.fail(); + } + return PackageResult.success(); + } +} diff --git a/script/tool/lib/src/main.dart b/script/tool/lib/src/main.dart index 078976d97376..414ca7f303c0 100644 --- a/script/tool/lib/src/main.dart +++ b/script/tool/lib/src/main.dart @@ -17,6 +17,7 @@ import 'dependabot_check_command.dart'; import 'drive_examples_command.dart'; import 'federation_safety_check_command.dart'; import 'firebase_test_lab_command.dart'; +import 'fix_command.dart'; import 'format_command.dart'; import 'license_check_command.dart'; import 'lint_android_command.dart'; @@ -61,6 +62,7 @@ void main(List args) { ..addCommand(DriveExamplesCommand(packagesDir)) ..addCommand(FederationSafetyCheckCommand(packagesDir)) ..addCommand(FirebaseTestLabCommand(packagesDir)) + ..addCommand(FixCommand(packagesDir)) ..addCommand(FormatCommand(packagesDir)) ..addCommand(LicenseCheckCommand(packagesDir)) ..addCommand(LintAndroidCommand(packagesDir)) diff --git a/script/tool/test/fix_command_test.dart b/script/tool/test/fix_command_test.dart new file mode 100644 index 000000000000..16061d2206cd --- /dev/null +++ b/script/tool/test/fix_command_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 'dart:io' as io; + +import 'package:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_plugin_tools/src/common/core.dart'; +import 'package:flutter_plugin_tools/src/fix_command.dart'; +import 'package:test/test.dart'; + +import 'mocks.dart'; +import 'util.dart'; + +void main() { + late FileSystem fileSystem; + late MockPlatform mockPlatform; + late Directory packagesDir; + late RecordingProcessRunner processRunner; + late CommandRunner runner; + + setUp(() { + fileSystem = MemoryFileSystem(); + mockPlatform = MockPlatform(); + packagesDir = createPackagesDirectory(fileSystem: fileSystem); + processRunner = RecordingProcessRunner(); + final FixCommand command = FixCommand( + packagesDir, + processRunner: processRunner, + platform: mockPlatform, + ); + + runner = CommandRunner('fix_command', 'Test for fix_command'); + runner.addCommand(command); + }); + + test('runs fix in top-level packages and subpackages', () async { + final RepositoryPackage package = createFakePackage('a', packagesDir); + final RepositoryPackage plugin = createFakePlugin('b', packagesDir); + + await runCapturingPrint(runner, ['fix']); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall('dart', const ['fix', '--apply'], package.path), + ProcessCall('dart', const ['fix', '--apply'], + package.getExamples().first.path), + ProcessCall('dart', const ['fix', '--apply'], plugin.path), + ProcessCall('dart', const ['fix', '--apply'], + plugin.getExamples().first.path), + ])); + }); + + test('fails if "dart fix" fails', () async { + createFakePlugin('foo', packagesDir); + + processRunner.mockProcessesForExecutable['dart'] = [ + MockProcess(exitCode: 1), + ]; + + Error? commandError; + final List output = await runCapturingPrint(runner, ['fix'], + errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains('Unable to automatically fix package.'), + ]), + ); + }); +} From d73b921da0950b663492d9b6527688f1d5fabd43 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 29 Sep 2022 11:50:23 -0400 Subject: [PATCH 770/844] Roll Flutter from 9bf849184d6c to a11bef968893 (37 revisions) (#6515) --- .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 e9a6ba9a1691..953ea3223c85 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -9bf849184d6ca75b8d9fa5ac8cdaa10e9bc6521e +a11bef968893d5c43a2d70cff649d251b8681277 From f46fe078c1841a7f378af2118e68e514a68ebd53 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 29 Sep 2022 12:58:22 -0400 Subject: [PATCH 771/844] Roll Flutter (stable) from e3c29ec00c9c to 18a827f3933c (4 revisions) (#6514) --- .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 4c15f9c261c3..20445c3150b3 100644 --- a/.ci/flutter_stable.version +++ b/.ci/flutter_stable.version @@ -1 +1 @@ -e3c29ec00c9c825c891d75054c63fcc46454dca1 +18a827f3933c19f51862dde3fa472197683249d6 From c2792606af661ce9b21e0f859ac3cd6f100482a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Sep 2022 18:02:23 +0000 Subject: [PATCH 772/844] [google_maps]: Bump play-services-maps from 18.0.2 to 18.1.0 in /packages/google_maps_flutter/google_maps_flutter_android/android (#6195) --- .../google_maps_flutter_android/CHANGELOG.md | 4 ++++ .../google_maps_flutter_android/android/build.gradle | 2 +- .../io/flutter/plugins/googlemaps/PolygonControllerTest.java | 4 ++-- .../io/flutter/plugins/googlemaps/PolylineControllerTest.java | 4 ++-- .../google_maps_flutter_android/pubspec.yaml | 2 +- 5 files changed, 10 insertions(+), 6 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 2ea420daec8a..6fbd8d7e0b17 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.2 + +* Update `com.google.android.gms:play-services-maps` to 18.1.0. + ## 2.3.1 * Updates imports for `prefer_relative_imports`. diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle index ecc85217c5bf..06599fc94c49 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle @@ -35,7 +35,7 @@ android { dependencies { implementation "androidx.annotation:annotation:1.1.0" - implementation 'com.google.android.gms:play-services-maps:18.0.2' + implementation 'com.google.android.gms:play-services-maps:18.1.0' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:rules:1.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java index 5c73a3f3e449..271c63bdc25c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolygonControllerTest.java @@ -7,7 +7,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import com.google.android.gms.internal.maps.zzaa; +import com.google.android.gms.internal.maps.zzad; import com.google.android.gms.maps.model.Polygon; import org.junit.Test; import org.mockito.Mockito; @@ -16,7 +16,7 @@ public class PolygonControllerTest { @Test public void controller_SetsStrokeDensity() { - final zzaa z = mock(zzaa.class); + final zzad z = mock(zzad.class); final Polygon polygon = spy(new Polygon(z)); final float density = 5; diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java index db570174e215..abb98627b35a 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/src/test/java/io/flutter/plugins/googlemaps/PolylineControllerTest.java @@ -7,7 +7,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import com.google.android.gms.internal.maps.zzad; +import com.google.android.gms.internal.maps.zzag; import com.google.android.gms.maps.model.Polyline; import org.junit.Test; import org.mockito.Mockito; @@ -16,7 +16,7 @@ public class PolylineControllerTest { @Test public void controller_SetsStrokeDensity() { - final zzad z = mock(zzad.class); + final zzag z = mock(zzag.class); final Polyline polyline = spy(new Polyline(z)); final float density = 5; 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 1ae73ca97cda..798380b5854d 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/plugins/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.3.1 +version: 2.3.2 environment: sdk: ">=2.14.0 <3.0.0" From b82bb69bd3f63e2079246bbd0f808334c74b4eb9 Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Thu, 29 Sep 2022 12:29:15 -0700 Subject: [PATCH 773/844] [camera] Re-land Add ProcessCameraProvider class to CameraX Plugin (#6508) --- .../camera_android_camerax/CHANGELOG.md | 1 + .../android/build.gradle | 23 +-- .../camerax/CameraAndroidCameraxPlugin.java | 31 +++- .../camerax/CameraInfoFlutterApiImpl.java | 3 +- .../camerax/GeneratedCameraXLibrary.java | 134 ++++++++++++++++++ .../ProcessCameraProviderFlutterApiImpl.java | 23 +++ .../ProcessCameraProviderHostApiImpl.java | 87 ++++++++++++ .../camerax/ProcessCameraProviderTest.java | 114 +++++++++++++++ ...roid_camera_camerax_flutter_api_impls.dart | 9 ++ .../lib/src/camerax_library.pigeon.dart | 109 ++++++++++++++ .../lib/src/process_camera_provider.dart | 119 ++++++++++++++++ .../pigeons/camerax_library.dart | 13 ++ .../test/process_camera_provider_test.dart | 96 +++++++++++++ .../process_camera_provider_test.mocks.dart | 40 ++++++ .../test/test_camerax_library.pigeon.dart | 50 +++++++ 15 files changed, 836 insertions(+), 16 deletions(-) create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderFlutterApiImpl.java create mode 100644 packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java create mode 100644 packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java create mode 100644 packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart create mode 100644 packages/camera/camera_android_camerax/test/process_camera_provider_test.dart create mode 100644 packages/camera/camera_android_camerax/test/process_camera_provider_test.mocks.dart diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md index 2bdfa594b3be..ce2fb9046c69 100644 --- a/packages/camera/camera_android_camerax/CHANGELOG.md +++ b/packages/camera/camera_android_camerax/CHANGELOG.md @@ -3,3 +3,4 @@ * Creates camera_android_camerax plugin for development. * Adds CameraInfo class and removes unnecessary code from plugin. * Adds CameraSelector class. +* Adds ProcessCameraProvider class. diff --git a/packages/camera/camera_android_camerax/android/build.gradle b/packages/camera/camera_android_camerax/android/build.gradle index 1772afebe429..b4209bbe62a6 100644 --- a/packages/camera/camera_android_camerax/android/build.gradle +++ b/packages/camera/camera_android_camerax/android/build.gradle @@ -36,16 +36,6 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } -dependencies { - // CameraX core library using the camera2 implementation must use same version number. - def camerax_version = "1.2.0-beta01" - implementation "androidx.camera:camera-core:${camerax_version}" - implementation "androidx.camera:camera-camera2:${camerax_version}" - implementation "androidx.camera:camera-lifecycle:${camerax_version}" - testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-inline:4.7.0' - testImplementation 'androidx.test:core:1.4.0' -} testOptions { unitTests.includeAndroidResources = true unitTests.returnDefaultValues = true @@ -58,3 +48,16 @@ dependencies { } } } + +dependencies { + // CameraX core library using the camera2 implementation must use same version number. + def camerax_version = "1.2.0-beta02" + implementation "androidx.camera:camera-core:${camerax_version}" + implementation "androidx.camera:camera-camera2:${camerax_version}" + implementation "androidx.camera:camera-lifecycle:${camerax_version}" + implementation 'com.google.guava:guava:31.1-android' + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.mockito:mockito-inline:4.7.0' + testImplementation 'androidx.test:core:1.4.0' + testImplementation 'org.robolectric:robolectric:4.8' +} 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 bb5756a7e3b9..b8fbaf539c32 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 @@ -15,6 +15,7 @@ public final class CameraAndroidCameraxPlugin implements FlutterPlugin, ActivityAware { private InstanceManager instanceManager; private FlutterPluginBinding pluginBinding; + private ProcessCameraProviderHostApiImpl processCameraProviderHostApi; /** * Initialize this within the {@code #configureFlutterEngine} of a Flutter activity or fragment. @@ -39,6 +40,10 @@ void setUp(BinaryMessenger binaryMessenger, Context context) { binaryMessenger, new JavaObjectHostApiImpl(instanceManager)); GeneratedCameraXLibrary.CameraSelectorHostApi.setup( binaryMessenger, new CameraSelectorHostApiImpl(binaryMessenger, instanceManager)); + processCameraProviderHostApi = + new ProcessCameraProviderHostApiImpl(binaryMessenger, instanceManager, context); + GeneratedCameraXLibrary.ProcessCameraProviderHostApi.setup( + binaryMessenger, processCameraProviderHostApi); } @Override @@ -60,15 +65,33 @@ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { // Activity Lifecycle methods: @Override - public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) {} + public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) { + updateContext(activityPluginBinding.getActivity()); + } @Override - public void onDetachedFromActivityForConfigChanges() {} + public void onDetachedFromActivityForConfigChanges() { + updateContext(pluginBinding.getApplicationContext()); + } @Override public void onReattachedToActivityForConfigChanges( - @NonNull ActivityPluginBinding activityPluginBinding) {} + @NonNull ActivityPluginBinding activityPluginBinding) { + updateContext(activityPluginBinding.getActivity()); + } @Override - public void onDetachedFromActivity() {} + public void onDetachedFromActivity() { + updateContext(pluginBinding.getApplicationContext()); + } + + /** + * Updates context that is used to fetch the corresponding instance of a {@code + * ProcessCameraProvider}. + */ + private void updateContext(Context context) { + if (processCameraProviderHostApi != null) { + processCameraProviderHostApi.setContext(context); + } + } } diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java index b5ba9fc1ff3b..c538e420cc7e 100644 --- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraInfoFlutterApiImpl.java @@ -18,7 +18,6 @@ public CameraInfoFlutterApiImpl( } void create(CameraInfo cameraInfo, Reply reply) { - instanceManager.addHostCreatedInstance(cameraInfo); - create(instanceManager.getIdentifierForStrongReference(cameraInfo), reply); + create(instanceManager.addHostCreatedInstance(cameraInfo), reply); } } 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 e87a80db030c..041564c3bfcb 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 @@ -22,6 +22,13 @@ /** Generated class from Pigeon. */ @SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"}) public class GeneratedCameraXLibrary { + + public interface Result { + void success(T result); + + void error(Throwable error); + } + private static class JavaObjectHostApiCodec extends StandardMessageCodec { public static final JavaObjectHostApiCodec INSTANCE = new JavaObjectHostApiCodec(); @@ -311,6 +318,133 @@ public void create( } } + private static class ProcessCameraProviderHostApiCodec extends StandardMessageCodec { + public static final ProcessCameraProviderHostApiCodec INSTANCE = + new ProcessCameraProviderHostApiCodec(); + + private ProcessCameraProviderHostApiCodec() {} + } + + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface ProcessCameraProviderHostApi { + void getInstance(Result result); + + @NonNull + List getAvailableCameraInfos(@NonNull Long identifier); + + /** The codec used by ProcessCameraProviderHostApi. */ + static MessageCodec getCodec() { + return ProcessCameraProviderHostApiCodec.INSTANCE; + } + + /** + * Sets up an instance of `ProcessCameraProviderHostApi` to handle messages through the + * `binaryMessenger`. + */ + static void setup(BinaryMessenger binaryMessenger, ProcessCameraProviderHostApi api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.ProcessCameraProviderHostApi.getInstance", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + Result resultCallback = + new Result() { + public void success(Long result) { + wrapped.put("result", result); + reply.reply(wrapped); + } + + public void error(Throwable error) { + wrapped.put("error", wrapError(error)); + reply.reply(wrapped); + } + }; + + api.getInstance(resultCallback); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + reply.reply(wrapped); + } + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos", + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList) message; + Number identifierArg = (Number) args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + List output = + api.getAvailableCameraInfos( + (identifierArg == null) ? null : identifierArg.longValue()); + wrapped.put("result", output); + } catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } + + private static class ProcessCameraProviderFlutterApiCodec extends StandardMessageCodec { + public static final ProcessCameraProviderFlutterApiCodec INSTANCE = + new ProcessCameraProviderFlutterApiCodec(); + + private ProcessCameraProviderFlutterApiCodec() {} + } + + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ + public static class ProcessCameraProviderFlutterApi { + private final BinaryMessenger binaryMessenger; + + public ProcessCameraProviderFlutterApi(BinaryMessenger argBinaryMessenger) { + this.binaryMessenger = argBinaryMessenger; + } + + public interface Reply { + void reply(T reply); + } + + static MessageCodec getCodec() { + return ProcessCameraProviderFlutterApiCodec.INSTANCE; + } + + public void create(@NonNull Long identifierArg, Reply callback) { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.ProcessCameraProviderFlutterApi.create", + getCodec()); + channel.send( + new ArrayList(Arrays.asList(identifierArg)), + channelReply -> { + callback.reply(null); + }); + } + } + private static Map wrapError(Throwable exception) { Map errorMap = new HashMap<>(); errorMap.put("message", exception.toString()); diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderFlutterApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderFlutterApiImpl.java new file mode 100644 index 000000000000..90c94d0c26cb --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderFlutterApiImpl.java @@ -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. + +package io.flutter.plugins.camerax; + +import androidx.camera.lifecycle.ProcessCameraProvider; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ProcessCameraProviderFlutterApi; + +public class ProcessCameraProviderFlutterApiImpl extends ProcessCameraProviderFlutterApi { + public ProcessCameraProviderFlutterApiImpl( + BinaryMessenger binaryMessenger, InstanceManager instanceManager) { + super(binaryMessenger); + this.instanceManager = instanceManager; + } + + private final InstanceManager instanceManager; + + void create(ProcessCameraProvider processCameraProvider, Reply reply) { + create(instanceManager.addHostCreatedInstance(processCameraProvider), reply); + } +} diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java new file mode 100644 index 000000000000..19c5eb5b3f70 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ProcessCameraProviderHostApiImpl.java @@ -0,0 +1,87 @@ +// 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.content.Context; +import androidx.annotation.NonNull; +import androidx.camera.core.CameraInfo; +import androidx.camera.lifecycle.ProcessCameraProvider; +import androidx.core.content.ContextCompat; +import com.google.common.util.concurrent.ListenableFuture; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugins.camerax.GeneratedCameraXLibrary.ProcessCameraProviderHostApi; +import java.util.ArrayList; +import java.util.List; + +public class ProcessCameraProviderHostApiImpl implements ProcessCameraProviderHostApi { + private final BinaryMessenger binaryMessenger; + private final InstanceManager instanceManager; + + private Context context; + + public ProcessCameraProviderHostApiImpl( + BinaryMessenger binaryMessenger, InstanceManager instanceManager, Context context) { + this.binaryMessenger = binaryMessenger; + this.instanceManager = instanceManager; + this.context = context; + } + + /** + * Sets the context that the {@code ProcessCameraProvider} will use to attach the lifecycle of the + * camera to. + * + *

If using the camera plugin in an add-to-app context, ensure that a new instance of the + * {@code ProcessCameraProvider} is fetched via {@code #getInstance} anytime the context changes. + */ + public void setContext(Context context) { + this.context = context; + } + + /** + * Returns the instance of the ProcessCameraProvider to manage the lifecycle of the camera for the + * current {@code Context}. + */ + @Override + public void getInstance(GeneratedCameraXLibrary.Result result) { + ListenableFuture processCameraProviderFuture = + ProcessCameraProvider.getInstance(context); + + processCameraProviderFuture.addListener( + () -> { + try { + // Camera provider is now guaranteed to be available. + ProcessCameraProvider processCameraProvider = processCameraProviderFuture.get(); + + if (!instanceManager.containsInstance(processCameraProvider)) { + final ProcessCameraProviderFlutterApiImpl flutterApi = + new ProcessCameraProviderFlutterApiImpl(binaryMessenger, instanceManager); + flutterApi.create(processCameraProvider, reply -> {}); + } + result.success(instanceManager.getIdentifierForStrongReference(processCameraProvider)); + } catch (Exception e) { + result.error(e); + } + }, + ContextCompat.getMainExecutor(context)); + } + + /** Returns cameras available to the ProcessCameraProvider. */ + @Override + public List getAvailableCameraInfos(@NonNull Long identifier) { + ProcessCameraProvider processCameraProvider = + (ProcessCameraProvider) instanceManager.getInstance(identifier); + + List availableCameras = processCameraProvider.getAvailableCameraInfos(); + List availableCamerasIds = new ArrayList(); + final CameraInfoFlutterApiImpl cameraInfoFlutterApi = + new CameraInfoFlutterApiImpl(binaryMessenger, instanceManager); + + for (CameraInfo cameraInfo : availableCameras) { + cameraInfoFlutterApi.create(cameraInfo, result -> {}); + availableCamerasIds.add(instanceManager.getIdentifierForStrongReference(cameraInfo)); + } + return availableCamerasIds; + } +} diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java new file mode 100644 index 000000000000..5008e4ef34b0 --- /dev/null +++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ProcessCameraProviderTest.java @@ -0,0 +1,114 @@ +// 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 static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import androidx.camera.core.CameraInfo; +import androidx.camera.lifecycle.ProcessCameraProvider; +import androidx.test.core.app.ApplicationProvider; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import io.flutter.plugin.common.BinaryMessenger; +import java.util.Arrays; +import java.util.Objects; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +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; + +@RunWith(RobolectricTestRunner.class) +public class ProcessCameraProviderTest { + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + @Mock public ProcessCameraProvider processCameraProvider; + @Mock public BinaryMessenger mockBinaryMessenger; + + InstanceManager testInstanceManager; + private Context context; + + @Before + public void setUp() { + testInstanceManager = InstanceManager.open(identifier -> {}); + context = ApplicationProvider.getApplicationContext(); + } + + @After + public void tearDown() { + testInstanceManager.close(); + } + + @Test + public void getInstanceTest() { + final ProcessCameraProviderHostApiImpl processCameraProviderHostApi = + new ProcessCameraProviderHostApiImpl(mockBinaryMessenger, testInstanceManager, context); + final ListenableFuture processCameraProviderFuture = + spy(Futures.immediateFuture(processCameraProvider)); + final GeneratedCameraXLibrary.Result mockResult = + mock(GeneratedCameraXLibrary.Result.class); + + testInstanceManager.addDartCreatedInstance(processCameraProvider, 0); + + try (MockedStatic mockedProcessCameraProvider = + Mockito.mockStatic(ProcessCameraProvider.class)) { + mockedProcessCameraProvider + .when(() -> ProcessCameraProvider.getInstance(context)) + .thenAnswer( + (Answer>) + invocation -> processCameraProviderFuture); + + final ArgumentCaptor runnableCaptor = ArgumentCaptor.forClass(Runnable.class); + + processCameraProviderHostApi.getInstance(mockResult); + verify(processCameraProviderFuture).addListener(runnableCaptor.capture(), any()); + runnableCaptor.getValue().run(); + verify(mockResult).success(0L); + } + } + + @Test + public void getAvailableCameraInfosTest() { + final ProcessCameraProviderHostApiImpl processCameraProviderHostApi = + new ProcessCameraProviderHostApiImpl(mockBinaryMessenger, testInstanceManager, context); + final CameraInfo mockCameraInfo = mock(CameraInfo.class); + + testInstanceManager.addDartCreatedInstance(processCameraProvider, 0); + testInstanceManager.addDartCreatedInstance(mockCameraInfo, 1); + + when(processCameraProvider.getAvailableCameraInfos()).thenReturn(Arrays.asList(mockCameraInfo)); + + assertEquals(processCameraProviderHostApi.getAvailableCameraInfos(0L), Arrays.asList(1L)); + verify(processCameraProvider).getAvailableCameraInfos(); + } + + @Test + public void flutterApiCreateTest() { + final ProcessCameraProviderFlutterApiImpl spyFlutterApi = + spy(new ProcessCameraProviderFlutterApiImpl(mockBinaryMessenger, testInstanceManager)); + + spyFlutterApi.create(processCameraProvider, reply -> {}); + + final long identifier = + Objects.requireNonNull( + testInstanceManager.getIdentifierForStrongReference(processCameraProvider)); + verify(spyFlutterApi).create(eq(identifier), any()); + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart index 576260c0b7b8..9c6564a06c08 100644 --- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart +++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart @@ -6,6 +6,7 @@ import 'camera_info.dart'; import 'camera_selector.dart'; import 'camerax_library.pigeon.dart'; import 'java_object.dart'; +import 'process_camera_provider.dart'; /// Handles initialization of Flutter APIs for the Android CameraX library. class AndroidCameraXCameraFlutterApis { @@ -14,6 +15,7 @@ class AndroidCameraXCameraFlutterApis { JavaObjectFlutterApiImpl? javaObjectFlutterApi, CameraInfoFlutterApiImpl? cameraInfoFlutterApi, CameraSelectorFlutterApiImpl? cameraSelectorFlutterApi, + ProcessCameraProviderFlutterApiImpl? processCameraProviderFlutterApi, }) { this.javaObjectFlutterApi = javaObjectFlutterApi ?? JavaObjectFlutterApiImpl(); @@ -21,6 +23,8 @@ class AndroidCameraXCameraFlutterApis { cameraInfoFlutterApi ?? CameraInfoFlutterApiImpl(); this.cameraSelectorFlutterApi = cameraSelectorFlutterApi ?? CameraSelectorFlutterApiImpl(); + this.processCameraProviderFlutterApi = processCameraProviderFlutterApi ?? + ProcessCameraProviderFlutterApiImpl(); } static bool _haveBeenSetUp = false; @@ -40,12 +44,17 @@ class AndroidCameraXCameraFlutterApis { /// Flutter Api for [CameraSelector]. late final CameraSelectorFlutterApiImpl cameraSelectorFlutterApi; + /// Flutter Api for [ProcessCameraProvider]. + late final ProcessCameraProviderFlutterApiImpl + processCameraProviderFlutterApi; + /// Ensures all the Flutter APIs have been setup to receive calls from native code. void ensureSetUp() { if (!_haveBeenSetUp) { JavaObjectFlutterApi.setup(javaObjectFlutterApi); CameraInfoFlutterApi.setup(cameraInfoFlutterApi); CameraSelectorFlutterApi.setup(cameraSelectorFlutterApi); + ProcessCameraProviderFlutterApi.setup(processCameraProviderFlutterApi); _haveBeenSetUp = true; } } diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart b/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart index a399001d35b0..c0b052378def 100644 --- a/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart +++ b/packages/camera/camera_android_camerax/lib/src/camerax_library.pigeon.dart @@ -263,3 +263,112 @@ abstract class CameraSelectorFlutterApi { } } } + +class _ProcessCameraProviderHostApiCodec extends StandardMessageCodec { + const _ProcessCameraProviderHostApiCodec(); +} + +class ProcessCameraProviderHostApi { + /// Constructor for [ProcessCameraProviderHostApi]. 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. + ProcessCameraProviderHostApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = + _ProcessCameraProviderHostApiCodec(); + + Future getInstance() async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getInstance', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send(null) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as int?)!; + } + } + + Future> getAvailableCameraInfos(int arg_identifier) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos', + codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_identifier]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } else if (replyMap['result'] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyMap['result'] as List?)!.cast(); + } + } +} + +class _ProcessCameraProviderFlutterApiCodec extends StandardMessageCodec { + const _ProcessCameraProviderFlutterApiCodec(); +} + +abstract class ProcessCameraProviderFlutterApi { + static const MessageCodec codec = + _ProcessCameraProviderFlutterApiCodec(); + + void create(int identifier); + static void setup(ProcessCameraProviderFlutterApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ProcessCameraProviderFlutterApi.create', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMessageHandler(null); + } else { + channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderFlutterApi.create 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.ProcessCameraProviderFlutterApi.create was null, expected non-null int.'); + api.create(arg_identifier!); + return; + }); + } + } + } +} diff --git a/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart b/packages/camera/camera_android_camerax/lib/src/process_camera_provider.dart new file mode 100644 index 000000000000..e2b588d15faa --- /dev/null +++ b/packages/camera/camera_android_camerax/lib/src/process_camera_provider.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/services.dart'; + +import 'android_camera_camerax_flutter_api_impls.dart'; +import 'camera_info.dart'; +import 'camerax_library.pigeon.dart'; +import 'instance_manager.dart'; +import 'java_object.dart'; + +/// Provides an object to manage the camera. +/// +/// See https://developer.android.com/reference/androidx/camera/lifecycle/ProcessCameraProvider. +class ProcessCameraProvider extends JavaObject { + /// Creates a detached [ProcessCameraProvider]. + ProcessCameraProvider.detached( + {BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) + : super.detached( + binaryMessenger: binaryMessenger, + instanceManager: instanceManager) { + _api = ProcessCameraProviderHostApiImpl( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); + } + + late final ProcessCameraProviderHostApiImpl _api; + + /// Gets an instance of [ProcessCameraProvider]. + static Future getInstance( + {BinaryMessenger? binaryMessenger, InstanceManager? instanceManager}) { + AndroidCameraXCameraFlutterApis.instance.ensureSetUp(); + final ProcessCameraProviderHostApiImpl api = + ProcessCameraProviderHostApiImpl( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + + return api.getInstancefromInstances(); + } + + /// Retrieves the cameras available to the device. + Future> getAvailableCameraInfos() { + return _api.getAvailableCameraInfosFromInstances(this); + } +} + +/// Host API implementation of [ProcessCameraProvider]. +class ProcessCameraProviderHostApiImpl extends ProcessCameraProviderHostApi { + /// Creates a [ProcessCameraProviderHostApiImpl]. + ProcessCameraProviderHostApiImpl( + {this.binaryMessenger, InstanceManager? instanceManager}) + : super(binaryMessenger: binaryMessenger) { + this.instanceManager = instanceManager ?? JavaObject.globalInstanceManager; + } + + /// Receives binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + late final InstanceManager instanceManager; + + /// Retrieves an instance of a ProcessCameraProvider from the context of + /// the FlutterActivity. + Future getInstancefromInstances() async { + return instanceManager.getInstanceWithWeakReference(await getInstance())! + as ProcessCameraProvider; + } + + /// Retrives the list of CameraInfos corresponding to the available cameras. + Future> getAvailableCameraInfosFromInstances( + ProcessCameraProvider instance) async { + int? identifier = instanceManager.getIdentifier(instance); + identifier ??= instanceManager.addDartCreatedInstance(instance, + onCopy: (ProcessCameraProvider original) { + return ProcessCameraProvider.detached( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + }); + + final List cameraInfos = await getAvailableCameraInfos(identifier); + return (cameraInfos.map((int? id) => + instanceManager.getInstanceWithWeakReference(id!)! as CameraInfo)) + .toList(); + } +} + +/// Flutter API Implementation of [ProcessCameraProvider]. +class ProcessCameraProviderFlutterApiImpl + implements ProcessCameraProviderFlutterApi { + /// Constructs a [ProcessCameraProviderFlutterApiImpl]. + ProcessCameraProviderFlutterApiImpl({ + this.binaryMessenger, + InstanceManager? instanceManager, + }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager; + + /// Receives binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used which routes to + /// the host platform. + final BinaryMessenger? binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + final InstanceManager instanceManager; + + @override + void create(int identifier) { + instanceManager.addHostCreatedInstance( + ProcessCameraProvider.detached( + binaryMessenger: binaryMessenger, instanceManager: instanceManager), + identifier, + onCopy: (ProcessCameraProvider original) { + return ProcessCameraProvider.detached( + binaryMessenger: binaryMessenger, instanceManager: instanceManager); + }, + ); + } +} diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart index aace7a06b1fd..4d7d96910246 100644 --- a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart +++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart @@ -57,3 +57,16 @@ abstract class CameraSelectorHostApi { abstract class CameraSelectorFlutterApi { void create(int identifier, int? lensFacing); } + +@HostApi(dartHostTestHandler: 'TestProcessCameraProviderHostApi') +abstract class ProcessCameraProviderHostApi { + @async + int getInstance(); + + List getAvailableCameraInfos(int identifier); +} + +@FlutterApi() +abstract class ProcessCameraProviderFlutterApi { + void create(int identifier); +} diff --git a/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart b/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart new file mode 100644 index 000000000000..65e7d00ddaea --- /dev/null +++ b/packages/camera/camera_android_camerax/test/process_camera_provider_test.dart @@ -0,0 +1,96 @@ +// 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:camera_android_camerax/src/camera_info.dart'; +import 'package:camera_android_camerax/src/instance_manager.dart'; +import 'package:camera_android_camerax/src/process_camera_provider.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'process_camera_provider_test.mocks.dart'; +import 'test_camerax_library.pigeon.dart'; + +@GenerateMocks([TestProcessCameraProviderHostApi]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('ProcessCameraProvider', () { + tearDown(() => TestProcessCameraProviderHostApi.setup(null)); + + test('getInstanceTest', () async { + final MockTestProcessCameraProviderHostApi mockApi = + MockTestProcessCameraProviderHostApi(); + TestProcessCameraProviderHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final ProcessCameraProvider processCameraProvider = + ProcessCameraProvider.detached( + instanceManager: instanceManager, + ); + + instanceManager.addHostCreatedInstance( + processCameraProvider, + 0, + onCopy: (_) => ProcessCameraProvider.detached(), + ); + + when(mockApi.getInstance()).thenAnswer((_) async => 0); + expect( + await ProcessCameraProvider.getInstance( + instanceManager: instanceManager), + equals(processCameraProvider)); + verify(mockApi.getInstance()); + }); + + test('getAvailableCameraInfosTest', () async { + final MockTestProcessCameraProviderHostApi mockApi = + MockTestProcessCameraProviderHostApi(); + TestProcessCameraProviderHostApi.setup(mockApi); + + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final ProcessCameraProvider processCameraProvider = + ProcessCameraProvider.detached( + instanceManager: instanceManager, + ); + + instanceManager.addHostCreatedInstance( + processCameraProvider, + 0, + onCopy: (_) => ProcessCameraProvider.detached(), + ); + final CameraInfo fakeAvailableCameraInfo = + CameraInfo.detached(instanceManager: instanceManager); + instanceManager.addHostCreatedInstance( + fakeAvailableCameraInfo, + 1, + onCopy: (_) => CameraInfo.detached(), + ); + + when(mockApi.getAvailableCameraInfos(0)).thenReturn([1]); + expect(await processCameraProvider.getAvailableCameraInfos(), + equals([fakeAvailableCameraInfo])); + verify(mockApi.getAvailableCameraInfos(0)); + }); + + test('flutterApiCreateTest', () { + final InstanceManager instanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + final ProcessCameraProviderFlutterApiImpl flutterApi = + ProcessCameraProviderFlutterApiImpl( + instanceManager: instanceManager, + ); + + flutterApi.create(0); + + expect(instanceManager.getInstanceWithWeakReference(0), + isA()); + }); + }); +} diff --git a/packages/camera/camera_android_camerax/test/process_camera_provider_test.mocks.dart b/packages/camera/camera_android_camerax/test/process_camera_provider_test.mocks.dart new file mode 100644 index 000000000000..9fcfe690c062 --- /dev/null +++ b/packages/camera/camera_android_camerax/test/process_camera_provider_test.mocks.dart @@ -0,0 +1,40 @@ +// Mocks generated by Mockito 5.3.0 from annotations +// in camera_android_camerax/test/process_camera_provider_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i3; + +import 'package:mockito/mockito.dart' as _i1; + +import 'test_camerax_library.pigeon.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: 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 [TestProcessCameraProviderHostApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockTestProcessCameraProviderHostApi extends _i1.Mock + implements _i2.TestProcessCameraProviderHostApi { + MockTestProcessCameraProviderHostApi() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future getInstance() => + (super.noSuchMethod(Invocation.method(#getInstance, []), + returnValue: _i3.Future.value(0)) as _i3.Future); + @override + List getAvailableCameraInfos(int? identifier) => (super.noSuchMethod( + Invocation.method(#getAvailableCameraInfos, [identifier]), + returnValue: []) as List); +} diff --git a/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart b/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart index b10e14e9d518..2196b73d7fdb 100644 --- a/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart +++ b/packages/camera/camera_android_camerax/test/test_camerax_library.pigeon.dart @@ -135,3 +135,53 @@ abstract class TestCameraSelectorHostApi { } } } + +class _TestProcessCameraProviderHostApiCodec extends StandardMessageCodec { + const _TestProcessCameraProviderHostApiCodec(); +} + +abstract class TestProcessCameraProviderHostApi { + static const MessageCodec codec = + _TestProcessCameraProviderHostApiCodec(); + + Future getInstance(); + List getAvailableCameraInfos(int identifier); + static void setup(TestProcessCameraProviderHostApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getInstance', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + // ignore message + final int output = await api.getInstance(); + return {'result': output}; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.ProcessCameraProviderHostApi.getAvailableCameraInfos 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.ProcessCameraProviderHostApi.getAvailableCameraInfos was null, expected non-null int.'); + final List output = + api.getAvailableCameraInfos(arg_identifier!); + return {'result': output}; + }); + } + } + } +} From 35b975504b594edce0685dbf8372929ce1d4dd48 Mon Sep 17 00:00:00 2001 From: eugerossetto <101729072+eugerossetto@users.noreply.github.com> Date: Fri, 30 Sep 2022 11:53:10 -0300 Subject: [PATCH 774/844] [image_picker_windows] Annotate XTypeGroup constructor with // ignore: prefer_const_constructors (#6504) --- .../image_picker_windows/lib/image_picker_windows.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart b/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart index 0c6d6fbd6d66..10880d12ae6b 100644 --- a/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart +++ b/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart @@ -119,6 +119,8 @@ class ImagePickerWindows extends ImagePickerPlatform { 'ImageSource.gallery is currently the only supported source on Windows'); } final XTypeGroup typeGroup = + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors XTypeGroup(label: 'images', extensions: imageFormats); final XFile? file = await fileSelector .openFile(acceptedTypeGroups: [typeGroup]); @@ -143,6 +145,8 @@ class ImagePickerWindows extends ImagePickerPlatform { 'ImageSource.gallery is currently the only supported source on Windows'); } final XTypeGroup typeGroup = + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors XTypeGroup(label: 'videos', extensions: videoFormats); final XFile? file = await fileSelector .openFile(acceptedTypeGroups: [typeGroup]); @@ -159,6 +163,8 @@ class ImagePickerWindows extends ImagePickerPlatform { int? imageQuality, }) async { final XTypeGroup typeGroup = + // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 + // ignore: prefer_const_constructors XTypeGroup(label: 'images', extensions: imageFormats); final List files = await fileSelector .openFiles(acceptedTypeGroups: [typeGroup]); From e8f19eda9c483ade75c7097b08fd8981cf24c93a Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 30 Sep 2022 10:53:12 -0400 Subject: [PATCH 775/844] [tool] Only run postsubmit on changed packages (#6516) --- script/tool/CHANGELOG.md | 6 +- .../lib/src/common/git_version_finder.dart | 2 +- .../tool/lib/src/common/package_command.dart | 38 +++++++------ script/tool/pubspec.yaml | 2 +- .../test/common/package_command_test.dart | 57 ++++++++++++++----- 5 files changed, 71 insertions(+), 34 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index bf549d4e79dc..f62509040a3d 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,5 +1,9 @@ -## 0.11.1 +## 12.0 +* Changes the behavior of `--packages-for-branch` on main/master to run for + packages changed in the last commit, rather than running for all packages. + This allows CI to test the same filtered set of packages in post-submit as are + tested in presubmit. * Adds a `fix` command to run `dart fix --apply` in target packages. ## 0.11 diff --git a/script/tool/lib/src/common/git_version_finder.dart b/script/tool/lib/src/common/git_version_finder.dart index eb8fba6b76b8..b135424827a6 100644 --- a/script/tool/lib/src/common/git_version_finder.dart +++ b/script/tool/lib/src/common/git_version_finder.dart @@ -103,7 +103,7 @@ class GitVersionFinder { ['merge-base', '--fork-point', 'FETCH_HEAD', 'HEAD'], throwOnError: false); final String stdout = (baseShaFromMergeBase.stdout as String? ?? '').trim(); - final String stderr = (baseShaFromMergeBase.stdout as String? ?? '').trim(); + final String stderr = (baseShaFromMergeBase.stderr as String? ?? '').trim(); if (stderr.isNotEmpty || stdout.isEmpty) { baseShaFromMergeBase = await baseGitDir .runCommand(['merge-base', 'FETCH_HEAD', 'HEAD']); diff --git a/script/tool/lib/src/common/package_command.dart b/script/tool/lib/src/common/package_command.dart index 60d7ecc80076..1d82c4e936b6 100644 --- a/script/tool/lib/src/common/package_command.dart +++ b/script/tool/lib/src/common/package_command.dart @@ -84,9 +84,8 @@ abstract class PackageCommand extends Command { 'Cannot be combined with $_packagesArg.\n', hide: true); argParser.addFlag(_packagesForBranchArg, - help: - 'This runs on all packages (equivalent to no package selection flag)\n' - 'on main (or master), and behaves like --run-on-changed-packages on ' + help: 'This runs on all packages changed in the last commit on main ' + '(or master), and behaves like --run-on-changed-packages on ' 'any other branch.\n\n' 'Cannot be combined with $_packagesArg.\n\n' 'This is intended for use in CI.\n', @@ -311,9 +310,9 @@ abstract class PackageCommand extends Command { Set packages = Set.from(getStringListArg(_packagesArg)); - final bool runOnChangedPackages; + final GitVersionFinder? changedFileFinder; if (getBoolArg(_runOnChangedPackagesArg)) { - runOnChangedPackages = true; + changedFileFinder = await retrieveVersionFinder(); } else if (getBoolArg(_packagesForBranchArg)) { final String? branch = await _getBranch(); if (branch == null) { @@ -321,24 +320,28 @@ abstract class PackageCommand extends Command { 'only be used in a git repository.'); throw ToolExit(exitInvalidArguments); } else { - runOnChangedPackages = branch != 'master' && branch != 'main'; - // Log the mode for auditing what was intended to run. - print('--$_packagesForBranchArg: running on ' - '${runOnChangedPackages ? 'changed' : 'all'} packages'); + // Configure the change finder the correct mode for the branch. + final bool lastCommitOnly = branch == 'main' || branch == 'master'; + if (lastCommitOnly) { + // Log the mode to make it easier to audit logs to see that the + // intended diff was used. + print('--$_packagesForBranchArg: running on default branch; ' + 'using parent commit as the diff base.'); + changedFileFinder = GitVersionFinder(await gitDir, 'HEAD~'); + } else { + changedFileFinder = await retrieveVersionFinder(); + } } } else { - runOnChangedPackages = false; + changedFileFinder = null; } - final Set excludedPackageNames = getExcludedPackageNames(); - - if (runOnChangedPackages) { - final GitVersionFinder gitVersionFinder = await retrieveVersionFinder(); - final String baseSha = await gitVersionFinder.getBaseSha(); + if (changedFileFinder != null) { + final String baseSha = await changedFileFinder.getBaseSha(); print( - 'Running for all packages that have changed relative to "$baseSha"\n'); + 'Running for all packages that have diffs relative to "$baseSha"\n'); final List changedFiles = - await gitVersionFinder.getChangedFiles(); + await changedFileFinder.getChangedFiles(); if (!_changesRequireFullTest(changedFiles)) { packages = _getChangedPackageNames(changedFiles); } @@ -362,6 +365,7 @@ abstract class PackageCommand extends Command { .childDirectory('third_party') .childDirectory('packages'); + final Set excludedPackageNames = getExcludedPackageNames(); for (final Directory dir in [ packagesDir, if (thirdPartyPackagesDirectory.existsSync()) thirdPartyPackagesDirectory, diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 246c2ade2564..9e767ad724c0 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.11.0 +version: 0.12.0 dependencies: args: ^2.1.0 diff --git a/script/tool/test/common/package_command_test.dart b/script/tool/test/common/package_command_test.dart index c3d1ee343ff4..be21b86bff79 100644 --- a/script/tool/test/common/package_command_test.dart +++ b/script/tool/test/common/package_command_test.dart @@ -523,7 +523,7 @@ packages/plugin1/CHANGELOG output, containsAllInOrder([ contains( - 'Running for all packages that have changed relative to "main"'), + 'Running for all packages that have diffs relative to "main"'), ])); expect(command.plugins, unorderedEquals([plugin1.path])); @@ -732,13 +732,17 @@ packages/b_package/lib/src/foo.dart }); group('--packages-for-branch', () { - test('only tests changed packages on a branch', () async { + test('only tests changed packages relative to the merge base on a branch', + () async { processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: 'packages/plugin1/plugin1.dart'), ]; processRunner.mockProcessesForExecutable['git-rev-parse'] = [ MockProcess(stdout: 'a-branch'), ]; + processRunner.mockProcessesForExecutable['git-merge-base'] = [ + MockProcess(stdout: 'abc123'), + ]; final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir); createFakePlugin('plugin2', packagesDir); @@ -750,11 +754,20 @@ packages/b_package/lib/src/foo.dart expect( output, containsAllInOrder([ - contains('--packages-for-branch: running on changed packages'), + contains( + 'Running for all packages that have diffs relative to "abc123"'), ])); + // Ensure that it's diffing against the merge-base. + expect( + processRunner.recordedCalls, + contains( + const ProcessCall( + 'git-diff', ['--name-only', 'abc123', 'HEAD'], null), + )); }); - test('tests all packages on main', () async { + test('only tests changed packages relative to the previous commit on main', + () async { processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: 'packages/plugin1/plugin1.dart'), ]; @@ -763,19 +776,27 @@ packages/b_package/lib/src/foo.dart ]; final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir); - final RepositoryPackage plugin2 = - createFakePlugin('plugin2', packagesDir); + createFakePlugin('plugin2', packagesDir); final List output = await runCapturingPrint( runner, ['sample', '--packages-for-branch']); - expect(command.plugins, - unorderedEquals([plugin1.path, plugin2.path])); + expect(command.plugins, unorderedEquals([plugin1.path])); expect( output, containsAllInOrder([ - contains('--packages-for-branch: running on all packages'), + contains('--packages-for-branch: running on default branch; ' + 'using parent commit as the diff base'), + contains( + 'Running for all packages that have diffs relative to "HEAD~"'), ])); + // Ensure that it's diffing against the prior commit. + expect( + processRunner.recordedCalls, + contains( + const ProcessCall( + 'git-diff', ['--name-only', 'HEAD~', 'HEAD'], null), + )); }); test('tests all packages on master', () async { @@ -787,19 +808,27 @@ packages/b_package/lib/src/foo.dart ]; final RepositoryPackage plugin1 = createFakePlugin('plugin1', packagesDir); - final RepositoryPackage plugin2 = - createFakePlugin('plugin2', packagesDir); + createFakePlugin('plugin2', packagesDir); final List output = await runCapturingPrint( runner, ['sample', '--packages-for-branch']); - expect(command.plugins, - unorderedEquals([plugin1.path, plugin2.path])); + expect(command.plugins, unorderedEquals([plugin1.path])); expect( output, containsAllInOrder([ - contains('--packages-for-branch: running on all packages'), + contains('--packages-for-branch: running on default branch; ' + 'using parent commit as the diff base'), + contains( + 'Running for all packages that have diffs relative to "HEAD~"'), ])); + // Ensure that it's diffing against the prior commit. + expect( + processRunner.recordedCalls, + contains( + const ProcessCall( + 'git-diff', ['--name-only', 'HEAD~', 'HEAD'], null), + )); }); test('throws if getting the branch fails', () async { From dc5f1855cb628a38dc52ca518189a3d97a599cbd Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 30 Sep 2022 11:54:13 -0400 Subject: [PATCH 776/844] Roll Flutter from a11bef968893 to 99fb2d36e807 (20 revisions) (#6520) --- .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 953ea3223c85..52837a9367c2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a11bef968893d5c43a2d70cff649d251b8681277 +99fb2d36e80737c16b51b3e014fc996c901fdabe From b7bb9db36c8175298c7276b5f6e549ad5ca7cf9a Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 30 Sep 2022 17:55:50 -0400 Subject: [PATCH 777/844] [tool] Improve changed-package run mode logging (#6521) --- .../tool/lib/src/common/package_command.dart | 9 ++-- .../test/common/package_command_test.dart | 51 ++++++++++++++++--- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/script/tool/lib/src/common/package_command.dart b/script/tool/lib/src/common/package_command.dart index 1d82c4e936b6..0e83d03e9846 100644 --- a/script/tool/lib/src/common/package_command.dart +++ b/script/tool/lib/src/common/package_command.dart @@ -338,11 +338,14 @@ abstract class PackageCommand extends Command { if (changedFileFinder != null) { final String baseSha = await changedFileFinder.getBaseSha(); - print( - 'Running for all packages that have diffs relative to "$baseSha"\n'); final List changedFiles = await changedFileFinder.getChangedFiles(); - if (!_changesRequireFullTest(changedFiles)) { + if (_changesRequireFullTest(changedFiles)) { + print('Running for all packages, since a file has changed that could ' + 'affect the entire repository.'); + } else { + print( + 'Running for all packages that have diffs relative to "$baseSha"\n'); packages = _getChangedPackageNames(changedFiles); } } else if (getBoolArg(_runOnDirtyPackagesArg)) { diff --git a/script/tool/test/common/package_command_test.dart b/script/tool/test/common/package_command_test.dart index be21b86bff79..aa0a20253955 100644 --- a/script/tool/test/common/package_command_test.dart +++ b/script/tool/test/common/package_command_test.dart @@ -408,11 +408,18 @@ packages/plugin1/CHANGELOG createFakePlugin('plugin1', packagesDir); final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir); - await runCapturingPrint(runner, + + final List output = await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); expect(command.plugins, unorderedEquals([plugin1.path, plugin2.path])); + expect( + output, + containsAllInOrder([ + contains('Running for all packages, since a file has changed ' + 'that could affect the entire repository.') + ])); }); test('all plugins should be tested if .ci.yaml changes', () async { @@ -426,11 +433,17 @@ packages/plugin1/CHANGELOG createFakePlugin('plugin1', packagesDir); final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir); - await runCapturingPrint(runner, + final List output = await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); expect(command.plugins, unorderedEquals([plugin1.path, plugin2.path])); + expect( + output, + containsAllInOrder([ + contains('Running for all packages, since a file has changed ' + 'that could affect the entire repository.') + ])); }); test('all plugins should be tested if anything in .ci/ changes', @@ -445,14 +458,20 @@ packages/plugin1/CHANGELOG createFakePlugin('plugin1', packagesDir); final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir); - await runCapturingPrint(runner, + final List output = await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); expect(command.plugins, unorderedEquals([plugin1.path, plugin2.path])); + expect( + output, + containsAllInOrder([ + contains('Running for all packages, since a file has changed ' + 'that could affect the entire repository.') + ])); }); - test('all plugins should be tested if anything in script changes.', + test('all plugins should be tested if anything in script/ changes.', () async { processRunner.mockProcessesForExecutable['git-diff'] = [ MockProcess(stdout: ''' @@ -464,11 +483,17 @@ packages/plugin1/CHANGELOG createFakePlugin('plugin1', packagesDir); final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir); - await runCapturingPrint(runner, + final List output = await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); expect(command.plugins, unorderedEquals([plugin1.path, plugin2.path])); + expect( + output, + containsAllInOrder([ + contains('Running for all packages, since a file has changed ' + 'that could affect the entire repository.') + ])); }); test('all plugins should be tested if the root analysis options change.', @@ -483,11 +508,17 @@ packages/plugin1/CHANGELOG createFakePlugin('plugin1', packagesDir); final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir); - await runCapturingPrint(runner, + final List output = await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); expect(command.plugins, unorderedEquals([plugin1.path, plugin2.path])); + expect( + output, + containsAllInOrder([ + contains('Running for all packages, since a file has changed ' + 'that could affect the entire repository.') + ])); }); test('all plugins should be tested if formatting options change.', @@ -502,11 +533,17 @@ packages/plugin1/CHANGELOG createFakePlugin('plugin1', packagesDir); final RepositoryPackage plugin2 = createFakePlugin('plugin2', packagesDir); - await runCapturingPrint(runner, + final List output = await runCapturingPrint(runner, ['sample', '--base-sha=main', '--run-on-changed-packages']); expect(command.plugins, unorderedEquals([plugin1.path, plugin2.path])); + expect( + output, + containsAllInOrder([ + contains('Running for all packages, since a file has changed ' + 'that could affect the entire repository.') + ])); }); test('Only changed plugin should be tested.', () async { From 28c9c0d034d952c0b5bab6674133946af119c3cd Mon Sep 17 00:00:00 2001 From: eugerossetto <101729072+eugerossetto@users.noreply.github.com> Date: Fri, 30 Sep 2022 21:53:05 -0300 Subject: [PATCH 778/844] [file_selector] Convert XTypeGroup to const (#6476) --- .../CHANGELOG.md | 4 +++ .../src/types/x_type_group/x_type_group.dart | 28 +++++++++++-------- .../pubspec.yaml | 2 +- .../method_channel_file_selector_test.dart | 12 ++++---- .../test/x_type_group_test.dart | 12 ++++---- 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md index c4ee86a4ce19..ad1fe2cb6cef 100644 --- a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md +++ b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.0 + +* Makes `XTypeGroup`'s constructor constant. + ## 2.1.1 * Updates imports for `prefer_relative_imports`. diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart index 506dc1ce2de9..00cd56563c32 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart @@ -1,37 +1,43 @@ // 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; -/// A set of allowed XTypes +/// A set of allowed XTypes. +@immutable class XTypeGroup { /// Creates a new group with the given label and file extensions. /// /// A group with none of the type options provided indicates that any type is /// allowed. - XTypeGroup({ + const XTypeGroup({ this.label, List? extensions, this.mimeTypes, this.macUTIs, this.webWildCards, - }) : extensions = _removeLeadingDots(extensions); + }) : _extensions = extensions; - /// The 'name' or reference to this group of types + /// The 'name' or reference to this group of types. final String? label; - /// The extensions for this group - final List? extensions; - - /// The MIME types for this group + /// The MIME types for this group. final List? mimeTypes; - /// The UTIs for this group + /// The UTIs for this group. final List? macUTIs; - /// The web wild cards for this group (ex: image/*, video/*) + /// The web wild cards for this group (ex: image/*, video/*). final List? webWildCards; - /// Converts this object into a JSON formatted object + final List? _extensions; + + /// The extensions for this group. + List? get extensions { + return _removeLeadingDots(_extensions); + } + + /// Converts this object into a JSON formatted object. Map toJSON() { return { 'label': label, diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml index b01af3501cc7..c4500061a3a1 100644 --- a/packages/file_selector/file_selector_platform_interface/pubspec.yaml +++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%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.1.1 +version: 2.2.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart b/packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart index 33f9fbf45a8b..0f5f3a17ae0c 100644 --- a/packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/method_channel_file_selector_test.dart @@ -26,14 +26,14 @@ void main() { group('#openFile', () { test('passes the accepted type groups correctly', () async { - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], mimeTypes: ['text/plain'], macUTIs: ['public.text'], ); - final XTypeGroup groupTwo = XTypeGroup( + const XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], mimeTypes: ['image/jpg'], @@ -91,14 +91,14 @@ void main() { }); group('#openFiles', () { test('passes the accepted type groups correctly', () async { - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], mimeTypes: ['text/plain'], macUTIs: ['public.text'], ); - final XTypeGroup groupTwo = XTypeGroup( + const XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], mimeTypes: ['image/jpg'], @@ -157,14 +157,14 @@ void main() { group('#getSavePath', () { test('passes the accepted type groups correctly', () async { - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], mimeTypes: ['text/plain'], macUTIs: ['public.text'], ); - final XTypeGroup groupTwo = XTypeGroup( + const XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], mimeTypes: ['image/jpg'], diff --git a/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart b/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart index e09605c109c5..107cdc3a2538 100644 --- a/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart @@ -31,7 +31,7 @@ void main() { }); test('A wildcard group can be created', () { - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'Any', ); @@ -44,7 +44,7 @@ void main() { }); test('allowsAny treats empty arrays the same as null', () { - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'Any', extensions: [], mimeTypes: [], @@ -56,13 +56,13 @@ void main() { }); test('allowsAny returns false if anything is set', () { - final XTypeGroup extensionOnly = + const XTypeGroup extensionOnly = XTypeGroup(label: 'extensions', extensions: ['txt']); - final XTypeGroup mimeOnly = + const XTypeGroup mimeOnly = XTypeGroup(label: 'mime', mimeTypes: ['text/plain']); - final XTypeGroup utiOnly = + const XTypeGroup utiOnly = XTypeGroup(label: 'utis', macUTIs: ['public.text']); - final XTypeGroup webOnly = + const XTypeGroup webOnly = XTypeGroup(label: 'web', webWildCards: ['.txt']); expect(extensionOnly.allowsAny, false); From a295dc1288f4fdf2383b1bdc442137cc7b689c72 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 3 Oct 2022 11:43:32 -0400 Subject: [PATCH 779/844] Roll Flutter from 99fb2d36e807 to a4b246f2a88c (28 revisions) (#6530) --- .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 52837a9367c2..df69827cfd5a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -99fb2d36e80737c16b51b3e014fc996c901fdabe +a4b246f2a88c3f5b838c5abff8063a4e7436d44b From 25e10b2a036d7684edb7927751e8c43411ea0695 Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Mon, 3 Oct 2022 16:35:22 -0700 Subject: [PATCH 780/844] [video_player] Fixes a bug where the aspect ratio of some HLS videos are incorrectly inverted (#6507) --- .../video_player_avfoundation/CHANGELOG.md | 3 +- .../ios/RunnerTests/VideoPlayerTests.m | 25 ++++------- .../ios/Classes/FLTVideoPlayerPlugin.m | 42 +++++++++---------- .../video_player_avfoundation/pubspec.yaml | 2 +- 4 files changed, 31 insertions(+), 41 deletions(-) diff --git a/packages/video_player/video_player_avfoundation/CHANGELOG.md b/packages/video_player/video_player_avfoundation/CHANGELOG.md index 7cfa025271cf..ed2f345784bd 100644 --- a/packages/video_player/video_player_avfoundation/CHANGELOG.md +++ b/packages/video_player/video_player_avfoundation/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.3.7 +* Fixes a bug where the aspect ratio of some HLS videos are incorrectly inverted. * Updates code for `no_leading_underscores_for_local_identifiers` lint. ## 2.3.6 diff --git a/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m index 813fca2b8e7d..f9f66e04bcb3 100644 --- a/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m +++ b/packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m @@ -11,9 +11,6 @@ @interface FLTVideoPlayer : NSObject @property(readonly, nonatomic) AVPlayer *player; -// This is to fix a bug (https://github.com/flutter/flutter/issues/111457) in iOS 16 with blank -// video for encrypted video streams. An invisible AVPlayerLayer is used to overwrite the -// protection of pixel buffers in those streams. @property(readonly, nonatomic) AVPlayerLayer *playerLayer; @end @@ -65,13 +62,12 @@ @interface VideoPlayerTests : XCTestCase @implementation VideoPlayerTests -- (void)testIOS16BugWithEncryptedVideoStream { - // This is to fix a bug (https://github.com/flutter/flutter/issues/111457) in iOS 16 with blank - // video for encrypted video streams. An invisible AVPlayerLayer is used to overwrite the - // protection of pixel buffers in those streams. - // Note that a better unit test is to validate that `copyPixelBuffer` API returns the pixel - // buffers as expected, which requires setting up the video player properly with a protected video - // stream (.m3u8 file). +- (void)testBlankVideoBugWithEncryptedVideoStreamAndInvertedAspectRatioBugForSomeVideoStream { + // This is to fix 2 bugs: 1. blank video for encrypted video streams on iOS 16 + // (https://github.com/flutter/flutter/issues/111457) and 2. swapped width and height for some + // video streams (not just iOS 16). (https://github.com/flutter/flutter/issues/109116). An + // invisible AVPlayerLayer is used to overwrite the protection of pixel buffers in those streams + // for issue #1, and restore the correct width and height for issue #2. NSObject *registry = (NSObject *)[[UIApplication sharedApplication] delegate]; NSObject *registrar = @@ -95,13 +91,8 @@ - (void)testIOS16BugWithEncryptedVideoStream { FLTVideoPlayer *player = videoPlayerPlugin.playersByTextureId[textureMessage.textureId]; XCTAssertNotNil(player); - if (@available(iOS 16.0, *)) { - XCTAssertNotNil(player.playerLayer, @"AVPlayerLayer should be present for iOS 16."); - XCTAssertNotNil(player.playerLayer.superlayer, - @"AVPlayerLayer should be added on screen for iOS 16."); - } else { - XCTAssertNil(player.playerLayer, @"AVPlayerLayer should not be present before iOS 16."); - } + XCTAssertNotNil(player.playerLayer, @"AVPlayerLayer should be present."); + XCTAssertNotNil(player.playerLayer.superlayer, @"AVPlayerLayer should be added on screen."); } - (void)testSeekToInvokesTextureFrameAvailableOnTextureRegistry { diff --git a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m index 645c86d6eade..3b066769621c 100644 --- a/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m +++ b/packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m @@ -36,7 +36,11 @@ - (void)onDisplayLink:(CADisplayLink *)link { @interface FLTVideoPlayer : NSObject @property(readonly, nonatomic) AVPlayer *player; @property(readonly, nonatomic) AVPlayerItemVideoOutput *videoOutput; -/// An invisible player layer used to access the pixel buffers in protected video streams in iOS 16. +// This is to fix 2 bugs: 1. blank video for encrypted video streams on iOS 16 +// (https://github.com/flutter/flutter/issues/111457) and 2. swapped width and height for some video +// streams (not just iOS 16). (https://github.com/flutter/flutter/issues/109116). +// An invisible AVPlayerLayer is used to overwrite the protection of pixel buffers in those streams +// for issue #1, and restore the correct width and height for issue #2. @property(readonly, nonatomic) AVPlayerLayer *playerLayer; @property(readonly, nonatomic) CADisplayLink *displayLink; @property(nonatomic) FlutterEventChannel *eventChannel; @@ -134,17 +138,13 @@ NS_INLINE CGFloat radiansToDegrees(CGFloat radians) { return degrees; }; -NS_INLINE UIViewController *rootViewController() API_AVAILABLE(ios(16.0)) { - for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) { - if ([scene isKindOfClass:UIWindowScene.class]) { - for (UIWindow *window in ((UIWindowScene *)scene).windows) { - if (window.isKeyWindow) { - return window.rootViewController; - } - } - } - } - return nil; +NS_INLINE UIViewController *rootViewController() { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // TODO: (hellohuanlin) Provide a non-deprecated codepath. See + // https://github.com/flutter/flutter/issues/104117 + return UIApplication.sharedApplication.keyWindow.rootViewController; +#pragma clang diagnostic pop } - (AVMutableVideoComposition *)getVideoCompositionWithTransform:(CGAffineTransform)transform @@ -242,13 +242,13 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem *)item _player = [AVPlayer playerWithPlayerItem:item]; _player.actionAtItemEnd = AVPlayerActionAtItemEndNone; - // This is to fix a bug (https://github.com/flutter/flutter/issues/111457) in iOS 16 with blank - // video for encrypted video streams. An invisible AVPlayerLayer is used to overwrite the - // protection of pixel buffers in those streams. - if (@available(iOS 16.0, *)) { - _playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player]; - [rootViewController().view.layer addSublayer:_playerLayer]; - } + // This is to fix 2 bugs: 1. blank video for encrypted video streams on iOS 16 + // (https://github.com/flutter/flutter/issues/111457) and 2. swapped width and height for some + // video streams (not just iOS 16). (https://github.com/flutter/flutter/issues/109116). An + // invisible AVPlayerLayer is used to overwrite the protection of pixel buffers in those streams + // for issue #1, and restore the correct width and height for issue #2. + _playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player]; + [rootViewController().view.layer addSublayer:_playerLayer]; [self createVideoOutputAndDisplayLink:frameUpdater]; @@ -481,9 +481,7 @@ - (FlutterError *_Nullable)onListenWithArguments:(id _Nullable)arguments /// so the channel is going to die or is already dead. - (void)disposeSansEventChannel { _disposed = YES; - if (@available(iOS 16.0, *)) { - [_playerLayer removeFromSuperlayer]; - } + [_playerLayer removeFromSuperlayer]; [_displayLink invalidate]; AVPlayerItem *currentItem = self.player.currentItem; [currentItem removeObserver:self forKeyPath:@"status"]; diff --git a/packages/video_player/video_player_avfoundation/pubspec.yaml b/packages/video_player/video_player_avfoundation/pubspec.yaml index bd88ddf94876..6da166791281 100644 --- a/packages/video_player/video_player_avfoundation/pubspec.yaml +++ b/packages/video_player/video_player_avfoundation/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avfoundation description: iOS implementation of the video_player plugin. repository: https://github.com/flutter/plugins/tree/main/packages/video_player/video_player_avfoundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22 -version: 2.3.6 +version: 2.3.7 environment: sdk: ">=2.14.0 <3.0.0" From 911dcc7670f736dd7785386c69cd1c298dbe7180 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 4 Oct 2022 11:52:20 -0400 Subject: [PATCH 781/844] Roll Flutter from a4b246f2a88c to 55d67cc7d992 (20 revisions) (#6534) --- .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 df69827cfd5a..aa54d97b2eaf 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a4b246f2a88c3f5b838c5abff8063a4e7436d44b +55d67cc7d99226cd12d6c4a76de357fc2c92823e From 221548ff75b1a3395fd7cd8107025aff42be9e2d Mon Sep 17 00:00:00 2001 From: hellohuanlin <41930132+hellohuanlin@users.noreply.github.com> Date: Tue, 4 Oct 2022 15:19:51 -0700 Subject: [PATCH 782/844] [quick_actions]Migrate the XCUITests to Swift (#6425) --- .../quick_actions_ios/CHANGELOG.md | 4 + .../ios/Runner.xcodeproj/project.pbxproj | 14 ++- .../example/ios/RunnerUITests/RunnerUITests.m | 99 ------------------- .../ios/RunnerUITests/RunnerUITests.swift | 96 ++++++++++++++++++ 4 files changed, 110 insertions(+), 103 deletions(-) delete mode 100644 packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/RunnerUITests.m create mode 100644 packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/RunnerUITests.swift diff --git a/packages/quick_actions/quick_actions_ios/CHANGELOG.md b/packages/quick_actions/quick_actions_ios/CHANGELOG.md index e002bd70ac09..31fe43832d2f 100644 --- a/packages/quick_actions/quick_actions_ios/CHANGELOG.md +++ b/packages/quick_actions/quick_actions_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Migrates `RunnerUITests` to Swift. + ## 1.0.1 * Removes custom modulemap file with "Test" submodule and private headers for Swift migration. diff --git a/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj index 47360fe51a70..c853a197ca9b 100644 --- a/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/quick_actions/quick_actions_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -10,13 +10,13 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 2632072169FF635893D8EB4D /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 436668746754BEEA28B76E55 /* libPods-RunnerTests.a */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 686BE83025E58CCF00862533 /* RunnerUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 686BE82F25E58CCF00862533 /* RunnerUITests.m */; }; 6A841C2B6AED5CF8DB2A1894 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C35AD3650AB6BF850E016715 /* libPods-Runner.a */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 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 */; }; + E092A7F628D128EB005C7F67 /* RunnerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E092A7F528D128EB005C7F67 /* RunnerUITests.swift */; }; E0C09C29289C729D00E6977E /* FLTQuickActionsPluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C09C28289C729D00E6977E /* FLTQuickActionsPluginTests.m */; }; E0C09C32289DBFCA00E6977E /* FLTShortcutStateManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E0C09C31289DBFCA00E6977E /* FLTShortcutStateManagerTests.m */; }; /* End PBXBuildFile section */ @@ -60,7 +60,6 @@ 436668746754BEEA28B76E55 /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 5278439583922091276A37C9 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 686BE82D25E58CCF00862533 /* RunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 686BE82F25E58CCF00862533 /* RunnerUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerUITests.m; sourceTree = ""; }; 686BE83125E58CCF00862533 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; @@ -76,6 +75,7 @@ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9D27FE1F0F21D4D47DDA16DE /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; C35AD3650AB6BF850E016715 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + E092A7F528D128EB005C7F67 /* RunnerUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerUITests.swift; sourceTree = ""; }; E0C09C28289C729D00E6977E /* FLTQuickActionsPluginTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTQuickActionsPluginTests.m; sourceTree = ""; }; E0C09C31289DBFCA00E6977E /* FLTShortcutStateManagerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTShortcutStateManagerTests.m; sourceTree = ""; }; F0609304FBCAEC2289164BD5 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; @@ -121,8 +121,8 @@ 686BE82E25E58CCF00862533 /* RunnerUITests */ = { isa = PBXGroup; children = ( - 686BE82F25E58CCF00862533 /* RunnerUITests.m */, 686BE83125E58CCF00862533 /* Info.plist */, + E092A7F528D128EB005C7F67 /* RunnerUITests.swift */, ); path = RunnerUITests; sourceTree = ""; @@ -281,6 +281,7 @@ }; 686BE82C25E58CCF00862533 = { CreatedOnToolsVersion = 12.4; + LastSwiftMigration = 1330; ProvisioningStyle = Automatic; TestTargetID = 97C146ED1CF9000F007C117D; }; @@ -424,7 +425,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 686BE83025E58CCF00862533 /* RunnerUITests.m in Sources */, + E092A7F628D128EB005C7F67 /* RunnerUITests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -512,6 +513,7 @@ buildSettings = { CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; @@ -529,6 +531,8 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerUITests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = Runner; }; @@ -539,6 +543,7 @@ buildSettings = { CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; @@ -555,6 +560,7 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.google.RunnerUITests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = Runner; }; diff --git a/packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/RunnerUITests.m b/packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/RunnerUITests.m deleted file mode 100644 index 0bad57f886de..000000000000 --- a/packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/RunnerUITests.m +++ /dev/null @@ -1,99 +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 -#import - -static const int kElementWaitingTime = 30; - -@interface RunnerUITests : XCTestCase - -@end - -@implementation RunnerUITests { - XCUIApplication *_exampleApp; -} - -- (void)setUp { - [super setUp]; - self.continueAfterFailure = NO; - _exampleApp = [[XCUIApplication alloc] init]; -} - -- (void)tearDown { - [super tearDown]; - [_exampleApp terminate]; - _exampleApp = nil; -} - -- (void)testQuickActionWithFreshStart { - XCUIApplication *springboard = - [[XCUIApplication alloc] initWithBundleIdentifier:@"com.apple.springboard"]; - XCUIElement *quickActionsAppIcon = springboard.icons[@"quick_actions_example"]; - if (![quickActionsAppIcon waitForExistenceWithTimeout:kElementWaitingTime]) { - os_log_error(OS_LOG_DEFAULT, "%@", springboard.debugDescription); - XCTFail(@"Failed due to not able to find the example app from springboard with %@ seconds", - @(kElementWaitingTime)); - } - - [quickActionsAppIcon pressForDuration:2]; - XCUIElement *actionTwo = springboard.buttons[@"Action two"]; - if (![actionTwo waitForExistenceWithTimeout:kElementWaitingTime]) { - os_log_error(OS_LOG_DEFAULT, "%@", springboard.debugDescription); - XCTFail(@"Failed due to not able to find the actionTwo button from springboard with %@ seconds", - @(kElementWaitingTime)); - } - - [actionTwo tap]; - - XCUIElement *actionTwoConfirmation = _exampleApp.otherElements[@"action_two"]; - if (![actionTwoConfirmation waitForExistenceWithTimeout:kElementWaitingTime]) { - os_log_error(OS_LOG_DEFAULT, "%@", springboard.debugDescription); - XCTFail(@"Failed due to not able to find the actionTwoConfirmation in the app with %@ seconds", - @(kElementWaitingTime)); - } - XCTAssertTrue(actionTwoConfirmation.exists); -} - -- (void)testQuickActionWhenAppIsInBackground { - [_exampleApp launch]; - - XCUIElement *actionsReady = _exampleApp.otherElements[@"actions ready"]; - if (![actionsReady waitForExistenceWithTimeout:kElementWaitingTime]) { - os_log_error(OS_LOG_DEFAULT, "%@", _exampleApp.debugDescription); - XCTFail(@"Failed due to not able to find the actionsReady in the app with %@ seconds", - @(kElementWaitingTime)); - } - - [[XCUIDevice sharedDevice] pressButton:XCUIDeviceButtonHome]; - - XCUIApplication *springboard = - [[XCUIApplication alloc] initWithBundleIdentifier:@"com.apple.springboard"]; - XCUIElement *quickActionsAppIcon = springboard.icons[@"quick_actions_example"]; - if (![quickActionsAppIcon waitForExistenceWithTimeout:kElementWaitingTime]) { - os_log_error(OS_LOG_DEFAULT, "%@", springboard.debugDescription); - XCTFail(@"Failed due to not able to find the example app from springboard with %@ seconds", - @(kElementWaitingTime)); - } - - [quickActionsAppIcon pressForDuration:2]; - XCUIElement *actionOne = springboard.buttons[@"Action one"]; - if (![actionOne waitForExistenceWithTimeout:kElementWaitingTime]) { - os_log_error(OS_LOG_DEFAULT, "%@", springboard.debugDescription); - XCTFail(@"Failed due to not able to find the actionOne button from springboard with %@ seconds", - @(kElementWaitingTime)); - } - - [actionOne tap]; - - XCUIElement *actionOneConfirmation = _exampleApp.otherElements[@"action_one"]; - if (![actionOneConfirmation waitForExistenceWithTimeout:kElementWaitingTime]) { - os_log_error(OS_LOG_DEFAULT, "%@", springboard.debugDescription); - XCTFail(@"Failed due to not able to find the actionOneConfirmation in the app with %@ seconds", - @(kElementWaitingTime)); - } - XCTAssertTrue(actionOneConfirmation.exists); -} - -@end diff --git a/packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/RunnerUITests.swift b/packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/RunnerUITests.swift new file mode 100644 index 000000000000..a59692e7639d --- /dev/null +++ b/packages/quick_actions/quick_actions_ios/example/ios/RunnerUITests/RunnerUITests.swift @@ -0,0 +1,96 @@ +// 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 + +private let elementWaitingTime: TimeInterval = 30 + +class RunnerUITests: XCTestCase { + + private var exampleApp: XCUIApplication! + + override func setUp() { + super.setUp() + self.continueAfterFailure = false + exampleApp = XCUIApplication() + } + + override func tearDown() { + super.tearDown() + exampleApp.terminate() + exampleApp = nil + } + + func testQuickActionWithFreshStart() { + let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") + let quickActionsAppIcon = springboard.icons["quick_actions_example"] + if !quickActionsAppIcon.waitForExistence(timeout: elementWaitingTime) { + XCTFail( + "Failed due to not able to find the example app from springboard with \(elementWaitingTime) seconds. Springboard debug description: \(springboard.debugDescription)" + ) + } + + quickActionsAppIcon.press(forDuration: 2) + + let actionTwo = springboard.buttons["Action two"] + if !actionTwo.waitForExistence(timeout: elementWaitingTime) { + XCTFail( + "Failed due to not able to find the actionTwo button from springboard with \(elementWaitingTime) seconds. Springboard debug description: \(springboard.debugDescription)" + ) + } + + actionTwo.tap() + + let actionTwoConfirmation = exampleApp.otherElements["action_two"] + if !actionTwoConfirmation.waitForExistence(timeout: elementWaitingTime) { + XCTFail( + "Failed due to not able to find the actionTwoConfirmation in the app with \(elementWaitingTime) seconds. Springboard debug description: \(springboard.debugDescription)" + ) + } + + XCTAssert(actionTwoConfirmation.exists) + } + + func testQuickActionWhenAppIsInBackground() { + exampleApp.launch() + + let actionsReady = exampleApp.otherElements["actions ready"] + + if !actionsReady.waitForExistence(timeout: elementWaitingTime) { + XCTFail( + "Failed due to not able to find the actionsReady in the app with \(elementWaitingTime) seconds. App debug description: \(exampleApp.debugDescription)" + ) + } + + XCUIDevice.shared.press(.home) + + let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") + let quickActionsAppIcon = springboard.icons["quick_actions_example"] + if !quickActionsAppIcon.waitForExistence(timeout: elementWaitingTime) { + XCTFail( + "Failed due to not able to find the example app from springboard with \(elementWaitingTime) seconds. Springboard debug description: \(springboard.debugDescription)" + ) + } + + quickActionsAppIcon.press(forDuration: 2) + + let actionOne = springboard.buttons["Action one"] + if !actionOne.waitForExistence(timeout: elementWaitingTime) { + XCTFail( + "Failed due to not able to find the actionOne button from springboard with \(elementWaitingTime) seconds. Springboard debug description: \(springboard.debugDescription)" + ) + } + + actionOne.tap() + + let actionOneConfirmation = exampleApp.otherElements["action_one"] + if !actionOneConfirmation.waitForExistence(timeout: elementWaitingTime) { + XCTFail( + "Failed due to not able to find the actionOneConfirmation in the app with \(elementWaitingTime) seconds. Springboard debug description: \(springboard.debugDescription)" + ) + } + + XCTAssert(actionOneConfirmation.exists) + } +} From 3a928b8a3d8b5714c0777e66d7f74cf4ffbe800f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Oct 2022 22:32:57 +0000 Subject: [PATCH 783/844] [webview]: Bump annotation from 1.4.0 to 1.5.0 in /packages/webview_flutter/webview_flutter_android/android (#6488) --- packages/webview_flutter/webview_flutter_android/CHANGELOG.md | 3 ++- .../webview_flutter_android/android/build.gradle | 2 +- packages/webview_flutter/webview_flutter_android/pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index dfad03ebaa15..fbb502c7cca8 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.10.4 * Updates code for `no_leading_underscores_for_local_identifiers` lint. +* Bumps androidx.annotation from 1.4.0 to 1.5.0. ## 2.10.3 diff --git a/packages/webview_flutter/webview_flutter_android/android/build.gradle b/packages/webview_flutter/webview_flutter_android/android/build.gradle index 0b95a4be66ff..65374fee88bf 100644 --- a/packages/webview_flutter/webview_flutter_android/android/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/android/build.gradle @@ -35,7 +35,7 @@ android { } dependencies { - implementation 'androidx.annotation:annotation:1.4.0' + implementation 'androidx.annotation:annotation:1.5.0' implementation 'androidx.webkit:webkit:1.5.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:4.8.0' diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index c2a326f7d4ac..e411b4e1326a 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/plugins/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: 2.10.3 +version: 2.10.4 environment: sdk: ">=2.14.0 <3.0.0" From fb5c86c9532ba883e9e5bf7598e64196c3e85554 Mon Sep 17 00:00:00 2001 From: Piotr Mitkowski Date: Wed, 5 Oct 2022 19:01:05 +0200 Subject: [PATCH 784/844] [image_picker] add requestFullMetadata for iOS (optional permissions) (#5915) --- .../image_picker/image_picker/CHANGELOG.md | 3 +- packages/image_picker/image_picker/README.md | 1 + .../image_picker/example/lib/main.dart | 2 +- .../image_picker/lib/image_picker.dart | 70 ++-- .../image_picker/image_picker/pubspec.yaml | 6 +- .../image_picker/test/image_picker_test.dart | 355 +++++++++++++++--- .../test/image_picker_test.mocks.dart | 18 + 7 files changed, 386 insertions(+), 69 deletions(-) diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index ccf1cb3c5e7d..76192566b18b 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,7 +1,8 @@ -## NEXT +## 0.8.6 * Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. +* Adds `requestFullMetadata` option to `pickImage`, so images on iOS can be picked without `Photo Library Usage` permission. ## 0.8.5+3 diff --git a/packages/image_picker/image_picker/README.md b/packages/image_picker/image_picker/README.md index 2fa20be34859..aadfc83ff5e6 100755 --- a/packages/image_picker/image_picker/README.md +++ b/packages/image_picker/image_picker/README.md @@ -23,6 +23,7 @@ As a result of implementing PHPicker it becomes impossible to pick HEIC images o Add the following keys to your _Info.plist_ file, located in `/ios/Runner/Info.plist`: * `NSPhotoLibraryUsageDescription` - describe why your app needs permission for the photo library. This is called _Privacy - Photo Library Usage Description_ in the visual editor. + * This permission is not required for image picking on iOS 11+ if you pass `false` for `requestFullMetadata`. * `NSCameraUsageDescription` - describe why your app needs access to the camera. This is called _Privacy - Camera Usage Description_ in the visual editor. * `NSMicrophoneUsageDescription` - describe why your app needs access to the microphone, if you intend to record videos. This is called _Privacy - Microphone Usage Description_ in the visual editor. diff --git a/packages/image_picker/image_picker/example/lib/main.dart b/packages/image_picker/image_picker/example/lib/main.dart index 4eecc5fa2a1a..5e448ddbee68 100755 --- a/packages/image_picker/image_picker/example/lib/main.dart +++ b/packages/image_picker/image_picker/example/lib/main.dart @@ -93,7 +93,7 @@ class _MyHomePageState extends State { await _displayPickImageDialog(context!, (double? maxWidth, double? maxHeight, int? quality) async { try { - final List? pickedFileList = await _picker.pickMultiImage( + final List pickedFileList = await _picker.pickMultiImage( maxWidth: maxWidth, maxHeight: maxHeight, imageQuality: quality, diff --git a/packages/image_picker/image_picker/lib/image_picker.dart b/packages/image_picker/image_picker/lib/image_picker.dart index 84c649028c96..2e266ccd5a5a 100755 --- a/packages/image_picker/image_picker/lib/image_picker.dart +++ b/packages/image_picker/image_picker/lib/image_picker.dart @@ -173,8 +173,9 @@ class ImagePicker { /// The `source` argument controls where the image comes from. This can /// be either [ImageSource.camera] or [ImageSource.gallery]. /// - /// Where iOS supports HEIC images, Android 8 and below doesn't. Android 9 and above only support HEIC images if used - /// in addition to a size modification, of which the usage is explained below. + /// Where iOS supports HEIC images, Android 8 and below doesn't. Android 9 and + /// above only support HEIC images if used in addition to a size modification, + /// of which the usage is explained below. /// /// If specified, the image will be at most `maxWidth` wide and /// `maxHeight` tall. Otherwise the image will be returned at it's @@ -182,14 +183,22 @@ class ImagePicker { /// The `imageQuality` argument modifies the quality of the image, ranging from 0-100 /// where 100 is the original/max quality. If `imageQuality` is null, the image with /// the original quality will be returned. Compression is only supported for certain - /// image types such as JPEG and on Android PNG and WebP, too. If compression is not supported for the image that is picked, - /// a warning message will be logged. - /// - /// Use `preferredCameraDevice` to specify the camera to use when the `source` is [ImageSource.camera]. - /// The `preferredCameraDevice` is ignored when `source` is [ImageSource.gallery]. It is also ignored if the chosen camera is not supported on the device. - /// Defaults to [CameraDevice.rear]. Note that Android has no documented parameter for an intent to specify if - /// the front or rear camera should be opened, this function is not guaranteed - /// to work on an Android device. + /// image types such as JPEG and on Android PNG and WebP, too. If compression is not + /// supported for the image that is picked, a warning message will be logged. + /// + /// Use `preferredCameraDevice` to specify the camera to use when the `source` is + /// [ImageSource.camera]. + /// The `preferredCameraDevice` is ignored when `source` is [ImageSource.gallery]. + /// It is also ignored if the chosen camera is not supported on the device. + /// Defaults to [CameraDevice.rear]. Note that Android has no documented parameter + /// for an intent to specify if the front or rear camera should be opened, this + /// function is not guaranteed to work on an Android device. + /// + /// Use `requestFullMetadata` (defaults to `true`) to control how much additional + /// information the plugin tries to get. + /// If `requestFullMetadata` is set to `true`, the plugin tries to get the full + /// image metadata which may require extra permission requests on some platforms, + /// such as `Photo Library Usage` permission on iOS. /// /// In Android, the MainActivity can be destroyed for various reasons. If that happens, the result will be lost /// in this call. You can then call [retrieveLostData] when your app relaunches to retrieve the lost data. @@ -206,6 +215,7 @@ class ImagePicker { double? maxHeight, int? imageQuality, CameraDevice preferredCameraDevice = CameraDevice.rear, + bool requestFullMetadata = true, }) { if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { throw ArgumentError.value( @@ -218,12 +228,15 @@ class ImagePicker { throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative'); } - return platform.getImage( + return platform.getImageFromSource( source: source, - maxWidth: maxWidth, - maxHeight: maxHeight, - imageQuality: imageQuality, - preferredCameraDevice: preferredCameraDevice, + options: ImagePickerOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + preferredCameraDevice: preferredCameraDevice, + requestFullMetadata: requestFullMetadata, + ), ); } @@ -239,11 +252,18 @@ class ImagePicker { /// If specified, the images will be at most `maxWidth` wide and /// `maxHeight` tall. Otherwise the images will be returned at it's /// original width and height. + /// /// The `imageQuality` argument modifies the quality of the images, ranging from 0-100 /// where 100 is the original/max quality. If `imageQuality` is null, the images with /// the original quality will be returned. Compression is only supported for certain - /// image types such as JPEG and on Android PNG and WebP, too. If compression is not supported for the image that is picked, - /// a warning message will be logged. + /// image types such as JPEG and on Android PNG and WebP, too. If compression is not + /// supported for the image that is picked, a warning message will be logged. + /// + /// Use `requestFullMetadata` (defaults to `true`) to control how much additional + /// information the plugin tries to get. + /// If `requestFullMetadata` is set to `true`, the plugin tries to get the full + /// image metadata which may require extra permission requests on some platforms, + /// such as `Photo Library Usage` permission on iOS. /// /// The method could throw [PlatformException] if the app does not have permission to access /// the camera or photos gallery, no camera is available, plugin is already in use, @@ -251,10 +271,11 @@ class ImagePicker { /// be allocated (Android only) or due to an unknown error. /// /// See also [pickImage] to allow users to only pick a single image. - Future?> pickMultiImage({ + Future> pickMultiImage({ double? maxWidth, double? maxHeight, int? imageQuality, + bool requestFullMetadata = true, }) { if (imageQuality != null && (imageQuality < 0 || imageQuality > 100)) { throw ArgumentError.value( @@ -267,10 +288,15 @@ class ImagePicker { throw ArgumentError.value(maxHeight, 'maxHeight', 'cannot be negative'); } - return platform.getMultiImage( - maxWidth: maxWidth, - maxHeight: maxHeight, - imageQuality: imageQuality, + return platform.getMultiImageWithOptions( + options: MultiImagePickerOptions( + imageOptions: ImageOptions( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: imageQuality, + requestFullMetadata: requestFullMetadata, + ), + ), ); } diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 58f9f0658483..7fed3bf4637b 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.5+3 +version: 0.8.6 environment: sdk: ">=2.14.0 <3.0.0" @@ -24,8 +24,8 @@ dependencies: sdk: flutter image_picker_android: ^0.8.4+11 image_picker_for_web: ^2.1.0 - image_picker_ios: ^0.8.4+11 - image_picker_platform_interface: ^2.3.0 + image_picker_ios: ^0.8.6+1 + image_picker_platform_interface: ^2.6.1 dev_dependencies: build_runner: ^2.1.10 diff --git a/packages/image_picker/image_picker/test/image_picker_test.dart b/packages/image_picker/image_picker/test/image_picker_test.dart index 10b5ac16e570..2d959bd20f7b 100644 --- a/packages/image_picker/image_picker/test/image_picker_test.dart +++ b/packages/image_picker/image_picker/test/image_picker_test.dart @@ -29,12 +29,8 @@ void main() { group('#Single image/video', () { group('#pickImage', () { setUp(() { - when(mockPlatform.getImage( - source: anyNamed('source'), - maxWidth: anyNamed('maxWidth'), - maxHeight: anyNamed('maxHeight'), - imageQuality: anyNamed('imageQuality'), - preferredCameraDevice: anyNamed('preferredCameraDevice'))) + when(mockPlatform.getImageFromSource( + source: anyNamed('source'), options: anyNamed('options'))) .thenAnswer((Invocation _) async => null); }); @@ -44,8 +40,20 @@ void main() { await picker.pickImage(source: ImageSource.gallery); verifyInOrder([ - mockPlatform.getImage(source: ImageSource.camera), - mockPlatform.getImage(source: ImageSource.gallery), + mockPlatform.getImageFromSource( + source: ImageSource.camera, + options: argThat( + isInstanceOf(), + named: 'options', + ), + ), + mockPlatform.getImageFromSource( + source: ImageSource.gallery, + options: argThat( + isInstanceOf(), + named: 'options', + ), + ), ]); }); @@ -76,20 +84,111 @@ void main() { imageQuality: 70); verifyInOrder([ - mockPlatform.getImage(source: ImageSource.camera), - mockPlatform.getImage(source: ImageSource.camera, maxWidth: 10.0), - mockPlatform.getImage(source: ImageSource.camera, maxHeight: 10.0), - mockPlatform.getImage( - source: ImageSource.camera, maxWidth: 10.0, maxHeight: 20.0), - mockPlatform.getImage( - source: ImageSource.camera, maxWidth: 10.0, imageQuality: 70), - mockPlatform.getImage( - source: ImageSource.camera, maxHeight: 10.0, imageQuality: 70), - mockPlatform.getImage( - source: ImageSource.camera, - maxWidth: 10.0, - maxHeight: 20.0, - imageQuality: 70), + mockPlatform.getImageFromSource( + source: ImageSource.camera, + options: argThat( + isInstanceOf() + .having((ImagePickerOptions options) => options.maxWidth, + 'maxWidth', isNull) + .having((ImagePickerOptions options) => options.maxHeight, + 'maxHeight', isNull) + .having( + (ImagePickerOptions options) => options.imageQuality, + 'imageQuality', + isNull), + named: 'options', + ), + ), + mockPlatform.getImageFromSource( + source: ImageSource.camera, + options: argThat( + isInstanceOf() + .having((ImagePickerOptions options) => options.maxWidth, + 'maxWidth', equals(10.0)) + .having((ImagePickerOptions options) => options.maxHeight, + 'maxHeight', isNull) + .having( + (ImagePickerOptions options) => options.imageQuality, + 'imageQuality', + isNull), + named: 'options', + ), + ), + mockPlatform.getImageFromSource( + source: ImageSource.camera, + options: argThat( + isInstanceOf() + .having((ImagePickerOptions options) => options.maxWidth, + 'maxWidth', isNull) + .having((ImagePickerOptions options) => options.maxHeight, + 'maxHeight', equals(10.0)) + .having( + (ImagePickerOptions options) => options.imageQuality, + 'imageQuality', + isNull), + named: 'options', + ), + ), + mockPlatform.getImageFromSource( + source: ImageSource.camera, + options: argThat( + isInstanceOf() + .having((ImagePickerOptions options) => options.maxWidth, + 'maxWidth', equals(10.0)) + .having((ImagePickerOptions options) => options.maxHeight, + 'maxHeight', equals(20.0)) + .having( + (ImagePickerOptions options) => options.imageQuality, + 'imageQuality', + isNull), + named: 'options', + ), + ), + mockPlatform.getImageFromSource( + source: ImageSource.camera, + options: argThat( + isInstanceOf() + .having((ImagePickerOptions options) => options.maxWidth, + 'maxWidth', equals(10.0)) + .having((ImagePickerOptions options) => options.maxHeight, + 'maxHeight', isNull) + .having( + (ImagePickerOptions options) => options.imageQuality, + 'imageQuality', + equals(70)), + named: 'options', + ), + ), + mockPlatform.getImageFromSource( + source: ImageSource.camera, + options: argThat( + isInstanceOf() + .having((ImagePickerOptions options) => options.maxWidth, + 'maxWidth', isNull) + .having((ImagePickerOptions options) => options.maxHeight, + 'maxHeight', equals(10.0)) + .having( + (ImagePickerOptions options) => options.imageQuality, + 'imageQuality', + equals(70)), + named: 'options', + ), + ), + mockPlatform.getImageFromSource( + source: ImageSource.camera, + options: argThat( + isInstanceOf() + .having((ImagePickerOptions options) => options.maxWidth, + 'maxWidth', equals(10.0)) + .having((ImagePickerOptions options) => options.maxHeight, + 'maxHeight', equals(20.0)) + .having( + (ImagePickerOptions options) => options.imageQuality, + 'imageQuality', + equals(70)), + named: 'options', + ), + ), ]); }); @@ -117,7 +216,16 @@ void main() { final ImagePicker picker = ImagePicker(); await picker.pickImage(source: ImageSource.camera); - verify(mockPlatform.getImage(source: ImageSource.camera)); + verify(mockPlatform.getImageFromSource( + source: ImageSource.camera, + options: argThat( + isInstanceOf().having( + (ImagePickerOptions options) => options.preferredCameraDevice, + 'preferredCameraDevice', + equals(CameraDevice.rear)), + named: 'options', + ), + )); }); test('camera position can set to front', () async { @@ -126,9 +234,51 @@ void main() { source: ImageSource.camera, preferredCameraDevice: CameraDevice.front); - verify(mockPlatform.getImage( - source: ImageSource.camera, - preferredCameraDevice: CameraDevice.front)); + verify(mockPlatform.getImageFromSource( + source: ImageSource.camera, + options: argThat( + isInstanceOf().having( + (ImagePickerOptions options) => options.preferredCameraDevice, + 'preferredCameraDevice', + equals(CameraDevice.front)), + named: 'options', + ), + )); + }); + + test('full metadata argument defaults to true', () async { + final ImagePicker picker = ImagePicker(); + await picker.pickImage(source: ImageSource.gallery); + + verify(mockPlatform.getImageFromSource( + source: ImageSource.gallery, + options: argThat( + isInstanceOf().having( + (ImagePickerOptions options) => options.requestFullMetadata, + 'requestFullMetadata', + isTrue), + named: 'options', + ), + )); + }); + + test('passes the full metadata argument correctly', () async { + final ImagePicker picker = ImagePicker(); + await picker.pickImage( + source: ImageSource.gallery, + requestFullMetadata: false, + ); + + verify(mockPlatform.getImageFromSource( + source: ImageSource.gallery, + options: argThat( + isInstanceOf().having( + (ImagePickerOptions options) => options.requestFullMetadata, + 'requestFullMetadata', + isFalse), + named: 'options', + ), + )); }); }); @@ -250,11 +400,11 @@ void main() { group('#Multi images', () { setUp(() { - when(mockPlatform.getMultiImage( - maxWidth: anyNamed('maxWidth'), - maxHeight: anyNamed('maxHeight'), - imageQuality: anyNamed('imageQuality'))) - .thenAnswer((Invocation _) async => null); + when( + mockPlatform.getMultiImageWithOptions( + options: anyNamed('options'), + ), + ).thenAnswer((Invocation _) async => []); }); group('#pickMultiImage', () { @@ -283,14 +433,101 @@ void main() { maxWidth: 10.0, maxHeight: 20.0, imageQuality: 70); verifyInOrder([ - mockPlatform.getMultiImage(), - mockPlatform.getMultiImage(maxWidth: 10.0), - mockPlatform.getMultiImage(maxHeight: 10.0), - mockPlatform.getMultiImage(maxWidth: 10.0, maxHeight: 20.0), - mockPlatform.getMultiImage(maxWidth: 10.0, imageQuality: 70), - mockPlatform.getMultiImage(maxHeight: 10.0, imageQuality: 70), - mockPlatform.getMultiImage( - maxWidth: 10.0, maxHeight: 20.0, imageQuality: 70), + mockPlatform.getMultiImageWithOptions( + options: argThat( + isInstanceOf(), + named: 'options', + ), + ), + mockPlatform.getMultiImageWithOptions( + options: argThat( + isInstanceOf().having( + (MultiImagePickerOptions options) => + options.imageOptions.maxWidth, + 'maxWidth', + equals(10.0)), + named: 'options', + ), + ), + mockPlatform.getMultiImageWithOptions( + options: argThat( + isInstanceOf().having( + (MultiImagePickerOptions options) => + options.imageOptions.maxHeight, + 'maxHeight', + equals(10.0)), + named: 'options', + ), + ), + mockPlatform.getMultiImageWithOptions( + options: argThat( + isInstanceOf() + .having( + (MultiImagePickerOptions options) => + options.imageOptions.maxWidth, + 'maxWidth', + equals(10.0)) + .having( + (MultiImagePickerOptions options) => + options.imageOptions.maxHeight, + 'maxHeight', + equals(20.0)), + named: 'options', + ), + ), + mockPlatform.getMultiImageWithOptions( + options: argThat( + isInstanceOf() + .having( + (MultiImagePickerOptions options) => + options.imageOptions.maxWidth, + 'maxWidth', + equals(10.0)) + .having( + (MultiImagePickerOptions options) => + options.imageOptions.imageQuality, + 'imageQuality', + equals(70)), + named: 'options', + ), + ), + mockPlatform.getMultiImageWithOptions( + options: argThat( + isInstanceOf() + .having( + (MultiImagePickerOptions options) => + options.imageOptions.maxHeight, + 'maxHeight', + equals(10.0)) + .having( + (MultiImagePickerOptions options) => + options.imageOptions.imageQuality, + 'imageQuality', + equals(70)), + named: 'options', + ), + ), + mockPlatform.getMultiImageWithOptions( + options: argThat( + isInstanceOf() + .having( + (MultiImagePickerOptions options) => + options.imageOptions.maxWidth, + 'maxWidth', + equals(10.0)) + .having( + (MultiImagePickerOptions options) => + options.imageOptions.maxWidth, + 'maxHeight', + equals(10.0)) + .having( + (MultiImagePickerOptions options) => + options.imageOptions.imageQuality, + 'imageQuality', + equals(70)), + named: 'options', + ), + ), ]); }); @@ -307,11 +544,45 @@ void main() { ); }); - test('handles a null image file response gracefully', () async { + test('handles an empty image file response gracefully', () async { + final ImagePicker picker = ImagePicker(); + + expect(await picker.pickMultiImage(), isEmpty); + expect(await picker.pickMultiImage(), isEmpty); + }); + + test('full metadata argument defaults to true', () async { final ImagePicker picker = ImagePicker(); + await picker.pickMultiImage(); + + verify(mockPlatform.getMultiImageWithOptions( + options: argThat( + isInstanceOf().having( + (MultiImagePickerOptions options) => + options.imageOptions.requestFullMetadata, + 'requestFullMetadata', + isTrue), + named: 'options', + ), + )); + }); + + test('passes the full metadata argument correctly', () async { + final ImagePicker picker = ImagePicker(); + await picker.pickMultiImage( + requestFullMetadata: false, + ); - expect(await picker.pickMultiImage(), isNull); - expect(await picker.pickMultiImage(), isNull); + verify(mockPlatform.getMultiImageWithOptions( + options: argThat( + isInstanceOf().having( + (MultiImagePickerOptions options) => + options.imageOptions.requestFullMetadata, + 'requestFullMetadata', + isFalse), + named: 'options', + ), + )); }); }); }); diff --git a/packages/image_picker/image_picker/test/image_picker_test.mocks.dart b/packages/image_picker/image_picker/test/image_picker_test.mocks.dart index a79d076dd42c..f749b538665b 100644 --- a/packages/image_picker/image_picker/test/image_picker_test.mocks.dart +++ b/packages/image_picker/image_picker/test/image_picker_test.mocks.dart @@ -51,6 +51,7 @@ class MockImagePickerPlatform extends _i1.Mock }), returnValue: Future<_i2.PickedFile?>.value()) as _i4.Future<_i2.PickedFile?>); + @override _i4.Future?> pickMultiImage( {double? maxWidth, double? maxHeight, int? imageQuality}) => @@ -62,6 +63,7 @@ class MockImagePickerPlatform extends _i1.Mock }), returnValue: Future?>.value()) as _i4.Future?>); + @override _i4.Future<_i2.PickedFile?> pickVideo( {_i2.ImageSource? source, @@ -75,11 +77,13 @@ class MockImagePickerPlatform extends _i1.Mock }), returnValue: Future<_i2.PickedFile?>.value()) as _i4.Future<_i2.PickedFile?>); + @override _i4.Future<_i2.LostData> retrieveLostData() => (super.noSuchMethod(Invocation.method(#retrieveLostData, []), returnValue: Future<_i2.LostData>.value(_FakeLostData_0())) as _i4.Future<_i2.LostData>); + @override _i4.Future<_i5.XFile?> getImage( {_i2.ImageSource? source, @@ -96,6 +100,7 @@ class MockImagePickerPlatform extends _i1.Mock #preferredCameraDevice: preferredCameraDevice }), returnValue: Future<_i5.XFile?>.value()) as _i4.Future<_i5.XFile?>); + @override _i4.Future?> getMultiImage( {double? maxWidth, double? maxHeight, int? imageQuality}) => @@ -107,6 +112,7 @@ class MockImagePickerPlatform extends _i1.Mock }), returnValue: Future?>.value()) as _i4.Future?>); + @override _i4.Future<_i5.XFile?> getVideo( {_i2.ImageSource? source, @@ -119,12 +125,14 @@ class MockImagePickerPlatform extends _i1.Mock #maxDuration: maxDuration }), returnValue: Future<_i5.XFile?>.value()) as _i4.Future<_i5.XFile?>); + @override _i4.Future<_i2.LostDataResponse> getLostData() => (super.noSuchMethod(Invocation.method(#getLostData, []), returnValue: Future<_i2.LostDataResponse>.value(_FakeLostDataResponse_1())) as _i4.Future<_i2.LostDataResponse>); + @override _i4.Future<_i5.XFile?> getImageFromSource( {_i2.ImageSource? source, @@ -133,4 +141,14 @@ class MockImagePickerPlatform extends _i1.Mock Invocation.method( #getImageFromSource, [], {#source: source, #options: options}), returnValue: Future<_i5.XFile?>.value()) as _i4.Future<_i5.XFile?>); + + @override + _i4.Future> getMultiImageWithOptions( + {_i2.MultiImagePickerOptions? options = + const _i2.MultiImagePickerOptions()}) => + (super.noSuchMethod( + Invocation.method(#getMultiImageWithOptions, [], {#options: options}), + returnValue: + Future>.value(<_i5.XFile>[])) as _i4 + .Future>); } From 45962d1a0b59e276724e27d360c06bc37898225f Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 5 Oct 2022 13:23:08 -0400 Subject: [PATCH 785/844] Roll Flutter from 55d67cc7d992 to d554fcb36a6e (23 revisions) (#6536) --- .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 aa54d97b2eaf..7dd277eedd71 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -55d67cc7d99226cd12d6c4a76de357fc2c92823e +d554fcb36a6e6a6a83d0c7759d2a4cd113701aef From 54e748c343ef4050246ed1a2b317028176f70e17 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 5 Oct 2022 13:26:39 -0400 Subject: [PATCH 786/844] Roll Flutter (stable) from 18a827f3933c to eb6d86ee27de (2 revisions) (#6537) --- .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 20445c3150b3..c26442d70e76 100644 --- a/.ci/flutter_stable.version +++ b/.ci/flutter_stable.version @@ -1 +1 @@ -18a827f3933c19f51862dde3fa472197683249d6 +eb6d86ee27deecba4a83536aa20f366a6044895c From 5f20bf1c8b6030218a7d6de438f3f750040a43c9 Mon Sep 17 00:00:00 2001 From: eugerossetto <101729072+eugerossetto@users.noreply.github.com> Date: Thu, 6 Oct 2022 13:46:21 -0300 Subject: [PATCH 787/844] [file_selector] Use const instead of final for XTypeGroup. (#6542) --- .../file_selector/file_selector/CHANGELOG.md | 3 +- .../file_selector/file_selector/README.md | 6 +- .../example/lib/open_image_page.dart | 5 +- .../lib/open_multiple_images_page.dart | 7 +-- .../example/lib/open_text_page.dart | 6 +- .../file_selector/file_selector/pubspec.yaml | 4 +- .../test/file_selector_test.dart | 9 +-- .../file_selector_ios/CHANGELOG.md | 3 +- .../example/lib/open_image_page.dart | 8 +-- .../lib/open_multiple_images_page.dart | 11 +--- .../example/lib/open_text_page.dart | 8 +-- .../file_selector_ios/example/pubspec.yaml | 2 +- .../file_selector_ios/pubspec.yaml | 4 +- .../test/file_selector_ios_test.dart | 35 +++-------- .../file_selector_linux/CHANGELOG.md | 3 +- .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 12 +--- .../example/lib/open_text_page.dart | 6 +- .../file_selector_linux/example/pubspec.yaml | 2 +- .../file_selector_linux/pubspec.yaml | 4 +- .../test/file_selector_linux_test.dart | 51 ++++----------- .../file_selector_macos/CHANGELOG.md | 4 ++ .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 12 +--- .../example/lib/open_text_page.dart | 6 +- .../file_selector_macos/example/pubspec.yaml | 2 +- .../file_selector_macos/pubspec.yaml | 4 +- .../test/file_selector_macos_test.dart | 63 +++++-------------- .../test/x_type_group_test.dart | 14 ++--- .../file_selector_web/CHANGELOG.md | 4 ++ .../file_selector_web_test.dart | 11 +--- .../file_selector_web/example/pubspec.yaml | 2 +- .../file_selector_web/pubspec.yaml | 4 +- .../file_selector_web/test/utils_test.dart | 37 ++--------- .../file_selector_windows/CHANGELOG.md | 4 ++ .../example/lib/open_image_page.dart | 6 +- .../lib/open_multiple_images_page.dart | 12 +--- .../example/lib/open_text_page.dart | 6 +- .../example/pubspec.yaml | 2 +- .../file_selector_windows/pubspec.yaml | 4 +- .../test/file_selector_windows_test.dart | 51 ++++----------- 41 files changed, 121 insertions(+), 328 deletions(-) diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 1cecd5605a7e..06d01090edc0 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.9.2+1 +* Changes XTypeGroup initialization from final to const. * Updates minimum Flutter version to 2.10. ## 0.9.2 diff --git a/packages/file_selector/file_selector/README.md b/packages/file_selector/file_selector/README.md index d5fb2d36263e..97433584b87a 100644 --- a/packages/file_selector/file_selector/README.md +++ b/packages/file_selector/file_selector/README.md @@ -34,7 +34,7 @@ Please also take a look at our [example][example] app. #### Open a single file ``` dart -final XTypeGroup typeGroup = XTypeGroup( +const XTypeGroup typeGroup = XTypeGroup( label: 'images', extensions: ['jpg', 'png'], ); @@ -45,11 +45,11 @@ final XFile? file = #### Open multiple files at once ``` dart -final XTypeGroup jpgsTypeGroup = XTypeGroup( +const XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', extensions: ['jpg', 'jpeg'], ); -final XTypeGroup pngTypeGroup = XTypeGroup( +const XTypeGroup pngTypeGroup = XTypeGroup( label: 'PNGs', extensions: ['png'], ); diff --git a/packages/file_selector/file_selector/example/lib/open_image_page.dart b/packages/file_selector/file_selector/example/lib/open_image_page.dart index b72d4e5f562f..ba18e6e78594 100644 --- a/packages/file_selector/file_selector/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_image_page.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 -// ignore_for_file: prefer_const_constructors, prefer_const_literals_to_create_immutables - import 'dart:io'; import 'package:file_selector/file_selector.dart'; @@ -18,7 +15,7 @@ class OpenImagePage extends StatelessWidget { Future _openImageFile(BuildContext context) async { // #docregion SingleOpen - final XTypeGroup typeGroup = XTypeGroup( + const XTypeGroup typeGroup = XTypeGroup( label: 'images', extensions: ['jpg', 'png'], ); diff --git a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart index 87549e046549..8ae83c2a85dc 100644 --- a/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_multiple_images_page.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 -// ignore_for_file: prefer_const_constructors, prefer_const_literals_to_create_immutables - import 'dart:io'; import 'package:file_selector/file_selector.dart'; @@ -18,11 +15,11 @@ class OpenMultipleImagesPage extends StatelessWidget { Future _openImageFile(BuildContext context) async { // #docregion MultiOpen - final XTypeGroup jpgsTypeGroup = XTypeGroup( + const XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', extensions: ['jpg', 'jpeg'], ); - final XTypeGroup pngTypeGroup = XTypeGroup( + const XTypeGroup pngTypeGroup = XTypeGroup( label: 'PNGs', extensions: ['png'], ); diff --git a/packages/file_selector/file_selector/example/lib/open_text_page.dart b/packages/file_selector/file_selector/example/lib/open_text_page.dart index 04d48afc21e4..f052db1eefc1 100644 --- a/packages/file_selector/file_selector/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector/example/lib/open_text_page.dart @@ -12,12 +12,8 @@ class OpenTextPage extends StatelessWidget { const OpenTextPage({Key? key}) : super(key: key); Future _openTextFile(BuildContext context) async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup typeGroup = XTypeGroup( + const XTypeGroup typeGroup = XTypeGroup( label: 'text', - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_literals_to_create_immutables extensions: ['txt', 'json'], ); // This demonstrates using an initial directory for the prompt, which should diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index 556ddf23b3be..cae08e47eeb1 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.9.2 +version: 0.9.2+1 environment: sdk: ">=2.12.0 <3.0.0" @@ -27,7 +27,7 @@ dependencies: file_selector_ios: ^0.5.0 file_selector_linux: ^0.9.0 file_selector_macos: ^0.9.0 - file_selector_platform_interface: ^2.0.0 + file_selector_platform_interface: ^2.2.0 file_selector_web: ^0.9.0 file_selector_windows: ^0.9.0 flutter: diff --git a/packages/file_selector/file_selector/test/file_selector_test.dart b/packages/file_selector/file_selector/test/file_selector_test.dart index 0bef946142ee..13c986b09922 100644 --- a/packages/file_selector/file_selector/test/file_selector_test.dart +++ b/packages/file_selector/file_selector/test/file_selector_test.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 -// ignore_for_file: prefer_const_literals_to_create_immutables - import 'package:file_selector/file_selector.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -15,15 +12,11 @@ void main() { const String initialDirectory = '/home/flutteruser'; const String confirmButtonText = 'Use this profile picture'; const String suggestedName = 'suggested_name'; - final List acceptedTypeGroups = [ - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors + const List acceptedTypeGroups = [ XTypeGroup(label: 'documents', mimeTypes: [ 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessing', ]), - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors XTypeGroup(label: 'images', extensions: [ 'jpg', 'png', diff --git a/packages/file_selector/file_selector_ios/CHANGELOG.md b/packages/file_selector/file_selector_ios/CHANGELOG.md index 2b2d2f8c2d58..439e1d4fd4c1 100644 --- a/packages/file_selector/file_selector_ios/CHANGELOG.md +++ b/packages/file_selector/file_selector_ios/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.5.0+2 +* Changes XTypeGroup initialization from final to const. * Updates minimum Flutter version to 2.10. ## 0.5.0+1 diff --git a/packages/file_selector/file_selector_ios/example/lib/open_image_page.dart b/packages/file_selector/file_selector_ios/example/lib/open_image_page.dart index eff6c4f7c028..606a64870566 100644 --- a/packages/file_selector/file_selector_ios/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_ios/example/lib/open_image_page.dart @@ -15,15 +15,9 @@ class OpenImagePage extends StatelessWidget { const OpenImagePage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup typeGroup = XTypeGroup( + const XTypeGroup typeGroup = XTypeGroup( label: 'images', - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_literals_to_create_immutables extensions: ['jpg', 'png'], - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_literals_to_create_immutables macUTIs: ['public.image'], ); final XFile? file = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_ios/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_ios/example/lib/open_multiple_images_page.dart index 0035ebcba5eb..adc4a65f12b5 100644 --- a/packages/file_selector/file_selector_ios/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_ios/example/lib/open_multiple_images_page.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 -// ignore_for_file: prefer_const_literals_to_create_immutables - import 'dart:io'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; @@ -18,16 +15,12 @@ class OpenMultipleImagesPage extends StatelessWidget { const OpenMultipleImagesPage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup jpgsTypeGroup = XTypeGroup( + const XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', extensions: ['jpg', 'jpeg'], macUTIs: ['public.jpeg'], ); - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup pngTypeGroup = XTypeGroup( + const XTypeGroup pngTypeGroup = XTypeGroup( label: 'PNGs', extensions: ['png'], macUTIs: ['public.png'], diff --git a/packages/file_selector/file_selector_ios/example/lib/open_text_page.dart b/packages/file_selector/file_selector_ios/example/lib/open_text_page.dart index b91195ef4a3d..e7bbf8bc937f 100644 --- a/packages/file_selector/file_selector_ios/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_ios/example/lib/open_text_page.dart @@ -12,15 +12,9 @@ class OpenTextPage extends StatelessWidget { const OpenTextPage({Key? key}) : super(key: key); Future _openTextFile(BuildContext context) async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup typeGroup = XTypeGroup( + const XTypeGroup typeGroup = XTypeGroup( label: 'text', - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_literals_to_create_immutables extensions: ['txt', 'json'], - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_literals_to_create_immutables macUTIs: ['public.text'], ); final XFile? file = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_ios/example/pubspec.yaml b/packages/file_selector/file_selector_ios/example/pubspec.yaml index eae4a2b45492..5a2eaa6f7dcd 100644 --- a/packages/file_selector/file_selector_ios/example/pubspec.yaml +++ b/packages/file_selector/file_selector_ios/example/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: .. - file_selector_platform_interface: ^2.0.0 + file_selector_platform_interface: ^2.2.0 flutter: sdk: flutter diff --git a/packages/file_selector/file_selector_ios/pubspec.yaml b/packages/file_selector/file_selector_ios/pubspec.yaml index b69db86b2950..3f8ecfac04ce 100644 --- a/packages/file_selector/file_selector_ios/pubspec.yaml +++ b/packages/file_selector/file_selector_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_ios description: iOS implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.5.0+1 +version: 0.5.0+2 environment: sdk: ">=2.14.4 <3.0.0" @@ -17,7 +17,7 @@ flutter: pluginClass: FFSFileSelectorPlugin dependencies: - file_selector_platform_interface: ^2.1.0 + file_selector_platform_interface: ^2.2.0 flutter: sdk: flutter diff --git a/packages/file_selector/file_selector_ios/test/file_selector_ios_test.dart b/packages/file_selector/file_selector_ios/test/file_selector_ios_test.dart index a388fe951a4c..f66bd7dc7ced 100644 --- a/packages/file_selector/file_selector_ios/test/file_selector_ios_test.dart +++ b/packages/file_selector/file_selector_ios/test/file_selector_ios_test.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 -// ignore_for_file: prefer_const_literals_to_create_immutables - import 'package:file_selector_ios/file_selector_ios.dart'; import 'package:file_selector_ios/src/messages.g.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; @@ -39,18 +36,14 @@ void main() { }); test('passes the accepted type groups correctly', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], mimeTypes: ['text/plain'], macUTIs: ['public.text'], ); - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup groupTwo = XTypeGroup( + const XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], mimeTypes: ['image/jpg'], @@ -69,9 +62,7 @@ void main() { expect(config.allowMultiSelection, isFalse); }); test('throws for a type group that does not support iOS', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'images', webWildCards: ['images/*'], ); @@ -82,9 +73,7 @@ void main() { }); test('allows a wildcard group', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', ); @@ -99,18 +88,14 @@ void main() { }); test('passes the accepted type groups correctly', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], mimeTypes: ['text/plain'], macUTIs: ['public.text'], ); - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup groupTwo = XTypeGroup( + const XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], mimeTypes: ['image/jpg'], @@ -129,9 +114,7 @@ void main() { expect(config.allowMultiSelection, isTrue); }); test('throws for a type group that does not support iOS', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'images', webWildCards: ['images/*'], ); @@ -142,9 +125,7 @@ void main() { }); test('allows a wildcard group', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', ); diff --git a/packages/file_selector/file_selector_linux/CHANGELOG.md b/packages/file_selector/file_selector_linux/CHANGELOG.md index d76c635cde8e..a1f57b5cc857 100644 --- a/packages/file_selector/file_selector_linux/CHANGELOG.md +++ b/packages/file_selector/file_selector_linux/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.9.0+1 +* Changes XTypeGroup initialization from final to const. * Updates minimum Flutter version to 2.10. ## 0.9.0 diff --git a/packages/file_selector/file_selector_linux/example/lib/open_image_page.dart b/packages/file_selector/file_selector_linux/example/lib/open_image_page.dart index 1925b27002df..b6ada56ebb2b 100644 --- a/packages/file_selector/file_selector_linux/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_linux/example/lib/open_image_page.dart @@ -15,12 +15,8 @@ class OpenImagePage extends StatelessWidget { const OpenImagePage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup typeGroup = XTypeGroup( + const XTypeGroup typeGroup = XTypeGroup( label: 'images', - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_literals_to_create_immutables extensions: ['jpg', 'png'], ); final XFile? file = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_linux/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_linux/example/lib/open_multiple_images_page.dart index ee7e7d1a967d..c8e352a5b8bd 100644 --- a/packages/file_selector/file_selector_linux/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_linux/example/lib/open_multiple_images_page.dart @@ -15,20 +15,12 @@ class OpenMultipleImagesPage extends StatelessWidget { const OpenMultipleImagesPage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup jpgsTypeGroup = XTypeGroup( + const XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_literals_to_create_immutables extensions: ['jpg', 'jpeg'], ); - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup pngTypeGroup = XTypeGroup( + const XTypeGroup pngTypeGroup = XTypeGroup( label: 'PNGs', - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_literals_to_create_immutables extensions: ['png'], ); final List files = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_linux/example/lib/open_text_page.dart b/packages/file_selector/file_selector_linux/example/lib/open_text_page.dart index d5e8d462de1d..4c88d7475049 100644 --- a/packages/file_selector/file_selector_linux/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_linux/example/lib/open_text_page.dart @@ -12,12 +12,8 @@ class OpenTextPage extends StatelessWidget { const OpenTextPage({Key? key}) : super(key: key); Future _openTextFile(BuildContext context) async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup typeGroup = XTypeGroup( + const XTypeGroup typeGroup = XTypeGroup( label: 'text', - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_literals_to_create_immutables extensions: ['txt', 'json'], ); final XFile? file = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_linux/example/pubspec.yaml b/packages/file_selector/file_selector_linux/example/pubspec.yaml index 857c950ecc95..51bdb28717aa 100644 --- a/packages/file_selector/file_selector_linux/example/pubspec.yaml +++ b/packages/file_selector/file_selector_linux/example/pubspec.yaml @@ -9,7 +9,7 @@ environment: dependencies: file_selector_linux: path: ../ - file_selector_platform_interface: ^2.0.0 + file_selector_platform_interface: ^2.2.0 flutter: sdk: flutter diff --git a/packages/file_selector/file_selector_linux/pubspec.yaml b/packages/file_selector/file_selector_linux/pubspec.yaml index e0b26d18e719..a8aea37d72e2 100644 --- a/packages/file_selector/file_selector_linux/pubspec.yaml +++ b/packages/file_selector/file_selector_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_linux description: Liunx implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.9.0 +version: 0.9.0+1 environment: sdk: ">=2.12.0 <3.0.0" @@ -18,7 +18,7 @@ flutter: dependencies: cross_file: ^0.3.1 - file_selector_platform_interface: ^2.1.0 + file_selector_platform_interface: ^2.2.0 flutter: sdk: flutter diff --git a/packages/file_selector/file_selector_linux/test/file_selector_linux_test.dart b/packages/file_selector/file_selector_linux/test/file_selector_linux_test.dart index 54111c27140e..748f922ae6ef 100644 --- a/packages/file_selector/file_selector_linux/test/file_selector_linux_test.dart +++ b/packages/file_selector/file_selector_linux/test/file_selector_linux_test.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 -// ignore_for_file: prefer_const_literals_to_create_immutables - import 'package:file_selector_linux/file_selector_linux.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter/services.dart'; @@ -32,18 +29,14 @@ void main() { group('#openFile', () { test('passes the accepted type groups correctly', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], mimeTypes: ['text/plain'], macUTIs: ['public.text'], ); - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup groupTwo = XTypeGroup( + const XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], mimeTypes: ['image/jpg'], @@ -108,9 +101,7 @@ void main() { }); test('throws for a type group that does not support Linux', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'images', webWildCards: ['images/*'], ); @@ -121,9 +112,7 @@ void main() { }); test('passes a wildcard group correctly', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'any', ); @@ -150,18 +139,14 @@ void main() { group('#openFiles', () { test('passes the accepted type groups correctly', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], mimeTypes: ['text/plain'], macUTIs: ['public.text'], ); - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup groupTwo = XTypeGroup( + const XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], mimeTypes: ['image/jpg'], @@ -226,9 +211,7 @@ void main() { }); test('throws for a type group that does not support Linux', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'images', webWildCards: ['images/*'], ); @@ -239,9 +222,7 @@ void main() { }); test('passes a wildcard group correctly', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'any', ); @@ -268,18 +249,14 @@ void main() { group('#getSavePath', () { test('passes the accepted type groups correctly', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], mimeTypes: ['text/plain'], macUTIs: ['public.text'], ); - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup groupTwo = XTypeGroup( + const XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], mimeTypes: ['image/jpg'], @@ -345,9 +322,7 @@ void main() { }); test('throws for a type group that does not support Linux', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'images', webWildCards: ['images/*'], ); @@ -358,9 +333,7 @@ void main() { }); test('passes a wildcard group correctly', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'any', ); diff --git a/packages/file_selector/file_selector_macos/CHANGELOG.md b/packages/file_selector/file_selector_macos/CHANGELOG.md index f9241da91476..af17db8ae3ef 100644 --- a/packages/file_selector/file_selector_macos/CHANGELOG.md +++ b/packages/file_selector/file_selector_macos/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.0+3 + +* Changes XTypeGroup initialization from final to const. + ## 0.9.0+2 * Updates imports for `prefer_relative_imports`. diff --git a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart index 1925b27002df..b6ada56ebb2b 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_image_page.dart @@ -15,12 +15,8 @@ class OpenImagePage extends StatelessWidget { const OpenImagePage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup typeGroup = XTypeGroup( + const XTypeGroup typeGroup = XTypeGroup( label: 'images', - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_literals_to_create_immutables extensions: ['jpg', 'png'], ); final XFile? file = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart index ee7e7d1a967d..c8e352a5b8bd 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_multiple_images_page.dart @@ -15,20 +15,12 @@ class OpenMultipleImagesPage extends StatelessWidget { const OpenMultipleImagesPage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup jpgsTypeGroup = XTypeGroup( + const XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_literals_to_create_immutables extensions: ['jpg', 'jpeg'], ); - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup pngTypeGroup = XTypeGroup( + const XTypeGroup pngTypeGroup = XTypeGroup( label: 'PNGs', - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_literals_to_create_immutables extensions: ['png'], ); final List files = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart index d5e8d462de1d..4c88d7475049 100644 --- a/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_macos/example/lib/open_text_page.dart @@ -12,12 +12,8 @@ class OpenTextPage extends StatelessWidget { const OpenTextPage({Key? key}) : super(key: key); Future _openTextFile(BuildContext context) async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup typeGroup = XTypeGroup( + const XTypeGroup typeGroup = XTypeGroup( label: 'text', - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_literals_to_create_immutables extensions: ['txt', 'json'], ); final XFile? file = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_macos/example/pubspec.yaml b/packages/file_selector/file_selector_macos/example/pubspec.yaml index 58c5d6e22b49..d3f3114bb481 100644 --- a/packages/file_selector/file_selector_macos/example/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/example/pubspec.yaml @@ -15,7 +15,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: .. - file_selector_platform_interface: ^2.0.0 + file_selector_platform_interface: ^2.2.0 flutter: sdk: flutter diff --git a/packages/file_selector/file_selector_macos/pubspec.yaml b/packages/file_selector/file_selector_macos/pubspec.yaml index b921cc8e8070..3fc3832d7280 100644 --- a/packages/file_selector/file_selector_macos/pubspec.yaml +++ b/packages/file_selector/file_selector_macos/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_macos description: macOS implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_macos issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.9.0+2 +version: 0.9.0+3 environment: sdk: ">=2.12.0 <3.0.0" @@ -18,7 +18,7 @@ flutter: dependencies: cross_file: ^0.3.1 - file_selector_platform_interface: ^2.1.0 + file_selector_platform_interface: ^2.2.0 flutter: sdk: flutter diff --git a/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart b/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart index 29702a5421b1..789d70a51777 100644 --- a/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart +++ b/packages/file_selector/file_selector_macos/test/file_selector_macos_test.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 -// ignore_for_file: prefer_const_literals_to_create_immutables - import 'package:file_selector_macos/file_selector_macos.dart'; import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:flutter/services.dart'; @@ -33,18 +30,14 @@ void main() { group('openFile', () { test('passes the accepted type groups correctly', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], mimeTypes: ['text/plain'], macUTIs: ['public.text'], ); - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup groupTwo = XTypeGroup( + const XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], mimeTypes: ['image/jpg'], @@ -103,9 +96,7 @@ void main() { }); test('throws for a type group that does not support macOS', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'images', webWildCards: ['images/*'], ); @@ -116,9 +107,7 @@ void main() { }); test('allows a wildcard group', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', ); @@ -129,18 +118,14 @@ void main() { group('openFiles', () { test('passes the accepted type groups correctly', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], mimeTypes: ['text/plain'], macUTIs: ['public.text'], ); - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup groupTwo = XTypeGroup( + const XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], mimeTypes: ['image/jpg'], @@ -199,9 +184,7 @@ void main() { }); test('throws for a type group that does not support macOS', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'images', webWildCards: ['images/*'], ); @@ -212,9 +195,7 @@ void main() { }); test('allows a wildcard group', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', ); @@ -225,18 +206,14 @@ void main() { group('getSavePath', () { test('passes the accepted type groups correctly', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], mimeTypes: ['text/plain'], macUTIs: ['public.text'], ); - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup groupTwo = XTypeGroup( + const XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], mimeTypes: ['image/jpg'], @@ -296,9 +273,7 @@ void main() { }); test('throws for a type group that does not support macOS', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'images', webWildCards: ['images/*'], ); @@ -309,9 +284,7 @@ void main() { }); test('allows a wildcard group', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', ); @@ -353,25 +326,19 @@ void main() { test('ignores all type groups if any of them is a wildcard', () async { await plugin.getSavePath(acceptedTypeGroups: [ - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - XTypeGroup( + const XTypeGroup( label: 'text', extensions: ['txt'], mimeTypes: ['text/plain'], macUTIs: ['public.text'], ), - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - XTypeGroup( + const XTypeGroup( label: 'image', extensions: ['jpg'], mimeTypes: ['image/jpg'], macUTIs: ['public.image'], ), - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - XTypeGroup( + const XTypeGroup( label: 'any', ), ]); diff --git a/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart b/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart index 107cdc3a2538..c5e65d0264f5 100644 --- a/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart @@ -9,12 +9,12 @@ void main() { group('XTypeGroup', () { test('toJSON() creates correct map', () { const String label = 'test group'; - final List extensions = ['txt', 'jpg']; - final List mimeTypes = ['text/plain']; - final List macUTIs = ['public.plain-text']; - final List webWildCards = ['image/*']; + const List extensions = ['txt', 'jpg']; + const List mimeTypes = ['text/plain']; + const List macUTIs = ['public.plain-text']; + const List webWildCards = ['image/*']; - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: label, extensions: extensions, mimeTypes: mimeTypes, @@ -72,8 +72,8 @@ void main() { }); test('Leading dots are removed from extensions', () { - final List extensions = ['.txt', '.jpg']; - final XTypeGroup group = XTypeGroup(extensions: extensions); + const List extensions = ['.txt', '.jpg']; + const XTypeGroup group = XTypeGroup(extensions: extensions); expect(group.extensions, ['txt', 'jpg']); }); diff --git a/packages/file_selector/file_selector_web/CHANGELOG.md b/packages/file_selector/file_selector_web/CHANGELOG.md index 69ca20363866..5e531bb633d2 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.0+2 + +* Changes XTypeGroup initialization from final to const. + ## 0.9.0+1 * Updates imports for `prefer_relative_imports`. 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 5f476d937977..664c40871f49 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 @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 -// ignore_for_file: prefer_const_literals_to_create_immutables - import 'dart:html'; import 'dart:typed_data'; @@ -29,9 +26,7 @@ void main() { final FileSelectorWeb plugin = FileSelectorWeb(domHelper: mockDomHelper); - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup typeGroup = XTypeGroup( + const XTypeGroup typeGroup = XTypeGroup( label: 'images', extensions: ['jpg', 'jpeg'], mimeTypes: ['image/png'], @@ -61,9 +56,7 @@ void main() { final FileSelectorWeb plugin = FileSelectorWeb(domHelper: mockDomHelper); - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup typeGroup = XTypeGroup( + const XTypeGroup typeGroup = XTypeGroup( label: 'files', extensions: ['.txt'], ); diff --git a/packages/file_selector/file_selector_web/example/pubspec.yaml b/packages/file_selector/file_selector_web/example/pubspec.yaml index 3a36ce5c74f3..e14f5c2eedea 100644 --- a/packages/file_selector/file_selector_web/example/pubspec.yaml +++ b/packages/file_selector/file_selector_web/example/pubspec.yaml @@ -6,7 +6,7 @@ environment: flutter: ">=2.10.0" dependencies: - file_selector_platform_interface: ^2.1.0 + file_selector_platform_interface: ^2.2.0 file_selector_web: path: ../ flutter: diff --git a/packages/file_selector/file_selector_web/pubspec.yaml b/packages/file_selector/file_selector_web/pubspec.yaml index 38229588fd27..848a41b754af 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/plugins/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.0+1 +version: 0.9.0+2 environment: sdk: ">=2.12.0 <3.0.0" @@ -17,7 +17,7 @@ flutter: fileName: file_selector_web.dart dependencies: - file_selector_platform_interface: ^2.1.0 + file_selector_platform_interface: ^2.2.0 flutter: sdk: flutter flutter_web_plugins: diff --git a/packages/file_selector/file_selector_web/test/utils_test.dart b/packages/file_selector/file_selector_web/test/utils_test.dart index dcb7c3d8c627..f9f3a41295f0 100644 --- a/packages/file_selector/file_selector_web/test/utils_test.dart +++ b/packages/file_selector/file_selector_web/test/utils_test.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 -// ignore_for_file: prefer_const_literals_to_create_immutables - import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:file_selector_web/src/utils.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -13,15 +10,9 @@ void main() { group('FileSelectorWeb utils', () { group('acceptedTypesToString', () { test('works', () { - final List acceptedTypes = [ - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors + const List acceptedTypes = [ XTypeGroup(label: 'images', webWildCards: ['images/*']), - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors XTypeGroup(label: 'jpgs', extensions: ['jpg', 'jpeg']), - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors XTypeGroup(label: 'pngs', mimeTypes: ['image/png']), ]; final String accepts = acceptedTypesToString(acceptedTypes); @@ -29,18 +20,14 @@ void main() { }); test('works with an empty list', () { - final List acceptedTypes = []; + const List acceptedTypes = []; final String accepts = acceptedTypesToString(acceptedTypes); expect(accepts, ''); }); test('works with extensions', () { - final List acceptedTypes = [ - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors + const List acceptedTypes = [ XTypeGroup(label: 'jpgs', extensions: ['jpeg', 'jpg']), - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors XTypeGroup(label: 'pngs', extensions: ['png']), ]; final String accepts = acceptedTypesToString(acceptedTypes); @@ -48,13 +35,9 @@ void main() { }); test('works with mime types', () { - final List acceptedTypes = [ - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors + const List acceptedTypes = [ XTypeGroup( label: 'jpgs', mimeTypes: ['image/jpeg', 'image/jpg']), - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors XTypeGroup(label: 'pngs', mimeTypes: ['image/png']), ]; final String accepts = acceptedTypesToString(acceptedTypes); @@ -62,15 +45,9 @@ void main() { }); test('works with web wild cards', () { - final List acceptedTypes = [ - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors + const List acceptedTypes = [ XTypeGroup(label: 'images', webWildCards: ['image/*']), - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors XTypeGroup(label: 'audios', webWildCards: ['audio/*']), - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors XTypeGroup(label: 'videos', webWildCards: ['video/*']), ]; final String accepts = acceptedTypesToString(acceptedTypes); @@ -78,9 +55,7 @@ void main() { }); test('throws for a type group that does not support web', () { - final List acceptedTypes = [ - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors + const List acceptedTypes = [ XTypeGroup(label: 'text', macUTIs: ['public.text']), ]; expect(() => acceptedTypesToString(acceptedTypes), throwsArgumentError); diff --git a/packages/file_selector/file_selector_windows/CHANGELOG.md b/packages/file_selector/file_selector_windows/CHANGELOG.md index 1221bbd4d508..13e895ca46f1 100644 --- a/packages/file_selector/file_selector_windows/CHANGELOG.md +++ b/packages/file_selector/file_selector_windows/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.1+4 + +* Changes XTypeGroup initialization from final to const. + ## 0.9.1+3 * Updates imports for `prefer_relative_imports`. diff --git a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart index 1925b27002df..b6ada56ebb2b 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_image_page.dart @@ -15,12 +15,8 @@ class OpenImagePage extends StatelessWidget { const OpenImagePage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup typeGroup = XTypeGroup( + const XTypeGroup typeGroup = XTypeGroup( label: 'images', - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_literals_to_create_immutables extensions: ['jpg', 'png'], ); final XFile? file = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart index ee7e7d1a967d..c8e352a5b8bd 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart @@ -15,20 +15,12 @@ class OpenMultipleImagesPage extends StatelessWidget { const OpenMultipleImagesPage({Key? key}) : super(key: key); Future _openImageFile(BuildContext context) async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup jpgsTypeGroup = XTypeGroup( + const XTypeGroup jpgsTypeGroup = XTypeGroup( label: 'JPEGs', - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_literals_to_create_immutables extensions: ['jpg', 'jpeg'], ); - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup pngTypeGroup = XTypeGroup( + const XTypeGroup pngTypeGroup = XTypeGroup( label: 'PNGs', - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_literals_to_create_immutables extensions: ['png'], ); final List files = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart index d5e8d462de1d..4c88d7475049 100644 --- a/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart +++ b/packages/file_selector/file_selector_windows/example/lib/open_text_page.dart @@ -12,12 +12,8 @@ class OpenTextPage extends StatelessWidget { const OpenTextPage({Key? key}) : super(key: key); Future _openTextFile(BuildContext context) async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup typeGroup = XTypeGroup( + const XTypeGroup typeGroup = XTypeGroup( label: 'text', - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_literals_to_create_immutables extensions: ['txt', 'json'], ); final XFile? file = await FileSelectorPlatform.instance diff --git a/packages/file_selector/file_selector_windows/example/pubspec.yaml b/packages/file_selector/file_selector_windows/example/pubspec.yaml index 99c251d0867f..bc886d32c896 100644 --- a/packages/file_selector/file_selector_windows/example/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/example/pubspec.yaml @@ -8,7 +8,7 @@ environment: flutter: ">=2.10.0" dependencies: - file_selector_platform_interface: ^2.0.0 + file_selector_platform_interface: ^2.2.0 file_selector_windows: # When depending on this package from a real application you should use: # file_selector_windows: ^x.y.z diff --git a/packages/file_selector/file_selector_windows/pubspec.yaml b/packages/file_selector/file_selector_windows/pubspec.yaml index 7859822701e1..ee0701b3fd30 100644 --- a/packages/file_selector/file_selector_windows/pubspec.yaml +++ b/packages/file_selector/file_selector_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: file_selector_windows description: Windows implementation of the file_selector plugin. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.9.1+3 +version: 0.9.1+4 environment: sdk: ">=2.12.0 <3.0.0" @@ -18,7 +18,7 @@ flutter: dependencies: cross_file: ^0.3.1 - file_selector_platform_interface: ^2.1.0 + file_selector_platform_interface: ^2.2.0 flutter: sdk: flutter diff --git a/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart b/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart index 2d8dc4b78688..f07c9b67618d 100644 --- a/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart +++ b/packages/file_selector/file_selector_windows/test/file_selector_windows_test.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 -// ignore_for_file: prefer_const_literals_to_create_immutables - import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; import 'package:file_selector_windows/file_selector_windows.dart'; import 'package:file_selector_windows/src/messages.g.dart'; @@ -50,18 +47,14 @@ void main() { }); test('passes the accepted type groups correctly', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], mimeTypes: ['text/plain'], macUTIs: ['public.text'], ); - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup groupTwo = XTypeGroup( + const XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], mimeTypes: ['image/jpg'], @@ -93,9 +86,7 @@ void main() { }); test('throws for a type group that does not support Windows', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', mimeTypes: ['text/plain'], ); @@ -106,9 +97,7 @@ void main() { }); test('allows a wildcard group', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', ); @@ -136,18 +125,14 @@ void main() { }); test('passes the accepted type groups correctly', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], mimeTypes: ['text/plain'], macUTIs: ['public.text'], ); - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup groupTwo = XTypeGroup( + const XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], mimeTypes: ['image/jpg'], @@ -179,9 +164,7 @@ void main() { }); test('throws for a type group that does not support Windows', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', mimeTypes: ['text/plain'], ); @@ -192,9 +175,7 @@ void main() { }); test('allows a wildcard group', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', ); @@ -250,18 +231,14 @@ void main() { }); test('passes the accepted type groups correctly', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', extensions: ['txt'], mimeTypes: ['text/plain'], macUTIs: ['public.text'], ); - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup groupTwo = XTypeGroup( + const XTypeGroup groupTwo = XTypeGroup( label: 'image', extensions: ['jpg'], mimeTypes: ['image/jpg'], @@ -300,9 +277,7 @@ void main() { }); test('throws for a type group that does not support Windows', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', mimeTypes: ['text/plain'], ); @@ -313,9 +288,7 @@ void main() { }); test('allows a wildcard group', () async { - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors - final XTypeGroup group = XTypeGroup( + const XTypeGroup group = XTypeGroup( label: 'text', ); From 3cd4360b674d3530840c2506b00793de585d73b6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 6 Oct 2022 13:35:55 -0400 Subject: [PATCH 788/844] Roll Flutter from d554fcb36a6e to be86a916548a (22 revisions) (#6548) --- .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 7dd277eedd71..f7578dc7ad5d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -d554fcb36a6e6a6a83d0c7759d2a4cd113701aef +be86a916548a66c93e4f2f919b5ca0ccd2cdae0c From d4db207f557b6cc4ed24d2aeef8fc5ec044f76b6 Mon Sep 17 00:00:00 2001 From: eugerossetto <101729072+eugerossetto@users.noreply.github.com> Date: Thu, 6 Oct 2022 15:39:24 -0300 Subject: [PATCH 789/844] [image_picker_windows] Use const instead of final for XTypeGroup. (#6544) --- .../image_picker/image_picker_windows/CHANGELOG.md | 3 ++- .../lib/image_picker_windows.dart | 12 +++--------- .../image_picker/image_picker_windows/pubspec.yaml | 4 ++-- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/packages/image_picker/image_picker_windows/CHANGELOG.md b/packages/image_picker/image_picker_windows/CHANGELOG.md index 2480d6da9ea4..427598760a4b 100644 --- a/packages/image_picker/image_picker_windows/CHANGELOG.md +++ b/packages/image_picker/image_picker_windows/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 0.1.0+3 +* Changes XTypeGroup initialization from final to const. * Updates minimum Flutter version to 2.10. ## 0.1.0+2 diff --git a/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart b/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart index 10880d12ae6b..90e86bf486b4 100644 --- a/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart +++ b/packages/image_picker/image_picker_windows/lib/image_picker_windows.dart @@ -118,9 +118,7 @@ class ImagePickerWindows extends ImagePickerPlatform { throw UnimplementedError( 'ImageSource.gallery is currently the only supported source on Windows'); } - final XTypeGroup typeGroup = - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors + const XTypeGroup typeGroup = XTypeGroup(label: 'images', extensions: imageFormats); final XFile? file = await fileSelector .openFile(acceptedTypeGroups: [typeGroup]); @@ -144,9 +142,7 @@ class ImagePickerWindows extends ImagePickerPlatform { throw UnimplementedError( 'ImageSource.gallery is currently the only supported source on Windows'); } - final XTypeGroup typeGroup = - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors + const XTypeGroup typeGroup = XTypeGroup(label: 'videos', extensions: videoFormats); final XFile? file = await fileSelector .openFile(acceptedTypeGroups: [typeGroup]); @@ -162,9 +158,7 @@ class ImagePickerWindows extends ImagePickerPlatform { double? maxHeight, int? imageQuality, }) async { - final XTypeGroup typeGroup = - // TODO(stuartmorgan): https://github.com/flutter/flutter/issues/111906 - // ignore: prefer_const_constructors + const XTypeGroup typeGroup = XTypeGroup(label: 'images', extensions: imageFormats); final List files = await fileSelector .openFiles(acceptedTypeGroups: [typeGroup]); diff --git a/packages/image_picker/image_picker_windows/pubspec.yaml b/packages/image_picker/image_picker_windows/pubspec.yaml index 2887b080cef3..5d6988cc2931 100644 --- a/packages/image_picker/image_picker_windows/pubspec.yaml +++ b/packages/image_picker/image_picker_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_windows description: Windows platform implementation of image_picker repository: https://github.com/flutter/plugins/tree/main/packages/image_picker/image_picker_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.1.0+2 +version: 0.1.0+3 environment: sdk: ">=2.12.0 <3.0.0" @@ -16,7 +16,7 @@ flutter: dartPluginClass: ImagePickerWindows dependencies: - file_selector_platform_interface: ^2.0.4 + file_selector_platform_interface: ^2.2.0 file_selector_windows: ^0.8.2 flutter: sdk: flutter From 51c808188cc9fc2a8b1d42f8fd852f515f7538e8 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 7 Oct 2022 11:46:26 -0400 Subject: [PATCH 790/844] Roll Flutter from be86a916548a to eec8d9d9cc29 (28 revisions) (#6551) --- .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 f7578dc7ad5d..914a9007d2d1 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -be86a916548a66c93e4f2f919b5ca0ccd2cdae0c +eec8d9d9cc2945e12f11bc714de107ad9799ad9b From 028e7494bb0c0391ba3e3a4b065af9f52a30cde4 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Fri, 7 Oct 2022 18:35:03 -0400 Subject: [PATCH 791/844] [tool] Fix version mistake in CHANGELOG (#6552) --- script/tool/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index f62509040a3d..b7864c9c0501 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,4 +1,4 @@ -## 12.0 +## 0.12.0 * Changes the behavior of `--packages-for-branch` on main/master to run for packages changed in the last commit, rather than running for all packages. @@ -6,7 +6,7 @@ tested in presubmit. * Adds a `fix` command to run `dart fix --apply` in target packages. -## 0.11 +## 0.11.0 * Renames `publish-plugin` to `publish`. * Renames arguments to `list`: From cfbdcd0b40faabfbd345ea4d6498efd99affe838 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 8 Oct 2022 11:55:22 -0400 Subject: [PATCH 792/844] Roll Flutter from eec8d9d9cc29 to 23f22cb8535c (22 revisions) (#6554) --- .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 914a9007d2d1..c651766c614a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -eec8d9d9cc2945e12f11bc714de107ad9799ad9b +23f22cb8535c0e8746badef943c8c178d30b5f07 From 1cf02a83a4809be90c6a3431b9a3b846c87aa0a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Oct 2022 16:14:57 +0000 Subject: [PATCH 793/844] [gh_actions]: Bump github/codeql-action from 2.1.25 to 2.1.27 (#6559) --- .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 e14935f4e455..ab43bd18872e 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -50,6 +50,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@86f3159a697a097a813ad9bfa0002412d97690a4 + uses: github/codeql-action/upload-sarif@807578363a7869ca324a79039e6db9c843e0e100 with: sarif_file: results.sarif From 61b6fee478560e2baa8493b58f2f3c04a6954a9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Oct 2022 20:10:28 +0000 Subject: [PATCH 794/844] [gh_actions]: Bump actions/checkout from 3.0.2 to 3.1.0 (#6561) --- .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 5ff15569f842..7008ebfa8966 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,7 @@ jobs: cd $GITHUB_WORKSPACE # Checks out a copy of the repo. - name: Check out code - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 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 ab43bd18872e..dbd4ac47b189 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -22,7 +22,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 with: persist-credentials: false From f64d53ca4af6053e9579b3fc885fa3dbd5e3d679 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Oct 2022 20:58:13 +0000 Subject: [PATCH 795/844] [gh_actions]: Bump ossf/scorecard-action from 2.0.3 to 2.0.4 (#6560) --- .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 dbd4ac47b189..22a0c413d8bc 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -27,7 +27,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@865b4092859256271290c77adbd10a43f4779972 + uses: ossf/scorecard-action@e363bfca00e752f91de7b7d2a77340e2e523cb18 with: results_file: results.sarif results_format: sarif From 88aa7b1a3de4ce7bcf70a03af777d73b2b628e77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Oct 2022 21:43:38 +0000 Subject: [PATCH 796/844] [gh_actions]: Bump actions/labeler from 4.0.1 to 4.0.2 (#6558) --- .github/workflows/pull_request_label.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull_request_label.yml b/.github/workflows/pull_request_label.yml index baec3deb4893..16ad0a3c171a 100644 --- a/.github/workflows/pull_request_label.yml +++ b/.github/workflows/pull_request_label.yml @@ -21,7 +21,7 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@e54e5b338fbd6e6cdb5d60f51c22335fc57c401e + - uses: actions/labeler@5c7539237e04b714afd8ad9b4aed733815b9fab4 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" sync-labels: true From 5239062837ef235c614b646d3143ffda6ec57dd0 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 10 Oct 2022 17:47:22 -0400 Subject: [PATCH 797/844] Roll Flutter from 23f22cb8535c to 89d9880f46e0 (3 revisions) (#6556) --- .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 c651766c614a..f1a12af1e745 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -23f22cb8535c0e8746badef943c8c178d30b5f07 +89d9880f46e066f79bdf6da19bd44edfaa64c041 From b175843a5f2ded85b4e1c32021e7485b2aacce0d Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 11 Oct 2022 09:41:35 -0400 Subject: [PATCH 798/844] [various] Add some more Java lint ignores (#6562) --- packages/camera/camera_android/android/build.gradle | 2 ++ packages/camera/camera_android_camerax/android/build.gradle | 5 +++++ packages/espresso/android/build.gradle | 2 ++ .../flutter_plugin_android_lifecycle/android/build.gradle | 2 ++ .../google_maps_flutter_android/android/build.gradle | 2 ++ .../google_sign_in_android/android/build.gradle | 2 ++ .../image_picker/image_picker_android/android/build.gradle | 1 + .../in_app_purchase_android/android/build.gradle | 2 ++ packages/local_auth/local_auth_android/android/build.gradle | 2 ++ .../path_provider/path_provider_android/android/build.gradle | 2 ++ .../quick_actions/quick_actions_android/android/build.gradle | 2 ++ .../shared_preferences_android/android/build.gradle | 1 + .../url_launcher/url_launcher_android/android/build.gradle | 1 + .../video_player/video_player_android/android/build.gradle | 1 + .../webview_flutter_android/android/build.gradle | 1 + 15 files changed, 28 insertions(+) diff --git a/packages/camera/camera_android/android/build.gradle b/packages/camera/camera_android/android/build.gradle index b060181316c1..b371f2b5d8b7 100644 --- a/packages/camera/camera_android/android/build.gradle +++ b/packages/camera/camera_android/android/build.gradle @@ -35,6 +35,8 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { + // TODO(stuartmorgan): Enable when gradle is updated. + // disable 'AndroidGradlePluginVersion' disable 'InvalidPackage' disable 'GradleDependency' baseline file("lint-baseline.xml") diff --git a/packages/camera/camera_android_camerax/android/build.gradle b/packages/camera/camera_android_camerax/android/build.gradle index b4209bbe62a6..d20643f70268 100644 --- a/packages/camera/camera_android_camerax/android/build.gradle +++ b/packages/camera/camera_android_camerax/android/build.gradle @@ -47,6 +47,11 @@ android { } } } + + lintOptions { + disable 'AndroidGradlePluginVersion' + disable 'GradleDependency' + } } dependencies { diff --git a/packages/espresso/android/build.gradle b/packages/espresso/android/build.gradle index d78609270bf4..a83e9cce859f 100644 --- a/packages/espresso/android/build.gradle +++ b/packages/espresso/android/build.gradle @@ -29,6 +29,8 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { + // TODO(stuartmorgan): Enable when gradle is updated. + // disable 'AndroidGradlePluginVersion' disable 'InvalidPackage' disable 'GradleDependency' baseline file("lint-baseline.xml") diff --git a/packages/flutter_plugin_android_lifecycle/android/build.gradle b/packages/flutter_plugin_android_lifecycle/android/build.gradle index a86e070bc693..5786a74e2e78 100644 --- a/packages/flutter_plugin_android_lifecycle/android/build.gradle +++ b/packages/flutter_plugin_android_lifecycle/android/build.gradle @@ -30,6 +30,8 @@ android { consumerProguardFiles 'proguard.txt' } lintOptions { + // TODO(stuartmorgan): Enable when gradle is updated. + // disable 'AndroidGradlePluginVersion' disable 'InvalidPackage' disable 'GradleDependency' } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle index 06599fc94c49..44ad60be4f55 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle @@ -29,6 +29,8 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { + // TODO(stuartmorgan): Enable when gradle is updated. + // disable 'AndroidGradlePluginVersion' disable 'InvalidPackage' disable 'GradleDependency' } 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 3384029d666a..f3a324fefbc9 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 @@ -29,6 +29,8 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { + // TODO(stuartmorgan): Enable when gradle is updated. + // disable 'AndroidGradlePluginVersion' disable 'InvalidPackage' disable 'GradleDependency' } diff --git a/packages/image_picker/image_picker_android/android/build.gradle b/packages/image_picker/image_picker_android/android/build.gradle index a6c01e386448..aed1ad5174ea 100644 --- a/packages/image_picker/image_picker_android/android/build.gradle +++ b/packages/image_picker/image_picker_android/android/build.gradle @@ -29,6 +29,7 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { + disable 'AndroidGradlePluginVersion' disable 'InvalidPackage' disable 'GradleDependency' } diff --git a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle index d58d1cc7c052..2375acdc856f 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle +++ b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle @@ -29,6 +29,8 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { + // TODO(stuartmorgan): Enable when gradle is updated. + // disable 'AndroidGradlePluginVersion' disable 'InvalidPackage' disable 'GradleDependency' } diff --git a/packages/local_auth/local_auth_android/android/build.gradle b/packages/local_auth/local_auth_android/android/build.gradle index 399d8a4fbb45..569d7e3564b4 100644 --- a/packages/local_auth/local_auth_android/android/build.gradle +++ b/packages/local_auth/local_auth_android/android/build.gradle @@ -29,6 +29,8 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { + // TODO(stuartmorgan): Enable when gradle is updated. + // disable 'AndroidGradlePluginVersion' disable 'InvalidPackage' disable 'GradleDependency' baseline file("lint-baseline.xml") diff --git a/packages/path_provider/path_provider_android/android/build.gradle b/packages/path_provider/path_provider_android/android/build.gradle index 4bfa738ac44c..32c046bf235f 100644 --- a/packages/path_provider/path_provider_android/android/build.gradle +++ b/packages/path_provider/path_provider_android/android/build.gradle @@ -29,6 +29,8 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { + // TODO(stuartmorgan): Enable when gradle is updated. + // disable 'AndroidGradlePluginVersion' disable 'InvalidPackage' disable 'GradleDependency' } diff --git a/packages/quick_actions/quick_actions_android/android/build.gradle b/packages/quick_actions/quick_actions_android/android/build.gradle index 8cb1ca327251..e4cdec819ec9 100644 --- a/packages/quick_actions/quick_actions_android/android/build.gradle +++ b/packages/quick_actions/quick_actions_android/android/build.gradle @@ -29,6 +29,8 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { + // TODO(stuartmorgan): Enable when gradle is updated. + // disable 'AndroidGradlePluginVersion' disable 'InvalidPackage' disable 'GradleDependency' } diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index bd6adf411077..feae770a475d 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -37,6 +37,7 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { + disable 'AndroidGradlePluginVersion' disable 'InvalidPackage' disable 'GradleDependency' baseline file("lint-baseline.xml") diff --git a/packages/url_launcher/url_launcher_android/android/build.gradle b/packages/url_launcher/url_launcher_android/android/build.gradle index a4fa269cbfc7..0353973b4ffe 100644 --- a/packages/url_launcher/url_launcher_android/android/build.gradle +++ b/packages/url_launcher/url_launcher_android/android/build.gradle @@ -29,6 +29,7 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { + disable 'AndroidGradlePluginVersion' disable 'InvalidPackage' disable 'GradleDependency' } diff --git a/packages/video_player/video_player_android/android/build.gradle b/packages/video_player/video_player_android/android/build.gradle index 2376d53a0ec1..2677050d303b 100644 --- a/packages/video_player/video_player_android/android/build.gradle +++ b/packages/video_player/video_player_android/android/build.gradle @@ -34,6 +34,7 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } lintOptions { + disable 'AndroidGradlePluginVersion' disable 'InvalidPackage' disable 'GradleDependency' } diff --git a/packages/webview_flutter/webview_flutter_android/android/build.gradle b/packages/webview_flutter/webview_flutter_android/android/build.gradle index 65374fee88bf..7384f8d453da 100644 --- a/packages/webview_flutter/webview_flutter_android/android/build.gradle +++ b/packages/webview_flutter/webview_flutter_android/android/build.gradle @@ -30,6 +30,7 @@ android { } lintOptions { + disable 'AndroidGradlePluginVersion' disable 'InvalidPackage' disable 'GradleDependency' } From 5a5725730cb20628d1ffbd86dc39f1861629a642 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 11 Oct 2022 11:53:58 -0400 Subject: [PATCH 799/844] Roll Flutter from 89d9880f46e0 to 4fed6aaeff8f (23 revisions) (#6564) --- .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 f1a12af1e745..255109237fbb 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -89d9880f46e066f79bdf6da19bd44edfaa64c041 +4fed6aaeff8f38d4e04e8c8e1c8648dded229617 From 5f0a5f225befdef9c8bbadc148a40f9673d20475 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 12 Oct 2022 13:14:26 -0400 Subject: [PATCH 800/844] Roll Flutter from 4fed6aaeff8f to 91d88336ddcf (13 revisions) (#6567) * 92b43050f Roll Flutter Engine from a5f44b8c08b6 to 7c4ad0b7d49e (1 revision) (flutter/flutter#113284) * 95687eb26 Roll Plugins from cfbdcd0b40fa to b175843a5f2d (6 revisions) (flutter/flutter#113287) * 4cbeb9ddb Roll Flutter Engine from 7c4ad0b7d49e to 281152f8322d (1 revision) (flutter/flutter#113288) * e1f3cff2e Roll Flutter Engine from 281152f8322d to c9f4e97abf68 (1 revision) (flutter/flutter#113291) * 8c8bb2222 BufferLogger should log stacktrace (flutter/flutter#113240) * ee38dbdd6 Roll Flutter Engine from c9f4e97abf68 to b783740a940f (1 revision) (flutter/flutter#113297) * a84a2582b Roll Flutter Engine from b783740a940f to 7979ffc80832 (1 revision) (flutter/flutter#113299) * b2bba4a28 Roll Flutter Engine from 7979ffc80832 to 30d57deea8a6 (1 revision) (flutter/flutter#113304) * 07e929363 Roll Flutter Engine from 30d57deea8a6 to bb0641278cd7 (1 revision) (flutter/flutter#113305) * 096cd8349 Roll Flutter Engine from bb0641278cd7 to c4da2d8903ce (1 revision) (flutter/flutter#113309) * aab696c23 Roll Flutter Engine from c4da2d8903ce to 481f20168905 (1 revision) (flutter/flutter#113313) * a5ee67ef8 Roll Flutter Engine from 481f20168905 to ea64def17550 (1 revision) (flutter/flutter#113321) * 91d88336d Added `Switch` Animation for Material 3 (flutter/flutter#113090) --- .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 255109237fbb..06ec521d86f7 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -4fed6aaeff8f38d4e04e8c8e1c8648dded229617 +91d88336ddcf53969674d7ec19c9f5ada2e6e799 From c407d929e5996c65976d6e4bd82d60e18d0ccc37 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 13 Oct 2022 11:53:10 -0400 Subject: [PATCH 801/844] Roll Flutter from 91d88336ddcf to c738c8aa9616 (8 revisions) (#6571) --- .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 06ec521d86f7..3f43e970bb54 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -91d88336ddcf53969674d7ec19c9f5ada2e6e799 +c738c8aa961670929f90124ace71b7d9b263e002 From ef8a9ff609208949e38307d23c0fb44d975a4b77 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 13 Oct 2022 16:17:22 -0400 Subject: [PATCH 802/844] [quick_actions] Update dependency versions (#6563) --- packages/quick_actions/quick_actions/CHANGELOG.md | 4 ++++ packages/quick_actions/quick_actions/pubspec.yaml | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/quick_actions/quick_actions/CHANGELOG.md b/packages/quick_actions/quick_actions/CHANGELOG.md index b8b431490cae..7d1881596255 100644 --- a/packages/quick_actions/quick_actions/CHANGELOG.md +++ b/packages/quick_actions/quick_actions/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.1 + +* Updates implementaion package versions to current versions. + ## 1.0.0 * Updates version to 1.0 to reflect current status. diff --git a/packages/quick_actions/quick_actions/pubspec.yaml b/packages/quick_actions/quick_actions/pubspec.yaml index 9ce18456969e..08b486fe50e3 100644 --- a/packages/quick_actions/quick_actions/pubspec.yaml +++ b/packages/quick_actions/quick_actions/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for creating shortcuts on home screen, also known as Quick Actions on iOS and App Shortcuts on Android. repository: https://github.com/flutter/plugins/tree/main/packages/quick_actions/quick_actions issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+quick_actions%22 -version: 1.0.0 +version: 1.0.1 environment: sdk: ">=2.14.0 <3.0.0" @@ -20,8 +20,8 @@ flutter: dependencies: flutter: sdk: flutter - quick_actions_android: ^0.6.0+9 - quick_actions_ios: ^0.6.0+9 + quick_actions_android: ^1.0.0 + quick_actions_ios: ^1.0.0 quick_actions_platform_interface: ^1.0.0 dev_dependencies: From b033ac7de5df235e7f3da0e539fa47e2e743e94a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 14 Oct 2022 11:52:23 -0400 Subject: [PATCH 803/844] Roll Flutter from c738c8aa9616 to 08e1729204f3 (8 revisions) (#6574) --- .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 3f43e970bb54..e96d0e3b813d 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -c738c8aa961670929f90124ace71b7d9b263e002 +08e1729204f3e2be062bf3aabbe14048561171ac From 3d44753ad9bab0965d465ff6c73c9c416923b5c8 Mon Sep 17 00:00:00 2001 From: Hosam Hasan Ramadan Date: Fri, 14 Oct 2022 19:22:37 +0200 Subject: [PATCH 804/844] [shared_preferences] fix typo (#6570) --- .../shared_preferences/shared_preferences_android/CHANGELOG.md | 3 ++- .../lib/shared_preferences_android.dart | 2 +- .../shared_preferences/shared_preferences_android/pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index f21af6aff7aa..d9d7bbd82f46 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.0.14 +* Fixes typo in `SharedPreferencesAndroid` docs. * Updates code for `no_leading_underscores_for_local_identifiers` lint. ## 2.0.13 diff --git a/packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart b/packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart index 86f447b8959c..da5147d32da2 100644 --- a/packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart +++ b/packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart @@ -10,7 +10,7 @@ import 'package:shared_preferences_platform_interface/shared_preferences_platfor const MethodChannel _kChannel = MethodChannel('plugins.flutter.io/shared_preferences_android'); -/// The macOS implementation of [SharedPreferencesStorePlatform]. +/// The Android implementation of [SharedPreferencesStorePlatform]. /// /// This class implements the `package:shared_preferences` functionality for Android. class SharedPreferencesAndroid extends SharedPreferencesStorePlatform { diff --git a/packages/shared_preferences/shared_preferences_android/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/pubspec.yaml index 7d7dfa85b71a..7692c114bfce 100644 --- a/packages/shared_preferences/shared_preferences_android/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_android description: Android implementation of the shared_preferences plugin repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.13 +version: 2.0.14 environment: sdk: ">=2.14.0 <3.0.0" From b74525777205166722553659de51abee3b14d177 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 15 Oct 2022 11:47:03 -0400 Subject: [PATCH 805/844] Roll Flutter from 08e1729204f3 to a707d05e9d4c (6 revisions) (#6577) --- .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 e96d0e3b813d..aedbdf2f7aaa 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -08e1729204f3e2be062bf3aabbe14048561171ac +a707d05e9d4c7ec95403444e04a65cd69c0da3b0 From 5a0209d99d8966e0907f9e0632558234f8dca71c Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 17 Oct 2022 12:51:20 -0400 Subject: [PATCH 806/844] Roll Flutter from a707d05e9d4c to f26dd3730df8 (10 revisions) (#6584) --- .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 aedbdf2f7aaa..b98166d52783 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -a707d05e9d4c7ec95403444e04a65cd69c0da3b0 +f26dd3730df8fe831243f9d58dd8ef6b8919d2ce From fd3ee3885c8b87724f8ad7e1223a97661affd06f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20=C3=81ngel=20Dausa?= Date: Tue, 18 Oct 2022 15:13:20 -0300 Subject: [PATCH 807/844] [file_selector] Update file_selector_platform_interface by adding the new property 'uniformTypeIdentifiers' to the XTypeGroup. (#6433) --- .../CHANGELOG.md | 4 ++ .../src/types/x_type_group/x_type_group.dart | 15 ++-- .../pubspec.yaml | 2 +- .../test/x_type_group_test.dart | 70 +++++++++++++++++-- 4 files changed, 82 insertions(+), 9 deletions(-) diff --git a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md index ad1fe2cb6cef..ad803fb12e66 100644 --- a/packages/file_selector/file_selector_platform_interface/CHANGELOG.md +++ b/packages/file_selector/file_selector_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.0 + +* Replaces `macUTIs` with `uniformTypeIdentifiers`. `macUTIs` is available as an alias, but will be deprecated in a future release. + ## 2.2.0 * Makes `XTypeGroup`'s constructor constant. diff --git a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart index 00cd56563c32..e12b431d91d8 100644 --- a/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart +++ b/packages/file_selector/file_selector_platform_interface/lib/src/types/x_type_group/x_type_group.dart @@ -14,9 +14,13 @@ class XTypeGroup { this.label, List? extensions, this.mimeTypes, - this.macUTIs, + List? macUTIs, + List? uniformTypeIdentifiers, this.webWildCards, - }) : _extensions = extensions; + }) : _extensions = extensions, + assert(uniformTypeIdentifiers == null || macUTIs == null, + 'Only one of uniformTypeIdentifiers or macUTIs can be non-null'), + uniformTypeIdentifiers = uniformTypeIdentifiers ?? macUTIs; /// The 'name' or reference to this group of types. final String? label; @@ -24,8 +28,8 @@ class XTypeGroup { /// The MIME types for this group. final List? mimeTypes; - /// The UTIs for this group. - final List? macUTIs; + /// The uniform type identifiers for this group + final List? uniformTypeIdentifiers; /// The web wild cards for this group (ex: image/*, video/*). final List? webWildCards; @@ -56,6 +60,9 @@ class XTypeGroup { (webWildCards?.isEmpty ?? true); } + /// Returns the list of uniform type identifiers for this group + List? get macUTIs => uniformTypeIdentifiers; + static List? _removeLeadingDots(List? exts) => exts ?.map((String ext) => ext.startsWith('.') ? ext.substring(1) : ext) .toList(); diff --git a/packages/file_selector/file_selector_platform_interface/pubspec.yaml b/packages/file_selector/file_selector_platform_interface/pubspec.yaml index c4500061a3a1..ac8727c09e36 100644 --- a/packages/file_selector/file_selector_platform_interface/pubspec.yaml +++ b/packages/file_selector/file_selector_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.2.0 +version: 2.3.0 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart b/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart index c5e65d0264f5..5ac5722716c7 100644 --- a/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart +++ b/packages/file_selector/file_selector_platform_interface/test/x_type_group_test.dart @@ -8,12 +8,11 @@ import 'package:flutter_test/flutter_test.dart'; void main() { group('XTypeGroup', () { test('toJSON() creates correct map', () { - const String label = 'test group'; const List extensions = ['txt', 'jpg']; const List mimeTypes = ['text/plain']; const List macUTIs = ['public.plain-text']; const List webWildCards = ['image/*']; - + const String label = 'test group'; const XTypeGroup group = XTypeGroup( label: label, extensions: extensions, @@ -30,7 +29,7 @@ void main() { expect(jsonMap['webWildCards'], webWildCards); }); - test('A wildcard group can be created', () { + test('a wildcard group can be created', () { const XTypeGroup group = XTypeGroup( label: 'Any', ); @@ -71,7 +70,70 @@ void main() { expect(webOnly.allowsAny, false); }); - test('Leading dots are removed from extensions', () { + test('passing only macUTIs should fill uniformTypeIdentifiers', () { + const List macUTIs = ['public.plain-text']; + const XTypeGroup group = XTypeGroup( + macUTIs: macUTIs, + ); + + expect(group.uniformTypeIdentifiers, macUTIs); + }); + + test( + 'passing only uniformTypeIdentifiers should fill uniformTypeIdentifiers', + () { + const List uniformTypeIdentifiers = ['public.plain-text']; + const XTypeGroup group = XTypeGroup( + uniformTypeIdentifiers: uniformTypeIdentifiers, + ); + + expect(group.uniformTypeIdentifiers, uniformTypeIdentifiers); + }); + + test('macUTIs getter return macUTIs value passed in constructor', () { + const List macUTIs = ['public.plain-text']; + const XTypeGroup group = XTypeGroup( + macUTIs: macUTIs, + ); + + expect(group.macUTIs, macUTIs); + }); + + test( + 'macUTIs getter returns uniformTypeIdentifiers value passed in constructor', + () { + const List uniformTypeIdentifiers = ['public.plain-text']; + const XTypeGroup group = XTypeGroup( + uniformTypeIdentifiers: uniformTypeIdentifiers, + ); + + expect(group.macUTIs, uniformTypeIdentifiers); + }); + + test('passing both uniformTypeIdentifiers and macUTIs should throw', () { + const List macUTIs = ['public.plain-text']; + const List uniformTypeIndentifiers = [ + 'public.plain-images' + ]; + expect( + () => XTypeGroup( + macUTIs: macUTIs, + uniformTypeIdentifiers: uniformTypeIndentifiers), + throwsA(predicate((Object? e) => + e is AssertionError && + e.message == + 'Only one of uniformTypeIdentifiers or macUTIs can be non-null'))); + }); + + test( + 'having uniformTypeIdentifiers and macUTIs as null should leave uniformTypeIdentifiers as null', + () { + const XTypeGroup group = XTypeGroup(); + + expect(group.uniformTypeIdentifiers, null); + }); + + test('leading dots are removed from extensions', () { const List extensions = ['.txt', '.jpg']; const XTypeGroup group = XTypeGroup(extensions: extensions); From e6184f9cca0139ad3a45c212c2189b928b4e690d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 18 Oct 2022 15:12:10 -0400 Subject: [PATCH 808/844] Roll Flutter from f26dd3730df8 to eea60f86f1b1 (19 revisions) (#6588) --- .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 b98166d52783..2c2862d5434f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -f26dd3730df8fe831243f9d58dd8ef6b8919d2ce +eea60f86f1b1f22b1f51538a976e44ae10c2badf From 79894bcdc117e615e76dfb43e9d9df9de89f46a3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 19 Oct 2022 11:44:24 -0400 Subject: [PATCH 809/844] Roll Flutter from eea60f86f1b1 to abfaec68114b (10 revisions) (#6590) --- .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 2c2862d5434f..43de99735cb2 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -eea60f86f1b1f22b1f51538a976e44ae10c2badf +abfaec68114b0616485df3ec2eb2f5c2d76b4ceb From aeab040673b0ab2662294bf803f393cd01eaf4a7 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 19 Oct 2022 14:52:57 -0400 Subject: [PATCH 810/844] [ci] Cirrus pre-alignment with flutter/packages, part 1 (#6569) --- .ci/scripts/prepare_tool.sh | 0 .cirrus.yml | 119 ++++++++++++++++++------------------ 2 files changed, 60 insertions(+), 59 deletions(-) mode change 100644 => 100755 .ci/scripts/prepare_tool.sh diff --git a/.ci/scripts/prepare_tool.sh b/.ci/scripts/prepare_tool.sh old mode 100644 new mode 100755 diff --git a/.cirrus.yml b/.cirrus.yml index 01de99c23e9c..89e1405a901e 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -4,13 +4,26 @@ gcp_credentials: ENCRYPTED[!f1177d1ddb5330ffaa9ea11c9c9e8e0c542185e895c36071f18c only_if: $CIRRUS_TAG == '' && ($CIRRUS_PR != '' || $CIRRUS_BRANCH == 'main') env: CHANNEL: "master" # Default to master when not explicitly set by a task. - PLUGIN_TOOL: "./script/tool/bin/flutter_plugin_tools.dart" + PLUGIN_TOOL_COMMAND: "dart ./script/tool/bin/flutter_plugin_tools.dart" tool_setup_template: &TOOL_SETUP_TEMPLATE tool_setup_script: - - git fetch origin main # To set FETCH_HEAD for "git merge-base" to work - - cd script/tool - - dart pub get + - .ci/scripts/prepare_tool.sh + +macos_template: &MACOS_TEMPLATE + # Only one macOS task can run in parallel without credits, so use them for + # PRs on macOS. + use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true' + +macos_intel_template: &MACOS_INTEL_TEMPLATE + << : *MACOS_TEMPLATE + osx_instance: + image: big-sur-xcode-13 + +macos_arm_template: &MACOS_ARM_TEMPLATE + << : *MACOS_TEMPLATE + macos_instance: + image: ghcr.io/cirruslabs/macos-ventura-xcode:14 flutter_upgrade_template: &FLUTTER_UPGRADE_TEMPLATE upgrade_flutter_script: @@ -39,7 +52,7 @@ flutter_upgrade_template: &FLUTTER_UPGRADE_TEMPLATE build_all_plugins_app_template: &BUILD_ALL_PLUGINS_APP_TEMPLATE create_all_plugins_app_script: - - dart $PLUGIN_TOOL all-plugins-app --output-dir=. --exclude script/configs/exclude_all_plugins_app.yaml + - $PLUGIN_TOOL_COMMAND all-plugins-app --output-dir=. --exclude script/configs/exclude_all_plugins_app.yaml build_all_plugins_debug_script: - cd all_plugins - if [[ "$BUILD_ALL_ARGS" == "web" ]]; then @@ -51,21 +64,6 @@ build_all_plugins_app_template: &BUILD_ALL_PLUGINS_APP_TEMPLATE - cd all_plugins - flutter build $BUILD_ALL_ARGS --release -macos_template: &MACOS_TEMPLATE - # Only one macOS task can run in parallel without credits, so use them for - # PRs on macOS. - use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true' - -macos_intel_template: &MACOS_INTEL_TEMPLATE - << : *MACOS_TEMPLATE - osx_instance: - image: big-sur-xcode-13 - -macos_arm_template: &MACOS_ARM_TEMPLATE - << : *MACOS_TEMPLATE - macos_instance: - image: ghcr.io/cirruslabs/macos-ventura-xcode:14 - # Light-workload Linux tasks. # These use default machines, with fewer CPUs, to reduce pressure on the # concurrency limits. @@ -84,22 +82,13 @@ task: script: - cd script/tool - dart pub run test - - name: publishable - version_check_script: - # For pre-submit, pass the PR labels to the script to allow for version - # check overrides. - # For post-submit, ignore platform version breaking version changes and - # missing version/CHANGELOG detection since the labels aren't - # available outside of the context of the PR. - - if [[ $CIRRUS_PR == "" ]]; then - - ./script/tool_runner.sh version-check --ignore-platform-interface-breaks - - else - - ./script/tool_runner.sh version-check --check-for-missing-changes --pr-labels="$CIRRUS_PR_LABELS" - - fi - publish_check_script: ./script/tool_runner.sh publish-check - - name: format + # Repository rules and best-practice enforcement. + # Only channel-agnostic tests should go here since it is only run once + # (on Flutter master). + - name: repo_checks always: format_script: ./script/tool_runner.sh format --fail-on-change + license_script: $PLUGIN_TOOL_COMMAND license-check pubspec_script: ./script/tool_runner.sh pubspec-check readme_script: - ./script/tool_runner.sh readme-check @@ -108,19 +97,33 @@ task: # has been fixed, this can be removed and there can just be a single # run with --require-excerpts and no exclusions. - ./script/tool_runner.sh readme-check --require-excerpts --exclude=script/configs/temp_exclude_excerpt.yaml - license_script: dart $PLUGIN_TOOL license-check - dependabot_script: dart $PLUGIN_TOOL dependabot-check - - name: federated_safety - # This check is only meaningful for PRs, as it validates changes - # rather than state. - only_if: $CIRRUS_PR != "" - script: ./script/tool_runner.sh federation-safety-check + dependabot_script: $PLUGIN_TOOL_COMMAND dependabot-check + version_script: + # For pre-submit, pass the PR labels to the script to allow for + # check overrides. + # For post-submit, ignore platform version breaking version changes + # and missing version/CHANGELOG detection since the labels aren't + # available outside of the context of the PR. + - if [[ $CIRRUS_PR == "" ]]; then + - ./script/tool_runner.sh version-check --ignore-platform-interface-breaks + - else + - ./script/tool_runner.sh version-check --check-for-missing-changes --pr-labels="$CIRRUS_PR_LABELS" + - fi + publishable_script: ./script/tool_runner.sh publish-check --allow-pre-release + federated_safety_script: + # This check is only meaningful for PRs, as it validates changes + # rather than state. + - if [[ $CIRRUS_PR == "" ]]; then + - ./script/tool_runner.sh federation-safety-check + - else + - echo "Only run in presubmit" + - fi - name: dart_unit_tests env: matrix: CHANNEL: "master" CHANNEL: "stable" - test_script: + unit_test_script: - ./script/tool_runner.sh test - name: analyze env: @@ -142,39 +145,42 @@ task: # This uses --run-on-dirty-packages rather than --packages-for-branch # since only the packages changed by 'make-deps-path-based' need to be # checked. - - dart $PLUGIN_TOOL analyze --run-on-dirty-packages --log-timing --custom-analysis=script/configs/custom_analysis.yaml + - $PLUGIN_TOOL_COMMAND analyze --run-on-dirty-packages --log-timing --custom-analysis=script/configs/custom_analysis.yaml # Restore the tree to a clean state, to avoid accidental issues if # other script steps are added to this task. - git checkout . - # Does a sanity check that plugins at least pass analysis on the N-1 and N-2 - # versions of Flutter stable if the plugin claims to support that version. + # Does a sanity check that packages at least pass analysis on the N-1 and N-2 + # versions of Flutter stable if the package claims to support that version. # This is to minimize accidentally making changes that break old versions # (which we don't commit to supporting, but don't want to actively break) # without updating the constraints. # Note: The versions below should be manually updated after a new stable # version comes out. - - name: legacy-version-analyze + - name: legacy_version_analyze depends_on: analyze - env: - matrix: + matrix: + env: CHANNEL: "3.0.5" + DART_VERSION: "2.17.6" + env: CHANNEL: "2.10.5" + DART_VERSION: "2.16.2" package_prep_script: - # Allow analyzing plugins that use a Pigeon version with a higher - # minimum Flutter/Dart version than the plugin itself. + # Allow analyzing packages that use a dev dependency with a higher + # minimum Flutter/Dart version than the package itself. - ./script/tool_runner.sh remove-dev-dependencies analyze_script: # Only analyze lib/; non-client code doesn't need to work on # all supported legacy version. - - ./script/tool_runner.sh analyze --lib-only --skip-if-not-supporting-flutter-version="$CHANNEL" --custom-analysis=script/configs/custom_analysis.yaml - # Does a sanity check that plugins pass analysis with the lowest possible + - ./script/tool_runner.sh analyze --lib-only --skip-if-not-supporting-flutter-version="$CHANNEL" --skip-if-not-supporting-dart-version="$DART_VERSION" --custom-analysis=script/configs/custom_analysis.yaml + # Does a sanity check that packages pass analysis with the lowest possible # versions of all dependencies. This is to catch cases where we add use of - # new APIs but forget to update minimum versions of dependencies to when + # new APIs but forget to update minimum versions of dependencies to where # those APIs are introduced. - name: downgraded_analyze depends_on: analyze analyze_script: - - ./script/tool_runner.sh analyze --downgrade + - ./script/tool_runner.sh analyze --downgrade --custom-analysis=script/configs/custom_analysis.yaml - name: readme_excerpts env: CIRRUS_CLONE_SUBMODULES: true @@ -194,8 +200,6 @@ task: matrix: CHANNEL: "master" CHANNEL: "stable" - setup_script: - - flutter config --enable-linux-desktop << : *BUILD_ALL_PLUGINS_APP_TEMPLATE - name: linux-platform_tests # Don't run full platform tests on both channels in pre-submit. @@ -205,7 +209,6 @@ task: CHANNEL: "master" CHANNEL: "stable" build_script: - - flutter config --enable-linux-desktop - ./script/tool_runner.sh build-examples --linux native_test_script: - xvfb-run ./script/tool_runner.sh native-test --linux --no-integration @@ -339,7 +342,6 @@ task: CHANNEL: "stable" PATH: $PATH:/usr/local/bin build_script: - - flutter config --enable-macos-desktop - ./script/tool_runner.sh build-examples --macos xcode_analyze_script: - ./script/tool_runner.sh xcode-analyze --macos @@ -408,5 +410,4 @@ task: CHANNEL: "master" CHANNEL: "stable" setup_script: - - flutter config --enable-macos-desktop << : *BUILD_ALL_PLUGINS_APP_TEMPLATE From 6b61dd2da492f6d7af966993c742a0ed38fe1e01 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 20 Oct 2022 11:50:53 -0400 Subject: [PATCH 811/844] Roll Flutter (stable) from eb6d86ee27de to d9111f640213 (4 revisions) (#6594) --- .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 c26442d70e76..cc156ea76c97 100644 --- a/.ci/flutter_stable.version +++ b/.ci/flutter_stable.version @@ -1 +1 @@ -eb6d86ee27deecba4a83536aa20f366a6044895c +d9111f64021372856901a1fd5bfbc386cade3318 From a577c004a4b97358a969303fe1d9e75ce1125477 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Thu, 20 Oct 2022 12:38:05 -0400 Subject: [PATCH 812/844] Roll Flutter from abfaec68114b to 637e5bce662f (13 revisions) (#6593) --- .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 43de99735cb2..cb8fe40cd622 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -abfaec68114b0616485df3ec2eb2f5c2d76b4ceb +637e5bce662fb43459d409e13ba5a7fb2c3fa930 From 32b1f7bd39ac5974d1d348f28fb71c567827edab Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Thu, 20 Oct 2022 10:42:17 -0700 Subject: [PATCH 813/844] [ci] Update web mocks. (#6591) --- .../google_maps_controller_test.mocks.dart | 501 ++- .../google_maps_plugin_test.mocks.dart | 335 +- .../example/pubspec.yaml | 2 +- .../url_launcher_web_test.mocks.dart | 1921 ++++++--- .../url_launcher_web/example/pubspec.yaml | 2 +- .../webview_flutter_web/pubspec.yaml | 2 +- .../test/webview_flutter_web_test.mocks.dart | 3540 +++++++++++------ 7 files changed, 4273 insertions(+), 2030 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart index a7fe6bae347b..efde66459327 100644 --- a/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart +++ b/packages/google_maps_flutter/google_maps_flutter_web/example/integration_test/google_maps_controller_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.3.0 from annotations +// Mocks generated by Mockito 5.3.2 from annotations // in google_maps_flutter_web_integration_tests/integration_test/google_maps_controller_test.dart. // Do not manually edit this file. @@ -21,8 +21,13 @@ import 'package:mockito/mockito.dart' as _i1; // ignore_for_file: subtype_of_sealed_class class _FakeGMap_0 extends _i1.SmartFake implements _i2.GMap { - _FakeGMap_0(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + _FakeGMap_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); } /// A class which mocks [CirclesController]. @@ -30,43 +35,85 @@ class _FakeGMap_0 extends _i1.SmartFake implements _i2.GMap { /// See the documentation for Mockito's code generation for more information. class MockCirclesController extends _i1.Mock implements _i3.CirclesController { @override - Map<_i4.CircleId, _i3.CircleController> get circles => - (super.noSuchMethod(Invocation.getter(#circles), - returnValue: <_i4.CircleId, _i3.CircleController>{}, - returnValueForMissingStub: <_i4.CircleId, _i3.CircleController>{}) - as Map<_i4.CircleId, _i3.CircleController>); - @override - _i2.GMap get googleMap => (super.noSuchMethod(Invocation.getter(#googleMap), - returnValue: _FakeGMap_0(this, Invocation.getter(#googleMap)), - returnValueForMissingStub: - _FakeGMap_0(this, Invocation.getter(#googleMap))) as _i2.GMap); - @override - set googleMap(_i2.GMap? _googleMap) => - super.noSuchMethod(Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null); - @override - int get mapId => (super.noSuchMethod(Invocation.getter(#mapId), - returnValue: 0, returnValueForMissingStub: 0) as int); - @override - set mapId(int? _mapId) => - super.noSuchMethod(Invocation.setter(#mapId, _mapId), - returnValueForMissingStub: null); - @override - void addCircles(Set<_i4.Circle>? circlesToAdd) => - super.noSuchMethod(Invocation.method(#addCircles, [circlesToAdd]), - returnValueForMissingStub: null); - @override - void changeCircles(Set<_i4.Circle>? circlesToChange) => - super.noSuchMethod(Invocation.method(#changeCircles, [circlesToChange]), - returnValueForMissingStub: null); + Map<_i4.CircleId, _i3.CircleController> get circles => (super.noSuchMethod( + Invocation.getter(#circles), + returnValue: <_i4.CircleId, _i3.CircleController>{}, + returnValueForMissingStub: <_i4.CircleId, _i3.CircleController>{}, + ) as Map<_i4.CircleId, _i3.CircleController>); + @override + _i2.GMap get googleMap => (super.noSuchMethod( + Invocation.getter(#googleMap), + returnValue: _FakeGMap_0( + this, + Invocation.getter(#googleMap), + ), + returnValueForMissingStub: _FakeGMap_0( + this, + Invocation.getter(#googleMap), + ), + ) as _i2.GMap); + @override + set googleMap(_i2.GMap? _googleMap) => super.noSuchMethod( + Invocation.setter( + #googleMap, + _googleMap, + ), + returnValueForMissingStub: null, + ); + @override + int get mapId => (super.noSuchMethod( + Invocation.getter(#mapId), + returnValue: 0, + returnValueForMissingStub: 0, + ) as int); + @override + set mapId(int? _mapId) => super.noSuchMethod( + Invocation.setter( + #mapId, + _mapId, + ), + returnValueForMissingStub: null, + ); + @override + void addCircles(Set<_i4.Circle>? circlesToAdd) => super.noSuchMethod( + Invocation.method( + #addCircles, + [circlesToAdd], + ), + returnValueForMissingStub: null, + ); + @override + void changeCircles(Set<_i4.Circle>? circlesToChange) => super.noSuchMethod( + Invocation.method( + #changeCircles, + [circlesToChange], + ), + returnValueForMissingStub: null, + ); @override void removeCircles(Set<_i4.CircleId>? circleIdsToRemove) => - super.noSuchMethod(Invocation.method(#removeCircles, [circleIdsToRemove]), - returnValueForMissingStub: null); - @override - void bindToMap(int? mapId, _i2.GMap? googleMap) => - super.noSuchMethod(Invocation.method(#bindToMap, [mapId, googleMap]), - returnValueForMissingStub: null); + super.noSuchMethod( + Invocation.method( + #removeCircles, + [circleIdsToRemove], + ), + returnValueForMissingStub: null, + ); + @override + void bindToMap( + int? mapId, + _i2.GMap? googleMap, + ) => + super.noSuchMethod( + Invocation.method( + #bindToMap, + [ + mapId, + googleMap, + ], + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [PolygonsController]. @@ -76,42 +123,84 @@ class MockPolygonsController extends _i1.Mock implements _i3.PolygonsController { @override Map<_i4.PolygonId, _i3.PolygonController> get polygons => (super.noSuchMethod( - Invocation.getter(#polygons), - returnValue: <_i4.PolygonId, _i3.PolygonController>{}, - returnValueForMissingStub: <_i4.PolygonId, _i3.PolygonController>{}) - as Map<_i4.PolygonId, _i3.PolygonController>); - @override - _i2.GMap get googleMap => (super.noSuchMethod(Invocation.getter(#googleMap), - returnValue: _FakeGMap_0(this, Invocation.getter(#googleMap)), - returnValueForMissingStub: - _FakeGMap_0(this, Invocation.getter(#googleMap))) as _i2.GMap); - @override - set googleMap(_i2.GMap? _googleMap) => - super.noSuchMethod(Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null); - @override - int get mapId => (super.noSuchMethod(Invocation.getter(#mapId), - returnValue: 0, returnValueForMissingStub: 0) as int); - @override - set mapId(int? _mapId) => - super.noSuchMethod(Invocation.setter(#mapId, _mapId), - returnValueForMissingStub: null); - @override - void addPolygons(Set<_i4.Polygon>? polygonsToAdd) => - super.noSuchMethod(Invocation.method(#addPolygons, [polygonsToAdd]), - returnValueForMissingStub: null); - @override - void changePolygons(Set<_i4.Polygon>? polygonsToChange) => - super.noSuchMethod(Invocation.method(#changePolygons, [polygonsToChange]), - returnValueForMissingStub: null); - @override - void removePolygons(Set<_i4.PolygonId>? polygonIdsToRemove) => super - .noSuchMethod(Invocation.method(#removePolygons, [polygonIdsToRemove]), - returnValueForMissingStub: null); - @override - void bindToMap(int? mapId, _i2.GMap? googleMap) => - super.noSuchMethod(Invocation.method(#bindToMap, [mapId, googleMap]), - returnValueForMissingStub: null); + Invocation.getter(#polygons), + returnValue: <_i4.PolygonId, _i3.PolygonController>{}, + returnValueForMissingStub: <_i4.PolygonId, _i3.PolygonController>{}, + ) as Map<_i4.PolygonId, _i3.PolygonController>); + @override + _i2.GMap get googleMap => (super.noSuchMethod( + Invocation.getter(#googleMap), + returnValue: _FakeGMap_0( + this, + Invocation.getter(#googleMap), + ), + returnValueForMissingStub: _FakeGMap_0( + this, + Invocation.getter(#googleMap), + ), + ) as _i2.GMap); + @override + set googleMap(_i2.GMap? _googleMap) => super.noSuchMethod( + Invocation.setter( + #googleMap, + _googleMap, + ), + returnValueForMissingStub: null, + ); + @override + int get mapId => (super.noSuchMethod( + Invocation.getter(#mapId), + returnValue: 0, + returnValueForMissingStub: 0, + ) as int); + @override + set mapId(int? _mapId) => super.noSuchMethod( + Invocation.setter( + #mapId, + _mapId, + ), + returnValueForMissingStub: null, + ); + @override + void addPolygons(Set<_i4.Polygon>? polygonsToAdd) => super.noSuchMethod( + Invocation.method( + #addPolygons, + [polygonsToAdd], + ), + returnValueForMissingStub: null, + ); + @override + void changePolygons(Set<_i4.Polygon>? polygonsToChange) => super.noSuchMethod( + Invocation.method( + #changePolygons, + [polygonsToChange], + ), + returnValueForMissingStub: null, + ); + @override + void removePolygons(Set<_i4.PolygonId>? polygonIdsToRemove) => + super.noSuchMethod( + Invocation.method( + #removePolygons, + [polygonIdsToRemove], + ), + returnValueForMissingStub: null, + ); + @override + void bindToMap( + int? mapId, + _i2.GMap? googleMap, + ) => + super.noSuchMethod( + Invocation.method( + #bindToMap, + [ + mapId, + googleMap, + ], + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [PolylinesController]. @@ -121,42 +210,85 @@ class MockPolylinesController extends _i1.Mock implements _i3.PolylinesController { @override Map<_i4.PolylineId, _i3.PolylineController> get lines => (super.noSuchMethod( - Invocation.getter(#lines), - returnValue: <_i4.PolylineId, _i3.PolylineController>{}, - returnValueForMissingStub: <_i4.PolylineId, _i3.PolylineController>{}) - as Map<_i4.PolylineId, _i3.PolylineController>); - @override - _i2.GMap get googleMap => (super.noSuchMethod(Invocation.getter(#googleMap), - returnValue: _FakeGMap_0(this, Invocation.getter(#googleMap)), - returnValueForMissingStub: - _FakeGMap_0(this, Invocation.getter(#googleMap))) as _i2.GMap); - @override - set googleMap(_i2.GMap? _googleMap) => - super.noSuchMethod(Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null); - @override - int get mapId => (super.noSuchMethod(Invocation.getter(#mapId), - returnValue: 0, returnValueForMissingStub: 0) as int); - @override - set mapId(int? _mapId) => - super.noSuchMethod(Invocation.setter(#mapId, _mapId), - returnValueForMissingStub: null); - @override - void addPolylines(Set<_i4.Polyline>? polylinesToAdd) => - super.noSuchMethod(Invocation.method(#addPolylines, [polylinesToAdd]), - returnValueForMissingStub: null); - @override - void changePolylines(Set<_i4.Polyline>? polylinesToChange) => super - .noSuchMethod(Invocation.method(#changePolylines, [polylinesToChange]), - returnValueForMissingStub: null); - @override - void removePolylines(Set<_i4.PolylineId>? polylineIdsToRemove) => super - .noSuchMethod(Invocation.method(#removePolylines, [polylineIdsToRemove]), - returnValueForMissingStub: null); - @override - void bindToMap(int? mapId, _i2.GMap? googleMap) => - super.noSuchMethod(Invocation.method(#bindToMap, [mapId, googleMap]), - returnValueForMissingStub: null); + Invocation.getter(#lines), + returnValue: <_i4.PolylineId, _i3.PolylineController>{}, + returnValueForMissingStub: <_i4.PolylineId, _i3.PolylineController>{}, + ) as Map<_i4.PolylineId, _i3.PolylineController>); + @override + _i2.GMap get googleMap => (super.noSuchMethod( + Invocation.getter(#googleMap), + returnValue: _FakeGMap_0( + this, + Invocation.getter(#googleMap), + ), + returnValueForMissingStub: _FakeGMap_0( + this, + Invocation.getter(#googleMap), + ), + ) as _i2.GMap); + @override + set googleMap(_i2.GMap? _googleMap) => super.noSuchMethod( + Invocation.setter( + #googleMap, + _googleMap, + ), + returnValueForMissingStub: null, + ); + @override + int get mapId => (super.noSuchMethod( + Invocation.getter(#mapId), + returnValue: 0, + returnValueForMissingStub: 0, + ) as int); + @override + set mapId(int? _mapId) => super.noSuchMethod( + Invocation.setter( + #mapId, + _mapId, + ), + returnValueForMissingStub: null, + ); + @override + void addPolylines(Set<_i4.Polyline>? polylinesToAdd) => super.noSuchMethod( + Invocation.method( + #addPolylines, + [polylinesToAdd], + ), + returnValueForMissingStub: null, + ); + @override + void changePolylines(Set<_i4.Polyline>? polylinesToChange) => + super.noSuchMethod( + Invocation.method( + #changePolylines, + [polylinesToChange], + ), + returnValueForMissingStub: null, + ); + @override + void removePolylines(Set<_i4.PolylineId>? polylineIdsToRemove) => + super.noSuchMethod( + Invocation.method( + #removePolylines, + [polylineIdsToRemove], + ), + returnValueForMissingStub: null, + ); + @override + void bindToMap( + int? mapId, + _i2.GMap? googleMap, + ) => + super.noSuchMethod( + Invocation.method( + #bindToMap, + [ + mapId, + googleMap, + ], + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [MarkersController]. @@ -164,53 +296,108 @@ class MockPolylinesController extends _i1.Mock /// See the documentation for Mockito's code generation for more information. class MockMarkersController extends _i1.Mock implements _i3.MarkersController { @override - Map<_i4.MarkerId, _i3.MarkerController> get markers => - (super.noSuchMethod(Invocation.getter(#markers), - returnValue: <_i4.MarkerId, _i3.MarkerController>{}, - returnValueForMissingStub: <_i4.MarkerId, _i3.MarkerController>{}) - as Map<_i4.MarkerId, _i3.MarkerController>); - @override - _i2.GMap get googleMap => (super.noSuchMethod(Invocation.getter(#googleMap), - returnValue: _FakeGMap_0(this, Invocation.getter(#googleMap)), - returnValueForMissingStub: - _FakeGMap_0(this, Invocation.getter(#googleMap))) as _i2.GMap); - @override - set googleMap(_i2.GMap? _googleMap) => - super.noSuchMethod(Invocation.setter(#googleMap, _googleMap), - returnValueForMissingStub: null); - @override - int get mapId => (super.noSuchMethod(Invocation.getter(#mapId), - returnValue: 0, returnValueForMissingStub: 0) as int); - @override - set mapId(int? _mapId) => - super.noSuchMethod(Invocation.setter(#mapId, _mapId), - returnValueForMissingStub: null); - @override - void addMarkers(Set<_i4.Marker>? markersToAdd) => - super.noSuchMethod(Invocation.method(#addMarkers, [markersToAdd]), - returnValueForMissingStub: null); - @override - void changeMarkers(Set<_i4.Marker>? markersToChange) => - super.noSuchMethod(Invocation.method(#changeMarkers, [markersToChange]), - returnValueForMissingStub: null); + Map<_i4.MarkerId, _i3.MarkerController> get markers => (super.noSuchMethod( + Invocation.getter(#markers), + returnValue: <_i4.MarkerId, _i3.MarkerController>{}, + returnValueForMissingStub: <_i4.MarkerId, _i3.MarkerController>{}, + ) as Map<_i4.MarkerId, _i3.MarkerController>); + @override + _i2.GMap get googleMap => (super.noSuchMethod( + Invocation.getter(#googleMap), + returnValue: _FakeGMap_0( + this, + Invocation.getter(#googleMap), + ), + returnValueForMissingStub: _FakeGMap_0( + this, + Invocation.getter(#googleMap), + ), + ) as _i2.GMap); + @override + set googleMap(_i2.GMap? _googleMap) => super.noSuchMethod( + Invocation.setter( + #googleMap, + _googleMap, + ), + returnValueForMissingStub: null, + ); + @override + int get mapId => (super.noSuchMethod( + Invocation.getter(#mapId), + returnValue: 0, + returnValueForMissingStub: 0, + ) as int); + @override + set mapId(int? _mapId) => super.noSuchMethod( + Invocation.setter( + #mapId, + _mapId, + ), + returnValueForMissingStub: null, + ); + @override + void addMarkers(Set<_i4.Marker>? markersToAdd) => super.noSuchMethod( + Invocation.method( + #addMarkers, + [markersToAdd], + ), + returnValueForMissingStub: null, + ); + @override + void changeMarkers(Set<_i4.Marker>? markersToChange) => super.noSuchMethod( + Invocation.method( + #changeMarkers, + [markersToChange], + ), + returnValueForMissingStub: null, + ); @override void removeMarkers(Set<_i4.MarkerId>? markerIdsToRemove) => - super.noSuchMethod(Invocation.method(#removeMarkers, [markerIdsToRemove]), - returnValueForMissingStub: null); - @override - void showMarkerInfoWindow(_i4.MarkerId? markerId) => - super.noSuchMethod(Invocation.method(#showMarkerInfoWindow, [markerId]), - returnValueForMissingStub: null); - @override - void hideMarkerInfoWindow(_i4.MarkerId? markerId) => - super.noSuchMethod(Invocation.method(#hideMarkerInfoWindow, [markerId]), - returnValueForMissingStub: null); - @override - bool isInfoWindowShown(_i4.MarkerId? markerId) => - (super.noSuchMethod(Invocation.method(#isInfoWindowShown, [markerId]), - returnValue: false, returnValueForMissingStub: false) as bool); - @override - void bindToMap(int? mapId, _i2.GMap? googleMap) => - super.noSuchMethod(Invocation.method(#bindToMap, [mapId, googleMap]), - returnValueForMissingStub: null); + super.noSuchMethod( + Invocation.method( + #removeMarkers, + [markerIdsToRemove], + ), + returnValueForMissingStub: null, + ); + @override + void showMarkerInfoWindow(_i4.MarkerId? markerId) => super.noSuchMethod( + Invocation.method( + #showMarkerInfoWindow, + [markerId], + ), + returnValueForMissingStub: null, + ); + @override + void hideMarkerInfoWindow(_i4.MarkerId? markerId) => super.noSuchMethod( + Invocation.method( + #hideMarkerInfoWindow, + [markerId], + ), + returnValueForMissingStub: null, + ); + @override + bool isInfoWindowShown(_i4.MarkerId? markerId) => (super.noSuchMethod( + Invocation.method( + #isInfoWindowShown, + [markerId], + ), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + @override + void bindToMap( + int? mapId, + _i2.GMap? googleMap, + ) => + super.noSuchMethod( + Invocation.method( + #bindToMap, + [ + mapId, + googleMap, + ], + ), + returnValueForMissingStub: null, + ); } 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 04cbc4a3416d..a85bce31e20f 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 @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.3.0 from annotations +// Mocks generated by Mockito 5.3.2 from annotations // in google_maps_flutter_web_integration_tests/integration_test/google_maps_plugin_test.dart. // Do not manually edit this file. @@ -24,24 +24,44 @@ import 'package:mockito/mockito.dart' as _i1; class _FakeStreamController_0 extends _i1.SmartFake implements _i2.StreamController { - _FakeStreamController_0(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + _FakeStreamController_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); } class _FakeLatLngBounds_1 extends _i1.SmartFake implements _i3.LatLngBounds { - _FakeLatLngBounds_1(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + _FakeLatLngBounds_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); } class _FakeScreenCoordinate_2 extends _i1.SmartFake implements _i3.ScreenCoordinate { - _FakeScreenCoordinate_2(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + _FakeScreenCoordinate_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); } class _FakeLatLng_3 extends _i1.SmartFake implements _i3.LatLng { - _FakeLatLng_3(Object parent, Invocation parentInvocation) - : super(parent, parentInvocation); + _FakeLatLng_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); } /// A class which mocks [GoogleMapController]. @@ -51,117 +71,226 @@ class MockGoogleMapController extends _i1.Mock implements _i4.GoogleMapController { @override _i2.StreamController<_i3.MapEvent> get stream => (super.noSuchMethod( - Invocation.getter(#stream), - returnValue: _FakeStreamController_0<_i3.MapEvent>( - this, Invocation.getter(#stream)), - returnValueForMissingStub: _FakeStreamController_0<_i3.MapEvent>( - this, Invocation.getter(#stream))) as _i2 - .StreamController<_i3.MapEvent>); + Invocation.getter(#stream), + returnValue: _FakeStreamController_0<_i3.MapEvent>( + this, + Invocation.getter(#stream), + ), + returnValueForMissingStub: + _FakeStreamController_0<_i3.MapEvent>( + this, + Invocation.getter(#stream), + ), + ) as _i2.StreamController<_i3.MapEvent>); @override _i2.Stream<_i3.MapEvent> get events => (super.noSuchMethod( - Invocation.getter(#events), - returnValue: _i2.Stream<_i3.MapEvent>.empty(), - returnValueForMissingStub: _i2.Stream<_i3.MapEvent>.empty()) - as _i2.Stream<_i3.MapEvent>); - @override - bool get isInitialized => - (super.noSuchMethod(Invocation.getter(#isInitialized), - returnValue: false, returnValueForMissingStub: false) as bool); - @override - void debugSetOverrides( - {_i4.DebugCreateMapFunction? createMap, - _i4.MarkersController? markers, - _i4.CirclesController? circles, - _i4.PolygonsController? polygons, - _i4.PolylinesController? polylines}) => + Invocation.getter(#events), + returnValue: _i2.Stream<_i3.MapEvent>.empty(), + returnValueForMissingStub: _i2.Stream<_i3.MapEvent>.empty(), + ) as _i2.Stream<_i3.MapEvent>); + @override + bool get isInitialized => (super.noSuchMethod( + Invocation.getter(#isInitialized), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + @override + void debugSetOverrides({ + _i4.DebugCreateMapFunction? createMap, + _i4.MarkersController? markers, + _i4.CirclesController? circles, + _i4.PolygonsController? polygons, + _i4.PolylinesController? polylines, + }) => super.noSuchMethod( - Invocation.method(#debugSetOverrides, [], { + Invocation.method( + #debugSetOverrides, + [], + { #createMap: createMap, #markers: markers, #circles: circles, #polygons: polygons, - #polylines: polylines - }), - returnValueForMissingStub: null); + #polylines: polylines, + }, + ), + returnValueForMissingStub: null, + ); @override - void init() => super.noSuchMethod(Invocation.method(#init, []), - returnValueForMissingStub: null); + void init() => super.noSuchMethod( + Invocation.method( + #init, + [], + ), + returnValueForMissingStub: null, + ); @override void updateMapConfiguration(_i3.MapConfiguration? update) => - super.noSuchMethod(Invocation.method(#updateMapConfiguration, [update]), - returnValueForMissingStub: null); + super.noSuchMethod( + Invocation.method( + #updateMapConfiguration, + [update], + ), + returnValueForMissingStub: null, + ); @override - void updateStyles(List<_i5.MapTypeStyle>? styles) => - super.noSuchMethod(Invocation.method(#updateStyles, [styles]), - returnValueForMissingStub: null); + void updateStyles(List<_i5.MapTypeStyle>? styles) => super.noSuchMethod( + Invocation.method( + #updateStyles, + [styles], + ), + returnValueForMissingStub: null, + ); @override _i2.Future<_i3.LatLngBounds> getVisibleRegion() => (super.noSuchMethod( - Invocation.method(#getVisibleRegion, []), - returnValue: _i2.Future<_i3.LatLngBounds>.value( - _FakeLatLngBounds_1(this, Invocation.method(#getVisibleRegion, []))), - returnValueForMissingStub: _i2.Future<_i3.LatLngBounds>.value( - _FakeLatLngBounds_1( - this, Invocation.method(#getVisibleRegion, [])))) as _i2 - .Future<_i3.LatLngBounds>); + Invocation.method( + #getVisibleRegion, + [], + ), + returnValue: _i2.Future<_i3.LatLngBounds>.value(_FakeLatLngBounds_1( + this, + Invocation.method( + #getVisibleRegion, + [], + ), + )), + returnValueForMissingStub: + _i2.Future<_i3.LatLngBounds>.value(_FakeLatLngBounds_1( + this, + Invocation.method( + #getVisibleRegion, + [], + ), + )), + ) as _i2.Future<_i3.LatLngBounds>); @override _i2.Future<_i3.ScreenCoordinate> getScreenCoordinate(_i3.LatLng? latLng) => - (super.noSuchMethod(Invocation.method(#getScreenCoordinate, [latLng]), - returnValue: _i2.Future<_i3.ScreenCoordinate>.value( - _FakeScreenCoordinate_2( - this, Invocation.method(#getScreenCoordinate, [latLng]))), - returnValueForMissingStub: _i2.Future<_i3.ScreenCoordinate>.value( - _FakeScreenCoordinate_2( - this, Invocation.method(#getScreenCoordinate, [latLng])))) - as _i2.Future<_i3.ScreenCoordinate>); + (super.noSuchMethod( + Invocation.method( + #getScreenCoordinate, + [latLng], + ), + returnValue: + _i2.Future<_i3.ScreenCoordinate>.value(_FakeScreenCoordinate_2( + this, + Invocation.method( + #getScreenCoordinate, + [latLng], + ), + )), + returnValueForMissingStub: + _i2.Future<_i3.ScreenCoordinate>.value(_FakeScreenCoordinate_2( + this, + Invocation.method( + #getScreenCoordinate, + [latLng], + ), + )), + ) as _i2.Future<_i3.ScreenCoordinate>); @override _i2.Future<_i3.LatLng> getLatLng(_i3.ScreenCoordinate? screenCoordinate) => - (super.noSuchMethod(Invocation.method(#getLatLng, [screenCoordinate]), - returnValue: _i2.Future<_i3.LatLng>.value(_FakeLatLng_3( - this, Invocation.method(#getLatLng, [screenCoordinate]))), - returnValueForMissingStub: _i2.Future<_i3.LatLng>.value(_FakeLatLng_3( - this, Invocation.method(#getLatLng, [screenCoordinate])))) as _i2 - .Future<_i3.LatLng>); + (super.noSuchMethod( + Invocation.method( + #getLatLng, + [screenCoordinate], + ), + returnValue: _i2.Future<_i3.LatLng>.value(_FakeLatLng_3( + this, + Invocation.method( + #getLatLng, + [screenCoordinate], + ), + )), + returnValueForMissingStub: _i2.Future<_i3.LatLng>.value(_FakeLatLng_3( + this, + Invocation.method( + #getLatLng, + [screenCoordinate], + ), + )), + ) as _i2.Future<_i3.LatLng>); @override _i2.Future moveCamera(_i3.CameraUpdate? cameraUpdate) => - (super.noSuchMethod(Invocation.method(#moveCamera, [cameraUpdate]), - returnValue: _i2.Future.value(), - returnValueForMissingStub: _i2.Future.value()) - as _i2.Future); - @override - _i2.Future getZoomLevel() => - (super.noSuchMethod(Invocation.method(#getZoomLevel, []), - returnValue: _i2.Future.value(0.0), - returnValueForMissingStub: _i2.Future.value(0.0)) - as _i2.Future); - @override - void updateCircles(_i3.CircleUpdates? updates) => - super.noSuchMethod(Invocation.method(#updateCircles, [updates]), - returnValueForMissingStub: null); - @override - void updatePolygons(_i3.PolygonUpdates? updates) => - super.noSuchMethod(Invocation.method(#updatePolygons, [updates]), - returnValueForMissingStub: null); - @override - void updatePolylines(_i3.PolylineUpdates? updates) => - super.noSuchMethod(Invocation.method(#updatePolylines, [updates]), - returnValueForMissingStub: null); - @override - void updateMarkers(_i3.MarkerUpdates? updates) => - super.noSuchMethod(Invocation.method(#updateMarkers, [updates]), - returnValueForMissingStub: null); - @override - void showInfoWindow(_i3.MarkerId? markerId) => - super.noSuchMethod(Invocation.method(#showInfoWindow, [markerId]), - returnValueForMissingStub: null); - @override - void hideInfoWindow(_i3.MarkerId? markerId) => - super.noSuchMethod(Invocation.method(#hideInfoWindow, [markerId]), - returnValueForMissingStub: null); - @override - bool isInfoWindowShown(_i3.MarkerId? markerId) => - (super.noSuchMethod(Invocation.method(#isInfoWindowShown, [markerId]), - returnValue: false, returnValueForMissingStub: false) as bool); - @override - void dispose() => super.noSuchMethod(Invocation.method(#dispose, []), - returnValueForMissingStub: null); + (super.noSuchMethod( + Invocation.method( + #moveCamera, + [cameraUpdate], + ), + returnValue: _i2.Future.value(), + returnValueForMissingStub: _i2.Future.value(), + ) as _i2.Future); + @override + _i2.Future getZoomLevel() => (super.noSuchMethod( + Invocation.method( + #getZoomLevel, + [], + ), + returnValue: _i2.Future.value(0.0), + returnValueForMissingStub: _i2.Future.value(0.0), + ) as _i2.Future); + @override + void updateCircles(_i3.CircleUpdates? updates) => super.noSuchMethod( + Invocation.method( + #updateCircles, + [updates], + ), + returnValueForMissingStub: null, + ); + @override + void updatePolygons(_i3.PolygonUpdates? updates) => super.noSuchMethod( + Invocation.method( + #updatePolygons, + [updates], + ), + returnValueForMissingStub: null, + ); + @override + void updatePolylines(_i3.PolylineUpdates? updates) => super.noSuchMethod( + Invocation.method( + #updatePolylines, + [updates], + ), + returnValueForMissingStub: null, + ); + @override + void updateMarkers(_i3.MarkerUpdates? updates) => super.noSuchMethod( + Invocation.method( + #updateMarkers, + [updates], + ), + returnValueForMissingStub: null, + ); + @override + void showInfoWindow(_i3.MarkerId? markerId) => super.noSuchMethod( + Invocation.method( + #showInfoWindow, + [markerId], + ), + returnValueForMissingStub: null, + ); + @override + void hideInfoWindow(_i3.MarkerId? markerId) => super.noSuchMethod( + Invocation.method( + #hideInfoWindow, + [markerId], + ), + returnValueForMissingStub: null, + ); + @override + bool isInfoWindowShown(_i3.MarkerId? markerId) => (super.noSuchMethod( + Invocation.method( + #isInfoWindowShown, + [markerId], + ), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); } 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 7e25b6e3b483..35c9c903e982 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 @@ -25,4 +25,4 @@ dev_dependencies: http: ^0.13.0 integration_test: sdk: flutter - mockito: ^5.3.0 + mockito: ^5.3.2 diff --git a/packages/url_launcher/url_launcher_web/example/integration_test/url_launcher_web_test.mocks.dart b/packages/url_launcher/url_launcher_web/example/integration_test/url_launcher_web_test.mocks.dart index 36903b0a4250..0717dc7ff478 100644 --- a/packages/url_launcher/url_launcher_web/example/integration_test/url_launcher_web_test.mocks.dart +++ b/packages/url_launcher/url_launcher_web/example/integration_test/url_launcher_web_test.mocks.dart @@ -1,13 +1,15 @@ -// Mocks generated by Mockito 5.0.17 from annotations +// Mocks generated by Mockito 5.3.2 from annotations // in regular_integration_tests/integration_test/url_launcher_web_test.dart. // Do not manually edit this file. +// ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i3; import 'dart:html' as _i2; import 'dart:math' as _i4; import 'package:mockito/mockito.dart' as _i1; +// ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references @@ -16,40 +18,170 @@ import 'package:mockito/mockito.dart' as _i1; // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class -class _FakeDocument_0 extends _i1.Fake implements _i2.Document {} +class _FakeDocument_0 extends _i1.SmartFake implements _i2.Document { + _FakeDocument_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeLocation_1 extends _i1.Fake implements _i2.Location {} +class _FakeLocation_1 extends _i1.SmartFake implements _i2.Location { + _FakeLocation_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeConsole_2 extends _i1.Fake implements _i2.Console {} +class _FakeConsole_2 extends _i1.SmartFake implements _i2.Console { + _FakeConsole_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeHistory_3 extends _i1.Fake implements _i2.History {} +class _FakeHistory_3 extends _i1.SmartFake implements _i2.History { + _FakeHistory_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeStorage_4 extends _i1.Fake implements _i2.Storage {} +class _FakeStorage_4 extends _i1.SmartFake implements _i2.Storage { + _FakeStorage_4( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeNavigator_5 extends _i1.Fake implements _i2.Navigator {} +class _FakeNavigator_5 extends _i1.SmartFake implements _i2.Navigator { + _FakeNavigator_5( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakePerformance_6 extends _i1.Fake implements _i2.Performance {} +class _FakePerformance_6 extends _i1.SmartFake implements _i2.Performance { + _FakePerformance_6( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeEvents_7 extends _i1.Fake implements _i2.Events {} +class _FakeEvents_7 extends _i1.SmartFake implements _i2.Events { + _FakeEvents_7( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeWindowBase_8 extends _i1.Fake implements _i2.WindowBase {} +class _FakeWindowBase_8 extends _i1.SmartFake implements _i2.WindowBase { + _FakeWindowBase_8( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeFileSystem_9 extends _i1.Fake implements _i2.FileSystem {} +class _FakeFileSystem_9 extends _i1.SmartFake implements _i2.FileSystem { + _FakeFileSystem_9( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeStylePropertyMapReadonly_10 extends _i1.Fake - implements _i2.StylePropertyMapReadonly {} +class _FakeStylePropertyMapReadonly_10 extends _i1.SmartFake + implements _i2.StylePropertyMapReadonly { + _FakeStylePropertyMapReadonly_10( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeMediaQueryList_11 extends _i1.Fake implements _i2.MediaQueryList {} +class _FakeMediaQueryList_11 extends _i1.SmartFake + implements _i2.MediaQueryList { + _FakeMediaQueryList_11( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeEntry_12 extends _i1.Fake implements _i2.Entry {} +class _FakeEntry_12 extends _i1.SmartFake implements _i2.Entry { + _FakeEntry_12( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeGeolocation_13 extends _i1.Fake implements _i2.Geolocation {} +class _FakeGeolocation_13 extends _i1.SmartFake implements _i2.Geolocation { + _FakeGeolocation_13( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeMediaStream_14 extends _i1.Fake implements _i2.MediaStream {} +class _FakeMediaStream_14 extends _i1.SmartFake implements _i2.MediaStream { + _FakeMediaStream_14( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeRelatedApplication_15 extends _i1.Fake - implements _i2.RelatedApplication {} +class _FakeRelatedApplication_15 extends _i1.SmartFake + implements _i2.RelatedApplication { + _FakeRelatedApplication_15( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} /// A class which mocks [Window]. /// @@ -60,589 +192,966 @@ class MockWindow extends _i1.Mock implements _i2.Window { } @override - _i3.Future get animationFrame => - (super.noSuchMethod(Invocation.getter(#animationFrame), - returnValue: Future.value(0)) as _i3.Future); - @override - _i2.Document get document => (super.noSuchMethod(Invocation.getter(#document), - returnValue: _FakeDocument_0()) as _i2.Document); - @override - _i2.Location get location => (super.noSuchMethod(Invocation.getter(#location), - returnValue: _FakeLocation_1()) as _i2.Location); - @override - set location(_i2.LocationBase? value) => - super.noSuchMethod(Invocation.setter(#location, value), - returnValueForMissingStub: null); - @override - _i2.Console get console => (super.noSuchMethod(Invocation.getter(#console), - returnValue: _FakeConsole_2()) as _i2.Console); - @override - set defaultStatus(String? value) => - super.noSuchMethod(Invocation.setter(#defaultStatus, value), - returnValueForMissingStub: null); - @override - set defaultstatus(String? value) => - super.noSuchMethod(Invocation.setter(#defaultstatus, value), - returnValueForMissingStub: null); - @override - num get devicePixelRatio => - (super.noSuchMethod(Invocation.getter(#devicePixelRatio), returnValue: 0) - as num); - @override - _i2.History get history => (super.noSuchMethod(Invocation.getter(#history), - returnValue: _FakeHistory_3()) as _i2.History); - @override - _i2.Storage get localStorage => - (super.noSuchMethod(Invocation.getter(#localStorage), - returnValue: _FakeStorage_4()) as _i2.Storage); - @override - set name(String? value) => super.noSuchMethod(Invocation.setter(#name, value), - returnValueForMissingStub: null); - @override - _i2.Navigator get navigator => - (super.noSuchMethod(Invocation.getter(#navigator), - returnValue: _FakeNavigator_5()) as _i2.Navigator); - @override - set opener(_i2.WindowBase? value) => - super.noSuchMethod(Invocation.setter(#opener, value), - returnValueForMissingStub: null); - @override - int get outerHeight => - (super.noSuchMethod(Invocation.getter(#outerHeight), returnValue: 0) - as int); - @override - int get outerWidth => - (super.noSuchMethod(Invocation.getter(#outerWidth), returnValue: 0) - as int); - @override - _i2.Performance get performance => - (super.noSuchMethod(Invocation.getter(#performance), - returnValue: _FakePerformance_6()) as _i2.Performance); - @override - _i2.Storage get sessionStorage => - (super.noSuchMethod(Invocation.getter(#sessionStorage), - returnValue: _FakeStorage_4()) as _i2.Storage); - @override - set status(String? value) => - super.noSuchMethod(Invocation.setter(#status, value), - returnValueForMissingStub: null); - @override - _i3.Stream<_i2.Event> get onContentLoaded => - (super.noSuchMethod(Invocation.getter(#onContentLoaded), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onAbort => - (super.noSuchMethod(Invocation.getter(#onAbort), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onBlur => - (super.noSuchMethod(Invocation.getter(#onBlur), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onCanPlay => - (super.noSuchMethod(Invocation.getter(#onCanPlay), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onCanPlayThrough => - (super.noSuchMethod(Invocation.getter(#onCanPlayThrough), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onChange => - (super.noSuchMethod(Invocation.getter(#onChange), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.MouseEvent> get onClick => - (super.noSuchMethod(Invocation.getter(#onClick), - returnValue: Stream<_i2.MouseEvent>.empty()) - as _i3.Stream<_i2.MouseEvent>); - @override - _i3.Stream<_i2.MouseEvent> get onContextMenu => - (super.noSuchMethod(Invocation.getter(#onContextMenu), - returnValue: Stream<_i2.MouseEvent>.empty()) - as _i3.Stream<_i2.MouseEvent>); - @override - _i3.Stream<_i2.Event> get onDoubleClick => - (super.noSuchMethod(Invocation.getter(#onDoubleClick), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.DeviceMotionEvent> get onDeviceMotion => - (super.noSuchMethod(Invocation.getter(#onDeviceMotion), - returnValue: Stream<_i2.DeviceMotionEvent>.empty()) - as _i3.Stream<_i2.DeviceMotionEvent>); + _i3.Future get animationFrame => (super.noSuchMethod( + Invocation.getter(#animationFrame), + returnValue: _i3.Future.value(0), + ) as _i3.Future); + @override + _i2.Document get document => (super.noSuchMethod( + Invocation.getter(#document), + returnValue: _FakeDocument_0( + this, + Invocation.getter(#document), + ), + ) as _i2.Document); + @override + _i2.Location get location => (super.noSuchMethod( + Invocation.getter(#location), + returnValue: _FakeLocation_1( + this, + Invocation.getter(#location), + ), + ) as _i2.Location); + @override + set location(_i2.LocationBase? value) => super.noSuchMethod( + Invocation.setter( + #location, + value, + ), + returnValueForMissingStub: null, + ); + @override + _i2.Console get console => (super.noSuchMethod( + Invocation.getter(#console), + returnValue: _FakeConsole_2( + this, + Invocation.getter(#console), + ), + ) as _i2.Console); + @override + set defaultStatus(String? value) => super.noSuchMethod( + Invocation.setter( + #defaultStatus, + value, + ), + returnValueForMissingStub: null, + ); + @override + set defaultstatus(String? value) => super.noSuchMethod( + Invocation.setter( + #defaultstatus, + value, + ), + returnValueForMissingStub: null, + ); + @override + num get devicePixelRatio => (super.noSuchMethod( + Invocation.getter(#devicePixelRatio), + returnValue: 0, + ) as num); + @override + _i2.History get history => (super.noSuchMethod( + Invocation.getter(#history), + returnValue: _FakeHistory_3( + this, + Invocation.getter(#history), + ), + ) as _i2.History); + @override + _i2.Storage get localStorage => (super.noSuchMethod( + Invocation.getter(#localStorage), + returnValue: _FakeStorage_4( + this, + Invocation.getter(#localStorage), + ), + ) as _i2.Storage); + @override + set name(String? value) => super.noSuchMethod( + Invocation.setter( + #name, + value, + ), + returnValueForMissingStub: null, + ); + @override + _i2.Navigator get navigator => (super.noSuchMethod( + Invocation.getter(#navigator), + returnValue: _FakeNavigator_5( + this, + Invocation.getter(#navigator), + ), + ) as _i2.Navigator); + @override + set opener(_i2.WindowBase? value) => super.noSuchMethod( + Invocation.setter( + #opener, + value, + ), + returnValueForMissingStub: null, + ); + @override + int get outerHeight => (super.noSuchMethod( + Invocation.getter(#outerHeight), + returnValue: 0, + ) as int); + @override + int get outerWidth => (super.noSuchMethod( + Invocation.getter(#outerWidth), + returnValue: 0, + ) as int); + @override + _i2.Performance get performance => (super.noSuchMethod( + Invocation.getter(#performance), + returnValue: _FakePerformance_6( + this, + Invocation.getter(#performance), + ), + ) as _i2.Performance); + @override + _i2.Storage get sessionStorage => (super.noSuchMethod( + Invocation.getter(#sessionStorage), + returnValue: _FakeStorage_4( + this, + Invocation.getter(#sessionStorage), + ), + ) as _i2.Storage); + @override + set status(String? value) => super.noSuchMethod( + Invocation.setter( + #status, + value, + ), + returnValueForMissingStub: null, + ); + @override + _i3.Stream<_i2.Event> get onContentLoaded => (super.noSuchMethod( + Invocation.getter(#onContentLoaded), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onAbort => (super.noSuchMethod( + Invocation.getter(#onAbort), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onBlur => (super.noSuchMethod( + Invocation.getter(#onBlur), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onCanPlay => (super.noSuchMethod( + Invocation.getter(#onCanPlay), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onCanPlayThrough => (super.noSuchMethod( + Invocation.getter(#onCanPlayThrough), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onChange => (super.noSuchMethod( + Invocation.getter(#onChange), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.MouseEvent> get onClick => (super.noSuchMethod( + Invocation.getter(#onClick), + returnValue: _i3.Stream<_i2.MouseEvent>.empty(), + ) as _i3.Stream<_i2.MouseEvent>); + @override + _i3.Stream<_i2.MouseEvent> get onContextMenu => (super.noSuchMethod( + Invocation.getter(#onContextMenu), + returnValue: _i3.Stream<_i2.MouseEvent>.empty(), + ) as _i3.Stream<_i2.MouseEvent>); + @override + _i3.Stream<_i2.Event> get onDoubleClick => (super.noSuchMethod( + Invocation.getter(#onDoubleClick), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.DeviceMotionEvent> get onDeviceMotion => (super.noSuchMethod( + Invocation.getter(#onDeviceMotion), + returnValue: _i3.Stream<_i2.DeviceMotionEvent>.empty(), + ) as _i3.Stream<_i2.DeviceMotionEvent>); @override _i3.Stream<_i2.DeviceOrientationEvent> get onDeviceOrientation => - (super.noSuchMethod(Invocation.getter(#onDeviceOrientation), - returnValue: Stream<_i2.DeviceOrientationEvent>.empty()) - as _i3.Stream<_i2.DeviceOrientationEvent>); - @override - _i3.Stream<_i2.MouseEvent> get onDrag => - (super.noSuchMethod(Invocation.getter(#onDrag), - returnValue: Stream<_i2.MouseEvent>.empty()) - as _i3.Stream<_i2.MouseEvent>); - @override - _i3.Stream<_i2.MouseEvent> get onDragEnd => - (super.noSuchMethod(Invocation.getter(#onDragEnd), - returnValue: Stream<_i2.MouseEvent>.empty()) - as _i3.Stream<_i2.MouseEvent>); - @override - _i3.Stream<_i2.MouseEvent> get onDragEnter => - (super.noSuchMethod(Invocation.getter(#onDragEnter), - returnValue: Stream<_i2.MouseEvent>.empty()) - as _i3.Stream<_i2.MouseEvent>); - @override - _i3.Stream<_i2.MouseEvent> get onDragLeave => - (super.noSuchMethod(Invocation.getter(#onDragLeave), - returnValue: Stream<_i2.MouseEvent>.empty()) - as _i3.Stream<_i2.MouseEvent>); - @override - _i3.Stream<_i2.MouseEvent> get onDragOver => - (super.noSuchMethod(Invocation.getter(#onDragOver), - returnValue: Stream<_i2.MouseEvent>.empty()) - as _i3.Stream<_i2.MouseEvent>); - @override - _i3.Stream<_i2.MouseEvent> get onDragStart => - (super.noSuchMethod(Invocation.getter(#onDragStart), - returnValue: Stream<_i2.MouseEvent>.empty()) - as _i3.Stream<_i2.MouseEvent>); - @override - _i3.Stream<_i2.MouseEvent> get onDrop => - (super.noSuchMethod(Invocation.getter(#onDrop), - returnValue: Stream<_i2.MouseEvent>.empty()) - as _i3.Stream<_i2.MouseEvent>); - @override - _i3.Stream<_i2.Event> get onDurationChange => - (super.noSuchMethod(Invocation.getter(#onDurationChange), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onEmptied => - (super.noSuchMethod(Invocation.getter(#onEmptied), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onEnded => - (super.noSuchMethod(Invocation.getter(#onEnded), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onError => - (super.noSuchMethod(Invocation.getter(#onError), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onFocus => - (super.noSuchMethod(Invocation.getter(#onFocus), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onHashChange => - (super.noSuchMethod(Invocation.getter(#onHashChange), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onInput => - (super.noSuchMethod(Invocation.getter(#onInput), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onInvalid => - (super.noSuchMethod(Invocation.getter(#onInvalid), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.KeyboardEvent> get onKeyDown => - (super.noSuchMethod(Invocation.getter(#onKeyDown), - returnValue: Stream<_i2.KeyboardEvent>.empty()) - as _i3.Stream<_i2.KeyboardEvent>); - @override - _i3.Stream<_i2.KeyboardEvent> get onKeyPress => - (super.noSuchMethod(Invocation.getter(#onKeyPress), - returnValue: Stream<_i2.KeyboardEvent>.empty()) - as _i3.Stream<_i2.KeyboardEvent>); - @override - _i3.Stream<_i2.KeyboardEvent> get onKeyUp => - (super.noSuchMethod(Invocation.getter(#onKeyUp), - returnValue: Stream<_i2.KeyboardEvent>.empty()) - as _i3.Stream<_i2.KeyboardEvent>); - @override - _i3.Stream<_i2.Event> get onLoad => - (super.noSuchMethod(Invocation.getter(#onLoad), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onLoadedData => - (super.noSuchMethod(Invocation.getter(#onLoadedData), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onLoadedMetadata => - (super.noSuchMethod(Invocation.getter(#onLoadedMetadata), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onLoadStart => - (super.noSuchMethod(Invocation.getter(#onLoadStart), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.MessageEvent> get onMessage => - (super.noSuchMethod(Invocation.getter(#onMessage), - returnValue: Stream<_i2.MessageEvent>.empty()) - as _i3.Stream<_i2.MessageEvent>); - @override - _i3.Stream<_i2.MouseEvent> get onMouseDown => - (super.noSuchMethod(Invocation.getter(#onMouseDown), - returnValue: Stream<_i2.MouseEvent>.empty()) - as _i3.Stream<_i2.MouseEvent>); - @override - _i3.Stream<_i2.MouseEvent> get onMouseEnter => - (super.noSuchMethod(Invocation.getter(#onMouseEnter), - returnValue: Stream<_i2.MouseEvent>.empty()) - as _i3.Stream<_i2.MouseEvent>); - @override - _i3.Stream<_i2.MouseEvent> get onMouseLeave => - (super.noSuchMethod(Invocation.getter(#onMouseLeave), - returnValue: Stream<_i2.MouseEvent>.empty()) - as _i3.Stream<_i2.MouseEvent>); - @override - _i3.Stream<_i2.MouseEvent> get onMouseMove => - (super.noSuchMethod(Invocation.getter(#onMouseMove), - returnValue: Stream<_i2.MouseEvent>.empty()) - as _i3.Stream<_i2.MouseEvent>); - @override - _i3.Stream<_i2.MouseEvent> get onMouseOut => - (super.noSuchMethod(Invocation.getter(#onMouseOut), - returnValue: Stream<_i2.MouseEvent>.empty()) - as _i3.Stream<_i2.MouseEvent>); - @override - _i3.Stream<_i2.MouseEvent> get onMouseOver => - (super.noSuchMethod(Invocation.getter(#onMouseOver), - returnValue: Stream<_i2.MouseEvent>.empty()) - as _i3.Stream<_i2.MouseEvent>); - @override - _i3.Stream<_i2.MouseEvent> get onMouseUp => - (super.noSuchMethod(Invocation.getter(#onMouseUp), - returnValue: Stream<_i2.MouseEvent>.empty()) - as _i3.Stream<_i2.MouseEvent>); - @override - _i3.Stream<_i2.WheelEvent> get onMouseWheel => - (super.noSuchMethod(Invocation.getter(#onMouseWheel), - returnValue: Stream<_i2.WheelEvent>.empty()) - as _i3.Stream<_i2.WheelEvent>); - @override - _i3.Stream<_i2.Event> get onOffline => - (super.noSuchMethod(Invocation.getter(#onOffline), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onOnline => - (super.noSuchMethod(Invocation.getter(#onOnline), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onPageHide => - (super.noSuchMethod(Invocation.getter(#onPageHide), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onPageShow => - (super.noSuchMethod(Invocation.getter(#onPageShow), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onPause => - (super.noSuchMethod(Invocation.getter(#onPause), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onPlay => - (super.noSuchMethod(Invocation.getter(#onPlay), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onPlaying => - (super.noSuchMethod(Invocation.getter(#onPlaying), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.PopStateEvent> get onPopState => - (super.noSuchMethod(Invocation.getter(#onPopState), - returnValue: Stream<_i2.PopStateEvent>.empty()) - as _i3.Stream<_i2.PopStateEvent>); - @override - _i3.Stream<_i2.Event> get onProgress => - (super.noSuchMethod(Invocation.getter(#onProgress), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onRateChange => - (super.noSuchMethod(Invocation.getter(#onRateChange), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onReset => - (super.noSuchMethod(Invocation.getter(#onReset), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onResize => - (super.noSuchMethod(Invocation.getter(#onResize), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onScroll => - (super.noSuchMethod(Invocation.getter(#onScroll), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onSearch => - (super.noSuchMethod(Invocation.getter(#onSearch), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onSeeked => - (super.noSuchMethod(Invocation.getter(#onSeeked), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onSeeking => - (super.noSuchMethod(Invocation.getter(#onSeeking), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onSelect => - (super.noSuchMethod(Invocation.getter(#onSelect), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onStalled => - (super.noSuchMethod(Invocation.getter(#onStalled), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.StorageEvent> get onStorage => - (super.noSuchMethod(Invocation.getter(#onStorage), - returnValue: Stream<_i2.StorageEvent>.empty()) - as _i3.Stream<_i2.StorageEvent>); - @override - _i3.Stream<_i2.Event> get onSubmit => - (super.noSuchMethod(Invocation.getter(#onSubmit), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onSuspend => - (super.noSuchMethod(Invocation.getter(#onSuspend), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onTimeUpdate => - (super.noSuchMethod(Invocation.getter(#onTimeUpdate), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.TouchEvent> get onTouchCancel => - (super.noSuchMethod(Invocation.getter(#onTouchCancel), - returnValue: Stream<_i2.TouchEvent>.empty()) - as _i3.Stream<_i2.TouchEvent>); - @override - _i3.Stream<_i2.TouchEvent> get onTouchEnd => - (super.noSuchMethod(Invocation.getter(#onTouchEnd), - returnValue: Stream<_i2.TouchEvent>.empty()) - as _i3.Stream<_i2.TouchEvent>); - @override - _i3.Stream<_i2.TouchEvent> get onTouchMove => - (super.noSuchMethod(Invocation.getter(#onTouchMove), - returnValue: Stream<_i2.TouchEvent>.empty()) - as _i3.Stream<_i2.TouchEvent>); - @override - _i3.Stream<_i2.TouchEvent> get onTouchStart => - (super.noSuchMethod(Invocation.getter(#onTouchStart), - returnValue: Stream<_i2.TouchEvent>.empty()) - as _i3.Stream<_i2.TouchEvent>); - @override - _i3.Stream<_i2.TransitionEvent> get onTransitionEnd => - (super.noSuchMethod(Invocation.getter(#onTransitionEnd), - returnValue: Stream<_i2.TransitionEvent>.empty()) - as _i3.Stream<_i2.TransitionEvent>); - @override - _i3.Stream<_i2.Event> get onUnload => - (super.noSuchMethod(Invocation.getter(#onUnload), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onVolumeChange => - (super.noSuchMethod(Invocation.getter(#onVolumeChange), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.Event> get onWaiting => - (super.noSuchMethod(Invocation.getter(#onWaiting), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.AnimationEvent> get onAnimationEnd => - (super.noSuchMethod(Invocation.getter(#onAnimationEnd), - returnValue: Stream<_i2.AnimationEvent>.empty()) - as _i3.Stream<_i2.AnimationEvent>); + (super.noSuchMethod( + Invocation.getter(#onDeviceOrientation), + returnValue: _i3.Stream<_i2.DeviceOrientationEvent>.empty(), + ) as _i3.Stream<_i2.DeviceOrientationEvent>); + @override + _i3.Stream<_i2.MouseEvent> get onDrag => (super.noSuchMethod( + Invocation.getter(#onDrag), + returnValue: _i3.Stream<_i2.MouseEvent>.empty(), + ) as _i3.Stream<_i2.MouseEvent>); + @override + _i3.Stream<_i2.MouseEvent> get onDragEnd => (super.noSuchMethod( + Invocation.getter(#onDragEnd), + returnValue: _i3.Stream<_i2.MouseEvent>.empty(), + ) as _i3.Stream<_i2.MouseEvent>); + @override + _i3.Stream<_i2.MouseEvent> get onDragEnter => (super.noSuchMethod( + Invocation.getter(#onDragEnter), + returnValue: _i3.Stream<_i2.MouseEvent>.empty(), + ) as _i3.Stream<_i2.MouseEvent>); + @override + _i3.Stream<_i2.MouseEvent> get onDragLeave => (super.noSuchMethod( + Invocation.getter(#onDragLeave), + returnValue: _i3.Stream<_i2.MouseEvent>.empty(), + ) as _i3.Stream<_i2.MouseEvent>); + @override + _i3.Stream<_i2.MouseEvent> get onDragOver => (super.noSuchMethod( + Invocation.getter(#onDragOver), + returnValue: _i3.Stream<_i2.MouseEvent>.empty(), + ) as _i3.Stream<_i2.MouseEvent>); + @override + _i3.Stream<_i2.MouseEvent> get onDragStart => (super.noSuchMethod( + Invocation.getter(#onDragStart), + returnValue: _i3.Stream<_i2.MouseEvent>.empty(), + ) as _i3.Stream<_i2.MouseEvent>); + @override + _i3.Stream<_i2.MouseEvent> get onDrop => (super.noSuchMethod( + Invocation.getter(#onDrop), + returnValue: _i3.Stream<_i2.MouseEvent>.empty(), + ) as _i3.Stream<_i2.MouseEvent>); + @override + _i3.Stream<_i2.Event> get onDurationChange => (super.noSuchMethod( + Invocation.getter(#onDurationChange), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onEmptied => (super.noSuchMethod( + Invocation.getter(#onEmptied), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onEnded => (super.noSuchMethod( + Invocation.getter(#onEnded), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onError => (super.noSuchMethod( + Invocation.getter(#onError), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onFocus => (super.noSuchMethod( + Invocation.getter(#onFocus), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onHashChange => (super.noSuchMethod( + Invocation.getter(#onHashChange), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onInput => (super.noSuchMethod( + Invocation.getter(#onInput), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onInvalid => (super.noSuchMethod( + Invocation.getter(#onInvalid), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.KeyboardEvent> get onKeyDown => (super.noSuchMethod( + Invocation.getter(#onKeyDown), + returnValue: _i3.Stream<_i2.KeyboardEvent>.empty(), + ) as _i3.Stream<_i2.KeyboardEvent>); + @override + _i3.Stream<_i2.KeyboardEvent> get onKeyPress => (super.noSuchMethod( + Invocation.getter(#onKeyPress), + returnValue: _i3.Stream<_i2.KeyboardEvent>.empty(), + ) as _i3.Stream<_i2.KeyboardEvent>); + @override + _i3.Stream<_i2.KeyboardEvent> get onKeyUp => (super.noSuchMethod( + Invocation.getter(#onKeyUp), + returnValue: _i3.Stream<_i2.KeyboardEvent>.empty(), + ) as _i3.Stream<_i2.KeyboardEvent>); + @override + _i3.Stream<_i2.Event> get onLoad => (super.noSuchMethod( + Invocation.getter(#onLoad), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onLoadedData => (super.noSuchMethod( + Invocation.getter(#onLoadedData), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onLoadedMetadata => (super.noSuchMethod( + Invocation.getter(#onLoadedMetadata), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onLoadStart => (super.noSuchMethod( + Invocation.getter(#onLoadStart), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.MessageEvent> get onMessage => (super.noSuchMethod( + Invocation.getter(#onMessage), + returnValue: _i3.Stream<_i2.MessageEvent>.empty(), + ) as _i3.Stream<_i2.MessageEvent>); + @override + _i3.Stream<_i2.MouseEvent> get onMouseDown => (super.noSuchMethod( + Invocation.getter(#onMouseDown), + returnValue: _i3.Stream<_i2.MouseEvent>.empty(), + ) as _i3.Stream<_i2.MouseEvent>); + @override + _i3.Stream<_i2.MouseEvent> get onMouseEnter => (super.noSuchMethod( + Invocation.getter(#onMouseEnter), + returnValue: _i3.Stream<_i2.MouseEvent>.empty(), + ) as _i3.Stream<_i2.MouseEvent>); + @override + _i3.Stream<_i2.MouseEvent> get onMouseLeave => (super.noSuchMethod( + Invocation.getter(#onMouseLeave), + returnValue: _i3.Stream<_i2.MouseEvent>.empty(), + ) as _i3.Stream<_i2.MouseEvent>); + @override + _i3.Stream<_i2.MouseEvent> get onMouseMove => (super.noSuchMethod( + Invocation.getter(#onMouseMove), + returnValue: _i3.Stream<_i2.MouseEvent>.empty(), + ) as _i3.Stream<_i2.MouseEvent>); + @override + _i3.Stream<_i2.MouseEvent> get onMouseOut => (super.noSuchMethod( + Invocation.getter(#onMouseOut), + returnValue: _i3.Stream<_i2.MouseEvent>.empty(), + ) as _i3.Stream<_i2.MouseEvent>); + @override + _i3.Stream<_i2.MouseEvent> get onMouseOver => (super.noSuchMethod( + Invocation.getter(#onMouseOver), + returnValue: _i3.Stream<_i2.MouseEvent>.empty(), + ) as _i3.Stream<_i2.MouseEvent>); + @override + _i3.Stream<_i2.MouseEvent> get onMouseUp => (super.noSuchMethod( + Invocation.getter(#onMouseUp), + returnValue: _i3.Stream<_i2.MouseEvent>.empty(), + ) as _i3.Stream<_i2.MouseEvent>); + @override + _i3.Stream<_i2.WheelEvent> get onMouseWheel => (super.noSuchMethod( + Invocation.getter(#onMouseWheel), + returnValue: _i3.Stream<_i2.WheelEvent>.empty(), + ) as _i3.Stream<_i2.WheelEvent>); + @override + _i3.Stream<_i2.Event> get onOffline => (super.noSuchMethod( + Invocation.getter(#onOffline), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onOnline => (super.noSuchMethod( + Invocation.getter(#onOnline), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onPageHide => (super.noSuchMethod( + Invocation.getter(#onPageHide), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onPageShow => (super.noSuchMethod( + Invocation.getter(#onPageShow), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onPause => (super.noSuchMethod( + Invocation.getter(#onPause), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onPlay => (super.noSuchMethod( + Invocation.getter(#onPlay), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onPlaying => (super.noSuchMethod( + Invocation.getter(#onPlaying), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.PopStateEvent> get onPopState => (super.noSuchMethod( + Invocation.getter(#onPopState), + returnValue: _i3.Stream<_i2.PopStateEvent>.empty(), + ) as _i3.Stream<_i2.PopStateEvent>); + @override + _i3.Stream<_i2.Event> get onProgress => (super.noSuchMethod( + Invocation.getter(#onProgress), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onRateChange => (super.noSuchMethod( + Invocation.getter(#onRateChange), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onReset => (super.noSuchMethod( + Invocation.getter(#onReset), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onResize => (super.noSuchMethod( + Invocation.getter(#onResize), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onScroll => (super.noSuchMethod( + Invocation.getter(#onScroll), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onSearch => (super.noSuchMethod( + Invocation.getter(#onSearch), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onSeeked => (super.noSuchMethod( + Invocation.getter(#onSeeked), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onSeeking => (super.noSuchMethod( + Invocation.getter(#onSeeking), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onSelect => (super.noSuchMethod( + Invocation.getter(#onSelect), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onStalled => (super.noSuchMethod( + Invocation.getter(#onStalled), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.StorageEvent> get onStorage => (super.noSuchMethod( + Invocation.getter(#onStorage), + returnValue: _i3.Stream<_i2.StorageEvent>.empty(), + ) as _i3.Stream<_i2.StorageEvent>); + @override + _i3.Stream<_i2.Event> get onSubmit => (super.noSuchMethod( + Invocation.getter(#onSubmit), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onSuspend => (super.noSuchMethod( + Invocation.getter(#onSuspend), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onTimeUpdate => (super.noSuchMethod( + Invocation.getter(#onTimeUpdate), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.TouchEvent> get onTouchCancel => (super.noSuchMethod( + Invocation.getter(#onTouchCancel), + returnValue: _i3.Stream<_i2.TouchEvent>.empty(), + ) as _i3.Stream<_i2.TouchEvent>); + @override + _i3.Stream<_i2.TouchEvent> get onTouchEnd => (super.noSuchMethod( + Invocation.getter(#onTouchEnd), + returnValue: _i3.Stream<_i2.TouchEvent>.empty(), + ) as _i3.Stream<_i2.TouchEvent>); + @override + _i3.Stream<_i2.TouchEvent> get onTouchMove => (super.noSuchMethod( + Invocation.getter(#onTouchMove), + returnValue: _i3.Stream<_i2.TouchEvent>.empty(), + ) as _i3.Stream<_i2.TouchEvent>); + @override + _i3.Stream<_i2.TouchEvent> get onTouchStart => (super.noSuchMethod( + Invocation.getter(#onTouchStart), + returnValue: _i3.Stream<_i2.TouchEvent>.empty(), + ) as _i3.Stream<_i2.TouchEvent>); + @override + _i3.Stream<_i2.TransitionEvent> get onTransitionEnd => (super.noSuchMethod( + Invocation.getter(#onTransitionEnd), + returnValue: _i3.Stream<_i2.TransitionEvent>.empty(), + ) as _i3.Stream<_i2.TransitionEvent>); + @override + _i3.Stream<_i2.Event> get onUnload => (super.noSuchMethod( + Invocation.getter(#onUnload), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onVolumeChange => (super.noSuchMethod( + Invocation.getter(#onVolumeChange), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.Event> get onWaiting => (super.noSuchMethod( + Invocation.getter(#onWaiting), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.AnimationEvent> get onAnimationEnd => (super.noSuchMethod( + Invocation.getter(#onAnimationEnd), + returnValue: _i3.Stream<_i2.AnimationEvent>.empty(), + ) as _i3.Stream<_i2.AnimationEvent>); @override _i3.Stream<_i2.AnimationEvent> get onAnimationIteration => - (super.noSuchMethod(Invocation.getter(#onAnimationIteration), - returnValue: Stream<_i2.AnimationEvent>.empty()) - as _i3.Stream<_i2.AnimationEvent>); - @override - _i3.Stream<_i2.AnimationEvent> get onAnimationStart => - (super.noSuchMethod(Invocation.getter(#onAnimationStart), - returnValue: Stream<_i2.AnimationEvent>.empty()) - as _i3.Stream<_i2.AnimationEvent>); - @override - _i3.Stream<_i2.Event> get onBeforeUnload => - (super.noSuchMethod(Invocation.getter(#onBeforeUnload), - returnValue: Stream<_i2.Event>.empty()) as _i3.Stream<_i2.Event>); - @override - _i3.Stream<_i2.WheelEvent> get onWheel => - (super.noSuchMethod(Invocation.getter(#onWheel), - returnValue: Stream<_i2.WheelEvent>.empty()) - as _i3.Stream<_i2.WheelEvent>); - @override - int get pageXOffset => - (super.noSuchMethod(Invocation.getter(#pageXOffset), returnValue: 0) - as int); - @override - int get pageYOffset => - (super.noSuchMethod(Invocation.getter(#pageYOffset), returnValue: 0) - as int); - @override - int get scrollX => - (super.noSuchMethod(Invocation.getter(#scrollX), returnValue: 0) as int); - @override - int get scrollY => - (super.noSuchMethod(Invocation.getter(#scrollY), returnValue: 0) as int); - @override - _i2.Events get on => - (super.noSuchMethod(Invocation.getter(#on), returnValue: _FakeEvents_7()) - as _i2.Events); - @override - _i2.WindowBase open(String? url, String? name, [String? options]) => - (super.noSuchMethod(Invocation.method(#open, [url, name, options]), - returnValue: _FakeWindowBase_8()) as _i2.WindowBase); + (super.noSuchMethod( + Invocation.getter(#onAnimationIteration), + returnValue: _i3.Stream<_i2.AnimationEvent>.empty(), + ) as _i3.Stream<_i2.AnimationEvent>); + @override + _i3.Stream<_i2.AnimationEvent> get onAnimationStart => (super.noSuchMethod( + Invocation.getter(#onAnimationStart), + returnValue: _i3.Stream<_i2.AnimationEvent>.empty(), + ) as _i3.Stream<_i2.AnimationEvent>); + @override + _i3.Stream<_i2.Event> get onBeforeUnload => (super.noSuchMethod( + Invocation.getter(#onBeforeUnload), + returnValue: _i3.Stream<_i2.Event>.empty(), + ) as _i3.Stream<_i2.Event>); + @override + _i3.Stream<_i2.WheelEvent> get onWheel => (super.noSuchMethod( + Invocation.getter(#onWheel), + returnValue: _i3.Stream<_i2.WheelEvent>.empty(), + ) as _i3.Stream<_i2.WheelEvent>); + @override + int get pageXOffset => (super.noSuchMethod( + Invocation.getter(#pageXOffset), + returnValue: 0, + ) as int); + @override + int get pageYOffset => (super.noSuchMethod( + Invocation.getter(#pageYOffset), + returnValue: 0, + ) as int); + @override + int get scrollX => (super.noSuchMethod( + Invocation.getter(#scrollX), + returnValue: 0, + ) as int); + @override + int get scrollY => (super.noSuchMethod( + Invocation.getter(#scrollY), + returnValue: 0, + ) as int); + @override + _i2.Events get on => (super.noSuchMethod( + Invocation.getter(#on), + returnValue: _FakeEvents_7( + this, + Invocation.getter(#on), + ), + ) as _i2.Events); + @override + _i2.WindowBase open( + String? url, + String? name, [ + String? options, + ]) => + (super.noSuchMethod( + Invocation.method( + #open, + [ + url, + name, + options, + ], + ), + returnValue: _FakeWindowBase_8( + this, + Invocation.method( + #open, + [ + url, + name, + options, + ], + ), + ), + ) as _i2.WindowBase); @override int requestAnimationFrame(_i2.FrameRequestCallback? callback) => - (super.noSuchMethod(Invocation.method(#requestAnimationFrame, [callback]), - returnValue: 0) as int); - @override - void cancelAnimationFrame(int? id) => - super.noSuchMethod(Invocation.method(#cancelAnimationFrame, [id]), - returnValueForMissingStub: null); - @override - _i3.Future<_i2.FileSystem> requestFileSystem(int? size, - {bool? persistent = false}) => (super.noSuchMethod( - Invocation.method( - #requestFileSystem, [size], {#persistent: persistent}), - returnValue: Future<_i2.FileSystem>.value(_FakeFileSystem_9())) - as _i3.Future<_i2.FileSystem>); - @override - void alert([String? message]) => - super.noSuchMethod(Invocation.method(#alert, [message]), - returnValueForMissingStub: null); - @override - void cancelIdleCallback(int? handle) => - super.noSuchMethod(Invocation.method(#cancelIdleCallback, [handle]), - returnValueForMissingStub: null); - @override - void close() => super.noSuchMethod(Invocation.method(#close, []), - returnValueForMissingStub: null); - @override - bool confirm([String? message]) => - (super.noSuchMethod(Invocation.method(#confirm, [message]), - returnValue: false) as bool); - @override - _i3.Future fetch(dynamic input, [Map? init]) => - (super.noSuchMethod(Invocation.method(#fetch, [input, init]), - returnValue: Future.value()) as _i3.Future); - @override - bool find(String? string, bool? caseSensitive, bool? backwards, bool? wrap, - bool? wholeWord, bool? searchInFrames, bool? showDialog) => + Invocation.method( + #requestAnimationFrame, + [callback], + ), + returnValue: 0, + ) as int); + @override + void cancelAnimationFrame(int? id) => super.noSuchMethod( + Invocation.method( + #cancelAnimationFrame, + [id], + ), + returnValueForMissingStub: null, + ); + @override + _i3.Future<_i2.FileSystem> requestFileSystem( + int? size, { + bool? persistent = false, + }) => + (super.noSuchMethod( + Invocation.method( + #requestFileSystem, + [size], + {#persistent: persistent}, + ), + returnValue: _i3.Future<_i2.FileSystem>.value(_FakeFileSystem_9( + this, + Invocation.method( + #requestFileSystem, + [size], + {#persistent: persistent}, + ), + )), + ) as _i3.Future<_i2.FileSystem>); + @override + void alert([String? message]) => super.noSuchMethod( + Invocation.method( + #alert, + [message], + ), + returnValueForMissingStub: null, + ); + @override + void cancelIdleCallback(int? handle) => super.noSuchMethod( + Invocation.method( + #cancelIdleCallback, + [handle], + ), + returnValueForMissingStub: null, + ); + @override + void close() => super.noSuchMethod( + Invocation.method( + #close, + [], + ), + returnValueForMissingStub: null, + ); + @override + bool confirm([String? message]) => (super.noSuchMethod( + Invocation.method( + #confirm, + [message], + ), + returnValue: false, + ) as bool); + @override + _i3.Future fetch( + dynamic input, [ + Map? init, + ]) => + (super.noSuchMethod( + Invocation.method( + #fetch, + [ + input, + init, + ], + ), + returnValue: _i3.Future.value(), + ) as _i3.Future); + @override + bool find( + String? string, + bool? caseSensitive, + bool? backwards, + bool? wrap, + bool? wholeWord, + bool? searchInFrames, + bool? showDialog, + ) => (super.noSuchMethod( - Invocation.method(#find, [ + Invocation.method( + #find, + [ string, caseSensitive, backwards, wrap, wholeWord, searchInFrames, - showDialog - ]), - returnValue: false) as bool); + showDialog, + ], + ), + returnValue: false, + ) as bool); @override _i2.StylePropertyMapReadonly getComputedStyleMap( - _i2.Element? element, String? pseudoElement) => + _i2.Element? element, + String? pseudoElement, + ) => (super.noSuchMethod( - Invocation.method(#getComputedStyleMap, [element, pseudoElement]), - returnValue: _FakeStylePropertyMapReadonly_10()) - as _i2.StylePropertyMapReadonly); + Invocation.method( + #getComputedStyleMap, + [ + element, + pseudoElement, + ], + ), + returnValue: _FakeStylePropertyMapReadonly_10( + this, + Invocation.method( + #getComputedStyleMap, + [ + element, + pseudoElement, + ], + ), + ), + ) as _i2.StylePropertyMapReadonly); @override List<_i2.CssRule> getMatchedCssRules( - _i2.Element? element, String? pseudoElement) => + _i2.Element? element, + String? pseudoElement, + ) => (super.noSuchMethod( - Invocation.method(#getMatchedCssRules, [element, pseudoElement]), - returnValue: <_i2.CssRule>[]) as List<_i2.CssRule>); - @override - _i2.MediaQueryList matchMedia(String? query) => - (super.noSuchMethod(Invocation.method(#matchMedia, [query]), - returnValue: _FakeMediaQueryList_11()) as _i2.MediaQueryList); - @override - void moveBy(int? x, int? y) => - super.noSuchMethod(Invocation.method(#moveBy, [x, y]), - returnValueForMissingStub: null); - @override - void postMessage(dynamic message, String? targetOrigin, - [List? transfer]) => + Invocation.method( + #getMatchedCssRules, + [ + element, + pseudoElement, + ], + ), + returnValue: <_i2.CssRule>[], + ) as List<_i2.CssRule>); + @override + _i2.MediaQueryList matchMedia(String? query) => (super.noSuchMethod( + Invocation.method( + #matchMedia, + [query], + ), + returnValue: _FakeMediaQueryList_11( + this, + Invocation.method( + #matchMedia, + [query], + ), + ), + ) as _i2.MediaQueryList); + @override + void moveBy( + int? x, + int? y, + ) => super.noSuchMethod( - Invocation.method(#postMessage, [message, targetOrigin, transfer]), - returnValueForMissingStub: null); - @override - void print() => super.noSuchMethod(Invocation.method(#print, []), - returnValueForMissingStub: null); - @override - int requestIdleCallback(_i2.IdleRequestCallback? callback, - [Map? options]) => + Invocation.method( + #moveBy, + [ + x, + y, + ], + ), + returnValueForMissingStub: null, + ); + @override + void postMessage( + dynamic message, + String? targetOrigin, [ + List? transfer, + ]) => + super.noSuchMethod( + Invocation.method( + #postMessage, + [ + message, + targetOrigin, + transfer, + ], + ), + returnValueForMissingStub: null, + ); + @override + void print() => super.noSuchMethod( + Invocation.method( + #print, + [], + ), + returnValueForMissingStub: null, + ); + @override + int requestIdleCallback( + _i2.IdleRequestCallback? callback, [ + Map? options, + ]) => (super.noSuchMethod( - Invocation.method(#requestIdleCallback, [callback, options]), - returnValue: 0) as int); - @override - void resizeBy(int? x, int? y) => - super.noSuchMethod(Invocation.method(#resizeBy, [x, y]), - returnValueForMissingStub: null); - @override - void resizeTo(int? x, int? y) => - super.noSuchMethod(Invocation.method(#resizeTo, [x, y]), - returnValueForMissingStub: null); - @override - void scroll( - [dynamic options_OR_x, - dynamic y, - Map? scrollOptions]) => + Invocation.method( + #requestIdleCallback, + [ + callback, + options, + ], + ), + returnValue: 0, + ) as int); + @override + void resizeBy( + int? x, + int? y, + ) => super.noSuchMethod( - Invocation.method(#scroll, [options_OR_x, y, scrollOptions]), - returnValueForMissingStub: null); - @override - void scrollBy( - [dynamic options_OR_x, - dynamic y, - Map? scrollOptions]) => + Invocation.method( + #resizeBy, + [ + x, + y, + ], + ), + returnValueForMissingStub: null, + ); + @override + void resizeTo( + int? x, + int? y, + ) => super.noSuchMethod( - Invocation.method(#scrollBy, [options_OR_x, y, scrollOptions]), - returnValueForMissingStub: null); - @override - void scrollTo( - [dynamic options_OR_x, - dynamic y, - Map? scrollOptions]) => + Invocation.method( + #resizeTo, + [ + x, + y, + ], + ), + returnValueForMissingStub: null, + ); + @override + void scroll([ + dynamic options_OR_x, + dynamic y, + Map? scrollOptions, + ]) => super.noSuchMethod( - Invocation.method(#scrollTo, [options_OR_x, y, scrollOptions]), - returnValueForMissingStub: null); - @override - void stop() => super.noSuchMethod(Invocation.method(#stop, []), - returnValueForMissingStub: null); + Invocation.method( + #scroll, + [ + options_OR_x, + y, + scrollOptions, + ], + ), + returnValueForMissingStub: null, + ); + @override + void scrollBy([ + dynamic options_OR_x, + dynamic y, + Map? scrollOptions, + ]) => + super.noSuchMethod( + Invocation.method( + #scrollBy, + [ + options_OR_x, + y, + scrollOptions, + ], + ), + returnValueForMissingStub: null, + ); + @override + void scrollTo([ + dynamic options_OR_x, + dynamic y, + Map? scrollOptions, + ]) => + super.noSuchMethod( + Invocation.method( + #scrollTo, + [ + options_OR_x, + y, + scrollOptions, + ], + ), + returnValueForMissingStub: null, + ); + @override + void stop() => super.noSuchMethod( + Invocation.method( + #stop, + [], + ), + returnValueForMissingStub: null, + ); @override _i3.Future<_i2.Entry> resolveLocalFileSystemUrl(String? url) => - (super.noSuchMethod(Invocation.method(#resolveLocalFileSystemUrl, [url]), - returnValue: Future<_i2.Entry>.value(_FakeEntry_12())) - as _i3.Future<_i2.Entry>); - @override - String atob(String? atob) => - (super.noSuchMethod(Invocation.method(#atob, [atob]), returnValue: '') - as String); - @override - String btoa(String? btoa) => - (super.noSuchMethod(Invocation.method(#btoa, [btoa]), returnValue: '') - as String); - @override - void moveTo(_i4.Point? p) => - super.noSuchMethod(Invocation.method(#moveTo, [p]), - returnValueForMissingStub: null); - @override - void addEventListener(String? type, _i2.EventListener? listener, - [bool? useCapture]) => + (super.noSuchMethod( + Invocation.method( + #resolveLocalFileSystemUrl, + [url], + ), + returnValue: _i3.Future<_i2.Entry>.value(_FakeEntry_12( + this, + Invocation.method( + #resolveLocalFileSystemUrl, + [url], + ), + )), + ) as _i3.Future<_i2.Entry>); + @override + String atob(String? atob) => (super.noSuchMethod( + Invocation.method( + #atob, + [atob], + ), + returnValue: '', + ) as String); + @override + String btoa(String? btoa) => (super.noSuchMethod( + Invocation.method( + #btoa, + [btoa], + ), + returnValue: '', + ) as String); + @override + void moveTo(_i4.Point? p) => super.noSuchMethod( + Invocation.method( + #moveTo, + [p], + ), + returnValueForMissingStub: null, + ); + @override + void addEventListener( + String? type, + _i2.EventListener? listener, [ + bool? useCapture, + ]) => super.noSuchMethod( - Invocation.method(#addEventListener, [type, listener, useCapture]), - returnValueForMissingStub: null); - @override - void removeEventListener(String? type, _i2.EventListener? listener, - [bool? useCapture]) => + Invocation.method( + #addEventListener, + [ + type, + listener, + useCapture, + ], + ), + returnValueForMissingStub: null, + ); + @override + void removeEventListener( + String? type, + _i2.EventListener? listener, [ + bool? useCapture, + ]) => super.noSuchMethod( - Invocation.method(#removeEventListener, [type, listener, useCapture]), - returnValueForMissingStub: null); - @override - bool dispatchEvent(_i2.Event? event) => - (super.noSuchMethod(Invocation.method(#dispatchEvent, [event]), - returnValue: false) as bool); + Invocation.method( + #removeEventListener, + [ + type, + listener, + useCapture, + ], + ), + returnValueForMissingStub: null, + ); + @override + bool dispatchEvent(_i2.Event? event) => (super.noSuchMethod( + Invocation.method( + #dispatchEvent, + [event], + ), + returnValue: false, + ) as bool); } /// A class which mocks [Navigator]. @@ -654,97 +1163,199 @@ class MockNavigator extends _i1.Mock implements _i2.Navigator { } @override - String get language => - (super.noSuchMethod(Invocation.getter(#language), returnValue: '') - as String); - @override - _i2.Geolocation get geolocation => - (super.noSuchMethod(Invocation.getter(#geolocation), - returnValue: _FakeGeolocation_13()) as _i2.Geolocation); - @override - String get vendor => - (super.noSuchMethod(Invocation.getter(#vendor), returnValue: '') - as String); - @override - String get vendorSub => - (super.noSuchMethod(Invocation.getter(#vendorSub), returnValue: '') - as String); - @override - String get appCodeName => - (super.noSuchMethod(Invocation.getter(#appCodeName), returnValue: '') - as String); - @override - String get appName => - (super.noSuchMethod(Invocation.getter(#appName), returnValue: '') - as String); - @override - String get appVersion => - (super.noSuchMethod(Invocation.getter(#appVersion), returnValue: '') - as String); - @override - String get product => - (super.noSuchMethod(Invocation.getter(#product), returnValue: '') - as String); - @override - String get userAgent => - (super.noSuchMethod(Invocation.getter(#userAgent), returnValue: '') - as String); - @override - List<_i2.Gamepad?> getGamepads() => - (super.noSuchMethod(Invocation.method(#getGamepads, []), - returnValue: <_i2.Gamepad?>[]) as List<_i2.Gamepad?>); - @override - _i3.Future<_i2.MediaStream> getUserMedia( - {dynamic audio = false, dynamic video = false}) => + String get language => (super.noSuchMethod( + Invocation.getter(#language), + returnValue: '', + ) as String); + @override + _i2.Geolocation get geolocation => (super.noSuchMethod( + Invocation.getter(#geolocation), + returnValue: _FakeGeolocation_13( + this, + Invocation.getter(#geolocation), + ), + ) as _i2.Geolocation); + @override + String get vendor => (super.noSuchMethod( + Invocation.getter(#vendor), + returnValue: '', + ) as String); + @override + String get vendorSub => (super.noSuchMethod( + Invocation.getter(#vendorSub), + returnValue: '', + ) as String); + @override + String get appCodeName => (super.noSuchMethod( + Invocation.getter(#appCodeName), + returnValue: '', + ) as String); + @override + String get appName => (super.noSuchMethod( + Invocation.getter(#appName), + returnValue: '', + ) as String); + @override + String get appVersion => (super.noSuchMethod( + Invocation.getter(#appVersion), + returnValue: '', + ) as String); + @override + String get product => (super.noSuchMethod( + Invocation.getter(#product), + returnValue: '', + ) as String); + @override + String get userAgent => (super.noSuchMethod( + Invocation.getter(#userAgent), + returnValue: '', + ) as String); + @override + List<_i2.Gamepad?> getGamepads() => (super.noSuchMethod( + Invocation.method( + #getGamepads, + [], + ), + returnValue: <_i2.Gamepad?>[], + ) as List<_i2.Gamepad?>); + @override + _i3.Future<_i2.MediaStream> getUserMedia({ + dynamic audio = false, + dynamic video = false, + }) => (super.noSuchMethod( - Invocation.method(#getUserMedia, [], {#audio: audio, #video: video}), - returnValue: - Future<_i2.MediaStream>.value(_FakeMediaStream_14())) as _i3 - .Future<_i2.MediaStream>); - @override - void cancelKeyboardLock() => - super.noSuchMethod(Invocation.method(#cancelKeyboardLock, []), - returnValueForMissingStub: null); - @override - _i3.Future getBattery() => - (super.noSuchMethod(Invocation.method(#getBattery, []), - returnValue: Future.value()) as _i3.Future); + Invocation.method( + #getUserMedia, + [], + { + #audio: audio, + #video: video, + }, + ), + returnValue: _i3.Future<_i2.MediaStream>.value(_FakeMediaStream_14( + this, + Invocation.method( + #getUserMedia, + [], + { + #audio: audio, + #video: video, + }, + ), + )), + ) as _i3.Future<_i2.MediaStream>); + @override + void cancelKeyboardLock() => super.noSuchMethod( + Invocation.method( + #cancelKeyboardLock, + [], + ), + returnValueForMissingStub: null, + ); + @override + _i3.Future getBattery() => (super.noSuchMethod( + Invocation.method( + #getBattery, + [], + ), + returnValue: _i3.Future.value(), + ) as _i3.Future); @override _i3.Future<_i2.RelatedApplication> getInstalledRelatedApps() => - (super.noSuchMethod(Invocation.method(#getInstalledRelatedApps, []), - returnValue: Future<_i2.RelatedApplication>.value( - _FakeRelatedApplication_15())) - as _i3.Future<_i2.RelatedApplication>); - @override - _i3.Future getVRDisplays() => - (super.noSuchMethod(Invocation.method(#getVRDisplays, []), - returnValue: Future.value()) as _i3.Future); - @override - void registerProtocolHandler(String? scheme, String? url, String? title) => + (super.noSuchMethod( + Invocation.method( + #getInstalledRelatedApps, + [], + ), + returnValue: + _i3.Future<_i2.RelatedApplication>.value(_FakeRelatedApplication_15( + this, + Invocation.method( + #getInstalledRelatedApps, + [], + ), + )), + ) as _i3.Future<_i2.RelatedApplication>); + @override + _i3.Future getVRDisplays() => (super.noSuchMethod( + Invocation.method( + #getVRDisplays, + [], + ), + returnValue: _i3.Future.value(), + ) as _i3.Future); + @override + void registerProtocolHandler( + String? scheme, + String? url, + String? title, + ) => super.noSuchMethod( - Invocation.method(#registerProtocolHandler, [scheme, url, title]), - returnValueForMissingStub: null); + Invocation.method( + #registerProtocolHandler, + [ + scheme, + url, + title, + ], + ), + returnValueForMissingStub: null, + ); @override _i3.Future requestKeyboardLock([List? keyCodes]) => - (super.noSuchMethod(Invocation.method(#requestKeyboardLock, [keyCodes]), - returnValue: Future.value()) as _i3.Future); + (super.noSuchMethod( + Invocation.method( + #requestKeyboardLock, + [keyCodes], + ), + returnValue: _i3.Future.value(), + ) as _i3.Future); @override _i3.Future requestMidiAccess([Map? options]) => - (super.noSuchMethod(Invocation.method(#requestMidiAccess, [options]), - returnValue: Future.value()) as _i3.Future); - @override - _i3.Future requestMediaKeySystemAccess(String? keySystem, - List>? supportedConfigurations) => (super.noSuchMethod( - Invocation.method(#requestMediaKeySystemAccess, - [keySystem, supportedConfigurations]), - returnValue: Future.value()) as _i3.Future); - @override - bool sendBeacon(String? url, Object? data) => - (super.noSuchMethod(Invocation.method(#sendBeacon, [url, data]), - returnValue: false) as bool); + Invocation.method( + #requestMidiAccess, + [options], + ), + returnValue: _i3.Future.value(), + ) as _i3.Future); + @override + _i3.Future requestMediaKeySystemAccess( + String? keySystem, + List>? supportedConfigurations, + ) => + (super.noSuchMethod( + Invocation.method( + #requestMediaKeySystemAccess, + [ + keySystem, + supportedConfigurations, + ], + ), + returnValue: _i3.Future.value(), + ) as _i3.Future); + @override + bool sendBeacon( + String? url, + Object? data, + ) => + (super.noSuchMethod( + Invocation.method( + #sendBeacon, + [ + url, + data, + ], + ), + returnValue: false, + ) as bool); @override _i3.Future share([Map? data]) => - (super.noSuchMethod(Invocation.method(#share, [data]), - returnValue: Future.value()) as _i3.Future); + (super.noSuchMethod( + Invocation.method( + #share, + [data], + ), + returnValue: _i3.Future.value(), + ) as _i3.Future); } diff --git a/packages/url_launcher/url_launcher_web/example/pubspec.yaml b/packages/url_launcher/url_launcher_web/example/pubspec.yaml index 881cb29c7599..f972b2857ecf 100644 --- a/packages/url_launcher/url_launcher_web/example/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/example/pubspec.yaml @@ -19,7 +19,7 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - mockito: ^5.0.0 + mockito: ^5.3.2 url_launcher_platform_interface: ^2.0.3 url_launcher_web: path: ../ diff --git a/packages/webview_flutter/webview_flutter_web/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/pubspec.yaml index 8f1b523be555..f27e6408335a 100644 --- a/packages/webview_flutter/webview_flutter_web/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/pubspec.yaml @@ -29,4 +29,4 @@ dev_dependencies: sdk: flutter flutter_test: sdk: flutter - mockito: ^5.0.0 + mockito: ^5.3.2 diff --git a/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.mocks.dart b/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.mocks.dart index e35d1e93c59f..db442eeea7a3 100644 --- a/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_web/test/webview_flutter_web_test.mocks.dart @@ -1,25 +1,22 @@ -// 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. - -// Mocks generated by Mockito 5.0.16 from annotations +// Mocks generated by Mockito 5.3.2 from annotations // in webview_flutter_web/test/webview_flutter_web_test.dart. // Do not manually edit this file. +// ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i6; import 'dart:html' as _i2; import 'dart:math' as _i3; import 'package:flutter/foundation.dart' as _i5; +import 'package:flutter/src/widgets/notification_listener.dart' as _i7; import 'package:flutter/widgets.dart' as _i4; import 'package:mockito/mockito.dart' as _i1; -import 'package:webview_flutter_platform_interface/src/types/auto_media_playback_policy.dart' - as _i8; -import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i7; +import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i8; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart' as _i9; import 'package:webview_flutter_web/webview_flutter_web.dart' as _i10; +// ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references @@ -28,66 +25,231 @@ import 'package:webview_flutter_web/webview_flutter_web.dart' as _i10; // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeCssClassSet_0 extends _i1.SmartFake implements _i2.CssClassSet { + _FakeCssClassSet_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeCssClassSet_0 extends _i1.Fake implements _i2.CssClassSet {} +class _FakeRectangle_1 extends _i1.SmartFake + implements _i3.Rectangle { + _FakeRectangle_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeRectangle_1 extends _i1.Fake - implements _i3.Rectangle {} +class _FakeCssRect_2 extends _i1.SmartFake implements _i2.CssRect { + _FakeCssRect_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeCssRect_2 extends _i1.Fake implements _i2.CssRect {} +class _FakePoint_3 extends _i1.SmartFake + implements _i3.Point { + _FakePoint_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakePoint_3 extends _i1.Fake implements _i3.Point {} +class _FakeElementEvents_4 extends _i1.SmartFake implements _i2.ElementEvents { + _FakeElementEvents_4( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeElementEvents_4 extends _i1.Fake implements _i2.ElementEvents {} +class _FakeCssStyleDeclaration_5 extends _i1.SmartFake + implements _i2.CssStyleDeclaration { + _FakeCssStyleDeclaration_5( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeCssStyleDeclaration_5 extends _i1.Fake - implements _i2.CssStyleDeclaration {} +class _FakeElementStream_6 extends _i1.SmartFake + implements _i2.ElementStream { + _FakeElementStream_6( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeElementStream_6 extends _i1.Fake - implements _i2.ElementStream {} +class _FakeElementList_7 extends _i1.SmartFake + implements _i2.ElementList { + _FakeElementList_7( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeElementList_7 extends _i1.Fake - implements _i2.ElementList {} +class _FakeScrollState_8 extends _i1.SmartFake implements _i2.ScrollState { + _FakeScrollState_8( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeScrollState_8 extends _i1.Fake implements _i2.ScrollState {} +class _FakeAnimation_9 extends _i1.SmartFake implements _i2.Animation { + _FakeAnimation_9( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeAnimation_9 extends _i1.Fake implements _i2.Animation {} +class _FakeElement_10 extends _i1.SmartFake implements _i2.Element { + _FakeElement_10( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeElement_10 extends _i1.Fake implements _i2.Element {} +class _FakeShadowRoot_11 extends _i1.SmartFake implements _i2.ShadowRoot { + _FakeShadowRoot_11( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeShadowRoot_11 extends _i1.Fake implements _i2.ShadowRoot {} +class _FakeDocumentFragment_12 extends _i1.SmartFake + implements _i2.DocumentFragment { + _FakeDocumentFragment_12( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeDocumentFragment_12 extends _i1.Fake - implements _i2.DocumentFragment {} +class _FakeNode_13 extends _i1.SmartFake implements _i2.Node { + _FakeNode_13( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeNode_13 extends _i1.Fake implements _i2.Node {} +class _FakeWidget_14 extends _i1.SmartFake implements _i4.Widget { + _FakeWidget_14( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); -class _FakeWidget_14 extends _i1.Fake implements _i4.Widget { @override String toString({_i5.DiagnosticLevel? minLevel = _i5.DiagnosticLevel.info}) => super.toString(); } -class _FakeInheritedWidget_15 extends _i1.Fake implements _i4.InheritedWidget { +class _FakeInheritedWidget_15 extends _i1.SmartFake + implements _i4.InheritedWidget { + _FakeInheritedWidget_15( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); + @override String toString({_i5.DiagnosticLevel? minLevel = _i5.DiagnosticLevel.info}) => super.toString(); } -class _FakeDiagnosticsNode_16 extends _i1.Fake implements _i5.DiagnosticsNode { +class _FakeDiagnosticsNode_16 extends _i1.SmartFake + implements _i5.DiagnosticsNode { + _FakeDiagnosticsNode_16( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); + @override - String toString( - {_i5.TextTreeConfiguration? parentConfiguration, - _i5.DiagnosticLevel? minLevel = _i5.DiagnosticLevel.info}) => + String toString({ + _i5.TextTreeConfiguration? parentConfiguration, + _i5.DiagnosticLevel? minLevel = _i5.DiagnosticLevel.info, + }) => super.toString(); } -class _FakeHttpRequest_17 extends _i1.Fake implements _i2.HttpRequest {} +class _FakeHttpRequest_17 extends _i1.SmartFake implements _i2.HttpRequest { + _FakeHttpRequest_17( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeHttpRequestUpload_18 extends _i1.Fake - implements _i2.HttpRequestUpload {} +class _FakeHttpRequestUpload_18 extends _i1.SmartFake + implements _i2.HttpRequestUpload { + _FakeHttpRequestUpload_18( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeEvents_19 extends _i1.Fake implements _i2.Events {} +class _FakeEvents_19 extends _i1.SmartFake implements _i2.Events { + _FakeEvents_19( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} /// A class which mocks [IFrameElement]. /// @@ -98,907 +260,1844 @@ class MockIFrameElement extends _i1.Mock implements _i2.IFrameElement { } @override - set allow(String? value) => - super.noSuchMethod(Invocation.setter(#allow, value), - returnValueForMissingStub: null); - @override - set allowFullscreen(bool? value) => - super.noSuchMethod(Invocation.setter(#allowFullscreen, value), - returnValueForMissingStub: null); - @override - set allowPaymentRequest(bool? value) => - super.noSuchMethod(Invocation.setter(#allowPaymentRequest, value), - returnValueForMissingStub: null); - @override - set csp(String? value) => super.noSuchMethod(Invocation.setter(#csp, value), - returnValueForMissingStub: null); - @override - set height(String? value) => - super.noSuchMethod(Invocation.setter(#height, value), - returnValueForMissingStub: null); - @override - set name(String? value) => super.noSuchMethod(Invocation.setter(#name, value), - returnValueForMissingStub: null); - @override - set referrerPolicy(String? value) => - super.noSuchMethod(Invocation.setter(#referrerPolicy, value), - returnValueForMissingStub: null); - @override - set src(String? value) => super.noSuchMethod(Invocation.setter(#src, value), - returnValueForMissingStub: null); - @override - set srcdoc(String? value) => - super.noSuchMethod(Invocation.setter(#srcdoc, value), - returnValueForMissingStub: null); - @override - set width(String? value) => - super.noSuchMethod(Invocation.setter(#width, value), - returnValueForMissingStub: null); - @override - set nonce(String? value) => - super.noSuchMethod(Invocation.setter(#nonce, value), - returnValueForMissingStub: null); - @override - Map get attributes => - (super.noSuchMethod(Invocation.getter(#attributes), - returnValue: {}) as Map); - @override - set attributes(Map? value) => - super.noSuchMethod(Invocation.setter(#attributes, value), - returnValueForMissingStub: null); - @override - List<_i2.Element> get children => - (super.noSuchMethod(Invocation.getter(#children), - returnValue: <_i2.Element>[]) as List<_i2.Element>); - @override - set children(List<_i2.Element>? value) => - super.noSuchMethod(Invocation.setter(#children, value), - returnValueForMissingStub: null); - @override - _i2.CssClassSet get classes => - (super.noSuchMethod(Invocation.getter(#classes), - returnValue: _FakeCssClassSet_0()) as _i2.CssClassSet); - @override - set classes(Iterable? value) => - super.noSuchMethod(Invocation.setter(#classes, value), - returnValueForMissingStub: null); - @override - Map get dataset => - (super.noSuchMethod(Invocation.getter(#dataset), - returnValue: {}) as Map); - @override - set dataset(Map? value) => - super.noSuchMethod(Invocation.setter(#dataset, value), - returnValueForMissingStub: null); - @override - _i3.Rectangle get client => - (super.noSuchMethod(Invocation.getter(#client), - returnValue: _FakeRectangle_1()) as _i3.Rectangle); - @override - _i3.Rectangle get offset => - (super.noSuchMethod(Invocation.getter(#offset), - returnValue: _FakeRectangle_1()) as _i3.Rectangle); - @override - String get localName => - (super.noSuchMethod(Invocation.getter(#localName), returnValue: '') - as String); - @override - _i2.CssRect get contentEdge => - (super.noSuchMethod(Invocation.getter(#contentEdge), - returnValue: _FakeCssRect_2()) as _i2.CssRect); - @override - _i2.CssRect get paddingEdge => - (super.noSuchMethod(Invocation.getter(#paddingEdge), - returnValue: _FakeCssRect_2()) as _i2.CssRect); - @override - _i2.CssRect get borderEdge => - (super.noSuchMethod(Invocation.getter(#borderEdge), - returnValue: _FakeCssRect_2()) as _i2.CssRect); - @override - _i2.CssRect get marginEdge => - (super.noSuchMethod(Invocation.getter(#marginEdge), - returnValue: _FakeCssRect_2()) as _i2.CssRect); - @override - _i3.Point get documentOffset => - (super.noSuchMethod(Invocation.getter(#documentOffset), - returnValue: _FakePoint_3()) as _i3.Point); - @override - set innerHtml(String? html) => - super.noSuchMethod(Invocation.setter(#innerHtml, html), - returnValueForMissingStub: null); - @override - String get innerText => - (super.noSuchMethod(Invocation.getter(#innerText), returnValue: '') - as String); - @override - set innerText(String? value) => - super.noSuchMethod(Invocation.setter(#innerText, value), - returnValueForMissingStub: null); - @override - _i2.ElementEvents get on => (super.noSuchMethod(Invocation.getter(#on), - returnValue: _FakeElementEvents_4()) as _i2.ElementEvents); - @override - int get offsetHeight => - (super.noSuchMethod(Invocation.getter(#offsetHeight), returnValue: 0) - as int); - @override - int get offsetLeft => - (super.noSuchMethod(Invocation.getter(#offsetLeft), returnValue: 0) - as int); - @override - int get offsetTop => - (super.noSuchMethod(Invocation.getter(#offsetTop), returnValue: 0) - as int); - @override - int get offsetWidth => - (super.noSuchMethod(Invocation.getter(#offsetWidth), returnValue: 0) - as int); - @override - int get scrollHeight => - (super.noSuchMethod(Invocation.getter(#scrollHeight), returnValue: 0) - as int); - @override - int get scrollLeft => - (super.noSuchMethod(Invocation.getter(#scrollLeft), returnValue: 0) - as int); - @override - set scrollLeft(int? value) => - super.noSuchMethod(Invocation.setter(#scrollLeft, value), - returnValueForMissingStub: null); - @override - int get scrollTop => - (super.noSuchMethod(Invocation.getter(#scrollTop), returnValue: 0) - as int); - @override - set scrollTop(int? value) => - super.noSuchMethod(Invocation.setter(#scrollTop, value), - returnValueForMissingStub: null); - @override - int get scrollWidth => - (super.noSuchMethod(Invocation.getter(#scrollWidth), returnValue: 0) - as int); - @override - String get contentEditable => - (super.noSuchMethod(Invocation.getter(#contentEditable), returnValue: '') - as String); - @override - set contentEditable(String? value) => - super.noSuchMethod(Invocation.setter(#contentEditable, value), - returnValueForMissingStub: null); - @override - set dir(String? value) => super.noSuchMethod(Invocation.setter(#dir, value), - returnValueForMissingStub: null); - @override - bool get draggable => - (super.noSuchMethod(Invocation.getter(#draggable), returnValue: false) - as bool); - @override - set draggable(bool? value) => - super.noSuchMethod(Invocation.setter(#draggable, value), - returnValueForMissingStub: null); - @override - bool get hidden => - (super.noSuchMethod(Invocation.getter(#hidden), returnValue: false) - as bool); - @override - set hidden(bool? value) => - super.noSuchMethod(Invocation.setter(#hidden, value), - returnValueForMissingStub: null); - @override - set inert(bool? value) => super.noSuchMethod(Invocation.setter(#inert, value), - returnValueForMissingStub: null); - @override - set inputMode(String? value) => - super.noSuchMethod(Invocation.setter(#inputMode, value), - returnValueForMissingStub: null); - @override - set lang(String? value) => super.noSuchMethod(Invocation.setter(#lang, value), - returnValueForMissingStub: null); - @override - set spellcheck(bool? value) => - super.noSuchMethod(Invocation.setter(#spellcheck, value), - returnValueForMissingStub: null); + set allow(String? value) => super.noSuchMethod( + Invocation.setter( + #allow, + value, + ), + returnValueForMissingStub: null, + ); + @override + set allowFullscreen(bool? value) => super.noSuchMethod( + Invocation.setter( + #allowFullscreen, + value, + ), + returnValueForMissingStub: null, + ); + @override + set allowPaymentRequest(bool? value) => super.noSuchMethod( + Invocation.setter( + #allowPaymentRequest, + value, + ), + returnValueForMissingStub: null, + ); + @override + set csp(String? value) => super.noSuchMethod( + Invocation.setter( + #csp, + value, + ), + returnValueForMissingStub: null, + ); + @override + set height(String? value) => super.noSuchMethod( + Invocation.setter( + #height, + value, + ), + returnValueForMissingStub: null, + ); + @override + set name(String? value) => super.noSuchMethod( + Invocation.setter( + #name, + value, + ), + returnValueForMissingStub: null, + ); + @override + set referrerPolicy(String? value) => super.noSuchMethod( + Invocation.setter( + #referrerPolicy, + value, + ), + returnValueForMissingStub: null, + ); + @override + set src(String? value) => super.noSuchMethod( + Invocation.setter( + #src, + value, + ), + returnValueForMissingStub: null, + ); + @override + set srcdoc(String? value) => super.noSuchMethod( + Invocation.setter( + #srcdoc, + value, + ), + returnValueForMissingStub: null, + ); + @override + set width(String? value) => super.noSuchMethod( + Invocation.setter( + #width, + value, + ), + returnValueForMissingStub: null, + ); + @override + set nonce(String? value) => super.noSuchMethod( + Invocation.setter( + #nonce, + value, + ), + returnValueForMissingStub: null, + ); + @override + Map get attributes => (super.noSuchMethod( + Invocation.getter(#attributes), + returnValue: {}, + ) as Map); + @override + set attributes(Map? value) => super.noSuchMethod( + Invocation.setter( + #attributes, + value, + ), + returnValueForMissingStub: null, + ); + @override + List<_i2.Element> get children => (super.noSuchMethod( + Invocation.getter(#children), + returnValue: <_i2.Element>[], + ) as List<_i2.Element>); + @override + set children(List<_i2.Element>? value) => super.noSuchMethod( + Invocation.setter( + #children, + value, + ), + returnValueForMissingStub: null, + ); + @override + _i2.CssClassSet get classes => (super.noSuchMethod( + Invocation.getter(#classes), + returnValue: _FakeCssClassSet_0( + this, + Invocation.getter(#classes), + ), + ) as _i2.CssClassSet); + @override + set classes(Iterable? value) => super.noSuchMethod( + Invocation.setter( + #classes, + value, + ), + returnValueForMissingStub: null, + ); + @override + Map get dataset => (super.noSuchMethod( + Invocation.getter(#dataset), + returnValue: {}, + ) as Map); + @override + set dataset(Map? value) => super.noSuchMethod( + Invocation.setter( + #dataset, + value, + ), + returnValueForMissingStub: null, + ); + @override + _i3.Rectangle get client => (super.noSuchMethod( + Invocation.getter(#client), + returnValue: _FakeRectangle_1( + this, + Invocation.getter(#client), + ), + ) as _i3.Rectangle); + @override + _i3.Rectangle get offset => (super.noSuchMethod( + Invocation.getter(#offset), + returnValue: _FakeRectangle_1( + this, + Invocation.getter(#offset), + ), + ) as _i3.Rectangle); + @override + String get localName => (super.noSuchMethod( + Invocation.getter(#localName), + returnValue: '', + ) as String); + @override + _i2.CssRect get contentEdge => (super.noSuchMethod( + Invocation.getter(#contentEdge), + returnValue: _FakeCssRect_2( + this, + Invocation.getter(#contentEdge), + ), + ) as _i2.CssRect); + @override + _i2.CssRect get paddingEdge => (super.noSuchMethod( + Invocation.getter(#paddingEdge), + returnValue: _FakeCssRect_2( + this, + Invocation.getter(#paddingEdge), + ), + ) as _i2.CssRect); + @override + _i2.CssRect get borderEdge => (super.noSuchMethod( + Invocation.getter(#borderEdge), + returnValue: _FakeCssRect_2( + this, + Invocation.getter(#borderEdge), + ), + ) as _i2.CssRect); + @override + _i2.CssRect get marginEdge => (super.noSuchMethod( + Invocation.getter(#marginEdge), + returnValue: _FakeCssRect_2( + this, + Invocation.getter(#marginEdge), + ), + ) as _i2.CssRect); + @override + _i3.Point get documentOffset => (super.noSuchMethod( + Invocation.getter(#documentOffset), + returnValue: _FakePoint_3( + this, + Invocation.getter(#documentOffset), + ), + ) as _i3.Point); + @override + set innerHtml(String? html) => super.noSuchMethod( + Invocation.setter( + #innerHtml, + html, + ), + returnValueForMissingStub: null, + ); + @override + String get innerText => (super.noSuchMethod( + Invocation.getter(#innerText), + returnValue: '', + ) as String); + @override + set innerText(String? value) => super.noSuchMethod( + Invocation.setter( + #innerText, + value, + ), + returnValueForMissingStub: null, + ); + @override + _i2.ElementEvents get on => (super.noSuchMethod( + Invocation.getter(#on), + returnValue: _FakeElementEvents_4( + this, + Invocation.getter(#on), + ), + ) as _i2.ElementEvents); + @override + int get offsetHeight => (super.noSuchMethod( + Invocation.getter(#offsetHeight), + returnValue: 0, + ) as int); + @override + int get offsetLeft => (super.noSuchMethod( + Invocation.getter(#offsetLeft), + returnValue: 0, + ) as int); + @override + int get offsetTop => (super.noSuchMethod( + Invocation.getter(#offsetTop), + returnValue: 0, + ) as int); + @override + int get offsetWidth => (super.noSuchMethod( + Invocation.getter(#offsetWidth), + returnValue: 0, + ) as int); + @override + int get scrollHeight => (super.noSuchMethod( + Invocation.getter(#scrollHeight), + returnValue: 0, + ) as int); + @override + int get scrollLeft => (super.noSuchMethod( + Invocation.getter(#scrollLeft), + returnValue: 0, + ) as int); + @override + set scrollLeft(int? value) => super.noSuchMethod( + Invocation.setter( + #scrollLeft, + value, + ), + returnValueForMissingStub: null, + ); + @override + int get scrollTop => (super.noSuchMethod( + Invocation.getter(#scrollTop), + returnValue: 0, + ) as int); + @override + set scrollTop(int? value) => super.noSuchMethod( + Invocation.setter( + #scrollTop, + value, + ), + returnValueForMissingStub: null, + ); + @override + int get scrollWidth => (super.noSuchMethod( + Invocation.getter(#scrollWidth), + returnValue: 0, + ) as int); + @override + String get contentEditable => (super.noSuchMethod( + Invocation.getter(#contentEditable), + returnValue: '', + ) as String); + @override + set contentEditable(String? value) => super.noSuchMethod( + Invocation.setter( + #contentEditable, + value, + ), + returnValueForMissingStub: null, + ); + @override + set dir(String? value) => super.noSuchMethod( + Invocation.setter( + #dir, + value, + ), + returnValueForMissingStub: null, + ); + @override + bool get draggable => (super.noSuchMethod( + Invocation.getter(#draggable), + returnValue: false, + ) as bool); + @override + set draggable(bool? value) => super.noSuchMethod( + Invocation.setter( + #draggable, + value, + ), + returnValueForMissingStub: null, + ); + @override + bool get hidden => (super.noSuchMethod( + Invocation.getter(#hidden), + returnValue: false, + ) as bool); + @override + set hidden(bool? value) => super.noSuchMethod( + Invocation.setter( + #hidden, + value, + ), + returnValueForMissingStub: null, + ); + @override + set inert(bool? value) => super.noSuchMethod( + Invocation.setter( + #inert, + value, + ), + returnValueForMissingStub: null, + ); + @override + set inputMode(String? value) => super.noSuchMethod( + Invocation.setter( + #inputMode, + value, + ), + returnValueForMissingStub: null, + ); + @override + set lang(String? value) => super.noSuchMethod( + Invocation.setter( + #lang, + value, + ), + returnValueForMissingStub: null, + ); + @override + set spellcheck(bool? value) => super.noSuchMethod( + Invocation.setter( + #spellcheck, + value, + ), + returnValueForMissingStub: null, + ); @override _i2.CssStyleDeclaration get style => (super.noSuchMethod( - Invocation.getter(#style), - returnValue: _FakeCssStyleDeclaration_5()) as _i2.CssStyleDeclaration); - @override - set tabIndex(int? value) => - super.noSuchMethod(Invocation.setter(#tabIndex, value), - returnValueForMissingStub: null); - @override - set title(String? value) => - super.noSuchMethod(Invocation.setter(#title, value), - returnValueForMissingStub: null); - @override - set translate(bool? value) => - super.noSuchMethod(Invocation.setter(#translate, value), - returnValueForMissingStub: null); - @override - String get className => - (super.noSuchMethod(Invocation.getter(#className), returnValue: '') - as String); - @override - set className(String? value) => - super.noSuchMethod(Invocation.setter(#className, value), - returnValueForMissingStub: null); - @override - int get clientHeight => - (super.noSuchMethod(Invocation.getter(#clientHeight), returnValue: 0) - as int); - @override - int get clientWidth => - (super.noSuchMethod(Invocation.getter(#clientWidth), returnValue: 0) - as int); - @override - String get id => - (super.noSuchMethod(Invocation.getter(#id), returnValue: '') as String); - @override - set id(String? value) => super.noSuchMethod(Invocation.setter(#id, value), - returnValueForMissingStub: null); - @override - set slot(String? value) => super.noSuchMethod(Invocation.setter(#slot, value), - returnValueForMissingStub: null); - @override - String get tagName => - (super.noSuchMethod(Invocation.getter(#tagName), returnValue: '') - as String); - @override - _i2.ElementStream<_i2.Event> get onAbort => - (super.noSuchMethod(Invocation.getter(#onAbort), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onBeforeCopy => - (super.noSuchMethod(Invocation.getter(#onBeforeCopy), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onBeforeCut => - (super.noSuchMethod(Invocation.getter(#onBeforeCut), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onBeforePaste => - (super.noSuchMethod(Invocation.getter(#onBeforePaste), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onBlur => - (super.noSuchMethod(Invocation.getter(#onBlur), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onCanPlay => - (super.noSuchMethod(Invocation.getter(#onCanPlay), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onCanPlayThrough => - (super.noSuchMethod(Invocation.getter(#onCanPlayThrough), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onChange => - (super.noSuchMethod(Invocation.getter(#onChange), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.MouseEvent> get onClick => - (super.noSuchMethod(Invocation.getter(#onClick), - returnValue: _FakeElementStream_6<_i2.MouseEvent>()) - as _i2.ElementStream<_i2.MouseEvent>); - @override - _i2.ElementStream<_i2.MouseEvent> get onContextMenu => - (super.noSuchMethod(Invocation.getter(#onContextMenu), - returnValue: _FakeElementStream_6<_i2.MouseEvent>()) - as _i2.ElementStream<_i2.MouseEvent>); - @override - _i2.ElementStream<_i2.ClipboardEvent> get onCopy => - (super.noSuchMethod(Invocation.getter(#onCopy), - returnValue: _FakeElementStream_6<_i2.ClipboardEvent>()) - as _i2.ElementStream<_i2.ClipboardEvent>); - @override - _i2.ElementStream<_i2.ClipboardEvent> get onCut => - (super.noSuchMethod(Invocation.getter(#onCut), - returnValue: _FakeElementStream_6<_i2.ClipboardEvent>()) - as _i2.ElementStream<_i2.ClipboardEvent>); - @override - _i2.ElementStream<_i2.Event> get onDoubleClick => - (super.noSuchMethod(Invocation.getter(#onDoubleClick), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.MouseEvent> get onDrag => - (super.noSuchMethod(Invocation.getter(#onDrag), - returnValue: _FakeElementStream_6<_i2.MouseEvent>()) - as _i2.ElementStream<_i2.MouseEvent>); - @override - _i2.ElementStream<_i2.MouseEvent> get onDragEnd => - (super.noSuchMethod(Invocation.getter(#onDragEnd), - returnValue: _FakeElementStream_6<_i2.MouseEvent>()) - as _i2.ElementStream<_i2.MouseEvent>); - @override - _i2.ElementStream<_i2.MouseEvent> get onDragEnter => - (super.noSuchMethod(Invocation.getter(#onDragEnter), - returnValue: _FakeElementStream_6<_i2.MouseEvent>()) - as _i2.ElementStream<_i2.MouseEvent>); - @override - _i2.ElementStream<_i2.MouseEvent> get onDragLeave => - (super.noSuchMethod(Invocation.getter(#onDragLeave), - returnValue: _FakeElementStream_6<_i2.MouseEvent>()) - as _i2.ElementStream<_i2.MouseEvent>); - @override - _i2.ElementStream<_i2.MouseEvent> get onDragOver => - (super.noSuchMethod(Invocation.getter(#onDragOver), - returnValue: _FakeElementStream_6<_i2.MouseEvent>()) - as _i2.ElementStream<_i2.MouseEvent>); - @override - _i2.ElementStream<_i2.MouseEvent> get onDragStart => - (super.noSuchMethod(Invocation.getter(#onDragStart), - returnValue: _FakeElementStream_6<_i2.MouseEvent>()) - as _i2.ElementStream<_i2.MouseEvent>); - @override - _i2.ElementStream<_i2.MouseEvent> get onDrop => - (super.noSuchMethod(Invocation.getter(#onDrop), - returnValue: _FakeElementStream_6<_i2.MouseEvent>()) - as _i2.ElementStream<_i2.MouseEvent>); - @override - _i2.ElementStream<_i2.Event> get onDurationChange => - (super.noSuchMethod(Invocation.getter(#onDurationChange), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onEmptied => - (super.noSuchMethod(Invocation.getter(#onEmptied), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onEnded => - (super.noSuchMethod(Invocation.getter(#onEnded), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onError => - (super.noSuchMethod(Invocation.getter(#onError), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onFocus => - (super.noSuchMethod(Invocation.getter(#onFocus), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onInput => - (super.noSuchMethod(Invocation.getter(#onInput), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onInvalid => - (super.noSuchMethod(Invocation.getter(#onInvalid), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.KeyboardEvent> get onKeyDown => - (super.noSuchMethod(Invocation.getter(#onKeyDown), - returnValue: _FakeElementStream_6<_i2.KeyboardEvent>()) - as _i2.ElementStream<_i2.KeyboardEvent>); - @override - _i2.ElementStream<_i2.KeyboardEvent> get onKeyPress => - (super.noSuchMethod(Invocation.getter(#onKeyPress), - returnValue: _FakeElementStream_6<_i2.KeyboardEvent>()) - as _i2.ElementStream<_i2.KeyboardEvent>); - @override - _i2.ElementStream<_i2.KeyboardEvent> get onKeyUp => - (super.noSuchMethod(Invocation.getter(#onKeyUp), - returnValue: _FakeElementStream_6<_i2.KeyboardEvent>()) - as _i2.ElementStream<_i2.KeyboardEvent>); - @override - _i2.ElementStream<_i2.Event> get onLoad => - (super.noSuchMethod(Invocation.getter(#onLoad), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onLoadedData => - (super.noSuchMethod(Invocation.getter(#onLoadedData), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onLoadedMetadata => - (super.noSuchMethod(Invocation.getter(#onLoadedMetadata), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.MouseEvent> get onMouseDown => - (super.noSuchMethod(Invocation.getter(#onMouseDown), - returnValue: _FakeElementStream_6<_i2.MouseEvent>()) - as _i2.ElementStream<_i2.MouseEvent>); - @override - _i2.ElementStream<_i2.MouseEvent> get onMouseEnter => - (super.noSuchMethod(Invocation.getter(#onMouseEnter), - returnValue: _FakeElementStream_6<_i2.MouseEvent>()) - as _i2.ElementStream<_i2.MouseEvent>); - @override - _i2.ElementStream<_i2.MouseEvent> get onMouseLeave => - (super.noSuchMethod(Invocation.getter(#onMouseLeave), - returnValue: _FakeElementStream_6<_i2.MouseEvent>()) - as _i2.ElementStream<_i2.MouseEvent>); - @override - _i2.ElementStream<_i2.MouseEvent> get onMouseMove => - (super.noSuchMethod(Invocation.getter(#onMouseMove), - returnValue: _FakeElementStream_6<_i2.MouseEvent>()) - as _i2.ElementStream<_i2.MouseEvent>); - @override - _i2.ElementStream<_i2.MouseEvent> get onMouseOut => - (super.noSuchMethod(Invocation.getter(#onMouseOut), - returnValue: _FakeElementStream_6<_i2.MouseEvent>()) - as _i2.ElementStream<_i2.MouseEvent>); - @override - _i2.ElementStream<_i2.MouseEvent> get onMouseOver => - (super.noSuchMethod(Invocation.getter(#onMouseOver), - returnValue: _FakeElementStream_6<_i2.MouseEvent>()) - as _i2.ElementStream<_i2.MouseEvent>); - @override - _i2.ElementStream<_i2.MouseEvent> get onMouseUp => - (super.noSuchMethod(Invocation.getter(#onMouseUp), - returnValue: _FakeElementStream_6<_i2.MouseEvent>()) - as _i2.ElementStream<_i2.MouseEvent>); - @override - _i2.ElementStream<_i2.WheelEvent> get onMouseWheel => - (super.noSuchMethod(Invocation.getter(#onMouseWheel), - returnValue: _FakeElementStream_6<_i2.WheelEvent>()) - as _i2.ElementStream<_i2.WheelEvent>); - @override - _i2.ElementStream<_i2.ClipboardEvent> get onPaste => - (super.noSuchMethod(Invocation.getter(#onPaste), - returnValue: _FakeElementStream_6<_i2.ClipboardEvent>()) - as _i2.ElementStream<_i2.ClipboardEvent>); - @override - _i2.ElementStream<_i2.Event> get onPause => - (super.noSuchMethod(Invocation.getter(#onPause), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onPlay => - (super.noSuchMethod(Invocation.getter(#onPlay), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onPlaying => - (super.noSuchMethod(Invocation.getter(#onPlaying), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onRateChange => - (super.noSuchMethod(Invocation.getter(#onRateChange), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onReset => - (super.noSuchMethod(Invocation.getter(#onReset), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onResize => - (super.noSuchMethod(Invocation.getter(#onResize), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onScroll => - (super.noSuchMethod(Invocation.getter(#onScroll), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onSearch => - (super.noSuchMethod(Invocation.getter(#onSearch), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onSeeked => - (super.noSuchMethod(Invocation.getter(#onSeeked), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onSeeking => - (super.noSuchMethod(Invocation.getter(#onSeeking), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onSelect => - (super.noSuchMethod(Invocation.getter(#onSelect), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onSelectStart => - (super.noSuchMethod(Invocation.getter(#onSelectStart), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onStalled => - (super.noSuchMethod(Invocation.getter(#onStalled), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onSubmit => - (super.noSuchMethod(Invocation.getter(#onSubmit), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onSuspend => - (super.noSuchMethod(Invocation.getter(#onSuspend), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onTimeUpdate => - (super.noSuchMethod(Invocation.getter(#onTimeUpdate), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.TouchEvent> get onTouchCancel => - (super.noSuchMethod(Invocation.getter(#onTouchCancel), - returnValue: _FakeElementStream_6<_i2.TouchEvent>()) - as _i2.ElementStream<_i2.TouchEvent>); - @override - _i2.ElementStream<_i2.TouchEvent> get onTouchEnd => - (super.noSuchMethod(Invocation.getter(#onTouchEnd), - returnValue: _FakeElementStream_6<_i2.TouchEvent>()) - as _i2.ElementStream<_i2.TouchEvent>); - @override - _i2.ElementStream<_i2.TouchEvent> get onTouchEnter => - (super.noSuchMethod(Invocation.getter(#onTouchEnter), - returnValue: _FakeElementStream_6<_i2.TouchEvent>()) - as _i2.ElementStream<_i2.TouchEvent>); - @override - _i2.ElementStream<_i2.TouchEvent> get onTouchLeave => - (super.noSuchMethod(Invocation.getter(#onTouchLeave), - returnValue: _FakeElementStream_6<_i2.TouchEvent>()) - as _i2.ElementStream<_i2.TouchEvent>); - @override - _i2.ElementStream<_i2.TouchEvent> get onTouchMove => - (super.noSuchMethod(Invocation.getter(#onTouchMove), - returnValue: _FakeElementStream_6<_i2.TouchEvent>()) - as _i2.ElementStream<_i2.TouchEvent>); - @override - _i2.ElementStream<_i2.TouchEvent> get onTouchStart => - (super.noSuchMethod(Invocation.getter(#onTouchStart), - returnValue: _FakeElementStream_6<_i2.TouchEvent>()) - as _i2.ElementStream<_i2.TouchEvent>); + Invocation.getter(#style), + returnValue: _FakeCssStyleDeclaration_5( + this, + Invocation.getter(#style), + ), + ) as _i2.CssStyleDeclaration); + @override + set tabIndex(int? value) => super.noSuchMethod( + Invocation.setter( + #tabIndex, + value, + ), + returnValueForMissingStub: null, + ); + @override + set title(String? value) => super.noSuchMethod( + Invocation.setter( + #title, + value, + ), + returnValueForMissingStub: null, + ); + @override + set translate(bool? value) => super.noSuchMethod( + Invocation.setter( + #translate, + value, + ), + returnValueForMissingStub: null, + ); + @override + String get className => (super.noSuchMethod( + Invocation.getter(#className), + returnValue: '', + ) as String); + @override + set className(String? value) => super.noSuchMethod( + Invocation.setter( + #className, + value, + ), + returnValueForMissingStub: null, + ); + @override + int get clientHeight => (super.noSuchMethod( + Invocation.getter(#clientHeight), + returnValue: 0, + ) as int); + @override + int get clientWidth => (super.noSuchMethod( + Invocation.getter(#clientWidth), + returnValue: 0, + ) as int); + @override + String get id => (super.noSuchMethod( + Invocation.getter(#id), + returnValue: '', + ) as String); + @override + set id(String? value) => super.noSuchMethod( + Invocation.setter( + #id, + value, + ), + returnValueForMissingStub: null, + ); + @override + set slot(String? value) => super.noSuchMethod( + Invocation.setter( + #slot, + value, + ), + returnValueForMissingStub: null, + ); + @override + String get tagName => (super.noSuchMethod( + Invocation.getter(#tagName), + returnValue: '', + ) as String); + @override + _i2.ElementStream<_i2.Event> get onAbort => (super.noSuchMethod( + Invocation.getter(#onAbort), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onAbort), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onBeforeCopy => (super.noSuchMethod( + Invocation.getter(#onBeforeCopy), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onBeforeCopy), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onBeforeCut => (super.noSuchMethod( + Invocation.getter(#onBeforeCut), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onBeforeCut), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onBeforePaste => (super.noSuchMethod( + Invocation.getter(#onBeforePaste), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onBeforePaste), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onBlur => (super.noSuchMethod( + Invocation.getter(#onBlur), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onBlur), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onCanPlay => (super.noSuchMethod( + Invocation.getter(#onCanPlay), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onCanPlay), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onCanPlayThrough => (super.noSuchMethod( + Invocation.getter(#onCanPlayThrough), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onCanPlayThrough), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onChange => (super.noSuchMethod( + Invocation.getter(#onChange), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onChange), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.MouseEvent> get onClick => (super.noSuchMethod( + Invocation.getter(#onClick), + returnValue: _FakeElementStream_6<_i2.MouseEvent>( + this, + Invocation.getter(#onClick), + ), + ) as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onContextMenu => (super.noSuchMethod( + Invocation.getter(#onContextMenu), + returnValue: _FakeElementStream_6<_i2.MouseEvent>( + this, + Invocation.getter(#onContextMenu), + ), + ) as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.ClipboardEvent> get onCopy => (super.noSuchMethod( + Invocation.getter(#onCopy), + returnValue: _FakeElementStream_6<_i2.ClipboardEvent>( + this, + Invocation.getter(#onCopy), + ), + ) as _i2.ElementStream<_i2.ClipboardEvent>); + @override + _i2.ElementStream<_i2.ClipboardEvent> get onCut => (super.noSuchMethod( + Invocation.getter(#onCut), + returnValue: _FakeElementStream_6<_i2.ClipboardEvent>( + this, + Invocation.getter(#onCut), + ), + ) as _i2.ElementStream<_i2.ClipboardEvent>); + @override + _i2.ElementStream<_i2.Event> get onDoubleClick => (super.noSuchMethod( + Invocation.getter(#onDoubleClick), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onDoubleClick), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.MouseEvent> get onDrag => (super.noSuchMethod( + Invocation.getter(#onDrag), + returnValue: _FakeElementStream_6<_i2.MouseEvent>( + this, + Invocation.getter(#onDrag), + ), + ) as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onDragEnd => (super.noSuchMethod( + Invocation.getter(#onDragEnd), + returnValue: _FakeElementStream_6<_i2.MouseEvent>( + this, + Invocation.getter(#onDragEnd), + ), + ) as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onDragEnter => (super.noSuchMethod( + Invocation.getter(#onDragEnter), + returnValue: _FakeElementStream_6<_i2.MouseEvent>( + this, + Invocation.getter(#onDragEnter), + ), + ) as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onDragLeave => (super.noSuchMethod( + Invocation.getter(#onDragLeave), + returnValue: _FakeElementStream_6<_i2.MouseEvent>( + this, + Invocation.getter(#onDragLeave), + ), + ) as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onDragOver => (super.noSuchMethod( + Invocation.getter(#onDragOver), + returnValue: _FakeElementStream_6<_i2.MouseEvent>( + this, + Invocation.getter(#onDragOver), + ), + ) as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onDragStart => (super.noSuchMethod( + Invocation.getter(#onDragStart), + returnValue: _FakeElementStream_6<_i2.MouseEvent>( + this, + Invocation.getter(#onDragStart), + ), + ) as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onDrop => (super.noSuchMethod( + Invocation.getter(#onDrop), + returnValue: _FakeElementStream_6<_i2.MouseEvent>( + this, + Invocation.getter(#onDrop), + ), + ) as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.Event> get onDurationChange => (super.noSuchMethod( + Invocation.getter(#onDurationChange), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onDurationChange), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onEmptied => (super.noSuchMethod( + Invocation.getter(#onEmptied), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onEmptied), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onEnded => (super.noSuchMethod( + Invocation.getter(#onEnded), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onEnded), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onError => (super.noSuchMethod( + Invocation.getter(#onError), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onError), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onFocus => (super.noSuchMethod( + Invocation.getter(#onFocus), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onFocus), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onInput => (super.noSuchMethod( + Invocation.getter(#onInput), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onInput), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onInvalid => (super.noSuchMethod( + Invocation.getter(#onInvalid), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onInvalid), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.KeyboardEvent> get onKeyDown => (super.noSuchMethod( + Invocation.getter(#onKeyDown), + returnValue: _FakeElementStream_6<_i2.KeyboardEvent>( + this, + Invocation.getter(#onKeyDown), + ), + ) as _i2.ElementStream<_i2.KeyboardEvent>); + @override + _i2.ElementStream<_i2.KeyboardEvent> get onKeyPress => (super.noSuchMethod( + Invocation.getter(#onKeyPress), + returnValue: _FakeElementStream_6<_i2.KeyboardEvent>( + this, + Invocation.getter(#onKeyPress), + ), + ) as _i2.ElementStream<_i2.KeyboardEvent>); + @override + _i2.ElementStream<_i2.KeyboardEvent> get onKeyUp => (super.noSuchMethod( + Invocation.getter(#onKeyUp), + returnValue: _FakeElementStream_6<_i2.KeyboardEvent>( + this, + Invocation.getter(#onKeyUp), + ), + ) as _i2.ElementStream<_i2.KeyboardEvent>); + @override + _i2.ElementStream<_i2.Event> get onLoad => (super.noSuchMethod( + Invocation.getter(#onLoad), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onLoad), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onLoadedData => (super.noSuchMethod( + Invocation.getter(#onLoadedData), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onLoadedData), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onLoadedMetadata => (super.noSuchMethod( + Invocation.getter(#onLoadedMetadata), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onLoadedMetadata), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.MouseEvent> get onMouseDown => (super.noSuchMethod( + Invocation.getter(#onMouseDown), + returnValue: _FakeElementStream_6<_i2.MouseEvent>( + this, + Invocation.getter(#onMouseDown), + ), + ) as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onMouseEnter => (super.noSuchMethod( + Invocation.getter(#onMouseEnter), + returnValue: _FakeElementStream_6<_i2.MouseEvent>( + this, + Invocation.getter(#onMouseEnter), + ), + ) as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onMouseLeave => (super.noSuchMethod( + Invocation.getter(#onMouseLeave), + returnValue: _FakeElementStream_6<_i2.MouseEvent>( + this, + Invocation.getter(#onMouseLeave), + ), + ) as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onMouseMove => (super.noSuchMethod( + Invocation.getter(#onMouseMove), + returnValue: _FakeElementStream_6<_i2.MouseEvent>( + this, + Invocation.getter(#onMouseMove), + ), + ) as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onMouseOut => (super.noSuchMethod( + Invocation.getter(#onMouseOut), + returnValue: _FakeElementStream_6<_i2.MouseEvent>( + this, + Invocation.getter(#onMouseOut), + ), + ) as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onMouseOver => (super.noSuchMethod( + Invocation.getter(#onMouseOver), + returnValue: _FakeElementStream_6<_i2.MouseEvent>( + this, + Invocation.getter(#onMouseOver), + ), + ) as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.MouseEvent> get onMouseUp => (super.noSuchMethod( + Invocation.getter(#onMouseUp), + returnValue: _FakeElementStream_6<_i2.MouseEvent>( + this, + Invocation.getter(#onMouseUp), + ), + ) as _i2.ElementStream<_i2.MouseEvent>); + @override + _i2.ElementStream<_i2.WheelEvent> get onMouseWheel => (super.noSuchMethod( + Invocation.getter(#onMouseWheel), + returnValue: _FakeElementStream_6<_i2.WheelEvent>( + this, + Invocation.getter(#onMouseWheel), + ), + ) as _i2.ElementStream<_i2.WheelEvent>); + @override + _i2.ElementStream<_i2.ClipboardEvent> get onPaste => (super.noSuchMethod( + Invocation.getter(#onPaste), + returnValue: _FakeElementStream_6<_i2.ClipboardEvent>( + this, + Invocation.getter(#onPaste), + ), + ) as _i2.ElementStream<_i2.ClipboardEvent>); + @override + _i2.ElementStream<_i2.Event> get onPause => (super.noSuchMethod( + Invocation.getter(#onPause), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onPause), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onPlay => (super.noSuchMethod( + Invocation.getter(#onPlay), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onPlay), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onPlaying => (super.noSuchMethod( + Invocation.getter(#onPlaying), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onPlaying), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onRateChange => (super.noSuchMethod( + Invocation.getter(#onRateChange), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onRateChange), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onReset => (super.noSuchMethod( + Invocation.getter(#onReset), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onReset), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onResize => (super.noSuchMethod( + Invocation.getter(#onResize), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onResize), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onScroll => (super.noSuchMethod( + Invocation.getter(#onScroll), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onScroll), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onSearch => (super.noSuchMethod( + Invocation.getter(#onSearch), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onSearch), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onSeeked => (super.noSuchMethod( + Invocation.getter(#onSeeked), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onSeeked), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onSeeking => (super.noSuchMethod( + Invocation.getter(#onSeeking), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onSeeking), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onSelect => (super.noSuchMethod( + Invocation.getter(#onSelect), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onSelect), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onSelectStart => (super.noSuchMethod( + Invocation.getter(#onSelectStart), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onSelectStart), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onStalled => (super.noSuchMethod( + Invocation.getter(#onStalled), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onStalled), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onSubmit => (super.noSuchMethod( + Invocation.getter(#onSubmit), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onSubmit), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onSuspend => (super.noSuchMethod( + Invocation.getter(#onSuspend), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onSuspend), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onTimeUpdate => (super.noSuchMethod( + Invocation.getter(#onTimeUpdate), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onTimeUpdate), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.TouchEvent> get onTouchCancel => (super.noSuchMethod( + Invocation.getter(#onTouchCancel), + returnValue: _FakeElementStream_6<_i2.TouchEvent>( + this, + Invocation.getter(#onTouchCancel), + ), + ) as _i2.ElementStream<_i2.TouchEvent>); + @override + _i2.ElementStream<_i2.TouchEvent> get onTouchEnd => (super.noSuchMethod( + Invocation.getter(#onTouchEnd), + returnValue: _FakeElementStream_6<_i2.TouchEvent>( + this, + Invocation.getter(#onTouchEnd), + ), + ) as _i2.ElementStream<_i2.TouchEvent>); + @override + _i2.ElementStream<_i2.TouchEvent> get onTouchEnter => (super.noSuchMethod( + Invocation.getter(#onTouchEnter), + returnValue: _FakeElementStream_6<_i2.TouchEvent>( + this, + Invocation.getter(#onTouchEnter), + ), + ) as _i2.ElementStream<_i2.TouchEvent>); + @override + _i2.ElementStream<_i2.TouchEvent> get onTouchLeave => (super.noSuchMethod( + Invocation.getter(#onTouchLeave), + returnValue: _FakeElementStream_6<_i2.TouchEvent>( + this, + Invocation.getter(#onTouchLeave), + ), + ) as _i2.ElementStream<_i2.TouchEvent>); + @override + _i2.ElementStream<_i2.TouchEvent> get onTouchMove => (super.noSuchMethod( + Invocation.getter(#onTouchMove), + returnValue: _FakeElementStream_6<_i2.TouchEvent>( + this, + Invocation.getter(#onTouchMove), + ), + ) as _i2.ElementStream<_i2.TouchEvent>); + @override + _i2.ElementStream<_i2.TouchEvent> get onTouchStart => (super.noSuchMethod( + Invocation.getter(#onTouchStart), + returnValue: _FakeElementStream_6<_i2.TouchEvent>( + this, + Invocation.getter(#onTouchStart), + ), + ) as _i2.ElementStream<_i2.TouchEvent>); @override _i2.ElementStream<_i2.TransitionEvent> get onTransitionEnd => - (super.noSuchMethod(Invocation.getter(#onTransitionEnd), - returnValue: _FakeElementStream_6<_i2.TransitionEvent>()) - as _i2.ElementStream<_i2.TransitionEvent>); - @override - _i2.ElementStream<_i2.Event> get onVolumeChange => - (super.noSuchMethod(Invocation.getter(#onVolumeChange), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onWaiting => - (super.noSuchMethod(Invocation.getter(#onWaiting), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onFullscreenChange => - (super.noSuchMethod(Invocation.getter(#onFullscreenChange), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.Event> get onFullscreenError => - (super.noSuchMethod(Invocation.getter(#onFullscreenError), - returnValue: _FakeElementStream_6<_i2.Event>()) - as _i2.ElementStream<_i2.Event>); - @override - _i2.ElementStream<_i2.WheelEvent> get onWheel => - (super.noSuchMethod(Invocation.getter(#onWheel), - returnValue: _FakeElementStream_6<_i2.WheelEvent>()) - as _i2.ElementStream<_i2.WheelEvent>); - @override - List<_i2.Node> get nodes => - (super.noSuchMethod(Invocation.getter(#nodes), returnValue: <_i2.Node>[]) - as List<_i2.Node>); - @override - set nodes(Iterable<_i2.Node>? value) => - super.noSuchMethod(Invocation.setter(#nodes, value), - returnValueForMissingStub: null); - @override - List<_i2.Node> get childNodes => - (super.noSuchMethod(Invocation.getter(#childNodes), - returnValue: <_i2.Node>[]) as List<_i2.Node>); - @override - int get nodeType => - (super.noSuchMethod(Invocation.getter(#nodeType), returnValue: 0) as int); - @override - set text(String? value) => super.noSuchMethod(Invocation.setter(#text, value), - returnValueForMissingStub: null); - @override - String? getAttribute(String? name) => - (super.noSuchMethod(Invocation.method(#getAttribute, [name])) as String?); - @override - String? getAttributeNS(String? namespaceURI, String? name) => (super.noSuchMethod( - Invocation.method(#getAttributeNS, [namespaceURI, name])) as String?); - @override - bool hasAttribute(String? name) => - (super.noSuchMethod(Invocation.method(#hasAttribute, [name]), - returnValue: false) as bool); - @override - bool hasAttributeNS(String? namespaceURI, String? name) => (super - .noSuchMethod(Invocation.method(#hasAttributeNS, [namespaceURI, name]), - returnValue: false) as bool); - @override - void removeAttribute(String? name) => - super.noSuchMethod(Invocation.method(#removeAttribute, [name]), - returnValueForMissingStub: null); - @override - void removeAttributeNS(String? namespaceURI, String? name) => super - .noSuchMethod(Invocation.method(#removeAttributeNS, [namespaceURI, name]), - returnValueForMissingStub: null); - @override - void setAttribute(String? name, Object? value) => - super.noSuchMethod(Invocation.method(#setAttribute, [name, value]), - returnValueForMissingStub: null); - @override - void setAttributeNS(String? namespaceURI, String? name, Object? value) => + Invocation.getter(#onTransitionEnd), + returnValue: _FakeElementStream_6<_i2.TransitionEvent>( + this, + Invocation.getter(#onTransitionEnd), + ), + ) as _i2.ElementStream<_i2.TransitionEvent>); + @override + _i2.ElementStream<_i2.Event> get onVolumeChange => (super.noSuchMethod( + Invocation.getter(#onVolumeChange), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onVolumeChange), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onWaiting => (super.noSuchMethod( + Invocation.getter(#onWaiting), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onWaiting), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onFullscreenChange => (super.noSuchMethod( + Invocation.getter(#onFullscreenChange), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onFullscreenChange), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.Event> get onFullscreenError => (super.noSuchMethod( + Invocation.getter(#onFullscreenError), + returnValue: _FakeElementStream_6<_i2.Event>( + this, + Invocation.getter(#onFullscreenError), + ), + ) as _i2.ElementStream<_i2.Event>); + @override + _i2.ElementStream<_i2.WheelEvent> get onWheel => (super.noSuchMethod( + Invocation.getter(#onWheel), + returnValue: _FakeElementStream_6<_i2.WheelEvent>( + this, + Invocation.getter(#onWheel), + ), + ) as _i2.ElementStream<_i2.WheelEvent>); + @override + List<_i2.Node> get nodes => (super.noSuchMethod( + Invocation.getter(#nodes), + returnValue: <_i2.Node>[], + ) as List<_i2.Node>); + @override + set nodes(Iterable<_i2.Node>? value) => super.noSuchMethod( + Invocation.setter( + #nodes, + value, + ), + returnValueForMissingStub: null, + ); + @override + List<_i2.Node> get childNodes => (super.noSuchMethod( + Invocation.getter(#childNodes), + returnValue: <_i2.Node>[], + ) as List<_i2.Node>); + @override + int get nodeType => (super.noSuchMethod( + Invocation.getter(#nodeType), + returnValue: 0, + ) as int); + @override + set text(String? value) => super.noSuchMethod( + Invocation.setter( + #text, + value, + ), + returnValueForMissingStub: null, + ); + @override + String? getAttribute(String? name) => (super.noSuchMethod(Invocation.method( + #getAttribute, + [name], + )) as String?); + @override + String? getAttributeNS( + String? namespaceURI, + String? name, + ) => + (super.noSuchMethod(Invocation.method( + #getAttributeNS, + [ + namespaceURI, + name, + ], + )) as String?); + @override + bool hasAttribute(String? name) => (super.noSuchMethod( + Invocation.method( + #hasAttribute, + [name], + ), + returnValue: false, + ) as bool); + @override + bool hasAttributeNS( + String? namespaceURI, + String? name, + ) => + (super.noSuchMethod( + Invocation.method( + #hasAttributeNS, + [ + namespaceURI, + name, + ], + ), + returnValue: false, + ) as bool); + @override + void removeAttribute(String? name) => super.noSuchMethod( + Invocation.method( + #removeAttribute, + [name], + ), + returnValueForMissingStub: null, + ); + @override + void removeAttributeNS( + String? namespaceURI, + String? name, + ) => + super.noSuchMethod( + Invocation.method( + #removeAttributeNS, + [ + namespaceURI, + name, + ], + ), + returnValueForMissingStub: null, + ); + @override + void setAttribute( + String? name, + Object? value, + ) => + super.noSuchMethod( + Invocation.method( + #setAttribute, + [ + name, + value, + ], + ), + returnValueForMissingStub: null, + ); + @override + void setAttributeNS( + String? namespaceURI, + String? name, + Object? value, + ) => super.noSuchMethod( - Invocation.method(#setAttributeNS, [namespaceURI, name, value]), - returnValueForMissingStub: null); + Invocation.method( + #setAttributeNS, + [ + namespaceURI, + name, + value, + ], + ), + returnValueForMissingStub: null, + ); @override _i2.ElementList querySelectorAll( String? selectors) => - (super.noSuchMethod(Invocation.method(#querySelectorAll, [selectors]), - returnValue: _FakeElementList_7()) as _i2.ElementList); + (super.noSuchMethod( + Invocation.method( + #querySelectorAll, + [selectors], + ), + returnValue: _FakeElementList_7( + this, + Invocation.method( + #querySelectorAll, + [selectors], + ), + ), + ) as _i2.ElementList); @override _i6.Future<_i2.ScrollState> setApplyScroll(String? nativeScrollBehavior) => (super.noSuchMethod( - Invocation.method(#setApplyScroll, [nativeScrollBehavior]), - returnValue: Future<_i2.ScrollState>.value(_FakeScrollState_8())) - as _i6.Future<_i2.ScrollState>); + Invocation.method( + #setApplyScroll, + [nativeScrollBehavior], + ), + returnValue: _i6.Future<_i2.ScrollState>.value(_FakeScrollState_8( + this, + Invocation.method( + #setApplyScroll, + [nativeScrollBehavior], + ), + )), + ) as _i6.Future<_i2.ScrollState>); @override _i6.Future<_i2.ScrollState> setDistributeScroll( String? nativeScrollBehavior) => (super.noSuchMethod( - Invocation.method(#setDistributeScroll, [nativeScrollBehavior]), - returnValue: Future<_i2.ScrollState>.value(_FakeScrollState_8())) - as _i6.Future<_i2.ScrollState>); + Invocation.method( + #setDistributeScroll, + [nativeScrollBehavior], + ), + returnValue: _i6.Future<_i2.ScrollState>.value(_FakeScrollState_8( + this, + Invocation.method( + #setDistributeScroll, + [nativeScrollBehavior], + ), + )), + ) as _i6.Future<_i2.ScrollState>); @override - Map getNamespacedAttributes(String? namespace) => (super - .noSuchMethod(Invocation.method(#getNamespacedAttributes, [namespace]), - returnValue: {}) as Map); + Map getNamespacedAttributes(String? namespace) => + (super.noSuchMethod( + Invocation.method( + #getNamespacedAttributes, + [namespace], + ), + returnValue: {}, + ) as Map); @override _i2.CssStyleDeclaration getComputedStyle([String? pseudoElement]) => - (super.noSuchMethod(Invocation.method(#getComputedStyle, [pseudoElement]), - returnValue: _FakeCssStyleDeclaration_5()) - as _i2.CssStyleDeclaration); - @override - void appendText(String? text) => - super.noSuchMethod(Invocation.method(#appendText, [text]), - returnValueForMissingStub: null); - @override - void appendHtml(String? text, - {_i2.NodeValidator? validator, - _i2.NodeTreeSanitizer? treeSanitizer}) => + (super.noSuchMethod( + Invocation.method( + #getComputedStyle, + [pseudoElement], + ), + returnValue: _FakeCssStyleDeclaration_5( + this, + Invocation.method( + #getComputedStyle, + [pseudoElement], + ), + ), + ) as _i2.CssStyleDeclaration); + @override + void appendText(String? text) => super.noSuchMethod( + Invocation.method( + #appendText, + [text], + ), + returnValueForMissingStub: null, + ); + @override + void appendHtml( + String? text, { + _i2.NodeValidator? validator, + _i2.NodeTreeSanitizer? treeSanitizer, + }) => super.noSuchMethod( - Invocation.method(#appendHtml, [text], - {#validator: validator, #treeSanitizer: treeSanitizer}), - returnValueForMissingStub: null); - @override - void attached() => super.noSuchMethod(Invocation.method(#attached, []), - returnValueForMissingStub: null); - @override - void detached() => super.noSuchMethod(Invocation.method(#detached, []), - returnValueForMissingStub: null); - @override - void enteredView() => super.noSuchMethod(Invocation.method(#enteredView, []), - returnValueForMissingStub: null); - @override - List<_i3.Rectangle> getClientRects() => - (super.noSuchMethod(Invocation.method(#getClientRects, []), - returnValue: <_i3.Rectangle>[]) as List<_i3.Rectangle>); - @override - void leftView() => super.noSuchMethod(Invocation.method(#leftView, []), - returnValueForMissingStub: null); - @override - _i2.Animation animate(Iterable>? frames, - [dynamic timing]) => - (super.noSuchMethod(Invocation.method(#animate, [frames, timing]), - returnValue: _FakeAnimation_9()) as _i2.Animation); - @override - void attributeChanged(String? name, String? oldValue, String? newValue) => + Invocation.method( + #appendHtml, + [text], + { + #validator: validator, + #treeSanitizer: treeSanitizer, + }, + ), + returnValueForMissingStub: null, + ); + @override + void attached() => super.noSuchMethod( + Invocation.method( + #attached, + [], + ), + returnValueForMissingStub: null, + ); + @override + void detached() => super.noSuchMethod( + Invocation.method( + #detached, + [], + ), + returnValueForMissingStub: null, + ); + @override + void enteredView() => super.noSuchMethod( + Invocation.method( + #enteredView, + [], + ), + returnValueForMissingStub: null, + ); + @override + List<_i3.Rectangle> getClientRects() => (super.noSuchMethod( + Invocation.method( + #getClientRects, + [], + ), + returnValue: <_i3.Rectangle>[], + ) as List<_i3.Rectangle>); + @override + void leftView() => super.noSuchMethod( + Invocation.method( + #leftView, + [], + ), + returnValueForMissingStub: null, + ); + @override + _i2.Animation animate( + Iterable>? frames, [ + dynamic timing, + ]) => + (super.noSuchMethod( + Invocation.method( + #animate, + [ + frames, + timing, + ], + ), + returnValue: _FakeAnimation_9( + this, + Invocation.method( + #animate, + [ + frames, + timing, + ], + ), + ), + ) as _i2.Animation); + @override + void attributeChanged( + String? name, + String? oldValue, + String? newValue, + ) => super.noSuchMethod( - Invocation.method(#attributeChanged, [name, oldValue, newValue]), - returnValueForMissingStub: null); - @override - String toString() => super.toString(); - @override - void scrollIntoView([_i2.ScrollAlignment? alignment]) => - super.noSuchMethod(Invocation.method(#scrollIntoView, [alignment]), - returnValueForMissingStub: null); - @override - void insertAdjacentText(String? where, String? text) => - super.noSuchMethod(Invocation.method(#insertAdjacentText, [where, text]), - returnValueForMissingStub: null); - @override - void insertAdjacentHtml(String? where, String? html, - {_i2.NodeValidator? validator, - _i2.NodeTreeSanitizer? treeSanitizer}) => + Invocation.method( + #attributeChanged, + [ + name, + oldValue, + newValue, + ], + ), + returnValueForMissingStub: null, + ); + @override + void scrollIntoView([_i2.ScrollAlignment? alignment]) => super.noSuchMethod( + Invocation.method( + #scrollIntoView, + [alignment], + ), + returnValueForMissingStub: null, + ); + @override + void insertAdjacentText( + String? where, + String? text, + ) => super.noSuchMethod( - Invocation.method(#insertAdjacentHtml, [where, html], - {#validator: validator, #treeSanitizer: treeSanitizer}), - returnValueForMissingStub: null); - @override - _i2.Element insertAdjacentElement(String? where, _i2.Element? element) => + Invocation.method( + #insertAdjacentText, + [ + where, + text, + ], + ), + returnValueForMissingStub: null, + ); + @override + void insertAdjacentHtml( + String? where, + String? html, { + _i2.NodeValidator? validator, + _i2.NodeTreeSanitizer? treeSanitizer, + }) => + super.noSuchMethod( + Invocation.method( + #insertAdjacentHtml, + [ + where, + html, + ], + { + #validator: validator, + #treeSanitizer: treeSanitizer, + }, + ), + returnValueForMissingStub: null, + ); + @override + _i2.Element insertAdjacentElement( + String? where, + _i2.Element? element, + ) => (super.noSuchMethod( - Invocation.method(#insertAdjacentElement, [where, element]), - returnValue: _FakeElement_10()) as _i2.Element); - @override - bool matches(String? selectors) => - (super.noSuchMethod(Invocation.method(#matches, [selectors]), - returnValue: false) as bool); - @override - bool matchesWithAncestors(String? selectors) => - (super.noSuchMethod(Invocation.method(#matchesWithAncestors, [selectors]), - returnValue: false) as bool); - @override - _i2.ShadowRoot createShadowRoot() => - (super.noSuchMethod(Invocation.method(#createShadowRoot, []), - returnValue: _FakeShadowRoot_11()) as _i2.ShadowRoot); - @override - _i3.Point offsetTo(_i2.Element? parent) => - (super.noSuchMethod(Invocation.method(#offsetTo, [parent]), - returnValue: _FakePoint_3()) as _i3.Point); - @override - _i2.DocumentFragment createFragment(String? html, - {_i2.NodeValidator? validator, - _i2.NodeTreeSanitizer? treeSanitizer}) => + Invocation.method( + #insertAdjacentElement, + [ + where, + element, + ], + ), + returnValue: _FakeElement_10( + this, + Invocation.method( + #insertAdjacentElement, + [ + where, + element, + ], + ), + ), + ) as _i2.Element); + @override + bool matches(String? selectors) => (super.noSuchMethod( + Invocation.method( + #matches, + [selectors], + ), + returnValue: false, + ) as bool); + @override + bool matchesWithAncestors(String? selectors) => (super.noSuchMethod( + Invocation.method( + #matchesWithAncestors, + [selectors], + ), + returnValue: false, + ) as bool); + @override + _i2.ShadowRoot createShadowRoot() => (super.noSuchMethod( + Invocation.method( + #createShadowRoot, + [], + ), + returnValue: _FakeShadowRoot_11( + this, + Invocation.method( + #createShadowRoot, + [], + ), + ), + ) as _i2.ShadowRoot); + @override + _i3.Point offsetTo(_i2.Element? parent) => (super.noSuchMethod( + Invocation.method( + #offsetTo, + [parent], + ), + returnValue: _FakePoint_3( + this, + Invocation.method( + #offsetTo, + [parent], + ), + ), + ) as _i3.Point); + @override + _i2.DocumentFragment createFragment( + String? html, { + _i2.NodeValidator? validator, + _i2.NodeTreeSanitizer? treeSanitizer, + }) => (super.noSuchMethod( - Invocation.method(#createFragment, [html], - {#validator: validator, #treeSanitizer: treeSanitizer}), - returnValue: _FakeDocumentFragment_12()) as _i2.DocumentFragment); - @override - void setInnerHtml(String? html, - {_i2.NodeValidator? validator, - _i2.NodeTreeSanitizer? treeSanitizer}) => + Invocation.method( + #createFragment, + [html], + { + #validator: validator, + #treeSanitizer: treeSanitizer, + }, + ), + returnValue: _FakeDocumentFragment_12( + this, + Invocation.method( + #createFragment, + [html], + { + #validator: validator, + #treeSanitizer: treeSanitizer, + }, + ), + ), + ) as _i2.DocumentFragment); + @override + void setInnerHtml( + String? html, { + _i2.NodeValidator? validator, + _i2.NodeTreeSanitizer? treeSanitizer, + }) => super.noSuchMethod( - Invocation.method(#setInnerHtml, [html], - {#validator: validator, #treeSanitizer: treeSanitizer}), - returnValueForMissingStub: null); - @override - void blur() => super.noSuchMethod(Invocation.method(#blur, []), - returnValueForMissingStub: null); - @override - void click() => super.noSuchMethod(Invocation.method(#click, []), - returnValueForMissingStub: null); - @override - void focus() => super.noSuchMethod(Invocation.method(#focus, []), - returnValueForMissingStub: null); + Invocation.method( + #setInnerHtml, + [html], + { + #validator: validator, + #treeSanitizer: treeSanitizer, + }, + ), + returnValueForMissingStub: null, + ); + @override + _i6.Future requestFullscreen([Map? options]) => + (super.noSuchMethod( + Invocation.method( + #requestFullscreen, + [options], + ), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); + @override + void blur() => super.noSuchMethod( + Invocation.method( + #blur, + [], + ), + returnValueForMissingStub: null, + ); + @override + void click() => super.noSuchMethod( + Invocation.method( + #click, + [], + ), + returnValueForMissingStub: null, + ); + @override + void focus() => super.noSuchMethod( + Invocation.method( + #focus, + [], + ), + returnValueForMissingStub: null, + ); @override _i2.ShadowRoot attachShadow(Map? shadowRootInitDict) => (super.noSuchMethod( - Invocation.method(#attachShadow, [shadowRootInitDict]), - returnValue: _FakeShadowRoot_11()) as _i2.ShadowRoot); + Invocation.method( + #attachShadow, + [shadowRootInitDict], + ), + returnValue: _FakeShadowRoot_11( + this, + Invocation.method( + #attachShadow, + [shadowRootInitDict], + ), + ), + ) as _i2.ShadowRoot); @override _i2.Element? closest(String? selectors) => - (super.noSuchMethod(Invocation.method(#closest, [selectors])) - as _i2.Element?); - @override - List<_i2.Animation> getAnimations() => - (super.noSuchMethod(Invocation.method(#getAnimations, []), - returnValue: <_i2.Animation>[]) as List<_i2.Animation>); - @override - List getAttributeNames() => - (super.noSuchMethod(Invocation.method(#getAttributeNames, []), - returnValue: []) as List); - @override - _i3.Rectangle getBoundingClientRect() => - (super.noSuchMethod(Invocation.method(#getBoundingClientRect, []), - returnValue: _FakeRectangle_1()) as _i3.Rectangle); - @override - List<_i2.Node> getDestinationInsertionPoints() => - (super.noSuchMethod(Invocation.method(#getDestinationInsertionPoints, []), - returnValue: <_i2.Node>[]) as List<_i2.Node>); - @override - List<_i2.Node> getElementsByClassName(String? classNames) => (super - .noSuchMethod(Invocation.method(#getElementsByClassName, [classNames]), - returnValue: <_i2.Node>[]) as List<_i2.Node>); - @override - bool hasPointerCapture(int? pointerId) => - (super.noSuchMethod(Invocation.method(#hasPointerCapture, [pointerId]), - returnValue: false) as bool); - @override - void releasePointerCapture(int? pointerId) => - super.noSuchMethod(Invocation.method(#releasePointerCapture, [pointerId]), - returnValueForMissingStub: null); - @override - void requestPointerLock() => - super.noSuchMethod(Invocation.method(#requestPointerLock, []), - returnValueForMissingStub: null); - @override - void scroll([dynamic options_OR_x, num? y]) => - super.noSuchMethod(Invocation.method(#scroll, [options_OR_x, y]), - returnValueForMissingStub: null); - @override - void scrollBy([dynamic options_OR_x, num? y]) => - super.noSuchMethod(Invocation.method(#scrollBy, [options_OR_x, y]), - returnValueForMissingStub: null); - @override - void scrollTo([dynamic options_OR_x, num? y]) => - super.noSuchMethod(Invocation.method(#scrollTo, [options_OR_x, y]), - returnValueForMissingStub: null); - @override - void setPointerCapture(int? pointerId) => - super.noSuchMethod(Invocation.method(#setPointerCapture, [pointerId]), - returnValueForMissingStub: null); - // TODO(ditman): Undo this manual change when the return type change to - // Future has propagated to stable. - /*@override - void requestFullscreen() => - super.noSuchMethod(Invocation.method(#requestFullscreen, []), - returnValueForMissingStub: null);*/ - @override - void after(Object? nodes) => - super.noSuchMethod(Invocation.method(#after, [nodes]), - returnValueForMissingStub: null); - @override - void before(Object? nodes) => - super.noSuchMethod(Invocation.method(#before, [nodes]), - returnValueForMissingStub: null); + (super.noSuchMethod(Invocation.method( + #closest, + [selectors], + )) as _i2.Element?); + @override + List<_i2.Animation> getAnimations() => (super.noSuchMethod( + Invocation.method( + #getAnimations, + [], + ), + returnValue: <_i2.Animation>[], + ) as List<_i2.Animation>); + @override + List getAttributeNames() => (super.noSuchMethod( + Invocation.method( + #getAttributeNames, + [], + ), + returnValue: [], + ) as List); + @override + _i3.Rectangle getBoundingClientRect() => (super.noSuchMethod( + Invocation.method( + #getBoundingClientRect, + [], + ), + returnValue: _FakeRectangle_1( + this, + Invocation.method( + #getBoundingClientRect, + [], + ), + ), + ) as _i3.Rectangle); + @override + List<_i2.Node> getDestinationInsertionPoints() => (super.noSuchMethod( + Invocation.method( + #getDestinationInsertionPoints, + [], + ), + returnValue: <_i2.Node>[], + ) as List<_i2.Node>); + @override + List<_i2.Node> getElementsByClassName(String? classNames) => + (super.noSuchMethod( + Invocation.method( + #getElementsByClassName, + [classNames], + ), + returnValue: <_i2.Node>[], + ) as List<_i2.Node>); + @override + bool hasPointerCapture(int? pointerId) => (super.noSuchMethod( + Invocation.method( + #hasPointerCapture, + [pointerId], + ), + returnValue: false, + ) as bool); + @override + void releasePointerCapture(int? pointerId) => super.noSuchMethod( + Invocation.method( + #releasePointerCapture, + [pointerId], + ), + returnValueForMissingStub: null, + ); + @override + void requestPointerLock() => super.noSuchMethod( + Invocation.method( + #requestPointerLock, + [], + ), + returnValueForMissingStub: null, + ); + @override + void scroll([ + dynamic options_OR_x, + num? y, + ]) => + super.noSuchMethod( + Invocation.method( + #scroll, + [ + options_OR_x, + y, + ], + ), + returnValueForMissingStub: null, + ); + @override + void scrollBy([ + dynamic options_OR_x, + num? y, + ]) => + super.noSuchMethod( + Invocation.method( + #scrollBy, + [ + options_OR_x, + y, + ], + ), + returnValueForMissingStub: null, + ); + @override + void scrollIntoViewIfNeeded([bool? centerIfNeeded]) => super.noSuchMethod( + Invocation.method( + #scrollIntoViewIfNeeded, + [centerIfNeeded], + ), + returnValueForMissingStub: null, + ); + @override + void scrollTo([ + dynamic options_OR_x, + num? y, + ]) => + super.noSuchMethod( + Invocation.method( + #scrollTo, + [ + options_OR_x, + y, + ], + ), + returnValueForMissingStub: null, + ); + @override + void setPointerCapture(int? pointerId) => super.noSuchMethod( + Invocation.method( + #setPointerCapture, + [pointerId], + ), + returnValueForMissingStub: null, + ); + @override + void after(Object? nodes) => super.noSuchMethod( + Invocation.method( + #after, + [nodes], + ), + returnValueForMissingStub: null, + ); + @override + void before(Object? nodes) => super.noSuchMethod( + Invocation.method( + #before, + [nodes], + ), + returnValueForMissingStub: null, + ); @override _i2.Element? querySelector(String? selectors) => - (super.noSuchMethod(Invocation.method(#querySelector, [selectors])) - as _i2.Element?); - @override - void remove() => super.noSuchMethod(Invocation.method(#remove, []), - returnValueForMissingStub: null); - @override - _i2.Node replaceWith(_i2.Node? otherNode) => - (super.noSuchMethod(Invocation.method(#replaceWith, [otherNode]), - returnValue: _FakeNode_13()) as _i2.Node); - @override - void insertAllBefore(Iterable<_i2.Node>? newNodes, _i2.Node? refChild) => + (super.noSuchMethod(Invocation.method( + #querySelector, + [selectors], + )) as _i2.Element?); + @override + void remove() => super.noSuchMethod( + Invocation.method( + #remove, + [], + ), + returnValueForMissingStub: null, + ); + @override + _i2.Node replaceWith(_i2.Node? otherNode) => (super.noSuchMethod( + Invocation.method( + #replaceWith, + [otherNode], + ), + returnValue: _FakeNode_13( + this, + Invocation.method( + #replaceWith, + [otherNode], + ), + ), + ) as _i2.Node); + @override + void insertAllBefore( + Iterable<_i2.Node>? newNodes, + _i2.Node? child, + ) => super.noSuchMethod( - Invocation.method(#insertAllBefore, [newNodes, refChild]), - returnValueForMissingStub: null); - @override - _i2.Node append(_i2.Node? node) => - (super.noSuchMethod(Invocation.method(#append, [node]), - returnValue: _FakeNode_13()) as _i2.Node); - @override - _i2.Node clone(bool? deep) => - (super.noSuchMethod(Invocation.method(#clone, [deep]), - returnValue: _FakeNode_13()) as _i2.Node); - @override - bool contains(_i2.Node? other) => - (super.noSuchMethod(Invocation.method(#contains, [other]), - returnValue: false) as bool); - @override - _i2.Node getRootNode([Map? options]) => - (super.noSuchMethod(Invocation.method(#getRootNode, [options]), - returnValue: _FakeNode_13()) as _i2.Node); - @override - bool hasChildNodes() => - (super.noSuchMethod(Invocation.method(#hasChildNodes, []), - returnValue: false) as bool); - @override - _i2.Node insertBefore(_i2.Node? node, _i2.Node? child) => - (super.noSuchMethod(Invocation.method(#insertBefore, [node, child]), - returnValue: _FakeNode_13()) as _i2.Node); - @override - void addEventListener(String? type, _i2.EventListener? listener, - [bool? useCapture]) => + Invocation.method( + #insertAllBefore, + [ + newNodes, + child, + ], + ), + returnValueForMissingStub: null, + ); + @override + _i2.Node append(_i2.Node? node) => (super.noSuchMethod( + Invocation.method( + #append, + [node], + ), + returnValue: _FakeNode_13( + this, + Invocation.method( + #append, + [node], + ), + ), + ) as _i2.Node); + @override + _i2.Node clone(bool? deep) => (super.noSuchMethod( + Invocation.method( + #clone, + [deep], + ), + returnValue: _FakeNode_13( + this, + Invocation.method( + #clone, + [deep], + ), + ), + ) as _i2.Node); + @override + bool contains(_i2.Node? other) => (super.noSuchMethod( + Invocation.method( + #contains, + [other], + ), + returnValue: false, + ) as bool); + @override + _i2.Node getRootNode([Map? options]) => (super.noSuchMethod( + Invocation.method( + #getRootNode, + [options], + ), + returnValue: _FakeNode_13( + this, + Invocation.method( + #getRootNode, + [options], + ), + ), + ) as _i2.Node); + @override + bool hasChildNodes() => (super.noSuchMethod( + Invocation.method( + #hasChildNodes, + [], + ), + returnValue: false, + ) as bool); + @override + _i2.Node insertBefore( + _i2.Node? node, + _i2.Node? child, + ) => + (super.noSuchMethod( + Invocation.method( + #insertBefore, + [ + node, + child, + ], + ), + returnValue: _FakeNode_13( + this, + Invocation.method( + #insertBefore, + [ + node, + child, + ], + ), + ), + ) as _i2.Node); + @override + void addEventListener( + String? type, + _i2.EventListener? listener, [ + bool? useCapture, + ]) => super.noSuchMethod( - Invocation.method(#addEventListener, [type, listener, useCapture]), - returnValueForMissingStub: null); - @override - void removeEventListener(String? type, _i2.EventListener? listener, - [bool? useCapture]) => + Invocation.method( + #addEventListener, + [ + type, + listener, + useCapture, + ], + ), + returnValueForMissingStub: null, + ); + @override + void removeEventListener( + String? type, + _i2.EventListener? listener, [ + bool? useCapture, + ]) => super.noSuchMethod( - Invocation.method(#removeEventListener, [type, listener, useCapture]), - returnValueForMissingStub: null); - @override - bool dispatchEvent(_i2.Event? event) => - (super.noSuchMethod(Invocation.method(#dispatchEvent, [event]), - returnValue: false) as bool); + Invocation.method( + #removeEventListener, + [ + type, + listener, + useCapture, + ], + ), + returnValueForMissingStub: null, + ); + @override + bool dispatchEvent(_i2.Event? event) => (super.noSuchMethod( + Invocation.method( + #dispatchEvent, + [event], + ), + returnValue: false, + ) as bool); } /// A class which mocks [BuildContext]. @@ -1010,80 +2109,162 @@ class MockBuildContext extends _i1.Mock implements _i4.BuildContext { } @override - _i4.Widget get widget => (super.noSuchMethod(Invocation.getter(#widget), - returnValue: _FakeWidget_14()) as _i4.Widget); - @override - bool get debugDoingBuild => (super - .noSuchMethod(Invocation.getter(#debugDoingBuild), returnValue: false) - as bool); - @override - _i4.InheritedWidget dependOnInheritedElement(_i4.InheritedElement? ancestor, - {Object? aspect}) => + _i4.Widget get widget => (super.noSuchMethod( + Invocation.getter(#widget), + returnValue: _FakeWidget_14( + this, + Invocation.getter(#widget), + ), + ) as _i4.Widget); + @override + bool get mounted => (super.noSuchMethod( + Invocation.getter(#mounted), + returnValue: false, + ) as bool); + @override + bool get debugDoingBuild => (super.noSuchMethod( + Invocation.getter(#debugDoingBuild), + returnValue: false, + ) as bool); + @override + _i4.InheritedWidget dependOnInheritedElement( + _i4.InheritedElement? ancestor, { + Object? aspect, + }) => (super.noSuchMethod( + Invocation.method( + #dependOnInheritedElement, + [ancestor], + {#aspect: aspect}, + ), + returnValue: _FakeInheritedWidget_15( + this, Invocation.method( - #dependOnInheritedElement, [ancestor], {#aspect: aspect}), - returnValue: _FakeInheritedWidget_15()) as _i4.InheritedWidget); + #dependOnInheritedElement, + [ancestor], + {#aspect: aspect}, + ), + ), + ) as _i4.InheritedWidget); @override void visitAncestorElements(bool Function(_i4.Element)? visitor) => - super.noSuchMethod(Invocation.method(#visitAncestorElements, [visitor]), - returnValueForMissingStub: null); - @override - void visitChildElements(_i4.ElementVisitor? visitor) => - super.noSuchMethod(Invocation.method(#visitChildElements, [visitor]), - returnValueForMissingStub: null); - @override - _i5.DiagnosticsNode describeElement(String? name, - {_i5.DiagnosticsTreeStyle? style = - _i5.DiagnosticsTreeStyle.errorProperty}) => + super.noSuchMethod( + Invocation.method( + #visitAncestorElements, + [visitor], + ), + returnValueForMissingStub: null, + ); + @override + void visitChildElements(_i4.ElementVisitor? visitor) => super.noSuchMethod( + Invocation.method( + #visitChildElements, + [visitor], + ), + returnValueForMissingStub: null, + ); + @override + void dispatchNotification(_i7.Notification? notification) => + super.noSuchMethod( + Invocation.method( + #dispatchNotification, + [notification], + ), + returnValueForMissingStub: null, + ); + @override + _i5.DiagnosticsNode describeElement( + String? name, { + _i5.DiagnosticsTreeStyle? style = _i5.DiagnosticsTreeStyle.errorProperty, + }) => (super.noSuchMethod( - Invocation.method(#describeElement, [name], {#style: style}), - returnValue: _FakeDiagnosticsNode_16()) as _i5.DiagnosticsNode); - @override - _i5.DiagnosticsNode describeWidget(String? name, - {_i5.DiagnosticsTreeStyle? style = - _i5.DiagnosticsTreeStyle.errorProperty}) => + Invocation.method( + #describeElement, + [name], + {#style: style}, + ), + returnValue: _FakeDiagnosticsNode_16( + this, + Invocation.method( + #describeElement, + [name], + {#style: style}, + ), + ), + ) as _i5.DiagnosticsNode); + @override + _i5.DiagnosticsNode describeWidget( + String? name, { + _i5.DiagnosticsTreeStyle? style = _i5.DiagnosticsTreeStyle.errorProperty, + }) => (super.noSuchMethod( - Invocation.method(#describeWidget, [name], {#style: style}), - returnValue: _FakeDiagnosticsNode_16()) as _i5.DiagnosticsNode); + Invocation.method( + #describeWidget, + [name], + {#style: style}, + ), + returnValue: _FakeDiagnosticsNode_16( + this, + Invocation.method( + #describeWidget, + [name], + {#style: style}, + ), + ), + ) as _i5.DiagnosticsNode); @override List<_i5.DiagnosticsNode> describeMissingAncestor( - {Type? expectedAncestorType}) => + {required Type? expectedAncestorType}) => (super.noSuchMethod( - Invocation.method(#describeMissingAncestor, [], - {#expectedAncestorType: expectedAncestorType}), - returnValue: <_i5.DiagnosticsNode>[]) as List<_i5.DiagnosticsNode>); + Invocation.method( + #describeMissingAncestor, + [], + {#expectedAncestorType: expectedAncestorType}, + ), + returnValue: <_i5.DiagnosticsNode>[], + ) as List<_i5.DiagnosticsNode>); @override _i5.DiagnosticsNode describeOwnershipChain(String? name) => - (super.noSuchMethod(Invocation.method(#describeOwnershipChain, [name]), - returnValue: _FakeDiagnosticsNode_16()) as _i5.DiagnosticsNode); - @override - String toString() => super.toString(); + (super.noSuchMethod( + Invocation.method( + #describeOwnershipChain, + [name], + ), + returnValue: _FakeDiagnosticsNode_16( + this, + Invocation.method( + #describeOwnershipChain, + [name], + ), + ), + ) as _i5.DiagnosticsNode); } /// A class which mocks [CreationParams]. /// /// See the documentation for Mockito's code generation for more information. -class MockCreationParams extends _i1.Mock implements _i7.CreationParams { +class MockCreationParams extends _i1.Mock implements _i8.CreationParams { MockCreationParams() { _i1.throwOnMissingStub(this); } @override - Set get javascriptChannelNames => - (super.noSuchMethod(Invocation.getter(#javascriptChannelNames), - returnValue: {}) as Set); + Set get javascriptChannelNames => (super.noSuchMethod( + Invocation.getter(#javascriptChannelNames), + returnValue: {}, + ) as Set); @override _i8.AutoMediaPlaybackPolicy get autoMediaPlaybackPolicy => - (super.noSuchMethod(Invocation.getter(#autoMediaPlaybackPolicy), - returnValue: _i8.AutoMediaPlaybackPolicy - .require_user_action_for_all_media_types) - as _i8.AutoMediaPlaybackPolicy); - @override - List<_i7.WebViewCookie> get cookies => - (super.noSuchMethod(Invocation.getter(#cookies), - returnValue: <_i7.WebViewCookie>[]) as List<_i7.WebViewCookie>); - @override - String toString() => super.toString(); + (super.noSuchMethod( + Invocation.getter(#autoMediaPlaybackPolicy), + returnValue: + _i8.AutoMediaPlaybackPolicy.require_user_action_for_all_media_types, + ) as _i8.AutoMediaPlaybackPolicy); + @override + List<_i8.WebViewCookie> get cookies => (super.noSuchMethod( + Invocation.getter(#cookies), + returnValue: <_i8.WebViewCookie>[], + ) as List<_i8.WebViewCookie>); } /// A class which mocks [WebViewPlatformCallbacksHandler]. @@ -1096,29 +2277,53 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock } @override - _i6.FutureOr onNavigationRequest({String? url, bool? isForMainFrame}) => + _i6.FutureOr onNavigationRequest({ + required String? url, + required bool? isForMainFrame, + }) => (super.noSuchMethod( - Invocation.method(#onNavigationRequest, [], - {#url: url, #isForMainFrame: isForMainFrame}), - returnValue: Future.value(false)) as _i6.FutureOr); - @override - void onPageStarted(String? url) => - super.noSuchMethod(Invocation.method(#onPageStarted, [url]), - returnValueForMissingStub: null); - @override - void onPageFinished(String? url) => - super.noSuchMethod(Invocation.method(#onPageFinished, [url]), - returnValueForMissingStub: null); - @override - void onProgress(int? progress) => - super.noSuchMethod(Invocation.method(#onProgress, [progress]), - returnValueForMissingStub: null); - @override - void onWebResourceError(_i7.WebResourceError? error) => - super.noSuchMethod(Invocation.method(#onWebResourceError, [error]), - returnValueForMissingStub: null); - @override - String toString() => super.toString(); + Invocation.method( + #onNavigationRequest, + [], + { + #url: url, + #isForMainFrame: isForMainFrame, + }, + ), + returnValue: _i6.Future.value(false), + ) as _i6.FutureOr); + @override + void onPageStarted(String? url) => super.noSuchMethod( + Invocation.method( + #onPageStarted, + [url], + ), + returnValueForMissingStub: null, + ); + @override + void onPageFinished(String? url) => super.noSuchMethod( + Invocation.method( + #onPageFinished, + [url], + ), + returnValueForMissingStub: null, + ); + @override + void onProgress(int? progress) => super.noSuchMethod( + Invocation.method( + #onProgress, + [progress], + ), + returnValueForMissingStub: null, + ); + @override + void onWebResourceError(_i8.WebResourceError? error) => super.noSuchMethod( + Invocation.method( + #onWebResourceError, + [error], + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [HttpRequestFactory]. @@ -1131,30 +2336,47 @@ class MockHttpRequestFactory extends _i1.Mock } @override - _i6.Future<_i2.HttpRequest> request(String? url, - {String? method, - bool? withCredentials, - String? responseType, - String? mimeType, - Map? requestHeaders, - dynamic sendData, - void Function(_i2.ProgressEvent)? onProgress}) => + _i6.Future<_i2.HttpRequest> request( + String? url, { + String? method, + bool? withCredentials, + String? responseType, + String? mimeType, + Map? requestHeaders, + dynamic sendData, + void Function(_i2.ProgressEvent)? onProgress, + }) => (super.noSuchMethod( - Invocation.method(#request, [ - url - ], { - #method: method, - #withCredentials: withCredentials, - #responseType: responseType, - #mimeType: mimeType, - #requestHeaders: requestHeaders, - #sendData: sendData, - #onProgress: onProgress - }), - returnValue: Future<_i2.HttpRequest>.value(_FakeHttpRequest_17())) - as _i6.Future<_i2.HttpRequest>); - @override - String toString() => super.toString(); + Invocation.method( + #request, + [url], + { + #method: method, + #withCredentials: withCredentials, + #responseType: responseType, + #mimeType: mimeType, + #requestHeaders: requestHeaders, + #sendData: sendData, + #onProgress: onProgress, + }, + ), + returnValue: _i6.Future<_i2.HttpRequest>.value(_FakeHttpRequest_17( + this, + Invocation.method( + #request, + [url], + { + #method: method, + #withCredentials: withCredentials, + #responseType: responseType, + #mimeType: mimeType, + #requestHeaders: requestHeaders, + #sendData: sendData, + #onProgress: onProgress, + }, + ), + )), + ) as _i6.Future<_i2.HttpRequest>); } /// A class which mocks [HttpRequest]. @@ -1166,122 +2388,216 @@ class MockHttpRequest extends _i1.Mock implements _i2.HttpRequest { } @override - Map get responseHeaders => - (super.noSuchMethod(Invocation.getter(#responseHeaders), - returnValue: {}) as Map); - @override - int get readyState => - (super.noSuchMethod(Invocation.getter(#readyState), returnValue: 0) - as int); - @override - String get responseType => - (super.noSuchMethod(Invocation.getter(#responseType), returnValue: '') - as String); - @override - set responseType(String? value) => - super.noSuchMethod(Invocation.setter(#responseType, value), - returnValueForMissingStub: null); - @override - set timeout(int? value) => - super.noSuchMethod(Invocation.setter(#timeout, value), - returnValueForMissingStub: null); - @override - _i2.HttpRequestUpload get upload => - (super.noSuchMethod(Invocation.getter(#upload), - returnValue: _FakeHttpRequestUpload_18()) as _i2.HttpRequestUpload); - @override - set withCredentials(bool? value) => - super.noSuchMethod(Invocation.setter(#withCredentials, value), - returnValueForMissingStub: null); - @override - _i6.Stream<_i2.Event> get onReadyStateChange => - (super.noSuchMethod(Invocation.getter(#onReadyStateChange), - returnValue: Stream<_i2.Event>.empty()) as _i6.Stream<_i2.Event>); - @override - _i6.Stream<_i2.ProgressEvent> get onAbort => - (super.noSuchMethod(Invocation.getter(#onAbort), - returnValue: Stream<_i2.ProgressEvent>.empty()) - as _i6.Stream<_i2.ProgressEvent>); - @override - _i6.Stream<_i2.ProgressEvent> get onError => - (super.noSuchMethod(Invocation.getter(#onError), - returnValue: Stream<_i2.ProgressEvent>.empty()) - as _i6.Stream<_i2.ProgressEvent>); - @override - _i6.Stream<_i2.ProgressEvent> get onLoad => - (super.noSuchMethod(Invocation.getter(#onLoad), - returnValue: Stream<_i2.ProgressEvent>.empty()) - as _i6.Stream<_i2.ProgressEvent>); - @override - _i6.Stream<_i2.ProgressEvent> get onLoadEnd => - (super.noSuchMethod(Invocation.getter(#onLoadEnd), - returnValue: Stream<_i2.ProgressEvent>.empty()) - as _i6.Stream<_i2.ProgressEvent>); - @override - _i6.Stream<_i2.ProgressEvent> get onLoadStart => - (super.noSuchMethod(Invocation.getter(#onLoadStart), - returnValue: Stream<_i2.ProgressEvent>.empty()) - as _i6.Stream<_i2.ProgressEvent>); - @override - _i6.Stream<_i2.ProgressEvent> get onProgress => - (super.noSuchMethod(Invocation.getter(#onProgress), - returnValue: Stream<_i2.ProgressEvent>.empty()) - as _i6.Stream<_i2.ProgressEvent>); - @override - _i6.Stream<_i2.ProgressEvent> get onTimeout => - (super.noSuchMethod(Invocation.getter(#onTimeout), - returnValue: Stream<_i2.ProgressEvent>.empty()) - as _i6.Stream<_i2.ProgressEvent>); - @override - _i2.Events get on => - (super.noSuchMethod(Invocation.getter(#on), returnValue: _FakeEvents_19()) - as _i2.Events); - @override - void open(String? method, String? url, - {bool? async, String? user, String? password}) => + Map get responseHeaders => (super.noSuchMethod( + Invocation.getter(#responseHeaders), + returnValue: {}, + ) as Map); + @override + int get readyState => (super.noSuchMethod( + Invocation.getter(#readyState), + returnValue: 0, + ) as int); + @override + String get responseType => (super.noSuchMethod( + Invocation.getter(#responseType), + returnValue: '', + ) as String); + @override + set responseType(String? value) => super.noSuchMethod( + Invocation.setter( + #responseType, + value, + ), + returnValueForMissingStub: null, + ); + @override + set timeout(int? value) => super.noSuchMethod( + Invocation.setter( + #timeout, + value, + ), + returnValueForMissingStub: null, + ); + @override + _i2.HttpRequestUpload get upload => (super.noSuchMethod( + Invocation.getter(#upload), + returnValue: _FakeHttpRequestUpload_18( + this, + Invocation.getter(#upload), + ), + ) as _i2.HttpRequestUpload); + @override + set withCredentials(bool? value) => super.noSuchMethod( + Invocation.setter( + #withCredentials, + value, + ), + returnValueForMissingStub: null, + ); + @override + _i6.Stream<_i2.Event> get onReadyStateChange => (super.noSuchMethod( + Invocation.getter(#onReadyStateChange), + returnValue: _i6.Stream<_i2.Event>.empty(), + ) as _i6.Stream<_i2.Event>); + @override + _i6.Stream<_i2.ProgressEvent> get onAbort => (super.noSuchMethod( + Invocation.getter(#onAbort), + returnValue: _i6.Stream<_i2.ProgressEvent>.empty(), + ) as _i6.Stream<_i2.ProgressEvent>); + @override + _i6.Stream<_i2.ProgressEvent> get onError => (super.noSuchMethod( + Invocation.getter(#onError), + returnValue: _i6.Stream<_i2.ProgressEvent>.empty(), + ) as _i6.Stream<_i2.ProgressEvent>); + @override + _i6.Stream<_i2.ProgressEvent> get onLoad => (super.noSuchMethod( + Invocation.getter(#onLoad), + returnValue: _i6.Stream<_i2.ProgressEvent>.empty(), + ) as _i6.Stream<_i2.ProgressEvent>); + @override + _i6.Stream<_i2.ProgressEvent> get onLoadEnd => (super.noSuchMethod( + Invocation.getter(#onLoadEnd), + returnValue: _i6.Stream<_i2.ProgressEvent>.empty(), + ) as _i6.Stream<_i2.ProgressEvent>); + @override + _i6.Stream<_i2.ProgressEvent> get onLoadStart => (super.noSuchMethod( + Invocation.getter(#onLoadStart), + returnValue: _i6.Stream<_i2.ProgressEvent>.empty(), + ) as _i6.Stream<_i2.ProgressEvent>); + @override + _i6.Stream<_i2.ProgressEvent> get onProgress => (super.noSuchMethod( + Invocation.getter(#onProgress), + returnValue: _i6.Stream<_i2.ProgressEvent>.empty(), + ) as _i6.Stream<_i2.ProgressEvent>); + @override + _i6.Stream<_i2.ProgressEvent> get onTimeout => (super.noSuchMethod( + Invocation.getter(#onTimeout), + returnValue: _i6.Stream<_i2.ProgressEvent>.empty(), + ) as _i6.Stream<_i2.ProgressEvent>); + @override + _i2.Events get on => (super.noSuchMethod( + Invocation.getter(#on), + returnValue: _FakeEvents_19( + this, + Invocation.getter(#on), + ), + ) as _i2.Events); + @override + void open( + String? method, + String? url, { + bool? async, + String? user, + String? password, + }) => super.noSuchMethod( - Invocation.method(#open, [method, url], - {#async: async, #user: user, #password: password}), - returnValueForMissingStub: null); - @override - void abort() => super.noSuchMethod(Invocation.method(#abort, []), - returnValueForMissingStub: null); - @override - String getAllResponseHeaders() => - (super.noSuchMethod(Invocation.method(#getAllResponseHeaders, []), - returnValue: '') as String); + Invocation.method( + #open, + [ + method, + url, + ], + { + #async: async, + #user: user, + #password: password, + }, + ), + returnValueForMissingStub: null, + ); + @override + void abort() => super.noSuchMethod( + Invocation.method( + #abort, + [], + ), + returnValueForMissingStub: null, + ); + @override + String getAllResponseHeaders() => (super.noSuchMethod( + Invocation.method( + #getAllResponseHeaders, + [], + ), + returnValue: '', + ) as String); @override String? getResponseHeader(String? name) => - (super.noSuchMethod(Invocation.method(#getResponseHeader, [name])) - as String?); - @override - void overrideMimeType(String? mime) => - super.noSuchMethod(Invocation.method(#overrideMimeType, [mime]), - returnValueForMissingStub: null); - @override - void send([dynamic body_OR_data]) => - super.noSuchMethod(Invocation.method(#send, [body_OR_data]), - returnValueForMissingStub: null); - @override - void setRequestHeader(String? name, String? value) => - super.noSuchMethod(Invocation.method(#setRequestHeader, [name, value]), - returnValueForMissingStub: null); - @override - void addEventListener(String? type, _i2.EventListener? listener, - [bool? useCapture]) => + (super.noSuchMethod(Invocation.method( + #getResponseHeader, + [name], + )) as String?); + @override + void overrideMimeType(String? mime) => super.noSuchMethod( + Invocation.method( + #overrideMimeType, + [mime], + ), + returnValueForMissingStub: null, + ); + @override + void send([dynamic body_OR_data]) => super.noSuchMethod( + Invocation.method( + #send, + [body_OR_data], + ), + returnValueForMissingStub: null, + ); + @override + void setRequestHeader( + String? name, + String? value, + ) => super.noSuchMethod( - Invocation.method(#addEventListener, [type, listener, useCapture]), - returnValueForMissingStub: null); - @override - void removeEventListener(String? type, _i2.EventListener? listener, - [bool? useCapture]) => + Invocation.method( + #setRequestHeader, + [ + name, + value, + ], + ), + returnValueForMissingStub: null, + ); + @override + void addEventListener( + String? type, + _i2.EventListener? listener, [ + bool? useCapture, + ]) => super.noSuchMethod( - Invocation.method(#removeEventListener, [type, listener, useCapture]), - returnValueForMissingStub: null); - @override - bool dispatchEvent(_i2.Event? event) => - (super.noSuchMethod(Invocation.method(#dispatchEvent, [event]), - returnValue: false) as bool); - @override - String toString() => super.toString(); + Invocation.method( + #addEventListener, + [ + type, + listener, + useCapture, + ], + ), + returnValueForMissingStub: null, + ); + @override + void removeEventListener( + String? type, + _i2.EventListener? listener, [ + bool? useCapture, + ]) => + super.noSuchMethod( + Invocation.method( + #removeEventListener, + [ + type, + listener, + useCapture, + ], + ), + returnValueForMissingStub: null, + ); + @override + bool dispatchEvent(_i2.Event? event) => (super.noSuchMethod( + Invocation.method( + #dispatchEvent, + [event], + ), + returnValue: false, + ) as bool); } From 09a4a096d1a55f8cb4378e192202ffc5092ee618 Mon Sep 17 00:00:00 2001 From: Nils Reichardt Date: Thu, 20 Oct 2022 20:04:19 +0200 Subject: [PATCH 814/844] [camera] Remove usage of `_ambiguate` method in example (#6555) --- packages/camera/camera/CHANGELOG.md | 5 +++++ packages/camera/camera/example/lib/main.dart | 13 +++---------- packages/camera/camera/example/pubspec.yaml | 2 +- packages/camera/camera/pubspec.yaml | 4 ++-- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 30bb098f8fd4..127da4561bc3 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.10.0+4 + +* Removes usage of `_ambiguate` method in example. +* Updates minimum Flutter version to 3.0. + ## 0.10.0+3 * Updates code for `no_leading_underscores_for_local_identifiers` lint. diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index c5804e7d0624..860263edf2d3 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -72,7 +72,7 @@ class _CameraExampleHomeState extends State @override void initState() { super.initState(); - _ambiguate(WidgetsBinding.instance)?.addObserver(this); + WidgetsBinding.instance.addObserver(this); _flashModeControlRowAnimationController = AnimationController( duration: const Duration(milliseconds: 300), @@ -102,7 +102,7 @@ class _CameraExampleHomeState extends State @override void dispose() { - _ambiguate(WidgetsBinding.instance)?.removeObserver(this); + WidgetsBinding.instance.removeObserver(this); _flashModeControlRowAnimationController.dispose(); _exposureModeControlRowAnimationController.dispose(); super.dispose(); @@ -585,7 +585,7 @@ class _CameraExampleHomeState extends State } if (_cameras.isEmpty) { - _ambiguate(SchedulerBinding.instance)?.addPostFrameCallback((_) async { + SchedulerBinding.instance.addPostFrameCallback((_) async { showInSnackBar('No camera found.'); }); return const Text('None'); @@ -1079,10 +1079,3 @@ Future main() async { } runApp(const CameraApp()); } - -/// This allows a value of type T or T? to be treated as a value of type T?. -/// -/// We use this so that APIs that have become non-nullable can still be used -/// with `!` and `?` on the stable branch. -// TODO(ianh): Remove this once we roll stable in late 2021. -T? _ambiguate(T? value) => value; diff --git a/packages/camera/camera/example/pubspec.yaml b/packages/camera/camera/example/pubspec.yaml index 22e275e633fe..e63024076fef 100644 --- a/packages/camera/camera/example/pubspec.yaml +++ b/packages/camera/camera/example/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.10.0" + flutter: ">=3.0.0" dependencies: camera: diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 1190989a63b7..0f75d10c36cd 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -4,11 +4,11 @@ description: A Flutter plugin for controlling the camera. Supports previewing Dart. repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.10.0+3 +version: 0.10.0+4 environment: sdk: ">=2.14.0 <3.0.0" - flutter: ">=2.10.0" + flutter: ">=3.0.0" flutter: plugin: From 3a03925ecb29adbb3285132d2adfd661d32823b1 Mon Sep 17 00:00:00 2001 From: David Iglesias Date: Thu, 20 Oct 2022 16:47:37 -0700 Subject: [PATCH 815/844] [tool] Get dependencies in package examples before publish check. (#6596) --- script/tool/CHANGELOG.md | 5 ++ .../tool/lib/src/publish_check_command.dart | 16 ++++++ script/tool/pubspec.yaml | 2 +- .../tool/test/publish_check_command_test.dart | 57 +++++++++++++++++-- 4 files changed, 75 insertions(+), 5 deletions(-) diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md index b7864c9c0501..c492dc00905e 100644 --- a/script/tool/CHANGELOG.md +++ b/script/tool/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.12.1 + +* Modifies `publish_check_command.dart` to do a `dart pub get` in all examples + of the package being checked. Workaround for [dart-lang/pub#3618](https://github.com/dart-lang/pub/issues/3618). + ## 0.12.0 * Changes the behavior of `--packages-for-branch` on main/master to run for diff --git a/script/tool/lib/src/publish_check_command.dart b/script/tool/lib/src/publish_check_command.dart index 38e1b7bdebbb..14b240dc04c2 100644 --- a/script/tool/lib/src/publish_check_command.dart +++ b/script/tool/lib/src/publish_check_command.dart @@ -130,7 +130,23 @@ class PublishCheckCommand extends PackageLoopingCommand { } } + // Run `dart pub get` on the examples of [package]. + Future _fetchExampleDeps(RepositoryPackage package) async { + for (final RepositoryPackage example in package.getExamples()) { + await processRunner.runAndStream( + 'dart', + ['pub', 'get'], + workingDir: example.directory, + ); + } + } + Future _hasValidPublishCheckRun(RepositoryPackage package) async { + // `pub publish` does not do `dart pub get` inside `example` directories + // of a package (but they're part of the analysis output!). + // Issue: https://github.com/flutter/flutter/issues/113788 + await _fetchExampleDeps(package); + print('Running pub publish --dry-run:'); final io.Process process = await processRunner.start( flutterCommand, diff --git a/script/tool/pubspec.yaml b/script/tool/pubspec.yaml index 9e767ad724c0..eecff3703b4c 100644 --- a/script/tool/pubspec.yaml +++ b/script/tool/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_plugin_tools description: Productivity utils for flutter/plugins and flutter/packages repository: https://github.com/flutter/plugins/tree/main/script/tool -version: 0.12.0 +version: 0.12.1 dependencies: args: ^2.1.0 diff --git a/script/tool/test/publish_check_command_test.dart b/script/tool/test/publish_check_command_test.dart index e6c5b9cdebc5..575f8509fd25 100644 --- a/script/tool/test/publish_check_command_test.dart +++ b/script/tool/test/publish_check_command_test.dart @@ -44,10 +44,16 @@ void main() { }); test('publish check all packages', () async { - final RepositoryPackage plugin1 = - createFakePlugin('plugin_tools_test_package_a', packagesDir); - final RepositoryPackage plugin2 = - createFakePlugin('plugin_tools_test_package_b', packagesDir); + final RepositoryPackage plugin1 = createFakePlugin( + 'plugin_tools_test_package_a', + packagesDir, + examples: [], + ); + final RepositoryPackage plugin2 = createFakePlugin( + 'plugin_tools_test_package_b', + packagesDir, + examples: [], + ); await runCapturingPrint(runner, ['publish-check']); @@ -65,6 +71,49 @@ void main() { ])); }); + test('publish prepares dependencies of examples (when present)', () async { + final RepositoryPackage plugin1 = createFakePlugin( + 'plugin_tools_test_package_a', + packagesDir, + examples: ['example1', 'example2'], + ); + final RepositoryPackage plugin2 = createFakePlugin( + 'plugin_tools_test_package_b', + packagesDir, + examples: [], + ); + + await runCapturingPrint(runner, ['publish-check']); + + // For plugin1, these are the expected pub get calls that will happen + final Iterable pubGetCalls = + plugin1.getExamples().map((RepositoryPackage example) { + return ProcessCall( + 'dart', + const ['pub', 'get'], + example.path, + ); + }); + + expect(pubGetCalls, hasLength(2)); + expect( + processRunner.recordedCalls, + orderedEquals([ + // plugin1 has 2 examples, so there's some 'dart pub get' calls. + ...pubGetCalls, + ProcessCall( + 'flutter', + const ['pub', 'publish', '--', '--dry-run'], + plugin1.path), + // plugin2 has no examples, so there's no extra 'dart pub get' calls. + ProcessCall( + 'flutter', + const ['pub', 'publish', '--', '--dry-run'], + plugin2.path), + ]), + ); + }); + test('fail on negative test', () async { createFakePlugin('plugin_tools_test_package_a', packagesDir); From 7a7480a5f392561702ab86acff2fcb73b1b7eb99 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Fri, 21 Oct 2022 10:42:18 -0400 Subject: [PATCH 816/844] [in_app_purchase] Bumps `in_app_purchase_android` minimum version to 0.2.3 (#6595) --- packages/in_app_purchase/in_app_purchase/CHANGELOG.md | 3 ++- packages/in_app_purchase/in_app_purchase/pubspec.yaml | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md index 902c4377e9d6..f38fe18a25d4 100644 --- a/packages/in_app_purchase/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 3.0.8 * Updates minimum Flutter version to 2.10. +* Bumps minimum in_app_purchase_android to 0.2.3. ## 3.0.7 diff --git a/packages/in_app_purchase/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/in_app_purchase/pubspec.yaml index 49382c90c01a..40b0ea9a152b 100644 --- a/packages/in_app_purchase/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase/pubspec.yaml @@ -2,7 +2,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22 -version: 3.0.7 +version: 3.0.8 environment: sdk: ">=2.12.0 <3.0.0" @@ -19,7 +19,7 @@ flutter: dependencies: flutter: sdk: flutter - in_app_purchase_android: ^0.2.1 + in_app_purchase_android: ^0.2.3 in_app_purchase_platform_interface: ^1.0.0 in_app_purchase_storekit: ^0.3.0+1 From f55c7ffc6984c75e6b2b6a72a02b48064e0b5bc1 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 21 Oct 2022 11:44:09 -0400 Subject: [PATCH 817/844] Roll Flutter from 637e5bce662f to 59d26c481acc (17 revisions) (#6599) --- .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 cb8fe40cd622..4362c020c5ba 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -637e5bce662fb43459d409e13ba5a7fb2c3fa930 +59d26c481acca4e5b086dc6502b45ca31b273c25 From 84f5ec608be404adcb0afe62446d9093ddabe5fd Mon Sep 17 00:00:00 2001 From: crystalstorm Date: Mon, 24 Oct 2022 17:55:27 +0300 Subject: [PATCH 818/844] [in_app_purchase][iOS] fix iOS promotional offers (SKPaymentDiscountWrapper was not used properly) (#6541) --- .../in_app_purchase_storekit/CHANGELOG.md | 5 +++ .../example/ios/RunnerTests/TranslatorTests.m | 23 +++++++++++++ .../ios/Classes/FIAObjectTranslator.m | 2 +- .../ios/Classes/InAppPurchasePlugin.m | 12 +++++-- .../in_app_purchase_storekit_platform.dart | 5 ++- .../sk_payment_queue_wrapper.dart | 3 +- .../src/types/app_store_purchase_param.dart | 4 +++ .../in_app_purchase_storekit/pubspec.yaml | 2 +- .../test/fakes/fake_storekit_platform.dart | 14 ++++++++ ...n_app_purchase_storekit_platform_test.dart | 32 +++++++++++++++++++ .../store_kit_wrappers/sk_product_test.dart | 15 +++++++++ .../sk_test_stub_objects.dart | 18 +++++++++++ 12 files changed, 128 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 52f59efacd6a..324e0608b7f9 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.3 + +* Supports adding discount information to AppStorePurchaseParam. +* Fixes iOS Promotional Offers bug which prevents them from working. + ## 0.3.2+2 * Updates imports for `prefer_relative_imports`. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m index ed302d61d9b0..34d686753762 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m @@ -390,4 +390,27 @@ - (void)testSKPaymentDiscountFromMapMissingTimestamp { } } +- (void)testSKPaymentDiscountFromMapOverflowingTimestamp { + if (@available(iOS 12.2, *)) { + NSDictionary *discountMap = @{ + @"identifier" : @"payment_discount_identifier", + @"keyIdentifier" : @"payment_discount_key_identifier", + @"nonce" : @"d18981e0-9003-4365-98a2-4b90e3b62c52", + @"signature" : @"this is a encrypted signature", + @"timestamp" : @1665044583595, // timestamp 2022 Oct + }; + NSString *error = nil; + SKPaymentDiscount *paymentDiscount = + [FIAObjectTranslator getSKPaymentDiscountFromMap:discountMap withError:&error]; + XCTAssertNil(error); + XCTAssertNotNil(paymentDiscount); + XCTAssertEqual(paymentDiscount.identifier, discountMap[@"identifier"]); + XCTAssertEqual(paymentDiscount.keyIdentifier, discountMap[@"keyIdentifier"]); + XCTAssertEqualObjects(paymentDiscount.nonce, + [[NSUUID alloc] initWithUUIDString:discountMap[@"nonce"]]); + XCTAssertEqual(paymentDiscount.signature, discountMap[@"signature"]); + XCTAssertEqual(paymentDiscount.timestamp, discountMap[@"timestamp"]); + } +} + @end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAObjectTranslator.m b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAObjectTranslator.m index d01eb9becf3d..c656b58808b3 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAObjectTranslator.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAObjectTranslator.m @@ -277,7 +277,7 @@ + (SKPaymentDiscount *)getSKPaymentDiscountFromMap:(NSDictionary *)map return nil; } - if (!timestamp || ![timestamp isKindOfClass:NSNumber.class] || [timestamp intValue] <= 0) { + if (!timestamp || ![timestamp isKindOfClass:NSNumber.class] || [timestamp longLongValue] <= 0) { if (error) { *error = @"When specifying a payment discount the 'timestamp' field is mandatory."; } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m index d64c24563b62..bfc90ea43716 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/InAppPurchasePlugin.m @@ -200,10 +200,11 @@ - (void)addPayment:(FlutterMethodCall *)call result:(FlutterResult)result { : [simulatesAskToBuyInSandbox boolValue]; if (@available(iOS 12.2, *)) { + NSDictionary *paymentDiscountMap = [self getNonNullValueFromDictionary:paymentMap + forKey:@"paymentDiscount"]; NSString *error = nil; - SKPaymentDiscount *paymentDiscount = [FIAObjectTranslator - getSKPaymentDiscountFromMap:[paymentMap objectForKey:@"paymentDiscount"] - withError:&error]; + SKPaymentDiscount *paymentDiscount = + [FIAObjectTranslator getSKPaymentDiscountFromMap:paymentDiscountMap withError:&error]; if (error) { result([FlutterError @@ -367,6 +368,11 @@ - (void)showPriceConsentIfNeeded:(FlutterResult)result { result(nil); } +- (id)getNonNullValueFromDictionary:(NSDictionary *)dictionary forKey:(NSString *)key { + id value = dictionary[key]; + return [value isKindOfClass:[NSNull class]] ? nil : value; +} + #pragma mark - transaction observer: - (void)handleTransactionsUpdated:(NSArray *)transactions { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart index c03f15f8ce48..0e5e420ece85 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/in_app_purchase_storekit_platform.dart @@ -75,7 +75,10 @@ class InAppPurchaseStoreKitPlatform extends InAppPurchasePlatform { purchaseParam is AppStorePurchaseParam ? purchaseParam.quantity : 1, applicationUsername: purchaseParam.applicationUserName, simulatesAskToBuyInSandbox: purchaseParam is AppStorePurchaseParam && - purchaseParam.simulatesAskToBuyInSandbox)); + purchaseParam.simulatesAskToBuyInSandbox, + paymentDiscount: purchaseParam is AppStorePurchaseParam + ? purchaseParam.discount + : null)); return true; // There's no error feedback from iOS here to 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 78e16e22416c..d360a2da3fe5 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 @@ -405,7 +405,8 @@ class SKPaymentWrapper { 'applicationUsername': applicationUsername, 'requestData': requestData, 'quantity': quantity, - 'simulatesAskToBuyInSandbox': simulatesAskToBuyInSandbox + 'simulatesAskToBuyInSandbox': simulatesAskToBuyInSandbox, + 'paymentDiscount': paymentDiscount?.toMap(), }; } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_param.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_param.dart index 168ef5cea5f4..0e7e24166c4d 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_param.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/types/app_store_purchase_param.dart @@ -14,6 +14,7 @@ class AppStorePurchaseParam extends PurchaseParam { String? applicationUserName, this.quantity = 1, this.simulatesAskToBuyInSandbox = false, + this.discount, }) : super( productDetails: productDetails, applicationUserName: applicationUserName, @@ -32,4 +33,7 @@ class AppStorePurchaseParam extends PurchaseParam { /// Quantity of the product user requested to buy. final int quantity; + + /// Discount applied to the product. The value is `null` when the product does not have a discount. + final SKPaymentDiscountWrapper? discount; } 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 f2193e53b591..0b6e21a26978 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 platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework. repository: https://github.com/flutter/plugins/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.2+2 +version: 0.3.3 environment: sdk: ">=2.14.0 <3.0.0" 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 08b9c85961a3..e64876deccba 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 @@ -30,6 +30,7 @@ class FakeStoreKitPlatform { PlatformException? restoreException; SKError? testRestoredError; bool queueIsActive = false; + Map discountReceived = {}; void reset() { transactions = []; @@ -54,6 +55,7 @@ class FakeStoreKitPlatform { restoreException = null; testRestoredError = null; queueIsActive = false; + discountReceived = {}; } SKPaymentTransactionWrapper createPendingTransaction(String id, @@ -169,6 +171,18 @@ class FakeStoreKitPlatform { case '-[InAppPurchasePlugin addPayment:result:]': final String id = call.arguments['productIdentifier'] as String; final int quantity = call.arguments['quantity'] as int; + + // Keep the received paymentDiscount parameter when testing payment with discount. + if (call.arguments['applicationUsername'] == 'userWithDiscount') { + if (call.arguments['paymentDiscount'] != null) { + final Map discountArgument = + call.arguments['paymentDiscount']; + discountReceived = discountArgument.cast(); + } else { + discountReceived = {}; + } + } + final SKPaymentTransactionWrapper transaction = createPendingTransaction(id, quantity: quantity); transactions.add(transaction); 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 852599ac3670..51ff2c229483 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 @@ -489,6 +489,38 @@ void main() { expect( fakeStoreKitPlatform.finishedTransactions.first.payment.quantity, 5); }); + + test( + 'buying non consumable with discount, should get purchase objects in the purchase update callback', + () async { + final List details = []; + final Completer> completer = + Completer>(); + final Stream> stream = + iapStoreKitPlatform.purchaseStream; + + late StreamSubscription> subscription; + subscription = stream.listen((List purchaseDetailsList) { + details.addAll(purchaseDetailsList); + if (purchaseDetailsList.first.status == PurchaseStatus.purchased) { + completer.complete(details); + subscription.cancel(); + } + }); + final AppStorePurchaseParam purchaseParam = AppStorePurchaseParam( + productDetails: + AppStoreProductDetails.fromSKProduct(dummyProductWrapper), + applicationUserName: 'userWithDiscount', + discount: dummyPaymentDiscountWrapper, + ); + await iapStoreKitPlatform.buyNonConsumable(purchaseParam: purchaseParam); + + final List result = await completer.future; + expect(result.length, 2); + expect(result.first.productID, dummyProductWrapper.productIdentifier); + expect(fakeStoreKitPlatform.discountReceived, + dummyPaymentDiscountWrapper.toMap()); + }); }); group('complete purchase', () { diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart index de61268e4009..b6de5e035c5e 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart @@ -141,6 +141,21 @@ void main() { expect(payment, equals(dummyPayment)); }); + test('SKPaymentWrapper should have propery values consistent with .toMap()', + () { + final Map mapResult = dummyPaymentWithDiscount.toMap(); + expect(mapResult['productIdentifier'], + dummyPaymentWithDiscount.productIdentifier); + expect(mapResult['applicationUsername'], + dummyPaymentWithDiscount.applicationUsername); + expect(mapResult['requestData'], dummyPaymentWithDiscount.requestData); + expect(mapResult['quantity'], dummyPaymentWithDiscount.quantity); + expect(mapResult['simulatesAskToBuyInSandbox'], + dummyPaymentWithDiscount.simulatesAskToBuyInSandbox); + expect(mapResult['paymentDiscount'], + equals(dummyPaymentWithDiscount.paymentDiscount?.toMap())); + }); + test('Should construct correct SKError from json', () { final SKError error = SKError.fromJson(buildErrorMap(dummyError)); expect(error, equals(dummyError)); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart index 946fbc81b74c..6601a21c4ee4 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart @@ -10,6 +10,15 @@ const SKPaymentWrapper dummyPayment = SKPaymentWrapper( requestData: 'fake-data-utf8', quantity: 2, simulatesAskToBuyInSandbox: true); + +final SKPaymentWrapper dummyPaymentWithDiscount = SKPaymentWrapper( + productIdentifier: 'prod-id', + applicationUsername: 'app-user-name', + requestData: 'fake-data-utf8', + quantity: 2, + simulatesAskToBuyInSandbox: true, + paymentDiscount: dummyPaymentDiscountWrapper); + const SKError dummyError = SKError( code: 111, domain: 'dummy-domain', @@ -186,3 +195,12 @@ Map buildTransactionMap( }; return map; } + +final SKPaymentDiscountWrapper dummyPaymentDiscountWrapper = + SKPaymentDiscountWrapper.fromJson(const { + 'identifier': 'dummy-discount-identifier', + 'keyIdentifier': 'KEYIDTEST1', + 'nonce': '00000000-0000-0000-0000-000000000000', + 'signature': 'dummy-signature-string', + 'timestamp': 1231231231, +}); From cb76ba558478a6268a4dd092a25e8dd3a8347d92 Mon Sep 17 00:00:00 2001 From: Camille Simon <43054281+camsim99@users.noreply.github.com> Date: Mon, 24 Oct 2022 15:13:18 -0700 Subject: [PATCH 819/844] [local_auth] Fix device credential only check for API < 30 (#6522) --- .../local_auth_android/CHANGELOG.md | 4 ++ .../local_auth_android/android/build.gradle | 1 + .../plugins/localauth/LocalAuthPlugin.java | 22 ++++++++- .../plugins/localauth/LocalAuthTest.java | 47 ++++++++++++++++++- .../local_auth_android/pubspec.yaml | 2 +- 5 files changed, 71 insertions(+), 5 deletions(-) diff --git a/packages/local_auth/local_auth_android/CHANGELOG.md b/packages/local_auth/local_auth_android/CHANGELOG.md index a26846d5ed01..c9eeed9d01dd 100644 --- a/packages/local_auth/local_auth_android/CHANGELOG.md +++ b/packages/local_auth/local_auth_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.14 + +* Fixes device credential authentication for API versions before R. + ## 1.0.13 * Updates imports for `prefer_relative_imports`. diff --git a/packages/local_auth/local_auth_android/android/build.gradle b/packages/local_auth/local_auth_android/android/build.gradle index 569d7e3564b4..6c9417008d27 100644 --- a/packages/local_auth/local_auth_android/android/build.gradle +++ b/packages/local_auth/local_auth_android/android/build.gradle @@ -56,6 +56,7 @@ dependencies { api "androidx.fragment:fragment:1.5.2" testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:4.7.0' + testImplementation 'org.robolectric:robolectric:4.5' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:rules:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' diff --git a/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java index e8632c474030..e545df01e7c0 100644 --- a/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java +++ b/packages/local_auth/local_auth_android/android/src/main/java/io/flutter/plugins/localauth/LocalAuthPlugin.java @@ -253,11 +253,16 @@ public ArrayList getEnrolledBiometrics() { } @VisibleForTesting - public boolean isDeviceSupported() { + public boolean isDeviceSecure() { if (keyguardManager == null) return false; return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && keyguardManager.isDeviceSecure()); } + @VisibleForTesting + public boolean isDeviceSupported() { + return isDeviceSecure() || canAuthenticateWithBiometrics(); + } + private boolean canAuthenticateWithBiometrics() { if (biometricManager == null) return false; return biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) @@ -270,7 +275,15 @@ private boolean hasBiometricHardware() { != BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE; } - private boolean canAuthenticateWithDeviceCredential() { + @VisibleForTesting + public boolean canAuthenticateWithDeviceCredential() { + if (Build.VERSION.SDK_INT < 30) { + // Checking for device credential only authentication via the BiometricManager + // is not allowed before API level 30, so we check for presence of PIN, pattern, + // or password instead. + return isDeviceSecure(); + } + if (biometricManager == null) return false; return biometricManager.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS; @@ -334,4 +347,9 @@ final Activity getActivity() { void setBiometricManager(BiometricManager biometricManager) { this.biometricManager = biometricManager; } + + @VisibleForTesting + void setKeyguardManager(KeyguardManager keyguardManager) { + this.keyguardManager = keyguardManager; + } } diff --git a/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java b/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java index 0eaf31255317..7279a3c49af2 100644 --- a/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java +++ b/packages/local_auth/local_auth_android/android/src/test/java/io/flutter/plugins/localauth/LocalAuthTest.java @@ -17,6 +17,7 @@ import static org.mockito.Mockito.when; import android.app.Activity; +import android.app.KeyguardManager; import android.app.NativeActivity; import android.content.Context; import androidx.biometric.BiometricManager; @@ -34,8 +35,12 @@ import java.util.Collections; import java.util.HashMap; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +@RunWith(RobolectricTestRunner.class) public class LocalAuthTest { @Test public void authenticate_returnsErrorWhenAuthInProgress() { @@ -107,14 +112,13 @@ public void authenticate_properlyConfiguresBiometricOnlyAuthenticationRequest() } @Test + @Config(sdk = 30) public void authenticate_properlyConfiguresBiometricAndDeviceCredentialAuthenticationRequest() { final LocalAuthPlugin plugin = spy(new LocalAuthPlugin()); setPluginActivity(plugin, buildMockActivityWithContext(mock(FragmentActivity.class))); when(plugin.isDeviceSupported()).thenReturn(true); final BiometricManager mockBiometricManager = mock(BiometricManager.class); - when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) - .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL)) .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); plugin.setBiometricManager(mockBiometricManager); @@ -135,6 +139,7 @@ public void authenticate_properlyConfiguresBiometricAndDeviceCredentialAuthentic } @Test + @Config(sdk = 30) public void authenticate_properlyConfiguresDeviceCredentialOnlyAuthenticationRequest() { final LocalAuthPlugin plugin = spy(new LocalAuthPlugin()); setPluginActivity(plugin, buildMockActivityWithContext(mock(FragmentActivity.class))); @@ -343,6 +348,44 @@ public void getEnrolledBiometrics_shouldAddStrongBiometrics() { }); } + @Test + @Config(sdk = 22) + public void isDeviceSecure_returnsFalseOnBelowApi23() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + assertFalse(plugin.isDeviceSecure()); + } + + @Test + @Config(sdk = 23) + public void isDeviceSecure_returnsTrueIfDeviceIsSecure() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + KeyguardManager mockKeyguardManager = mock(KeyguardManager.class); + plugin.setKeyguardManager(mockKeyguardManager); + + when(mockKeyguardManager.isDeviceSecure()).thenReturn(true); + assertTrue(plugin.isDeviceSecure()); + + when(mockKeyguardManager.isDeviceSecure()).thenReturn(false); + assertFalse(plugin.isDeviceSecure()); + } + + @Test + @Config(sdk = 30) + public void + canAuthenticateWithDeviceCredential_returnsTrueIfHasBiometricManagerSupportAboveApi30() { + final LocalAuthPlugin plugin = new LocalAuthPlugin(); + final BiometricManager mockBiometricManager = mock(BiometricManager.class); + plugin.setBiometricManager(mockBiometricManager); + + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL)) + .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); + assertTrue(plugin.canAuthenticateWithDeviceCredential()); + + when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL)) + .thenReturn(BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED); + assertFalse(plugin.canAuthenticateWithDeviceCredential()); + } + private Activity buildMockActivityWithContext(Activity mockActivity) { final Context mockContext = mock(Context.class); when(mockActivity.getBaseContext()).thenReturn(mockContext); diff --git a/packages/local_auth/local_auth_android/pubspec.yaml b/packages/local_auth/local_auth_android/pubspec.yaml index 35c2d3af983c..99e9e2c547cc 100644 --- a/packages/local_auth/local_auth_android/pubspec.yaml +++ b/packages/local_auth/local_auth_android/pubspec.yaml @@ -2,7 +2,7 @@ name: local_auth_android description: Android implementation of the local_auth plugin. repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22 -version: 1.0.13 +version: 1.0.14 environment: sdk: ">=2.14.0 <3.0.0" From fed91041ddd9b44ac90015a3f0a1bc77e6404414 Mon Sep 17 00:00:00 2001 From: Julius Bredemeyer <48645716+IVLIVS-III@users.noreply.github.com> Date: Tue, 25 Oct 2022 17:04:05 +0200 Subject: [PATCH 820/844] [in_app_purchase_storekit] Fixup iOS integration test placeholder and remove exclusion (#6604) --- .../example/test_driver/{test => }/integration_test.dart | 0 script/configs/exclude_integration_ios.yaml | 2 -- 2 files changed, 2 deletions(-) rename packages/in_app_purchase/in_app_purchase_storekit/example/test_driver/{test => }/integration_test.dart (100%) diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/test_driver/test/integration_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/example/test_driver/integration_test.dart similarity index 100% rename from packages/in_app_purchase/in_app_purchase_storekit/example/test_driver/test/integration_test.dart rename to packages/in_app_purchase/in_app_purchase_storekit/example/test_driver/integration_test.dart diff --git a/script/configs/exclude_integration_ios.yaml b/script/configs/exclude_integration_ios.yaml index 19dfdb0350dc..2d535cd4f0dc 100644 --- a/script/configs/exclude_integration_ios.yaml +++ b/script/configs/exclude_integration_ios.yaml @@ -1,5 +1,3 @@ -# Currently missing: https://github.com/flutter/flutter/issues/81695 -- in_app_purchase_storekit # Currently missing: https://github.com/flutter/flutter/issues/82208 - ios_platform_images # Can't use Flutter integration tests due to native modal UI. From 6c025ff15278a91992324c66125838ab5cd15ba4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Oct 2022 17:40:18 +0000 Subject: [PATCH 821/844] [camera]: Bump annotation from 1.1.0 to 1.5.0 in /packages/camera/camera_android/android (#6492) --- 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 af871474f68b..a62d3169e409 100644 --- a/packages/camera/camera_android/CHANGELOG.md +++ b/packages/camera/camera_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.0+4 + +* Upgrades `androidx.annotation` version to 1.5.0. + ## 0.10.0+3 * Updates code for `no_leading_underscores_for_local_identifiers` lint. diff --git a/packages/camera/camera_android/android/build.gradle b/packages/camera/camera_android/android/build.gradle index b371f2b5d8b7..4fbb2270b556 100644 --- a/packages/camera/camera_android/android/build.gradle +++ b/packages/camera/camera_android/android/build.gradle @@ -61,7 +61,7 @@ android { } dependencies { - compileOnly 'androidx.annotation:annotation:1.1.0' + implementation 'androidx.annotation:annotation:1.5.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:4.7.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 a464759c3c46..6f1b667670e8 100644 --- a/packages/camera/camera_android/pubspec.yaml +++ b/packages/camera/camera_android/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_android description: Android implementation of the camera plugin. repository: https://github.com/flutter/plugins/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.0+3 +version: 0.10.0+4 environment: sdk: ">=2.14.0 <3.0.0" From e9975010ce7a4cef29d39cf0597ed4e9363996e5 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 25 Oct 2022 14:29:09 -0400 Subject: [PATCH 822/844] [camera_platform_interface][flutter roll] Fix hashCode tests and manual roll (#6618) --- .ci/flutter_master.version | 2 +- .../test/events/camera_event_test.dart | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 4362c020c5ba..7f1873fb9f55 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -59d26c481acca4e5b086dc6502b45ca31b273c25 +29397c2c7dbb9195c142279ed56dabf5fc9e7b85 diff --git a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart index 3914859d44b0..074f203bea21 100644 --- a/packages/camera/camera_platform_interface/test/events/camera_event_test.dart +++ b/packages/camera/camera_platform_interface/test/events/camera_event_test.dart @@ -136,7 +136,7 @@ void main() { const CameraInitializedEvent event = CameraInitializedEvent( 1, 1024, 640, ExposureMode.auto, true, FocusMode.auto, true); final int expectedHashCode = Object.hash( - event.cameraId, + event.cameraId.hashCode, event.previewWidth, event.previewHeight, event.exposureMode, @@ -222,8 +222,11 @@ void main() { test('hashCode should match hashCode of all properties', () { const CameraResolutionChangedEvent event = CameraResolutionChangedEvent(1, 1024, 640); - final int expectedHashCode = - Object.hash(event.cameraId, event.captureWidth, event.captureHeight); + final int expectedHashCode = Object.hash( + event.cameraId.hashCode, + event.captureWidth, + event.captureHeight, + ); expect(event.hashCode, expectedHashCode); }); @@ -326,7 +329,7 @@ void main() { test('hashCode should match hashCode of all properties', () { const CameraErrorEvent event = CameraErrorEvent(1, 'Error'); final int expectedHashCode = - Object.hash(event.cameraId, event.description); + Object.hash(event.cameraId.hashCode, event.description); expect(event.hashCode, expectedHashCode); }); From 44978fec046c2106281872f3a7fb1d8812e5db4b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Oct 2022 19:31:13 +0000 Subject: [PATCH 823/844] [in_app_pur]: Bump gradle from 4.1.0 to 7.3.1 in /packages/in_app_purchase/in_app_purchase_android/android (#6581) --- packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md | 4 ++++ .../in_app_purchase_android/android/build.gradle | 2 +- packages/in_app_purchase/in_app_purchase_android/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) 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 6540fdfedafe..b595d7e148d9 100644 --- a/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.3+6 + +* Updates android gradle plugin to 7.3.1. + ## 0.2.3+5 * Updates imports for `prefer_relative_imports`. diff --git a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle index 2375acdc856f..84579ce26c43 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle +++ b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:7.3.1' } } 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 8c57a982937b..555773a47910 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/plugins/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.2.3+5 +version: 0.2.3+6 environment: sdk: ">=2.14.0 <3.0.0" From e17f931e216d9d390b0cd6ab1c97f4efe3291713 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Oct 2022 19:59:05 +0000 Subject: [PATCH 824/844] [url_launcher]: Bump gradle from 3.4.2 to 4.2.0 in /packages/url_launcher/url_launcher_android/android (#6580) --- packages/url_launcher/url_launcher_android/CHANGELOG.md | 4 ++++ .../url_launcher/url_launcher_android/android/build.gradle | 2 +- packages/url_launcher/url_launcher_android/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md index c75e93e821a1..7662c69e1b54 100644 --- a/packages/url_launcher/url_launcher_android/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.0.20 + +* Updates android gradle plugin to 4.2.0. + ## 6.0.19 * Revert gradle back to 3.4.2. diff --git a/packages/url_launcher/url_launcher_android/android/build.gradle b/packages/url_launcher/url_launcher_android/android/build.gradle index 0353973b4ffe..e72288bae582 100644 --- a/packages/url_launcher/url_launcher_android/android/build.gradle +++ b/packages/url_launcher/url_launcher_android/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.4.2' + classpath 'com.android.tools.build:gradle:4.2.0' } } diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index db2bddcc54c3..70c47e2abd2c 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_android description: Android implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.19 +version: 6.0.20 environment: sdk: ">=2.14.0 <3.0.0" From 42f55ed93b6feda084c5349633abbe5111ac1700 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 25 Oct 2022 16:23:14 -0400 Subject: [PATCH 825/844] [various] Update Gradle in examples (#6614) --- .cirrus.yml | 20 ------------------- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- 31 files changed, 30 insertions(+), 50 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 89e1405a901e..4a7ec5bb1598 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -247,35 +247,15 @@ task: MAPS_API_KEY: ENCRYPTED[596a9f6bca436694625ac50851dc5da6b4d34cba8025f7db5bc9465142e8cd44e15f69e3507787753accebfc4910d550] GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[30e6cf7189e3ff3868edc25d2e638ef2aec70546456427064bbc74b297d36145364f49f9d26b327787a59df149d69262] build_script: - # Unsetting CIRRUS_CHANGE_MESSAGE and CIRRUS_COMMIT_MESSAGE as they - # might include non-ASCII characters which makes Gradle crash. - # TODO(stuartmorgan): See https://github.com/flutter/flutter/issues/24935 - - export CIRRUS_CHANGE_MESSAGE="" - - export CIRRUS_COMMIT_MESSAGE="" - ./script/tool_runner.sh build-examples --apk lint_script: - # Unsetting CIRRUS_CHANGE_MESSAGE and CIRRUS_COMMIT_MESSAGE as they - # might include non-ASCII characters which makes Gradle crash. - # TODO(stuartmorgan): See https://github.com/flutter/flutter/issues/24935 - - export CIRRUS_CHANGE_MESSAGE="" - - export CIRRUS_COMMIT_MESSAGE="" - ./script/tool_runner.sh lint-android # must come after build-examples native_unit_test_script: - # Unsetting CIRRUS_CHANGE_MESSAGE and CIRRUS_COMMIT_MESSAGE as they - # might include non-ASCII characters which makes Gradle crash. - # TODO(stuartmorgan): See https://github.com/flutter/flutter/issues/24935 - - export CIRRUS_CHANGE_MESSAGE="" - - export CIRRUS_COMMIT_MESSAGE="" # Native integration tests are handled by firebase-test-lab below, so # only run unit tests. # Must come after build-examples. - ./script/tool_runner.sh native-test --android --no-integration --exclude script/configs/exclude_native_unit_android.yaml firebase_test_lab_script: - # Unsetting CIRRUS_CHANGE_MESSAGE and CIRRUS_COMMIT_MESSAGE as they - # might include non-ASCII characters which makes Gradle crash. - # TODO(stuartmorgan): See https://github.com/flutter/flutter/issues/24935 - - export CIRRUS_CHANGE_MESSAGE="" - - export CIRRUS_COMMIT_MESSAGE="" - if [[ -n "$GCLOUD_FIREBASE_TESTLAB_KEY" ]]; then - echo $GCLOUD_FIREBASE_TESTLAB_KEY > ${HOME}/gcloud-service-key.json - ./script/tool_runner.sh firebase-test-lab --device model=redfin,version=30 --device model=starqlteue,version=26 --exclude=script/configs/exclude_integration_android.yaml diff --git a/packages/camera/camera/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/camera/camera/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/camera/camera/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/camera/camera/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/camera/camera_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/camera/camera_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/camera/camera_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/camera/camera_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/google_maps_flutter/google_maps_flutter/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/google_maps_flutter/google_maps_flutter/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/google_maps_flutter/google_maps_flutter/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/google_maps_flutter/google_maps_flutter/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/google_maps_flutter/google_maps_flutter/example/android/gradle/wrapper/gradle-wrapper.properties index 9ec7236c631e..296b146b7318 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/google_maps_flutter/google_maps_flutter/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle/wrapper/gradle-wrapper.properties index 9ec7236c631e..296b146b7318 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/packages/google_sign_in/google_sign_in/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/google_sign_in/google_sign_in/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/google_sign_in/google_sign_in/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/google_sign_in/google_sign_in/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/google_sign_in/google_sign_in/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/google_sign_in/google_sign_in/example/android/gradle/wrapper/gradle-wrapper.properties index 019065d1d650..01a286e96a21 100644 --- a/packages/google_sign_in/google_sign_in/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/google_sign_in/google_sign_in/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/google_sign_in/google_sign_in_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/google_sign_in/google_sign_in_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/google_sign_in/google_sign_in_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/google_sign_in/google_sign_in_android/example/android/gradle/wrapper/gradle-wrapper.properties index 019065d1d650..01a286e96a21 100644 --- a/packages/google_sign_in/google_sign_in_android/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/google_sign_in/google_sign_in_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/packages/image_picker/image_picker/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/image_picker/image_picker/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/image_picker/image_picker/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/image_picker/image_picker/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/image_picker/image_picker/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/image_picker/image_picker/example/android/gradle/wrapper/gradle-wrapper.properties index 019065d1d650..01a286e96a21 100644 --- a/packages/image_picker/image_picker/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/image_picker/image_picker/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/packages/image_picker/image_picker_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/image_picker/image_picker_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/image_picker/image_picker_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/image_picker/image_picker_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/in_app_purchase/in_app_purchase/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/in_app_purchase/in_app_purchase/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/in_app_purchase/in_app_purchase/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/in_app_purchase/in_app_purchase/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/in_app_purchase/in_app_purchase_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/in_app_purchase/in_app_purchase_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/in_app_purchase/in_app_purchase_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/path_provider/path_provider/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/path_provider/path_provider/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/path_provider/path_provider/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/path_provider/path_provider/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/path_provider/path_provider/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/path_provider/path_provider/example/android/gradle/wrapper/gradle-wrapper.properties index caf54fa2801c..01a286e96a21 100644 --- a/packages/path_provider/path_provider/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/path_provider/path_provider/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/packages/path_provider/path_provider_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/path_provider/path_provider_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/path_provider/path_provider_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/path_provider/path_provider_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/path_provider/path_provider_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/path_provider/path_provider_android/example/android/gradle/wrapper/gradle-wrapper.properties index caf54fa2801c..01a286e96a21 100644 --- a/packages/path_provider/path_provider_android/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/path_provider/path_provider_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/packages/quick_actions/quick_actions/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/quick_actions/quick_actions/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/quick_actions/quick_actions/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/quick_actions/quick_actions/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/quick_actions/quick_actions/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/quick_actions/quick_actions/example/android/gradle/wrapper/gradle-wrapper.properties index 019065d1d650..01a286e96a21 100644 --- a/packages/quick_actions/quick_actions/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/quick_actions/quick_actions/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/packages/quick_actions/quick_actions_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/quick_actions/quick_actions_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/quick_actions/quick_actions_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/quick_actions/quick_actions_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/quick_actions/quick_actions_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/quick_actions/quick_actions_android/example/android/gradle/wrapper/gradle-wrapper.properties index 019065d1d650..01a286e96a21 100644 --- a/packages/quick_actions/quick_actions_android/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/quick_actions/quick_actions_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/packages/url_launcher/url_launcher/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/url_launcher/url_launcher/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/url_launcher/url_launcher/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/url_launcher/url_launcher/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/url_launcher/url_launcher_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/url_launcher/url_launcher_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/url_launcher/url_launcher_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/url_launcher/url_launcher_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/video_player/video_player/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/video_player/video_player/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/video_player/video_player/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/video_player/video_player/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/video_player/video_player_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/video_player/video_player_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/video_player/video_player_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/video_player/video_player_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/webview_flutter/webview_flutter/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/webview_flutter/webview_flutter/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/webview_flutter/webview_flutter/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/webview_flutter/webview_flutter/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/webview_flutter/webview_flutter/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/webview_flutter/webview_flutter/example/android/gradle/wrapper/gradle-wrapper.properties index 2819f022f1fd..296b146b7318 100644 --- a/packages/webview_flutter/webview_flutter/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/webview_flutter/webview_flutter/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/packages/webview_flutter/webview_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/webview_flutter/webview_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f5ee..ca9d62814a25 100644 --- a/packages/webview_flutter/webview_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/webview_flutter/webview_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From f5d2d300a82c09ff369936e19bd6ea907f79402c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Oct 2022 21:25:05 +0000 Subject: [PATCH 826/844] [espresso]: Bump gradle from 3.5.0 to 7.3.1 in /packages/espresso/android (#6579) --- packages/espresso/CHANGELOG.md | 4 ++++ packages/espresso/android/build.gradle | 2 +- packages/espresso/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/espresso/CHANGELOG.md b/packages/espresso/CHANGELOG.md index bd04ecde59b6..38726df01fa6 100644 --- a/packages/espresso/CHANGELOG.md +++ b/packages/espresso/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.0+5 + +* Updates android gradle plugin to 7.3.1. + ## 0.2.0+4 * Updates minimum Flutter version to 2.10. diff --git a/packages/espresso/android/build.gradle b/packages/espresso/android/build.gradle index a83e9cce859f..ce461b1ebe55 100644 --- a/packages/espresso/android/build.gradle +++ b/packages/espresso/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:7.3.1' } } diff --git a/packages/espresso/pubspec.yaml b/packages/espresso/pubspec.yaml index c0068ec9883c..16fcbfb3aa2c 100644 --- a/packages/espresso/pubspec.yaml +++ b/packages/espresso/pubspec.yaml @@ -3,7 +3,7 @@ description: Java classes for testing Flutter apps using Espresso. Allows driving Flutter widgets from a native Espresso test. repository: https://github.com/flutter/plugins/tree/main/packages/espresso issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+espresso%22 -version: 0.2.0+4 +version: 0.2.0+5 environment: sdk: ">=2.12.0 <3.0.0" From 5d4c57ad6fc3a582b46c190c52b9fd6401586ae8 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 25 Oct 2022 18:07:08 -0400 Subject: [PATCH 827/844] Roll Flutter from 29397c2c7dbb to 0fe29f585791 (30 revisions) (#6621) --- .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 7f1873fb9f55..dba16e0386bf 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -29397c2c7dbb9195c142279ed56dabf5fc9e7b85 +0fe29f585791e78212c2ba3239e85caa8f2014d9 From 8e8726a5183404b3b6f5e7087345f888c6ff052b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Oct 2022 22:07:10 +0000 Subject: [PATCH 828/844] [google_maps]: Bump gradle from 3.5.4 to 7.3.1 in /packages/google_maps_flutter/google_maps_flutter_android/android (#6582) --- .../google_maps_flutter_android/CHANGELOG.md | 4 ++++ .../google_maps_flutter_android/android/build.gradle | 2 +- .../google_maps_flutter_android/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 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 6fbd8d7e0b17..9bc8b195f6e2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.3 + +* Update android gradle plugin to 7.3.1. + ## 2.3.2 * Update `com.google.android.gms:play-services-maps` to 18.1.0. diff --git a/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle index 44ad60be4f55..2d507c6cc9fa 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter_android/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.4' + classpath 'com.android.tools.build:gradle:7.3.1' } } 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 798380b5854d..d20322bc6ee1 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/plugins/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.3.2 +version: 2.3.3 environment: sdk: ">=2.14.0 <3.0.0" From cd8bb0a20753182d43bc6244d229770edb09cfbe Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 26 Oct 2022 08:14:01 -0700 Subject: [PATCH 829/844] Fix strict language checks (#6622) --- analysis_options.yaml | 2 +- .../google_sign_in_web/lib/src/generated/gapiauth2.dart | 2 +- .../test/fakes/fake_storekit_platform.dart | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 98352af415de..b12af6cf11e6 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -22,7 +22,7 @@ # repository. analyzer: - strong-mode: + language: strict-casts: true strict-raw-types: true errors: diff --git a/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart b/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart index e1721668f41f..f474e0d00f69 100644 --- a/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart +++ b/packages/google_sign_in/google_sign_in_web/lib/src/generated/gapiauth2.dart @@ -12,7 +12,7 @@ // https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/gapi.auth2 -// ignore_for_file: public_member_api_docs, unused_element, non_constant_identifier_names, sort_constructors_first, always_specify_types +// ignore_for_file: public_member_api_docs, unused_element, non_constant_identifier_names, sort_constructors_first, always_specify_types, strict_raw_type @JS() library gapiauth2; 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 e64876deccba..e6b9696c8cb1 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 @@ -176,7 +176,7 @@ class FakeStoreKitPlatform { if (call.arguments['applicationUsername'] == 'userWithDiscount') { if (call.arguments['paymentDiscount'] != null) { final Map discountArgument = - call.arguments['paymentDiscount']; + call.arguments['paymentDiscount'] as Map; discountReceived = discountArgument.cast(); } else { discountReceived = {}; From 145976792029a768e8ef20730e314ed463169bfb Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 26 Oct 2022 12:01:14 -0400 Subject: [PATCH 830/844] [various] Remove unused Gradle files (#6623) --- .../android/gradle/wrapper/gradle-wrapper.properties | 6 ------ .../android/gradle/wrapper/gradle-wrapper.properties | 5 ----- .../android/gradle/wrapper/gradle-wrapper.properties | 6 ------ .../android/gradle/wrapper/gradle-wrapper.properties | 5 ----- .../android/gradle/wrapper/gradle-wrapper.properties | 6 ------ 5 files changed, 28 deletions(-) delete mode 100644 packages/espresso/android/gradle/wrapper/gradle-wrapper.properties delete mode 100644 packages/flutter_plugin_android_lifecycle/android/gradle/wrapper/gradle-wrapper.properties delete mode 100644 packages/in_app_purchase/in_app_purchase_android/android/gradle/wrapper/gradle-wrapper.properties delete mode 100644 packages/shared_preferences/shared_preferences_android/android/gradle/wrapper/gradle-wrapper.properties delete mode 100644 packages/video_player/video_player_android/android/gradle/wrapper/gradle-wrapper.properties diff --git a/packages/espresso/android/gradle/wrapper/gradle-wrapper.properties b/packages/espresso/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 4751774dd352..000000000000 --- a/packages/espresso/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Tue Nov 26 13:04:21 PST 2019 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/packages/flutter_plugin_android_lifecycle/android/gradle/wrapper/gradle-wrapper.properties b/packages/flutter_plugin_android_lifecycle/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index d757f3d33fcc..000000000000 --- a/packages/flutter_plugin_android_lifecycle/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-5.4.1-all.zip diff --git a/packages/in_app_purchase/in_app_purchase_android/android/gradle/wrapper/gradle-wrapper.properties b/packages/in_app_purchase/in_app_purchase_android/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index baf2285f8c53..000000000000 --- a/packages/in_app_purchase/in_app_purchase_android/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Mon Oct 29 10:30:44 PDT 2018 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/packages/shared_preferences/shared_preferences_android/android/gradle/wrapper/gradle-wrapper.properties b/packages/shared_preferences/shared_preferences_android/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 3c9d0852bfa5..000000000000 --- a/packages/shared_preferences/shared_preferences_android/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-6.7-all.zip diff --git a/packages/video_player/video_player_android/android/gradle/wrapper/gradle-wrapper.properties b/packages/video_player/video_player_android/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 9f96ce648a03..000000000000 --- a/packages/video_player/video_player_android/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Wed Oct 17 09:04:56 PDT 2018 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip From 98bced009c315f223f1ded1c56b120bc47eb3c21 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 26 Oct 2022 12:21:12 -0400 Subject: [PATCH 831/844] Roll Flutter from 0fe29f585791 to 8b36497946f6 (28 revisions) (#6624) --- .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 dba16e0386bf..8ee8bd4dc03a 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -0fe29f585791e78212c2ba3239e85caa8f2014d9 +8b36497946f601c8199068f19b67ab429e8af9cb From db127f06ef7d8078ec854cc158d770a8a9da69d5 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Wed, 26 Oct 2022 14:57:46 -0400 Subject: [PATCH 832/844] Roll Flutter (stable) from d9111f640213 to 6928314d505d (1 revision) (#6626) --- .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 cc156ea76c97..55a6a928f5b9 100644 --- a/.ci/flutter_stable.version +++ b/.ci/flutter_stable.version @@ -1 +1 @@ -d9111f64021372856901a1fd5bfbc386cade3318 +6928314d505d2bb4777be05e45d7808a5aa91d2a From 91122ec54c62079507776a5202ca5bb238d494ea Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 26 Oct 2022 15:42:05 -0400 Subject: [PATCH 833/844] Update Gradle and AGP in examples to 7.0 (#6625) --- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../camera/camera/example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../camera_android/example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- packages/espresso/example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../google_maps_flutter/example/pubspec.yaml | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../example/pubspec.yaml | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../google_sign_in/example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../google_sign_in/example/pubspec.yaml | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../google_sign_in_android/example/pubspec.yaml | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../image_picker/example/android/build.gradle | 2 +- .../example/android/gradle.properties | 1 - .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../in_app_purchase/example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../local_auth/example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../path_provider/example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../quick_actions/example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../quick_actions/example/pubspec.yaml | 2 +- .../example/android/app/build.gradle | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../quick_actions_android/example/pubspec.yaml | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../url_launcher/example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../example/android/app/build.gradle | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../webview_flutter/example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../webview_flutter/example/pubspec.yaml | 2 +- .../app/gradle/wrapper/gradle-wrapper.properties | 2 +- .../webview_flutter_web/example/pubspec.yaml | 1 - .../example/pubspec.yaml | 1 - .../tool/lib/src/common/package_state_utils.dart | 15 +++++++++++++++ .../test/common/package_state_utils_test.dart | 16 ++++++++++++++-- 76 files changed, 100 insertions(+), 76 deletions(-) diff --git a/packages/camera/camera/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/camera/camera/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/camera/camera/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/camera/camera/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/camera/camera/example/android/build.gradle b/packages/camera/camera/example/android/build.gradle index 456d020f6e2c..c21bff8e0a2f 100644 --- a/packages/camera/camera/example/android/build.gradle +++ b/packages/camera/camera/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/camera/camera/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/camera/camera/example/android/gradle/wrapper/gradle-wrapper.properties index 01a286e96a21..297f2fec363f 100644 --- a/packages/camera/camera/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/camera/camera/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/camera/camera_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/camera/camera_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/camera/camera_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/camera/camera_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/camera/camera_android/example/android/build.gradle b/packages/camera/camera_android/example/android/build.gradle index 456d020f6e2c..c21bff8e0a2f 100644 --- a/packages/camera/camera_android/example/android/build.gradle +++ b/packages/camera/camera_android/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/camera/camera_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/camera/camera_android/example/android/gradle/wrapper/gradle-wrapper.properties index 01a286e96a21..297f2fec363f 100644 --- a/packages/camera/camera_android/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/camera/camera_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/espresso/example/android/build.gradle b/packages/espresso/example/android/build.gradle index 456d020f6e2c..c21bff8e0a2f 100644 --- a/packages/espresso/example/android/build.gradle +++ b/packages/espresso/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/espresso/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/espresso/example/android/gradle/wrapper/gradle-wrapper.properties index 296b146b7318..b8793d3c0d69 100644 --- a/packages/espresso/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/espresso/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/flutter_plugin_android_lifecycle/example/android/build.gradle b/packages/flutter_plugin_android_lifecycle/example/android/build.gradle index 456d020f6e2c..c21bff8e0a2f 100644 --- a/packages/flutter_plugin_android_lifecycle/example/android/build.gradle +++ b/packages/flutter_plugin_android_lifecycle/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/flutter_plugin_android_lifecycle/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/flutter_plugin_android_lifecycle/example/android/gradle/wrapper/gradle-wrapper.properties index 296b146b7318..b8793d3c0d69 100644 --- a/packages/flutter_plugin_android_lifecycle/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/flutter_plugin_android_lifecycle/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/google_maps_flutter/google_maps_flutter/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/google_maps_flutter/google_maps_flutter/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/google_maps_flutter/google_maps_flutter/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/google_maps_flutter/google_maps_flutter/example/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter/example/android/build.gradle index 4d8d45d13a0b..c21bff8e0a2f 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/android/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.4' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/google_maps_flutter/google_maps_flutter/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/google_maps_flutter/google_maps_flutter/example/android/gradle/wrapper/gradle-wrapper.properties index 296b146b7318..b8793d3c0d69 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/google_maps_flutter/google_maps_flutter/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip 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 ad1ff2cad0a7..b86f05f3360a 100644 --- a/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml @@ -22,7 +22,7 @@ dependencies: google_maps_flutter_platform_interface: ^2.2.1 dev_dependencies: - espresso: ^0.1.0+2 + espresso: ^0.2.0 flutter_driver: sdk: flutter flutter_test: diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/build.gradle b/packages/google_maps_flutter/google_maps_flutter_android/example/android/build.gradle index 4d8d45d13a0b..c21bff8e0a2f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/android/build.gradle +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.4' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle/wrapper/gradle-wrapper.properties index 296b146b7318..b8793d3c0d69 100644 --- a/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/google_maps_flutter/google_maps_flutter_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip 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 bc9a4dd03657..07decea39920 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 @@ -22,7 +22,7 @@ dependencies: dev_dependencies: build_runner: ^2.1.10 - espresso: ^0.1.0+2 + espresso: ^0.2.0 flutter_driver: sdk: flutter flutter_test: diff --git a/packages/google_sign_in/google_sign_in/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/google_sign_in/google_sign_in/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/google_sign_in/google_sign_in/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/google_sign_in/google_sign_in/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/google_sign_in/google_sign_in/example/android/build.gradle b/packages/google_sign_in/google_sign_in/example/android/build.gradle index e101ac08df55..c21bff8e0a2f 100644 --- a/packages/google_sign_in/google_sign_in/example/android/build.gradle +++ b/packages/google_sign_in/google_sign_in/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/google_sign_in/google_sign_in/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/google_sign_in/google_sign_in/example/android/gradle/wrapper/gradle-wrapper.properties index 01a286e96a21..297f2fec363f 100644 --- a/packages/google_sign_in/google_sign_in/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/google_sign_in/google_sign_in/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/google_sign_in/google_sign_in/example/pubspec.yaml b/packages/google_sign_in/google_sign_in/example/pubspec.yaml index b836ffea4664..fbf8f7cf0591 100644 --- a/packages/google_sign_in/google_sign_in/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in/example/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: http: ^0.13.0 dev_dependencies: - espresso: ^0.1.0+2 + espresso: ^0.2.0 flutter_driver: sdk: flutter flutter_test: diff --git a/packages/google_sign_in/google_sign_in_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/google_sign_in/google_sign_in_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/google_sign_in/google_sign_in_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/google_sign_in/google_sign_in_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/google_sign_in/google_sign_in_android/example/android/build.gradle b/packages/google_sign_in/google_sign_in_android/example/android/build.gradle index e101ac08df55..c21bff8e0a2f 100644 --- a/packages/google_sign_in/google_sign_in_android/example/android/build.gradle +++ b/packages/google_sign_in/google_sign_in_android/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/google_sign_in/google_sign_in_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/google_sign_in/google_sign_in_android/example/android/gradle/wrapper/gradle-wrapper.properties index 01a286e96a21..297f2fec363f 100644 --- a/packages/google_sign_in/google_sign_in_android/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/google_sign_in/google_sign_in_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml index f81df99b7d28..5ac2240cbba1 100644 --- a/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_android/example/pubspec.yaml @@ -20,7 +20,7 @@ dependencies: http: ^0.13.0 dev_dependencies: - espresso: ^0.1.0+2 + espresso: ^0.2.0 flutter_driver: sdk: flutter flutter_test: diff --git a/packages/image_picker/image_picker/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/image_picker/image_picker/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/image_picker/image_picker/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/image_picker/image_picker/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/image_picker/image_picker/example/android/build.gradle b/packages/image_picker/image_picker/example/android/build.gradle index e101ac08df55..c21bff8e0a2f 100755 --- a/packages/image_picker/image_picker/example/android/build.gradle +++ b/packages/image_picker/image_picker/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/image_picker/image_picker/example/android/gradle.properties b/packages/image_picker/image_picker/example/android/gradle.properties index 6effed032590..38c8d4544ff1 100755 --- a/packages/image_picker/image_picker/example/android/gradle.properties +++ b/packages/image_picker/image_picker/example/android/gradle.properties @@ -2,4 +2,3 @@ org.gradle.jvmargs=-Xmx1536M android.enableR8=true android.useAndroidX=true android.enableJetifier=true -android.enableUnitTestBinaryResources=true diff --git a/packages/image_picker/image_picker/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/image_picker/image_picker/example/android/gradle/wrapper/gradle-wrapper.properties index 01a286e96a21..297f2fec363f 100644 --- a/packages/image_picker/image_picker/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/image_picker/image_picker/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/image_picker/image_picker_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/image_picker/image_picker_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/image_picker/image_picker_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/image_picker/image_picker_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/in_app_purchase/in_app_purchase/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/in_app_purchase/in_app_purchase/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/in_app_purchase/in_app_purchase/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/in_app_purchase/in_app_purchase/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/in_app_purchase/in_app_purchase/example/android/build.gradle b/packages/in_app_purchase/in_app_purchase/example/android/build.gradle index 0b4cf534e0aa..c21bff8e0a2f 100644 --- a/packages/in_app_purchase/in_app_purchase/example/android/build.gradle +++ b/packages/in_app_purchase/in_app_purchase/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/in_app_purchase/in_app_purchase/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/in_app_purchase/in_app_purchase/example/android/gradle/wrapper/gradle-wrapper.properties index bc6a58afdda2..b8793d3c0d69 100644 --- a/packages/in_app_purchase/in_app_purchase/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/in_app_purchase/in_app_purchase/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/in_app_purchase/in_app_purchase_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/in_app_purchase/in_app_purchase_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/in_app_purchase/in_app_purchase_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/in_app_purchase/in_app_purchase_android/example/android/build.gradle b/packages/in_app_purchase/in_app_purchase_android/example/android/build.gradle index 0b4cf534e0aa..c21bff8e0a2f 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/android/build.gradle +++ b/packages/in_app_purchase/in_app_purchase_android/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/in_app_purchase/in_app_purchase_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/in_app_purchase/in_app_purchase_android/example/android/gradle/wrapper/gradle-wrapper.properties index bc6a58afdda2..b8793d3c0d69 100644 --- a/packages/in_app_purchase/in_app_purchase_android/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/in_app_purchase/in_app_purchase_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/local_auth/local_auth/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/local_auth/local_auth/example/android/app/gradle/wrapper/gradle-wrapper.properties index 186b71557c50..29e413457635 100644 --- a/packages/local_auth/local_auth/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/local_auth/local_auth/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/local_auth/local_auth/example/android/build.gradle b/packages/local_auth/local_auth/example/android/build.gradle index 54c943621de5..c21bff8e0a2f 100644 --- a/packages/local_auth/local_auth/example/android/build.gradle +++ b/packages/local_auth/local_auth/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.1.1' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/local_auth/local_auth/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/local_auth/local_auth/example/android/gradle/wrapper/gradle-wrapper.properties index cd9fe1c68282..3f383641d7c3 100644 --- a/packages/local_auth/local_auth/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/local_auth/local_auth/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/local_auth/local_auth_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/local_auth/local_auth_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index 186b71557c50..29e413457635 100644 --- a/packages/local_auth/local_auth_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/local_auth/local_auth_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/local_auth/local_auth_android/example/android/build.gradle b/packages/local_auth/local_auth_android/example/android/build.gradle index 54c943621de5..c21bff8e0a2f 100644 --- a/packages/local_auth/local_auth_android/example/android/build.gradle +++ b/packages/local_auth/local_auth_android/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.1.1' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/local_auth/local_auth_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/local_auth/local_auth_android/example/android/gradle/wrapper/gradle-wrapper.properties index cd9fe1c68282..3f383641d7c3 100644 --- a/packages/local_auth/local_auth_android/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/local_auth/local_auth_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/path_provider/path_provider/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/path_provider/path_provider/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/path_provider/path_provider/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/path_provider/path_provider/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/path_provider/path_provider/example/android/build.gradle b/packages/path_provider/path_provider/example/android/build.gradle index e101ac08df55..c21bff8e0a2f 100644 --- a/packages/path_provider/path_provider/example/android/build.gradle +++ b/packages/path_provider/path_provider/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/path_provider/path_provider/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/path_provider/path_provider/example/android/gradle/wrapper/gradle-wrapper.properties index 01a286e96a21..297f2fec363f 100644 --- a/packages/path_provider/path_provider/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/path_provider/path_provider/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/path_provider/path_provider_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/path_provider/path_provider_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/path_provider/path_provider_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/path_provider/path_provider_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/path_provider/path_provider_android/example/android/build.gradle b/packages/path_provider/path_provider_android/example/android/build.gradle index e101ac08df55..c21bff8e0a2f 100644 --- a/packages/path_provider/path_provider_android/example/android/build.gradle +++ b/packages/path_provider/path_provider_android/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/path_provider/path_provider_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/path_provider/path_provider_android/example/android/gradle/wrapper/gradle-wrapper.properties index 01a286e96a21..297f2fec363f 100644 --- a/packages/path_provider/path_provider_android/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/path_provider/path_provider_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/quick_actions/quick_actions/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/quick_actions/quick_actions/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/quick_actions/quick_actions/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/quick_actions/quick_actions/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/quick_actions/quick_actions/example/android/build.gradle b/packages/quick_actions/quick_actions/example/android/build.gradle index e101ac08df55..c21bff8e0a2f 100644 --- a/packages/quick_actions/quick_actions/example/android/build.gradle +++ b/packages/quick_actions/quick_actions/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/quick_actions/quick_actions/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/quick_actions/quick_actions/example/android/gradle/wrapper/gradle-wrapper.properties index 01a286e96a21..297f2fec363f 100644 --- a/packages/quick_actions/quick_actions/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/quick_actions/quick_actions/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/quick_actions/quick_actions/example/pubspec.yaml b/packages/quick_actions/quick_actions/example/pubspec.yaml index 46be008390d8..1a10a653db06 100644 --- a/packages/quick_actions/quick_actions/example/pubspec.yaml +++ b/packages/quick_actions/quick_actions/example/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: path: ../ dev_dependencies: - espresso: ^0.1.0+2 + espresso: ^0.2.0 flutter_driver: sdk: flutter flutter_test: diff --git a/packages/quick_actions/quick_actions_android/example/android/app/build.gradle b/packages/quick_actions/quick_actions_android/example/android/app/build.gradle index d01dce6dab26..666194bc11b0 100644 --- a/packages/quick_actions/quick_actions_android/example/android/app/build.gradle +++ b/packages/quick_actions/quick_actions_android/example/android/app/build.gradle @@ -27,7 +27,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" def androidXTestVersion = '1.2.0' android { - compileSdkVersion 31 + compileSdkVersion 32 lintOptions { disable 'InvalidPackage' diff --git a/packages/quick_actions/quick_actions_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/quick_actions/quick_actions_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/quick_actions/quick_actions_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/quick_actions/quick_actions_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/quick_actions/quick_actions_android/example/android/build.gradle b/packages/quick_actions/quick_actions_android/example/android/build.gradle index e101ac08df55..c21bff8e0a2f 100644 --- a/packages/quick_actions/quick_actions_android/example/android/build.gradle +++ b/packages/quick_actions/quick_actions_android/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/quick_actions/quick_actions_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/quick_actions/quick_actions_android/example/android/gradle/wrapper/gradle-wrapper.properties index 01a286e96a21..297f2fec363f 100644 --- a/packages/quick_actions/quick_actions_android/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/quick_actions/quick_actions_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/quick_actions/quick_actions_android/example/pubspec.yaml b/packages/quick_actions/quick_actions_android/example/pubspec.yaml index 17d50f5d0829..c560d4dd5f1e 100644 --- a/packages/quick_actions/quick_actions_android/example/pubspec.yaml +++ b/packages/quick_actions/quick_actions_android/example/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: path: ../ dev_dependencies: - espresso: ^0.1.0+2 + espresso: ^0.2.0 flutter_driver: sdk: flutter flutter_test: diff --git a/packages/url_launcher/url_launcher/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/url_launcher/url_launcher/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/url_launcher/url_launcher/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/url_launcher/url_launcher/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/url_launcher/url_launcher/example/android/build.gradle b/packages/url_launcher/url_launcher/example/android/build.gradle index 328175bb6ac5..c21bff8e0a2f 100644 --- a/packages/url_launcher/url_launcher/example/android/build.gradle +++ b/packages/url_launcher/url_launcher/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.2.1' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/url_launcher/url_launcher/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/url_launcher/url_launcher/example/android/gradle/wrapper/gradle-wrapper.properties index 4ae10e927b38..e7c709db2454 100644 --- a/packages/url_launcher/url_launcher/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/url_launcher/url_launcher/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/url_launcher/url_launcher_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/url_launcher/url_launcher_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/url_launcher/url_launcher_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/url_launcher/url_launcher_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/url_launcher/url_launcher_android/example/android/build.gradle b/packages/url_launcher/url_launcher_android/example/android/build.gradle index 328175bb6ac5..c21bff8e0a2f 100644 --- a/packages/url_launcher/url_launcher_android/example/android/build.gradle +++ b/packages/url_launcher/url_launcher_android/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.2.1' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/url_launcher/url_launcher_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/url_launcher/url_launcher_android/example/android/gradle/wrapper/gradle-wrapper.properties index 4ae10e927b38..e7c709db2454 100644 --- a/packages/url_launcher/url_launcher_android/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/url_launcher/url_launcher_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/video_player/video_player/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/video_player/video_player/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/video_player/video_player/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/video_player/video_player/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/video_player/video_player_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/video_player/video_player_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/video_player/video_player_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/video_player/video_player_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/webview_flutter/webview_flutter/example/android/app/build.gradle b/packages/webview_flutter/webview_flutter/example/android/app/build.gradle index 8548d5b30ddd..968eed6cad85 100644 --- a/packages/webview_flutter/webview_flutter/example/android/app/build.gradle +++ b/packages/webview_flutter/webview_flutter/example/android/app/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.application' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 31 + compileSdkVersion 32 lintOptions { disable 'InvalidPackage' diff --git a/packages/webview_flutter/webview_flutter/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/webview_flutter/webview_flutter/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/webview_flutter/webview_flutter/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/webview_flutter/webview_flutter/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/webview_flutter/webview_flutter/example/android/build.gradle b/packages/webview_flutter/webview_flutter/example/android/build.gradle index e101ac08df55..c21bff8e0a2f 100644 --- a/packages/webview_flutter/webview_flutter/example/android/build.gradle +++ b/packages/webview_flutter/webview_flutter/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:7.0.1' } } diff --git a/packages/webview_flutter/webview_flutter/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/webview_flutter/webview_flutter/example/android/gradle/wrapper/gradle-wrapper.properties index 296b146b7318..b8793d3c0d69 100644 --- a/packages/webview_flutter/webview_flutter/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/webview_flutter/webview_flutter/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/packages/webview_flutter/webview_flutter/example/pubspec.yaml b/packages/webview_flutter/webview_flutter/example/pubspec.yaml index 7af8b2f9dcc4..6b01b53ee4a3 100644 --- a/packages/webview_flutter/webview_flutter/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/example/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: path: ../ dev_dependencies: - espresso: ^0.1.0+2 + espresso: ^0.2.0 flutter_driver: sdk: flutter flutter_test: diff --git a/packages/webview_flutter/webview_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/webview_flutter/webview_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties index ca9d62814a25..29e413457635 100644 --- a/packages/webview_flutter/webview_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties +++ b/packages/webview_flutter/webview_flutter_android/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml index 7ceb586a9316..e2e0796e7ea3 100644 --- a/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_web/example/pubspec.yaml @@ -20,7 +20,6 @@ dependencies: path: ../ dev_dependencies: - espresso: ^0.1.0+2 flutter_driver: sdk: flutter flutter_test: diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml index 495f5aeb87e6..a3f65b861944 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml @@ -19,7 +19,6 @@ dependencies: path: ../ dev_dependencies: - espresso: ^0.1.0+2 flutter_driver: sdk: flutter flutter_test: diff --git a/script/tool/lib/src/common/package_state_utils.dart b/script/tool/lib/src/common/package_state_utils.dart index a0c82400e1db..65f311974f3a 100644 --- a/script/tool/lib/src/common/package_state_utils.dart +++ b/script/tool/lib/src/common/package_state_utils.dart @@ -173,10 +173,25 @@ Future _isDevChange(List pathComponents, pathComponents.first == 'run_tests.sh' || // Ignoring lints doesn't affect clients. pathComponents.contains('lint-baseline.xml') || + // Example build files are very unlikely to be interesting to clients. + _isExampleBuildFile(pathComponents) || + // Test-only gradle depenedencies don't affect clients. await _isGradleTestDependencyChange(pathComponents, git: git, repoPath: repoPath); } +bool _isExampleBuildFile(List pathComponents) { + if (!pathComponents.contains('example')) { + return false; + } + return pathComponents.contains('gradle-wrapper.properties') || + pathComponents.contains('gradle.properties') || + pathComponents.contains('build.gradle') || + pathComponents.contains('Runner.xcodeproj') || + pathComponents.contains('CMakeLists.txt') || + pathComponents.contains('pubspec.yaml'); +} + Future _isGradleTestDependencyChange(List pathComponents, {GitVersionFinder? git, String? repoPath}) async { if (git == null) { diff --git a/script/tool/test/common/package_state_utils_test.dart b/script/tool/test/common/package_state_utils_test.dart index c20951876e39..c9ae5ba4c742 100644 --- a/script/tool/test/common/package_state_utils_test.dart +++ b/script/tool/test/common/package_state_utils_test.dart @@ -61,13 +61,25 @@ void main() { createFakePlugin('a_plugin', packagesDir); const List changedFiles = [ + 'packages/a_plugin/CHANGELOG.md', + // Analysis. 'packages/a_plugin/example/android/lint-baseline.xml', + // Tests. 'packages/a_plugin/example/android/src/androidTest/foo/bar/FooTest.java', 'packages/a_plugin/example/ios/RunnerTests/Foo.m', 'packages/a_plugin/example/ios/RunnerUITests/info.plist', - 'packages/a_plugin/tool/a_development_tool.dart', + // Test scripts. 'packages/a_plugin/run_tests.sh', - 'packages/a_plugin/CHANGELOG.md', + // Tools. + 'packages/a_plugin/tool/a_development_tool.dart', + // Example build files. + 'packages/a_plugin/example/android/build.gradle', + 'packages/a_plugin/example/android/gradle/wrapper/gradle-wrapper.properties', + 'packages/a_plugin/example/ios/Runner.xcodeproj/project.pbxproj', + 'packages/a_plugin/example/linux/flutter/CMakeLists.txt', + 'packages/a_plugin/example/macos/Runner.xcodeproj/project.pbxproj', + 'packages/a_plugin/example/windows/CMakeLists.txt', + 'packages/a_plugin/example/pubspec.yaml', ]; final PackageChangeState state = await checkPackageChangeState(package, From 655599b9222c8322cd0c0e36ade25e2033892fcf Mon Sep 17 00:00:00 2001 From: Vanesa Oshiro <64811191+VanesaOshiro@users.noreply.github.com> Date: Wed, 26 Oct 2022 17:35:04 -0300 Subject: [PATCH 834/844] [file_selector] Include the missing GetDirectoryPath method example and table of supported platforms (#6454) --- .../file_selector/file_selector/CHANGELOG.md | 3 +- .../file_selector/file_selector/README.md | 29 +++++++++++++++++-- .../lib/readme_standalone_excerpts.dart | 10 +++++++ .../file_selector/file_selector/pubspec.yaml | 2 +- 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/packages/file_selector/file_selector/CHANGELOG.md b/packages/file_selector/file_selector/CHANGELOG.md index 06d01090edc0..7983aa57561f 100644 --- a/packages/file_selector/file_selector/CHANGELOG.md +++ b/packages/file_selector/file_selector/CHANGELOG.md @@ -1,5 +1,6 @@ -## 0.9.2+1 +## 0.9.2+2 +* Improves API docs and examples. * Changes XTypeGroup initialization from final to const. * Updates minimum Flutter version to 2.10. diff --git a/packages/file_selector/file_selector/README.md b/packages/file_selector/file_selector/README.md index 97433584b87a..938e796b879c 100644 --- a/packages/file_selector/file_selector/README.md +++ b/packages/file_selector/file_selector/README.md @@ -11,6 +11,7 @@ A Flutter plugin that manages files and interactions with file dialogs. | **Support** | iOS 9+ | Any | 10.11+ | Any | Windows 10+ | ## Usage + To use this plugin, add `file_selector` as a [dependency in your pubspec.yaml file](https://flutter.dev/platform-plugins/). ### macOS @@ -28,10 +29,12 @@ or read/write access: depending on your use case. ### Examples + Here are small examples that show you how to use the API. Please also take a look at our [example][example] app. #### Open a single file + ``` dart const XTypeGroup typeGroup = XTypeGroup( @@ -43,6 +46,7 @@ final XFile? file = ``` #### Open multiple files at once + ``` dart const XTypeGroup jpgsTypeGroup = XTypeGroup( @@ -59,7 +63,8 @@ final List files = await openFiles(acceptedTypeGroups: [ ]); ``` -#### Saving a file +#### Save a file + ```dart const String fileName = 'suggested_name.txt'; @@ -76,6 +81,17 @@ final XFile textFile = await textFile.saveTo(path); ``` +#### Get a directory path + + +```dart +final String? directoryPath = await getDirectoryPath(); +if (directoryPath == null) { + // Operation was canceled by the user. + return; +} +``` + ### Filtering by file types Different platforms support different type group filter options. To avoid @@ -92,5 +108,14 @@ pass different `XTypeGroup`s based on `Platform`. † `mimeTypes` are not supported on version of macOS earlier than 11 (Big Sur). +### Features supported by platform + +| Feature | Description | iOS | Linux | macOS | Windows | Web | +| ---------------------- |----------------------------------- |--------- | ---------- | -------- | ------------ | ----------- | +| Choose a single file | Pick a file/image | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| Choose multiple files | Pick multiple files/images | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| Choose a save location | Pick a directory to save a file in | ❌ | ✔️ | ✔️ | ✔️ | ❌ | +| Choose a directory | Pick a folder and get its path | ❌ | ✔️ | ✔️ | ✔️ | ❌ | + [example]:./example -[entitlement]: https://docs.flutter.dev/desktop#entitlements-and-the-app-sandbox +[entitlement]: https://docs.flutter.dev/desktop#entitlements-and-the-app-sandbox \ No newline at end of file diff --git a/packages/file_selector/file_selector/example/lib/readme_standalone_excerpts.dart b/packages/file_selector/file_selector/example/lib/readme_standalone_excerpts.dart index c67c93fa63f2..f8126045019a 100644 --- a/packages/file_selector/file_selector/example/lib/readme_standalone_excerpts.dart +++ b/packages/file_selector/file_selector/example/lib/readme_standalone_excerpts.dart @@ -54,4 +54,14 @@ class _MyAppState extends State { await textFile.saveTo(path); // #enddocregion Save } + + Future directoryPath() async { + // #docregion GetDirectory + final String? directoryPath = await getDirectoryPath(); + if (directoryPath == null) { + // Operation was canceled by the user. + return; + } + // #enddocregion GetDirectory + } } diff --git a/packages/file_selector/file_selector/pubspec.yaml b/packages/file_selector/file_selector/pubspec.yaml index cae08e47eeb1..ad187d6f446a 100644 --- a/packages/file_selector/file_selector/pubspec.yaml +++ b/packages/file_selector/file_selector/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for opening and saving files, or selecting directories, using native file selection UI. repository: https://github.com/flutter/plugins/tree/main/packages/file_selector/file_selector issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+file_selector%22 -version: 0.9.2+1 +version: 0.9.2+2 environment: sdk: ">=2.12.0 <3.0.0" From efef3ab28eb4a9fa4f1aa36e82becf1699bdc968 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Oct 2022 16:42:02 +0000 Subject: [PATCH 835/844] [gh_actions]: Bump actions/upload-artifact from 3.1.0 to 3.1.1 (#6608) --- .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 22a0c413d8bc..55fa7f14591e 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -42,7 +42,7 @@ jobs: # Upload the results as artifacts (optional). - name: "Upload artifact" - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 + uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb with: name: SARIF file path: results.sarif From 27dcab189e438cb0321337aa30e031183790efc2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Oct 2022 20:15:29 +0000 Subject: [PATCH 836/844] [in_app_pur]: Bump json from 20220320 to 20220924 in /packages/in_app_purchase/in_app_purchase_android/android (#6490) --- .../in_app_purchase_android/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle index 84579ce26c43..704ab36c253b 100644 --- a/packages/in_app_purchase/in_app_purchase_android/android/build.gradle +++ b/packages/in_app_purchase/in_app_purchase_android/android/build.gradle @@ -57,7 +57,7 @@ dependencies { implementation 'androidx.annotation:annotation:1.3.0' implementation 'com.android.billingclient:billing:5.0.0' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.json:json:20220320' + testImplementation 'org.json:json:20220924' testImplementation 'org.mockito:mockito-core:4.7.0' androidTestImplementation 'androidx.test:runner:1.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' From 204724a646bc20baa7c447502917d5f0bd24db59 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 27 Oct 2022 20:15:31 +0000 Subject: [PATCH 837/844] [in_app_pur]: Bump json from 20180813 to 20220924 in /packages/in_app_purchase/in_app_purchase_android/example/android/app (#6482) --- .../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 e2ec6af97c7a..281f349989be 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 @@ -109,7 +109,7 @@ dependencies { implementation 'com.android.billingclient:billing:5.0.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:4.7.0' - testImplementation 'org.json:json:20180813' + testImplementation 'org.json:json:20220924' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } From 2eb9a1b8372fcffc97966d11271d463d6cca970f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Oct 2022 12:41:25 +0000 Subject: [PATCH 838/844] [gh_actions]: Bump lewagon/wait-on-check-action from 1.1.2 to 1.2.0 (#6611) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7008ebfa8966..292db55dede7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,7 +36,7 @@ jobs: # This workflow should be the last to run. So wait for all the other tests to succeed. - name: Wait on all tests - uses: lewagon/wait-on-check-action@e2558238c09778af25867eb5de5a3ce4bbae3dcd + uses: lewagon/wait-on-check-action@3a563271c3f8d1611ed7352809303617ee7e54ac with: ref: ${{ github.sha }} running-workflow-name: 'release' From 6f911222751604b579db7937254da648e00d6e7e Mon Sep 17 00:00:00 2001 From: Julius Bredemeyer <48645716+IVLIVS-III@users.noreply.github.com> Date: Fri, 28 Oct 2022 17:09:30 +0200 Subject: [PATCH 839/844] [tool] Update tool to set macOS deployment target to 10.15. (#6605) --- .../src/create_all_plugins_app_command.dart | 87 +++++++++++++++++- .../create_all_plugins_app_command_test.dart | 90 +++++++++++++++++++ 2 files changed, 175 insertions(+), 2 deletions(-) diff --git a/script/tool/lib/src/create_all_plugins_app_command.dart b/script/tool/lib/src/create_all_plugins_app_command.dart index a23dc83d98f3..ea7d5a5c4388 100644 --- a/script/tool/lib/src/create_all_plugins_app_command.dart +++ b/script/tool/lib/src/create_all_plugins_app_command.dart @@ -6,22 +6,30 @@ import 'dart:io' as io; import 'package:file/file.dart'; import 'package:path/path.dart' as p; +import 'package:platform/platform.dart'; import 'package:pub_semver/pub_semver.dart'; import 'package:pubspec_parse/pubspec_parse.dart'; import 'common/core.dart'; import 'common/package_command.dart'; +import 'common/process_runner.dart'; import 'common/repository_package.dart'; const String _outputDirectoryFlag = 'output-dir'; +const int _exitUpdateMacosPodfileFailed = 3; +const int _exitUpdateMacosPbxprojFailed = 4; +const int _exitGenNativeBuildFilesFailed = 5; + /// A command to create an application that builds all in a single application. class CreateAllPluginsAppCommand extends PackageCommand { /// Creates an instance of the builder command. CreateAllPluginsAppCommand( Directory packagesDir, { + ProcessRunner processRunner = const ProcessRunner(), Directory? pluginsRoot, - }) : super(packagesDir) { + Platform platform = const LocalPlatform(), + }) : super(packagesDir, processRunner: processRunner, platform: platform) { final Directory defaultDir = pluginsRoot ?? packagesDir.fileSystem.currentDirectory; argParser.addOption(_outputDirectoryFlag, @@ -61,10 +69,28 @@ class CreateAllPluginsAppCommand extends PackageCommand { print(''); } + await _genPubspecWithAllPlugins(); + + // Run `flutter pub get` to generate all native build files. + // TODO(stuartmorgan): This hangs on Windows for some reason. Since it's + // currently not needed on Windows, skip it there, but we should investigate + // further and/or implement https://github.com/flutter/flutter/issues/93407, + // and remove the need for this conditional. + if (!platform.isWindows) { + if (!await _genNativeBuildFiles()) { + printError( + "Failed to generate native build files via 'flutter pub get'"); + throw ToolExit(_exitGenNativeBuildFilesFailed); + } + } + await Future.wait(>[ - _genPubspecWithAllPlugins(), _updateAppGradle(), _updateManifest(), + _updateMacosPbxproj(), + // This step requires the native file generation triggered by + // flutter pub get above, so can't currently be run on Windows. + if (!platform.isWindows) _updateMacosPodfile(), ]); } @@ -259,4 +285,61 @@ dev_dependencies:${_pubspecMapString(pubspec.devDependencies)} return buffer.toString(); } + + Future _genNativeBuildFiles() async { + final int exitCode = await processRunner.runAndStream( + flutterCommand, + ['pub', 'get'], + workingDir: _appDirectory, + ); + return exitCode == 0; + } + + Future _updateMacosPodfile() async { + /// Only change the macOS deployment target if the host platform is macOS. + /// The Podfile is not generated on other platforms. + if (!platform.isMacOS) { + return; + } + + final File podfileFile = + app.platformDirectory(FlutterPlatform.macos).childFile('Podfile'); + if (!podfileFile.existsSync()) { + printError("Can't find Podfile for macOS"); + throw ToolExit(_exitUpdateMacosPodfileFailed); + } + + final StringBuffer newPodfile = StringBuffer(); + for (final String line in podfileFile.readAsLinesSync()) { + if (line.contains('platform :osx')) { + // macOS 10.15 is required by in_app_purchase. + newPodfile.writeln("platform :osx, '10.15'"); + } else { + newPodfile.writeln(line); + } + } + podfileFile.writeAsStringSync(newPodfile.toString()); + } + + Future _updateMacosPbxproj() async { + final File pbxprojFile = app + .platformDirectory(FlutterPlatform.macos) + .childDirectory('Runner.xcodeproj') + .childFile('project.pbxproj'); + if (!pbxprojFile.existsSync()) { + printError("Can't find project.pbxproj for macOS"); + throw ToolExit(_exitUpdateMacosPbxprojFailed); + } + + final StringBuffer newPbxproj = StringBuffer(); + for (final String line in pbxprojFile.readAsLinesSync()) { + if (line.contains('MACOSX_DEPLOYMENT_TARGET')) { + // macOS 10.15 is required by in_app_purchase. + newPbxproj.writeln(' MACOSX_DEPLOYMENT_TARGET = 10.15;'); + } else { + newPbxproj.writeln(line); + } + } + pbxprojFile.writeAsStringSync(newPbxproj.toString()); + } } diff --git a/script/tool/test/create_all_plugins_app_command_test.dart b/script/tool/test/create_all_plugins_app_command_test.dart index 830dd59a8d42..cb2347fe9cc8 100644 --- a/script/tool/test/create_all_plugins_app_command_test.dart +++ b/script/tool/test/create_all_plugins_app_command_test.dart @@ -7,10 +7,12 @@ import 'dart:io' as io; import 'package:args/command_runner.dart'; import 'package:file/file.dart'; import 'package:file/local.dart'; +import 'package:flutter_plugin_tools/src/common/core.dart'; import 'package:flutter_plugin_tools/src/create_all_plugins_app_command.dart'; import 'package:platform/platform.dart'; import 'package:test/test.dart'; +import 'mocks.dart'; import 'util.dart'; void main() { @@ -20,6 +22,7 @@ void main() { late FileSystem fileSystem; late Directory testRoot; late Directory packagesDir; + late RecordingProcessRunner processRunner; setUp(() { // Since the core of this command is a call to 'flutter create', the test @@ -28,9 +31,11 @@ void main() { fileSystem = const LocalFileSystem(); testRoot = fileSystem.systemTempDirectory.createTempSync(); packagesDir = testRoot.childDirectory('packages'); + processRunner = RecordingProcessRunner(); command = CreateAllPluginsAppCommand( packagesDir, + processRunner: processRunner, pluginsRoot: testRoot, ); runner = CommandRunner( @@ -103,6 +108,91 @@ void main() { baselinePubspec.environment?[dartSdkKey]); }); + test('macOS deployment target is modified in Podfile', () async { + createFakePlugin('plugina', packagesDir); + + final File podfileFile = command.packagesDir.parent + .childDirectory('all_plugins') + .childDirectory('macos') + .childFile('Podfile'); + podfileFile.createSync(recursive: true); + podfileFile.writeAsStringSync(""" +platform :osx, '10.11' +# some other line +"""); + + await runCapturingPrint(runner, ['all-plugins-app']); + final List podfile = command.app + .platformDirectory(FlutterPlatform.macos) + .childFile('Podfile') + .readAsLinesSync(); + + expect( + podfile, + everyElement((String line) => + !line.contains('platform :osx') || line.contains("'10.15'"))); + }, + // Podfile is only generated (and thus only edited) on macOS. + skip: !io.Platform.isMacOS); + + test('macOS deployment target is modified in pbxproj', () async { + createFakePlugin('plugina', packagesDir); + + await runCapturingPrint(runner, ['all-plugins-app']); + final List pbxproj = command.app + .platformDirectory(FlutterPlatform.macos) + .childDirectory('Runner.xcodeproj') + .childFile('project.pbxproj') + .readAsLinesSync(); + + expect( + pbxproj, + everyElement((String line) => + !line.contains('MACOSX_DEPLOYMENT_TARGET') || + line.contains('10.15'))); + }); + + test('calls flutter pub get', () async { + createFakePlugin('plugina', packagesDir); + + await runCapturingPrint(runner, ['all-plugins-app']); + + expect( + processRunner.recordedCalls, + orderedEquals([ + ProcessCall( + getFlutterCommand(const LocalPlatform()), + const ['pub', 'get'], + testRoot.childDirectory('all_plugins').path), + ])); + }, + // See comment about Windows in create_all_plugins_app_command.dart + skip: io.Platform.isWindows); + + test('fails if flutter pub get fails', () async { + createFakePlugin('plugina', packagesDir); + + processRunner.mockProcessesForExecutable[ + getFlutterCommand(const LocalPlatform())] = [ + MockProcess(exitCode: 1) + ]; + Error? commandError; + final List output = await runCapturingPrint( + runner, ['all-plugins-app'], errorHandler: (Error e) { + commandError = e; + }); + + expect(commandError, isA()); + expect( + output, + containsAllInOrder([ + contains( + "Failed to generate native build files via 'flutter pub get'"), + ])); + }, + // See comment about Windows in create_all_plugins_app_command.dart + skip: io.Platform.isWindows); + test('handles --output-dir', () async { createFakePlugin('plugina', packagesDir); From def545731a453d88ec6a30d22b8ccec8844d6c2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 28 Oct 2022 17:47:14 +0000 Subject: [PATCH 840/844] [url_launcher]: Bump annotation from 1.0.0 to 1.5.0 in /packages/url_launcher/url_launcher_android/android (#6487) --- packages/url_launcher/url_launcher_android/CHANGELOG.md | 4 ++++ .../url_launcher/url_launcher_android/android/build.gradle | 4 ++-- packages/url_launcher/url_launcher_android/pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/url_launcher/url_launcher_android/CHANGELOG.md b/packages/url_launcher/url_launcher_android/CHANGELOG.md index 7662c69e1b54..934d8da556b7 100644 --- a/packages/url_launcher/url_launcher_android/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.0.21 + +* Updates androidx.annotation to 1.2.0. + ## 6.0.20 * Updates android gradle plugin to 4.2.0. diff --git a/packages/url_launcher/url_launcher_android/android/build.gradle b/packages/url_launcher/url_launcher_android/android/build.gradle index e72288bae582..dbd68d99c1a2 100644 --- a/packages/url_launcher/url_launcher_android/android/build.gradle +++ b/packages/url_launcher/url_launcher_android/android/build.gradle @@ -22,7 +22,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' android { - compileSdkVersion 31 + compileSdkVersion 33 defaultConfig { minSdkVersion 16 @@ -49,7 +49,7 @@ android { } dependencies { - compileOnly 'androidx.annotation:annotation:1.0.0' + compileOnly 'androidx.annotation:annotation:1.2.0' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:4.8.0' testImplementation 'androidx.test:core:1.0.0' diff --git a/packages/url_launcher/url_launcher_android/pubspec.yaml b/packages/url_launcher/url_launcher_android/pubspec.yaml index 70c47e2abd2c..e97fde31b2e0 100644 --- a/packages/url_launcher/url_launcher_android/pubspec.yaml +++ b/packages/url_launcher/url_launcher_android/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_android description: Android implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 6.0.20 +version: 6.0.21 environment: sdk: ">=2.14.0 <3.0.0" From 91d7fe59ab03882708e8c4cecc358554d46eae85 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Fri, 28 Oct 2022 15:19:25 -0400 Subject: [PATCH 841/844] [quick_actions_android] Manual roll to ignore failing `appShortcutLaunchActivityAfterStarting` test (#6633) --- .ci/flutter_master.version | 2 +- .../flutter/plugins/quickactionsexample/QuickActionsTest.java | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.ci/flutter_master.version b/.ci/flutter_master.version index 8ee8bd4dc03a..68543c61125f 100644 --- a/.ci/flutter_master.version +++ b/.ci/flutter_master.version @@ -1 +1 @@ -8b36497946f601c8199068f19b67ab429e8af9cb +ea0ddc94ccc63cec77bcac3ef02832806adcd667 diff --git a/packages/quick_actions/quick_actions_android/example/android/app/src/androidTest/java/io/flutter/plugins/quickactionsexample/QuickActionsTest.java b/packages/quick_actions/quick_actions_android/example/android/app/src/androidTest/java/io/flutter/plugins/quickactionsexample/QuickActionsTest.java index 8b50fd7a90eb..cfcef3e1c76f 100644 --- a/packages/quick_actions/quick_actions_android/example/android/app/src/androidTest/java/io/flutter/plugins/quickactionsexample/QuickActionsTest.java +++ b/packages/quick_actions/quick_actions_android/example/android/app/src/androidTest/java/io/flutter/plugins/quickactionsexample/QuickActionsTest.java @@ -28,6 +28,7 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -85,6 +86,9 @@ public void appShortcutsAreCreated() { } } + // TODO(bparrishMines): The test is ignored because it fails when ran on Firebase Test Lab. See + // https://github.com/flutter/flutter/issues/114246. + @Ignore @Test public void appShortcutLaunchActivityAfterStarting() { // Arrange From 17ce4e63ef359698f113a515eb3a759371046a2d Mon Sep 17 00:00:00 2001 From: rekire Date: Mon, 31 Oct 2022 12:21:01 +0100 Subject: [PATCH 842/844] Regenerate code to make the flutter plugin working --- .../webview_flutter/pubspec.yaml | 6 +- .../GeneratedAndroidWebView.java | 848 ++++----- .../example/lib/main.dart | 4 +- .../lib/src/android_webview.pigeon.dart | 929 ++++------ .../lib/src/android_webview_api_impls.dart | 2 +- .../test/android_webview_test.mocks.dart | 1590 ++++++++++++----- .../test/test_android_webview.pigeon.dart | 694 +++---- ...iew_android_cookie_manager_test.mocks.dart | 35 +- .../webview_android_widget_test.mocks.dart | 1257 +++++++++---- 9 files changed, 3022 insertions(+), 2343 deletions(-) diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 07ce4769f1f3..3eba0b89a205 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -19,9 +19,9 @@ flutter: dependencies: flutter: sdk: flutter - webview_flutter_android: ^2.8.3+3 - webview_flutter_platform_interface: ^1.9.3+1 - webview_flutter_wkwebview: ^2.7.1+3 + webview_pro_android: ^2.8.3+3 + webview_pro_platform_interface: ^1.8.1+2 #^1.9.3+1 + webview_pro_wkwebview: ^2.7.1+3 dev_dependencies: build_runner: ^2.1.5 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 66bc6f939c36..cd3db00288c8 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 @@ -1,4 +1,7 @@ -// Autogenerated from Pigeon (v4.0.2), do not edit directly. +// 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 (v4.2.3), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.webviewflutter; @@ -14,94 +17,101 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.HashMap; -/** Generated class from Pigeon. */ +/**Generated class from Pigeon. */ @SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"}) public class GeneratedAndroidWebView { /** Generated class from Pigeon that represents data sent in messages. */ public static class WebResourceRequestData { - private @NonNullString url; - public @NonNullString getUrl() { return url; } - public void setUrl(@NonNullString setterArg) {if (setterArg == null) { + private @NonNull String url; + public @NonNull String getUrl() { return url; } + public void setUrl(@NonNull String setterArg) { + if (setterArg == null) { throw new IllegalStateException("Nonnull field \"url\" is null."); - } this.url = setterArg; } + } + this.url = setterArg; + } - private @NonNullBoolean isForMainFrame; - public @NonNullBoolean getIsForMainFrame() { return isForMainFrame; } - public void setIsForMainFrame(@NonNullBoolean setterArg) {if (setterArg == null) { + private @NonNull Boolean isForMainFrame; + public @NonNull Boolean getIsForMainFrame() { return isForMainFrame; } + public void setIsForMainFrame(@NonNull Boolean setterArg) { + if (setterArg == null) { throw new IllegalStateException("Nonnull field \"isForMainFrame\" is null."); - } this.isForMainFrame = setterArg; } + } + this.isForMainFrame = setterArg; + } private @Nullable Boolean isRedirect; public @Nullable Boolean getIsRedirect() { return isRedirect; } - public void setIsRedirect(@Nullable Boolean setterArg) { this.isRedirect = setterArg; } + public void setIsRedirect(@Nullable Boolean setterArg) { + this.isRedirect = setterArg; + } - private @NonNullBoolean hasGesture; - public @NonNullBoolean getHasGesture() { return hasGesture; } - public void setHasGesture(@NonNullBoolean setterArg) {if (setterArg == null) { + private @NonNull Boolean hasGesture; + public @NonNull Boolean getHasGesture() { return hasGesture; } + public void setHasGesture(@NonNull Boolean setterArg) { + if (setterArg == null) { throw new IllegalStateException("Nonnull field \"hasGesture\" is null."); - } this.hasGesture = setterArg; } + } + this.hasGesture = setterArg; + } - private @NonNullString method; - public @NonNullString getMethod() { return method; } - public void setMethod(@NonNullString setterArg) {if (setterArg == null) { + private @NonNull String method; + public @NonNull String getMethod() { return method; } + public void setMethod(@NonNull String setterArg) { + if (setterArg == null) { throw new IllegalStateException("Nonnull field \"method\" is null."); - } this.method = setterArg; } + } + this.method = setterArg; + } - private @NonNullMap requestHeaders; - public @NonNullMap getRequestHeaders() { return requestHeaders; } - public void setRequestHeaders(@NonNullMap setterArg) {if (setterArg == null) { + private @NonNull Map requestHeaders; + public @NonNull Map getRequestHeaders() { return requestHeaders; } + public void setRequestHeaders(@NonNull Map setterArg) { + if (setterArg == null) { throw new IllegalStateException("Nonnull field \"requestHeaders\" is null."); - } this.requestHeaders = setterArg; }/** Constructor is private to enforce null safety; use Builder. */ - private WebResourceRequestData() {} + } + this.requestHeaders = setterArg; + } + /**Constructor is private to enforce null safety; use Builder. */ + private WebResourceRequestData() {} public static final class Builder { private @Nullable String url; - public @NonNull Builder setUrl(@NonNull String setterArg) { this.url = setterArg; return this; } - private @Nullable Boolean isForMainFrame; - public @NonNull Builder setIsForMainFrame(@NonNull Boolean setterArg) { this.isForMainFrame = setterArg; return this; } - private @Nullable Boolean isRedirect; - public @NonNull Builder setIsRedirect(@Nullable Boolean setterArg) { this.isRedirect = setterArg; return this; } - private @Nullable Boolean hasGesture; - public @NonNull Builder setHasGesture(@NonNull Boolean setterArg) { this.hasGesture = setterArg; return this; } - private @Nullable String method; - public @NonNull Builder setMethod(@NonNull String setterArg) { this.method = setterArg; return this; } - private @Nullable Map requestHeaders; - public @NonNull Builder setRequestHeaders(@NonNull Map setterArg) { this.requestHeaders = setterArg; return this; } - public @NonNull WebResourceRequestData build() { WebResourceRequestData pigeonReturn = new WebResourceRequestData(); pigeonReturn.setUrl(url); @@ -113,9 +123,7 @@ public static final class Builder { return pigeonReturn; } } - - @NonNull - Map toMap() { + @NonNull Map toMap() { Map toMapResult = new HashMap<>(); toMapResult.put("url", url); toMapResult.put("isForMainFrame", isForMainFrame); @@ -145,34 +153,37 @@ Map toMap() { /** Generated class from Pigeon that represents data sent in messages. */ public static class WebResourceErrorData { - private @NonNullLong errorCode; - public @NonNullLong getErrorCode() { return errorCode; } - public void setErrorCode(@NonNullLong setterArg) {if (setterArg == null) { + private @NonNull Long errorCode; + public @NonNull Long getErrorCode() { return errorCode; } + public void setErrorCode(@NonNull Long setterArg) { + if (setterArg == null) { throw new IllegalStateException("Nonnull field \"errorCode\" is null."); - } this.errorCode = setterArg; } + } + this.errorCode = setterArg; + } - private @NonNullString description; - public @NonNullString getDescription() { return description; } - public void setDescription(@NonNullString setterArg) {if (setterArg == null) { + private @NonNull String description; + public @NonNull String getDescription() { return description; } + public void setDescription(@NonNull String setterArg) { + if (setterArg == null) { throw new IllegalStateException("Nonnull field \"description\" is null."); - } this.description = setterArg; }/** Constructor is private to enforce null safety; use Builder. */ - private WebResourceErrorData() {} + } + this.description = setterArg; + } + /**Constructor is private to enforce null safety; use Builder. */ + private WebResourceErrorData() {} public static final class Builder { private @Nullable Long errorCode; - public @NonNull Builder setErrorCode(@NonNull Long setterArg) { this.errorCode = setterArg; return this; } - private @Nullable String description; - public @NonNull Builder setDescription(@NonNull String setterArg) { this.description = setterArg; return this; } - public @NonNull WebResourceErrorData build() { WebResourceErrorData pigeonReturn = new WebResourceErrorData(); pigeonReturn.setErrorCode(errorCode); @@ -180,9 +191,7 @@ public static final class Builder { return pigeonReturn; } } - - @NonNull - Map toMap() { + @NonNull Map toMap() { Map toMapResult = new HashMap<>(); toMapResult.put("errorCode", errorCode); toMapResult.put("description", description); @@ -191,9 +200,9 @@ Map toMap() { static @NonNull WebResourceErrorData fromMap(@NonNull Map map) { WebResourceErrorData pigeonResult = new WebResourceErrorData(); Object errorCode = map.get("errorCode"); - pigeonResult.setErrorCode( (errorCode == null) ? null : ((errorCode instanceof Integer) ? (Integer)errorCode : (Long)errorCode)); + pigeonResult.setErrorCode((errorCode == null) ? null : ((errorCode instanceof Integer) ? (Integer)errorCode : (Long)errorCode)); Object description = map.get("description"); - pigeonResult.setDescription((String) description); + pigeonResult.setDescription((String)description); return pigeonResult; } } @@ -201,11 +210,7 @@ Map toMap() { /** Generated class from Pigeon that represents data sent in messages. */ public static class WebViewPoint { private @NonNull Long x; - - public @NonNull Long getX() { - return x; - } - + public @NonNull Long getX() { return x; } public void setX(@NonNull Long setterArg) { if (setterArg == null) { throw new IllegalStateException("Nonnull field \"x\" is null."); @@ -214,11 +219,7 @@ public void setX(@NonNull Long setterArg) { } private @NonNull Long y; - - public @NonNull Long getY() { - return y; - } - + public @NonNull Long getY() { return y; } public void setY(@NonNull Long setterArg) { if (setterArg == null) { throw new IllegalStateException("Nonnull field \"y\" is null."); @@ -226,24 +227,19 @@ public void setY(@NonNull Long setterArg) { this.y = setterArg; } - /** Constructor is private to enforce null safety; use Builder. */ + /**Constructor is private to enforce null safety; use Builder. */ private WebViewPoint() {} - public static final class Builder { private @Nullable Long x; - public @NonNull Builder setX(@NonNull Long setterArg) { this.x = setterArg; return this; } - private @Nullable Long y; - public @NonNull Builder setY(@NonNull Long setterArg) { this.y = setterArg; return this; } - public @NonNull WebViewPoint build() { WebViewPoint pigeonReturn = new WebViewPoint(); pigeonReturn.setX(x); @@ -251,21 +247,18 @@ public static final class Builder { return pigeonReturn; } } - - @NonNull - Map toMap() { + @NonNull Map toMap() { Map toMapResult = new HashMap<>(); toMapResult.put("x", x); toMapResult.put("y", y); return toMapResult; } - static @NonNull WebViewPoint fromMap(@NonNull Map map) { WebViewPoint pigeonResult = new WebViewPoint(); Object x = map.get("x"); - pigeonResult.setX((x == null) ? null : ((x instanceof Integer) ? (Integer) x : (Long) x)); + pigeonResult.setX((x == null) ? null : ((x instanceof Integer) ? (Integer)x : (Long)x)); Object y = map.get("y"); - pigeonResult.setY((y == null) ? null : ((y instanceof Integer) ? (Integer) y : (Long) y)); + pigeonResult.setY((y == null) ? null : ((y instanceof Integer) ? (Integer)y : (Long)y)); return pigeonResult; } } @@ -274,104 +267,86 @@ public interface Result { void success(T result); void error(Throwable error); } - - private static class JavaObjectHostApiCodec extends StandardMessageCodec { - public static final JavaObjectHostApiCodec INSTANCE = new JavaObjectHostApiCodec(); - - private JavaObjectHostApiCodec() {} - } - - /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + /** + * Handles methods calls to the native Java Object class. + * + * Also handles calls to remove the reference to an instance with `dispose`. + * + * See https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html. + * + * Generated interface from Pigeon that represents a handler of messages from Flutter. + */ public interface JavaObjectHostApi { void dispose(@NonNull Long identifier); /** The codec used by JavaObjectHostApi. */ static MessageCodec getCodec() { - return JavaObjectHostApiCodec.INSTANCE; - } - - /** - * Sets up an instance of `JavaObjectHostApi` to handle messages through the `binaryMessenger`. - */ + return new StandardMessageCodec(); } + /**Sets up an instance of `JavaObjectHostApi` to handle messages through the `binaryMessenger`. */ static void setup(BinaryMessenger binaryMessenger, JavaObjectHostApi api) { { BasicMessageChannel channel = - new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.JavaObjectHostApi.dispose", getCodec()); + new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.JavaObjectHostApi.dispose", getCodec()); if (api != null) { - channel.setMessageHandler( - (message, reply) -> { - Map wrapped = new HashMap<>(); - try { - ArrayList args = (ArrayList) message; - Number identifierArg = (Number) args.get(0); - if (identifierArg == null) { - throw new NullPointerException("identifierArg unexpectedly null."); - } - api.dispose((identifierArg == null) ? null : identifierArg.longValue()); - wrapped.put("result", null); - } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); + channel.setMessageHandler((message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList)message; + assert args != null; + Number identifierArg = (Number)args.get(0); + if (identifierArg == null) { + throw new NullPointerException("identifierArg unexpectedly null."); + } + api.dispose((identifierArg == null) ? null : identifierArg.longValue()); + wrapped.put("result", null); + } + catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); } else { channel.setMessageHandler(null); } } } } - - private static class JavaObjectFlutterApiCodec extends StandardMessageCodec { - public static final JavaObjectFlutterApiCodec INSTANCE = new JavaObjectFlutterApiCodec(); - - private JavaObjectFlutterApiCodec() {} - } - - /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ + /** + * Handles callbacks methods for the native Java Object class. + * + * See https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html. + * + * Generated class from Pigeon that represents Flutter messages that can be called from Java. + */ public static class JavaObjectFlutterApi { private final BinaryMessenger binaryMessenger; - - public JavaObjectFlutterApi(BinaryMessenger argBinaryMessenger) { + public JavaObjectFlutterApi(BinaryMessenger argBinaryMessenger){ this.binaryMessenger = argBinaryMessenger; } - public interface Reply { void reply(T reply); } - + /** The codec used by JavaObjectFlutterApi. */ static MessageCodec getCodec() { - return JavaObjectFlutterApiCodec.INSTANCE; + return new StandardMessageCodec(); } - public void dispose(@NonNull Long identifierArg, Reply callback) { BasicMessageChannel channel = - new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.JavaObjectFlutterApi.dispose", getCodec()); - channel.send( - new ArrayList(Arrays.asList(identifierArg)), - channelReply -> { - callback.reply(null); - }); + new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.JavaObjectFlutterApi.dispose", getCodec()); + channel.send(new ArrayList(Collections.singletonList(identifierArg)), channelReply -> { + callback.reply(null); + }); } } - - private static class CookieManagerHostApiCodec extends StandardMessageCodec { - public static final CookieManagerHostApiCodec INSTANCE = new CookieManagerHostApiCodec(); - private CookieManagerHostApiCodec() {} - } - - /** Generated interface from Pigeon that represents a handler of messages from Flutter.*/ + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface CookieManagerHostApi { void clearCookies(Result result); void setCookie(@NonNull String url, @NonNull String value); /** The codec used by CookieManagerHostApi. */ static MessageCodec getCodec() { - return CookieManagerHostApiCodec.INSTANCE; - } - - /** Sets up an instance of `CookieManagerHostApi` to handle messages through the `binaryMessenger`. */ + return new StandardMessageCodec(); } + /**Sets up an instance of `CookieManagerHostApi` to handle messages through the `binaryMessenger`. */ static void setup(BinaryMessenger binaryMessenger, CookieManagerHostApi api) { { BasicMessageChannel channel = @@ -410,6 +385,7 @@ public void error(Throwable error) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; String urlArg = (String)args.get(0); if (urlArg == null) { throw new NullPointerException("urlArg unexpectedly null."); @@ -435,73 +411,63 @@ public void error(Throwable error) { private static class WebViewHostApiCodec extends StandardMessageCodec { public static final WebViewHostApiCodec INSTANCE = new WebViewHostApiCodec(); private WebViewHostApiCodec() {} - @Override - protected Object readValueOfType(byte type, ByteBuffer buffer) { + protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { switch (type) { - case (byte) 128: + case (byte)128: return WebViewPoint.fromMap((Map) readValue(buffer)); - - default: + + default: return super.readValueOfType(type, buffer); + } } - @Override - protected void writeValue(ByteArrayOutputStream stream, Object value) { + protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { if (value instanceof WebViewPoint) { stream.write(128); writeValue(stream, ((WebViewPoint) value).toMap()); - } else { + } else +{ super.writeValue(stream, value); } } } - /** Generated interface from Pigeon that represents a handler of messages from Flutter.*/ + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface WebViewHostApi { - void create(@NonNull Long instanceId, @NonNullBoolean useHybridComposition); - void dispose(@NonNullLong instanceId); - void loadData(@NonNull Long instanceId, - @NonNull String data, - @Nullable String mimeType, - @Nullable String encoding); - void loadDataWithBaseUrl(@NonNullLong instanceId,@Nullable String baseUrl,@NonNull String data,@Nullable String mimeType,@Nullable String encoding,@Nullable String historyUrl); - void loadUrl(@NonNull Long instanceId, @NonNull String url, @NonNullMap headers); - void postUrl(@NonNull Long instanceId, @NonNull String url, @NonNullbyte[] data); - @Nullable - String getUrl(@NonNullLong instanceId);@NonNull - Boolean canGoBack(@NonNullLong instanceId);@NonNull - Boolean canGoForward(@NonNullLong instanceId); - void goBack(@NonNullLong instanceId); - void goForward(@NonNullLong instanceId); - void reload(@NonNullLong instanceId); - void clearCache(@NonNull Long instanceId, @NonNullBoolean includeDiskFiles); - void evaluateJavascript(@NonNull Long instanceId, @NonNullString javascriptString, Result result);@Nullable - String getTitle(@NonNullLong instanceId); - void scrollTo(@NonNull Long instanceId, @NonNull Long x, @NonNullLong y); - void scrollBy(@NonNull Long instanceId, @NonNull Long x, @NonNullLong y); - @NonNull - Long getScrollX(@NonNullLong instanceId); -@NonNull - Long getScrollY(@NonNull Long instanceId); - - @NonNull - WebViewPoint getScrollPosition(@NonNullLong instanceId); - void setWebContentsDebuggingEnabled(@NonNullBoolean enabled); - void setWebViewClient(@NonNull Long instanceId, @NonNullLong webViewClientInstanceId); - void addJavaScriptChannel(@NonNull Long instanceId, @NonNullLong javaScriptChannelInstanceId); - void removeJavaScriptChannel(@NonNull Long instanceId, @NonNullLong javaScriptChannelInstanceId); - void setDownloadListener(@NonNull Long instanceId, @NullableLong listenerInstanceId); - void setWebChromeClient(@NonNull Long instanceId, @NullableLong clientInstanceId); + void create(@NonNull Long instanceId, @NonNull Boolean useHybridComposition); + void dispose(@NonNull Long instanceId); + void loadData(@NonNull Long instanceId, @NonNull String data, @Nullable String mimeType, @Nullable String encoding); + void loadDataWithBaseUrl(@NonNull Long instanceId, @Nullable String baseUrl, @NonNull String data, @Nullable String mimeType, @Nullable String encoding, @Nullable String historyUrl); + void loadUrl(@NonNull Long instanceId, @NonNull String url, @NonNull Map headers); + void postUrl(@NonNull Long instanceId, @NonNull String url, @NonNull byte[] data); + @Nullable String getUrl(@NonNull Long instanceId); + @NonNull Boolean canGoBack(@NonNull Long instanceId); + @NonNull Boolean canGoForward(@NonNull Long instanceId); + void goBack(@NonNull Long instanceId); + void goForward(@NonNull Long instanceId); + void reload(@NonNull Long instanceId); + void clearCache(@NonNull Long instanceId, @NonNull Boolean includeDiskFiles); + void evaluateJavascript(@NonNull Long instanceId, @NonNull String javascriptString, Result result); + @Nullable String getTitle(@NonNull Long instanceId); + void scrollTo(@NonNull Long instanceId, @NonNull Long x, @NonNull Long y); + void scrollBy(@NonNull Long instanceId, @NonNull Long x, @NonNull Long y); + @NonNull Long getScrollX(@NonNull Long instanceId); + @NonNull Long getScrollY(@NonNull Long instanceId); + @NonNull WebViewPoint getScrollPosition(@NonNull Long instanceId); + void setWebContentsDebuggingEnabled(@NonNull Boolean enabled); + void setWebViewClient(@NonNull Long instanceId, @NonNull Long webViewClientInstanceId); + void addJavaScriptChannel(@NonNull Long instanceId, @NonNull Long javaScriptChannelInstanceId); + void removeJavaScriptChannel(@NonNull Long instanceId, @NonNull Long javaScriptChannelInstanceId); + void setDownloadListener(@NonNull Long instanceId, @Nullable Long listenerInstanceId); + void setWebChromeClient(@NonNull Long instanceId, @Nullable Long clientInstanceId); void setBackgroundColor(@NonNull Long instanceId, @NonNull Long color); /** The codec used by WebViewHostApi. */ static MessageCodec getCodec() { - return WebViewHostApiCodec.INSTANCE; - } - - /** Sets up an instance of `WebViewHostApi` to handle messages through the `binaryMessenger`. */ + return WebViewHostApiCodec.INSTANCE; } + /**Sets up an instance of `WebViewHostApi` to handle messages through the `binaryMessenger`. */ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { { BasicMessageChannel channel = @@ -511,6 +477,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -519,9 +486,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (useHybridCompositionArg == null) { throw new NullPointerException("useHybridCompositionArg unexpectedly null."); } - api.create( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), - useHybridCompositionArg); + api.create((instanceIdArg == null) ? null : instanceIdArg.longValue(), useHybridCompositionArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -541,6 +506,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -565,6 +531,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -574,10 +541,8 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { throw new NullPointerException("dataArg unexpectedly null."); } String mimeTypeArg = (String)args.get(2); - String encodingArg = (String)args.get(3); - api.loadData( - (instanceIdArg == null) ? null :instanceIdArg.longValue(), dataArg, mimeTypeArg, encodingArg); + api.loadData((instanceIdArg == null) ? null : instanceIdArg.longValue(), dataArg, mimeTypeArg, encodingArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -597,23 +562,20 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } String baseUrlArg = (String)args.get(1); - String dataArg = (String)args.get(2); if (dataArg == null) { throw new NullPointerException("dataArg unexpectedly null."); } String mimeTypeArg = (String)args.get(3); - String encodingArg = (String)args.get(4); - String historyUrlArg = (String)args.get(5); - - api.loadDataWithBaseUrl((instanceIdArg == null) ? null :instanceIdArg.longValue(), baseUrlArg, dataArg, mimeTypeArg, encodingArg, historyUrlArg); + api.loadDataWithBaseUrl((instanceIdArg == null) ? null : instanceIdArg.longValue(), baseUrlArg, dataArg, mimeTypeArg, encodingArg, historyUrlArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -633,6 +595,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -645,10 +608,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (headersArg == null) { throw new NullPointerException("headersArg unexpectedly null."); } - api.loadUrl( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), - urlArg, - headersArg); + api.loadUrl((instanceIdArg == null) ? null : instanceIdArg.longValue(), urlArg, headersArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -668,6 +628,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -680,8 +641,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (dataArg == null) { throw new NullPointerException("dataArg unexpectedly null."); } - api.postUrl( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), urlArg, dataArg); + api.postUrl((instanceIdArg == null) ? null : instanceIdArg.longValue(), urlArg, dataArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -701,12 +661,12 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - String output = - api.getUrl((instanceIdArg == null) ? null : instanceIdArg.longValue()); + String output = api.getUrl((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", output); } catch (Error | RuntimeException exception) { @@ -726,12 +686,12 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - Boolean output = - api.canGoBack((instanceIdArg == null) ? null : instanceIdArg.longValue()); + Boolean output = api.canGoBack((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", output); } catch (Error | RuntimeException exception) { @@ -751,12 +711,12 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - Boolean output = - api.canGoForward((instanceIdArg == null) ? null : instanceIdArg.longValue()); + Boolean output = api.canGoForward((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", output); } catch (Error | RuntimeException exception) { @@ -776,6 +736,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -800,6 +761,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -824,6 +786,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -848,6 +811,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -856,9 +820,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { if (includeDiskFilesArg == null) { throw new NullPointerException("includeDiskFilesArg unexpectedly null."); } - api.clearCache( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), - includeDiskFilesArg); + api.clearCache((instanceIdArg == null) ? null : instanceIdArg.longValue(), includeDiskFilesArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -878,6 +840,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -897,7 +860,7 @@ public void error(Throwable error) { } }; - api.evaluateJavascript((instanceIdArg == null) ? null :instanceIdArg.longValue(), javascriptStringArg, resultCallback); + api.evaluateJavascript((instanceIdArg == null) ? null : instanceIdArg.longValue(), javascriptStringArg, resultCallback); } catch (Error | RuntimeException exception) { wrapped.put("error", wrapError(exception)); @@ -916,12 +879,12 @@ public void error(Throwable error) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - String output = - api.getTitle((instanceIdArg == null) ? null : instanceIdArg.longValue()); + String output = api.getTitle((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", output); } catch (Error | RuntimeException exception) { @@ -941,6 +904,7 @@ public void error(Throwable error) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -953,10 +917,7 @@ public void error(Throwable error) { if (yArg == null) { throw new NullPointerException("yArg unexpectedly null."); } - api.scrollTo( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), - (xArg == null) ? null : xArg.longValue(), - (yArg == null) ? null : yArg.longValue()); + api.scrollTo((instanceIdArg == null) ? null : instanceIdArg.longValue(), (xArg == null) ? null : xArg.longValue(), (yArg == null) ? null : yArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -976,6 +937,7 @@ public void error(Throwable error) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -988,10 +950,7 @@ public void error(Throwable error) { if (yArg == null) { throw new NullPointerException("yArg unexpectedly null."); } - api.scrollBy( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), - (xArg == null) ? null : xArg.longValue(), - (yArg == null) ? null : yArg.longValue()); + api.scrollBy((instanceIdArg == null) ? null : instanceIdArg.longValue(), (xArg == null) ? null : xArg.longValue(), (yArg == null) ? null : yArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1011,12 +970,12 @@ public void error(Throwable error) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - Long output = - api.getScrollX((instanceIdArg == null) ? null : instanceIdArg.longValue()); + Long output = api.getScrollX((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", output); } catch (Error | RuntimeException exception) { @@ -1036,39 +995,37 @@ public void error(Throwable error) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } - Long output = - api.getScrollY((instanceIdArg == null) ? null : instanceIdArg.longValue()); - wrapped.put("result", output); - } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); + Long output = api.getScrollY((instanceIdArg == null) ? null : instanceIdArg.longValue()); + wrapped.put("result", output); + } + catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); } else { channel.setMessageHandler(null); } } { BasicMessageChannel channel = - new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.WebViewHostApi.getScrollPosition", getCodec()); + new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.WebViewHostApi.getScrollPosition", getCodec()); if (api != null) { - channel.setMessageHandler( - (message, reply) -> { - Map wrapped = new HashMap<>(); - try { - ArrayList args = (ArrayList) message; - Number instanceIdArg = (Number) args.get(0); - if (instanceIdArg == null) { - throw new NullPointerException("instanceIdArg unexpectedly null."); - } - WebViewPoint output = - api.getScrollPosition( - (instanceIdArg == null) ? null : instanceIdArg.longValue()); + channel.setMessageHandler((message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList)message; + assert args != null; + Number instanceIdArg = (Number)args.get(0); + if (instanceIdArg == null) { + throw new NullPointerException("instanceIdArg unexpectedly null."); + } + WebViewPoint output = api.getScrollPosition((instanceIdArg == null) ? null : instanceIdArg.longValue()); wrapped.put("result", output); } catch (Error | RuntimeException exception) { @@ -1088,6 +1045,7 @@ public void error(Throwable error) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Boolean enabledArg = (Boolean)args.get(0); if (enabledArg == null) { throw new NullPointerException("enabledArg unexpectedly null."); @@ -1112,6 +1070,7 @@ public void error(Throwable error) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1120,10 +1079,7 @@ public void error(Throwable error) { if (webViewClientInstanceIdArg == null) { throw new NullPointerException("webViewClientInstanceIdArg unexpectedly null."); } - api.setWebViewClient((instanceIdArg == null) ? null : instanceIdArg.longValue(), - (webViewClientInstanceIdArg == null) - ? null - : webViewClientInstanceIdArg.longValue()); + api.setWebViewClient((instanceIdArg == null) ? null : instanceIdArg.longValue(), (webViewClientInstanceIdArg == null) ? null : webViewClientInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1143,6 +1099,7 @@ public void error(Throwable error) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1151,10 +1108,7 @@ public void error(Throwable error) { if (javaScriptChannelInstanceIdArg == null) { throw new NullPointerException("javaScriptChannelInstanceIdArg unexpectedly null."); } - api.addJavaScriptChannel((instanceIdArg == null) ? null : instanceIdArg.longValue(), - (javaScriptChannelInstanceIdArg == null) - ? null - : javaScriptChannelInstanceIdArg.longValue()); + api.addJavaScriptChannel((instanceIdArg == null) ? null : instanceIdArg.longValue(), (javaScriptChannelInstanceIdArg == null) ? null : javaScriptChannelInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1174,6 +1128,7 @@ public void error(Throwable error) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1182,10 +1137,7 @@ public void error(Throwable error) { if (javaScriptChannelInstanceIdArg == null) { throw new NullPointerException("javaScriptChannelInstanceIdArg unexpectedly null."); } - api.removeJavaScriptChannel((instanceIdArg == null) ? null : instanceIdArg.longValue(), - (javaScriptChannelInstanceIdArg == null) - ? null - : javaScriptChannelInstanceIdArg.longValue()); + api.removeJavaScriptChannel((instanceIdArg == null) ? null : instanceIdArg.longValue(), (javaScriptChannelInstanceIdArg == null) ? null : javaScriptChannelInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1205,14 +1157,13 @@ public void error(Throwable error) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } Number listenerInstanceIdArg = (Number)args.get(1); - - api.setDownloadListener((instanceIdArg == null) ? null : instanceIdArg.longValue(), - (listenerInstanceIdArg == null) ? null : listenerInstanceIdArg.longValue()); + api.setDownloadListener((instanceIdArg == null) ? null : instanceIdArg.longValue(), (listenerInstanceIdArg == null) ? null : listenerInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1232,14 +1183,13 @@ public void error(Throwable error) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } Number clientInstanceIdArg = (Number)args.get(1); - - api.setWebChromeClient((instanceIdArg == null) ? null : instanceIdArg.longValue(), - (clientInstanceIdArg == null) ? null : clientInstanceIdArg.longValue()); + api.setWebChromeClient((instanceIdArg == null) ? null : instanceIdArg.longValue(), (clientInstanceIdArg == null) ? null : clientInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1259,6 +1209,7 @@ public void error(Throwable error) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1267,9 +1218,7 @@ public void error(Throwable error) { if (colorArg == null) { throw new NullPointerException("colorArg unexpectedly null."); } - api.setBackgroundColor( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), - (colorArg == null) ? null : colorArg.longValue()); + api.setBackgroundColor((instanceIdArg == null) ? null : instanceIdArg.longValue(), (colorArg == null) ? null : colorArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1283,12 +1232,7 @@ public void error(Throwable error) { } } } - private static class WebSettingsHostApiCodec extends StandardMessageCodec { - public static final WebSettingsHostApiCodec INSTANCE = new WebSettingsHostApiCodec(); - private WebSettingsHostApiCodec() {} - } - - /** Generated interface from Pigeon that represents a handler of messages from Flutter.*/ + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface WebSettingsHostApi { void create(@NonNull Long instanceId, @NonNull Long webViewInstanceId); void dispose(@NonNull Long instanceId); @@ -1303,15 +1247,13 @@ public interface WebSettingsHostApi { void setUseWideViewPort(@NonNull Long instanceId, @NonNull Boolean use); void setDisplayZoomControls(@NonNull Long instanceId, @NonNull Boolean enabled); void setBuiltInZoomControls(@NonNull Long instanceId, @NonNull Boolean enabled); - void setAllowFileAccess(Long instanceId, Boolean enabled); + void setAllowFileAccess(@NonNull Long instanceId, @NonNull Boolean enabled); void setGeolocationEnabled(@NonNull Long instanceId, @NonNull Boolean enabled); /** The codec used by WebSettingsHostApi. */ static MessageCodec getCodec() { - return WebSettingsHostApiCodec.INSTANCE; - } - - /** Sets up an instance of `WebSettingsHostApi` to handle messages through the `binaryMessenger`. */ + return new StandardMessageCodec(); } + /**Sets up an instance of `WebSettingsHostApi` to handle messages through the `binaryMessenger`. */ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { { BasicMessageChannel channel = @@ -1321,6 +1263,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1329,9 +1272,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (webViewInstanceIdArg == null) { throw new NullPointerException("webViewInstanceIdArg unexpectedly null."); } - api.create( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), - (webViewInstanceIdArg == null) ? null : webViewInstanceIdArg.longValue()); + api.create((instanceIdArg == null) ? null : instanceIdArg.longValue(), (webViewInstanceIdArg == null) ? null : webViewInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1351,6 +1292,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1375,6 +1317,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1383,8 +1326,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (flagArg == null) { throw new NullPointerException("flagArg unexpectedly null."); } - api.setDomStorageEnabled( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), flagArg); + api.setDomStorageEnabled((instanceIdArg == null) ? null : instanceIdArg.longValue(), flagArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1404,6 +1346,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1412,8 +1355,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (flagArg == null) { throw new NullPointerException("flagArg unexpectedly null."); } - api.setJavaScriptCanOpenWindowsAutomatically( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), flagArg); + api.setJavaScriptCanOpenWindowsAutomatically((instanceIdArg == null) ? null : instanceIdArg.longValue(), flagArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1433,6 +1375,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1441,8 +1384,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (supportArg == null) { throw new NullPointerException("supportArg unexpectedly null."); } - api.setSupportMultipleWindows( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), supportArg); + api.setSupportMultipleWindows((instanceIdArg == null) ? null : instanceIdArg.longValue(), supportArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1462,6 +1404,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1470,8 +1413,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (flagArg == null) { throw new NullPointerException("flagArg unexpectedly null."); } - api.setJavaScriptEnabled( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), flagArg); + api.setJavaScriptEnabled((instanceIdArg == null) ? null : instanceIdArg.longValue(), flagArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1491,13 +1433,13 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); } String userAgentStringArg = (String)args.get(1); - - api.setUserAgentString((instanceIdArg == null) ? null :instanceIdArg.longValue(), userAgentStringArg); + api.setUserAgentString((instanceIdArg == null) ? null : instanceIdArg.longValue(), userAgentStringArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1517,6 +1459,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1525,8 +1468,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (requireArg == null) { throw new NullPointerException("requireArg unexpectedly null."); } - api.setMediaPlaybackRequiresUserGesture( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), requireArg); + api.setMediaPlaybackRequiresUserGesture((instanceIdArg == null) ? null : instanceIdArg.longValue(), requireArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1546,6 +1488,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1554,8 +1497,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (supportArg == null) { throw new NullPointerException("supportArg unexpectedly null."); } - api.setSupportZoom( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), supportArg); + api.setSupportZoom((instanceIdArg == null) ? null : instanceIdArg.longValue(), supportArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1575,6 +1517,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1583,8 +1526,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (overviewArg == null) { throw new NullPointerException("overviewArg unexpectedly null."); } - api.setLoadWithOverviewMode( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), overviewArg); + api.setLoadWithOverviewMode((instanceIdArg == null) ? null : instanceIdArg.longValue(), overviewArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1604,6 +1546,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1612,8 +1555,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (useArg == null) { throw new NullPointerException("useArg unexpectedly null."); } - api.setUseWideViewPort( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), useArg); + api.setUseWideViewPort((instanceIdArg == null) ? null : instanceIdArg.longValue(), useArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1633,6 +1575,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1641,8 +1584,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (enabledArg == null) { throw new NullPointerException("enabledArg unexpectedly null."); } - api.setDisplayZoomControls( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), enabledArg); + api.setDisplayZoomControls((instanceIdArg == null) ? null : instanceIdArg.longValue(), enabledArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1662,6 +1604,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1670,8 +1613,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (enabledArg == null) { throw new NullPointerException("enabledArg unexpectedly null."); } - api.setBuiltInZoomControls( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), enabledArg); + api.setBuiltInZoomControls((instanceIdArg == null) ? null : instanceIdArg.longValue(), enabledArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1691,6 +1633,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1699,8 +1642,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (enabledArg == null) { throw new NullPointerException("enabledArg unexpectedly null."); } - api.setAllowFileAccess( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), enabledArg); + api.setAllowFileAccess((instanceIdArg == null) ? null : instanceIdArg.longValue(), enabledArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1720,6 +1662,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1728,7 +1671,7 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { if (enabledArg == null) { throw new NullPointerException("enabledArg unexpectedly null."); } - api.setGeolocationEnabled(instanceIdArg.longValue(), enabledArg); + api.setGeolocationEnabled((instanceIdArg == null) ? null : instanceIdArg.longValue(), enabledArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1742,21 +1685,14 @@ static void setup(BinaryMessenger binaryMessenger, WebSettingsHostApi api) { } } } - private static class JavaScriptChannelHostApiCodec extends StandardMessageCodec { - public static final JavaScriptChannelHostApiCodec INSTANCE = new JavaScriptChannelHostApiCodec(); - private JavaScriptChannelHostApiCodec() {} - } - - /** Generated interface from Pigeon that represents a handler of messages from Flutter.*/ + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface JavaScriptChannelHostApi { void create(@NonNull Long instanceId, @NonNull String channelName); /** The codec used by JavaScriptChannelHostApi. */ static MessageCodec getCodec() { - return JavaScriptChannelHostApiCodec.INSTANCE; - } - - /** Sets up an instance of `JavaScriptChannelHostApi` to handle messages through the `binaryMessenger`. */ + return new StandardMessageCodec(); } + /**Sets up an instance of `JavaScriptChannelHostApi` to handle messages through the `binaryMessenger`. */ static void setup(BinaryMessenger binaryMessenger, JavaScriptChannelHostApi api) { { BasicMessageChannel channel = @@ -1766,6 +1702,7 @@ static void setup(BinaryMessenger binaryMessenger, JavaScriptChannelHostApi api) Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1774,8 +1711,7 @@ static void setup(BinaryMessenger binaryMessenger, JavaScriptChannelHostApi api) if (channelNameArg == null) { throw new NullPointerException("channelNameArg unexpectedly null."); } - api.create( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), channelNameArg); + api.create((instanceIdArg == null) ? null : instanceIdArg.longValue(), channelNameArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1789,12 +1725,7 @@ static void setup(BinaryMessenger binaryMessenger, JavaScriptChannelHostApi api) } } } - private static class JavaScriptChannelFlutterApiCodec extends StandardMessageCodec { - public static final JavaScriptChannelFlutterApiCodec INSTANCE = new JavaScriptChannelFlutterApiCodec(); - private JavaScriptChannelFlutterApiCodec() {} - } - - /** Generated class from Pigeon that represents Flutter messages that can be called from Java.*/ + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ public static class JavaScriptChannelFlutterApi { private final BinaryMessenger binaryMessenger; public JavaScriptChannelFlutterApi(BinaryMessenger argBinaryMessenger){ @@ -1803,19 +1734,18 @@ public JavaScriptChannelFlutterApi(BinaryMessenger argBinaryMessenger){ public interface Reply { void reply(T reply); } + /** The codec used by JavaScriptChannelFlutterApi. */ static MessageCodec getCodec() { - return JavaScriptChannelFlutterApiCodec.INSTANCE; + return new StandardMessageCodec(); } - public void dispose(@NonNull Long instanceIdArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.JavaScriptChannelFlutterApi.dispose", getCodec()); - channel.send(new ArrayList(Arrays.asList(instanceIdArg)), channelReply -> { + channel.send(new ArrayList(Collections.singletonList(instanceIdArg)), channelReply -> { callback.reply(null); }); } - public void postMessage( - @NonNull Long instanceIdArg, @NonNull String messageArg, Reply callback) { + public void postMessage(@NonNull Long instanceIdArg, @NonNull String messageArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.JavaScriptChannelFlutterApi.postMessage", getCodec()); channel.send(new ArrayList(Arrays.asList(instanceIdArg, messageArg)), channelReply -> { @@ -1823,21 +1753,14 @@ public void postMessage( }); } } - private static class WebViewClientHostApiCodec extends StandardMessageCodec { - public static final WebViewClientHostApiCodec INSTANCE = new WebViewClientHostApiCodec(); - private WebViewClientHostApiCodec() {} - } - - /** Generated interface from Pigeon that represents a handler of messages from Flutter.*/ + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface WebViewClientHostApi { void create(@NonNull Long instanceId, @NonNull Boolean shouldOverrideUrlLoading); /** The codec used by WebViewClientHostApi. */ static MessageCodec getCodec() { - return WebViewClientHostApiCodec.INSTANCE; - } - - /** Sets up an instance of `WebViewClientHostApi` to handle messages through the `binaryMessenger`. */ + return new StandardMessageCodec(); } + /**Sets up an instance of `WebViewClientHostApi` to handle messages through the `binaryMessenger`. */ static void setup(BinaryMessenger binaryMessenger, WebViewClientHostApi api) { { BasicMessageChannel channel = @@ -1847,6 +1770,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewClientHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -1855,9 +1779,7 @@ static void setup(BinaryMessenger binaryMessenger, WebViewClientHostApi api) { if (shouldOverrideUrlLoadingArg == null) { throw new NullPointerException("shouldOverrideUrlLoadingArg unexpectedly null."); } - api.create( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), - shouldOverrideUrlLoadingArg); + api.create((instanceIdArg == null) ? null : instanceIdArg.longValue(), shouldOverrideUrlLoadingArg); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -1875,36 +1797,36 @@ private static class WebViewClientFlutterApiCodec extends StandardMessageCodec { public static final WebViewClientFlutterApiCodec INSTANCE = new WebViewClientFlutterApiCodec(); private WebViewClientFlutterApiCodec() {} @Override - protected Object readValueOfType(byte type, ByteBuffer buffer) { + protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { switch (type) { - case (byte)128: + case (byte)128: return WebResourceErrorData.fromMap((Map) readValue(buffer)); - - case (byte)129: + + case (byte)129: return WebResourceRequestData.fromMap((Map) readValue(buffer)); - - default: + + default: return super.readValueOfType(type, buffer); - + } } @Override - protected void writeValue(ByteArrayOutputStream stream, Object value) { + protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { if (value instanceof WebResourceErrorData) { stream.write(128); writeValue(stream, ((WebResourceErrorData) value).toMap()); - } else + } else if (value instanceof WebResourceRequestData) { stream.write(129); writeValue(stream, ((WebResourceRequestData) value).toMap()); - } else + } else { super.writeValue(stream, value); } } } - /** Generated class from Pigeon that represents Flutter messages that can be called from Java.*/ + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ public static class WebViewClientFlutterApi { private final BinaryMessenger binaryMessenger; public WebViewClientFlutterApi(BinaryMessenger argBinaryMessenger){ @@ -1913,62 +1835,53 @@ public WebViewClientFlutterApi(BinaryMessenger argBinaryMessenger){ public interface Reply { void reply(T reply); } + /** The codec used by WebViewClientFlutterApi. */ static MessageCodec getCodec() { - return WebViewClientFlutterApiCodec.INSTANCE; + return WebViewClientFlutterApiCodec.INSTANCE; } - public void dispose(@NonNull Long instanceIdArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.WebViewClientFlutterApi.dispose", getCodec()); - channel.send(new ArrayList(Arrays.asList(instanceIdArg)), channelReply -> { + channel.send(new ArrayList(Collections.singletonList(instanceIdArg)), channelReply -> { callback.reply(null); }); } - public void onPageStarted(@NonNull Long instanceIdArg, - @NonNull Long webViewInstanceIdArg, - @NonNull String urlArg, - Reply callback) { + public void onPageStarted(@NonNull Long instanceIdArg, @NonNull Long webViewInstanceIdArg, @NonNull String urlArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.WebViewClientFlutterApi.onPageStarted", getCodec()); channel.send(new ArrayList(Arrays.asList(instanceIdArg, webViewInstanceIdArg, urlArg)), channelReply -> { callback.reply(null); }); } - public void onPageFinished(@NonNull Long instanceIdArg, - @NonNull Long webViewInstanceIdArg, - @NonNull String urlArg, - Reply callback) { + public void onPageFinished(@NonNull Long instanceIdArg, @NonNull Long webViewInstanceIdArg, @NonNull String urlArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.WebViewClientFlutterApi.onPageFinished", getCodec()); channel.send(new ArrayList(Arrays.asList(instanceIdArg, webViewInstanceIdArg, urlArg)), channelReply -> { callback.reply(null); }); } - public void onReceivedRequestError(@NonNullLong instanceIdArg,@NonNull Long webViewInstanceIdArg,@NonNull WebResourceRequestData requestArg,@NonNull WebResourceErrorData errorArg, Reply callback) { + public void onReceivedRequestError(@NonNull Long instanceIdArg, @NonNull Long webViewInstanceIdArg, @NonNull WebResourceRequestData requestArg, @NonNull WebResourceErrorData errorArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedRequestError", getCodec()); channel.send(new ArrayList(Arrays.asList(instanceIdArg, webViewInstanceIdArg, requestArg, errorArg)), channelReply -> { callback.reply(null); }); } - public void onReceivedError(@NonNullLong instanceIdArg,@NonNull Long webViewInstanceIdArg,@NonNull Long errorCodeArg,@NonNull String descriptionArg,@NonNull String failingUrlArg, Reply callback) { + public void onReceivedError(@NonNull Long instanceIdArg, @NonNull Long webViewInstanceIdArg, @NonNull Long errorCodeArg, @NonNull String descriptionArg, @NonNull String failingUrlArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedError", getCodec()); channel.send(new ArrayList(Arrays.asList(instanceIdArg, webViewInstanceIdArg, errorCodeArg, descriptionArg, failingUrlArg)), channelReply -> { callback.reply(null); }); } - public void requestLoading(@NonNullLong instanceIdArg,@NonNull Long webViewInstanceIdArg,@NonNull WebResourceRequestData requestArg, Reply callback) { + public void requestLoading(@NonNull Long instanceIdArg, @NonNull Long webViewInstanceIdArg, @NonNull WebResourceRequestData requestArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.WebViewClientFlutterApi.requestLoading", getCodec()); channel.send(new ArrayList(Arrays.asList(instanceIdArg, webViewInstanceIdArg, requestArg)), channelReply -> { callback.reply(null); }); } - public void urlLoading(@NonNull Long instanceIdArg, - @NonNull Long webViewInstanceIdArg, - @NonNull String urlArg, - Reply callback) { + public void urlLoading(@NonNull Long instanceIdArg, @NonNull Long webViewInstanceIdArg, @NonNull String urlArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.WebViewClientFlutterApi.urlLoading", getCodec()); channel.send(new ArrayList(Arrays.asList(instanceIdArg, webViewInstanceIdArg, urlArg)), channelReply -> { @@ -1976,21 +1889,14 @@ public void urlLoading(@NonNull Long instanceIdArg, }); } } - private static class DownloadListenerHostApiCodec extends StandardMessageCodec { - public static final DownloadListenerHostApiCodec INSTANCE = new DownloadListenerHostApiCodec(); - private DownloadListenerHostApiCodec() {} - } - - /** Generated interface from Pigeon that represents a handler of messages from Flutter.*/ + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface DownloadListenerHostApi { void create(@NonNull Long instanceId); /** The codec used by DownloadListenerHostApi. */ static MessageCodec getCodec() { - return DownloadListenerHostApiCodec.INSTANCE; - } - - /** Sets up an instance of `DownloadListenerHostApi` to handle messages through the `binaryMessenger`. */ + return new StandardMessageCodec(); } + /**Sets up an instance of `DownloadListenerHostApi` to handle messages through the `binaryMessenger`. */ static void setup(BinaryMessenger binaryMessenger, DownloadListenerHostApi api) { { BasicMessageChannel channel = @@ -2000,6 +1906,7 @@ static void setup(BinaryMessenger binaryMessenger, DownloadListenerHostApi api) Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -2018,12 +1925,7 @@ static void setup(BinaryMessenger binaryMessenger, DownloadListenerHostApi api) } } } - private static class DownloadListenerFlutterApiCodec extends StandardMessageCodec { - public static final DownloadListenerFlutterApiCodec INSTANCE = new DownloadListenerFlutterApiCodec(); - private DownloadListenerFlutterApiCodec() {} - } - - /** Generated class from Pigeon that represents Flutter messages that can be called from Java.*/ + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ public static class DownloadListenerFlutterApi { private final BinaryMessenger binaryMessenger; public DownloadListenerFlutterApi(BinaryMessenger argBinaryMessenger){ @@ -2032,18 +1934,18 @@ public DownloadListenerFlutterApi(BinaryMessenger argBinaryMessenger){ public interface Reply { void reply(T reply); } + /** The codec used by DownloadListenerFlutterApi. */ static MessageCodec getCodec() { - return DownloadListenerFlutterApiCodec.INSTANCE; + return new StandardMessageCodec(); } - public void dispose(@NonNull Long instanceIdArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.DownloadListenerFlutterApi.dispose", getCodec()); - channel.send(new ArrayList(Arrays.asList(instanceIdArg)), channelReply -> { + channel.send(new ArrayList(Collections.singletonList(instanceIdArg)), channelReply -> { callback.reply(null); }); } - public void onDownloadStart(@NonNullLong instanceIdArg,@NonNull String urlArg,@NonNull String userAgentArg,@NonNull String contentDispositionArg,@NonNull String mimetypeArg,@NonNull Long contentLengthArg, Reply callback) { + public void onDownloadStart(@NonNull Long instanceIdArg, @NonNull String urlArg, @NonNull String userAgentArg, @NonNull String contentDispositionArg, @NonNull String mimetypeArg, @NonNull Long contentLengthArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.DownloadListenerFlutterApi.onDownloadStart", getCodec()); channel.send(new ArrayList(Arrays.asList(instanceIdArg, urlArg, userAgentArg, contentDispositionArg, mimetypeArg, contentLengthArg)), channelReply -> { @@ -2051,21 +1953,14 @@ public void onDownloadStart(@NonNullLong instanceIdArg,@NonNull String urlArg,@N }); } } - private static class WebChromeClientHostApiCodec extends StandardMessageCodec { - public static final WebChromeClientHostApiCodec INSTANCE = new WebChromeClientHostApiCodec(); - private WebChromeClientHostApiCodec() {} - } - - /** Generated interface from Pigeon that represents a handler of messages from Flutter.*/ + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface WebChromeClientHostApi { void create(@NonNull Long instanceId, @NonNull Long webViewClientInstanceId); /** The codec used by WebChromeClientHostApi. */ static MessageCodec getCodec() { - return WebChromeClientHostApiCodec.INSTANCE; - } - - /** Sets up an instance of `WebChromeClientHostApi` to handle messages through the `binaryMessenger`. */ + return new StandardMessageCodec(); } + /**Sets up an instance of `WebChromeClientHostApi` to handle messages through the `binaryMessenger`. */ static void setup(BinaryMessenger binaryMessenger, WebChromeClientHostApi api) { { BasicMessageChannel channel = @@ -2075,6 +1970,7 @@ static void setup(BinaryMessenger binaryMessenger, WebChromeClientHostApi api) { Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; Number instanceIdArg = (Number)args.get(0); if (instanceIdArg == null) { throw new NullPointerException("instanceIdArg unexpectedly null."); @@ -2083,11 +1979,7 @@ static void setup(BinaryMessenger binaryMessenger, WebChromeClientHostApi api) { if (webViewClientInstanceIdArg == null) { throw new NullPointerException("webViewClientInstanceIdArg unexpectedly null."); } - api.create( - (instanceIdArg == null) ? null : instanceIdArg.longValue(), - (webViewClientInstanceIdArg == null) - ? null - : webViewClientInstanceIdArg.longValue()); + api.create((instanceIdArg == null) ? null : instanceIdArg.longValue(), (webViewClientInstanceIdArg == null) ? null : webViewClientInstanceIdArg.longValue()); wrapped.put("result", null); } catch (Error | RuntimeException exception) { @@ -2101,24 +1993,15 @@ static void setup(BinaryMessenger binaryMessenger, WebChromeClientHostApi api) { } } } - private static class FlutterAssetManagerHostApiCodec extends StandardMessageCodec { - public static final FlutterAssetManagerHostApiCodec INSTANCE = new FlutterAssetManagerHostApiCodec(); - private FlutterAssetManagerHostApiCodec() {} - } - - /** Generated interface from Pigeon that represents a handler of messages from Flutter.*/ + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface FlutterAssetManagerHostApi { - @NonNull - List list(@NonNullString path); -@NonNull - String getAssetFilePathByName(@NonNull String name); + @NonNull List list(@NonNull String path); + @NonNull String getAssetFilePathByName(@NonNull String name); /** The codec used by FlutterAssetManagerHostApi. */ static MessageCodec getCodec() { - return FlutterAssetManagerHostApiCodec.INSTANCE; - } - - /** Sets up an instance of `FlutterAssetManagerHostApi` to handle messages through the `binaryMessenger`. */ + return new StandardMessageCodec(); } + /**Sets up an instance of `FlutterAssetManagerHostApi` to handle messages through the `binaryMessenger`. */ static void setup(BinaryMessenger binaryMessenger, FlutterAssetManagerHostApi api) { { BasicMessageChannel channel = @@ -2128,6 +2011,7 @@ static void setup(BinaryMessenger binaryMessenger, FlutterAssetManagerHostApi ap Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; String pathArg = (String)args.get(0); if (pathArg == null) { throw new NullPointerException("pathArg unexpectedly null."); @@ -2152,6 +2036,7 @@ static void setup(BinaryMessenger binaryMessenger, FlutterAssetManagerHostApi ap Map wrapped = new HashMap<>(); try { ArrayList args = (ArrayList)message; + assert args != null; String nameArg = (String)args.get(0); if (nameArg == null) { throw new NullPointerException("nameArg unexpectedly null."); @@ -2170,12 +2055,7 @@ static void setup(BinaryMessenger binaryMessenger, FlutterAssetManagerHostApi ap } } } - private static class WebChromeClientFlutterApiCodec extends StandardMessageCodec { - public static final WebChromeClientFlutterApiCodec INSTANCE = new WebChromeClientFlutterApiCodec(); - private WebChromeClientFlutterApiCodec() {} - } - - /** Generated class from Pigeon that represents Flutter messages that can be called from Java.*/ + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ public static class WebChromeClientFlutterApi { private final BinaryMessenger binaryMessenger; public WebChromeClientFlutterApi(BinaryMessenger argBinaryMessenger){ @@ -2184,21 +2064,18 @@ public WebChromeClientFlutterApi(BinaryMessenger argBinaryMessenger){ public interface Reply { void reply(T reply); } + /** The codec used by WebChromeClientFlutterApi. */ static MessageCodec getCodec() { - return WebChromeClientFlutterApiCodec.INSTANCE; + return new StandardMessageCodec(); } - public void dispose(@NonNull Long instanceIdArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.WebChromeClientFlutterApi.dispose", getCodec()); - channel.send(new ArrayList(Arrays.asList(instanceIdArg)), channelReply -> { + channel.send(new ArrayList(Collections.singletonList(instanceIdArg)), channelReply -> { callback.reply(null); }); } - public void onProgressChanged(@NonNull Long instanceIdArg, - @NonNull Long webViewInstanceIdArg, - @NonNull Long progressArg, - Reply callback) { + public void onProgressChanged(@NonNull Long instanceIdArg, @NonNull Long webViewInstanceIdArg, @NonNull Long progressArg, Reply callback) { BasicMessageChannel channel = new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.WebChromeClientFlutterApi.onProgressChanged", getCodec()); channel.send(new ArrayList(Arrays.asList(instanceIdArg, webViewInstanceIdArg, progressArg)), channelReply -> { @@ -2206,88 +2083,73 @@ public void onProgressChanged(@NonNull Long instanceIdArg, }); } } - - private static class WebStorageHostApiCodec extends StandardMessageCodec { - public static final WebStorageHostApiCodec INSTANCE = new WebStorageHostApiCodec(); - - private WebStorageHostApiCodec() {} - } - /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ public interface WebStorageHostApi { void create(@NonNull Long instanceId); - void deleteAllData(@NonNull Long instanceId); /** The codec used by WebStorageHostApi. */ static MessageCodec getCodec() { - return WebStorageHostApiCodec.INSTANCE; - } - - /** - * Sets up an instance of `WebStorageHostApi` to handle messages through the `binaryMessenger`. - */ + return new StandardMessageCodec(); } + /**Sets up an instance of `WebStorageHostApi` to handle messages through the `binaryMessenger`. */ static void setup(BinaryMessenger binaryMessenger, WebStorageHostApi api) { { BasicMessageChannel channel = - new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.WebStorageHostApi.create", getCodec()); + new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.WebStorageHostApi.create", getCodec()); if (api != null) { - channel.setMessageHandler( - (message, reply) -> { - Map wrapped = new HashMap<>(); - try { - ArrayList args = (ArrayList) message; - Number instanceIdArg = (Number) args.get(0); - if (instanceIdArg == null) { - throw new NullPointerException("instanceIdArg unexpectedly null."); - } - api.create((instanceIdArg == null) ? null : instanceIdArg.longValue()); - wrapped.put("result", null); - } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); + channel.setMessageHandler((message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList)message; + assert args != null; + Number instanceIdArg = (Number)args.get(0); + if (instanceIdArg == null) { + throw new NullPointerException("instanceIdArg unexpectedly null."); + } + api.create((instanceIdArg == null) ? null : instanceIdArg.longValue()); + wrapped.put("result", null); + } + catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); } else { channel.setMessageHandler(null); } } { BasicMessageChannel channel = - new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.WebStorageHostApi.deleteAllData", getCodec()); + new BasicMessageChannel<>(binaryMessenger, "dev.flutter.pigeon.WebStorageHostApi.deleteAllData", getCodec()); if (api != null) { - channel.setMessageHandler( - (message, reply) -> { - Map wrapped = new HashMap<>(); - try { - ArrayList args = (ArrayList) message; - Number instanceIdArg = (Number) args.get(0); - if (instanceIdArg == null) { - throw new NullPointerException("instanceIdArg unexpectedly null."); - } - api.deleteAllData((instanceIdArg == null) ? null : instanceIdArg.longValue()); - wrapped.put("result", null); - } catch (Error | RuntimeException exception) { - wrapped.put("error", wrapError(exception)); - } - reply.reply(wrapped); - }); + channel.setMessageHandler((message, reply) -> { + Map wrapped = new HashMap<>(); + try { + ArrayList args = (ArrayList)message; + assert args != null; + Number instanceIdArg = (Number)args.get(0); + if (instanceIdArg == null) { + throw new NullPointerException("instanceIdArg unexpectedly null."); + } + api.deleteAllData((instanceIdArg == null) ? null : instanceIdArg.longValue()); + wrapped.put("result", null); + } + catch (Error | RuntimeException exception) { + wrapped.put("error", wrapError(exception)); + } + reply.reply(wrapped); + }); } else { channel.setMessageHandler(null); } } } } - - private static Map wrapError(Throwable exception) { + @NonNull private static Map wrapError(@NonNull Throwable exception) { Map errorMap = new HashMap<>(); errorMap.put("message", exception.toString()); errorMap.put("code", exception.getClass().getSimpleName()); - errorMap.put( - "details", - "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); + errorMap.put("details", "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); return errorMap; } } 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 86d977b5c57c..5a7f14a161e4 100644 --- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart @@ -110,9 +110,9 @@ class _WebViewExampleState extends State<_WebViewExample> { ], ), body: WebView( - initialUrl: 'https:flutter.dev', + //initialUrl: 'https:flutter.dev', // initialUrl: 'https://www.weiyun.com/', - // initialUrl: 'https://www.wjx.cn/jq/27265670.aspx', + initialUrl: 'https://www.wjx.cn/jq/27265670.aspx', onWebViewCreated: (WebViewController controller) { _controller.complete(controller); }, diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart index 27a6ee41d95a..a16531c18e29 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart @@ -1,4 +1,7 @@ -// Autogenerated from Pigeon (v4.0.2), do not edit directly. +// 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 (v4.2.3), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import import 'dart:async'; @@ -43,8 +46,7 @@ class WebResourceRequestData { isRedirect: pigeonMap['isRedirect'] as bool?, hasGesture: pigeonMap['hasGesture']! as bool, method: pigeonMap['method']! as String, - requestHeaders: (pigeonMap['requestHeaders'] as Map?)! - .cast(), + requestHeaders: (pigeonMap['requestHeaders'] as Map?)!.cast(), ); } } @@ -99,25 +101,24 @@ class WebViewPoint { } } -class _JavaObjectHostApiCodec extends StandardMessageCodec { - const _JavaObjectHostApiCodec(); -} +/// Handles methods calls to the native Java Object class. +/// +/// Also handles calls to remove the reference to an instance with `dispose`. +/// +/// See https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html. class JavaObjectHostApi { /// Constructor for [JavaObjectHostApi]. 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. - JavaObjectHostApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - + JavaObjectHostApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; - static const MessageCodec codec = _JavaObjectHostApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); Future dispose(int arg_identifier) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.JavaObjectHostApi.dispose', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.JavaObjectHostApi.dispose', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_identifier]) as Map?; if (replyMap == null) { @@ -126,8 +127,7 @@ class JavaObjectHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -139,30 +139,25 @@ class JavaObjectHostApi { } } -class _JavaObjectFlutterApiCodec extends StandardMessageCodec { - const _JavaObjectFlutterApiCodec(); -} - +/// Handles callbacks methods for the native Java Object class. +/// +/// See https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html. abstract class JavaObjectFlutterApi { - static const MessageCodec codec = _JavaObjectFlutterApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); void dispose(int identifier); - static void setup(JavaObjectFlutterApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setup(JavaObjectFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.JavaObjectFlutterApi.dispose', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.JavaObjectFlutterApi.dispose', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.JavaObjectFlutterApi.dispose was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.JavaObjectFlutterApi.dispose 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.JavaObjectFlutterApi.dispose was null, expected non-null int.'); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.JavaObjectFlutterApi.dispose was null, expected non-null int.'); api.dispose(arg_identifier!); return; }); @@ -171,25 +166,19 @@ abstract class JavaObjectFlutterApi { } } -class _CookieManagerHostApiCodec extends StandardMessageCodec { - const _CookieManagerHostApiCodec(); -} class CookieManagerHostApi { /// Constructor for [CookieManagerHostApi]. 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. - CookieManagerHostApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - + CookieManagerHostApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; - static const MessageCodec codec = _CookieManagerHostApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); Future clearCookies() async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.CookieManagerHostApi.clearCookies', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.CookieManagerHostApi.clearCookies', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send(null) as Map?; if (replyMap == null) { @@ -198,8 +187,7 @@ class CookieManagerHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -217,18 +205,16 @@ class CookieManagerHostApi { Future setCookie(String arg_url, String arg_value) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.CookieManagerHostApi.setCookie', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_url, arg_value]) as Map?; + 'dev.flutter.pigeon.CookieManagerHostApi.setCookie', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_url, arg_value]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -240,26 +226,27 @@ class CookieManagerHostApi { } } -class _WebViewHostApiCodec extends StandardMessageCodec { +class _WebViewHostApiCodec extends StandardMessageCodec{ const _WebViewHostApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { if (value is WebViewPoint) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else { + } else +{ super.writeValue(buffer, value); } } - @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return WebViewPoint.decode(readValue(buffer)!); - - default: + + default: return super.readValueOfType(type, buffer); + } } } @@ -268,28 +255,23 @@ class WebViewHostApi { /// Constructor for [WebViewHostApi]. 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. - WebViewHostApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - + WebViewHostApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; static const MessageCodec codec = _WebViewHostApiCodec(); Future create(int arg_instanceId, bool arg_useHybridComposition) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.create', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_useHybridComposition]) - as Map?; + await channel.send([arg_instanceId, arg_useHybridComposition]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -302,8 +284,7 @@ class WebViewHostApi { Future dispose(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.dispose', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.dispose', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { @@ -312,8 +293,7 @@ class WebViewHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -324,22 +304,18 @@ class WebViewHostApi { } } - Future loadData(int arg_instanceId, String arg_data, - String? arg_mimeType, String? arg_encoding) async { + Future loadData(int arg_instanceId, String arg_data, String? arg_mimeType, String? arg_encoding) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.loadData', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel.send( - [arg_instanceId, arg_data, arg_mimeType, arg_encoding]) - as Map?; + 'dev.flutter.pigeon.WebViewHostApi.loadData', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_data, arg_mimeType, arg_encoding]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -350,32 +326,18 @@ class WebViewHostApi { } } - Future loadDataWithBaseUrl( - int arg_instanceId, - String? arg_baseUrl, - String arg_data, - String? arg_mimeType, - String? arg_encoding, - String? arg_historyUrl) async { + Future loadDataWithBaseUrl(int arg_instanceId, String? arg_baseUrl, String arg_data, String? arg_mimeType, String? arg_encoding, String? arg_historyUrl) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel.send([ - arg_instanceId, - arg_baseUrl, - arg_data, - arg_mimeType, - arg_encoding, - arg_historyUrl - ]) as Map?; + 'dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_baseUrl, arg_data, arg_mimeType, arg_encoding, arg_historyUrl]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -386,22 +348,18 @@ class WebViewHostApi { } } - Future loadUrl(int arg_instanceId, String arg_url, - Map arg_headers) async { + Future loadUrl(int arg_instanceId, String arg_url, Map arg_headers) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.loadUrl', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.loadUrl', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_url, arg_headers]) - as Map?; + await channel.send([arg_instanceId, arg_url, arg_headers]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -412,22 +370,18 @@ class WebViewHostApi { } } - Future postUrl( - int arg_instanceId, String arg_url, Uint8List arg_data) async { + Future postUrl(int arg_instanceId, String arg_url, Uint8List arg_data) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.postUrl', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.postUrl', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_url, arg_data]) - as Map?; + await channel.send([arg_instanceId, arg_url, arg_data]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -440,8 +394,7 @@ class WebViewHostApi { Future getUrl(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.getUrl', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.getUrl', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { @@ -450,8 +403,7 @@ class WebViewHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -464,8 +416,7 @@ class WebViewHostApi { Future canGoBack(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.canGoBack', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.canGoBack', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { @@ -474,8 +425,7 @@ class WebViewHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -493,8 +443,7 @@ class WebViewHostApi { Future canGoForward(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.canGoForward', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.canGoForward', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { @@ -503,8 +452,7 @@ class WebViewHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -522,8 +470,7 @@ class WebViewHostApi { Future goBack(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.goBack', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.goBack', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { @@ -532,8 +479,7 @@ class WebViewHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -546,8 +492,7 @@ class WebViewHostApi { Future goForward(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.goForward', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.goForward', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { @@ -556,8 +501,7 @@ class WebViewHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -570,8 +514,7 @@ class WebViewHostApi { Future reload(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.reload', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.reload', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { @@ -580,8 +523,7 @@ class WebViewHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -594,19 +536,16 @@ class WebViewHostApi { Future clearCache(int arg_instanceId, bool arg_includeDiskFiles) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.clearCache', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.clearCache', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_includeDiskFiles]) - as Map?; + await channel.send([arg_instanceId, arg_includeDiskFiles]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -617,22 +556,18 @@ class WebViewHostApi { } } - Future evaluateJavascript( - int arg_instanceId, String arg_javascriptString) async { + Future evaluateJavascript(int arg_instanceId, String arg_javascriptString) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.evaluateJavascript', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.evaluateJavascript', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_javascriptString]) - as Map?; + await channel.send([arg_instanceId, arg_javascriptString]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -645,8 +580,7 @@ class WebViewHostApi { Future getTitle(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.getTitle', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.getTitle', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { @@ -655,8 +589,7 @@ class WebViewHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -669,19 +602,16 @@ class WebViewHostApi { Future scrollTo(int arg_instanceId, int arg_x, int arg_y) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.scrollTo', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.scrollTo', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_x, arg_y]) - as Map?; + await channel.send([arg_instanceId, arg_x, arg_y]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -694,19 +624,16 @@ class WebViewHostApi { Future scrollBy(int arg_instanceId, int arg_x, int arg_y) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.scrollBy', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.scrollBy', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_x, arg_y]) - as Map?; + await channel.send([arg_instanceId, arg_x, arg_y]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -719,8 +646,7 @@ class WebViewHostApi { Future getScrollX(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.getScrollX', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.getScrollX', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { @@ -729,8 +655,7 @@ class WebViewHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -748,8 +673,7 @@ class WebViewHostApi { Future getScrollY(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.getScrollY', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.getScrollY', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { @@ -758,8 +682,7 @@ class WebViewHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -777,8 +700,7 @@ class WebViewHostApi { Future getScrollPosition(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.getScrollPosition', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.getScrollPosition', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { @@ -787,8 +709,7 @@ class WebViewHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -806,9 +727,7 @@ class WebViewHostApi { Future setWebContentsDebuggingEnabled(bool arg_enabled) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.setWebContentsDebuggingEnabled', - codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.setWebContentsDebuggingEnabled', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_enabled]) as Map?; if (replyMap == null) { @@ -817,8 +736,7 @@ class WebViewHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -829,22 +747,18 @@ class WebViewHostApi { } } - Future setWebViewClient( - int arg_instanceId, int arg_webViewClientInstanceId) async { + Future setWebViewClient(int arg_instanceId, int arg_webViewClientInstanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.setWebViewClient', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_webViewClientInstanceId]) - as Map?; + 'dev.flutter.pigeon.WebViewHostApi.setWebViewClient', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_webViewClientInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -855,22 +769,18 @@ class WebViewHostApi { } } - Future addJavaScriptChannel( - int arg_instanceId, int arg_javaScriptChannelInstanceId) async { + Future addJavaScriptChannel(int arg_instanceId, int arg_javaScriptChannelInstanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.addJavaScriptChannel', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_javaScriptChannelInstanceId]) - as Map?; + 'dev.flutter.pigeon.WebViewHostApi.addJavaScriptChannel', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_javaScriptChannelInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -881,22 +791,18 @@ class WebViewHostApi { } } - Future removeJavaScriptChannel( - int arg_instanceId, int arg_javaScriptChannelInstanceId) async { + Future removeJavaScriptChannel(int arg_instanceId, int arg_javaScriptChannelInstanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.removeJavaScriptChannel', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_javaScriptChannelInstanceId]) - as Map?; + 'dev.flutter.pigeon.WebViewHostApi.removeJavaScriptChannel', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_javaScriptChannelInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -907,22 +813,18 @@ class WebViewHostApi { } } - Future setDownloadListener( - int arg_instanceId, int? arg_listenerInstanceId) async { + Future setDownloadListener(int arg_instanceId, int? arg_listenerInstanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.setDownloadListener', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.setDownloadListener', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_listenerInstanceId]) - as Map?; + await channel.send([arg_instanceId, arg_listenerInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -933,22 +835,18 @@ class WebViewHostApi { } } - Future setWebChromeClient( - int arg_instanceId, int? arg_clientInstanceId) async { + Future setWebChromeClient(int arg_instanceId, int? arg_clientInstanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.setWebChromeClient', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.setWebChromeClient', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_clientInstanceId]) - as Map?; + await channel.send([arg_instanceId, arg_clientInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -961,18 +859,16 @@ class WebViewHostApi { Future setBackgroundColor(int arg_instanceId, int arg_color) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.setBackgroundColor', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_color]) as Map?; + 'dev.flutter.pigeon.WebViewHostApi.setBackgroundColor', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_color]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -984,36 +880,28 @@ class WebViewHostApi { } } -class _WebSettingsHostApiCodec extends StandardMessageCodec { - const _WebSettingsHostApiCodec(); -} class WebSettingsHostApi { /// Constructor for [WebSettingsHostApi]. 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. - WebSettingsHostApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - + WebSettingsHostApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; - static const MessageCodec codec = _WebSettingsHostApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); Future create(int arg_instanceId, int arg_webViewInstanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.create', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebSettingsHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_webViewInstanceId]) - as Map?; + await channel.send([arg_instanceId, arg_webViewInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1026,8 +914,7 @@ class WebSettingsHostApi { Future dispose(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.dispose', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebSettingsHostApi.dispose', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { @@ -1036,8 +923,7 @@ class WebSettingsHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1050,18 +936,16 @@ class WebSettingsHostApi { Future setDomStorageEnabled(int arg_instanceId, bool arg_flag) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_flag]) as Map?; + 'dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_flag]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1072,22 +956,18 @@ class WebSettingsHostApi { } } - Future setJavaScriptCanOpenWindowsAutomatically( - int arg_instanceId, bool arg_flag) async { + Future setJavaScriptCanOpenWindowsAutomatically(int arg_instanceId, bool arg_flag) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptCanOpenWindowsAutomatically', - codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_flag]) as Map?; + 'dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptCanOpenWindowsAutomatically', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_flag]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1098,22 +978,18 @@ class WebSettingsHostApi { } } - Future setSupportMultipleWindows( - int arg_instanceId, bool arg_support) async { + Future setSupportMultipleWindows(int arg_instanceId, bool arg_support) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setSupportMultipleWindows', - codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_support]) as Map?; + 'dev.flutter.pigeon.WebSettingsHostApi.setSupportMultipleWindows', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_support]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1126,18 +1002,16 @@ class WebSettingsHostApi { Future setJavaScriptEnabled(int arg_instanceId, bool arg_flag) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptEnabled', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_flag]) as Map?; + 'dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptEnabled', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_flag]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1148,22 +1022,18 @@ class WebSettingsHostApi { } } - Future setUserAgentString( - int arg_instanceId, String? arg_userAgentString) async { + Future setUserAgentString(int arg_instanceId, String? arg_userAgentString) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_userAgentString]) - as Map?; + await channel.send([arg_instanceId, arg_userAgentString]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1174,22 +1044,18 @@ class WebSettingsHostApi { } } - Future setMediaPlaybackRequiresUserGesture( - int arg_instanceId, bool arg_require) async { + Future setMediaPlaybackRequiresUserGesture(int arg_instanceId, bool arg_require) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setMediaPlaybackRequiresUserGesture', - codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_require]) as Map?; + 'dev.flutter.pigeon.WebSettingsHostApi.setMediaPlaybackRequiresUserGesture', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_require]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1202,18 +1068,16 @@ class WebSettingsHostApi { Future setSupportZoom(int arg_instanceId, bool arg_support) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setSupportZoom', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_support]) as Map?; + 'dev.flutter.pigeon.WebSettingsHostApi.setSupportZoom', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_support]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1224,22 +1088,18 @@ class WebSettingsHostApi { } } - Future setLoadWithOverviewMode( - int arg_instanceId, bool arg_overview) async { + Future setLoadWithOverviewMode(int arg_instanceId, bool arg_overview) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setLoadWithOverviewMode', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebSettingsHostApi.setLoadWithOverviewMode', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_overview]) - as Map?; + await channel.send([arg_instanceId, arg_overview]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1252,18 +1112,16 @@ class WebSettingsHostApi { Future setUseWideViewPort(int arg_instanceId, bool arg_use) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setUseWideViewPort', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_use]) as Map?; + 'dev.flutter.pigeon.WebSettingsHostApi.setUseWideViewPort', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_use]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1274,21 +1132,18 @@ class WebSettingsHostApi { } } - Future setDisplayZoomControls( - int arg_instanceId, bool arg_enabled) async { + Future setDisplayZoomControls(int arg_instanceId, bool arg_enabled) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setDisplayZoomControls', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_enabled]) as Map?; + 'dev.flutter.pigeon.WebSettingsHostApi.setDisplayZoomControls', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_enabled]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1299,21 +1154,18 @@ class WebSettingsHostApi { } } - Future setBuiltInZoomControls( - int arg_instanceId, bool arg_enabled) async { + Future setBuiltInZoomControls(int arg_instanceId, bool arg_enabled) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setBuiltInZoomControls', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_enabled]) as Map?; + 'dev.flutter.pigeon.WebSettingsHostApi.setBuiltInZoomControls', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_enabled]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1326,18 +1178,16 @@ class WebSettingsHostApi { Future setAllowFileAccess(int arg_instanceId, bool arg_enabled) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_enabled]) as Map?; + 'dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_enabled]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1348,22 +1198,18 @@ class WebSettingsHostApi { } } - Future setGeolocationEnabled( - int arg_instanceId, bool arg_enabled) async { + Future setGeolocationEnabled(int arg_instanceId, bool arg_enabled) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setGeolocationEnabled', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_enabled]) as Map?; + 'dev.flutter.pigeon.WebSettingsHostApi.setGeolocationEnabled', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_enabled]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', - details: null, ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1375,36 +1221,28 @@ class WebSettingsHostApi { } } -class _JavaScriptChannelHostApiCodec extends StandardMessageCodec { - const _JavaScriptChannelHostApiCodec(); -} class JavaScriptChannelHostApi { /// Constructor for [JavaScriptChannelHostApi]. 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. - JavaScriptChannelHostApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - + JavaScriptChannelHostApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; - static const MessageCodec codec = _JavaScriptChannelHostApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); Future create(int arg_instanceId, String arg_channelName) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.JavaScriptChannelHostApi.create', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.JavaScriptChannelHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = - await channel.send([arg_instanceId, arg_channelName]) - as Map?; + await channel.send([arg_instanceId, arg_channelName]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1416,32 +1254,23 @@ class JavaScriptChannelHostApi { } } -class _JavaScriptChannelFlutterApiCodec extends StandardMessageCodec { - const _JavaScriptChannelFlutterApiCodec(); -} - abstract class JavaScriptChannelFlutterApi { - static const MessageCodec codec = - _JavaScriptChannelFlutterApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); void dispose(int instanceId); void postMessage(int instanceId, String message); - static void setup(JavaScriptChannelFlutterApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setup(JavaScriptChannelFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.JavaScriptChannelFlutterApi.dispose', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.JavaScriptChannelFlutterApi.dispose', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.JavaScriptChannelFlutterApi.dispose was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.JavaScriptChannelFlutterApi.dispose 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.JavaScriptChannelFlutterApi.dispose was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.JavaScriptChannelFlutterApi.dispose was null, expected non-null int.'); api.dispose(arg_instanceId!); return; }); @@ -1449,21 +1278,17 @@ abstract class JavaScriptChannelFlutterApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.JavaScriptChannelFlutterApi.postMessage', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.JavaScriptChannelFlutterApi.postMessage', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.JavaScriptChannelFlutterApi.postMessage was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.JavaScriptChannelFlutterApi.postMessage 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.JavaScriptChannelFlutterApi.postMessage was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.JavaScriptChannelFlutterApi.postMessage was null, expected non-null int.'); final String? arg_message = (args[1] as String?); - assert(arg_message != null, - 'Argument for dev.flutter.pigeon.JavaScriptChannelFlutterApi.postMessage was null, expected non-null String.'); + assert(arg_message != null, 'Argument for dev.flutter.pigeon.JavaScriptChannelFlutterApi.postMessage was null, expected non-null String.'); api.postMessage(arg_instanceId!, arg_message!); return; }); @@ -1472,37 +1297,28 @@ abstract class JavaScriptChannelFlutterApi { } } -class _WebViewClientHostApiCodec extends StandardMessageCodec { - const _WebViewClientHostApiCodec(); -} class WebViewClientHostApi { /// Constructor for [WebViewClientHostApi]. 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. - WebViewClientHostApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - + WebViewClientHostApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; - static const MessageCodec codec = _WebViewClientHostApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); - Future create( - int arg_instanceId, bool arg_shouldOverrideUrlLoading) async { + Future create(int arg_instanceId, bool arg_shouldOverrideUrlLoading) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewClientHostApi.create', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_shouldOverrideUrlLoading]) - as Map?; + 'dev.flutter.pigeon.WebViewClientHostApi.create', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_shouldOverrideUrlLoading]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1514,65 +1330,59 @@ class WebViewClientHostApi { } } -class _WebViewClientFlutterApiCodec extends StandardMessageCodec { +class _WebViewClientFlutterApiCodec extends StandardMessageCodec{ const _WebViewClientFlutterApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { if (value is WebResourceErrorData) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else if (value is WebResourceRequestData) { + } else + if (value is WebResourceRequestData) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else { + } else +{ super.writeValue(buffer, value); } } - @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return WebResourceErrorData.decode(readValue(buffer)!); - - case 129: + + case 129: return WebResourceRequestData.decode(readValue(buffer)!); - - default: + + default: return super.readValueOfType(type, buffer); + } } } - abstract class WebViewClientFlutterApi { static const MessageCodec codec = _WebViewClientFlutterApiCodec(); void dispose(int instanceId); void onPageStarted(int instanceId, int webViewInstanceId, String url); void onPageFinished(int instanceId, int webViewInstanceId, String url); - void onReceivedRequestError(int instanceId, int webViewInstanceId, - WebResourceRequestData request, WebResourceErrorData error); - void onReceivedError(int instanceId, int webViewInstanceId, int errorCode, - String description, String failingUrl); - void requestLoading( - int instanceId, int webViewInstanceId, WebResourceRequestData request); + void onReceivedRequestError(int instanceId, int webViewInstanceId, WebResourceRequestData request, WebResourceErrorData error); + void onReceivedError(int instanceId, int webViewInstanceId, int errorCode, String description, String failingUrl); + void requestLoading(int instanceId, int webViewInstanceId, WebResourceRequestData request); void urlLoading(int instanceId, int webViewInstanceId, String url); - static void setup(WebViewClientFlutterApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setup(WebViewClientFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewClientFlutterApi.dispose', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewClientFlutterApi.dispose', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.dispose was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.dispose 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.WebViewClientFlutterApi.dispose was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.dispose was null, expected non-null int.'); api.dispose(arg_instanceId!); return; }); @@ -1580,24 +1390,19 @@ abstract class WebViewClientFlutterApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewClientFlutterApi.onPageStarted', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewClientFlutterApi.onPageStarted', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onPageStarted was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onPageStarted 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.WebViewClientFlutterApi.onPageStarted was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onPageStarted was null, expected non-null int.'); final int? arg_webViewInstanceId = (args[1] as int?); - assert(arg_webViewInstanceId != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onPageStarted was null, expected non-null int.'); + assert(arg_webViewInstanceId != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onPageStarted was null, expected non-null int.'); final String? arg_url = (args[2] as String?); - assert(arg_url != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onPageStarted was null, expected non-null String.'); + assert(arg_url != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onPageStarted was null, expected non-null String.'); api.onPageStarted(arg_instanceId!, arg_webViewInstanceId!, arg_url!); return; }); @@ -1605,24 +1410,19 @@ abstract class WebViewClientFlutterApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewClientFlutterApi.onPageFinished', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewClientFlutterApi.onPageFinished', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onPageFinished was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onPageFinished 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.WebViewClientFlutterApi.onPageFinished was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onPageFinished was null, expected non-null int.'); final int? arg_webViewInstanceId = (args[1] as int?); - assert(arg_webViewInstanceId != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onPageFinished was null, expected non-null int.'); + assert(arg_webViewInstanceId != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onPageFinished was null, expected non-null int.'); final String? arg_url = (args[2] as String?); - assert(arg_url != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onPageFinished was null, expected non-null String.'); + assert(arg_url != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onPageFinished was null, expected non-null String.'); api.onPageFinished(arg_instanceId!, arg_webViewInstanceId!, arg_url!); return; }); @@ -1630,115 +1430,85 @@ abstract class WebViewClientFlutterApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedRequestError', - codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedRequestError', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedRequestError was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedRequestError 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.WebViewClientFlutterApi.onReceivedRequestError was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedRequestError was null, expected non-null int.'); final int? arg_webViewInstanceId = (args[1] as int?); - assert(arg_webViewInstanceId != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedRequestError was null, expected non-null int.'); - final WebResourceRequestData? arg_request = - (args[2] as WebResourceRequestData?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedRequestError was null, expected non-null WebResourceRequestData.'); - final WebResourceErrorData? arg_error = - (args[3] as WebResourceErrorData?); - assert(arg_error != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedRequestError was null, expected non-null WebResourceErrorData.'); - api.onReceivedRequestError(arg_instanceId!, arg_webViewInstanceId!, - arg_request!, arg_error!); + assert(arg_webViewInstanceId != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedRequestError was null, expected non-null int.'); + final WebResourceRequestData? arg_request = (args[2] as WebResourceRequestData?); + assert(arg_request != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedRequestError was null, expected non-null WebResourceRequestData.'); + final WebResourceErrorData? arg_error = (args[3] as WebResourceErrorData?); + assert(arg_error != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedRequestError was null, expected non-null WebResourceErrorData.'); + api.onReceivedRequestError(arg_instanceId!, arg_webViewInstanceId!, arg_request!, arg_error!); return; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedError', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedError', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedError was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedError 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.WebViewClientFlutterApi.onReceivedError was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedError was null, expected non-null int.'); final int? arg_webViewInstanceId = (args[1] as int?); - assert(arg_webViewInstanceId != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedError was null, expected non-null int.'); + assert(arg_webViewInstanceId != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedError was null, expected non-null int.'); final int? arg_errorCode = (args[2] as int?); - assert(arg_errorCode != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedError was null, expected non-null int.'); + assert(arg_errorCode != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedError was null, expected non-null int.'); final String? arg_description = (args[3] as String?); - assert(arg_description != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedError was null, expected non-null String.'); + assert(arg_description != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedError was null, expected non-null String.'); final String? arg_failingUrl = (args[4] as String?); - assert(arg_failingUrl != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedError was null, expected non-null String.'); - api.onReceivedError(arg_instanceId!, arg_webViewInstanceId!, - arg_errorCode!, arg_description!, arg_failingUrl!); + assert(arg_failingUrl != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.onReceivedError was null, expected non-null String.'); + api.onReceivedError(arg_instanceId!, arg_webViewInstanceId!, arg_errorCode!, arg_description!, arg_failingUrl!); return; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewClientFlutterApi.requestLoading', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewClientFlutterApi.requestLoading', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.requestLoading was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.requestLoading 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.WebViewClientFlutterApi.requestLoading was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.requestLoading was null, expected non-null int.'); final int? arg_webViewInstanceId = (args[1] as int?); - assert(arg_webViewInstanceId != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.requestLoading was null, expected non-null int.'); - final WebResourceRequestData? arg_request = - (args[2] as WebResourceRequestData?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.requestLoading was null, expected non-null WebResourceRequestData.'); - api.requestLoading( - arg_instanceId!, arg_webViewInstanceId!, arg_request!); + assert(arg_webViewInstanceId != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.requestLoading was null, expected non-null int.'); + final WebResourceRequestData? arg_request = (args[2] as WebResourceRequestData?); + assert(arg_request != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.requestLoading was null, expected non-null WebResourceRequestData.'); + api.requestLoading(arg_instanceId!, arg_webViewInstanceId!, arg_request!); return; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewClientFlutterApi.urlLoading', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewClientFlutterApi.urlLoading', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.urlLoading was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.urlLoading 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.WebViewClientFlutterApi.urlLoading was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.urlLoading was null, expected non-null int.'); final int? arg_webViewInstanceId = (args[1] as int?); - assert(arg_webViewInstanceId != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.urlLoading was null, expected non-null int.'); + assert(arg_webViewInstanceId != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.urlLoading was null, expected non-null int.'); final String? arg_url = (args[2] as String?); - assert(arg_url != null, - 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.urlLoading was null, expected non-null String.'); + assert(arg_url != null, 'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.urlLoading was null, expected non-null String.'); api.urlLoading(arg_instanceId!, arg_webViewInstanceId!, arg_url!); return; }); @@ -1747,25 +1517,19 @@ abstract class WebViewClientFlutterApi { } } -class _DownloadListenerHostApiCodec extends StandardMessageCodec { - const _DownloadListenerHostApiCodec(); -} class DownloadListenerHostApi { /// Constructor for [DownloadListenerHostApi]. 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. - DownloadListenerHostApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - + DownloadListenerHostApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; - static const MessageCodec codec = _DownloadListenerHostApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); Future create(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.DownloadListenerHostApi.create', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.DownloadListenerHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { @@ -1774,8 +1538,7 @@ class DownloadListenerHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1787,32 +1550,23 @@ class DownloadListenerHostApi { } } -class _DownloadListenerFlutterApiCodec extends StandardMessageCodec { - const _DownloadListenerFlutterApiCodec(); -} - abstract class DownloadListenerFlutterApi { - static const MessageCodec codec = _DownloadListenerFlutterApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); void dispose(int instanceId); - void onDownloadStart(int instanceId, String url, String userAgent, - String contentDisposition, String mimetype, int contentLength); - static void setup(DownloadListenerFlutterApi? api, - {BinaryMessenger? binaryMessenger}) { + void onDownloadStart(int instanceId, String url, String userAgent, String contentDisposition, String mimetype, int contentLength); + static void setup(DownloadListenerFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.DownloadListenerFlutterApi.dispose', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.DownloadListenerFlutterApi.dispose', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.DownloadListenerFlutterApi.dispose was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.DownloadListenerFlutterApi.dispose 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.DownloadListenerFlutterApi.dispose was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.DownloadListenerFlutterApi.dispose was null, expected non-null int.'); api.dispose(arg_instanceId!); return; }); @@ -1820,36 +1574,26 @@ abstract class DownloadListenerFlutterApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.DownloadListenerFlutterApi.onDownloadStart', - codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.DownloadListenerFlutterApi.onDownloadStart', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.DownloadListenerFlutterApi.onDownloadStart was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.DownloadListenerFlutterApi.onDownloadStart 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.DownloadListenerFlutterApi.onDownloadStart was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.DownloadListenerFlutterApi.onDownloadStart was null, expected non-null int.'); final String? arg_url = (args[1] as String?); - assert(arg_url != null, - 'Argument for dev.flutter.pigeon.DownloadListenerFlutterApi.onDownloadStart was null, expected non-null String.'); + assert(arg_url != null, 'Argument for dev.flutter.pigeon.DownloadListenerFlutterApi.onDownloadStart was null, expected non-null String.'); final String? arg_userAgent = (args[2] as String?); - assert(arg_userAgent != null, - 'Argument for dev.flutter.pigeon.DownloadListenerFlutterApi.onDownloadStart was null, expected non-null String.'); + assert(arg_userAgent != null, 'Argument for dev.flutter.pigeon.DownloadListenerFlutterApi.onDownloadStart was null, expected non-null String.'); final String? arg_contentDisposition = (args[3] as String?); - assert(arg_contentDisposition != null, - 'Argument for dev.flutter.pigeon.DownloadListenerFlutterApi.onDownloadStart was null, expected non-null String.'); + assert(arg_contentDisposition != null, 'Argument for dev.flutter.pigeon.DownloadListenerFlutterApi.onDownloadStart was null, expected non-null String.'); final String? arg_mimetype = (args[4] as String?); - assert(arg_mimetype != null, - 'Argument for dev.flutter.pigeon.DownloadListenerFlutterApi.onDownloadStart was null, expected non-null String.'); + assert(arg_mimetype != null, 'Argument for dev.flutter.pigeon.DownloadListenerFlutterApi.onDownloadStart was null, expected non-null String.'); final int? arg_contentLength = (args[5] as int?); - assert(arg_contentLength != null, - 'Argument for dev.flutter.pigeon.DownloadListenerFlutterApi.onDownloadStart was null, expected non-null int.'); - api.onDownloadStart(arg_instanceId!, arg_url!, arg_userAgent!, - arg_contentDisposition!, arg_mimetype!, arg_contentLength!); + assert(arg_contentLength != null, 'Argument for dev.flutter.pigeon.DownloadListenerFlutterApi.onDownloadStart was null, expected non-null int.'); + api.onDownloadStart(arg_instanceId!, arg_url!, arg_userAgent!, arg_contentDisposition!, arg_mimetype!, arg_contentLength!); return; }); } @@ -1857,37 +1601,28 @@ abstract class DownloadListenerFlutterApi { } } -class _WebChromeClientHostApiCodec extends StandardMessageCodec { - const _WebChromeClientHostApiCodec(); -} class WebChromeClientHostApi { /// Constructor for [WebChromeClientHostApi]. 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. - WebChromeClientHostApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - + WebChromeClientHostApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; - static const MessageCodec codec = _WebChromeClientHostApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); - Future create( - int arg_instanceId, int arg_webViewClientInstanceId) async { + Future create(int arg_instanceId, int arg_webViewClientInstanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebChromeClientHostApi.create', codec, - binaryMessenger: _binaryMessenger); - final Map? replyMap = await channel - .send([arg_instanceId, arg_webViewClientInstanceId]) - as Map?; + 'dev.flutter.pigeon.WebChromeClientHostApi.create', codec, binaryMessenger: _binaryMessenger); + final Map? replyMap = + await channel.send([arg_instanceId, arg_webViewClientInstanceId]) as Map?; if (replyMap == null) { throw PlatformException( code: 'channel-error', message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1899,25 +1634,19 @@ class WebChromeClientHostApi { } } -class _FlutterAssetManagerHostApiCodec extends StandardMessageCodec { - const _FlutterAssetManagerHostApiCodec(); -} class FlutterAssetManagerHostApi { /// Constructor for [FlutterAssetManagerHostApi]. 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. - FlutterAssetManagerHostApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - + FlutterAssetManagerHostApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; - static const MessageCodec codec = _FlutterAssetManagerHostApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); Future> list(String arg_path) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.FlutterAssetManagerHostApi.list', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.FlutterAssetManagerHostApi.list', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_path]) as Map?; if (replyMap == null) { @@ -1926,8 +1655,7 @@ class FlutterAssetManagerHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1945,9 +1673,7 @@ class FlutterAssetManagerHostApi { Future getAssetFilePathByName(String arg_name) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.FlutterAssetManagerHostApi.getAssetFilePathByName', - codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.FlutterAssetManagerHostApi.getAssetFilePathByName', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_name]) as Map?; if (replyMap == null) { @@ -1956,8 +1682,7 @@ class FlutterAssetManagerHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -1974,31 +1699,23 @@ class FlutterAssetManagerHostApi { } } -class _WebChromeClientFlutterApiCodec extends StandardMessageCodec { - const _WebChromeClientFlutterApiCodec(); -} - abstract class WebChromeClientFlutterApi { - static const MessageCodec codec = _WebChromeClientFlutterApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); void dispose(int instanceId); void onProgressChanged(int instanceId, int webViewInstanceId, int progress); - static void setup(WebChromeClientFlutterApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setup(WebChromeClientFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebChromeClientFlutterApi.dispose', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebChromeClientFlutterApi.dispose', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebChromeClientFlutterApi.dispose was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebChromeClientFlutterApi.dispose 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.WebChromeClientFlutterApi.dispose was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebChromeClientFlutterApi.dispose was null, expected non-null int.'); api.dispose(arg_instanceId!); return; }); @@ -2006,27 +1723,20 @@ abstract class WebChromeClientFlutterApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebChromeClientFlutterApi.onProgressChanged', - codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebChromeClientFlutterApi.onProgressChanged', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMessageHandler(null); } else { channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebChromeClientFlutterApi.onProgressChanged was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebChromeClientFlutterApi.onProgressChanged 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.WebChromeClientFlutterApi.onProgressChanged was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebChromeClientFlutterApi.onProgressChanged was null, expected non-null int.'); final int? arg_webViewInstanceId = (args[1] as int?); - assert(arg_webViewInstanceId != null, - 'Argument for dev.flutter.pigeon.WebChromeClientFlutterApi.onProgressChanged was null, expected non-null int.'); + assert(arg_webViewInstanceId != null, 'Argument for dev.flutter.pigeon.WebChromeClientFlutterApi.onProgressChanged was null, expected non-null int.'); final int? arg_progress = (args[2] as int?); - assert(arg_progress != null, - 'Argument for dev.flutter.pigeon.WebChromeClientFlutterApi.onProgressChanged was null, expected non-null int.'); - api.onProgressChanged( - arg_instanceId!, arg_webViewInstanceId!, arg_progress!); + assert(arg_progress != null, 'Argument for dev.flutter.pigeon.WebChromeClientFlutterApi.onProgressChanged was null, expected non-null int.'); + api.onProgressChanged(arg_instanceId!, arg_webViewInstanceId!, arg_progress!); return; }); } @@ -2034,25 +1744,19 @@ abstract class WebChromeClientFlutterApi { } } -class _WebStorageHostApiCodec extends StandardMessageCodec { - const _WebStorageHostApiCodec(); -} class WebStorageHostApi { /// Constructor for [WebStorageHostApi]. 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. - WebStorageHostApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - + WebStorageHostApi({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger; final BinaryMessenger? _binaryMessenger; - static const MessageCodec codec = _WebStorageHostApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); Future create(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebStorageHostApi.create', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebStorageHostApi.create', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { @@ -2061,8 +1765,7 @@ class WebStorageHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, @@ -2075,8 +1778,7 @@ class WebStorageHostApi { Future deleteAllData(int arg_instanceId) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebStorageHostApi.deleteAllData', codec, - binaryMessenger: _binaryMessenger); + 'dev.flutter.pigeon.WebStorageHostApi.deleteAllData', codec, binaryMessenger: _binaryMessenger); final Map? replyMap = await channel.send([arg_instanceId]) as Map?; if (replyMap == null) { @@ -2085,8 +1787,7 @@ class WebStorageHostApi { message: 'Unable to establish connection on channel.', ); } else if (replyMap['error'] != null) { - final Map error = - (replyMap['error'] as Map?)!; + final Map error = (replyMap['error'] as Map?)!; throw PlatformException( code: (error['code'] as String?)!, message: error['message'] as String?, 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 05f730891506..dd3145b69ea3 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 @@ -421,7 +421,7 @@ class WebSettingsHostApiImpl extends WebSettingsHostApi { bool flag, ) { return setGeolocationEnabled( - instanceManager.getInstanceId(instance)!, + instanceManager.getIdentifier(instance)!, flag, ); } 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 8cd14300c8d7..38bfb5563171 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 @@ -1,14 +1,15 @@ -// Mocks generated by Mockito 5.2.0 from annotations -// in webview_flutter_android/test/android_webview_test.dart. +// Mocks generated by Mockito 5.3.2 from annotations +// in webview_pro_android/test/android_webview_test.dart. // Do not manually edit this file. +// ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i5; import 'dart:typed_data' as _i7; -import 'dart:ui' as _i4; +import 'dart:ui' as _i3; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_pro_android/src/android_webview.dart' as _i2; -import 'package:webview_pro_android/src/android_webview.pigeon.dart' as _i3; +import 'package:webview_pro_android/src/android_webview.pigeon.dart' as _i4; import 'test_android_webview.pigeon.dart' as _i6; @@ -21,43 +22,114 @@ import 'test_android_webview.pigeon.dart' as _i6; // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class -class _FakeDownloadListener_0 extends _i1.Fake - implements _i2.DownloadListener {} - -class _FakeJavaScriptChannel_1 extends _i1.Fake - implements _i2.JavaScriptChannel {} +class _FakeDownloadListener_0 extends _i1.SmartFake + implements _i2.DownloadListener { + _FakeDownloadListener_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeWebViewPoint_2 extends _i1.Fake implements _i3.WebViewPoint {} +class _FakeJavaScriptChannel_1 extends _i1.SmartFake + implements _i2.JavaScriptChannel { + _FakeJavaScriptChannel_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeWebChromeClient_3 extends _i1.Fake implements _i2.WebChromeClient {} +class _FakeWebChromeClient_2 extends _i1.SmartFake + implements _i2.WebChromeClient { + _FakeWebChromeClient_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeWebSettings_4 extends _i1.Fake implements _i2.WebSettings {} +class _FakeWebSettings_3 extends _i1.SmartFake implements _i2.WebSettings { + _FakeWebSettings_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeOffset_5 extends _i1.Fake implements _i4.Offset {} +class _FakeOffset_4 extends _i1.SmartFake implements _i3.Offset { + _FakeOffset_4( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeWebView_6 extends _i1.Fake implements _i2.WebView {} +class _FakeWebView_5 extends _i1.SmartFake implements _i2.WebView { + _FakeWebView_5( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeWebViewClient_7 extends _i1.Fake implements _i2.WebViewClient {} +class _FakeWebViewClient_6 extends _i1.SmartFake implements _i2.WebViewClient { + _FakeWebViewClient_6( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} /// A class which mocks [CookieManagerHostApi]. /// /// See the documentation for Mockito's code generation for more information. class MockCookieManagerHostApi extends _i1.Mock - implements _i3.CookieManagerHostApi { + implements _i4.CookieManagerHostApi { MockCookieManagerHostApi() { _i1.throwOnMissingStub(this); } @override - _i5.Future clearCookies() => - (super.noSuchMethod(Invocation.method(#clearCookies, []), - returnValue: Future.value(false)) as _i5.Future); - @override - _i5.Future setCookie(String? arg_url, String? arg_value) => - (super.noSuchMethod(Invocation.method(#setCookie, [arg_url, arg_value]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + _i5.Future clearCookies() => (super.noSuchMethod( + Invocation.method( + #clearCookies, + [], + ), + returnValue: _i5.Future.value(false), + ) as _i5.Future); + @override + _i5.Future setCookie( + String? arg_url, + String? arg_value, + ) => + (super.noSuchMethod( + Invocation.method( + #setCookie, + [ + arg_url, + arg_value, + ], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); } /// A class which mocks [DownloadListener]. @@ -69,16 +141,40 @@ class MockDownloadListener extends _i1.Mock implements _i2.DownloadListener { } @override - void onDownloadStart(String? url, String? userAgent, - String? contentDisposition, String? mimetype, int? contentLength) => + void onDownloadStart( + String? url, + String? userAgent, + String? contentDisposition, + String? mimetype, + int? contentLength, + ) => super.noSuchMethod( - Invocation.method(#onDownloadStart, - [url, userAgent, contentDisposition, mimetype, contentLength]), - returnValueForMissingStub: null); - @override - _i2.DownloadListener copy() => - (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeDownloadListener_0()) as _i2.DownloadListener); + Invocation.method( + #onDownloadStart, + [ + url, + userAgent, + contentDisposition, + mimetype, + contentLength, + ], + ), + returnValueForMissingStub: null, + ); + @override + _i2.DownloadListener copy() => (super.noSuchMethod( + Invocation.method( + #copy, + [], + ), + returnValue: _FakeDownloadListener_0( + this, + Invocation.method( + #copy, + [], + ), + ), + ) as _i2.DownloadListener); } /// A class which mocks [JavaScriptChannel]. @@ -90,17 +186,32 @@ class MockJavaScriptChannel extends _i1.Mock implements _i2.JavaScriptChannel { } @override - String get channelName => - (super.noSuchMethod(Invocation.getter(#channelName), returnValue: '') - as String); - @override - void postMessage(String? message) => - super.noSuchMethod(Invocation.method(#postMessage, [message]), - returnValueForMissingStub: null); - @override - _i2.JavaScriptChannel copy() => - (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeJavaScriptChannel_1()) as _i2.JavaScriptChannel); + String get channelName => (super.noSuchMethod( + Invocation.getter(#channelName), + returnValue: '', + ) as String); + @override + void postMessage(String? message) => super.noSuchMethod( + Invocation.method( + #postMessage, + [message], + ), + returnValueForMissingStub: null, + ); + @override + _i2.JavaScriptChannel copy() => (super.noSuchMethod( + Invocation.method( + #copy, + [], + ), + returnValue: _FakeJavaScriptChannel_1( + this, + Invocation.method( + #copy, + [], + ), + ), + ) as _i2.JavaScriptChannel); } /// A class which mocks [TestDownloadListenerHostApi]. @@ -113,9 +224,13 @@ class MockTestDownloadListenerHostApi extends _i1.Mock } @override - void create(int? instanceId) => - super.noSuchMethod(Invocation.method(#create, [instanceId]), - returnValueForMissingStub: null); + void create(int? instanceId) => super.noSuchMethod( + Invocation.method( + #create, + [instanceId], + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [TestJavaObjectHostApi]. @@ -128,9 +243,13 @@ class MockTestJavaObjectHostApi extends _i1.Mock } @override - void dispose(int? identifier) => - super.noSuchMethod(Invocation.method(#dispose, [identifier]), - returnValueForMissingStub: null); + void dispose(int? identifier) => super.noSuchMethod( + Invocation.method( + #dispose, + [identifier], + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [TestJavaScriptChannelHostApi]. @@ -143,9 +262,20 @@ class MockTestJavaScriptChannelHostApi extends _i1.Mock } @override - void create(int? instanceId, String? channelName) => - super.noSuchMethod(Invocation.method(#create, [instanceId, channelName]), - returnValueForMissingStub: null); + void create( + int? instanceId, + String? channelName, + ) => + super.noSuchMethod( + Invocation.method( + #create, + [ + instanceId, + channelName, + ], + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [TestWebChromeClientHostApi]. @@ -158,10 +288,20 @@ class MockTestWebChromeClientHostApi extends _i1.Mock } @override - void create(int? instanceId, int? webViewClientInstanceId) => + void create( + int? instanceId, + int? webViewClientInstanceId, + ) => super.noSuchMethod( - Invocation.method(#create, [instanceId, webViewClientInstanceId]), - returnValueForMissingStub: null); + Invocation.method( + #create, + [ + instanceId, + webViewClientInstanceId, + ], + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [TestWebSettingsHostApi]. @@ -174,70 +314,223 @@ class MockTestWebSettingsHostApi extends _i1.Mock } @override - void create(int? instanceId, int? webViewInstanceId) => super.noSuchMethod( - Invocation.method(#create, [instanceId, webViewInstanceId]), - returnValueForMissingStub: null); - @override - void dispose(int? instanceId) => - super.noSuchMethod(Invocation.method(#dispose, [instanceId]), - returnValueForMissingStub: null); - @override - void setDomStorageEnabled(int? instanceId, bool? flag) => super.noSuchMethod( - Invocation.method(#setDomStorageEnabled, [instanceId, flag]), - returnValueForMissingStub: null); - @override - void setJavaScriptCanOpenWindowsAutomatically(int? instanceId, bool? flag) => + void create( + int? instanceId, + int? webViewInstanceId, + ) => super.noSuchMethod( - Invocation.method( - #setJavaScriptCanOpenWindowsAutomatically, [instanceId, flag]), - returnValueForMissingStub: null); - @override - void setSupportMultipleWindows(int? instanceId, bool? support) => + Invocation.method( + #create, + [ + instanceId, + webViewInstanceId, + ], + ), + returnValueForMissingStub: null, + ); + @override + void dispose(int? instanceId) => super.noSuchMethod( + Invocation.method( + #dispose, + [instanceId], + ), + returnValueForMissingStub: null, + ); + @override + void setDomStorageEnabled( + int? instanceId, + bool? flag, + ) => super.noSuchMethod( - Invocation.method(#setSupportMultipleWindows, [instanceId, support]), - returnValueForMissingStub: null); - @override - void setJavaScriptEnabled(int? instanceId, bool? flag) => super.noSuchMethod( - Invocation.method(#setJavaScriptEnabled, [instanceId, flag]), - returnValueForMissingStub: null); - @override - void setUserAgentString(int? instanceId, String? userAgentString) => + Invocation.method( + #setDomStorageEnabled, + [ + instanceId, + flag, + ], + ), + returnValueForMissingStub: null, + ); + @override + void setJavaScriptCanOpenWindowsAutomatically( + int? instanceId, + bool? flag, + ) => super.noSuchMethod( - Invocation.method(#setUserAgentString, [instanceId, userAgentString]), - returnValueForMissingStub: null); - @override - void setMediaPlaybackRequiresUserGesture(int? instanceId, bool? require) => + Invocation.method( + #setJavaScriptCanOpenWindowsAutomatically, + [ + instanceId, + flag, + ], + ), + returnValueForMissingStub: null, + ); + @override + void setSupportMultipleWindows( + int? instanceId, + bool? support, + ) => super.noSuchMethod( - Invocation.method( - #setMediaPlaybackRequiresUserGesture, [instanceId, require]), - returnValueForMissingStub: null); - @override - void setSupportZoom(int? instanceId, bool? support) => super.noSuchMethod( - Invocation.method(#setSupportZoom, [instanceId, support]), - returnValueForMissingStub: null); - @override - void setLoadWithOverviewMode(int? instanceId, bool? overview) => + Invocation.method( + #setSupportMultipleWindows, + [ + instanceId, + support, + ], + ), + returnValueForMissingStub: null, + ); + @override + void setJavaScriptEnabled( + int? instanceId, + bool? flag, + ) => super.noSuchMethod( - Invocation.method(#setLoadWithOverviewMode, [instanceId, overview]), - returnValueForMissingStub: null); - @override - void setUseWideViewPort(int? instanceId, bool? use) => super.noSuchMethod( - Invocation.method(#setUseWideViewPort, [instanceId, use]), - returnValueForMissingStub: null); - @override - void setDisplayZoomControls(int? instanceId, bool? enabled) => + Invocation.method( + #setJavaScriptEnabled, + [ + instanceId, + flag, + ], + ), + returnValueForMissingStub: null, + ); + @override + void setUserAgentString( + int? instanceId, + String? userAgentString, + ) => super.noSuchMethod( - Invocation.method(#setDisplayZoomControls, [instanceId, enabled]), - returnValueForMissingStub: null); - @override - void setBuiltInZoomControls(int? instanceId, bool? enabled) => + Invocation.method( + #setUserAgentString, + [ + instanceId, + userAgentString, + ], + ), + returnValueForMissingStub: null, + ); + @override + void setMediaPlaybackRequiresUserGesture( + int? instanceId, + bool? require, + ) => super.noSuchMethod( - Invocation.method(#setBuiltInZoomControls, [instanceId, enabled]), - returnValueForMissingStub: null); - @override - void setAllowFileAccess(int? instanceId, bool? enabled) => super.noSuchMethod( - Invocation.method(#setAllowFileAccess, [instanceId, enabled]), - returnValueForMissingStub: null); + Invocation.method( + #setMediaPlaybackRequiresUserGesture, + [ + instanceId, + require, + ], + ), + returnValueForMissingStub: null, + ); + @override + void setSupportZoom( + int? instanceId, + bool? support, + ) => + super.noSuchMethod( + Invocation.method( + #setSupportZoom, + [ + instanceId, + support, + ], + ), + returnValueForMissingStub: null, + ); + @override + void setLoadWithOverviewMode( + int? instanceId, + bool? overview, + ) => + super.noSuchMethod( + Invocation.method( + #setLoadWithOverviewMode, + [ + instanceId, + overview, + ], + ), + returnValueForMissingStub: null, + ); + @override + void setUseWideViewPort( + int? instanceId, + bool? use, + ) => + super.noSuchMethod( + Invocation.method( + #setUseWideViewPort, + [ + instanceId, + use, + ], + ), + returnValueForMissingStub: null, + ); + @override + void setDisplayZoomControls( + int? instanceId, + bool? enabled, + ) => + super.noSuchMethod( + Invocation.method( + #setDisplayZoomControls, + [ + instanceId, + enabled, + ], + ), + returnValueForMissingStub: null, + ); + @override + void setBuiltInZoomControls( + int? instanceId, + bool? enabled, + ) => + super.noSuchMethod( + Invocation.method( + #setBuiltInZoomControls, + [ + instanceId, + enabled, + ], + ), + returnValueForMissingStub: null, + ); + @override + void setAllowFileAccess( + int? instanceId, + bool? enabled, + ) => + super.noSuchMethod( + Invocation.method( + #setAllowFileAccess, + [ + instanceId, + enabled, + ], + ), + returnValueForMissingStub: null, + ); + @override + void setGeolocationEnabled( + int? instanceId, + bool? enabled, + ) => + super.noSuchMethod( + Invocation.method( + #setGeolocationEnabled, + [ + instanceId, + enabled, + ], + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [TestWebStorageHostApi]. @@ -250,13 +543,21 @@ class MockTestWebStorageHostApi extends _i1.Mock } @override - void create(int? instanceId) => - super.noSuchMethod(Invocation.method(#create, [instanceId]), - returnValueForMissingStub: null); - @override - void deleteAllData(int? instanceId) => - super.noSuchMethod(Invocation.method(#deleteAllData, [instanceId]), - returnValueForMissingStub: null); + void create(int? instanceId) => super.noSuchMethod( + Invocation.method( + #create, + [instanceId], + ), + returnValueForMissingStub: null, + ); + @override + void deleteAllData(int? instanceId) => super.noSuchMethod( + Invocation.method( + #deleteAllData, + [instanceId], + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [TestWebViewClientHostApi]. @@ -269,10 +570,20 @@ class MockTestWebViewClientHostApi extends _i1.Mock } @override - void create(int? instanceId, bool? shouldOverrideUrlLoading) => + void create( + int? instanceId, + bool? shouldOverrideUrlLoading, + ) => super.noSuchMethod( - Invocation.method(#create, [instanceId, shouldOverrideUrlLoading]), - returnValueForMissingStub: null); + Invocation.method( + #create, + [ + instanceId, + shouldOverrideUrlLoading, + ], + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [TestWebViewHostApi]. @@ -285,135 +596,338 @@ class MockTestWebViewHostApi extends _i1.Mock } @override - void create(int? instanceId, bool? useHybridComposition) => + void create( + int? instanceId, + bool? useHybridComposition, + ) => super.noSuchMethod( - Invocation.method(#create, [instanceId, useHybridComposition]), - returnValueForMissingStub: null); - @override - void dispose(int? instanceId) => - super.noSuchMethod(Invocation.method(#dispose, [instanceId]), - returnValueForMissingStub: null); + Invocation.method( + #create, + [ + instanceId, + useHybridComposition, + ], + ), + returnValueForMissingStub: null, + ); + @override + void dispose(int? instanceId) => super.noSuchMethod( + Invocation.method( + #dispose, + [instanceId], + ), + returnValueForMissingStub: null, + ); @override void loadData( - int? instanceId, String? data, String? mimeType, String? encoding) => + int? instanceId, + String? data, + String? mimeType, + String? encoding, + ) => super.noSuchMethod( - Invocation.method(#loadData, [instanceId, data, mimeType, encoding]), - returnValueForMissingStub: null); - @override - void loadDataWithBaseUrl(int? instanceId, String? baseUrl, String? data, - String? mimeType, String? encoding, String? historyUrl) => + Invocation.method( + #loadData, + [ + instanceId, + data, + mimeType, + encoding, + ], + ), + returnValueForMissingStub: null, + ); + @override + void loadDataWithBaseUrl( + int? instanceId, + String? baseUrl, + String? data, + String? mimeType, + String? encoding, + String? historyUrl, + ) => super.noSuchMethod( - Invocation.method(#loadDataWithBaseUrl, - [instanceId, baseUrl, data, mimeType, encoding, historyUrl]), - returnValueForMissingStub: null); - @override - void loadUrl(int? instanceId, String? url, Map? headers) => + Invocation.method( + #loadDataWithBaseUrl, + [ + instanceId, + baseUrl, + data, + mimeType, + encoding, + historyUrl, + ], + ), + returnValueForMissingStub: null, + ); + @override + void loadUrl( + int? instanceId, + String? url, + Map? headers, + ) => super.noSuchMethod( - Invocation.method(#loadUrl, [instanceId, url, headers]), - returnValueForMissingStub: null); - @override - void postUrl(int? instanceId, String? url, _i7.Uint8List? data) => - super.noSuchMethod(Invocation.method(#postUrl, [instanceId, url, data]), - returnValueForMissingStub: null); - @override - String? getUrl(int? instanceId) => - (super.noSuchMethod(Invocation.method(#getUrl, [instanceId])) as String?); - @override - bool canGoBack(int? instanceId) => - (super.noSuchMethod(Invocation.method(#canGoBack, [instanceId]), - returnValue: false) as bool); - @override - bool canGoForward(int? instanceId) => - (super.noSuchMethod(Invocation.method(#canGoForward, [instanceId]), - returnValue: false) as bool); - @override - void goBack(int? instanceId) => - super.noSuchMethod(Invocation.method(#goBack, [instanceId]), - returnValueForMissingStub: null); - @override - void goForward(int? instanceId) => - super.noSuchMethod(Invocation.method(#goForward, [instanceId]), - returnValueForMissingStub: null); - @override - void reload(int? instanceId) => - super.noSuchMethod(Invocation.method(#reload, [instanceId]), - returnValueForMissingStub: null); - @override - void clearCache(int? instanceId, bool? includeDiskFiles) => + Invocation.method( + #loadUrl, + [ + instanceId, + url, + headers, + ], + ), + returnValueForMissingStub: null, + ); + @override + void postUrl( + int? instanceId, + String? url, + _i7.Uint8List? data, + ) => + super.noSuchMethod( + Invocation.method( + #postUrl, + [ + instanceId, + url, + data, + ], + ), + returnValueForMissingStub: null, + ); + @override + String? getUrl(int? instanceId) => (super.noSuchMethod(Invocation.method( + #getUrl, + [instanceId], + )) as String?); + @override + bool canGoBack(int? instanceId) => (super.noSuchMethod( + Invocation.method( + #canGoBack, + [instanceId], + ), + returnValue: false, + ) as bool); + @override + bool canGoForward(int? instanceId) => (super.noSuchMethod( + Invocation.method( + #canGoForward, + [instanceId], + ), + returnValue: false, + ) as bool); + @override + void goBack(int? instanceId) => super.noSuchMethod( + Invocation.method( + #goBack, + [instanceId], + ), + returnValueForMissingStub: null, + ); + @override + void goForward(int? instanceId) => super.noSuchMethod( + Invocation.method( + #goForward, + [instanceId], + ), + returnValueForMissingStub: null, + ); + @override + void reload(int? instanceId) => super.noSuchMethod( + Invocation.method( + #reload, + [instanceId], + ), + returnValueForMissingStub: null, + ); + @override + void clearCache( + int? instanceId, + bool? includeDiskFiles, + ) => super.noSuchMethod( - Invocation.method(#clearCache, [instanceId, includeDiskFiles]), - returnValueForMissingStub: null); + Invocation.method( + #clearCache, + [ + instanceId, + includeDiskFiles, + ], + ), + returnValueForMissingStub: null, + ); @override _i5.Future evaluateJavascript( - int? instanceId, String? javascriptString) => + int? instanceId, + String? javascriptString, + ) => (super.noSuchMethod( - Invocation.method( - #evaluateJavascript, [instanceId, javascriptString]), - returnValue: Future.value()) as _i5.Future); - @override - String? getTitle(int? instanceId) => - (super.noSuchMethod(Invocation.method(#getTitle, [instanceId])) - as String?); - @override - void scrollTo(int? instanceId, int? x, int? y) => - super.noSuchMethod(Invocation.method(#scrollTo, [instanceId, x, y]), - returnValueForMissingStub: null); - @override - void scrollBy(int? instanceId, int? x, int? y) => - super.noSuchMethod(Invocation.method(#scrollBy, [instanceId, x, y]), - returnValueForMissingStub: null); - @override - int getScrollX(int? instanceId) => - (super.noSuchMethod(Invocation.method(#getScrollX, [instanceId]), - returnValue: 0) as int); - @override - int getScrollY(int? instanceId) => - (super.noSuchMethod(Invocation.method(#getScrollY, [instanceId]), - returnValue: 0) as int); - @override - _i3.WebViewPoint getScrollPosition(int? instanceId) => - (super.noSuchMethod(Invocation.method(#getScrollPosition, [instanceId]), - returnValue: _FakeWebViewPoint_2()) as _i3.WebViewPoint); + Invocation.method( + #evaluateJavascript, + [ + instanceId, + javascriptString, + ], + ), + returnValue: _i5.Future.value(), + ) as _i5.Future); + @override + String? getTitle(int? instanceId) => (super.noSuchMethod(Invocation.method( + #getTitle, + [instanceId], + )) as String?); + @override + void scrollTo( + int? instanceId, + int? x, + int? y, + ) => + super.noSuchMethod( + Invocation.method( + #scrollTo, + [ + instanceId, + x, + y, + ], + ), + returnValueForMissingStub: null, + ); + @override + void scrollBy( + int? instanceId, + int? x, + int? y, + ) => + super.noSuchMethod( + Invocation.method( + #scrollBy, + [ + instanceId, + x, + y, + ], + ), + returnValueForMissingStub: null, + ); + @override + int getScrollX(int? instanceId) => (super.noSuchMethod( + Invocation.method( + #getScrollX, + [instanceId], + ), + returnValue: 0, + ) as int); + @override + int getScrollY(int? instanceId) => (super.noSuchMethod( + Invocation.method( + #getScrollY, + [instanceId], + ), + returnValue: 0, + ) as int); + @override + dynamic getScrollPosition(int? instanceId) => + super.noSuchMethod(Invocation.method( + #getScrollPosition, + [instanceId], + )); @override void setWebContentsDebuggingEnabled(bool? enabled) => super.noSuchMethod( - Invocation.method(#setWebContentsDebuggingEnabled, [enabled]), - returnValueForMissingStub: null); - @override - void setWebViewClient(int? instanceId, int? webViewClientInstanceId) => + Invocation.method( + #setWebContentsDebuggingEnabled, + [enabled], + ), + returnValueForMissingStub: null, + ); + @override + void setWebViewClient( + int? instanceId, + int? webViewClientInstanceId, + ) => super.noSuchMethod( - Invocation.method( - #setWebViewClient, [instanceId, webViewClientInstanceId]), - returnValueForMissingStub: null); + Invocation.method( + #setWebViewClient, + [ + instanceId, + webViewClientInstanceId, + ], + ), + returnValueForMissingStub: null, + ); @override void addJavaScriptChannel( - int? instanceId, int? javaScriptChannelInstanceId) => + int? instanceId, + int? javaScriptChannelInstanceId, + ) => super.noSuchMethod( - Invocation.method( - #addJavaScriptChannel, [instanceId, javaScriptChannelInstanceId]), - returnValueForMissingStub: null); + Invocation.method( + #addJavaScriptChannel, + [ + instanceId, + javaScriptChannelInstanceId, + ], + ), + returnValueForMissingStub: null, + ); @override void removeJavaScriptChannel( - int? instanceId, int? javaScriptChannelInstanceId) => + int? instanceId, + int? javaScriptChannelInstanceId, + ) => super.noSuchMethod( - Invocation.method(#removeJavaScriptChannel, - [instanceId, javaScriptChannelInstanceId]), - returnValueForMissingStub: null); - @override - void setDownloadListener(int? instanceId, int? listenerInstanceId) => + Invocation.method( + #removeJavaScriptChannel, + [ + instanceId, + javaScriptChannelInstanceId, + ], + ), + returnValueForMissingStub: null, + ); + @override + void setDownloadListener( + int? instanceId, + int? listenerInstanceId, + ) => super.noSuchMethod( - Invocation.method( - #setDownloadListener, [instanceId, listenerInstanceId]), - returnValueForMissingStub: null); - @override - void setWebChromeClient(int? instanceId, int? clientInstanceId) => + Invocation.method( + #setDownloadListener, + [ + instanceId, + listenerInstanceId, + ], + ), + returnValueForMissingStub: null, + ); + @override + void setWebChromeClient( + int? instanceId, + int? clientInstanceId, + ) => super.noSuchMethod( - Invocation.method( - #setWebChromeClient, [instanceId, clientInstanceId]), - returnValueForMissingStub: null); - @override - void setBackgroundColor(int? instanceId, int? color) => super.noSuchMethod( - Invocation.method(#setBackgroundColor, [instanceId, color]), - returnValueForMissingStub: null); + Invocation.method( + #setWebChromeClient, + [ + instanceId, + clientInstanceId, + ], + ), + returnValueForMissingStub: null, + ); + @override + void setBackgroundColor( + int? instanceId, + int? color, + ) => + super.noSuchMethod( + Invocation.method( + #setBackgroundColor, + [ + instanceId, + color, + ], + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [TestAssetManagerHostApi]. @@ -426,13 +940,21 @@ class MockTestAssetManagerHostApi extends _i1.Mock } @override - List list(String? path) => - (super.noSuchMethod(Invocation.method(#list, [path]), - returnValue: []) as List); - @override - String getAssetFilePathByName(String? name) => - (super.noSuchMethod(Invocation.method(#getAssetFilePathByName, [name]), - returnValue: '') as String); + List list(String? path) => (super.noSuchMethod( + Invocation.method( + #list, + [path], + ), + returnValue: [], + ) as List); + @override + String getAssetFilePathByName(String? name) => (super.noSuchMethod( + Invocation.method( + #getAssetFilePathByName, + [name], + ), + returnValue: '', + ) as String); } /// A class which mocks [WebChromeClient]. @@ -444,13 +966,34 @@ class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient { } @override - void onProgressChanged(_i2.WebView? webView, int? progress) => super - .noSuchMethod(Invocation.method(#onProgressChanged, [webView, progress]), - returnValueForMissingStub: null); - @override - _i2.WebChromeClient copy() => - (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebChromeClient_3()) as _i2.WebChromeClient); + void onProgressChanged( + _i2.WebView? webView, + int? progress, + ) => + super.noSuchMethod( + Invocation.method( + #onProgressChanged, + [ + webView, + progress, + ], + ), + returnValueForMissingStub: null, + ); + @override + _i2.WebChromeClient copy() => (super.noSuchMethod( + Invocation.method( + #copy, + [], + ), + returnValue: _FakeWebChromeClient_2( + this, + Invocation.method( + #copy, + [], + ), + ), + ) as _i2.WebChromeClient); } /// A class which mocks [WebView]. @@ -462,153 +1005,315 @@ class MockWebView extends _i1.Mock implements _i2.WebView { } @override - bool get useHybridComposition => - (super.noSuchMethod(Invocation.getter(#useHybridComposition), - returnValue: false) as bool); - @override - _i2.WebSettings get settings => - (super.noSuchMethod(Invocation.getter(#settings), - returnValue: _FakeWebSettings_4()) as _i2.WebSettings); - @override - _i5.Future loadData( - {String? data, String? mimeType, String? encoding}) => + bool get useHybridComposition => (super.noSuchMethod( + Invocation.getter(#useHybridComposition), + returnValue: false, + ) as bool); + @override + _i2.WebSettings get settings => (super.noSuchMethod( + Invocation.getter(#settings), + returnValue: _FakeWebSettings_3( + this, + Invocation.getter(#settings), + ), + ) as _i2.WebSettings); + @override + _i5.Future loadData({ + required String? data, + String? mimeType, + String? encoding, + }) => (super.noSuchMethod( - Invocation.method(#loadData, [], - {#data: data, #mimeType: mimeType, #encoding: encoding}), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future loadDataWithBaseUrl( - {String? baseUrl, - String? data, - String? mimeType, - String? encoding, - String? historyUrl}) => + Invocation.method( + #loadData, + [], + { + #data: data, + #mimeType: mimeType, + #encoding: encoding, + }, + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future loadDataWithBaseUrl({ + String? baseUrl, + required String? data, + String? mimeType, + String? encoding, + String? historyUrl, + }) => (super.noSuchMethod( - Invocation.method(#loadDataWithBaseUrl, [], { + Invocation.method( + #loadDataWithBaseUrl, + [], + { #baseUrl: baseUrl, #data: data, #mimeType: mimeType, #encoding: encoding, - #historyUrl: historyUrl - }), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future loadUrl(String? url, Map? headers) => - (super.noSuchMethod(Invocation.method(#loadUrl, [url, headers]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future postUrl(String? url, _i7.Uint8List? data) => - (super.noSuchMethod(Invocation.method(#postUrl, [url, data]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future getUrl() => - (super.noSuchMethod(Invocation.method(#getUrl, []), - returnValue: Future.value()) as _i5.Future); - @override - _i5.Future canGoBack() => - (super.noSuchMethod(Invocation.method(#canGoBack, []), - returnValue: Future.value(false)) as _i5.Future); - @override - _i5.Future canGoForward() => - (super.noSuchMethod(Invocation.method(#canGoForward, []), - returnValue: Future.value(false)) as _i5.Future); - @override - _i5.Future goBack() => - (super.noSuchMethod(Invocation.method(#goBack, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future goForward() => - (super.noSuchMethod(Invocation.method(#goForward, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future reload() => - (super.noSuchMethod(Invocation.method(#reload, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future clearCache(bool? includeDiskFiles) => - (super.noSuchMethod(Invocation.method(#clearCache, [includeDiskFiles]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future evaluateJavascript(String? javascriptString) => (super - .noSuchMethod(Invocation.method(#evaluateJavascript, [javascriptString]), - returnValue: Future.value()) as _i5.Future); - @override - _i5.Future getTitle() => - (super.noSuchMethod(Invocation.method(#getTitle, []), - returnValue: Future.value()) as _i5.Future); - @override - _i5.Future scrollTo(int? x, int? y) => - (super.noSuchMethod(Invocation.method(#scrollTo, [x, y]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future scrollBy(int? x, int? y) => - (super.noSuchMethod(Invocation.method(#scrollBy, [x, y]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future getScrollX() => - (super.noSuchMethod(Invocation.method(#getScrollX, []), - returnValue: Future.value(0)) as _i5.Future); - @override - _i5.Future getScrollY() => - (super.noSuchMethod(Invocation.method(#getScrollY, []), - returnValue: Future.value(0)) as _i5.Future); - @override - _i5.Future<_i4.Offset> getScrollPosition() => - (super.noSuchMethod(Invocation.method(#getScrollPosition, []), - returnValue: Future<_i4.Offset>.value(_FakeOffset_5())) - as _i5.Future<_i4.Offset>); + #historyUrl: historyUrl, + }, + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future loadUrl( + String? url, + Map? headers, + ) => + (super.noSuchMethod( + Invocation.method( + #loadUrl, + [ + url, + headers, + ], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future postUrl( + String? url, + _i7.Uint8List? data, + ) => + (super.noSuchMethod( + Invocation.method( + #postUrl, + [ + url, + data, + ], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future getUrl() => (super.noSuchMethod( + Invocation.method( + #getUrl, + [], + ), + returnValue: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future canGoBack() => (super.noSuchMethod( + Invocation.method( + #canGoBack, + [], + ), + returnValue: _i5.Future.value(false), + ) as _i5.Future); + @override + _i5.Future canGoForward() => (super.noSuchMethod( + Invocation.method( + #canGoForward, + [], + ), + returnValue: _i5.Future.value(false), + ) as _i5.Future); + @override + _i5.Future goBack() => (super.noSuchMethod( + Invocation.method( + #goBack, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future goForward() => (super.noSuchMethod( + Invocation.method( + #goForward, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future reload() => (super.noSuchMethod( + Invocation.method( + #reload, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future clearCache(bool? includeDiskFiles) => (super.noSuchMethod( + Invocation.method( + #clearCache, + [includeDiskFiles], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future evaluateJavascript(String? javascriptString) => + (super.noSuchMethod( + Invocation.method( + #evaluateJavascript, + [javascriptString], + ), + returnValue: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future getTitle() => (super.noSuchMethod( + Invocation.method( + #getTitle, + [], + ), + returnValue: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future scrollTo( + int? x, + int? y, + ) => + (super.noSuchMethod( + Invocation.method( + #scrollTo, + [ + x, + y, + ], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future scrollBy( + int? x, + int? y, + ) => + (super.noSuchMethod( + Invocation.method( + #scrollBy, + [ + x, + y, + ], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future getScrollX() => (super.noSuchMethod( + Invocation.method( + #getScrollX, + [], + ), + returnValue: _i5.Future.value(0), + ) as _i5.Future); + @override + _i5.Future getScrollY() => (super.noSuchMethod( + Invocation.method( + #getScrollY, + [], + ), + returnValue: _i5.Future.value(0), + ) as _i5.Future); + @override + _i5.Future<_i3.Offset> getScrollPosition() => (super.noSuchMethod( + Invocation.method( + #getScrollPosition, + [], + ), + returnValue: _i5.Future<_i3.Offset>.value(_FakeOffset_4( + this, + Invocation.method( + #getScrollPosition, + [], + ), + )), + ) as _i5.Future<_i3.Offset>); @override _i5.Future setWebViewClient(_i2.WebViewClient? webViewClient) => - (super.noSuchMethod(Invocation.method(#setWebViewClient, [webViewClient]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + (super.noSuchMethod( + Invocation.method( + #setWebViewClient, + [webViewClient], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); @override _i5.Future addJavaScriptChannel( _i2.JavaScriptChannel? javaScriptChannel) => (super.noSuchMethod( - Invocation.method(#addJavaScriptChannel, [javaScriptChannel]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + Invocation.method( + #addJavaScriptChannel, + [javaScriptChannel], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); @override _i5.Future removeJavaScriptChannel( _i2.JavaScriptChannel? javaScriptChannel) => (super.noSuchMethod( - Invocation.method(#removeJavaScriptChannel, [javaScriptChannel]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + Invocation.method( + #removeJavaScriptChannel, + [javaScriptChannel], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); @override _i5.Future setDownloadListener(_i2.DownloadListener? listener) => - (super.noSuchMethod(Invocation.method(#setDownloadListener, [listener]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + (super.noSuchMethod( + Invocation.method( + #setDownloadListener, + [listener], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); @override _i5.Future setWebChromeClient(_i2.WebChromeClient? client) => - (super.noSuchMethod(Invocation.method(#setWebChromeClient, [client]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future setBackgroundColor(_i4.Color? color) => - (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future release() => - (super.noSuchMethod(Invocation.method(#release, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i2.WebView copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebView_6()) as _i2.WebView); + (super.noSuchMethod( + Invocation.method( + #setWebChromeClient, + [client], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future setBackgroundColor(_i3.Color? color) => (super.noSuchMethod( + Invocation.method( + #setBackgroundColor, + [color], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future release() => (super.noSuchMethod( + Invocation.method( + #release, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i2.WebView copy() => (super.noSuchMethod( + Invocation.method( + #copy, + [], + ), + returnValue: _FakeWebView_5( + this, + Invocation.method( + #copy, + [], + ), + ), + ) as _i2.WebView); } /// A class which mocks [WebViewClient]. @@ -620,39 +1325,118 @@ class MockWebViewClient extends _i1.Mock implements _i2.WebViewClient { } @override - bool get shouldOverrideUrlLoading => - (super.noSuchMethod(Invocation.getter(#shouldOverrideUrlLoading), - returnValue: false) as bool); - @override - void onPageStarted(_i2.WebView? webView, String? url) => - super.noSuchMethod(Invocation.method(#onPageStarted, [webView, url]), - returnValueForMissingStub: null); + bool get shouldOverrideUrlLoading => (super.noSuchMethod( + Invocation.getter(#shouldOverrideUrlLoading), + returnValue: false, + ) as bool); @override - void onPageFinished(_i2.WebView? webView, String? url) => - super.noSuchMethod(Invocation.method(#onPageFinished, [webView, url]), - returnValueForMissingStub: null); - @override - void onReceivedRequestError(_i2.WebView? webView, - _i2.WebResourceRequest? request, _i2.WebResourceError? error) => + void onPageStarted( + _i2.WebView? webView, + String? url, + ) => super.noSuchMethod( - Invocation.method(#onReceivedRequestError, [webView, request, error]), - returnValueForMissingStub: null); - @override - void onReceivedError(_i2.WebView? webView, int? errorCode, - String? description, String? failingUrl) => + Invocation.method( + #onPageStarted, + [ + webView, + url, + ], + ), + returnValueForMissingStub: null, + ); + @override + void onPageFinished( + _i2.WebView? webView, + String? url, + ) => + super.noSuchMethod( + Invocation.method( + #onPageFinished, + [ + webView, + url, + ], + ), + returnValueForMissingStub: null, + ); + @override + void onReceivedRequestError( + _i2.WebView? webView, + _i2.WebResourceRequest? request, + _i2.WebResourceError? error, + ) => super.noSuchMethod( + Invocation.method( + #onReceivedRequestError, + [ + webView, + request, + error, + ], + ), + returnValueForMissingStub: null, + ); + @override + void onReceivedError( + _i2.WebView? webView, + int? errorCode, + String? description, + String? failingUrl, + ) => + super.noSuchMethod( + Invocation.method( + #onReceivedError, + [ + webView, + errorCode, + description, + failingUrl, + ], + ), + returnValueForMissingStub: null, + ); + @override + void requestLoading( + _i2.WebView? webView, + _i2.WebResourceRequest? request, + ) => + super.noSuchMethod( + Invocation.method( + #requestLoading, + [ + webView, + request, + ], + ), + returnValueForMissingStub: null, + ); + @override + void urlLoading( + _i2.WebView? webView, + String? url, + ) => + super.noSuchMethod( + Invocation.method( + #urlLoading, + [ + webView, + url, + ], + ), + returnValueForMissingStub: null, + ); + @override + _i2.WebViewClient copy() => (super.noSuchMethod( + Invocation.method( + #copy, + [], + ), + returnValue: _FakeWebViewClient_6( + this, Invocation.method( - #onReceivedError, [webView, errorCode, description, failingUrl]), - returnValueForMissingStub: null); - @override - void requestLoading(_i2.WebView? webView, _i2.WebResourceRequest? request) => - super.noSuchMethod(Invocation.method(#requestLoading, [webView, request]), - returnValueForMissingStub: null); - @override - void urlLoading(_i2.WebView? webView, String? url) => - super.noSuchMethod(Invocation.method(#urlLoading, [webView, url]), - returnValueForMissingStub: null); - @override - _i2.WebViewClient copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebViewClient_7()) as _i2.WebViewClient); + #copy, + [], + ), + ), + ) as _i2.WebViewClient); } diff --git a/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart index 31226f9c89d7..ff43a116f592 100644 --- a/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart @@ -1,4 +1,7 @@ -// Autogenerated from Pigeon (v4.0.2), do not edit directly. +// 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 (v4.2.3), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import // ignore_for_file: avoid_relative_lib_imports @@ -8,32 +11,29 @@ import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:webview_flutter_android/src/android_webview.pigeon.dart'; - -class _TestJavaObjectHostApiCodec extends StandardMessageCodec { - const _TestJavaObjectHostApiCodec(); -} +import 'package:webview_pro_android/src/android_webview.pigeon.dart'; +/// Handles methods calls to the native Java Object class. +/// +/// Also handles calls to remove the reference to an instance with `dispose`. +/// +/// See https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html. abstract class TestJavaObjectHostApi { - static const MessageCodec codec = _TestJavaObjectHostApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); void dispose(int identifier); - static void setup(TestJavaObjectHostApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setup(TestJavaObjectHostApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.JavaObjectHostApi.dispose', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.JavaObjectHostApi.dispose', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.JavaObjectHostApi.dispose was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.JavaObjectHostApi.dispose 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.JavaObjectHostApi.dispose was null, expected non-null int.'); + assert(arg_identifier != null, 'Argument for dev.flutter.pigeon.JavaObjectHostApi.dispose was null, expected non-null int.'); api.dispose(arg_identifier!); return {}; }); @@ -42,39 +42,37 @@ abstract class TestJavaObjectHostApi { } } -class _TestWebViewHostApiCodec extends StandardMessageCodec { +class _TestWebViewHostApiCodec extends StandardMessageCodec{ const _TestWebViewHostApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { if (value is WebViewPoint) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else { + } else +{ super.writeValue(buffer, value); } } - @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return WebViewPoint.decode(readValue(buffer)!); - - default: + + default: return super.readValueOfType(type, buffer); + } } } - abstract class TestWebViewHostApi { static const MessageCodec codec = _TestWebViewHostApiCodec(); void create(int instanceId, bool useHybridComposition); void dispose(int instanceId); - void loadData( - int instanceId, String data, String? mimeType, String? encoding); - void loadDataWithBaseUrl(int instanceId, String? baseUrl, String data, - String? mimeType, String? encoding, String? historyUrl); + void loadData(int instanceId, String data, String? mimeType, String? encoding); + void loadDataWithBaseUrl(int instanceId, String? baseUrl, String data, String? mimeType, String? encoding, String? historyUrl); void loadUrl(int instanceId, String url, Map headers); void postUrl(int instanceId, String url, Uint8List data); String? getUrl(int instanceId); @@ -98,25 +96,20 @@ abstract class TestWebViewHostApi { void setDownloadListener(int instanceId, int? listenerInstanceId); void setWebChromeClient(int instanceId, int? clientInstanceId); void setBackgroundColor(int instanceId, int color); - static void setup(TestWebViewHostApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setup(TestWebViewHostApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.create', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.create', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.create was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.create 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.WebViewHostApi.create was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.create was null, expected non-null int.'); final bool? arg_useHybridComposition = (args[1] as bool?); - assert(arg_useHybridComposition != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.create was null, expected non-null bool.'); + assert(arg_useHybridComposition != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.create was null, expected non-null bool.'); api.create(arg_instanceId!, arg_useHybridComposition!); return {}; }); @@ -124,18 +117,15 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.dispose', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.dispose', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.dispose was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.dispose 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.WebViewHostApi.dispose was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.dispose was null, expected non-null int.'); api.dispose(arg_instanceId!); return {}; }); @@ -143,21 +133,17 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.loadData', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.loadData', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.loadData was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadData 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.WebViewHostApi.loadData was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadData was null, expected non-null int.'); final String? arg_data = (args[1] as String?); - assert(arg_data != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.loadData was null, expected non-null String.'); + assert(arg_data != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadData was null, expected non-null String.'); final String? arg_mimeType = (args[2] as String?); final String? arg_encoding = (args[3] as String?); api.loadData(arg_instanceId!, arg_data!, arg_mimeType, arg_encoding); @@ -167,52 +153,41 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl 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.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null int.'); final String? arg_baseUrl = (args[1] as String?); final String? arg_data = (args[2] as String?); - assert(arg_data != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.'); + assert(arg_data != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.'); final String? arg_mimeType = (args[3] as String?); final String? arg_encoding = (args[4] as String?); final String? arg_historyUrl = (args[5] as String?); - api.loadDataWithBaseUrl(arg_instanceId!, arg_baseUrl, arg_data!, - arg_mimeType, arg_encoding, arg_historyUrl); + api.loadDataWithBaseUrl(arg_instanceId!, arg_baseUrl, arg_data!, arg_mimeType, arg_encoding, arg_historyUrl); return {}; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.loadUrl', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.loadUrl', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.loadUrl was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadUrl 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.WebViewHostApi.loadUrl was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadUrl was null, expected non-null int.'); final String? arg_url = (args[1] as String?); - assert(arg_url != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.loadUrl was null, expected non-null String.'); - final Map? arg_headers = - (args[2] as Map?)?.cast(); - assert(arg_headers != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.loadUrl was null, expected non-null Map.'); + assert(arg_url != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadUrl was null, expected non-null String.'); + final Map? arg_headers = (args[2] as Map?)?.cast(); + assert(arg_headers != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.loadUrl was null, expected non-null Map.'); api.loadUrl(arg_instanceId!, arg_url!, arg_headers!); return {}; }); @@ -220,24 +195,19 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.postUrl', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.postUrl', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.postUrl was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.postUrl 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.WebViewHostApi.postUrl was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.postUrl was null, expected non-null int.'); final String? arg_url = (args[1] as String?); - assert(arg_url != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.postUrl was null, expected non-null String.'); + assert(arg_url != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.postUrl was null, expected non-null String.'); final Uint8List? arg_data = (args[2] as Uint8List?); - assert(arg_data != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.postUrl was null, expected non-null Uint8List.'); + assert(arg_data != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.postUrl was null, expected non-null Uint8List.'); api.postUrl(arg_instanceId!, arg_url!, arg_data!); return {}; }); @@ -245,18 +215,15 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.getUrl', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.getUrl', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.getUrl was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getUrl 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.WebViewHostApi.getUrl was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getUrl was null, expected non-null int.'); final String? output = api.getUrl(arg_instanceId!); return {'result': output}; }); @@ -264,18 +231,15 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.canGoBack', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.canGoBack', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.canGoBack was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.canGoBack 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.WebViewHostApi.canGoBack was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.canGoBack was null, expected non-null int.'); final bool output = api.canGoBack(arg_instanceId!); return {'result': output}; }); @@ -283,18 +247,15 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.canGoForward', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.canGoForward', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.canGoForward was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.canGoForward 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.WebViewHostApi.canGoForward was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.canGoForward was null, expected non-null int.'); final bool output = api.canGoForward(arg_instanceId!); return {'result': output}; }); @@ -302,18 +263,15 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.goBack', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.goBack', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.goBack was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.goBack 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.WebViewHostApi.goBack was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.goBack was null, expected non-null int.'); api.goBack(arg_instanceId!); return {}; }); @@ -321,18 +279,15 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.goForward', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.goForward', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.goForward was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.goForward 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.WebViewHostApi.goForward was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.goForward was null, expected non-null int.'); api.goForward(arg_instanceId!); return {}; }); @@ -340,18 +295,15 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.reload', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.reload', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.reload was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.reload 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.WebViewHostApi.reload was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.reload was null, expected non-null int.'); api.reload(arg_instanceId!); return {}; }); @@ -359,21 +311,17 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.clearCache', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.clearCache', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.clearCache was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.clearCache 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.WebViewHostApi.clearCache was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.clearCache was null, expected non-null int.'); final bool? arg_includeDiskFiles = (args[1] as bool?); - assert(arg_includeDiskFiles != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.clearCache was null, expected non-null bool.'); + assert(arg_includeDiskFiles != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.clearCache was null, expected non-null bool.'); api.clearCache(arg_instanceId!, arg_includeDiskFiles!); return {}; }); @@ -381,41 +329,33 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.evaluateJavascript', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.evaluateJavascript', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.evaluateJavascript was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.evaluateJavascript 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.WebViewHostApi.evaluateJavascript was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.evaluateJavascript was null, expected non-null int.'); final String? arg_javascriptString = (args[1] as String?); - assert(arg_javascriptString != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.evaluateJavascript was null, expected non-null String.'); - final String? output = await api.evaluateJavascript( - arg_instanceId!, arg_javascriptString!); + assert(arg_javascriptString != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.evaluateJavascript was null, expected non-null String.'); + final String? output = await api.evaluateJavascript(arg_instanceId!, arg_javascriptString!); return {'result': output}; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.getTitle', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.getTitle', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.getTitle was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getTitle 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.WebViewHostApi.getTitle was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getTitle was null, expected non-null int.'); final String? output = api.getTitle(arg_instanceId!); return {'result': output}; }); @@ -423,24 +363,19 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.scrollTo', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.scrollTo', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollTo was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollTo 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.WebViewHostApi.scrollTo was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollTo was null, expected non-null int.'); final int? arg_x = (args[1] as int?); - assert(arg_x != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollTo was null, expected non-null int.'); + assert(arg_x != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollTo was null, expected non-null int.'); final int? arg_y = (args[2] as int?); - assert(arg_y != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollTo was null, expected non-null int.'); + assert(arg_y != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollTo was null, expected non-null int.'); api.scrollTo(arg_instanceId!, arg_x!, arg_y!); return {}; }); @@ -448,24 +383,19 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.scrollBy', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.scrollBy', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollBy was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollBy 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.WebViewHostApi.scrollBy was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollBy was null, expected non-null int.'); final int? arg_x = (args[1] as int?); - assert(arg_x != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollBy was null, expected non-null int.'); + assert(arg_x != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollBy was null, expected non-null int.'); final int? arg_y = (args[2] as int?); - assert(arg_y != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollBy was null, expected non-null int.'); + assert(arg_y != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.scrollBy was null, expected non-null int.'); api.scrollBy(arg_instanceId!, arg_x!, arg_y!); return {}; }); @@ -473,18 +403,15 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.getScrollX', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.getScrollX', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollX was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollX 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.WebViewHostApi.getScrollX was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollX was null, expected non-null int.'); final int output = api.getScrollX(arg_instanceId!); return {'result': output}; }); @@ -492,18 +419,15 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.getScrollY', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.getScrollY', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollY was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollY 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.WebViewHostApi.getScrollY was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollY was null, expected non-null int.'); final int output = api.getScrollY(arg_instanceId!); return {'result': output}; }); @@ -511,18 +435,15 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.getScrollPosition', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.getScrollPosition', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollPosition was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollPosition 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.WebViewHostApi.getScrollPosition was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollPosition was null, expected non-null int.'); final WebViewPoint output = api.getScrollPosition(arg_instanceId!); return {'result': output}; }); @@ -530,19 +451,15 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.setWebContentsDebuggingEnabled', - codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.setWebContentsDebuggingEnabled', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebContentsDebuggingEnabled was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebContentsDebuggingEnabled was null.'); final List args = (message as List?)!; final bool? arg_enabled = (args[0] as bool?); - assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebContentsDebuggingEnabled was null, expected non-null bool.'); + assert(arg_enabled != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebContentsDebuggingEnabled was null, expected non-null bool.'); api.setWebContentsDebuggingEnabled(arg_enabled!); return {}; }); @@ -550,21 +467,17 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.setWebViewClient', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.setWebViewClient', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebViewClient was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebViewClient 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.WebViewHostApi.setWebViewClient was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebViewClient was null, expected non-null int.'); final int? arg_webViewClientInstanceId = (args[1] as int?); - assert(arg_webViewClientInstanceId != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebViewClient was null, expected non-null int.'); + assert(arg_webViewClientInstanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebViewClient was null, expected non-null int.'); api.setWebViewClient(arg_instanceId!, arg_webViewClientInstanceId!); return {}; }); @@ -572,64 +485,51 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.addJavaScriptChannel', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.addJavaScriptChannel', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.addJavaScriptChannel was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.addJavaScriptChannel 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.WebViewHostApi.addJavaScriptChannel was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.addJavaScriptChannel was null, expected non-null int.'); final int? arg_javaScriptChannelInstanceId = (args[1] as int?); - assert(arg_javaScriptChannelInstanceId != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.addJavaScriptChannel was null, expected non-null int.'); - api.addJavaScriptChannel( - arg_instanceId!, arg_javaScriptChannelInstanceId!); + assert(arg_javaScriptChannelInstanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.addJavaScriptChannel was null, expected non-null int.'); + api.addJavaScriptChannel(arg_instanceId!, arg_javaScriptChannelInstanceId!); return {}; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.removeJavaScriptChannel', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.removeJavaScriptChannel', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.removeJavaScriptChannel was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.removeJavaScriptChannel 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.WebViewHostApi.removeJavaScriptChannel was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.removeJavaScriptChannel was null, expected non-null int.'); final int? arg_javaScriptChannelInstanceId = (args[1] as int?); - assert(arg_javaScriptChannelInstanceId != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.removeJavaScriptChannel was null, expected non-null int.'); - api.removeJavaScriptChannel( - arg_instanceId!, arg_javaScriptChannelInstanceId!); + assert(arg_javaScriptChannelInstanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.removeJavaScriptChannel was null, expected non-null int.'); + api.removeJavaScriptChannel(arg_instanceId!, arg_javaScriptChannelInstanceId!); return {}; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.setDownloadListener', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.setDownloadListener', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.setDownloadListener was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setDownloadListener 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.WebViewHostApi.setDownloadListener was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setDownloadListener was null, expected non-null int.'); final int? arg_listenerInstanceId = (args[1] as int?); api.setDownloadListener(arg_instanceId!, arg_listenerInstanceId); return {}; @@ -638,18 +538,15 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.setWebChromeClient', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.setWebChromeClient', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebChromeClient was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebChromeClient 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.WebViewHostApi.setWebChromeClient was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setWebChromeClient was null, expected non-null int.'); final int? arg_clientInstanceId = (args[1] as int?); api.setWebChromeClient(arg_instanceId!, arg_clientInstanceId); return {}; @@ -658,21 +555,17 @@ abstract class TestWebViewHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewHostApi.setBackgroundColor', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewHostApi.setBackgroundColor', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.setBackgroundColor was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setBackgroundColor 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.WebViewHostApi.setBackgroundColor was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setBackgroundColor was null, expected non-null int.'); final int? arg_color = (args[1] as int?); - assert(arg_color != null, - 'Argument for dev.flutter.pigeon.WebViewHostApi.setBackgroundColor was null, expected non-null int.'); + assert(arg_color != null, 'Argument for dev.flutter.pigeon.WebViewHostApi.setBackgroundColor was null, expected non-null int.'); api.setBackgroundColor(arg_instanceId!, arg_color!); return {}; }); @@ -681,12 +574,8 @@ abstract class TestWebViewHostApi { } } -class _TestWebSettingsHostApiCodec extends StandardMessageCodec { - const _TestWebSettingsHostApiCodec(); -} - abstract class TestWebSettingsHostApi { - static const MessageCodec codec = _TestWebSettingsHostApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); void create(int instanceId, int webViewInstanceId); void dispose(int instanceId); @@ -703,25 +592,20 @@ abstract class TestWebSettingsHostApi { void setBuiltInZoomControls(int instanceId, bool enabled); void setAllowFileAccess(int instanceId, bool enabled); void setGeolocationEnabled(int instanceId, bool enabled); - static void setup(TestWebSettingsHostApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setup(TestWebSettingsHostApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.create', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebSettingsHostApi.create', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.create was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.create 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.WebSettingsHostApi.create was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.create was null, expected non-null int.'); final int? arg_webViewInstanceId = (args[1] as int?); - assert(arg_webViewInstanceId != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.create was null, expected non-null int.'); + assert(arg_webViewInstanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.create was null, expected non-null int.'); api.create(arg_instanceId!, arg_webViewInstanceId!); return {}; }); @@ -729,18 +613,15 @@ abstract class TestWebSettingsHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.dispose', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebSettingsHostApi.dispose', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.dispose was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.dispose 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.WebSettingsHostApi.dispose was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.dispose was null, expected non-null int.'); api.dispose(arg_instanceId!); return {}; }); @@ -748,21 +629,17 @@ abstract class TestWebSettingsHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled 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.WebSettingsHostApi.setDomStorageEnabled was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled was null, expected non-null int.'); final bool? arg_flag = (args[1] as bool?); - assert(arg_flag != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled was null, expected non-null bool.'); + assert(arg_flag != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled was null, expected non-null bool.'); api.setDomStorageEnabled(arg_instanceId!, arg_flag!); return {}; }); @@ -770,46 +647,35 @@ abstract class TestWebSettingsHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptCanOpenWindowsAutomatically', - codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptCanOpenWindowsAutomatically', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptCanOpenWindowsAutomatically was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptCanOpenWindowsAutomatically 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.WebSettingsHostApi.setJavaScriptCanOpenWindowsAutomatically was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptCanOpenWindowsAutomatically was null, expected non-null int.'); final bool? arg_flag = (args[1] as bool?); - assert(arg_flag != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptCanOpenWindowsAutomatically was null, expected non-null bool.'); - api.setJavaScriptCanOpenWindowsAutomatically( - arg_instanceId!, arg_flag!); + assert(arg_flag != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptCanOpenWindowsAutomatically was null, expected non-null bool.'); + api.setJavaScriptCanOpenWindowsAutomatically(arg_instanceId!, arg_flag!); return {}; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setSupportMultipleWindows', - codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebSettingsHostApi.setSupportMultipleWindows', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportMultipleWindows was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportMultipleWindows 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.WebSettingsHostApi.setSupportMultipleWindows was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportMultipleWindows was null, expected non-null int.'); final bool? arg_support = (args[1] as bool?); - assert(arg_support != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportMultipleWindows was null, expected non-null bool.'); + assert(arg_support != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportMultipleWindows was null, expected non-null bool.'); api.setSupportMultipleWindows(arg_instanceId!, arg_support!); return {}; }); @@ -817,21 +683,17 @@ abstract class TestWebSettingsHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptEnabled', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptEnabled', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptEnabled was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptEnabled 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.WebSettingsHostApi.setJavaScriptEnabled was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptEnabled was null, expected non-null int.'); final bool? arg_flag = (args[1] as bool?); - assert(arg_flag != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptEnabled was null, expected non-null bool.'); + assert(arg_flag != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptEnabled was null, expected non-null bool.'); api.setJavaScriptEnabled(arg_instanceId!, arg_flag!); return {}; }); @@ -839,18 +701,15 @@ abstract class TestWebSettingsHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString 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.WebSettingsHostApi.setUserAgentString was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString was null, expected non-null int.'); final String? arg_userAgentString = (args[1] as String?); api.setUserAgentString(arg_instanceId!, arg_userAgentString); return {}; @@ -859,45 +718,35 @@ abstract class TestWebSettingsHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setMediaPlaybackRequiresUserGesture', - codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebSettingsHostApi.setMediaPlaybackRequiresUserGesture', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setMediaPlaybackRequiresUserGesture was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setMediaPlaybackRequiresUserGesture 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.WebSettingsHostApi.setMediaPlaybackRequiresUserGesture was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setMediaPlaybackRequiresUserGesture was null, expected non-null int.'); final bool? arg_require = (args[1] as bool?); - assert(arg_require != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setMediaPlaybackRequiresUserGesture was null, expected non-null bool.'); - api.setMediaPlaybackRequiresUserGesture( - arg_instanceId!, arg_require!); + assert(arg_require != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setMediaPlaybackRequiresUserGesture was null, expected non-null bool.'); + api.setMediaPlaybackRequiresUserGesture(arg_instanceId!, arg_require!); return {}; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setSupportZoom', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebSettingsHostApi.setSupportZoom', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportZoom was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportZoom 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.WebSettingsHostApi.setSupportZoom was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportZoom was null, expected non-null int.'); final bool? arg_support = (args[1] as bool?); - assert(arg_support != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportZoom was null, expected non-null bool.'); + assert(arg_support != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportZoom was null, expected non-null bool.'); api.setSupportZoom(arg_instanceId!, arg_support!); return {}; }); @@ -905,22 +754,17 @@ abstract class TestWebSettingsHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setLoadWithOverviewMode', - codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebSettingsHostApi.setLoadWithOverviewMode', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setLoadWithOverviewMode was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setLoadWithOverviewMode 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.WebSettingsHostApi.setLoadWithOverviewMode was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setLoadWithOverviewMode was null, expected non-null int.'); final bool? arg_overview = (args[1] as bool?); - assert(arg_overview != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setLoadWithOverviewMode was null, expected non-null bool.'); + assert(arg_overview != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setLoadWithOverviewMode was null, expected non-null bool.'); api.setLoadWithOverviewMode(arg_instanceId!, arg_overview!); return {}; }); @@ -928,21 +772,17 @@ abstract class TestWebSettingsHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setUseWideViewPort', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebSettingsHostApi.setUseWideViewPort', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUseWideViewPort was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUseWideViewPort 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.WebSettingsHostApi.setUseWideViewPort was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUseWideViewPort was null, expected non-null int.'); final bool? arg_use = (args[1] as bool?); - assert(arg_use != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUseWideViewPort was null, expected non-null bool.'); + assert(arg_use != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUseWideViewPort was null, expected non-null bool.'); api.setUseWideViewPort(arg_instanceId!, arg_use!); return {}; }); @@ -950,21 +790,17 @@ abstract class TestWebSettingsHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setDisplayZoomControls', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebSettingsHostApi.setDisplayZoomControls', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDisplayZoomControls was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDisplayZoomControls 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.WebSettingsHostApi.setDisplayZoomControls was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDisplayZoomControls was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); - assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDisplayZoomControls was null, expected non-null bool.'); + assert(arg_enabled != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDisplayZoomControls was null, expected non-null bool.'); api.setDisplayZoomControls(arg_instanceId!, arg_enabled!); return {}; }); @@ -972,21 +808,17 @@ abstract class TestWebSettingsHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setBuiltInZoomControls', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebSettingsHostApi.setBuiltInZoomControls', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setBuiltInZoomControls was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setBuiltInZoomControls 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.WebSettingsHostApi.setBuiltInZoomControls was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setBuiltInZoomControls was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); - assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setBuiltInZoomControls was null, expected non-null bool.'); + assert(arg_enabled != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setBuiltInZoomControls was null, expected non-null bool.'); api.setBuiltInZoomControls(arg_instanceId!, arg_enabled!); return {}; }); @@ -994,21 +826,17 @@ abstract class TestWebSettingsHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess 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.WebSettingsHostApi.setAllowFileAccess was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); - assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess was null, expected non-null bool.'); + assert(arg_enabled != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setAllowFileAccess was null, expected non-null bool.'); api.setAllowFileAccess(arg_instanceId!, arg_enabled!); return {}; }); @@ -1016,21 +844,17 @@ abstract class TestWebSettingsHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebSettingsHostApi.setGeolocationEnabled', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebSettingsHostApi.setGeolocationEnabled', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setGeolocationEnabled was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setGeolocationEnabled 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.WebSettingsHostApi.setGeolocationEnabled was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setGeolocationEnabled was null, expected non-null int.'); final bool? arg_enabled = (args[1] as bool?); - assert(arg_enabled != null, - 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setGeolocationEnabled was null, expected non-null bool.'); + assert(arg_enabled != null, 'Argument for dev.flutter.pigeon.WebSettingsHostApi.setGeolocationEnabled was null, expected non-null bool.'); api.setGeolocationEnabled(arg_instanceId!, arg_enabled!); return {}; }); @@ -1039,34 +863,24 @@ abstract class TestWebSettingsHostApi { } } -class _TestJavaScriptChannelHostApiCodec extends StandardMessageCodec { - const _TestJavaScriptChannelHostApiCodec(); -} - abstract class TestJavaScriptChannelHostApi { - static const MessageCodec codec = - _TestJavaScriptChannelHostApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); void create(int instanceId, String channelName); - static void setup(TestJavaScriptChannelHostApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setup(TestJavaScriptChannelHostApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.JavaScriptChannelHostApi.create', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.JavaScriptChannelHostApi.create', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.JavaScriptChannelHostApi.create was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.JavaScriptChannelHostApi.create 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.JavaScriptChannelHostApi.create was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.JavaScriptChannelHostApi.create was null, expected non-null int.'); final String? arg_channelName = (args[1] as String?); - assert(arg_channelName != null, - 'Argument for dev.flutter.pigeon.JavaScriptChannelHostApi.create was null, expected non-null String.'); + assert(arg_channelName != null, 'Argument for dev.flutter.pigeon.JavaScriptChannelHostApi.create was null, expected non-null String.'); api.create(arg_instanceId!, arg_channelName!); return {}; }); @@ -1075,33 +889,24 @@ abstract class TestJavaScriptChannelHostApi { } } -class _TestWebViewClientHostApiCodec extends StandardMessageCodec { - const _TestWebViewClientHostApiCodec(); -} - abstract class TestWebViewClientHostApi { - static const MessageCodec codec = _TestWebViewClientHostApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); void create(int instanceId, bool shouldOverrideUrlLoading); - static void setup(TestWebViewClientHostApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setup(TestWebViewClientHostApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebViewClientHostApi.create', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebViewClientHostApi.create', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebViewClientHostApi.create was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebViewClientHostApi.create 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.WebViewClientHostApi.create was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebViewClientHostApi.create was null, expected non-null int.'); final bool? arg_shouldOverrideUrlLoading = (args[1] as bool?); - assert(arg_shouldOverrideUrlLoading != null, - 'Argument for dev.flutter.pigeon.WebViewClientHostApi.create was null, expected non-null bool.'); + assert(arg_shouldOverrideUrlLoading != null, 'Argument for dev.flutter.pigeon.WebViewClientHostApi.create was null, expected non-null bool.'); api.create(arg_instanceId!, arg_shouldOverrideUrlLoading!); return {}; }); @@ -1110,31 +915,22 @@ abstract class TestWebViewClientHostApi { } } -class _TestDownloadListenerHostApiCodec extends StandardMessageCodec { - const _TestDownloadListenerHostApiCodec(); -} - abstract class TestDownloadListenerHostApi { - static const MessageCodec codec = - _TestDownloadListenerHostApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); void create(int instanceId); - static void setup(TestDownloadListenerHostApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setup(TestDownloadListenerHostApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.DownloadListenerHostApi.create', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.DownloadListenerHostApi.create', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.DownloadListenerHostApi.create was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.DownloadListenerHostApi.create 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.DownloadListenerHostApi.create was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.DownloadListenerHostApi.create was null, expected non-null int.'); api.create(arg_instanceId!); return {}; }); @@ -1143,33 +939,24 @@ abstract class TestDownloadListenerHostApi { } } -class _TestWebChromeClientHostApiCodec extends StandardMessageCodec { - const _TestWebChromeClientHostApiCodec(); -} - abstract class TestWebChromeClientHostApi { - static const MessageCodec codec = _TestWebChromeClientHostApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); void create(int instanceId, int webViewClientInstanceId); - static void setup(TestWebChromeClientHostApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setup(TestWebChromeClientHostApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebChromeClientHostApi.create', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebChromeClientHostApi.create', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebChromeClientHostApi.create was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebChromeClientHostApi.create 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.WebChromeClientHostApi.create was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebChromeClientHostApi.create was null, expected non-null int.'); final int? arg_webViewClientInstanceId = (args[1] as int?); - assert(arg_webViewClientInstanceId != null, - 'Argument for dev.flutter.pigeon.WebChromeClientHostApi.create was null, expected non-null int.'); + assert(arg_webViewClientInstanceId != null, 'Argument for dev.flutter.pigeon.WebChromeClientHostApi.create was null, expected non-null int.'); api.create(arg_instanceId!, arg_webViewClientInstanceId!); return {}; }); @@ -1178,31 +965,23 @@ abstract class TestWebChromeClientHostApi { } } -class _TestAssetManagerHostApiCodec extends StandardMessageCodec { - const _TestAssetManagerHostApiCodec(); -} - abstract class TestAssetManagerHostApi { - static const MessageCodec codec = _TestAssetManagerHostApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); List list(String path); String getAssetFilePathByName(String name); - static void setup(TestAssetManagerHostApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setup(TestAssetManagerHostApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.FlutterAssetManagerHostApi.list', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.FlutterAssetManagerHostApi.list', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.FlutterAssetManagerHostApi.list was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.FlutterAssetManagerHostApi.list was null.'); final List args = (message as List?)!; final String? arg_path = (args[0] as String?); - assert(arg_path != null, - 'Argument for dev.flutter.pigeon.FlutterAssetManagerHostApi.list was null, expected non-null String.'); + assert(arg_path != null, 'Argument for dev.flutter.pigeon.FlutterAssetManagerHostApi.list was null, expected non-null String.'); final List output = api.list(arg_path!); return {'result': output}; }); @@ -1210,19 +989,15 @@ abstract class TestAssetManagerHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.FlutterAssetManagerHostApi.getAssetFilePathByName', - codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.FlutterAssetManagerHostApi.getAssetFilePathByName', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.FlutterAssetManagerHostApi.getAssetFilePathByName was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.FlutterAssetManagerHostApi.getAssetFilePathByName was null.'); final List args = (message as List?)!; final String? arg_name = (args[0] as String?); - assert(arg_name != null, - 'Argument for dev.flutter.pigeon.FlutterAssetManagerHostApi.getAssetFilePathByName was null, expected non-null String.'); + assert(arg_name != null, 'Argument for dev.flutter.pigeon.FlutterAssetManagerHostApi.getAssetFilePathByName was null, expected non-null String.'); final String output = api.getAssetFilePathByName(arg_name!); return {'result': output}; }); @@ -1231,31 +1006,23 @@ abstract class TestAssetManagerHostApi { } } -class _TestWebStorageHostApiCodec extends StandardMessageCodec { - const _TestWebStorageHostApiCodec(); -} - abstract class TestWebStorageHostApi { - static const MessageCodec codec = _TestWebStorageHostApiCodec(); + static const MessageCodec codec = StandardMessageCodec(); void create(int instanceId); void deleteAllData(int instanceId); - static void setup(TestWebStorageHostApi? api, - {BinaryMessenger? binaryMessenger}) { + static void setup(TestWebStorageHostApi? api, {BinaryMessenger? binaryMessenger}) { { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebStorageHostApi.create', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebStorageHostApi.create', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebStorageHostApi.create was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebStorageHostApi.create 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.WebStorageHostApi.create was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebStorageHostApi.create was null, expected non-null int.'); api.create(arg_instanceId!); return {}; }); @@ -1263,18 +1030,15 @@ abstract class TestWebStorageHostApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.WebStorageHostApi.deleteAllData', codec, - binaryMessenger: binaryMessenger); + 'dev.flutter.pigeon.WebStorageHostApi.deleteAllData', codec, binaryMessenger: binaryMessenger); if (api == null) { channel.setMockMessageHandler(null); } else { channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.WebStorageHostApi.deleteAllData was null.'); + assert(message != null, 'Argument for dev.flutter.pigeon.WebStorageHostApi.deleteAllData 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.WebStorageHostApi.deleteAllData was null, expected non-null int.'); + assert(arg_instanceId != null, 'Argument for dev.flutter.pigeon.WebStorageHostApi.deleteAllData was null, expected non-null int.'); api.deleteAllData(arg_instanceId!); return {}; }); diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart index 6698bf097678..d5eba097310d 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart @@ -1,7 +1,8 @@ -// Mocks generated by Mockito 5.2.0 from annotations -// in webview_flutter_android/test/webview_android_cookie_manager_test.dart. +// Mocks generated by Mockito 5.3.2 from annotations +// in webview_pro_android/test/webview_android_cookie_manager_test.dart. // Do not manually edit this file. +// ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i3; import 'package:mockito/mockito.dart' as _i1; @@ -16,6 +17,7 @@ import 'package:webview_pro_android/src/android_webview.dart' as _i2; // 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 [CookieManager]. /// @@ -26,12 +28,27 @@ class MockCookieManager extends _i1.Mock implements _i2.CookieManager { } @override - _i3.Future setCookie(String? url, String? value) => - (super.noSuchMethod(Invocation.method(#setCookie, [url, value]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i3.Future); + _i3.Future setCookie( + String? url, + String? value, + ) => + (super.noSuchMethod( + Invocation.method( + #setCookie, + [ + url, + value, + ], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); @override - _i3.Future clearCookies() => - (super.noSuchMethod(Invocation.method(#clearCookies, []), - returnValue: Future.value(false)) as _i3.Future); + _i3.Future clearCookies() => (super.noSuchMethod( + Invocation.method( + #clearCookies, + [], + ), + returnValue: _i3.Future.value(false), + ) as _i3.Future); } diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart index b8560388ac75..c9f4bcd98f17 100644 --- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart @@ -1,7 +1,8 @@ -// Mocks generated by Mockito 5.2.0 from annotations -// in webview_flutter_android/test/webview_android_widget_test.dart. +// Mocks generated by Mockito 5.3.2 from annotations +// in webview_pro_android/test/webview_android_widget_test.dart. // Do not manually edit this file. +// ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i5; import 'dart:typed_data' as _i6; import 'dart:ui' as _i3; @@ -21,27 +22,101 @@ import 'package:webview_pro_platform_interface/webview_flutter_platform_interfac // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class -class _FakeWebSettings_0 extends _i1.Fake implements _i2.WebSettings {} +class _FakeWebSettings_0 extends _i1.SmartFake implements _i2.WebSettings { + _FakeWebSettings_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeWebStorage_1 extends _i1.Fake implements _i2.WebStorage {} +class _FakeWebStorage_1 extends _i1.SmartFake implements _i2.WebStorage { + _FakeWebStorage_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeOffset_2 extends _i1.Fake implements _i3.Offset {} +class _FakeOffset_2 extends _i1.SmartFake implements _i3.Offset { + _FakeOffset_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeWebView_3 extends _i1.Fake implements _i2.WebView {} +class _FakeWebView_3 extends _i1.SmartFake implements _i2.WebView { + _FakeWebView_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeDownloadListener_4 extends _i1.Fake - implements _i2.DownloadListener {} +class _FakeDownloadListener_4 extends _i1.SmartFake + implements _i2.DownloadListener { + _FakeDownloadListener_4( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeJavascriptChannelRegistry_5 extends _i1.Fake - implements _i4.JavascriptChannelRegistry {} +class _FakeJavascriptChannelRegistry_5 extends _i1.SmartFake + implements _i4.JavascriptChannelRegistry { + _FakeJavascriptChannelRegistry_5( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeJavaScriptChannel_6 extends _i1.Fake - implements _i2.JavaScriptChannel {} +class _FakeJavaScriptChannel_6 extends _i1.SmartFake + implements _i2.JavaScriptChannel { + _FakeJavaScriptChannel_6( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeWebChromeClient_7 extends _i1.Fake implements _i2.WebChromeClient {} +class _FakeWebChromeClient_7 extends _i1.SmartFake + implements _i2.WebChromeClient { + _FakeWebChromeClient_7( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} -class _FakeWebViewClient_8 extends _i1.Fake implements _i2.WebViewClient {} +class _FakeWebViewClient_8 extends _i1.SmartFake implements _i2.WebViewClient { + _FakeWebViewClient_8( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} /// A class which mocks [FlutterAssetManager]. /// @@ -53,14 +128,22 @@ class MockFlutterAssetManager extends _i1.Mock } @override - _i5.Future> list(String? path) => - (super.noSuchMethod(Invocation.method(#list, [path]), - returnValue: Future>.value([])) - as _i5.Future>); + _i5.Future> list(String? path) => (super.noSuchMethod( + Invocation.method( + #list, + [path], + ), + returnValue: _i5.Future>.value([]), + ) as _i5.Future>); @override _i5.Future getAssetFilePathByName(String? name) => - (super.noSuchMethod(Invocation.method(#getAssetFilePathByName, [name]), - returnValue: Future.value('')) as _i5.Future); + (super.noSuchMethod( + Invocation.method( + #getAssetFilePathByName, + [name], + ), + returnValue: _i5.Future.value(''), + ) as _i5.Future); } /// A class which mocks [WebSettings]. @@ -72,70 +155,141 @@ class MockWebSettings extends _i1.Mock implements _i2.WebSettings { } @override - _i5.Future setDomStorageEnabled(bool? flag) => - (super.noSuchMethod(Invocation.method(#setDomStorageEnabled, [flag]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + _i5.Future setDomStorageEnabled(bool? flag) => (super.noSuchMethod( + Invocation.method( + #setDomStorageEnabled, + [flag], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); @override _i5.Future setJavaScriptCanOpenWindowsAutomatically(bool? flag) => (super.noSuchMethod( - Invocation.method(#setJavaScriptCanOpenWindowsAutomatically, [flag]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future setSupportMultipleWindows(bool? support) => (super - .noSuchMethod(Invocation.method(#setSupportMultipleWindows, [support]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future setJavaScriptEnabled(bool? flag) => - (super.noSuchMethod(Invocation.method(#setJavaScriptEnabled, [flag]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future setUserAgentString(String? userAgentString) => (super - .noSuchMethod(Invocation.method(#setUserAgentString, [userAgentString]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + Invocation.method( + #setJavaScriptCanOpenWindowsAutomatically, + [flag], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future setSupportMultipleWindows(bool? support) => + (super.noSuchMethod( + Invocation.method( + #setSupportMultipleWindows, + [support], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future setJavaScriptEnabled(bool? flag) => (super.noSuchMethod( + Invocation.method( + #setJavaScriptEnabled, + [flag], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future setUserAgentString(String? userAgentString) => + (super.noSuchMethod( + Invocation.method( + #setUserAgentString, + [userAgentString], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); @override _i5.Future setMediaPlaybackRequiresUserGesture(bool? require) => (super.noSuchMethod( - Invocation.method(#setMediaPlaybackRequiresUserGesture, [require]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future setSupportZoom(bool? support) => - (super.noSuchMethod(Invocation.method(#setSupportZoom, [support]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future setLoadWithOverviewMode(bool? overview) => (super - .noSuchMethod(Invocation.method(#setLoadWithOverviewMode, [overview]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future setUseWideViewPort(bool? use) => - (super.noSuchMethod(Invocation.method(#setUseWideViewPort, [use]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future setDisplayZoomControls(bool? enabled) => - (super.noSuchMethod(Invocation.method(#setDisplayZoomControls, [enabled]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future setBuiltInZoomControls(bool? enabled) => - (super.noSuchMethod(Invocation.method(#setBuiltInZoomControls, [enabled]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future setAllowFileAccess(bool? enabled) => - (super.noSuchMethod(Invocation.method(#setAllowFileAccess, [enabled]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i2.WebSettings copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebSettings_0()) as _i2.WebSettings); + Invocation.method( + #setMediaPlaybackRequiresUserGesture, + [require], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future setSupportZoom(bool? support) => (super.noSuchMethod( + Invocation.method( + #setSupportZoom, + [support], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future setGeolocation(bool? support) => (super.noSuchMethod( + Invocation.method( + #setGeolocation, + [support], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future setLoadWithOverviewMode(bool? overview) => + (super.noSuchMethod( + Invocation.method( + #setLoadWithOverviewMode, + [overview], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future setUseWideViewPort(bool? use) => (super.noSuchMethod( + Invocation.method( + #setUseWideViewPort, + [use], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future setDisplayZoomControls(bool? enabled) => (super.noSuchMethod( + Invocation.method( + #setDisplayZoomControls, + [enabled], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future setBuiltInZoomControls(bool? enabled) => (super.noSuchMethod( + Invocation.method( + #setBuiltInZoomControls, + [enabled], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future setAllowFileAccess(bool? enabled) => (super.noSuchMethod( + Invocation.method( + #setAllowFileAccess, + [enabled], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i2.WebSettings copy() => (super.noSuchMethod( + Invocation.method( + #copy, + [], + ), + returnValue: _FakeWebSettings_0( + this, + Invocation.method( + #copy, + [], + ), + ), + ) as _i2.WebSettings); } /// A class which mocks [WebStorage]. @@ -147,13 +301,28 @@ class MockWebStorage extends _i1.Mock implements _i2.WebStorage { } @override - _i5.Future deleteAllData() => - (super.noSuchMethod(Invocation.method(#deleteAllData, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i2.WebStorage copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebStorage_1()) as _i2.WebStorage); + _i5.Future deleteAllData() => (super.noSuchMethod( + Invocation.method( + #deleteAllData, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i2.WebStorage copy() => (super.noSuchMethod( + Invocation.method( + #copy, + [], + ), + returnValue: _FakeWebStorage_1( + this, + Invocation.method( + #copy, + [], + ), + ), + ) as _i2.WebStorage); } /// A class which mocks [WebView]. @@ -165,153 +334,315 @@ class MockWebView extends _i1.Mock implements _i2.WebView { } @override - bool get useHybridComposition => - (super.noSuchMethod(Invocation.getter(#useHybridComposition), - returnValue: false) as bool); - @override - _i2.WebSettings get settings => - (super.noSuchMethod(Invocation.getter(#settings), - returnValue: _FakeWebSettings_0()) as _i2.WebSettings); - @override - _i5.Future loadData( - {String? data, String? mimeType, String? encoding}) => + bool get useHybridComposition => (super.noSuchMethod( + Invocation.getter(#useHybridComposition), + returnValue: false, + ) as bool); + @override + _i2.WebSettings get settings => (super.noSuchMethod( + Invocation.getter(#settings), + returnValue: _FakeWebSettings_0( + this, + Invocation.getter(#settings), + ), + ) as _i2.WebSettings); + @override + _i5.Future loadData({ + required String? data, + String? mimeType, + String? encoding, + }) => (super.noSuchMethod( - Invocation.method(#loadData, [], - {#data: data, #mimeType: mimeType, #encoding: encoding}), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future loadDataWithBaseUrl( - {String? baseUrl, - String? data, - String? mimeType, - String? encoding, - String? historyUrl}) => + Invocation.method( + #loadData, + [], + { + #data: data, + #mimeType: mimeType, + #encoding: encoding, + }, + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future loadDataWithBaseUrl({ + String? baseUrl, + required String? data, + String? mimeType, + String? encoding, + String? historyUrl, + }) => (super.noSuchMethod( - Invocation.method(#loadDataWithBaseUrl, [], { + Invocation.method( + #loadDataWithBaseUrl, + [], + { #baseUrl: baseUrl, #data: data, #mimeType: mimeType, #encoding: encoding, - #historyUrl: historyUrl - }), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future loadUrl(String? url, Map? headers) => - (super.noSuchMethod(Invocation.method(#loadUrl, [url, headers]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future postUrl(String? url, _i6.Uint8List? data) => - (super.noSuchMethod(Invocation.method(#postUrl, [url, data]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future getUrl() => - (super.noSuchMethod(Invocation.method(#getUrl, []), - returnValue: Future.value()) as _i5.Future); - @override - _i5.Future canGoBack() => - (super.noSuchMethod(Invocation.method(#canGoBack, []), - returnValue: Future.value(false)) as _i5.Future); - @override - _i5.Future canGoForward() => - (super.noSuchMethod(Invocation.method(#canGoForward, []), - returnValue: Future.value(false)) as _i5.Future); - @override - _i5.Future goBack() => - (super.noSuchMethod(Invocation.method(#goBack, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future goForward() => - (super.noSuchMethod(Invocation.method(#goForward, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future reload() => - (super.noSuchMethod(Invocation.method(#reload, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future clearCache(bool? includeDiskFiles) => - (super.noSuchMethod(Invocation.method(#clearCache, [includeDiskFiles]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future evaluateJavascript(String? javascriptString) => (super - .noSuchMethod(Invocation.method(#evaluateJavascript, [javascriptString]), - returnValue: Future.value()) as _i5.Future); - @override - _i5.Future getTitle() => - (super.noSuchMethod(Invocation.method(#getTitle, []), - returnValue: Future.value()) as _i5.Future); - @override - _i5.Future scrollTo(int? x, int? y) => - (super.noSuchMethod(Invocation.method(#scrollTo, [x, y]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future scrollBy(int? x, int? y) => - (super.noSuchMethod(Invocation.method(#scrollBy, [x, y]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future getScrollX() => - (super.noSuchMethod(Invocation.method(#getScrollX, []), - returnValue: Future.value(0)) as _i5.Future); - @override - _i5.Future getScrollY() => - (super.noSuchMethod(Invocation.method(#getScrollY, []), - returnValue: Future.value(0)) as _i5.Future); - @override - _i5.Future<_i3.Offset> getScrollPosition() => - (super.noSuchMethod(Invocation.method(#getScrollPosition, []), - returnValue: Future<_i3.Offset>.value(_FakeOffset_2())) - as _i5.Future<_i3.Offset>); + #historyUrl: historyUrl, + }, + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future loadUrl( + String? url, + Map? headers, + ) => + (super.noSuchMethod( + Invocation.method( + #loadUrl, + [ + url, + headers, + ], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future postUrl( + String? url, + _i6.Uint8List? data, + ) => + (super.noSuchMethod( + Invocation.method( + #postUrl, + [ + url, + data, + ], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future getUrl() => (super.noSuchMethod( + Invocation.method( + #getUrl, + [], + ), + returnValue: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future canGoBack() => (super.noSuchMethod( + Invocation.method( + #canGoBack, + [], + ), + returnValue: _i5.Future.value(false), + ) as _i5.Future); + @override + _i5.Future canGoForward() => (super.noSuchMethod( + Invocation.method( + #canGoForward, + [], + ), + returnValue: _i5.Future.value(false), + ) as _i5.Future); + @override + _i5.Future goBack() => (super.noSuchMethod( + Invocation.method( + #goBack, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future goForward() => (super.noSuchMethod( + Invocation.method( + #goForward, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future reload() => (super.noSuchMethod( + Invocation.method( + #reload, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future clearCache(bool? includeDiskFiles) => (super.noSuchMethod( + Invocation.method( + #clearCache, + [includeDiskFiles], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future evaluateJavascript(String? javascriptString) => + (super.noSuchMethod( + Invocation.method( + #evaluateJavascript, + [javascriptString], + ), + returnValue: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future getTitle() => (super.noSuchMethod( + Invocation.method( + #getTitle, + [], + ), + returnValue: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future scrollTo( + int? x, + int? y, + ) => + (super.noSuchMethod( + Invocation.method( + #scrollTo, + [ + x, + y, + ], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future scrollBy( + int? x, + int? y, + ) => + (super.noSuchMethod( + Invocation.method( + #scrollBy, + [ + x, + y, + ], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future getScrollX() => (super.noSuchMethod( + Invocation.method( + #getScrollX, + [], + ), + returnValue: _i5.Future.value(0), + ) as _i5.Future); + @override + _i5.Future getScrollY() => (super.noSuchMethod( + Invocation.method( + #getScrollY, + [], + ), + returnValue: _i5.Future.value(0), + ) as _i5.Future); + @override + _i5.Future<_i3.Offset> getScrollPosition() => (super.noSuchMethod( + Invocation.method( + #getScrollPosition, + [], + ), + returnValue: _i5.Future<_i3.Offset>.value(_FakeOffset_2( + this, + Invocation.method( + #getScrollPosition, + [], + ), + )), + ) as _i5.Future<_i3.Offset>); @override _i5.Future setWebViewClient(_i2.WebViewClient? webViewClient) => - (super.noSuchMethod(Invocation.method(#setWebViewClient, [webViewClient]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + (super.noSuchMethod( + Invocation.method( + #setWebViewClient, + [webViewClient], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); @override _i5.Future addJavaScriptChannel( _i2.JavaScriptChannel? javaScriptChannel) => (super.noSuchMethod( - Invocation.method(#addJavaScriptChannel, [javaScriptChannel]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + Invocation.method( + #addJavaScriptChannel, + [javaScriptChannel], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); @override _i5.Future removeJavaScriptChannel( _i2.JavaScriptChannel? javaScriptChannel) => (super.noSuchMethod( - Invocation.method(#removeJavaScriptChannel, [javaScriptChannel]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + Invocation.method( + #removeJavaScriptChannel, + [javaScriptChannel], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); @override _i5.Future setDownloadListener(_i2.DownloadListener? listener) => - (super.noSuchMethod(Invocation.method(#setDownloadListener, [listener]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + (super.noSuchMethod( + Invocation.method( + #setDownloadListener, + [listener], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); @override _i5.Future setWebChromeClient(_i2.WebChromeClient? client) => - (super.noSuchMethod(Invocation.method(#setWebChromeClient, [client]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future setBackgroundColor(_i3.Color? color) => - (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i5.Future release() => - (super.noSuchMethod(Invocation.method(#release, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); - @override - _i2.WebView copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebView_3()) as _i2.WebView); + (super.noSuchMethod( + Invocation.method( + #setWebChromeClient, + [client], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future setBackgroundColor(_i3.Color? color) => (super.noSuchMethod( + Invocation.method( + #setBackgroundColor, + [color], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i5.Future release() => (super.noSuchMethod( + Invocation.method( + #release, + [], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override + _i2.WebView copy() => (super.noSuchMethod( + Invocation.method( + #copy, + [], + ), + returnValue: _FakeWebView_3( + this, + Invocation.method( + #copy, + [], + ), + ), + ) as _i2.WebView); } /// A class which mocks [WebResourceRequest]. @@ -324,24 +655,30 @@ class MockWebResourceRequest extends _i1.Mock } @override - String get url => - (super.noSuchMethod(Invocation.getter(#url), returnValue: '') as String); - @override - bool get isForMainFrame => (super - .noSuchMethod(Invocation.getter(#isForMainFrame), returnValue: false) - as bool); - @override - bool get hasGesture => - (super.noSuchMethod(Invocation.getter(#hasGesture), returnValue: false) - as bool); - @override - String get method => - (super.noSuchMethod(Invocation.getter(#method), returnValue: '') - as String); - @override - Map get requestHeaders => - (super.noSuchMethod(Invocation.getter(#requestHeaders), - returnValue: {}) as Map); + String get url => (super.noSuchMethod( + Invocation.getter(#url), + returnValue: '', + ) as String); + @override + bool get isForMainFrame => (super.noSuchMethod( + Invocation.getter(#isForMainFrame), + returnValue: false, + ) as bool); + @override + bool get hasGesture => (super.noSuchMethod( + Invocation.getter(#hasGesture), + returnValue: false, + ) as bool); + @override + String get method => (super.noSuchMethod( + Invocation.getter(#method), + returnValue: '', + ) as String); + @override + Map get requestHeaders => (super.noSuchMethod( + Invocation.getter(#requestHeaders), + returnValue: {}, + ) as Map); } /// A class which mocks [WebViewAndroidDownloadListener]. @@ -354,22 +691,55 @@ class MockWebViewAndroidDownloadListener extends _i1.Mock } @override - _i5.Future Function(String, Map?) get loadUrl => - (super.noSuchMethod(Invocation.getter(#loadUrl), - returnValue: (String url, Map? headers) => - Future.value()) as _i5.Future Function( - String, Map?)); - @override - void onDownloadStart(String? url, String? userAgent, - String? contentDisposition, String? mimetype, int? contentLength) => + _i5.Future Function( + String, + Map?, + ) get loadUrl => (super.noSuchMethod( + Invocation.getter(#loadUrl), + returnValue: ( + String url, + Map? headers, + ) => + _i5.Future.value(), + ) as _i5.Future Function( + String, + Map?, + )); + @override + void onDownloadStart( + String? url, + String? userAgent, + String? contentDisposition, + String? mimetype, + int? contentLength, + ) => super.noSuchMethod( - Invocation.method(#onDownloadStart, - [url, userAgent, contentDisposition, mimetype, contentLength]), - returnValueForMissingStub: null); - @override - _i2.DownloadListener copy() => - (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeDownloadListener_4()) as _i2.DownloadListener); + Invocation.method( + #onDownloadStart, + [ + url, + userAgent, + contentDisposition, + mimetype, + contentLength, + ], + ), + returnValueForMissingStub: null, + ); + @override + _i2.DownloadListener copy() => (super.noSuchMethod( + Invocation.method( + #copy, + [], + ), + returnValue: _FakeDownloadListener_4( + this, + Invocation.method( + #copy, + [], + ), + ), + ) as _i2.DownloadListener); } /// A class which mocks [WebViewAndroidJavaScriptChannel]. @@ -383,21 +753,40 @@ class MockWebViewAndroidJavaScriptChannel extends _i1.Mock @override _i4.JavascriptChannelRegistry get javascriptChannelRegistry => - (super.noSuchMethod(Invocation.getter(#javascriptChannelRegistry), - returnValue: _FakeJavascriptChannelRegistry_5()) - as _i4.JavascriptChannelRegistry); - @override - String get channelName => - (super.noSuchMethod(Invocation.getter(#channelName), returnValue: '') - as String); - @override - void postMessage(String? message) => - super.noSuchMethod(Invocation.method(#postMessage, [message]), - returnValueForMissingStub: null); - @override - _i2.JavaScriptChannel copy() => - (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeJavaScriptChannel_6()) as _i2.JavaScriptChannel); + (super.noSuchMethod( + Invocation.getter(#javascriptChannelRegistry), + returnValue: _FakeJavascriptChannelRegistry_5( + this, + Invocation.getter(#javascriptChannelRegistry), + ), + ) as _i4.JavascriptChannelRegistry); + @override + String get channelName => (super.noSuchMethod( + Invocation.getter(#channelName), + returnValue: '', + ) as String); + @override + void postMessage(String? message) => super.noSuchMethod( + Invocation.method( + #postMessage, + [message], + ), + returnValueForMissingStub: null, + ); + @override + _i2.JavaScriptChannel copy() => (super.noSuchMethod( + Invocation.method( + #copy, + [], + ), + returnValue: _FakeJavaScriptChannel_6( + this, + Invocation.method( + #copy, + [], + ), + ), + ) as _i2.JavaScriptChannel); } /// A class which mocks [WebViewAndroidWebChromeClient]. @@ -410,13 +799,34 @@ class MockWebViewAndroidWebChromeClient extends _i1.Mock } @override - void onProgressChanged(_i2.WebView? webView, int? progress) => super - .noSuchMethod(Invocation.method(#onProgressChanged, [webView, progress]), - returnValueForMissingStub: null); - @override - _i2.WebChromeClient copy() => - (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebChromeClient_7()) as _i2.WebChromeClient); + void onProgressChanged( + _i2.WebView? webView, + int? progress, + ) => + super.noSuchMethod( + Invocation.method( + #onProgressChanged, + [ + webView, + progress, + ], + ), + returnValueForMissingStub: null, + ); + @override + _i2.WebChromeClient copy() => (super.noSuchMethod( + Invocation.method( + #copy, + [], + ), + returnValue: _FakeWebChromeClient_7( + this, + Invocation.method( + #copy, + [], + ), + ), + ) as _i2.WebChromeClient); } /// A class which mocks [WebViewAndroidWebViewClient]. @@ -429,65 +839,151 @@ class MockWebViewAndroidWebViewClient extends _i1.Mock } @override - void Function(String) get onPageStartedCallback => - (super.noSuchMethod(Invocation.getter(#onPageStartedCallback), - returnValue: (String url) {}) as void Function(String)); + void Function(String) get onPageStartedCallback => (super.noSuchMethod( + Invocation.getter(#onPageStartedCallback), + returnValue: (String url) {}, + ) as void Function(String)); @override - void Function(String) get onPageFinishedCallback => - (super.noSuchMethod(Invocation.getter(#onPageFinishedCallback), - returnValue: (String url) {}) as void Function(String)); + void Function(String) get onPageFinishedCallback => (super.noSuchMethod( + Invocation.getter(#onPageFinishedCallback), + returnValue: (String url) {}, + ) as void Function(String)); @override void Function(_i4.WebResourceError) get onWebResourceErrorCallback => - (super.noSuchMethod(Invocation.getter(#onWebResourceErrorCallback), - returnValue: (_i4.WebResourceError error) {}) - as void Function(_i4.WebResourceError)); + (super.noSuchMethod( + Invocation.getter(#onWebResourceErrorCallback), + returnValue: (_i4.WebResourceError error) {}, + ) as void Function(_i4.WebResourceError)); @override set onWebResourceErrorCallback( void Function(_i4.WebResourceError)? _onWebResourceErrorCallback) => super.noSuchMethod( - Invocation.setter( - #onWebResourceErrorCallback, _onWebResourceErrorCallback), - returnValueForMissingStub: null); - @override - bool get handlesNavigation => - (super.noSuchMethod(Invocation.getter(#handlesNavigation), - returnValue: false) as bool); - @override - bool get shouldOverrideUrlLoading => - (super.noSuchMethod(Invocation.getter(#shouldOverrideUrlLoading), - returnValue: false) as bool); - @override - void onPageStarted(_i2.WebView? webView, String? url) => - super.noSuchMethod(Invocation.method(#onPageStarted, [webView, url]), - returnValueForMissingStub: null); - @override - void onPageFinished(_i2.WebView? webView, String? url) => - super.noSuchMethod(Invocation.method(#onPageFinished, [webView, url]), - returnValueForMissingStub: null); - @override - void onReceivedError(_i2.WebView? webView, int? errorCode, - String? description, String? failingUrl) => + Invocation.setter( + #onWebResourceErrorCallback, + _onWebResourceErrorCallback, + ), + returnValueForMissingStub: null, + ); + @override + bool get handlesNavigation => (super.noSuchMethod( + Invocation.getter(#handlesNavigation), + returnValue: false, + ) as bool); + @override + bool get shouldOverrideUrlLoading => (super.noSuchMethod( + Invocation.getter(#shouldOverrideUrlLoading), + returnValue: false, + ) as bool); + @override + void onPageStarted( + _i2.WebView? webView, + String? url, + ) => super.noSuchMethod( - Invocation.method( - #onReceivedError, [webView, errorCode, description, failingUrl]), - returnValueForMissingStub: null); - @override - void onReceivedRequestError(_i2.WebView? webView, - _i2.WebResourceRequest? request, _i2.WebResourceError? error) => + Invocation.method( + #onPageStarted, + [ + webView, + url, + ], + ), + returnValueForMissingStub: null, + ); + @override + void onPageFinished( + _i2.WebView? webView, + String? url, + ) => super.noSuchMethod( - Invocation.method(#onReceivedRequestError, [webView, request, error]), - returnValueForMissingStub: null); - @override - void urlLoading(_i2.WebView? webView, String? url) => - super.noSuchMethod(Invocation.method(#urlLoading, [webView, url]), - returnValueForMissingStub: null); - @override - void requestLoading(_i2.WebView? webView, _i2.WebResourceRequest? request) => - super.noSuchMethod(Invocation.method(#requestLoading, [webView, request]), - returnValueForMissingStub: null); - @override - _i2.WebViewClient copy() => (super.noSuchMethod(Invocation.method(#copy, []), - returnValue: _FakeWebViewClient_8()) as _i2.WebViewClient); + Invocation.method( + #onPageFinished, + [ + webView, + url, + ], + ), + returnValueForMissingStub: null, + ); + @override + void onReceivedError( + _i2.WebView? webView, + int? errorCode, + String? description, + String? failingUrl, + ) => + super.noSuchMethod( + Invocation.method( + #onReceivedError, + [ + webView, + errorCode, + description, + failingUrl, + ], + ), + returnValueForMissingStub: null, + ); + @override + void onReceivedRequestError( + _i2.WebView? webView, + _i2.WebResourceRequest? request, + _i2.WebResourceError? error, + ) => + super.noSuchMethod( + Invocation.method( + #onReceivedRequestError, + [ + webView, + request, + error, + ], + ), + returnValueForMissingStub: null, + ); + @override + void urlLoading( + _i2.WebView? webView, + String? url, + ) => + super.noSuchMethod( + Invocation.method( + #urlLoading, + [ + webView, + url, + ], + ), + returnValueForMissingStub: null, + ); + @override + void requestLoading( + _i2.WebView? webView, + _i2.WebResourceRequest? request, + ) => + super.noSuchMethod( + Invocation.method( + #requestLoading, + [ + webView, + request, + ], + ), + returnValueForMissingStub: null, + ); + @override + _i2.WebViewClient copy() => (super.noSuchMethod( + Invocation.method( + #copy, + [], + ), + returnValue: _FakeWebViewClient_8( + this, + Invocation.method( + #copy, + [], + ), + ), + ) as _i2.WebViewClient); } /// A class which mocks [JavascriptChannelRegistry]. @@ -500,20 +996,34 @@ class MockJavascriptChannelRegistry extends _i1.Mock } @override - Map get channels => - (super.noSuchMethod(Invocation.getter(#channels), - returnValue: {}) - as Map); + Map get channels => (super.noSuchMethod( + Invocation.getter(#channels), + returnValue: {}, + ) as Map); @override - void onJavascriptChannelMessage(String? channel, String? message) => + void onJavascriptChannelMessage( + String? channel, + String? message, + ) => super.noSuchMethod( - Invocation.method(#onJavascriptChannelMessage, [channel, message]), - returnValueForMissingStub: null); + Invocation.method( + #onJavascriptChannelMessage, + [ + channel, + message, + ], + ), + returnValueForMissingStub: null, + ); @override void updateJavascriptChannelsFromSet(Set<_i4.JavascriptChannel>? channels) => super.noSuchMethod( - Invocation.method(#updateJavascriptChannelsFromSet, [channels]), - returnValueForMissingStub: null); + Invocation.method( + #updateJavascriptChannelsFromSet, + [channels], + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [WebViewPlatformCallbacksHandler]. @@ -526,27 +1036,53 @@ class MockWebViewPlatformCallbacksHandler extends _i1.Mock } @override - _i5.FutureOr onNavigationRequest({String? url, bool? isForMainFrame}) => + _i5.FutureOr onNavigationRequest({ + required String? url, + required bool? isForMainFrame, + }) => (super.noSuchMethod( - Invocation.method(#onNavigationRequest, [], - {#url: url, #isForMainFrame: isForMainFrame}), - returnValue: Future.value(false)) as _i5.FutureOr); - @override - void onPageStarted(String? url) => - super.noSuchMethod(Invocation.method(#onPageStarted, [url]), - returnValueForMissingStub: null); - @override - void onPageFinished(String? url) => - super.noSuchMethod(Invocation.method(#onPageFinished, [url]), - returnValueForMissingStub: null); - @override - void onProgress(int? progress) => - super.noSuchMethod(Invocation.method(#onProgress, [progress]), - returnValueForMissingStub: null); - @override - void onWebResourceError(_i4.WebResourceError? error) => - super.noSuchMethod(Invocation.method(#onWebResourceError, [error]), - returnValueForMissingStub: null); + Invocation.method( + #onNavigationRequest, + [], + { + #url: url, + #isForMainFrame: isForMainFrame, + }, + ), + returnValue: _i5.Future.value(false), + ) as _i5.FutureOr); + @override + void onPageStarted(String? url) => super.noSuchMethod( + Invocation.method( + #onPageStarted, + [url], + ), + returnValueForMissingStub: null, + ); + @override + void onPageFinished(String? url) => super.noSuchMethod( + Invocation.method( + #onPageFinished, + [url], + ), + returnValueForMissingStub: null, + ); + @override + void onProgress(int? progress) => super.noSuchMethod( + Invocation.method( + #onProgress, + [progress], + ), + returnValueForMissingStub: null, + ); + @override + void onWebResourceError(_i4.WebResourceError? error) => super.noSuchMethod( + Invocation.method( + #onWebResourceError, + [error], + ), + returnValueForMissingStub: null, + ); } /// A class which mocks [WebViewProxy]. @@ -558,15 +1094,30 @@ class MockWebViewProxy extends _i1.Mock implements _i7.WebViewProxy { } @override - _i2.WebView createWebView({bool? useHybridComposition}) => + _i2.WebView createWebView({required bool? useHybridComposition}) => (super.noSuchMethod( - Invocation.method(#createWebView, [], - {#useHybridComposition: useHybridComposition}), - returnValue: _FakeWebView_3()) as _i2.WebView); + Invocation.method( + #createWebView, + [], + {#useHybridComposition: useHybridComposition}, + ), + returnValue: _FakeWebView_3( + this, + Invocation.method( + #createWebView, + [], + {#useHybridComposition: useHybridComposition}, + ), + ), + ) as _i2.WebView); @override _i5.Future setWebContentsDebuggingEnabled(bool? enabled) => (super.noSuchMethod( - Invocation.method(#setWebContentsDebuggingEnabled, [enabled]), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i5.Future); + Invocation.method( + #setWebContentsDebuggingEnabled, + [enabled], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); } From 5292ea2dde45952ee8914ee836a7e56170401e06 Mon Sep 17 00:00:00 2001 From: rekire Date: Mon, 31 Oct 2022 13:29:24 +0100 Subject: [PATCH 843/844] Check if the camera permission is declared if not directly open the file chooser --- .../WebChromeClientHostApiImpl.java | 61 +++++++++++-------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientHostApiImpl.java index 01282d847300..8e00659013e8 100644 --- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientHostApiImpl.java +++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientHostApiImpl.java @@ -6,46 +6,43 @@ import static io.flutter.plugins.webviewflutter.WebViewFlutterPlugin.application; -import android.app.Application; +import android.Manifest; +import android.annotation.TargetApi; +import android.app.Activity; +import android.app.AlertDialog; +import android.content.ClipData; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Build; import android.os.Message; +import android.provider.MediaStore; +import android.util.Log; import android.webkit.GeolocationPermissions; +import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebResourceRequest; import android.webkit.WebView; import android.webkit.WebViewClient; +import android.widget.Toast; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.annotation.Size; import androidx.annotation.VisibleForTesting; -import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebChromeClientHostApi; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +import androidx.core.content.FileProvider; -import android.net.Uri; -import android.util.Log; -import android.Manifest; -import android.annotation.TargetApi; -import android.app.Activity; -import android.app.AlertDialog; -import android.content.ClipData; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.hardware.display.DisplayManager; -import util.FileUtil; -import android.widget.Toast; -import android.content.ClipData; -import android.webkit.ValueCallback; -import android.provider.MediaStore; import java.io.File; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.Arrays; -import androidx.core.content.ContextCompat; -import androidx.core.content.FileProvider; -import androidx.core.app.ActivityCompat; +import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebChromeClientHostApi; +import util.FileUtil; /** * Host api implementation for {@link WebChromeClient}. @@ -250,6 +247,20 @@ private static void takePhotoOrOpenGallery() { if (WebViewFlutterPlugin.activity==null||!FileUtil.checkSDcard(WebViewFlutterPlugin.activity)) { return; } + String[] declaredPermissions = new String[0]; + try { + PackageInfo info = WebViewFlutterPlugin.activity.getPackageManager().getPackageInfo( + WebViewFlutterPlugin.activity.getPackageName(), + PackageManager.GET_PERMISSIONS + ); + declaredPermissions = info.requestedPermissions; + } catch (PackageManager.NameNotFoundException ignored) { + } + if (!Arrays.asList(declaredPermissions).contains(Manifest.permission.CAMERA)) { + openImageChooserActivity(); + return; + } + String[] selectPicTypeStr = {WebViewFlutterPlugin.activity.getString(R.string.take_photo), WebViewFlutterPlugin.activity.getString(R.string.photo_library)}; new AlertDialog.Builder(WebViewFlutterPlugin.activity, AlertDialog.THEME_DEVICE_DEFAULT_DARK) From 9713d3b589e8db565d2046bf8a11aee73af33aca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kilczan?= Date: Fri, 18 Nov 2022 19:40:22 +0100 Subject: [PATCH 844/844] Fix packages --- packages/webview_flutter/webview_flutter/pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 3eba0b89a205..6b271040c43a 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -12,9 +12,9 @@ flutter: plugin: platforms: android: - default_package: webview_flutter_android + default_package: webview_pro_android ios: - default_package: webview_flutter_wkwebview + default_package: webview_pro_wkwebview dependencies: flutter: